[英]How can I optimize my code to process faster?
我編寫的代碼存在一些性能問題。 該代碼的目的是比較2個csv文件(一個中有900k行,另一個中有50k〜80k行)。
目的是比較csv1和csv2,並將匹配的數據寫入第三個csv。
我的數據如下所示:
CSV1:
address,name,order_no
add1,John,d009
add2,Smith,d019
add3,Mary,d890
.....(900k more rows)
CSV2:
address,hub_id
add3,x345
add4,x310
add1,a109
....(50k ~ 80k more rows)
預期輸出:
CSV3:
order_no,hub_id
d890,x345
d009,a109
.....(etc)
我現在正在處理的代碼(盡管很簡單)實際上有效。 但是,比較和編寫的整個過程需要很長時間才能完成。
任何指針將不勝感激。 自從我剛開始學習以來,我可能已經忽略了一些在比較大型數據時可以使用的python函數。
import csv
import time
start_time = time.time()
with open('csv1.csv', newline='', encoding='Latin-1') as masterfile:
reader = csv.DictReader(masterfile)
for row in reader:
with open('csv2.csv', newline='', encoding='Latin-1') as list1:
reader2 = csv.DictReader(list1)
for row2 in reader2:
if row2['address'] == row['address']:
with open('csv3.csv', 'a') as corder:
print(row2['wip'] + ', ' + row['lat'] + ', ' + row['long'], file=corder)
print("--- %s seconds ---" % (time.time() - start_time))
您的算法當前正在做什么:
所有這些步驟完成了900k次以上。
步驟#2(打開較小的文件)應該只執行一次。 打開文件並從磁盤加載它是一項昂貴的操作。 僅僅從一開始就加載一次並在內存中執行線性搜索(第3步),您就會看到很大的改進。
步驟#4同樣:打開輸出文件只能執行一次。 每次關閉文件時,系統都會將文件刷新到磁盤。 這是非常浪費的步驟。 如果使文件保持打開狀態,則將數據緩沖輸出,直到有足夠的空間將完整的塊寫入磁盤為止,這是完成此操作的更快方法。
通過使用正確的數據結構,可以對步驟3進行很多優化。 哈希表是日常生活中最常見的概率用途之一。 它們無處不在,因為它們使查找成為恆定時間的操作(與線性搜索不同,線性搜索隨輸入大小而線性縮放)。 哈希表在Python的dict
類中實現。 通過創建一個以address
為鍵的dict
,可以將處理時間減少到900k + 80k
的倍數,而不是900k * 80k
的倍數。 查找算法復雜度以了解更多信息。 我特別推薦Steve Skiena的“算法設計手冊”。
最后一步是在每個文件中找到地址的交集。 有一些可用的選項。 您可以將兩個文件都轉換為dict
並執行一set
類似鍵的交集,也可以將一個文件加載到dict
,然后逐行對其進行測試。 我強烈建議您使用后者,將較小的文件作為您加載到dict
。 從算法角度來看,元素減少10倍意味着您可以降低哈希沖突的可能性。 這也是最便宜的方法,因為它會在較大文件的無關行上快速失敗,而不會對其進行記錄。 從實際的角度來看,如果我懷疑它有多個具有相同地址的行,您甚至可能沒有選擇將較大的文件直接轉換為字典。
這是我一直在談論的實現:
with open('csv2.csv', newline='', encoding='Latin-1') as lookupfile:
lookup = dict(csv.reader(lookupfile))
with open('csv1.csv', newline='', encoding='Latin-1') as masterfile, open('csv3.csv', 'w') as corder:
reader = csv.reader(masterfile)
corder.write('order_no,hub_id\n')
for address, name, order_no in reader:
hub_id = lookup.get(address)
if hub_id is not None:
corder.write(f'{order_no},{hub_id}\n')
如果任何行的長度不完全是兩個元素,則表達式dict(csv.reader(lookupfile))
將失敗。 例如,空白行將使其崩潰。 這是因為dict
的構造函數期望兩個元素的序列可迭代來初始化鍵值映射。
作為次要的優化,我沒有使用csv.DictReader
,因為這需要對每一行進行額外的處理。 此外,我已經從輸出中完全刪除了csv
模塊,因為您可以更快地完成工作,而無需添加包裝器。 如果你的文件一樣整潔格式化為你展示,你可以從身邊分裂他們得到一個小的性能提升,
你自己,而不是使用csv
。
很長一段時間是因為:
O(n**2)
。 永遠不會對像這樣的大數據執行線性搜索 通過創建2個字典, 地址作為鍵 ,整行作為值,可以做得更好。
然后執行鍵的交點,並寫入結果,並根據需要在每個字典中選擇數據。
以下代碼已針對您的示例數據進行了測試
import csv
with open('csv1.csv', newline='', encoding='Latin-1') as f:
reader = csv.DictReader(f)
master_dict = {row["address"]:row for row in reader}
with open('csv2.csv', newline='', encoding='Latin-1') as f:
reader = csv.DictReader(f)
secondary_dict = {row["address"]:row for row in reader}
# key intersection
common_keys = set(master_dict) & set(secondary_dict)
with open("result.csv", "w", newline='', encoding='Latin-1') as f:
writer = csv.writer(f)
writer.writerow(['order_no',"hub_id"])
writer.writerows([master_dict[x]['order_no'],secondary_dict[x]["hub_id"]] for x in common_keys)
結果是:
order_no,hub_id
d009,a109
d890,x345
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.