快速解析类似csv的文件

时间:2014-10-30 08:32:54

标签: python csv file-io

我需要读入的文件类型具有以下结构:一行标题,然后逐行分隔条目,如下所示:

Date    Timestamp    Identifier    Value

文件大小约为2MB,每个标识符大约有200个值(大约600k行)。可以使用pythons csv reader读取此文件并提取所需的Identifier列。由于我有几组这些文件,读入感觉很慢:

import csv
def read_file(fhandle, identifiers):
    #identifiers = [identifier1, identifier2, ...]
    #dialect from csvr.sniffer()
    csvr = csv.reader(fhandle, dialect) 
    data = []
    EOF = False            
    while not EOF:
        try:
            row=csvr.next()                        
            if row[2] in identifiers:
                data.append(tuple(row[1:]))
        except StopIteration:
            EOF = True
    fhandle.close()
    sorted(data, keyfunc) #keyfunc = lambda x: (x[1],x[0])
    return data

我开始尝试使用此功能加速它(目前只读取一个标识符进行测试)。它使用正则表达式,以便只解析包含所需信息的行。它还添加到一个数组而不是附加到一个列表,我发现它在过去更快(对于大型数据集)。

import re
import numpy as np
def power_read(fhandle, identifier):
    findme = '(?<=%s\W)[0-9.]+' %identifier       
    m = re.compile(findme)
    result = np.zeros(10000)
    cnt = 0
    EOF = False
    while not EOF:
        try: 
            ln = fhandle.next()
            found = re.search(m, ln)
            if found:
                result[cnt] = float(found.group(0))
                cnt += 1
        except StopIteration: 
            EOF=True
            fhandle.close()
    return result[0:cnt]   

这样可行,但速度并不快。我还能调整什么呢?

2 个答案:

答案 0 :(得分:1)

有类似的要求,我做了类似的事情:

data = [line.strip('\n').split(',') for line in open('test.txt','r').readlines()]
identifiers = ['2069784', '2640650']
filteredData = filter(lambda x:x[2] in identifiers, data)

现在尝试计时,对于大小为52 MB(64列,60897行)的文本文件,需要3秒钟才能获取所需的行。结果有308行。

请注意我的文件以逗号分隔,所以用逗号分隔。我还使用带有8GB RAM的Windows 7机器。

此外,您能否分享您的代码的效果详情。我很想知道我应该采取哪种方法。

答案 1 :(得分:0)

简短回答:

csv readerharaprasadj(见下面的答案)速度几乎相同。

答案很长:

我尝试了所有建议的方法,numpy.genfromtxt(),代码haraprasadj建议,我修改了我的power_read()函数,如下所示:

def power_read(fhandle, identifiers):
    findme = '(?<=%s\W)[0-9.]+' %identifiers[0]       
    result = np.zeros(10000)
    cnt = 0
    ALL = fhandle.read()
    fhandle.close()
    found = re.findall(findme, ALL, flags=re.S)
    for f in found:
        result[cnt] = float(f)
    return result[0:cnt]

为什么要改变?我想出来,这种明智的调查显然需要更长的时间。问题版本是18次合并10次,而上述版本只有6次。

numpy.genfromtxt(fhandle, delimiter='\t', 
    dtype={'names':('Time', 'Identifier', 'Value'), 
           'formats':('datetime64[ns]', 'S50', 'f8')})

在同一设置中使用约41秒并取消其自身资格。

接下来,我评估了平均超过50次运行,结果如下:

  • new power_read:0.582 s
  • haraprasadj:1.033 s
  • readfile:1.081 s

虽然此时re解决方案看起来像赢家,但它仍然不会立即读出多个标识符。此外,csv已经读取时间戳。我现在将研究如何处理几个关键字以及它如何影响执行时间。此外,完整阅读必须始终牢记内存限制。

使用power_read()函数的下一步是,我添加了一些更多功能,包括时间戳提取和多个关键字的支持,返回一个方便的字典:

def power_read(fhandle, identifiers):
    ALL = fhandle.read()
    fhandle.close()
    result = {}
    for i in identifiers:
        findme = ('(?P<timestamp>\d+-\d+-\d+ \d+:\d+:\d+.[\d\+]+)\W%s\W(?P<value>[\d.]+)' %i)
        res = np.empty(shape=(10000, 2), dtype=[('time','datetime64[ns]'), ('value','f4')])
        cnt = 0
        found = re.findall(findme, ALL, flags=re.S)
        for f in found:
            res[cnt] = np.array(f, dtype=[('time','datetime64[ns]'), ('value','f4')])
        result[i] = res[0:cnt,:]
    return result

我测试了1个关键字和3个关键字:

  • read_file 1kword = 1.1s 3kword = 1.1s
  • haraprasadj 1kword = 1.0s 3kword = 1.1s
  • power_read 1kword = 1.4s 3kword = 4.2s

总结一下,除非一个人只想提取一个值csv阅读器和haraprasadj的方法看起来更优越。尽管如此,前两种方法还没有发生类型转换。有人如何有效地对结果进行类型转换?为了调查,我更改了以下方法,并使用三个关键字进行调用:

def read_file(fhandle, identifiers, dialect):
    csvr = csv.reader(fhandle, dialect) 
    data = []
    EOF = False            
    while not EOF:
        try:
            row=csvr.next()                        
            if row[2] in identifiers:
                data.append(tuple(row[1:-1]))
        except StopIteration:
            EOF = True
    fhandle.close()
    data = np.array(data, dtype=None)
    time = data[:,0]
    ids = data[:,1]
    value = data[:,2]
    res = {}
    for i in identifiers:
        msk = np.where(ids==i, True, False)
        res[i] = np.array(zip(time[msk], value[msk]), dtype=[('time','datetime64[us]'), ('value','f8')])
    return res

和第二个函数,针对我处理的确切数据进行了优化:

def haraprasadj(fhandle, identifiers):
    data = [line.strip().split('\t')[1:] for line in fhandle.readlines()]
    fhandle.close()
    result = np.array(filter(lambda x:x[1] in identifiers, data))
    time =result[:,0]
    ids = result[:,1]
    value = result[:,2]
    res = {}
    for i in identifiers:
        msk = np.where(ids==i, True, False)
        res[i] = np.array(zip(time[msk], value[msk]), dtype=[('time','datetime64[us]'), ('value','f8')])
    return res

事实证明,两种方法的速度大致相同(至少对于我的测试文件大小):

  • read_file() 1.12 s
  • haraprasadj 1.06 s

与之前的结果比较表明,类型转换只需要很短的时间,这对我来说是令人惊讶的。
剩下的区别是haraprasadj需要更多内存,这对某些应用程序可能很重要。为了便于阅读,我将使用我原来的read_file(),现在开始研究并行计算。