繁体   English   中英

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

[英]Fastest way to check whether a value exists more often than X in a list

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

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

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

这是基于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)]

基于反制的解决方案仍然是一小部分。

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

数据来自:

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]

如果你正在寻找一种更优化的方法,你可以使用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

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

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]

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

在您的方法中,您选择第一个元素并浏览整个列表以查看与其存在的元素相等的元素数量。 然后你取第二个元素并再次遍历整个列表。 您将在FOR EACH元素中浏览整个列表。

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

使用itertools.islice 它仅返回iterable中的选定项。

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的负值。 可用于从内部结构已展平的数据中提取相关字段(例如,多行报表可能会在每个第三行列出名称字段)

来自摩西科莱约耶的回答

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM