将丑陋的 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,每个文件是否包含单个 lambdas
、fqbi
和 reference
或多个?
@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 中解析带有嵌套值的 JSON 列
pandas使用read_csv函数读取csv数据设置parse_dates参数将csv数据中的指定字段数据列解析为时间日期对象
pandas使用read_csv函数读取文件并解析日期数据列(parse dates)pandas使用read_csv函数读取文件并将缺失值转化为空字符串