Python / Numpy: конвертировать список bools в unsigned int - PullRequest
12 голосов
/ 01 ноября 2010
  1. Какой самый быстрый (или самый "питонский") способ конвертировать

    x = [False, False, True, True]
    

    в 12?(Если есть такой способ.)

  2. Что если x вместо numpy.array bools?Есть ли для этого специальная команда?

У меня есть большой массив логических выражений 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:

Конечно, я приветствую любые независимые тесты, которые могут подтвердить или опровергнуть моиdata!


Редактировать : В моем ответе ниже изменение int(j) на просто j все еще работает, но работает в шесть раз медленнее!Тогда, возможно, другие ответы стали бы быстрее, если бы bool был приведен с использованием int.Но мне лень проверять все снова.


Редактировать : liori опубликовал результаты независимых тестов здесь .

Ответы [ 10 ]

10 голосов
/ 01 ноября 2010

Взяв различные идеи из различных других ответов, вот еще один способ сделать это:

sum(1<<i for i, b in enumerate(x) if b)

Это довольно быстро в моих тестах - вплоть до метода numpy для большого количества бит, даже если он переполняетсякак сумасшедший.Я использовал модуль тестирования Лиори для тестирования.Метод Стива, с предложенными мною изменениями, чуть быстрее.Однако, если за один раз нужно выполнить много таких преобразований (и с не слишком большим количеством битов), я держу пари, что numpy будет быстрее.

6 голосов
/ 01 ноября 2010

Большинство Pythonic может быть таким:

sum(2**i*b for i, b in enumerate(x))

Трудно сказать, если это также самый быстрый.

В NumPy я бы использовал

numpy.sum(2**numpy.arange(len(x))*x)

, ноэто не будет быстрее для маленьких массивов x и не будет работать для больших массивов x, поскольку вместо чисел с точными числами Питона используются целые числа машинного размера.

3 голосов
/ 01 ноября 2010
reduce(lambda a,b:2*a+b, reversed(x))

Вы можете избавиться от reversed (), если у вас есть младший бит в конце массива.Это также работает с numpy.array и не требует перечисления ().Из моих тестов, кажется, тоже быстрее: не нужно использовать возведение в степень.

2 голосов
/ 01 ноября 2010

Моя первая попытка, просто для справки:

def bool2int(x):
    y = 0
    for i,j in enumerate(x):
        if j: y += int(j)<<i
    return y
2 голосов
/ 01 ноября 2010

Элегантный, питонический, всегда работающий способ таков:

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 (путем перечисления и возведения в квадрат в понимании, какдругие ответы делают) - но, может быть, так понятнее.

1 голос
/ 19 июля 2014

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 у васдополнить нулями

1 голос
/ 24 августа 2013

Я пытался ipython %timeit, и кажется, что выполнить следующее быстрее:

y = 0
for i,j in enumerate(x):
    if j: y += 1<<i

Кроме того, если ваш логический вектор представляет собой numpy.ndarray, преобразование его в массив python x.tolist() и его запуск в этом случае работает быстрее. Это все маргинально, но непротиворечиво, а также, на этих скоростях маргиналы хорошо складываются.

1 голос
/ 10 ноября 2010

Если у вас есть матрица, вы, вероятно, хотите сделать это так:

#precompute powers of two
vals = 2.**np.arange(20)

B = ....
compressed = np.dot(B, vals) # matrix multiplication.

np.dot должен быть быстрее любого цикла в Python. Гораздо быстрее.

1 голос
/ 01 ноября 2010

Как то так?

>>> x = [False, False, True, True]
>>> sum([int(y[1])*2**y[0] for y in enumerate(x)])
12

Вы можете преобразовать пустой массив в обычный список, используя приведение list().

>>> a = numpy.array([1,2,3,4])
>>> a
array([1, 2, 3, 4])
>>> list(a)
[1, 2, 3, 4]
0 голосов
/ 01 ноября 2010

Если вы хотите добавить другое расширение в микс, я добавил 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...