2016-11-19 39 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