2016-11-19 27 views
1

我必須在嵌套列表中找到哪個列表有一個單詞並返回一個boolear numpy數組。在嵌套列表上尋找大量數據python

nested_list = [['a','b','c'],['a','b'],['b','c'],['c']] 
words=c 
result=[1,0,1,1] 

我使用這個列表理解這樣做,它工作

np.array([word in x for x in nested_list]) 

但我用700K列表內部嵌套列表工作,所以它需要大量的時間。此外,我必須做很多次,列表是靜態的,但詞可以改變。

1循環與列表理解需要0.36s,我需要一種方法來做得更快,有沒有辦法做到這一點?

+0

如果列表是靜態的,並且您執行了很多操作,則可以索引一次並使用該索引。由於索引本身很昂貴,因此一次傳遞就不值得。 – tdelaney

+0

一次,作爲'文字',你會只有一個字符或可能有多個? – Divakar

+0

實際上,單詞可以有更多的字符。如果words = ['c','b'],那麼我需要2個布爾數組:result = [[1,0,1,1],[1,1,1,0]]。 – jevanio

回答

1

我們可以拉平所有子列表元素給我們一維數組。然後,我們只需在扁平1D陣列中的每個子列表的範圍內尋找任何發生的'c'。因此,根據這一理念,我們可以使用兩種方法,基於如何計算任何c的發生。

方法1:一種方法與np.bincount -

lens = np.array([len(i) for i in nested_list]) 
arr = np.concatenate(nested_list) 
ids = np.repeat(np.arange(lens.size),lens) 
out = np.bincount(ids, arr=='c')!=0 

因爲,如問題所說,nested_list不會跨越迭代變化,我們可以重新使用的一切,只爲循環的最後步。


方法2:np.add.reduceat從以前的一個重用arrlens另一種方法 -

grp_idx = np.append(0,lens[:-1].cumsum()) 
out = np.add.reduceat(arr=='c', grp_idx)!=0 

當通過words列表循環,我們能保持這種方法矢量在最後一步通過沿軸使用np.add.reduceat並使用broadcasting給我們一個2D數組布爾,就像這樣 -

np.add.reduceat(arr==np.array(words)[:,None], grp_idx, axis=1)!=0 

樣品運行 -

In [344]: nested_list 
Out[344]: [['a', 'b', 'c'], ['a', 'b'], ['b', 'c'], ['c']] 

In [345]: words 
Out[345]: ['c', 'b'] 

In [346]: lens = np.array([len(i) for i in nested_list]) 
    ...: arr = np.concatenate(nested_list) 
    ...: grp_idx = np.append(0,lens[:-1].cumsum()) 
    ...: 

In [347]: np.add.reduceat(arr==np.array(words)[:,None], grp_idx, axis=1)!=0 
Out[347]: 
array([[ True, False, True, True], # matches for 'c' 
     [ True, True, True, False]]) # matches for 'b' 
+0

在不同的循環上重複_lens_和_arr_是必須的嗎? – jevanio

+0

@jevanio使用方法#1:最後一步'np.bincount(ids,arr =='c')!= 0'將是唯一循環的事情。使用方法#2:您不需要循環,如示例運行中所示。 – Divakar

+0

實際上,它使得它更快。 現在它花了0.13s。 – jevanio

0

多少時間它把你完成你的循環?在我的測試案例中,它只需要幾百毫秒。

import random 

# generate the nested lists 
a = list('abcdefghijklmnop') 
nested_list = [ [random.choice(a) for x in range(random.randint(1,30))] 
       for n in range(700000)] 

%%timeit -n 10 
word = 'c' 
b = [word in x for x in nested_list] 
# 10 loops, best of 3: 191 ms per loop 

減少每個內部列表,一組給予一些時間節省...

nested_sets = [set(x) for x in nested_list] 
%%timeit -n 10 
word = 'c' 
b = [word in s for s in nested_sets] 
# 10 loops, best of 3: 132 ms per loop 

而一旦你把它變成一個集合列表,你可以建立一個布爾元組列表。雖然沒有實時節省。

%%timeit -n 10 
words = list('abcde') 
b = [(word in s for word in words) for s in nested_sets] 
# 10 loops, best of 3: 749 ms per loop 
+0

現在它花了我0.327 seg per循環,它太高u.u – jevanio

+0

你要找幾個單詞? – James

+0

從10k到700k ... – jevanio