Как извлечь биты из больших числовых типов данных Numpy

Numpy имеет библиотечную функцию, np.unpackbits, который распакует uint8 в битовый вектор длины 8. Есть ли соответственно быстрый способ распаковки больших числовых типов? Например uint16 или же uint32, Я работаю над вопросом, который включает частую трансляцию между числами для индексации массивов и их представлений в битовых векторах, и узким местом являются наши функции pack и unpack.

4 ответа

Решение

Вы можете сделать это с view а также unpackbits

Входные данные:

unpackbits(arange(2, dtype=uint16).view(uint8))

Выход:

[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0]

За a = arange(int(1e6), dtype=uint16) это довольно быстро на моей машине около 7 мс

%%timeit
unpackbits(a.view(uint8))

100 loops, best of 3: 7.03 ms per loop

Что касается порядка байтов, вам нужно посмотреть на http://docs.scipy.org/doc/numpy/user/basics.byteswapping.html и применить там предложения в зависимости от ваших потребностей.

Это решение, которое я использую:

def unpackbits(x,num_bits):
  xshape = list(x.shape)
  x = x.reshape([-1,1])
  to_and = 2**np.arange(num_bits).reshape([1,num_bits])
  return (x & to_and).astype(bool).astype(int).reshape(xshape + [num_bits])

Это работает с любым измерением ndarray и может распаковать сколько угодно битов. Это также не включает никаких петель.

Я также не нашел никакой функции для этого, но, возможно, использование встроенного в Python struct.unpack может помочь сделать пользовательскую функцию быстрее, чем сдвигать и увеличивать длину uint (обратите внимание, что я использую uint64).

>>> import struct
>>> N = np.uint64(2 + 2**10 + 2**18 + 2**26)
>>> struct.unpack('>BBBBBBBB', N)
(2, 4, 4, 4, 0, 0, 0, 0)

Идея состоит в том, чтобы преобразовать их в uint8, использовать unpackbits, объединить результат. Или, в зависимости от вашего приложения, может быть удобнее использовать структурированные массивы.

Есть также встроенная функция bin(), которая выдает строку из 0 и 1, но я не уверен, насколько она быстра и требует постобработки.

Это работает для произвольных массивов произвольного uint (то есть также для многомерных массивов, а также для чисел, превышающих максимальное значение uint8).

Он циклически повторяется по количеству битов, а не по количеству элементов массива, поэтому он достаточно быстрый.

def my_ManyParallel_uint2bits(in_intAr,Nbits):
    ''' convert (numpyarray of uint => array of Nbits bits) for many bits in parallel'''
    inSize_T= in_intAr.shape
    in_intAr_flat=in_intAr.flatten()
    out_NbitAr= numpy.zeros((len(in_intAr_flat),Nbits))
    for iBits in xrange(Nbits):
        out_NbitAr[:,iBits]= (in_intAr_flat>>iBits)&1
    out_NbitAr= out_NbitAr.reshape(inSize_T+(Nbits,))
    return out_NbitAr  

A=numpy.arange(256,261).astype('uint16')
# array([256, 257, 258, 259, 260], dtype=uint16)
B=my_ManyParallel_uint2bits(A,16).astype('uint16')
# array([[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
#       [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
#       [0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
#       [1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
#       [0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]], dtype=uint16)
Другие вопросы по тегам