将 numpy 数组转储为字符串的最快方法



我需要组织一个包含命名数据块的数据文件。数据是 NUMPY 数组。但我不想使用 numpy.save 或 numpy.savez 函数,因为在某些情况下,数据必须通过管道或其他接口在服务器上发送。所以我想将 numpy 数组转储到内存中,压缩它,然后将其发送到服务器中。

我尝试过简单的泡菜,像这样:

try:
import cPickle as pkl
except:
import pickle as pkl
import ziplib
import numpy as np
def send_to_db(data, compress=5):
send( zlib.compress(pkl.dumps(data),compress) )

.. 但这是一个极其缓慢的过程。

即使压缩级别为 0(无压缩),该过程也非常缓慢,并且只是因为酸洗。

有没有办法在没有泡菜的情况下将 numpy 数组转储到字符串中?我知道 numpy 允许获取缓冲区 numpy.getbuffer,但对我来说并不明显,如何使用这个转储的缓冲区来获取数组。

你绝对应该使用numpy.save,你仍然可以在内存中执行此操作:

>>> import io
>>> import numpy as np
>>> import zlib
>>> f = io.BytesIO()
>>> arr = np.random.rand(100, 100)
>>> np.save(f, arr)
>>> compressed = zlib.compress(f.getbuffer())

要解压,请逆转该过程:

>>> np.load(io.BytesIO(zlib.decompress(compressed)))
array([[ 0.80881898,  0.50553303,  0.03859795, ...,  0.05850996,
0.9174782 ,  0.48671767],
[ 0.79715979,  0.81465744,  0.93529834, ...,  0.53577085,
0.59098735,  0.22716425],
[ 0.49570713,  0.09599001,  0.74023709, ...,  0.85172897,
0.05066641,  0.10364143],
...,
[ 0.89720137,  0.60616688,  0.62966729, ...,  0.6206728 ,
0.96160519,  0.69746633],
[ 0.59276237,  0.71586014,  0.35959289, ...,  0.46977027,
0.46586237,  0.10949621],
[ 0.8075795 ,  0.70107856,  0.81389246, ...,  0.92068768,
0.38013495,  0.21489793]])
>>>

如您所见,这与我们之前保存的内容相匹配:

>>> arr
array([[ 0.80881898,  0.50553303,  0.03859795, ...,  0.05850996,
0.9174782 ,  0.48671767],
[ 0.79715979,  0.81465744,  0.93529834, ...,  0.53577085,
0.59098735,  0.22716425],
[ 0.49570713,  0.09599001,  0.74023709, ...,  0.85172897,
0.05066641,  0.10364143],
...,
[ 0.89720137,  0.60616688,  0.62966729, ...,  0.6206728 ,
0.96160519,  0.69746633],
[ 0.59276237,  0.71586014,  0.35959289, ...,  0.46977027,
0.46586237,  0.10949621],
[ 0.8075795 ,  0.70107856,  0.81389246, ...,  0.92068768,
0.38013495,  0.21489793]])
>>>

默认的pickle方法提供纯ASCII输出。要获得(更多)更好的性能,请使用可用的最新版本。版本 2 及更高版本是二进制的,如果没记错的话,允许 numpy 数组将其缓冲区直接转储到流中,而无需其他操作。

要选择要使用的版本,请在酸洗时添加可选参数(无需在酸洗时指定),例如pkl.dumps(data, 2)。 若要选择可能的最新版本,请使用pkl.dumps(data, -1)

请注意,如果使用不同的 python 版本,则需要指定支持的最低版本。 有关不同版本的详细信息,请参阅 Pickle 文档

根据我的基准测试,有一种方法tobytes比其他替代方案更快。

带着一粒盐,因为我的一些实验可能是误导的或明显错误的,但这是一种将 numpy 数组转储到字符串中的方法。

请记住,您需要有一些带外的其他数据,主要是数组的数据类型及其形状。这可能是一个交易破坏者,也可能不是相关的。通过调用.fromstring(..., dtype=...).reshape(...)很容易恢复原始形状。

>编辑:一个可能不完整的例子
##############
# Generation #
##############
import numpy as np
arr = np.random.randint(1, 7, (4,6))
arr_dtype = arr.dtype.str
arr_shape = arr.shape
arr_data = arr.tobytes()
# Now send / store arr_dtype, arr_shape, arr_data, where:
# arr_dtype is string
# arr_shape is tuple of integers
# arr_data is bytes
############
# Recovery #
############
arr = np.frombuffer(arr_data, dtype=arr_dtype).reshape(arr_shape)

我不考虑列/行排序,因为我知道 numpy 支持这方面的事情,但我从未使用过它。如果你想支持/需要以特定的方式排列内存 - 关于多维数组的行/列 - 你可能需要在某个时候考虑到这一点。

另外:frombuffer不复制缓冲区数据,它会创建 numpy 结构作为视图(也许不完全是这样,但你知道我的意思)。如果这是不希望的行为,您可以使用 fromstring(已弃用,但似乎适用于 1.19)或使用frombuffer后跟一个np.copy

最新更新