Pandas : 将一系列词典保存到磁盘

Pandas : saving Series of dictionaries to disk

我有一本pythonpandas系列词典:

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。 将此对象写入磁盘的最佳方法是什么(理想情况下磁盘使用率低 space,写入速度快,之后读回速度快)?

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

我很好奇你的 Series 为什么只占用 500MB。如果您使用 .memory_usage 方法,这只会 return 每个 python 对象引用使用的总内存,这是您的系列存储的全部内容。这并没有考虑字典的实际内存。粗略计算 20,000,000 * 288 bytes = 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}

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

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:

This is a convenience function for quick storage of array data. Information on endianness and precision is lost, so this method is not a good choice for files intended to archive data or transport data between machines with different endianness.

所以这个方法会丢失任何 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))