首页 > 解决方案 > 有效地将具有反向字典的文件读取到DataFrame

问题描述

我有一堆具有这种结构的 20MB 文本文件:

{'col-1': ['data-11', 'data-12'], 'col-2': [1, 2], 'col-n': [1, 2]}
{'col-1': ['data-21', 'data-22'], 'col-2': [1, 2], 'col-n': [1, 2]}
{'col-1': ['data-31', 'data-32', 'data-33'], 'col-2': [1, 2, 3], 'col-n': [1, 2, 3]}
...

每行中的许多记录行各不相同。

我想读为:

        col-1       col-2   col-n
0       data-11     1       1
1       data-12     2       2
2       data-21     1       1
3       data-22     2       2
4       data-31     1       1
5       data-32     2       2
6       data-33     3       3

我已经准备了这样的代码,它工作得很好:

import ast
import pandas as pd
import itertools

def _eval(line: str):
    ''' Turns: {'col-1': ['data-11', 'data-12'], 'col-2': [1, 2], 'col-n': [1, 2]}
        Into: [{'col-1': 'data-11', 'col-2': 1, 'col-n': 1}, {'col-1': 'data-12', 'col-2': 2, 'col-n': 2}]
    '''
    try:
        v = ast.literal_eval(line.rstrip()) # Reads line to dict
        v = [{k: v[k][i] for k in v} for i in range(len(v['col-n']))] # Revers dict, by splitting them and rejoin
        return v
    
    # In case of file structure error
    except Exception as e:
        print('err', e)
        return []

def read_structure(fp):
    with open(fp) as fh:
        return pd.DataFrame(itertools.chain(*list(map(_eval, fh.readlines()))))

但必须有更好的方法。使用 itertools 和 map 我已经不到 5 秒了。

我想要:

A)优化循环时刻v = [{k: v[k][i] for k in v} for i in range(len(v['col-n']))]

B)知道是否已经有可以读取这种结构的pandas方法(我已经搜索并尝试了所有具有不同参数的read_dict和records方法)

C) 优化它。

该函数已经在线程中运行,因此多线程/处理不是一个最佳主意。

标签: pythonpandasalgorithmdataframedictionary

解决方案


首先,我会尽量避免readlines将整个文件作为行列表加载到内存中。然后我会尝试为 Dataframe 构造函数提供一个列表字典:

def read_structure(fp):
    cols = 'col-1', 'col-2', 'col-n'  # declare the column names
    data = {col: [] for col in cols}  # initialize the data structure

    with open(fp) as fh:
        for i, line in enumerate(io.StringIO(t), 1):
            try:
                row = ast.literal_eval(line)
                for k, v in row.items():
                    data[k].extend(v)
            except Exception as e:
                print('Error line', i, line, e)
                raise           # unsure that we can continue after an error
    return pd.DataFrame(data)

不确定它是否很快,但至少它很简单


推荐阅读