Python:如何在未排序的列表中查找大于某个数字的所有项(大数据集)

时间:2012-07-01 01:48:33

标签: python search sorting zodb

虽然其他人已经提出了类似的问题,但是here,但他们略有不同,并没有真正解决我的问题,所以我再来一次。

我有N个列表(N> 20,000),每个列表包含M个列表(M> 20,000),方式如下(数据为虚拟):

Key1: [ [4,3,1], [5,1,0] ...... [43,21,0 ] ]   # List 1 with collection of M smaller lists
:
:
KeyN: [ [5,4,1], [55,1,1] ...... [ 221, 0, 0] ] # Nth list

数据未排序。逐个迭代一个阈值列表,比如Threshold =[2, 3, 5, 7, 8],其中阈值应用于中间元素,我想提取所有键的所有元素,大于阈值。对于前者根据我上面写的数据,Threshold = 2将产生

 For Key1: [ [4,3,1], [43,21,0]]
 :
 : 
 For KeyN: [[5,4,1]]

同样适用于其他阈值。由于列表太多,我的观察是排序会导致很多开销,因此我想避免它。在 python 中执行此操作的最佳方法是什么?另外一个重点是,我自己构建数据,因此可能有一个更好的数据结构来存储数据。我目前将PersistentList形式的数据存储在Btree的{​​{1}}容器中,建议here。以下是用于它的代码片段:

ZODB

关于什么应该是最有效的方法的任何建议?排序第一的确是最佳方式吗?

2 个答案:

答案 0 :(得分:3)

使用生成器理解:

(sublist for sublist in Key1 if sublist[1] > Threshold)

生成器只按需计算元素,因为它按顺序遍历列表的元素,所以不需要排序。 (也就是说,它在每个Keyn的长度上以线性时间运行,而不是用于排序的M * log(M)。)

等效地,在功能样式中(仅在Python 3中等效;对于Python 2,使用itertools.ifilter):

filter(lambda sublist: sublist[1] > Threshold, Key1)

如果您的Keyn列表存储在列表(或其他可订阅对象)中,您可以一次处理它们(显示一些替代样式):

filtered_Keys = [(sublist for sublist in Key if sublist[1] > Threshold)
    for Key in Keys
]

filtered_Keys = list(map(
    lambda Key: filter(lambda sublist: sublist[1] > Threshold, Key1),
    Keys
))

此方法相对于排序的性能

此方法是否比排序更快取决于 M 以及您拥有的阈值 T 。运行时间(对于每个Key列表)是O(M * T)。如果对列表进行排序(O(M * log(M))),则可以对每个阈值使用二进制搜索,总运行时间为O(M * log(M)+ T * log(M))= O(max(M,T)* log(M))。当 T 相对于 M 足够大时,排序更快。我们无法先验地知道常数,因此请根据您的数据测试两种方法是否更快。

如果两者都不够快,请考虑编写自己的线性时间排序。例如,radix sort can be generalized to work on (non-negative) floats。如果你真的关心这里的性能,你可能不得不把它写成C或Cython扩展。

答案 1 :(得分:2)

在numpy中,您可以使用NxMx3阵列轻松完成此操作:

data = array([
    [ [4,3,1], [5,1,0],  [43,21,0]    ],
    [ [5,4,1], [55,1,1], [ 221, 0, 0] ]
    ])
data[ data[:,:,1]>2 ]

返回:

array([[ 4,  3,  1],
   [43, 21,  0],
   [ 5,  4,  1]])

如果您需要超过阈值的元素的位置,请使用argwhere()。

修改

也可以同时进行多个阈值比较:

>>> mask = data[:,:,1,np.newaxis] > array([[[2, 3, 4]]])
>>> data[mask[...,0]]
array([[ 4,  3,  1],
   [43, 21,  0],
   [ 5,  4,  1]])

>>> data[mask[...,1]]
array([[43, 21,  0],
   [ 5,  4,  1]])

>>> data[mask[...,2]]
array([[43, 21,  0]])