使用Python频繁更新数值实验中的存储数据
我正在进行一个需要多次迭代的数值实验。在每次迭代之后,我想把数据存储在一个pickle文件或者类似的文件里,以防程序超时或者数据结构出现问题。请问我该怎么做?以下是我的基本代码:
data_dict = {} # maybe a dictionary is not the best choice
for j in parameters: # j = (alpha, beta, gamma) and cycle through
for k in number_of_experiments: # lots of experiments (10^4)
file = open('storage.pkl', 'ab')
data = experiment() # experiment returns some numerical value
# experiment takes ~ 1 seconds, but increase
# as parameters scale
data_dict.setdefault(j, []).append(data)
pickle.dump(data_dict, file)
file.close()
问题:
- 在这种情况下,使用shelve会更好吗?或者有没有其他我不知道的Python库?
- 我使用数据字典是因为这样编码更简单,而且如果我在做更多实验时需要更改东西,它也更灵活。使用预先分配的数组会有很大的优势吗?
- 打开和关闭文件会影响运行时间吗?我这样做是为了能检查进度,除了我设置的文本日志之外。
谢谢大家的帮助!
2 个回答
Shelve 可能不是一个好的选择,不过……
你可以试试 klepto
或 joblib
。这两个工具都很擅长缓存结果,并且可以使用高效的存储格式。
这两个工具都可以把你的结果保存到磁盘上的文件或文件夹里。它们还可以利用 numpy
的内部存储格式,或者在保存时进行压缩……如果你愿意的话,也可以保存到内存映射文件中。
如果你使用 klepto
,它会把字典的键当作文件名,把值当作文件内容。使用 klepto
时,你还可以选择使用 pickle
、json
或其他存储格式。
Python 2.7.7 (default, Jun 2 2014, 01:33:50)
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import klepto
>>> data_dict = klepto.archives.dir_archive('storage', cached=False, serialized=True)
>>> import string
>>> import random
>>> for j in string.ascii_letters:
... for k in range(1000):
... data_dict.setdefault(j, []).append([int(10*random.random()) for i in range(3)])
...
>>>
这会创建一个名为 storage
的文件夹,里面包含了每个 data_dict
键对应的 pickle 文件。你可以使用 memmap
文件的关键词,也可以设置压缩级别。如果你选择 cached=False
,那么每次写入 data_dict
时,不会立即保存到文件,而是先写入内存……然后你可以随时使用 data_dict.dump()
将数据保存到磁盘,或者设置一个内存限制,当达到这个限制时,就会自动保存到磁盘。此外,你还可以选择缓存策略(比如 lru
或 lfu
),来决定哪些键会被清除出内存并保存到磁盘。
在这里获取 klepto
: https://github.com/uqfoundation
或者在这里获取 joblib
: https://github.com/joblib/joblib
如果你重构代码,可能会找到一种方法来利用预分配的数组。不过,这可能取决于你代码的运行情况。
打开和关闭文件会影响运行时间吗?会的。如果你使用 klepto
,你可以设置何时将数据保存到磁盘的粒度。这样你就可以在速度和中间结果存储之间做出权衡。
- 假设你在做数字实验时使用的是
numpy
,我建议你用 numpy.savez,而不是 pickle。 - 保持简单,只有在你觉得脚本运行时间太长的时候再进行优化。
- 打开和关闭文件确实会影响运行时间,但有备份总是更好的。
我会用 collections.defaultdict(list)
来代替普通的 dict
和 setdefault
。