检查值是否比列表中的X更常存在的最快方法

时间:2017-03-18 02:30:46

标签: python arrays count

我有一个很长的列表(300 000个元素),我想检查该列表中的每个元素是否存在超过5次。所以最简单的代码是

[x for x in x_list if x_list.count(x) > 5]

但是,我不需要计算x出现在列表中的频率,我可以在达到至少5个元素后停止计数? 我也不需要遍历x_list中的所有元素,因为在查看列表时我有可能已经检查了值x。 知道如何获得此代码的最佳版本吗? 我的输出应该是一个列表,如果可能,请使用相同的顺序...

5 个答案:

答案 0 :(得分:14)

以下是基于Counter的解决方案:

from collections import Counter

items = [2,3,4,1,2,3,4,1,2,1,3,4,4,1,2,4,3,1,4,3,4,1,2,1]
counts = Counter(items)
print(all(c >= 5 for c in counts.values())) #prints True

如果我使用

items = [random.randint(1,1000) for i in range(300000)]

基于反制的解决方案仍然只有一秒钟。

答案 1 :(得分:9)

信不信由你,只做常规循环效率更高:

通过以下方式生成数据:

import random
N = 300000
arr = [random.random() for i in range(N)]
#and random ints are generated: arr = [random.randint(1,1000) for i in range(N)]

常规循环在0.22秒内计算,如果我使用整数,那么它是.12(非常类似于集合)(在2.4 Ghz处理器上)。

di = {}
for item in arr:
    if item in di:
        di[item] += 1
    else:
        di[item] = 1
print (min(di.values()) > 5)

你的版本大于30秒,有或没有整数。

[x for x in arr if arr.count(x) > 5]

如果我使用整数,使用集合大约需要0.33秒和.11。

from collections import Counter

counts = Counter(arr)
print(all(c >= 5 for c in counts.values()))

最后,无论是否有整数,这都需要30秒以上:

count = [0]*(max(x_list)+1)
for x in x_list:
    count[x]+=1;
return [index for index, value in enumerate(count) if value >= 5]

答案 2 :(得分:4)

如果您正在寻找更优化的方法,可以使用numpy.unique()方法,这比大型数组(如您正在处理的数组)的python方法要快得多:

import numpy as np
(np.unique(arr, return_counts=True)[1] > 5).all()

同样作为pythonic方式,您可以使用collections.defaultdict(),如下所示:

In [56]: from collections import defaultdict

In [57]: def check_defaultdict(arr):                                   
             di = defaultdict(int)
             for item in arr:
                 di[item] += 1
             return (min(di.values()) > 5)
   ....: 

以下是其他方法的基准:

In [39]: %timeit (np.unique(arr, return_counts=True)[1] > 5).all()
100 loops, best of 3: 18.8 ms per loop

In [58]: %timeit check_defaultdict(arr)
10 loops, best of 3: 46.1 ms per loop
"""
In [42]: def check(arr):
             di = {}
             for item in arr:
                 if item in di:
                    di[item] += 1
                 else:
                    di[item] = 1
             return (min(di.values()) > 5)
   ....:          
"""
In [43]: %timeit check(arr)
10 loops, best of 3: 56.6 ms per loop

In [38]: %timeit all(c >= 5 for c in Counter(arr).values())
10 loops, best of 3: 89.5 ms per loop

答案 3 :(得分:2)

要计算所有元素,您可以执行以下操作:

def atLeastFiveOfEach(x_list):
    count = [0]*(max(x_list)+1)
    for x in x_list:
        count[x]+=1;
    if min(count)<5:
        return False
    return True

然后你有list,count其中count [i]是x_list中i的出现次数。

如果你想要所有这些元素的列表,你可以这样做:

def atLeastFiveOfEach(x_list):
    count = [0]*(max(x_list)+1)
    for x in x_list:
        count[x]+=1;
    return [index for index, value in enumerate(count) if value >= 5]

为了解释一下为什么这么快:

在你的方法中,你选择第一个元素并浏览整个列表,看看有多少元素等于它存在的元素。然后你取第二个元素并再次遍历整个列表。您将在每个元素中浏览整个列表。

另一方面,这种方法只能通过列表一次。这就是为什么它要快得多。

答案 4 :(得分:0)

使用itertools.islice。它仅返回可迭代的选定项目。

from itertools import islice

def has_at_least_n(iterable, item, n=5):
    filter = (i for i in iterable if i == item)
    return next(islice(filter, n-1, None), False)

从Python文档中,以下是itertools.islice

的内容

创建一个从迭代中返回所选元素的迭代器。如果start为非零,则跳过iterable中的元素,直到达到start。之后,连续返回元素,除非将step设置为高于导致跳过项目的步骤。如果stop为None,则迭代继续,直到迭代器耗尽,如果有的话;否则,它停在指定位置。与常规切片不同,islice()不支持start,stop或step的负值。可用于从内部结构已展平的数据中提取相关字段(例如,多行报表可能会在每个第三行列出名称字段)

来自Moses Koledoye的回答here: