[英]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.