Как я могу подсчитать вхождения элемента списка?

1530

Учитывая элемент, как я могу подсчитать его вхождения в списке в Python?

weakish
источник

Ответы:

1853

Если вам нужен только один элемент, используйте countметод:

>>> [1, 2, 3, 4, 1, 4, 1].count(1)
3

Не используйте это, если вы хотите сосчитать несколько предметов. Вызов countв цикле требует отдельного прохода по списку для каждого countвызова, что может иметь катастрофические последствия для производительности. Если вы хотите сосчитать все элементы или даже просто несколько элементов, используйте Counter, как описано в других ответах.

Лукаш
источник
6
mylist = [1,7,7,7,3,9,9,9,7,9,10,0] print sorted(set([i for i in mylist if mylist.count(i)>2]))
cpp-coder
1747

Используйте, Counterесли вы используете Python 2.7 или 3.x, и вы хотите количество вхождений для каждого элемента:

>>> from collections import Counter
>>> z = ['blue', 'red', 'blue', 'yellow', 'blue', 'red']
>>> Counter(z)
Counter({'blue': 3, 'red': 2, 'yellow': 1})
user52028778
источник
2
Я обнаружил, что при использовании этого много (говоря о миллионах строк), это очень медленно из-за своих вызовов isinstance. Поэтому, если вы уверены в данных, с которыми работаете, может быть лучше написать пользовательскую функцию без проверки типа и экземпляра.
Брэм Ванрой
2
@BramVanroy: что isinstanceзвонит? Даже с миллионами строк вызов Counterтребует только одного isinstanceвызова, чтобы проверить, является ли его аргумент отображением. Скорее всего, вы неправильно оценили, что кушает все время.
user2357112 поддерживает Monica
Вы неправильно поняли, что я имел в виду: Counter проверяет типы ваших данных перед созданием Counter. Это занимает относительно много времени, и если вы заранее знаете тип ваших данных. Если вы посмотрите на метод обновления Counter, вы увидите, что он должен пройти три оператора if, прежде чем что-то делать. Если вы часто звоните обновить, это быстро складывается. Если у вас есть контроль над вашими данными, и вы знаете, что ввод будет действительно повторяемым, то вы можете пропустить первые две проверки. Как я уже сказал, я заметил это только при работе с миллионами обновлений, так что это крайний случай.
Брэм Ванрой
2
@BramVanroy: Если вы выполняете миллионы обновлений, а не просто подсчитываете миллионы строк, это другая история. Усилия по оптимизации Counterбыли направлены на подсчет больших итераций, а не на количество итераций. Подсчет итерируемой миллионной строки будет проходить быстрее, Counterчем при ручной реализации. Если вы хотите вызывать updateмного итераций, вы можете ускорить процесс, соединив их в одну итерацию с itertools.chain.
user2357112 поддерживает Monica
262

Подсчет вхождений одного элемента в списке

Для подсчета вхождений только одного элемента списка вы можете использовать count()

>>> l = ["a","b","b"]
>>> l.count("a")
1
>>> l.count("b")
2

Подсчет вхождений всех элементов в списке также называется «подсчетом» списка или созданием счетчика.

Подсчет всех предметов с помощью count ()

Для подсчета вхождений предметов в lодин можно просто использовать понимание списка и count()метод

[[x,l.count(x)] for x in set(l)]

(или аналогично со словарем dict((x,l.count(x)) for x in set(l)))

Пример:

>>> l = ["a","b","b"]
>>> [[x,l.count(x)] for x in set(l)]
[['a', 1], ['b', 2]]
>>> dict((x,l.count(x)) for x in set(l))
{'a': 1, 'b': 2}

Подсчет всех предметов с помощью счетчика ()

Кроме того, есть более быстрый Counterкласс из collectionsбиблиотеки

Counter(l)

Пример:

>>> l = ["a","b","b"]
>>> from collections import Counter
>>> Counter(l)
Counter({'b': 2, 'a': 1})

Насколько быстрее счетчик?

Я проверил, насколько быстрее Counterдля подсчета списков. Я опробовал оба метода с несколькими значениями, nи оказалось, что Counterбыстрее с постоянным коэффициентом примерно 2.

