将字节数组转换回numpy数组

48

您可以使用.tobytes()函数将numpy数组转换为字节数组。

如何将它从这个字节数组解码回numpy数组呢? 我尝试了像下面这样的代码,其中i是形状为(28,28)的数组:

>> k=i.tobytes()

>> np.frombuffer(k)==i

False

也尝试过uint8。


你尝试过查看这个吗?https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.frombuffer.html - ritlew
它不是那样工作的。我尝试了这种方式,对于形状(28,28)的i:首先我尝试了:`>>k=i.tobytes()
np.frombuffer(k)==i False 也尝试了uint8,但仍然无法正常工作。`
- Gautham Santhosh
你正在使用MNIST吗? :) - Yauhen Yakimenka
是的,它在MNIST数据集之上。它被用作示例数据集。 - Gautham Santhosh
4个回答

59
你所做的有一些问题:
  1. frombuffer总是将输入解释为一维数组。这是文档的第一行。因此,你需要重新调整形状为(28, 28)

  2. 默认的 dtypefloat。所以如果你没序列化出浮点数,那么你就需要手动指定dtype(没有人能事先知道字节流代表什么:你必须说明它们代表什么)。

  3. 如果你想确保数组相等,那么你必须使用np.array_equal。使用==会进行逐个元素操作,并返回一个Numpy布尔数组(这可能不是你想要的)。

如何从这个字节数组解码回Numpy数组?

例如:

In [3]: i = np.arange(28*28).reshape(28, 28)

In [4]: k = i.tobytes()

In [5]: y = np.frombuffer(k, dtype=i.dtype)

In [6]: y.shape
Out[6]: (784,)

In [7]: np.array_equal(y.reshape(28, 28), i)
Out[7]: True

祝好运。


16
重要提示:np.frombuffer() 返回的对象是只读的。 - Brad Solomon

20

虽然您可以使用tobytes()方法,但它不是理想的方法,因为它不会存储numpy数组的形状信息。

在需要将其发送到另一个进程且没有有关形状信息的情况下,您将必须显式地发送形状信息。

更优雅的解决方案是使用np.save将其保存到BytesIO缓冲区中,并使用np.load进行恢复。在此过程中,您无需在任何地方特别存储形状信息,可以轻松地从字节值中恢复您的numpy数组。

示例:

>>> import numpy as np
>>> from io import BytesIO

>>> x = np.arange(28*28).reshape(28, 28)
>>> x.shape
(28, 28)

# save in to BytesIo buffer 
>>> np_bytes = BytesIO()
>>> np.save(np_bytes, x, allow_pickle=True)

# get bytes value
>>> np_bytes = np_bytes.getvalue()
>>> type(np_bytes)
<class 'bytes'>

# load from bytes into numpy array
>>> load_bytes = BytesIO(np_bytes)
>>> loaded_np = np.load(load_bytes, allow_pickle=True)

# shape is preserved
>>> loaded_np.shape
(28, 28)

# both arrays are equal without sending shape
>>> np.array_equal(x,loaded_np)
True

11
很不幸,对于大的numpy数组来说,这种方法非常非常慢。请参考相关的stackoverflow帖子:https://dev59.com/L1IG5IYBdhLWcg3w5GEu?noredirect=1#comment110277408_62352670 - David Parks

8

为了方便起见,这里提供了一个序列化/反序列化函数,实现了Saket Kumar的答案。

from io import BytesIO
import numpy as np

def array_to_bytes(x: np.ndarray) -> bytes:
    np_bytes = BytesIO()
    np.save(np_bytes, x, allow_pickle=True)
    return np_bytes.getvalue()


def bytes_to_array(b: bytes) -> np.ndarray:
    np_bytes = BytesIO(b)
    return np.load(np_bytes, allow_pickle=True)

# ----------
# quick test

def test():
    x = np.random.uniform(0, 155, (2, 3)).astype(np.float16)
    b = array_to_bytes(x)
    x1 = bytes_to_array(b)
    assert np.all(x == x1)


if __name__ == '__main__':
    test()


0
如果您只需要二进制数组,而不限于使用np.tobytes方法,您可以使用pickle.dumpspickle.loads
以下是一个示例:
import pickle
A = np.random.randint(0, 10, [2,2])
A_bytes=pickle.dumps(A, protocol=0)
A_restore=pickle.loads(A_bytes)

# test byte type and restored np mat
np.testing.assert_array_equal(A_restore, A)
assert type(A_bytes)==bytes


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接