使用Pandas vs. CSV读/写器处理和保存大CSV文件

时间:2018-08-08 15:58:16

标签: python pandas csv

我对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文件的逐行迭代真的是解析这些文件的计算效率最高的方法吗?

2 个答案:

答案 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时,这对我来说更快。

https://dask.pydata.org/en/latest/