Вот скрипт, который я использовал:

from __future__ import print_function
import timeit

t1=timeit.Timer('Counter(l)', \
                'import random;import string;from collections import Counter;n=1000;l=[random.choice(string.ascii_letters) for x in range(n)]'
                )

t2=timeit.Timer('[[x,l.count(x)] for x in set(l)]',
                'import random;import string;n=1000;l=[random.choice(string.ascii_letters) for x in range(n)]'
                )

print("Counter(): ", t1.repeat(repeat=3,number=10000))
print("count():   ", t2.repeat(repeat=3,number=10000)

И вывод:

Counter():  [0.46062711701961234, 0.4022796869976446, 0.3974247490405105]
count():    [7.779430688009597, 7.962715800967999, 8.420845870045014]
user2314737
источник
32
Counterэто намного быстрее для больших списков. Метод понимания списка O (n ^ 2), Counterдолжен быть O (n).
фхучо
20
Счетчик не быстрее в 2 раза, Счетчик быстрее в n (O (n ^ 2) против O (n)).
Мартин Питерс
Я обнаружил, что при использовании этого много (говоря о миллионах строк), это очень медленно из-за своих вызовов isinstance. Поэтому, если вы уверены в данных, с которыми работаете, может быть лучше написать пользовательскую функцию без проверки типа и экземпляра.
Брэм Ванрой
66

Другой способ получить количество вхождений каждого элемента в словаре:

dict((i, a.count(i)) for i in a)
tj80
источник
49
это похоже на одну из конструкций, которые я часто придумываю в пылу битвы, но она будет проходить через len (a) раз, что означает квадратичную сложность во время выполнения (поскольку каждый прогон снова зависит от len (a)).
Nicolas78
5
будет dict ((i, a.count (i)) для i в множестве (a)) быть более правильным и более быстрым?
hugo24
6
@ hugo24: Немного, но в худшем случае он не будет асимптотически быстрее; это займет n * (number of different items)операции, не считая время, необходимое для создания набора. Использование collections.Counterдействительно намного лучше.
Климент
очень поздно для вечеринки, но не следуя следующему коду, выдает ошибку, если список содержит более одного экземпляра i, потому что он попытается ввести несколько ключей одинакового значения в словаре. dict((i, a.count(i)) for i in a)
rp1
45

Учитывая элемент, как я могу подсчитать его вхождения в списке в Python?

Вот список примеров:

>>> l = list('aaaaabbbbcccdde')
>>> l
['a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'e']

list.count

Есть list.countметод

>>> l.count('b')
4

Это прекрасно работает для любого списка. У кортежей также есть этот метод:

>>> t = tuple('aabbbffffff')
>>> t
('a', 'a', 'b', 'b', 'b', 'f', 'f', 'f', 'f', 'f', 'f')
>>> t.count('f')
6

collections.Counter

А потом есть коллекции. Счетчик. Вы можете сбросить любую итерацию в счетчик, а не просто в список, и счетчик сохранит структуру данных счетчиков элементов.

Применение:

>>> from collections import Counter
>>> c = Counter(l)
>>> c['b']
4

Счетчики основаны на словарях Python, их ключи являются элементами, поэтому ключи должны быть хэшируемыми. Они в основном похожи на наборы, которые допускают избыточные элементы в них.

Дальнейшее использование collections.Counter

Вы можете добавить или вычесть с помощью итераций из вашего счетчика:

>>> c.update(list('bbb'))
>>> c['b']
7
>>> c.subtract(list('bbb'))
>>> c['b']
4

И вы можете выполнять множественные операции со счетчиком:

>>> c2 = Counter(list('aabbxyz'))
>>> c - c2                   # set difference
Counter({'a': 3, 'c': 3, 'b': 2, 'd': 2, 'e': 1})
>>> c + c2                   # addition of all elements
Counter({'a': 7, 'b': 6, 'c': 3, 'd': 2, 'e': 1, 'y': 1, 'x': 1, 'z': 1})
>>> c | c2                   # set union
Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1, 'y': 1, 'x': 1, 'z': 1})
>>> c & c2                   # set intersection
Counter({'a': 2, 'b': 2})

Почему не панды?

Другой ответ предполагает:

Почему бы не использовать панд?

