大体积嵌套字典快速写入硬盘的最优实现方案咨询
快速存储大型词共现嵌套字典的实用方案
我完全懂你这种痛点——几GB级的词共现计数嵌套字典,用Pickle甚至Msgpack存起来居然比重新计算还慢,简直离谱。下面几个经过实践验证的方案,应该能帮你把读写速度拉上来:
1. 用HDF5结构化存储(h5py库)
HDF5天生适合处理大型分层数据,完美匹配你的嵌套字典结构。你可以把外层的字符串键作为HDF5的"组",内层的键值对存在对应组的数据集里,读写效率比序列化工具高得多,还支持分块加载,不用把整个字典塞进内存。
示例代码:
import h5py # 写入字典 with h5py.File("cooccur_counts.h5", "w") as f: for outer_key, inner_dict in large_dict.items(): group = f.create_group(outer_key) # 将内层字典的键和值转成数组存储 keys = list(inner_dict.keys()) values = list(inner_dict.values()) group.create_dataset("keys", data=keys, dtype=h5py.string_dtype()) group.create_dataset("counts", data=values, dtype="int64") # 读取重建字典 with h5py.File("cooccur_counts.h5", "r") as f: loaded_dict = {} for outer_key in f.keys(): group = f[outer_key] keys = group["keys"][()].tolist() counts = group["counts"][()].tolist() loaded_dict[outer_key] = dict(zip(keys, counts))
2. 转成列式存储格式(Parquet)
把嵌套字典展开成扁平的三列结构:外层词、共现词、计数,然后用Parquet存储。Parquet是专门为大数据设计的列式存储格式,压缩率极高,读写速度远超CSV和普通序列化工具,用pandas或pyarrow就能轻松处理。
示例代码:
import pandas as pd # 展开嵌套字典为列表 flat_data = [] for outer_word, cooccur_dict in large_dict.items(): for cooccur_word, count in cooccur_dict.items(): flat_data.append({"outer_word": outer_word, "cooccur_word": cooccur_word, "count": count}) # 转成DataFrame并存储为Parquet df = pd.DataFrame(flat_data) df.to_parquet("cooccur_counts.parquet", compression="snappy") # 读取重建字典 df_loaded = pd.read_parquet("cooccur_counts.parquet") loaded_dict = {} for _, row in df_loaded.iterrows(): outer_word = row["outer_word"] if outer_word not in loaded_dict: loaded_dict[outer_word] = {} loaded_dict[outer_word][row["cooccur_word"]] = row["count"]
提示:如果字典太大内存装不下,可以分批次展开和写入,不用一次性处理全部数据。
3. 用键值数据库(LevelDB/RocksDB)
如果你的数据量已经大到内存都扛不住,试试LevelDB或RocksDB这类磁盘键值数据库。可以把外层键和内层键拼接成复合键(比如"outer_word:cooccur_word"),直接存储计数值,读写都是O(1)级别的操作,而且支持增量更新,不用每次全量写入。
用plyvel操作LevelDB的示例:
import plyvel # 写入数据库 db = plyvel.DB("cooccur_db", create_if_missing=True) for outer_word, cooccur_dict in large_dict.items(): for cooccur_word, count in cooccur_dict.items(): key = f"{outer_word}:{cooccur_word}".encode("utf-8") value = str(count).encode("utf-8") db.put(key, value) db.close() # 读取重建字典 loaded_dict = {} db = plyvel.DB("cooccur_db", create_if_missing=False) for key, value in db: outer_word, cooccur_word = key.decode("utf-8").split(":", 1) count = int(value.decode("utf-8")) if outer_word not in loaded_dict: loaded_dict[outer_word] = {} loaded_dict[outer_word][cooccur_word] = count db.close()
4. 优化现有序列化方式(备选)
如果你还是想坚持用序列化工具,试试这两个小技巧:
- 用Pickle的最高协议版本:
pickle.dump(large_dict, file, protocol=pickle.HIGHEST_PROTOCOL),比默认协议快不少 - 用
joblib.dump代替Pickle,它对大型numpy数组和字典的序列化优化更好,速度也更快
内容的提问来源于stack exchange,提问作者Alexey Trofimov
相关产品推荐
相关产品推荐

