Python/Numpy: преобразовать список bool в unsigned int
Автор вопроса: Steve TjoaОтветов: 10
Каков самый быстрый (или самый "питоновский") способ конвертации
x = [False, False, True, True]
в 12? (Если есть такой способ.)
Что, если бы x было вместо numpy.array логических значений? Есть ли для этого специальная команда?
У меня есть большой массив логических значений размером m на 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 по-прежнему работает, но работает в шесть раз медленнее! Тогда, возможно, другие ответы станут быстрее, если логическое значение будет приведено с использованием int. Но мне лень все заново тестировать.
Изменить: liori опубликовал результаты независимых тестов здесь.
Каково правило преобразования [False, False, True, True] в 12? 01.11.2010
x[0] - это младший бит, а x[-1] - это старший бит. Steve Tjoa 01.11.2010
Пожалуйста, используйте timeit для тестирования, это гораздо менее подвержено ошибкам. Мое время: pastebin.com/x1FEP9gY liori 01.11.2010
Спасибо за тесты! Я в них совершенно не сомневаюсь. Я добавил их в пост. Steve Tjoa 01.11.2010
Стоит отметить: в тесте liori sven2() с треском проваливается, потому что мы используем 1000-битные числа. Проверьте результаты (например, числа, возвращаемые каждой функцией), и вы увидите, что ее результат неверен для такого большого числа. Justin Peel 01.11.2010
@Justing Peel. Хотя в целом вы совершенно правы, если я что-то не упустил, в примере используются 20-битные числа, не 1000-битные! Не должно быть проблем с 20 столбцами в методах на основе numpy.... Joe Kington 01.11.2010
Взяв различные идеи из разных других ответов, вот еще один способ сделать это:
sum(1<<i for i, b in enumerate(x) if b)
В моих тестах это довольно быстро - вплоть до метода numpy для большого количества битов, даже если он переполняется как сумасшедший. Я использовал тестовый модуль liori для тестирования. Метод Стива с предложенным мною изменением чуть быстрее. Однако, если нужно выполнять много таких преобразований за раз (и с не слишком большим количеством битов), я уверен, что numpy будет быстрее.
Ответ 2 · рейтинг 6 · Sven Marnach
Большинство Pythonic может быть таким:
sum(2**i*b for i, b in enumerate(x))
Трудно сказать, является ли он также самым быстрым.
В numpy я бы использовал
numpy.sum(2**numpy.arange(len(x))*x)
но это не будет быстрее для небольших массивов x и не будет работать для больших массивов x, поскольку вместо целых чисел произвольной точности Python используются целые числа машинного размера.
Ответ 3 · рейтинг 3 · liori
reduce(lambda a,b:2*a+b, reversed(x))
Вы можете избавиться от reversed(), если у вас есть младший бит в конце массива. Это также работает с numpy.array и не требует enumerate(). Судя по моим тестам, тоже быстрее: нет необходимости использовать возведение в степень.
Ответ 4 · рейтинг 2 · Steve Tjoa
Моя первая попытка, просто для справки:
def bool2int(x):
y = 0
for i,j in enumerate(x):
if j: y += int(j)<<i
return y
Ответ 5 · рейтинг 2 · Community
Элегантный, питонический, всегда работающий способ:
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 (путем перечисления и возведения в квадрат понимания, как это делают другие ответы), но, возможно, так будет понятнее.
Ответ 6 · рейтинг 1 · Emil H
Что-то вроде этого?
>>> 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 · рейтинг 1 · luispedro
Если у вас есть матрица, вы, вероятно, захотите сделать это так:
#precompute powers of two
vals = 2.**np.arange(20)
B = ....
compressed = np.dot(B, vals) # matrix multiplication.
np.dot должен быть быстрее любого цикла в Python. Намного быстрее.
Ответ 8 · рейтинг 1 · Atreya
Я пробовал ipython %timeit, и кажется, что быстрее сделать следующее:
y = 0
for i,j in enumerate(x):
if j: y += 1<<i
Кроме того, если ваш логический вектор представляет собой numpy.ndarray, преобразование его в массив python x.tolist() и запуск того же, похоже, в этом случае работает быстрее. Все это маргинально, но последовательно, а на таких скоростях маргиналы хорошо складываются.
Ответ 9 · рейтинг 1 · jtaylor
Для этого в 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 · casevh
Если вы хотите добавить еще одно расширение, я добавил pack() и unpack() в ветку разработки gmpy. Мои тесты показывают, что это может быть в 2 или 3 раза быстрее.
Отказ от ответственности: разрабатываемая версия называется gmpy2 и может сосуществовать со стабильной версией. Он все еще находится в альфа-фазе, но, надеюсь, станет бета-версией через несколько недель. У вас должны быть установлены библиотеки GMP и MPFR. Исходный код доступен по адресу http://code.google.com/p/gmpy/source/checkout