Панды - это обычная библиотека, но ее нет в стандартной библиотеке. Добавление его в качестве требования нетривиально.

Для этого варианта использования есть встроенные решения как в самом объекте списка, так и в стандартной библиотеке.

Если ваш проект еще не требует панды, было бы глупо сделать это требованием только для этой функциональности.

Аарон Холл
источник
4
Хотя «почему бы не Pandas» является уместным, оно, вероятно, должно сопровождаться «когда использовать NumPy», то есть для больших числовых массивов. Решающим фактором являются не только ограничения проекта, но и эффективность использования памяти с NumPy, которая проявляется при работе с большими данными.
19
Спасибо за упоминание Pandas / etc как серьезной зависимости. Некоторые из этих пакетов имеют негативные побочные эффекты. Таким образом, добавление этих активов для тривиальных нужд может стоить много времени и долларов. Лично я испытал, что Numpy и SciPi добавили 30 минут к нашему конвейеру CI, и потребовались дни, чтобы правильно кэшировать пакеты. Большие пакеты, но иногда есть скрытые расходы. + +1
Марк
36

Я сравнил все предложенные решения (и несколько новых) с perfplot ( мой небольшой проект).

Считая один предмета

Для достаточно больших массивов получается, что

numpy.sum(numpy.array(a) == 1) 

немного быстрее, чем другие решения.

введите описание изображения здесь

Считать все предметов

Как установлено ранее ,

numpy.bincount(a)

это то, что вы хотите.

введите описание изображения здесь


Код для воспроизведения сюжетов:

from collections import Counter
from collections import defaultdict
import numpy
import operator
import pandas
import perfplot


def counter(a):
    return Counter(a)


def count(a):
    return dict((i, a.count(i)) for i in set(a))


def bincount(a):
    return numpy.bincount(a)


def pandas_value_counts(a):
    return pandas.Series(a).value_counts()


def occur_dict(a):
    d = {}
    for i in a:
        if i in d:
            d[i] = d[i]+1
        else:
            d[i] = 1
    return d


def count_unsorted_list_items(items):
    counts = defaultdict(int)
    for item in items:
        counts[item] += 1
    return dict(counts)


def operator_countof(a):
    return dict((i, operator.countOf(a, i)) for i in set(a))


perfplot.show(
    setup=lambda n: list(numpy.random.randint(0, 100, n)),
    n_range=[2**k for k in range(20)],
    kernels=[
        counter, count, bincount, pandas_value_counts, occur_dict,
        count_unsorted_list_items, operator_countof
        ],
    equality_check=None,
    logx=True,
    logy=True,
    )

2.

from collections import Counter
from collections import defaultdict
import numpy
import operator
import pandas
import perfplot


def counter(a):
    return Counter(a)


def count(a):
    return dict((i, a.count(i)) for i in set(a))


def bincount(a):
    return numpy.bincount(a)


def pandas_value_counts(a):
    return pandas.Series(a).value_counts()


def occur_dict(a):
    d = {}
    for i in a:
        if i in d:
            d[i] = d[i]+1
        else:
            d[i] = 1
    return d


def count_unsorted_list_items(items):
    counts = defaultdict(int)
    for item in items:
        counts[item] += 1
    return dict(counts)


def operator_countof(a):
    return dict((i, operator.countOf(a, i)) for i in set(a))


perfplot.show(
    setup=lambda n: list(numpy.random.randint(0, 100, n)),
    n_range=[2**k for k in range(20)],
    kernels=[
        counter, count, bincount, pandas_value_counts, occur_dict,
        count_unsorted_list_items, operator_countof
        ],
    equality_check=None,
    logx=True,
    logy=True,
    )
Нико Шлёмер
источник
7
numpy.bincount () будет работать только для списков с элементами int.
Мукаррам Паша
35

Если вы хотите посчитать все значения одновременно, вы можете сделать это очень быстро, используя числовые массивы и bincountследующим образом

import numpy as np
a = np.array([1, 2, 3, 4, 1, 4, 1])
np.bincount(a)

который дает

>>> array([0, 3, 1, 1, 2])
flonk
источник
19

Если вы можете использовать pandas, то value_countsесть для спасения.

