Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Python/Numpy: преобразовать список bool в unsigned int

Автор вопроса: Steve TjoaОтветов: 10
  1. Каков самый быстрый (или самый "питоновский") способ конвертации

    x = [False, False, True, True]
    

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

  2. Что, если бы 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

pythonnumpyscipylist

Ответы

Ответ 1 · рейтинг 11 · Justin Peel

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

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 раза быстрее.

>>> 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

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы