2016-03-01 36 views
2

我想根據給定的id過濾來自大文件(列表清單,10M+行)的記錄。從Python中的大文件中選擇部分記錄的更有效方法

selected_id = list()   # 70k+ elements 

for line in in_fp:    # input file: 10M+ lines 
    id = line.split()[0]  # id (str type), such as '10000872820081804' 

    if id in selected_id: 
     out_fp.write(line) 

上面的代碼很耗時。我想起了一個想法。商店selected_iddict而不是list

任何更好的解決方案?

+2

你是不是存儲每個ID單獨的值,所以'dict'似乎有點小題大做。也許是'set'? –

+0

@JohnGordon,因爲'set'不可用。將'selected_id'映射到任意值,例如'True'。 – SparkAndShine

+1

你確定它不可用? http://stackoverflow.com/questions/3949310/how-is-set-implemented –

回答

1

你有一些問題,但只有第一個是真的討厭:

  1. (通過很可能是最大的成本)檢查list的會員資格是O(n);對於一個70K元素list,這是很多工作。使其成爲set/frozenset,查找通常是O(1),節省了成千上萬的比較。如果這些類型不可用,您可以預先sortselected_list並使用bisect模塊在O(log n)時間內執行查找,對於如此大的list,仍可獲得多個數量級的加速。
  2. 如果您的線條很大,並且有幾個空格,那麼所有點的分割會浪費時間;你可以指定maxsplit到只夠分拿到ID
  3. 如果ID總是整數值它可能是值得花時間做的strselected_idint,而不是和轉換上閱讀,因此查找比較跑快一點(這將需要測試)。這可能不會產生重大影響,所以我會從示例中省略它。

結合所有建議:

selected_id = frozenset(... Your original list of 70k+ str elements ...) 

for line in in_fp:    # input file: 10M+ lines 
    id, _ = line.split(None, 1) # id (str type), such as '10000872820081804' 

    if id in selected_id: 
     out_fp.write(line) 

您可以在for循環,甚至轉化爲與發電機表達的單個調用(雖然它變得有點過於緊湊型),這推動更多的工作在C層CPython的,減少的Python字節碼執行開銷:

out_fp.writelines(x for x in in_fp if x.split(None, 1)[0] in selected_id) 
+0

thx。在......中......中......和在......中......中不......在......中的表現是否相同? – SparkAndShine

+1

@SparkandShine:除了'for'之外,除非你參考'if'測試,否則''不要''不要''在'中。如果你的意思是'if x in y'與'if x not in y'一樣快,它實際上是相同的(兩者都檢查'if y in y',Python只是爲你翻轉結果)。從技術上講,成功的查找通常涉及的檢查次數少於查找失敗的檢查次數(平均而言,發現成功的數量是衝突解決的一半),但我們在談論的是C中的一小部分鏈接操作。幾乎所有名稱Python中的分辨率涉及類似代價的「dict」查找,所以噪聲中的開銷會丟失。 – ShadowRanger

+0

抱歉我的文書錯誤。它是'如果...(不)在......'。 – SparkAndShine

1

首先,爲了從你的線條獲得的第一列,你可以使用csv模塊讀取正確的文件分隔符他們爲了獲得使用zip()功能(在Python 3和pyhton 2 itertools.izip())和next()功能第一列然後將結果傳遞給set()函數以保留唯一值。

import csv 

with open('file_name') as f: 
    spam_reader = csv.reader(f, delimiter=' ') 
    unique_ids = set(next(zip(*spam_reader))) 

如果你想保留的順序,你可以使用collections.OrderedDict()

import csv 
from collections import OrderedDict 
with open('file_name') as f: 
    spam_reader = csv.reader(f, delimiter=' ') 
    unique_ids = OrderedDict.fromkeys(next(zip(*spam_reader))) 
+1

使用'zip'來實現這一點意味着您必須將整個解析的輸入文件保存在內存中,並且在生成單個輸出之前必須解析整個事物。使用帶'*'的解壓縮包來執行轉置操作是很聰明的,但是當輸入很大時它是一個非常糟糕的主意,因爲它會強制執行一個完整的操作。 – ShadowRanger

相關問題