>>> import pandas as pd
>>> a = [1, 2, 3, 4, 1, 4, 1]
>>> pd.Series(a).value_counts()
1    3
4    2
3    1
2    1
dtype: int64

Он также автоматически сортирует результат по частоте.

Если вы хотите, чтобы результат был в списке, выполните следующие действия.

>>> pd.Series(a).value_counts().reset_index().values.tolist()
[[1, 3], [4, 2], [3, 1], [2, 1]]
Тирупати Тангавель
источник
Однако у pandas много накладных расходов, поэтому это самое медленное решение с небольшими объемами данных. stackoverflow.com/a/46195192/125507
эндолиты
14

Почему бы не использовать панд?

import pandas as pd

l = ['a', 'b', 'c', 'd', 'a', 'd', 'a']

# converting the list to a Series and counting the values
my_count = pd.Series(l).value_counts()
my_count

Вывод:

a    3
d    2
b    1
c    1
dtype: int64

Если вы ищете отсчет определенного элемента, скажем , попробуйте:

my_count['a']

Вывод:

3
шореш
источник
13

У меня была эта проблема сегодня, и я нашел собственное решение, прежде чем я решил проверить SO. Эта:

dict((i,a.count(i)) for i in a)

действительно очень медленно для больших списков. Мое решение

def occurDict(items):
    d = {}
    for i in items:
        if i in d:
            d[i] = d[i]+1
        else:
            d[i] = 1
return d

на самом деле немного быстрее, чем решение Counter, по крайней мере для Python 2.7.

D Blanc
источник
1
Счетчик сортирует записи, а ваша - нет, следовательно, разница в скорости (Истинно на момент написания, не уверен, было ли это, когда вы писали ответ. Тем не менее, это может быть актуально для прокрутки вниз).
chaosflaws
3
Счетчик в Python 2 был немного медленным, да. Однако он использует C-оптимизированный код для подсчета в Python 3, и теперь с легкостью обходит ваш цикл.
Мартин Питерс
12
# Python >= 2.6 (defaultdict) && < 2.7 (Counter, OrderedDict)
from collections import defaultdict
def count_unsorted_list_items(items):
    """
    :param items: iterable of hashable items to count
    :type items: iterable

    :returns: dict of counts like Py2.7 Counter
    :rtype: dict
    """
    counts = defaultdict(int)
    for item in items:
        counts[item] += 1
    return dict(counts)


# Python >= 2.2 (generators)
def count_sorted_list_items(items):
    """
    :param items: sorted iterable of items to count
    :type items: sorted iterable

    :returns: generator of (item, count) tuples
    :rtype: generator
    """
    if not items:
        return
    elif len(items) == 1:
        yield (items[0], 1)
        return
    prev_item = items[0]
    count = 1
    for item in items[1:]:
        if prev_item == item:
            count += 1
        else:
            yield (prev_item, count)
            count = 1
            prev_item = item
    yield (item, count)
    return


import unittest
class TestListCounters(unittest.TestCase):
    def test_count_unsorted_list_items(self):
        D = (
            ([], []),
            ([2], [(2,1)]),
            ([2,2], [(2,2)]),
            ([2,2,2,2,3,3,5,5], [(2,4), (3,2), (5,2)]),
            )
        for inp, exp_outp in D:
            counts = count_unsorted_list_items(inp) 
            print inp, exp_outp, counts
            self.assertEqual(counts, dict( exp_outp ))

        inp, exp_outp = UNSORTED_WIN = ([2,2,4,2], [(2,3), (4,1)])
        self.assertEqual(dict( exp_outp ), count_unsorted_list_items(inp) )


    def test_count_sorted_list_items(self):
        D = (
            ([], []),
            ([2], [(2,1)]),
            ([2,2], [(2,2)]),
            ([2,2,2,2,3,3,5,5], [(2,4), (3,2), (5,2)]),
            )
        for inp, exp_outp in D:
            counts = list( count_sorted_list_items(inp) )
            print inp, exp_outp, counts
            self.assertEqual(counts, exp_outp)

        inp, exp_outp = UNSORTED_FAIL = ([2,2,4,2], [(2,3), (4,1)])
        self.assertEqual(exp_outp, list( count_sorted_list_items(inp) ))
        # ... [(2,2), (4,1), (2,1)]
