Подтвердить что ты не робот

Самый быстрый способ проверить, существует ли значение больше, чем X в списке

У меня длинный список (300 000 элементов), и я хочу проверить, что каждый элемент в этом списке существует более 5 раз. Таким образом, самый простой код

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

Однако мне не нужно подсчитывать, как часто появляется x в списке, я могу остановить подсчет, достигнув хотя бы 5 элементов? Мне также не нужно проходить через все элементы в x_list, так как есть вероятность, что я проверил значение x уже ранее при просмотре списка. Любая идея, как получить оптимальную версию для этого кода? Мой вывод должен быть списком с тем же порядком, если это возможно...

4b9b3361

Ответ 1

Вот решение на основе 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)]

Решение на основе счетчика по-прежнему составляет долю секунды.

Ответ 2

Верьте или нет, просто выполнение регулярного цикла намного эффективнее:

Данные генерируются через:

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 секунды, и если я использую ints, то он равен .12 (очень сопоставимый с коллекциями) (на процессоре 2,4 ГГц).

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]

Ответ 3

Если вы ищете более оптимизированный способ, вы можете использовать метод 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

Ответ 4

Чтобы подсчитать все элементы, вы можете сделать что-то вроде этого:

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

Затем у вас есть список, count, где count [i] - количество вхождений я в x_list.

Если вам нужен список всех этих элементов, вы можете сделать следующее:

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.

Этот метод, с другой стороны, только один раз проходит через список. Вот почему он намного быстрее.

Ответ 5

Используйте 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 не равен нулю, элементы из итерируемого пропускаются до тех пор, пока не будет достигнуто начало. После этого элементы возвращаются последовательно, если шаг не установлен выше одного, что приводит к пропуску элементов. Если stop - None, то итерация продолжается до тех пор, пока итератор не будет исчерпан, если вообще; в противном случае он останавливается в указанной позиции. В отличие от регулярного среза, islice() не поддерживает отрицательные значения для начала, остановки или шага. Может использоваться для извлечения связанных полей из данных, где внутренняя структура была сглажена (например, многострочный отчет может отображать поле имени на каждой третьей строке)

От Моисея Коледоя ответьте здесь: