Как извлечь биты из больших числовых типов данных 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)