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

Posted

技术标签:

【中文标题】如何将字典和数组保存在同一个存档中(使用 numpy.savez)【英文标题】:How to save dictionaries and arrays in the same archive (with numpy.savez) 【发布时间】:2012-04-21 23:17:59 【问题描述】:

这里的第一个问题。我会尽量简洁。

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

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

总而言之,它看起来像这样:

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... 

现在我的目标是这样做:

np.savez(outputFile, 
         saveFeature1 = feature1, 
         saveFeature2 = feature2, 
         saveLabel1 = label1, 
         saveLabel2 = label2,
         saveString = docString)

这似乎有效(即这样的文件被保存而没有抛出错误并且可以再次加载)。但是,当我尝试再次从文件中加载功能时:

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

然后,我没有得到字典,而是得到一个形状为 (0) 的 numpy 数组,我不知道如何访问其中的内容:

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

字符串也变成数组,得到一个奇怪的数据类型:

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

简而言之,我假设这不是正确完成的方式。但是,我不希望将所有变量放入一个大字典中,因为我将在另一个进程中检索它们,并且只想循环遍历 dictionary.keys() 而不必担心字符串比较。

非常感谢任何想法。 谢谢

【问题讨论】:

【参考方案1】:

正如@fraxel 已经建议的那样,在这种情况下使用 pickle 是一个更好的选择。只需将您的物品保存在 dict 中即可。

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

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 变成一个数组,你会得到一个对象数组。例如

import numpy as np

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

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

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 旨在存储数组,而不是任意对象。由于它的工作方式,它可以存储完全任意的对象,但不应该那样使用。

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

np.savez(outputFile, 
         saveFeature1 = [feature1], 
         saveFeature2 = [feature2], 
         saveLabel1 = [label1], 
         saveLabel2 = [label2],
         saveString = docString)

然后您可以使用

访问内容
loadedArchive = np.load(outFile)
loadedFeature1 = loadedArchive['saveFeature1'][0]
loadedString = str(loadedArchive['saveString'])

但是,这显然比仅使用泡菜要笨重得多。当你只是保存数组时使用numpy.savez。在这种情况下,您保存的是嵌套数据结构,而不是数组。

【讨论】:

我更喜欢通过testarr.flat[0] 访问() 形数组中的元素。对于好奇的读者,也可以使用 testarr[()] 中的空元组,但这会损害可读性。【参考方案2】:

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

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

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()

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

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"]

在文档和网站上可以找到更多功能和可能性(Quick Start Guide 可能会感兴趣)。

【讨论】:

【参考方案3】:

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

【讨论】:

谢谢。但这是否意味着,我每次都必须使用相同数量的变量?如果出于某种原因,我只保存一个字典,那么使用 pickle 加载此文件的每个脚本都会弄乱存储变量的顺序。

以上是关于如何将字典和数组保存在同一个存档中(使用 numpy.savez)的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Cloudkit 将字典(或任何其他复杂结构)保存到 iCloud 中?

如何使用 numpy.savez 将带有子数组的数组保存到单独的 .npy 文件中

如何使用 Java 将每个文件存储在 zip 存档中的数组中?

如何从字典输入中更改 UITableview 的数组内容?

在核心数据中如何保存字典或数组,如字符串数据? [关闭]

nump库的简单函数介绍