Python/Numpy: преобразовать список bools в unsigned int
-
Каков самый быстрый (или самый "Pythonic" ) способ конвертировать
x = [False, False, True, True]
в 12
? (Если есть такой способ.)
-
Что делать, если x
были вместо numpy.array
для bools? Есть ли для этого специальная команда?
У меня есть большой массив m-by-n из булевых элементов, где каждая строка n-элементов представляет собой один низкоразмерный хеш высокомерного вектор-функции. (В приведенном выше примере, n = 4.) Я хотел бы узнать ответ, чтобы максимально сжать мои данные. Спасибо.
Изменить: Спасибо за ответы! Используя следующий тестовый код,
t = 0
for iter in range(500):
B = scipy.signbit(scipy.randn(1000,20))
for b in B:
t0 = time.clock()
# test code here
t1 = time.clock()
t += (t1-t0)
print t
... здесь были течения на моем ноутбуке Thinkpad:
Конечно, я приветствую любые независимые тесты, которые могут подтвердить или опровергнуть мои данные!
Изменить: в моем ответе ниже изменение int(j)
на просто j
по-прежнему работает, но выполняется в шесть раз медленнее! Тогда, возможно, другие ответы станут быстрее, если bool будет запущен с помощью int
. Но я слишком ленив, чтобы снова проверить все.
Изменить: liori опубликовал результаты независимых тестов здесь.
Ответы
Ответ 1
Принимая различные идеи из различных других ответов, здесь другой способ сделать это:
sum(1<<i for i, b in enumerate(x) if b)
В моих тестах довольно быстро - прямо с методом numpy для большого количества бит, даже если он переполняется как сумасшедший. Я использовал модуль тестирования liori для тестирования. Метод Стива, с изменением, который я предложил, только чуть быстрее. Однако, если много таких преобразований нужно делать за раз (и с не слишком большим количеством бит), я уверен, что numpy будет быстрее.
Ответ 2
В большинстве случаев Pythonic может быть следующим:
sum(2**i*b for i, b in enumerate(x))
Трудно сказать, является ли это также самым быстрым.
В numpy я бы использовал
numpy.sum(2**numpy.arange(len(x))*x)
но это не будет быстрее для небольших массивов x
, и оно не будет работать для больших массивов x
, так как целые числа размера машины используются вместо целых чисел с произвольной точностью Pythons.
Ответ 3
reduce(lambda a,b:2*a+b, reversed(x))
Вы можете избавиться от reverse(), если у вас был младший бит в конце массива. Это также работает с numpy.array и не нуждается в перечислении(). Из моих тестов, похоже, тоже быстрее: нет необходимости использовать возведение в степень.
Ответ 4
Элегантный, питонический, всегда работающий способ:
def powers(x):
"""yield powers of x, starting from x**0 forever"""
power = 1
while True:
yield power
power *= x
def bools_to_int(bools):
# in Python 2, use itertools.izip!
return sum(int(place) * place_weight for place_weight, place in
zip(powers(2), bools))
Обратите внимание, что вы можете избавиться от powers
(путем перечисления и возведения в квадрат в понимании, как это делают другие ответы), но, возможно, это яснее.
Ответ 5
Моя первоначальная попытка, только для справки:
def bool2int(x):
y = 0
for i,j in enumerate(x):
if j: y += int(j)<<i
return y
Ответ 6
Что-то вроде этого?
>>> x = [False, False, True, True]
>>> sum([int(y[1])*2**y[0] for y in enumerate(x)])
12
Вы можете преобразовать массив numpy в обычный список, используя приведение list()
.
>>> a = numpy.array([1,2,3,4])
>>> a
array([1, 2, 3, 4])
>>> list(a)
[1, 2, 3, 4]
Ответ 7
Если у вас есть матрица, вы, вероятно, захотите сделать это следующим образом:
#precompute powers of two
vals = 2.**np.arange(20)
B = ....
compressed = np.dot(B, vals) # matrix multiplication.
np.dot должен быть быстрее любого цикла в Python. Гораздо быстрее.
Ответ 8
Я пытался ipython %timeit
, и кажется, что выполнение следующего выполняется быстрее:
y = 0
for i,j in enumerate(x):
if j: y += 1<<i
Кроме того, если ваш логический вектор - numpy.ndarray, преобразование его в массив python x.tolist()
и работающий, похоже, работает быстрее в этом случае. Все это маргинальное, но последовательное, а также на этих скоростях маргиналы складываются хорошо.
Ответ 9
numpy имеет packbits для этого.
Он также поддерживает операции по осям:
In [3]: B = scipy.signbit(scipy.randn(1000,8)).astype("i1")
In [3]: B[0]
Out[3]: array([0, 1, 0, 0, 0, 1, 0, 0], dtype=int8)
In [4]: np.packbits(B[0])
Out[4]: array([68], dtype=uint8)
In [5]: %timeit np.packbits(B, axis=1)
10000 loops, best of 3: 37 µs per loop
он работает для размеров int8 для больших размеров, которые вам нужно сдвинуть, и/или
In [8]: x # multiple of 8
Out[8]: array([1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1], dtype=int8)
In [9]: r = np.packbits(x).astype(np.int32); r
Out[9]: array([171, 129], dtype=uint8)
In [10]: r[0] << 8 | r[1]
Out[10]: 33237
In [11]: sum(1<<i for i, b in enumerate(x[::-1]) if b)
Out[11]: 33237
если x
не кратно 8, вам нужно заполнить нулями
Ответ 10
Если вы хотите добавить еще одно расширение в микс, я добавил pack() и unpack() в ветку разработки gmpy. Мои тесты показывают, что это может быть в 2 или 3 раза быстрее.
>>> import gmpy2
>>> gmpy2.pack([0,0,1,1],1)
mpz(12)
>>> gmpy2.unpack(12,1)
[mpz(0), mpz(0), mpz(1), mpz(1)]
Отказ от ответственности: версия разработки называется gmpy2 и может сосуществовать со стабильной версией. Он все еще находится в альфа-фазе, но, надеюсь, станет бета-версией в течение нескольких недель. Вам необходимо установить библиотеки GMP и MPFR. Источник доступен по адресу http://code.google.com/p/gmpy/source/checkout