在Python中解析大型CSV文件的最有效方法是什么?

11

我曾尝试查看其他答案,但仍不确定正确的方法。 我有许多非常大的 .csv文件(每个文件可能都有一千兆字节),我想首先获取它们的列标签,因为它们并不完全相同,然后根据用户的偏好使用某些条件提取其中的一些列。 在开始提取部分之前,我进行了简单的测试,以查看解析这些文件的最快方式,以下是我的代码:

def mmapUsage():
    start=time.time()
    with open("csvSample.csv", "r+b") as f:
        # memory-mapInput the file, size 0 means whole file
        mapInput = mmap.mmap(f.fileno(), 0)
        # read content via standard file methods
        L=list()
        for s in iter(mapInput.readline, ""):
            L.append(s)
        print "List length: " ,len(L)
        #print "Sample element: ",L[1]
        mapInput.close()
        end=time.time()
        print "Time for completion",end-start

def fileopenUsage():
    start=time.time()
    fileInput=open("csvSample.csv")
    M=list()
    for s in fileInput:
            M.append(s)
    print "List length: ",len(M)
    #print "Sample element: ",M[1]
    fileInput.close()
    end=time.time()
    print "Time for completion",end-start

def readAsCsv():
    X=list()
    start=time.time()
    spamReader = csv.reader(open('csvSample.csv', 'rb'))
    for row in spamReader:
        X.append(row)
    print "List length: ",len(X)
    #print "Sample element: ",X[1]
    end=time.time()
    print "Time for completion",end-start

我的结果是:

=======================
Populating list from Mmap
List length:  1181220
Time for completion 0.592000007629

=======================
Populating list from Fileopen
List length:  1181220
Time for completion 0.833999872208

=======================
Populating list by csv library
List length:  1181220
Time for completion 5.06700015068

看起来大多数人使用的csv库比其他库慢得多。 也许当我开始从csv文件中提取数据时,它后来会证明更快,但我还不能确定。 在我开始实施之前有什么建议和提示吗? 非常感谢!


首先要确保代码正确性,然后再考虑速度——即从“csv”模块开始。如果您想进行更多的性能测试而不是从“csv”开始,请添加一个检查以确保您的其他方法正确解析数据。您的fileopenUsage()看起来是一个测试正确性的好地方。 - istruble
3
你的前两种方法没有将每一行解析成字段,因此你的基准测试并未衡量你所声称的内容。 - S.Lott
将来尝试使用 timeit 模块进行类似这样的基准测试。 - nfirvine
使用 [] 创建空列表,而非 list() - nfirvine
4个回答

16

正如其他人多次指出的那样,前两种方法实际上没有进行字符串解析,它们只是逐行读取而不提取字段。我想CSV中看到的大部分速度差异都是由于这个原因。

如果您包含任何可能包括更多“标准”CSV语法而不仅仅是逗号的文本数据,特别是如果您从Excel格式中读取数据,则CSV模块是无价之宝。

如果您只有像 "1,2,3,4" 这样的行,那么使用简单的 split 函数可能就足够了,但是如果您有像 "1,2,'Hello, my name\'s fred'" 这样的行,您将难以在没有错误的情况下解析它。

CSV还可以透明地处理在引号引起的字符串中间出现的换行符。使用简单的 for..in 语句并不容易做到这一点。

如果按以下方式使用CSV模块,则对于我读取Unicode字符串来说,CSV模块始终可以正常工作:

f = csv.reader(codecs.open(filename, 'rU'))

它非常强大,能够导入包含Unicode、带引号的字符串、引号内换行符、末尾缺少字段等多千行文件,并且读取时间合理。

如果您确实需要额外的速度,建议先尝试使用它,然后再进行优化。


1
非常感谢提供的信息。这是我第一次接触Python,所以每一个细节都很重要。 - spagi

3

你对消毒有多关注?

csv 模块非常擅长理解不同的 csv 文件方言并确保适当地进行转义,但它绝对是杀鸡焉用牛刀,而且通常会比其价值更高的(特别是如果涉及 unicode!)

一个真正天真的实现方法是正确地转义\,

import re

def read_csv_naive():
    with open(<file_str>, 'r') as file_obj:
      return [re.split('[^\\],', x) for x in file_obj.splitlines()]

如果你的数据比较简单,这个方法非常适用。但如果你的数据需要更多的转义处理,csv 模块可能是最稳定的选择。

他正在阅读一个日志文件,据我们所知。这意味着他对所阅读的内容/结构几乎没有控制权。我们只能假设他需要进行大量的“净化”,否则东西会表现不正确。 - Zoran Pavlovic

2

为了读取大型csv文件,我们需要创建子进程来读取文件的块。 打开文件以获取文件资源对象。 创建一个子进程,将资源作为参数。 将一组行作为块读取。 重复以上3个步骤,直到到达文件末尾。

from multiprocessing import Process

def child_process(name):
    # Do the Read and Process stuff here.if __name__ == '__main__':
    # Get file object resource.
    .....
    p = Process(target=child_process, args=(resource,))
    p.start()
    p.join()

请访问此链接以获取代码,这将对您有所帮助。 http://besttechlab.wordpress.com/2013/12/14/read-csv-file-in-python/


1

你的前两种方法并没有将每一行解析成字段。使用 csv 的方式是解析出一行中的多个字段(不同于每一行)。

你真的需要在内存中构建所有行的列表吗?


我还不确定实际的实现会是什么样子。我仍然不知道用户会向我返回什么样的查询,根据这些查询,我必须从CSV文件中提取所需的信息。我忘了提到CSV文件包含日志。 一个查询可能看起来像:“返回最近2天的温度(其中之一列)”。 - spagi
1
@spagi:请更新问题以澄清您的要求。您的基准测试正在测量不同的内容。您的前两个示例解析文件。在声称它们速度较慢之前,请修复您的基准测试以包括完整的CSV文件解析。 - S.Lott
但我的意图是对所有三个文件执行相同的操作。将文件内容放入列表中不包括解析吗? - spagi
1
@spagi:如果你不需要,把文件内容放入列表中是浪费内存的好方法,不应该随意在1GB文件中这样做。 - John Machin
好的,这主要是为了测量而做的。对于实际实现,我不打算把所有东西都放在内存中。无论如何,我还不确定最好的方法,这就是为什么我正在寻找建议的原因。 - spagi
@spagi:对于那些在实际实现中不会使用的东西进行基准测试是浪费时间并可能导致错误决策。字符串列表(方法1和2)占用的内存比列表列表(方法3)少。 - John Machin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接