使用Python频繁更新数值实验中的存储数据

0 投票
2 回答
2114 浏览
提问于 2025-04-18 11:21

我正在进行一个需要多次迭代的数值实验。在每次迭代之后,我想把数据存储在一个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()

问题:

  1. 在这种情况下,使用shelve会更好吗?或者有没有其他我不知道的Python库?
  2. 我使用数据字典是因为这样编码更简单,而且如果我在做更多实验时需要更改东西,它也更灵活。使用预先分配的数组会有很大的优势吗?
  3. 打开和关闭文件会影响运行时间吗?我这样做是为了能检查进度,除了我设置的文本日志之外。

谢谢大家的帮助!

2 个回答

1

Shelve 可能不是一个好的选择,不过……

你可以试试 kleptojoblib。这两个工具都很擅长缓存结果,并且可以使用高效的存储格式。

这两个工具都可以把你的结果保存到磁盘上的文件或文件夹里。它们还可以利用 numpy 的内部存储格式,或者在保存时进行压缩……如果你愿意的话,也可以保存到内存映射文件中。

如果你使用 klepto,它会把字典的键当作文件名,把值当作文件内容。使用 klepto 时,你还可以选择使用 picklejson 或其他存储格式。

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() 将数据保存到磁盘,或者设置一个内存限制,当达到这个限制时,就会自动保存到磁盘。此外,你还可以选择缓存策略(比如 lrulfu),来决定哪些键会被清除出内存并保存到磁盘。

在这里获取 kleptohttps://github.com/uqfoundation

或者在这里获取 joblibhttps://github.com/joblib/joblib

如果你重构代码,可能会找到一种方法来利用预分配的数组。不过,这可能取决于你代码的运行情况。

打开和关闭文件会影响运行时间吗?会的。如果你使用 klepto,你可以设置何时将数据保存到磁盘的粒度。这样你就可以在速度和中间结果存储之间做出权衡。

1
  1. 假设你在做数字实验时使用的是 numpy,我建议你用 numpy.savez,而不是 pickle
  2. 保持简单,只有在你觉得脚本运行时间太长的时候再进行优化。
  3. 打开和关闭文件确实会影响运行时间,但有备份总是更好的。

我会用 collections.defaultdict(list) 来代替普通的 dictsetdefault

撰写回答