Упаковка логического массива должна проходить через int (numpy 1.8.2)
Я ищу более компактный способ хранения логических значений. Numpy внутренне нужно 8 бит для хранения одного логического, но np.packbits
позволить упаковать их, это очень круто.
Проблема состоит в том, что для упаковки в массив 4e6 байтов массива 32e6 байтов логического нам нужно сначала потратить 256e6 байтов, чтобы преобразовать логический массив в массив int!
In [1]: db_bool = np.array(np.random.randint(2, size=(int(2e6), 16)), dtype=bool)
In [2]: db_int = np.asarray(db_bool, dtype=int)
In [3]: db_packed = np.packbits(db_int, axis=0)
In [4]: db.nbytes, db_int.nbytes, db_packed.nbytes
Out[5]: (32000000, 256000000, 4000000)
Об этом в трекере Numpy было открыто годовалое издание (см. https://github.com/numpy/numpy/issues/5377).
Есть ли у кого-нибудь решение / лучший обходной путь?
Трассировка, когда мы пытаемся сделать это правильно:
In [28]: db_pb = np.packbits(db_bool)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-28-3715e167166b> in <module>()
----> 1 db_pb = np.packbits(db_bool)
TypeError: Expected an input array of integer data type
In [29]:
PS: я попробую bitarray, но получу его в чистом виде.
2 ответа
Нет необходимости преобразовывать ваш логический массив в нативный int
dtype (который будет 64-битным на x86_64). Вы можете избежать копирования вашего логического массива, просмотрев его как np.uint8
, который также использует один байт на элемент:
packed = np.packbits(db_bool.view(np.uint8))
unpacked = np.unpackbits(packed)[:db_bool.size].reshape(db_bool.shape).view(np.bool)
print(np.all(db_bool == unpacked))
# True
Также, np.packbits
теперь должен работать непосредственно с логическими массивами, начиная с этого коммита более года назад (numpy v1.10.0 и новее).
Буквально вчера я ответил новичку на вопрос, как работать с битами в Python - по сравнению с C++. После предупреждения об отсутствии увеличения скорости я набросал наивный "битрейр", используя внутренне объекты Python bytearray.
Это ни в коем случае не быстро - но если вы больше не работаете с битами массива и просто хотите получить вывод, возможно, он достаточно хорош - поскольку у вас есть полный контроль в коде Python по поводу преобразования. В противном случае вы можете попробовать просто намекать на статические типы и запустить тот же код, что и в Cython, и, возможно, вы захотите использовать массив np с dtype = int8 вместо bytearray:
class BitArray(object):
def __init__(self, length):
self.values = bytearray(b"\x00" * (length // 8 + (1 if length % 8 else 0)))
self.length = length
def __setitem__(self, index, value):
value = int(bool(value)) << (7 - index % 8)
mask = 0xff ^ (7 - index % 8)
self.values[index // 8] &= mask
self.values[index // 8] |= value
def __getitem__(self, index):
mask = 1 << (7 - index % 8)
return bool(self.values[index // 8] & mask)
def __len__(self):
return self.length
def __repr__(self):
return "<{}>".format(", ".join("{:d}".format(value) for value in self))
Этот код был первоначально размещен здесь: есть ли встроенный набор битов в Python, который похож на std::bitset из C++?