Подтвердить что ты не робот

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 опубликовал результаты независимых тестов здесь.

4b9b3361

Ответ 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