打包布尔数组需要通过 int (numpy 1.8.2)

packing boolean array needs go throught int (numpy 1.8.2)

我正在寻找更紧凑的布尔值存储方式。 numpy 内部需要 8 位来存储一个布尔值,但 np.packbits 允许打包 他们,太棒了。

问题是要打包一个 4e6 字节 数组 32e6 字节 布尔数组我们需要先花费 256e6字节将boolean数组转换为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 跟踪器中打开了一个一年前的问题(Cf. https://github.com/numpy/numpy/issues/5377)

有人 solution/better 解决方法吗?

当我们尝试以正确的方式进行时的回溯:

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,但会在纯 numpy 中得到它。

就在昨天,我回答了一个新手关于如何处理 Python 中的位的问题 - 与 C++ 相比。在警告不会有速度提升之后,我 sketched-up 一个天真的 "bitarray" 使用内部 Python 的 bytearray 对象。

这绝对不是很快 - 但如果您不再对数组位进行操作,而只是想要输出,也许它就足够了 - 因为您可以完全控制 Python 代码转换。否则,您可以尝试仅提示静态类型和 运行 与 Cython 相同的代码,并且您可能希望使用 dtype=int8 的 np 数组而不是 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))

此代码最初发布于此:

无需将您的布尔数组转换为本机 int dtype(在 x86_64 上将是 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 从一年多前的 this commit 开始(numpy v1.10.0 和更新版本)现在应该可以直接处理布尔数组。