Уэс Тернер
источник
2
@plaes: как так? Если под «enterprisey» вы подразумеваете «документированный» при подготовке к аннотациям Py3k, я согласен.
Уэс Тернер
1
Это отличный пример, так как я разрабатываю в основном в 2.7, но мне нужно иметь пути миграции до 2.4.
Адам Льюис
9

Ниже приведены три решения:

Самый быстрый использует цикл for и хранит его в Dict.

import time
from collections import Counter


def countElement(a):
    g = {}
    for i in a:
        if i in g: 
            g[i] +=1
        else: 
            g[i] =1
    return g


z = [1,1,1,1,2,2,2,2,3,3,4,5,5,234,23,3,12,3,123,12,31,23,13,2,4,23,42,42,34,234,23,42,34,23,423,42,34,23,423,4,234,23,42,34,23,4,23,423,4,23,4]


#Solution 1 - Faster
st = time.monotonic()
for i in range(1000000):
    b = countElement(z)
et = time.monotonic()
print(b)
print('Simple for loop and storing it in dict - Duration: {}'.format(et - st))

#Solution 2 - Fast
st = time.monotonic()
for i in range(1000000):
    a = Counter(z)
et = time.monotonic()
print (a)
print('Using collections.Counter - Duration: {}'.format(et - st))

#Solution 3 - Slow
st = time.monotonic()
for i in range(1000000):
    g = dict([(i, z.count(i)) for i in set(z)])
et = time.monotonic()
print(g)
print('Using list comprehension - Duration: {}'.format(et - st))

Результат

#Solution 1 - Faster
{1: 4, 2: 5, 3: 4, 4: 6, 5: 2, 234: 3, 23: 10, 12: 2, 123: 1, 31: 1, 13: 1, 42: 5, 34: 4, 423: 3}
Simple for loop and storing it in dict - Duration: 12.032000000000153
#Solution 2 - Fast
Counter({23: 10, 4: 6, 2: 5, 42: 5, 1: 4, 3: 4, 34: 4, 234: 3, 423: 3, 5: 2, 12: 2, 123: 1, 31: 1, 13: 1})
Using collections.Counter - Duration: 15.889999999999418
#Solution 3 - Slow
{1: 4, 2: 5, 3: 4, 4: 6, 5: 2, 34: 4, 423: 3, 234: 3, 42: 5, 12: 2, 13: 1, 23: 10, 123: 1, 31: 1}
Using list comprehension - Duration: 33.0
Акаш Суэйн
источник
9

Подсчет всех элементов с itertools.groupby()

Другая возможность получить количество всех элементов в списке может быть с помощью itertools.groupby() .

С «дубликатами»

from itertools import groupby

L = ['a', 'a', 'a', 't', 'q', 'a', 'd', 'a', 'd', 'c']  # Input list

counts = [(i, len(list(c))) for i,c in groupby(L)]      # Create value-count pairs as list of tuples 
print(counts)

Возвращает

[('a', 3), ('t', 1), ('q', 1), ('a', 1), ('d', 1), ('a', 1), ('d', 1), ('c', 1)]

Обратите внимание, как он объединил первые три aв качестве первой группы, в то время как другие группы aприсутствуют ниже по списку. Это происходит потому, что список ввода Lне был отсортирован. Иногда это может быть полезным, если группы фактически должны быть отдельными.

С уникальным количеством

Если требуется уникальное количество групп, просто отсортируйте входной список:

counts = [(i, len(list(c))) for i,c in groupby(sorted(L))]
print(counts)

Возвращает

[('a', 5), ('c', 1), ('d', 2), ('q', 1), ('t', 1)]

Примечание. Для создания уникальных подсчетов многие другие ответы предоставляют более простой и читаемый код по сравнению с groupbyрешением. Но здесь показано, чтобы провести параллель с примером дублирования счета.

Тим Сков Якобсен
источник
7

Было предложено использовать bincount numpy , однако он работает только для 1d массивов с неотрицательными целыми числами . Кроме того, результирующий массив может сбивать с толку (он содержит вхождения целых чисел от минимального до максимального в исходном списке и устанавливает 0 отсутствующих целых чисел).

