簡體   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