4

我正在寻找更紧凑的方式来存储布尔值。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,但会在纯 numpy 中得到它。

4

2 回答 2

5

无需将布尔数组转换为本机intdtype(在 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开始,现在应该直接在布尔数组上工作(numpy v1.10.0 和更高版本)。

于 2015-12-29T16:54:51.310 回答
4

就在昨天,我向一位新手回答了一个关于如何在 Python 中处理位的问题——与 C++ 相比。在警告不会提高速度后,我使用内部 Python 的 bytearray 对象勾勒出一个幼稚的“bitarray”。

这绝不是快 - 但如果您不再对数组位进行操作,而只想要输出,那么它可能就足够了 - 因为您可以完全控制 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))

此代码最初发布在此处:Python 中是否存在类似于 C++ 中的 std::bitset 的内置位集?

于 2015-12-29T12:59:39.563 回答