【问题标题】:Optimization of json.load() to reduce in-memory usage and time in Python优化 json.load() 以减少 Python 中的内存使用和时间
【发布时间】:2020-11-04 09:55:33
【问题描述】:

我有 10K 个文件夹,每个文件夹有 200 个 JSON 格式文件中的 200 条记录。 尝试将所有记录编译成一个数据框,然后最终编译成 CSV(欢迎其他格式建议)

这是我的工作解决方案,仅用于数据框构建过程大约需要 8.3 小时。 (不转换成 CSV)

%%time
finalDf = pd.DataFrame()
rootdir ='/path/foldername'
all_files = Path(rootdir).rglob('*.json')
for filename in all_files:
    with open(filename, 'r+') as f:
        data = json.load(f)
        df = pd.json_normalize(data).drop(columns=[A]).rename(columns={'B': 'Date'})
        finalDf = finalDf.append(df, ignore_index=True)

任何优化这个并缩短时间的建议。

【问题讨论】:

  • 我发现了类似的帖子。试试这个怎么样? stackoverflow.com/questions/27407430/…
  • 是的,将尝试使用 Ultra JSON。但不是很乐观。
  • 你在哪个平台上?这需要在 Windows 上运行吗?
  • 目标只是编写 CSV 还是要先处理完整的 DF?
  • 还有其他更快的序列化例如feather、parquet、hdf文件系统。根据您希望长期处理数据的方式,像 mongdb 这样的 nosql 解决方案甚至是好的 ole sql 都是不错的选择。导入这些 json 后,您就拥有了丰富的查询功能,如果随着时间的推移会随着更多 json 的增长而增长,请继续导入更多的数据。

标签: python json dataframe optimization


【解决方案1】:

一个重要的问题来自O(n^2) 中执行的数据帧附加。事实上,对于每个新处理的 json 文件,finalDf 都会被完全复制!

这是在O(n)时间运行的修改版本:

%%time
finalDf = pd.DataFrame()
rootdir ='/path/foldername'
all_files = Path(rootdir).rglob('*.json')
allDf = []
for filename in all_files:
    with open(filename, 'r+') as f:
        data = json.load(f)
        df = pd.json_normalize(data).drop(columns=[A]).rename(columns={'B': 'Date'})
        allDf.append(df)
finalDf = pd.concat(allDf, ignore_index=True)

如果这还不够,可以使用 multiprocessing 模块并行执行 json 解析和 pandas 后处理。

【讨论】:

  • 这当然有帮助。您能否详细说明 json 解析和 pandas 后处理可以使用多处理模块并行执行。任何实现/代码?
【解决方案2】:

如果目标只是写入 CSV,您可以使用多处理来并行化读取/反序列化/序列化步骤并使用锁定控制文件写入。使用 CSV,您不必将整个内容保存在内存中,只需将每个 DF 附加为其生成。如果您使用的是硬盘驱动器而不是 ssd,如果 CSV 位于不同的驱动器(不仅仅是分区)上,您也可能会得到提升。

import multiprocessing as mp
import json
import pandas as pd
from pathlib import Path
import os

def update_csv(args):
    lock, infile, outfile = args
    with open(infile) as f:
        data = json.load(f)
    df = pd.json_normalize(data).drop(columns=[A]).rename(columns={'B': 'Date'})
    with lock:
        with open(outfile, mode="a", newline="") as f:
            df.to_csv(f)

if __name__ == "__main__":
    rootdir ='/path/foldername'
    outfile = 'myoutput.csv'
    if os.path.exists(outfile):
        os.remove(outfile)
    all_files = [str(p) for p in Path(rootdir).rglob('*.json')]
    mgr = mp.Manager()
    lock = mgr.Lock()
    # pool sizing is a bit of a guess....
    with mp.Pool(mp.cpu_count()-1) as pool:
        result = pool.map(update_csv, [(lock, fn, outfile) for fn in all_files],
            chunksize=1)

就我个人而言,我更喜欢对这类事情使用文件系统锁定文件,但这取决于平台,并且您可能在某些文件系统类型上遇到问题(例如已安装的远程文件系统)。 multiprocessing.Manager 使用后台同步 - 我不确定它的 Lock 是否有效。但这里已经足够好了....这只是成本的一​​小部分。

【讨论】:

  • 在最后一行 result = pool.map.. 传递这个“args=[(lock, outfile, fn) for fn in all_files]”给出了 TypeError: map( ) 得到了一个意外的关键字参数“args”。传递这个“[(lock, outfile, fn) for fn in all_files]”会给出 RuntimeError: Lock objects should only be shared between processes through inheritance
  • 好的,有一些问题......我已经发布了更新。
  • 这个序列 args=[(lock, outfile, fn) 是否正确,因为我遇到了同样的错误。甚至更正,如果 _name_ == "_main_": 并且 存在 错别字
  • 好的,现在可以运行了,至少要处理0个json文件。
  • 还有很多其他选项,但这取决于您以后要如何使用数据。您可以将 json 放入 SQL 数据库、非 SQL 数据库(如 mongodb 或 couchdb)放入 HDFS 中的表中,使用 apache 箭头。这些都有在读取数据时查询和过滤数据的方法,这样您就不必将整个数据集拉入内存。如果你真的想把整个数据集放在内存中,那么像 parquet 和 feather 这样的格式对于读入 pandas 是很有效的。
猜你喜欢
  • 1970-01-01
  • 2018-03-30
  • 1970-01-01
  • 1970-01-01
  • 2011-01-22
  • 2017-02-15
  • 1970-01-01
  • 2015-03-01
  • 2012-06-28
相关资源
最近更新 更多