【问题标题】:Pandas : saving Series of dictionaries to diskPandas:将字典系列保存到磁盘
【发布时间】:2016-12-01 23:17:37
【问题描述】:

我有一个 python pandas 系列字典:

id           dicts
1            {'5': 1, '8': 20, '1800': 2}
2            {'2': 2, '8': 1, '1000': 25, '1651': 1}
...          ...
...          ...
...          ...
20000000     {'2': 1, '10': 20}

字典中的(key, value)表示('feature', count)。存在大约 2000 个独特的功能。

该系列在 pandas 中的内存使用量约为 500MB。 将此对象写入磁盘的最佳方法是什么(理想情况下磁盘空间使用率低,并且写入速度快,之后读取速度快)?

考虑的选项(并尝试了前 2 个):
- to_csv(但将字典视为字符串,因此之后转换回字典非常慢)
- cPickle(但在执行期间内存不足)
- 转换为 scipy 稀疏矩阵结构

【问题讨论】:

  • 关于磁盘空间,to_csv() 确实有一个压缩参数,例如,to_csv(..., compression = 'gzip')。更一般地说,您可能想要查看的工具是Blaze。您可以将 pandas 数据结构转换为 blaze ds 并使用 blaze 的 pandas-like API 对其进行操作。主要优点是 blaze 不会像 pandas 那样将所有数据加载到内存中。
  • 如果您在Series 上使用to_pickle 方法,您是否仍然会耗尽内存?一般来说,在 pandas 中将字典存储在行中并不是很好 - 将它们保存在内置列表中基本上没有什么不同 - 这似乎是某种稀疏数组的情况?
  • @InNoam csv 的问题是之后恢复字典(使用 eval())很慢并且在我的情况下内存不足。将调查 Blaze!
  • 为什么要将字典放在一个系列中?
  • @Darn。那么使用稀疏结构可能会更好。

标签: python pandas dictionary scipy sparse-matrix


【解决方案1】:

我很好奇你的Series 怎么只占用 500MB。如果您使用.memory_usage 方法,这将只返回每个 python 对象引用使用的总内存,这是您的 Series 存储的所有内存。这不考虑字典的实际内存。粗略计算 20,000,000 * 288 字节 = 5.76GB 应该是你的内存使用量。 288 字节是每个字典所需内存的保守估计。

转换为稀疏矩阵

无论如何,请尝试以下方法将您的数据转换为稀疏矩阵表示:

import numpy as np, pandas as pd
from sklearn.feature_extraction import DictVectorizer
from scipy.sparse import csr_matrix
import pickle

我会使用ints 而不是字符串作为键,因为这将在以后保持正确的顺序。所以,假设你的系列名为dict_series

dict_series = dict_series.apply(lambda d: {int(k):d[k] for k in d}

这可能会占用大量内存,您最好从一开始就使用ints 作为键创建Seriesdicts。或者干脆你可以跳过这一步。现在,构建你的稀疏矩阵:

dv = DictVectorizer(dtype=np.int32)
sparse = dv.fit_transform(dict_series)

保存到磁盘

现在,基本上,您的稀疏矩阵可以从 3 个字段重构:sparse.datasparse.indicessparse.indptr,以及可选的sparse.shape。保存加载数组sparse.datasparse.indicessparse.indptr 的最快和最节省内存的方法是使用 np.ndarray tofile 方法,它将数组保存为原始字节。来自documentation

这是一个方便快速存储数组数据的功能。 关于字节顺序和精度的信息丢失了,所以这种方法不是 用于归档数据或传输数据的文件的理想选择 不同字节序的机器之间。

因此,此方法会丢失任何 dtype 信息和字节序。前一个问题可以简单地通过事先记下数据类型来解决,无论如何你都会使用 np.int32 。如果您在本地工作,后一个问题不是问题,但如果可移植性很重要,您将需要研究存储信息的替代方法。

# to save
sparse.data.tofile('data.dat')
sparse.indices.tofile('indices.dat')
sparse.indptr.tofile('indptr.dat')
# don't forget your dict vectorizer!
with open('dv.pickle', 'wb') as f:
    pickle.dump(dv,f) # pickle your dv to be able to recover your original data!

恢复一切:

with open('dv.pickle', 'rb') as f:
    dv = pickle.load(f)

sparse = csr_matrix((np.fromfile('data.dat', dtype = np.int32),
                     np.fromfile('indices.dat', dtype = np.int32),
                     np.fromfile('indptr.dat', dtype = np.int32))

original = pd.Series(dv.inverse_transform(sparse))

【讨论】:

  • 优秀的答案。确实,我错误地使用了.memory_usage 方法。您的答案允许将整个对象存储在不到 700MB 的磁盘上。 inverse_transform 方法对我来说非常慢,所以我决定直接在 csr_matrix 上进行所需的操作(使用 DictVectorizer 的 .vocabulary_ 属性来跟踪特征和列之间的映射)
  • @SLeon 很高兴我能帮上忙。我很好奇,原来的转换需要很长时间吗?我只用玩具示例尝试过这个。
  • fit_transform 运行得相当快,inverse_transform 运行得非常慢。
猜你喜欢
  • 2014-01-21
  • 1970-01-01
  • 2012-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-26
  • 2013-01-17
相关资源
最近更新 更多