[英]Python: performance issues with islice
使用以下代码,随着我增加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)
例如,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
。 或使用数据库。
使用islice()
进行操作的问题是,在返回任何内容之前,要遍历所有行,直到要查找的第一行。 显然,起始行越大,所需的时间就越长。 另一个是您正在使用csv.reader
读取这些行,这可能会导致不必要的开销,因为csv文件的一行通常是一行。 唯一不正确的时间是csv文件中包含包含嵌入式换行符的字符串字段-以我的经验,这是罕见的。
如果这是对数据的有效假设,则首先对文件建立索引并建立一个(文件名,偏移量,行数)元组表可能会快得多,该表元表示行/行的逻辑块大小大致相等在文件中。 这样,您可以通过首先查找起始偏移量然后从该点开始读取指定数量的csv行来相对快速地处理它们。
这种方法的另一个优点是,它允许您并行处理这些块,我怀疑这是您要根据先前的问题尝试解决的实际问题。 因此,即使您在这里没有提到多重处理,也可以将以下内容编写为与之兼容。
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)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.