我对python和pandas还是相当陌生,但是试图通过解析和处理大数据文件来使其变得更好。我目前正在从事一个项目,该项目需要我同时解析几十个大型CSV CAN文件。这些文件有9列感兴趣的内容(1个ID和7个数据字段),大约有1-2百万行,并以十六进制编码。
一些数据示例如下:
id Flags DLC Data0 Data1 Data2 Data3 Data4 Data5 Data6 Data7
cf11505 4 1 ff
cf11505 4 1 ff
cf11505 4 1 ff
cf11a05 4 1 0
cf11505 4 1 ff
cf11505 4 1 ff
cf11505 4 1 ff
cf11005 4 8 ff ff ff ff ff ff ff ff
我需要解码十六进制,然后根据CAN ID从中提取一堆不同的变量。
我的一位同事编写了一个脚本来解析这些看起来像这样的文件(以下称为“脚本#1”):
import sys # imports the sys module
import itertools
import datetime
import time
import glob, os
for filename in glob.glob(sys.argv[1] + "/*.csv"):
print('working on ' + filename +'...')
#Initialize a bunch of variables
csvInput = open(filename, 'r') # opens the csv file
csvOutput = open((os.path.split(filename)[0] + os.path.split(filename)[1]), 'w', newline='')
writer = csv.writer(csvOutput) #creates the writer object
writer.writerow([var1, var2, var3, ...])
try:
reader = csv.reader(csvInput)
data=list(reader)
if (data[3][1] == 'HEX'): dataType = 16
elif (data[3][1] == 'DEC'): dataType = 10
else: print('Invalid Data Type')
if (data[4][1] == 'HEX'): idType = 16
elif (data[4][1] == 'DEC'): idType = 10
else: print('Invalid ID Type')
start_date = datetime.datetime.strptime(data[6][1],'%Y-%m-%d %H:%M:%S')
for row in itertools.islice(data,8,None):
try: ID = int(row[2],idType)
except: ID = 0
if (ID == 0xcf11005):
for i in range(0,4): var1[i] = float((int(row[2*i+6],dataType)<<8)|
#similar operations for a bunch of variables go here
writer.writerow([var1[0], var2[1],.....])
finally:
csvInput.close()
csvOutput.close()
print(end - start)
print('done')
基本上,它使用CSV读取器和写入器为每个CSV逐行生成已处理的CSV文件。对于200万行CSV CAN文件,大约需要40秒才能在我的工作桌面上完全运行。知道逐行迭代比对熊猫数据帧执行矢量化运算要慢得多,我以为我可以做得更好,所以我写了一个看起来像这样的脚本(脚本2):
from timeit import default_timer as timer
import numpy as np
import pandas as pd
import os
import datetime
from tkinter import filedialog
from tkinter import Tk
Tk().withdraw()
filename = filedialog.askopenfile(title="Select .csv log file", filetypes=(("CSV files", "*.csv"), ("all files", "*.*")))
name = os.path.basename(filename.name)
##################################################
df = pd.read_csv(name, skiprows = 7, usecols = ['id', 'Data0', 'Data1', 'Data2', 'Data3', 'Data4', 'Data5', 'Data6', 'Data7'],
dtype = {'id':str, 'Data0':str, 'Data1':str, 'Data2':str, 'Data3':str, 'Data4':str, 'Data5':str, 'Data6':str, 'Data7':str})
log_cols = ['id', 'Data0', 'Data1','Data2', 'Data3', 'Data4', 'Data5', 'Data6', 'Data7']
for col in log_cols:
df[col] = df[col].dropna().astype(str).apply(lambda x: int(x, 16))
df.loc[:, 'Data0':'Data7'] = df.loc[:, 'Data0':'Data7'].fillna(method = 'ffill') #forward fill empty rows
df.loc[:, 'Data0':'Data7'] = df.loc[:, 'Data0':'Data7'].fillna(value = 0) #replace any remaining nans with 0
df['Data0'] = df['Data0'].astype(np.uint8)
df.loc[:, 'Data0':'Data7'] = df.loc[:, 'Data0':'Data7'].astype(np.uint8)
processed_df = pd.DataFrame(np.nan, index= range(0, len(df)), columns= ['var1' 'var2', 'var3', ...])
start_date = datetime.datetime.strptime('7/17/2018 14:12:48','%m/%d/%Y %H:%M:%S')
processed_df ['Time Since Start (s)'] = pd.read_csv(name, skiprows = 7, usecols = ['Time'], dtype = {'Time':np.float32}, engine = 'c')
processed_df['Date'] = pd.to_timedelta(processed_df['Time Since Start (s)'], unit = 's') + start_date
processed_df['id'] = df['id']
processed_df.loc[:, 'var1':'var37'] = processed_df.loc[:, 'var1':'var37'].astype(np.float32)
##################Data Processing###########################
processed_df.loc[processed_df.id == int(0xcf11005), 'var1'] = np.bitwise_or(np.left_shift(df['Data1'], 8), df['Data0'])/10
#a bunch of additional similar vectorized calculations go here to pull useful values
name_string = "Processed_" + name
processed_df.to_csv(name_string) #dump dataframe to CSV
处理部分绝对更快,尽管没有我希望的那么快-处理大约200万行CSV文件花了大约13秒。我可能还可以做一些优化脚本2的工作,但这是另一篇文章的主题。
无论如何,当我尝试将数据帧另存为CSV时,我希望脚本#2实际上比脚本#1更快。 .to_csv()方法仅花费了40秒钟!我尝试使用.to_csv()方法中的一些参数,包括块大小和压缩,以及减少数据帧的内存占用,但是即使进行了这些调整,保存数据帧仍需要30秒钟,并且一旦考虑到初始处理时间,整个脚本要比原始的逐行脚本#1慢。
CSV文件的逐行迭代真的是解析这些文件的计算效率最高的方法吗?
答案 0 :(得分:0)
您是否尝试过设置chunksize
,即一次要写入的行数,如您所见,here超过100,000,则设置为1。
要考虑的另一件事是添加mode='a'
(从默认w
起)以进行追加。
所以我建议使用:
processed_df.to_csv(name_string, mode='a', chunksize=100000)
我会玩chunksize
直到适合您的需求。
答案 1 :(得分:0)
dask
库可能值得一看。它实现了熊猫DataFrame功能的子集,但将DataFrame存储在磁盘上而不是存储在内存中,并允许您像使用内存中一样使用DataFrame。我相信它甚至可以将多个文件视为一个DataFrame,例如使用多台机器并行执行操作。
当我处理具有数百万行的6GB CSV时,这对我来说更快。