2016-01-05 48 views
1

我有這樣的:緩慢的Python代碼似乎適合itertools:如何優化?

entity_key = 'pid' 
data = [ { ... }, { ... } ] 
entities = list(set([ row[entity_key] for row in data ])) 
parsed = [] 
total_keys = ['a','b','c'] 
for entity_id in entities: 
    entity_rows = [ row for row in data if row[entity_key] == entity_id ] 
    totals = { key: sum(filter(None, [ row.get(key) for row in entity_rows ])) for key in total_keys } 
    totals[entity_key] = entity_id 
    parsed.append(totals) 
return parsed 

我的方案,data約30,000個項目,這是大的。

的每一項都是一個dict,每個dict包含在total_keys定義的每個項目,例如標識符pid,和數值{ 'pid': 5011, 'a': 3, 'b': 20, 'c': 33 }

如您所見,代碼將爲每個pid返回一個唯一行的列表,並在total_keys列表中定義加法列。可能有大約800-1000個獨特的pid值,因此parsed最終大約有800-1000個項目。

這很慢。我試圖用itertools.groupby重新寫這個,但它似乎不是最合適的。有沒有我失蹤的魔法?

+1

過濾器只需使用'總和(行[關鍵] ...如果行鍵...)',這也是快於使用GET請求,你也要先創建一個列表,然後一再次設置一個列表,只需使用一組'實體= {row [entity_key]作爲行中的數據}' –

回答

2

使用的PID爲外鍵一個字典:

entity_key = 'pid' 

data = [ { 'pid': 5011, 'a': 3, 'b': 20, 'c': 33 },{ 'pid': 5012, 'a': 3, 'b': 20, 'c': 33 }, 
{ 'pid': 5011, 'a': 3, 'b': 20, 'c': 33 },{ 'pid': 5012, 'a': 3, 'b': 20, 'c': 33 }] 


from collections import defaultdict 

totals = ["a", "b", "c"] 

dfd = defaultdict(lambda: {"a":0, "b", 0, "c": 0}) 
for d in data: 
    for k in d.keys() & totals: 
     dfd[d["pid"]][k] += d[k] 

輸出將所有PID的分組和任何AB或C鍵值總結:

defaultdict(<function <lambda> at 0x7f2cf93ed2f0>, 
{5011: {'a': 6, 'c': 66, 'b': 40}, 5012: {'a': 6, 'c': 66, 'b': 40}}) 

對於python2你需要使用uni = d.viewkeys() & totals

如果你的數據實際上是分組的,你可能會產生一個組TA時間:不需要

from collections import defaultdict 
from itertools import groupby 
from operator import itemgetter 

def yield_d(data,k, keys): 
    for k,v in groupby(data, key=itemgetter(k)): 
     d = defaultdict(lambda: dict.fromkeys(keys, 0)) 
     for dct in v: 
      for _k in dct.keys() & keys: 
       d[k][_k] += dct[_k] 
     yield d 
+0

'defaultdict'並迭代'data'似乎是一個非常穩固的解決方案,而且速度相當快。 – Wells

+0

@Wells,我們只是對數據進行一次遍歷,然後根據我認爲是儘可能少的工作總數找到共用鍵,運行時應該在'O(len(data)* len (總計))' –

+0

算法的好轉!只是一個nit:你可以創建你的'dfd = defaultdict(lambda:{k:0 for k in totals})'(dry-spot) – xtofl

1

你試過熊貓嗎?

如果您的PID列,看起來像一個良好的匹配

import pandas as pd 

df = pd.DataFrame(your dictionary) 

df.groupby(['pid']).sum() 
+0

從功能上看,這似乎是正確的,但是性能呢? – xtofl

+0

@xtofl好了,每一列都是一個熊貓系列,反過來,它是一個numpy數組,它們是像sum,scale等東西可用的最快選項。 –

+0

pandas絕對值得研究,我會給它一個 – Wells

2

你已經有了,因爲在循環中您的會員資格測試的O(n^2)算法。如果您創建索引數據結構,則可以提高性能。

entity_key = 'pid' 
data = [ { ... }, { ... } ] 
totals_keys = ['a','b','c'] 
parsed = [] 

indexed = {} 
for row in data: # construct a map of data rows, indexed by id 
    entity_id = row[entity_key] 
    indexed.setdefault(entity_id, []) # start with an empty list 
    indexed[entity_id].append(row) 

for entity_id in entities: 
    entity_rows = indexed[entity_id] # fast lookup of matching ids 
    totals = { key: sum(row[key] for row in entity_rows if key in row) for key in totals_keys } 
    totals[entity_key] = entity_id 
    parsed.append(totals) 

return parsed