Python:islice的性能问题

时间:2015-07-04 23:01:52

标签: python performance csv itertools

使用以下代码,随着我在islice中增加起始行,我看到执行时间越来越长。例如,start_row为4将在1s内执行,但start_row为500004将需要11s。为什么会发生这种情况,并且有更快的方法吗?我希望能够在大型CSV文件(几GB)中迭代多个行范围并进行一些计算。

import csv
import itertools
from collections import deque
import time

my_queue = deque()

start_row = 500004
stop_row = start_row + 50000

with open('test.csv', 'rb') as fin:
    #load into csv's reader
    csv_f = csv.reader(fin)

    #start logging time for performance
    start = time.time()

    for row in itertools.islice(csv_f, start_row, stop_row):
        my_queue.append(float(row[4])*float(row[10]))

    #stop logging time
    end = time.time()
    #display performance
    print "Initial queue populating time: %.2f" % (end-start)

2 个答案:

答案 0 :(得分:2)

  

例如,start_row为4将在1s内执行,但start_row为   500004将需要11s

这就是智慧。或者懒惰,取决于你喜欢的术语。

事实上,文件只是硬盘上的“字母”字节串。他们没有任何内部组织。 \n只是该长字符串中的另一组字节。 如果没有查看之前的所有信息,就无法访​​问任何特定的行(除非您的行具有完全相同的长度,在这种情况下您可以使用file.seek)。

第4行?查找第4行很快,您的计算机只需找到3 \n。 50004线?您的计算机必须通读该文件,直至找到500003 \n。没办法,如果有人告诉你,他们要么有其他类型的量子计算机,要么他们的计算机正在读取文件,就像世界上其他所有计算机一样,只是在他们背后。

至于你能做些什么:尝试抓住线来迭代时要聪明一些。聪明,懒惰。安排您的请求,这样您只需迭代一次文件,并在提取所需数据后立即关闭文件。 (顺便说一句,islice完成所有这些。)

在python中

lines_I_want = [(start1, stop1), (start2, stop2),...]
with f as open(filename):
     for i,j in enumerate(f):
          if i >= lines_I_want[0][0]:
              if i >= lines_I_want[0][1]:
                   lines_I_want.pop(0)
                   if not lines_I_want: #list is empty
                         break
              else:
                   #j is a line I want. Do something

如果您对制作该文件有任何控制权,请使每一行的长度相同,以便seek。{或者使用数据库。

答案 1 :(得分:0)

使用islice()进行操作的问题是在返回任何内容之前迭代所需的第一行之前的所有行。显然,起始行越大,这将花费的时间越长。另一个是你使用csv.reader读取这些行,这可能会产生不必要的开销,因为csv文件的一行通常是它的一行。唯一不合时宜的是当csv文件中包含包含嵌入换行符的字符串字段时 - 根据我的经验这种情况并不常见。

如果这是对您的数据的有效假设,那么首先索引文件并构建(文件名,偏移量,行数)元组的表可能要快得多,这表明文件中大致相等大小的行/行的逻辑块。有了它,您可以通过首先寻找起始偏移然后从该点读取指定数量的csv行来相对快速地处理它们。

这种方法的另一个优点是它允许你并行处理块,我怀疑这是你试图根据你的previous question解决的真正问题。所以,即使你没有在这里提到过多处理,如果是这样的话,下面的内容已被编写为兼容这样做。

import csv
from itertools import islice
import os
import sys

def open_binary_mode(filename, mode='r'):
    """ Open a file proper way (depends on Python verion). """
    kwargs = (dict(mode=mode+'b') if sys.version_info[0] == 2 else
              dict(mode=mode, newline=''))
    return open(filename, **kwargs)

def split(infilename, num_chunks):
    infile_size = os.path.getsize(infilename)
    chunk_size = infile_size // num_chunks
    offset = 0
    num_rows = 0
    bytes_read = 0
    chunks = []
    with open_binary_mode(infilename, 'r') as infile:
        for _ in range(num_chunks):
            while bytes_read < chunk_size:
                try:
                    bytes_read += len(next(infile))
                    num_rows += 1
                except StopIteration:  # end of infile
                    break
            chunks.append((infilename, offset, num_rows))
            offset += bytes_read
            num_rows = 0
            bytes_read = 0
    return chunks

chunks = split('sample_simple.csv', num_chunks=4)
for filename, offset, rows in chunks:
    print('processing: {} rows starting at offset {}'.format(rows, offset))
    with open_binary_mode(filename, 'r') as fin:
        fin.seek(offset)
        for row in islice(csv.reader(fin), rows):
            print(row)