加快将许多 JSON 文件转换为 Pandas 数据帧的 for 循环

Speed up a for-loop converting many JSON files to a Pandas dataframe

我有以下功能:

def json_to_pickle(json_path=REVIEWS_JSON_DIR,
                   pickle_path=REVIEWS_PICKLE_DIR,
                   force_update=False):
    '''Create a pickled dataframe from selected JSON files.'''

    current_date = datetime.today().strftime("%Y%m%d")
    filename_safe_path = json_path.replace("/", "_")

    # Get a list of all JSON files from a given directory
    in_paths = Path(json_path).glob("**/*.json")
    in_paths_list = []
    for path in in_paths:  # Convert generator to a simple list
        in_paths_list.append(path)

    out_path = (f"{pickle_path}"
                f"/{current_date}"
                f"_{filename_safe_path}"
                f"_reviews.pkl")

    if exists(out_path) == False or force_update == True:
        pprint("Creating a new pickle file from raw JSON files")
        df = pd.DataFrame()
        for path in tqdm(in_paths_list):
            with open(path, "r") as file:
                normalized_json_df = pd.json_normalize(json.load(file))
                df = pd.concat([df, normalized_json_df])
        df.to_pickle(out_path, compression="infer")
    else:
        pprint(f"Using existing pickle file ({out_path})")

    return out_path

除非 pickle 文件已经存在,否则它会找到给定目录(包括所有子目录)中的所有 JSON 文件,对它们进行规范化,将它们连接到数据帧,并将数据帧作为 pickle 保存到磁盘。处理 240.255 JSON 个文件需要 54 分钟。

根据 tqdmfor 循环平均每秒迭代 73.51 次(运行 在具有 10 个内核的 M1 MacBook Pro 上),但它似乎随着时间的推移而变慢.据推测,随着数据框变大。它以每秒约 1668.44 次迭代开始。

所有 JSON 文件都具有相同的结构,但可能缺少几个字段。大小在 500 字节到 2 KB 之间变化。这里是 the JSON spec from Google's documentation.

我能做些什么来加快这个 for 循环?

编辑:

这就是我根据所选答案更改 for 循环的方式:

data_frames = []
for path in tqdm(in_paths_list):
    with open(path, "r") as file:
        data_frames.append(pd.json_normalize(json.load(file)))
pd.concat(data_frames).to_pickle(out_path, compression="infer")

现在它在 1 分 29 秒内完成。相当大的进步!

不是加载每个文件并将其附加到更大的临时数据帧,而是将所有文件加载到数据帧中并在单个操作中连接它们。

当前代码加载与文件对应的 N 个数据帧 并且 使用完全相同的数据创建 N-1 个更大的数据帧。

我使用此代码将文件夹中的所有 Excel 文件加载到单个数据框中:

root = rf"C:\path\to\folder"
matches=Path(root).glob("22*.xls*")
files=tqdm((pd.read_excel(file,skipfooter=1) 
            for file in matches 
            if not file.name.startswith("~")))
df=pd.concat(files)

在问题的情况下你可以使用:

def load_json(path):
    with open(path, "r") as file:
        df = pd.json_normalize(json.load(file))
        return df

in_paths = Path(json_path).glob("**/*.json")
files=tqdm((load_json(file) for file in in_paths))