python - 以最小尺寸保存numpy数组到文件
现在我有一个Python程序,它正在构建一个相当大的二维numpy数组,并使用numpy.savetxt将其保存为一个以制表符分隔的文本文件。这个numpy数组只包含浮点数。然后,我在一个单独的C++程序中每次读取文件的一行。
我想找到一种方法来完成这个任务,尽量少改动我的代码,同时减少我在两个程序之间传递的文件大小。
我发现可以使用numpy.savetxt将数据保存为压缩的.gz文件,而不是普通的文本文件。这样可以把文件大小从大约2MB减少到大约100kB。
有没有更好的方法呢?我是否可以将numpy数组以二进制格式写入文件,以节省空间?如果可以,我该怎么做才能让我的C++程序仍然能够读取它呢?
谢谢你的帮助。我非常感谢任何指导。
编辑:
我的数组中有很多零(大约70%的值都是0.0000),我不太确定如何利用这一点来生成一个小文件,让我的C++程序可以读取。
5 个回答
numpy.ndarray.tofile
和 numpy.fromfile
是在 Python 中进行直接的二进制输出和输入的好工具。而在 C++ 中,std::ostream::write
和 std::istream::read
则是用于二进制输出和输入的工具。
如果数据是从一台机器传输到另一台机器,你需要注意字节序的问题。
除非你确定不需要担心字节序等问题,否则最好使用 numpy.savez
,就像 @unutbu 的回答和 @jorgeca 的评论中提到的那样,具体可以参考这里的链接:numpy的tostring/fromstring --- 我需要指定什么来恢复数组。
如果保存后的文件大小仍然太大,可以考虑使用 zlib
(在Python中使用:import zlib
,在C++中也有类似的实现)。
另外一个选择是使用 hdf5
格式:虽然它不一定能减少文件在磁盘上的大小,但可以让保存和加载的速度更快(这个格式就是为了处理大数据数组而设计的)。对于 hdf5
,Python和C++都有相应的读写工具。
因为你有很多零,所以你可以只写出那些非零的元素,格式是(索引,数字)。
假设你有一个数组,里面只有少量的非零数字:
In [5]: a = np.zeros((10, 10))
In [6]: a
Out[6]:
array([[ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
In [7]: a[3,1] = 2.0
In [8]: a[7,4] = 17.0
In [9]: a[9,0] = 1.5
首先,找出那些有趣的数字和它们的位置:
In [11]: x, y = a.nonzero()
In [12]: zip(x,y)
Out[12]: [(3, 1), (7, 4), (9, 0)]
In [13]: nonzero = zip(x,y)
现在你只剩下少量的数据了。最简单的方法就是把它们写到一个文本文件里:
In [17]: with open('numbers.txt', 'w+') as outf:
....: for r, k in nonzero:
....: outf.write('{:d} {:d} {:g}\n'.format(r, k, a[r,k]))
....:
In [18]: cat numbers.txt
3 1 2
7 4 17
9 0 1.5
这样你也可以方便地查看这些数据。在你的C++程序中,你可以用 fscanf
来读取这些数据。
不过,你还可以通过使用 struct 来进一步减小文件的大小,写成二进制数据:
In [17]: import struct
In [19]: c = struct.Struct('=IId')
In [20]: with open('numbers.bin', 'w+') as outf:
....: for r, k in nonzero:
....: outf.write(c.pack(r, k, a[r,k]))
传给 Struct
构造函数的参数表示;使用本地的数据格式 '='。第一个和第二个数据元素是无符号整数 'I',第三个元素是一个双精度浮点数 'd'。
在你的C++程序中,最好把这些数据作为二进制数据读入一个打包的 struct
中。
编辑:答案已更新为二维数组的情况。