Python:islice 的性能问题
Posted
技术标签:
【中文标题】Python:islice 的性能问题【英文标题】:Python: performance issues with islice 【发布时间】:2015-07-04 23:01:52 【问题描述】:使用以下代码,我发现执行时间越来越长,因为我增加了 islice 中的起始行。例如,4 的 start_row 将在 1 秒内执行,但 500004 的 start_row 将需要 11 秒。为什么会发生这种情况,是否有更快的方法来做到这一点?我希望能够遍历大型 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)
【问题讨论】:
相关:***.com/questions/620367/…(虽然,呃,如果你在这里尝试使用它,接受的答案会惨败) 所以不要使用公认的答案,使用基于Rosenfield's的东西。 【参考方案1】:例如,start_row 为 4 将在 1s 内执行,但 start_row 为 500004 需要 11 秒
这就是 islice 是聪明的。或者懒惰,这取决于你喜欢哪个术语。
问题是,文件“只是”硬盘上的字节字符串。他们没有任何内部组织。 \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
。或者使用数据库。
【讨论】:
【参考方案2】:使用islice()
进行操作的问题在于,在返回任何内容之前,它会遍历您想要的第一行之前的所有行。显然,起始行越大,所需的时间就越长。另一个是您使用csv.reader
来读取这些行,这可能会产生不必要的开销,因为 csv 文件的一行通常是其中的一行。唯一不正确的情况是 csv 文件中的字符串字段包含嵌入的换行符——根据我的经验,这种情况并不常见。
如果这是对您的数据的有效假设,那么首先索引文件并构建一个 (filename, offset, number-of-rows) 元组表可能会更快文件中大约相同大小的行/行逻辑块。这样,您可以通过首先寻找起始偏移量然后从该点读取指定数量的 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)
【讨论】:
嗨,martineau,我已经用 4.5GB 的 csv 文件尝试了您的代码,拆分功能大约需要 42 秒。我还使用了 NightShadeQueen 在上面的链接中发布的“索引每一行”方法。它也需要 42 秒。似乎总时间取决于我们必须读取文件中的每一行这一事实,而不是我们存储的偏移量。 索引每一行需要更多的内存,即使不需要更长的时间。最好的情况是,您可以将文件分成大约相同行数的块,而不管它们的长度如何,这与一起表示大约相同字节数的行块不同。以上是关于Python:islice 的性能问题的主要内容,如果未能解决你的问题,请参考以下文章
与 functools.partial 一起使用时,itertools.islice 会引发 ValueError
itertools 的 islice(count()) vs range()