Лучший способ сделать это с помощью numpy - использовать уникальную функцию с атрибутом return_countsTrue. Он возвращает кортеж с массивом уникальных значений и массивом экземпляров каждого уникального значения.

# a = [1, 1, 0, 2, 1, 0, 3, 3]
a_uniq, counts = np.unique(a, return_counts=True)  # array([0, 1, 2, 3]), array([2, 3, 1, 2]

и тогда мы можем соединить их как

dict(zip(a_uniq, counts))  # {0: 2, 1: 3, 2: 1, 3: 2}

Он также работает с другими типами данных и «2d списками», например

>>> a = [['a', 'b', 'b', 'b'], ['a', 'c', 'c', 'a']]
>>> dict(zip(*np.unique(a, return_counts=True)))
{'a': 3, 'b': 3, 'c': 2}
X Æ A-12
источник
6

Для подсчета количества различных элементов, имеющих общий тип:

li = ['A0','c5','A8','A2','A5','c2','A3','A9']

print sum(1 for el in li if el[0]=='A' and el[1] in '01234')

дает

3 не 6

Eyquem
источник
4

Хотя это очень старый вопрос, но так как я не нашел один лайнер, я сделал один.

# original numbers in list
l = [1, 2, 2, 3, 3, 3, 4]

# empty dictionary to hold pair of number and its count
d = {}

# loop through all elements and store count
[ d.update( {i:d.get(i, 0)+1} ) for i in l ]

print(d)
Суровая Гундеча
источник
Не используйте списки для побочных эффектов. Смотрите: это Pythonic, чтобы использовать списочные понимания только для побочных эффектов?
Георгий
3

Вы также можете использовать countOfметод встроенного модуля operator.

>>> import operator
>>> operator.countOf([1, 2, 3, 4, 1, 4, 1], 1)
3
vishes_shell
источник
1
Как countOfэто реализовано? Как это соотносится с более очевидным list.count(что выгодно от реализации C)? Есть ли преимущества?
Chris_Rands
2

Может быть не самым эффективным, требуется дополнительный проход для удаления дубликатов.

Функциональная реализация:

arr = np.array(['a','a','b','b','b','c'])
print(set(map(lambda x  : (x , list(arr).count(x)) , arr)))

возвращает:

{('c', 1), ('b', 3), ('a', 2)}

или вернуть как dict:

print(dict(map(lambda x  : (x , list(arr).count(x)) , arr)))

возвращает:

{'b': 3, 'c': 1, 'a': 2}
голубое небо
источник
1
sum([1 for elem in <yourlist> if elem==<your_value>])

Это вернет количество вхождений your_value

whackamadoodle3000
источник
1

Я бы использовал filter(), возьмите пример Лукаша:

>>> lst = [1, 2, 3, 4, 1, 4, 1]
>>> len(filter(lambda x: x==1, lst))
3
IPython
источник
0

если вы хотите количество вхождений для конкретного элемента:

>>> from collections import Counter
>>> z = ['blue', 'red', 'blue', 'yellow', 'blue', 'red']
>>> single_occurrences = Counter(z)
>>> print(single_occurrences.get("blue"))
3
>>> print(single_occurrences.values())
dict_values([3, 2, 1])
измерение
источник
-1
def countfrequncyinarray(arr1):
    r=len(arr1)
    return {i:arr1.count(i) for i in range(1,r+1)}
arr1=[4,4,4,4]
a=countfrequncyinarray(arr1)
print(a)
Рави Танвар
источник
3
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, почему и / или как этот код отвечает на вопрос, повышает его долгосрочную ценность.
Алекс Рябов
-1
l2=[1,"feto",["feto",1,["feto"]],['feto',[1,2,3,['feto']]]]
count=0
 def Test(l):   
        global count 
        if len(l)==0:
             return count
        count=l.count("feto")
        for i in l:
             if type(i) is list:
                count+=Test(i)
        return count   
    print(Test(l2))

это будет рекурсивный подсчет или поиск элемента в списке, даже если он в списке списков

Мохамед Фаталла
источник
я не знаю, почему кто-то просто отрицает голос за ответ, и он полностью полезен
Мохамед Фаталла