关于python:如何将字典和数组保存在同一个存档中(使用numpy.savez)

How to save dictionaries and arrays in the same archive (with numpy.savez)

第一个问题。我尽量简明扼要。

我正在为机器学习应用程序生成包含功能信息的多个数组。由于数组的维数不相等,所以我将它们存储在字典中而不是数组中。有两种不同的功能,所以我使用两种不同的字典。

我还生成了与特性匹配的标签。这些标签存储在数组中。此外,还有包含用于运行脚本和时间戳的确切参数的字符串。

总之,它看起来像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
import numpy as np    

feature1 = {}
feature2 = {}
label1 = np.array([])
label2 = np.array([])
docString = 'Commands passed to the script were...'

# features look like this:
feature1 = {'case 1': np.array([1, 2, 3, ...]),
            'case 2': np.array([2, 1, 3, ...]),
            'case 3': np.array([2, 3, 1, ...]),
            and so on... }

现在我的目标是:

1
2
3
4
5
6
np.savez(outputFile,
         saveFeature1 = feature1,
         saveFeature2 = feature2,
         saveLabel1 = label1,
         saveLabel2 = label2,
         saveString = docString)

这看起来是可行的(即这样的文件保存时不会引发错误,可以再次加载)。但是,当我尝试再次从文件加载功能时,例如:

1
2
3
loadedArchive = np.load(outFile)
loadedFeature1 = loadedArchive['saveFeature1']
loadedString = loadedArchive['saveString']

然后,我得到了一个麻木的形状数组(0),而不是一个字典,我不知道如何访问内容:

1
2
3
4
5
In []: loadedFeature1
Out[]:
       array({'case 1': array([1, 2, 3, ...]),
              'case 2': array([2, 3, 1, ...]),
              ..., }, dtype=object)

字符串也会成为数组并获得奇怪的数据类型:

1
2
In []: loadedString.dtype
Out[]: dtype('|S20')

所以简而言之,我假设这不是正确的做法。但是,我不希望将所有变量都放在一个大字典中,因为我将在另一个进程中检索它们,并且希望在不担心字符串比较的情况下循环dictionary.keys()。

任何想法都非常感谢。谢谢


正如@fraxel已经建议的那样,在这种情况下使用pickle是更好的选择。把你的东西放在里面就行了。

但是,请确保将pickle与二进制协议一起使用。默认情况下,它的格式效率较低,如果数组很大,则会导致内存使用过度和文件过大。

1
2
3
4
5
6
7
8
9
saved_data = dict(outputFile,
                  saveFeature1 = feature1,
                  saveFeature2 = feature2,
                  saveLabel1 = label1,
                  saveLabel2 = label2,
                  saveString = docString)

with open('test.dat', 'wb') as outfile:
    pickle.dump(saved_data, outfile, protocol=pickle.HIGHEST_PROTOCOL)

已经说过了,为了便于说明,让我们更详细地看一下正在发生的事情。

numpy.savez希望每个项都是一个数组。实际上,它对你传递的所有信息都称为np.asarray

如果将dict转换为数组,将得到一个对象数组。例如。

1
2
3
4
import numpy as np

test = {'a':np.arange(10), 'b':np.arange(20)}
testarr = np.asarray(test)

同样,如果用字符串创建数组,则会得到一个字符串数组:

1
2
3
4
In [1]: np.asarray('abc')
Out[1]:
array('abc',
      dtype='|S3')

但是,由于处理对象数组的方式有点奇怪,如果传入一个不是元组、列表或数组的单个对象(在本例中是dict),则会得到一个0维对象数组。

这意味着您不能直接索引它。实际上,执行testarr[0]将提高IndexError。数据仍然存在,但您需要先添加一个维度,因此您必须执行yourdictionary = testarr.reshape(-1)[0]

如果这一切看起来都很笨拙,那是因为它是。对象数组本质上总是错误的答案。(虽然可以说,asarray应该把ndmin=1传给array,这可以解决这个特殊问题,但可能会破坏其他事情。)

savez用于存储数组,而不是存储任意对象。因为它的工作方式,它可以存储完全任意的对象,但不应该这样使用。

不过,如果您确实想使用它,一个快速的解决方法是:

1
2
3
4
5
6
np.savez(outputFile,
         saveFeature1 = [feature1],
         saveFeature2 = [feature2],
         saveLabel1 = [label1],
         saveLabel2 = [label2],
         saveString = docString)

然后你就可以用

1
2
3
loadedArchive = np.load(outFile)
loadedFeature1 = loadedArchive['saveFeature1'][0]
loadedString = str(loadedArchive['saveString'])

然而,这显然比只用泡菜要笨重得多。在保存数组时使用numpy.savez。在本例中,您保存的是嵌套数据结构,而不是数组。


如果需要以结构化方式保存数据,则应考虑使用HDF5文件格式(http://www.hdfgroup.org/hdf5/)。它非常灵活、易于使用、高效,其他软件可能已经支持它(hdfview、mathematica、matlab、origin…)。有一个名为h5py的简单python绑定。

您可以将数据集存储在类似文件系统的结构中,并为每个数据集(如字典)定义属性。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import numpy as np
import h5py

# some data
table1 = np.array([(1,1), (2,2), (3,3)], dtype=[('x', float), ('y', float)])
table2 = np.ones(shape=(3,3))

# save to data to file
h5file = h5py.File("test.h5","w")
h5file.create_dataset("Table1", data=table1)
h5file.create_dataset("Table2", data=table2, compression=True)
# add attributes
h5file["Table2"].attrs["attribute1"] ="some info"
h5file["Table2"].attrs["attribute2"] = 42
h5file.close()

读取数据也很简单,如果需要,您甚至可以从大文件中加载几个元素:

1
2
3
4
5
6
7
h5file = h5py.File("test.h5","r")
# read from file (numpy-like behavior)
print h5file["Table1"]["x"][:2]
# read everything into memory (real numpy array)
print np.array(h5file["Table2"])
# read attributes
print h5file["Table2"].attrs["attribute1"]

更多的功能和可能性可以在文档和网站上找到(快速入门指南可能会感兴趣)。


将所有变量放入一个对象中,然后使用pickle。这是存储状态信息的更好方法。