【问题标题】:How do you split reading a large csv file into evenly-sized chunks in Python?如何在 Python 中将读取一个大的 csv 文件分成大小均匀的块?
【发布时间】:2011-06-24 19:50:26
【问题描述】:

基本上我有下一个过程。

import csv
reader = csv.reader(open('huge_file.csv', 'rb'))

for line in reader:
    process_line(line)

看到这个相关的question。我想每 100 行发送一次流程线,以实现批量分片。

实现相关答案的问题是csv对象不可订阅,不能使用len。

>>> import csv
>>> reader = csv.reader(open('dataimport/tests/financial_sample.csv', 'rb'))
>>> len(reader)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type '_csv.reader' has no len()
>>> reader[10:]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '_csv.reader' object is unsubscriptable
>>> reader[10]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '_csv.reader' object is unsubscriptable

我该如何解决这个问题?

【问题讨论】:

    标签: python list csv chunks


    【解决方案1】:

    只需将您的reader 包装成list 即可下标。显然这会破坏非常大的文件(请参阅下面的更新中的替代方案):

    >>> reader = csv.reader(open('big.csv', 'rb'))
    >>> lines = list(reader)
    >>> print lines[:100]
    ...
    

    延伸阅读:How do you split a list into evenly sized chunks in Python?


    更新 1(列表版本):另一种可能的方法是处理每个卡盘,因为它在遍历行时到达:

    #!/usr/bin/env python
    
    import csv
    reader = csv.reader(open('4956984.csv', 'rb'))
    
    chunk, chunksize = [], 100
    
    def process_chunk(chuck):
        print len(chuck)
        # do something useful ...
    
    for i, line in enumerate(reader):
        if (i % chunksize == 0 and i > 0):
            process_chunk(chunk)
            del chunk[:]  # or: chunk = []
        chunk.append(line)
    
    # process the remainder
    process_chunk(chunk)
    

    Update 2(生成器版本):我没有对它进行基准测试,但也许你可以通过使用块generator来提高性能:

    #!/usr/bin/env python
    
    import csv
    reader = csv.reader(open('4956984.csv', 'rb'))
    
    def gen_chunks(reader, chunksize=100):
        """ 
        Chunk generator. Take a CSV `reader` and yield
        `chunksize` sized slices. 
        """
        chunk = []
        for i, line in enumerate(reader):
            if (i % chunksize == 0 and i > 0):
                yield chunk
                del chunk[:]  # or: chunk = []
            chunk.append(line)
        yield chunk
    
    for chunk in gen_chunks(reader):
        print chunk # process chunk
    
    # test gen_chunk on some dummy sequence:
    for chunk in gen_chunks(range(10), chunksize=3):
        print chunk # process chunk
    
    # => yields
    # [0, 1, 2]
    # [3, 4, 5]
    # [6, 7, 8]
    # [9]
    

    有一个小问题,如@totalhack points out

    请注意,这会一遍又一遍地产生具有不同内容的相同对象。如果您计划在每次迭代之间使用块做所有需要的事情,这很好用。

    【讨论】:

    • 问题是订阅文件,强制读取文件的所有行。这是一个非常大的文件,如果我这样做,内存使用会大大增加。
    • @Mario:添加了一个生成器版本,它可能会更快(但我没有时间测试它 - 也许你会这样做)。
    • enumerate() 上的第二个参数“只需要 1 个参数(给定 2 个)”是正确的?
    • @Mario:哇,这很烦人。这是另一个要点 (gist.github.com/820490),我自己用 python 2.5 尝试过。如果这不能解决它,我没有选择(和时间;)这个答案。祝你好运!
    • 小问题:请注意,这会一遍又一遍地产生具有不同内容的相同对象。如果您计划在每次迭代之间使用块做所有需要的事情,这很好用。在大多数情况下,这可能是意图,但如果该行为不适用于您的情况,一种选择是将 del chunk[:] 更改为 chunk = []
    【解决方案2】:

    我们可以使用 pandas 模块来处理这些大的 csv 文件。

    df = pd.DataFrame()
    temp = pd.read_csv('BIG_File.csv', iterator=True, chunksize=1000)
    df = pd.concat(temp, ignore_index=True)
    

    【讨论】:

      【解决方案3】:

      对于所有.csv 文件,没有一种好的方法可以做到这一点。您应该能够使用file.seek 将文件分成块以跳过文件的一部分。然后你必须一次扫描一个字节才能找到行尾。您可以独立处理这两个块。下面的(未经测试的)代码应该可以帮助您入门。

      file_one = open('foo.csv')
      file_two = open('foo.csv') 
      file_two.seek(0, 2)     # seek to the end of the file
      sz = file_two.tell()    # fetch the offset
      file_two.seek(sz / 2)   # seek back to the middle
      chr = ''
      while chr != '\n':
          chr = file_two.read(1)
      # file_two is now positioned at the start of a record
      segment_one = csv.reader(file_one)
      segment_two = csv.reader(file_two)
      

      我不知道你怎么知道你已经完成了遍历segment_one。如果您在 CSV 中有一个列是行 ID,那么当您遇到 segment_two 中第一行的行 ID 时,您可以停止处理 segment_one

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-06-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-02-16
        • 2022-10-05
        • 1970-01-01
        相关资源
        最近更新 更多