将丑陋的 csv 解析为 Pandas DataFrame 的最佳方法

Posted

技术标签:

【中文标题】将丑陋的 csv 解析为 Pandas DataFrame 的最佳方法【英文标题】:Best way to parse ugly csv into Pandas DataFrame 【发布时间】:2020-04-26 01:41:26 【问题描述】:

我的目的是将 Matlab 代码迁移到 Python 代码中。 我是 python 新手,但我仍在尝试分割一个 csv 文件

我的意图是解析一个结构类似于以下的 CSV 文件:

SENSORID;DATESMPL;TRE;ISRC
FQBI-000-001;08/01/2020 13:56:00;-10.0956;0.03662119
LAMBDAS;1550;1551;1552;1553;1554
REFERENCE;6961.058824;6959.564706;6959.423529;6960.988235;6961.788235
1;166;164;162;138;162
2;146;152;161;143;142
3;138;147;150;133;124
4;134;120;158;145;133
5;135;157;135;139;137

预期结果(在 python DataFrame 上):

    SENSORID         DATESMPL           TRE       ISRC     1550  1551  1552  1553  1554
0 FQBI-000-001  08/01/2020 13:56:00  -10.0956  0.03662119   166  164   162   138   162
1 FQBI-000-001  08/01/2020 13:56:00  -10.0956  0.03662119   146  152   161   143   142
2 FQBI-000-001  08/01/2020 13:56:00  -10.0956  0.03662119   138  147   150   133   124
3 FQBI-000-001  08/01/2020 13:56:00  -10.0956  0.03662119   134  120   158   145   133
4 FQBI-000-001  08/01/2020 13:56:00  -10.0956  0.03662119   135  157   135   139   137

Reference 行将被丢弃。 SENSORID、DATESMPL、TRE 和 ISRC 的值必须为每个实际测量数据行(以 1 到 5 的整数开头)重复。

当然,我必须解析的实际 CSV 比我的示例大得多,即 LAMBDA 从 1550 到 1850,并且有 255 个测量行(每个文件大约 250 kB)。

为了让事情变得更简单,我最终将不得不导入多达 10 000 个这些文件并将它们存储在一个唯一的 DataFrame 中。

使用 Matlab,我可以使用 textscan 函数解析这些文件,并将数据存储在统计工具箱提供的数据集对象中。导入其中的 10 000 个文件可以在不到 10 分钟的时间内完成,这对于该案例来说是可以接受的。

在 Python 下最好的方法是什么?

似乎有很多方法可以做到这一点:

将文件内容读取为列表中的字符串 使用NumPy数组或干脆 使用DataFrame.read_csv()

但我不确定最有效的方法是什么

我真的很想保持性能接近(或更好,当然)我在 Matlab 中的表现。

【问题讨论】:

请分享您使用了什么代码,结果如何? pd.read_csv() 很有活力,或许能帮到你 令人印象深刻的是,matlab 可以在没有任何前置条件的情况下处理这样的 csv,每个文件是否包含单个 lambdasfqbireference 或多个? @Datanovice:对不起,如果这是误导,我在 Matlab 下使用 textscan,但我需要 4 个调用和它们之间的一些处理。第一次调用是获取标题的第一行(SENSORID,...),第二次调用相关信息(FQBI-000-001,...),第三次调用获取 lambda 列表(1550,...)和最后调用以读取所有剩余行(实际测量行) 读取 csv 文件是问题的一部分。另一部分是如何处理 10000 个文件并避免在构建一个唯一的 DataFrame 时复制数据。所有文件的 LAMBDAS 行是否相同?是否所有数据行都包含 255 个测量值? 【参考方案1】:

正如您提到的性能很重要,我想投入两分钱作为更快的解决方案。与 Code_Different 的数据示例解决方案相比,该方法的执行速度大约每个文件快 5-10 倍 - 它将如何处理更大的文件,但您必须自己测试

def parse(file):
       columns = []
       #general_values = [] # use this if the meta data columns are different
       column_values = ['SENSORID', 'DATESMPL', 'TRE', 'ISRC']
       measurement_values = []

       with open('tmp.csv', "r") as f:
              for index, row in enumerate(f):
                     if index > 3:  # test for measurement rows first as you will do it most often
                            measurement_values.append(row[:-1].split(';')[1:])
                     # uncomment next elif-clause if the meta data column names differ per file
                     #elif index == 0:  # first row -> SENSORID;DATESMPL;TRE;ISRC
                     #       columns += row[:-1].split(';')  # get rid of newline and split
                     elif index == 1:  # second row -> meta data
                            general_values = row[:-1].split(';') # get rid of newline and split
                     elif index == 2:  # fourth row  -> Lambdas as column names
                            columns += row[:-1].split(';')[1:]  # get rid of newline, split and delete 'LAMBDAS'

       df_array = [columns]
       for measurement in measurement_values:
              df_array.append(general_values + measurement)
       return pd.DataFrame(df_array)

df = parse('tmp.csv')

【讨论】:

【参考方案2】:

您解析文件两次:一次获取元数据,另一次获取数据。然后将两个数据框连接在一起:

path = '/path/to/file.txt'
meta = pd.read_csv(path, sep=';', nrows=1)
data = pd.read_csv(path, sep=';', skiprows=[0,1,3]).drop(columns='LAMBDAS')

# Limit the `fillna` to the columns in `meta`
df = pd.concat([meta, data], axis=1)
df[meta.columns] = df[meta.columns].fillna(method='ffill')

# If you are sure `data` has no NaN
df = pd.concat([meta, data], axis=1).fillna(method='ffill')

【讨论】:

以上是关于将丑陋的 csv 解析为 Pandas DataFrame 的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

用pandas编写单个CSV标头

python之pandas库

使用 Pandas 在巨大的 CSV 中解析带有嵌套值的 JSON 列

pandas使用read_csv函数读取csv数据设置parse_dates参数将csv数据中的指定字段数据列解析为时间日期对象

pandas使用read_csv函数读取文件并解析日期数据列(parse dates)pandas使用read_csv函数读取文件并将缺失值转化为空字符串

Python Pandas read_csv 函数不允许将解析日期更改为所需格式