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


376

В Python у меня есть ndarray, y который печатается какarray([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])

Я пытаюсь подсчитать, сколько 0и сколько 1s в этом массиве.

Но когда я печатаю y.count(0)или y.count(1), это говорит

numpy.ndarray объект не имеет атрибута count

Что мне делать?


8
Вы не можете использовать функцию суммы и длины, так как у вас есть только тузы и нули?
codingEnthusiast

В этом случае также можно просто использовать numpy.count_nonzero.
Мон Х. Нг

Ответы:


610
>>> a = numpy.array([0, 3, 0, 1, 0, 1, 2, 1, 0, 0, 0, 0, 1, 3, 4])
>>> unique, counts = numpy.unique(a, return_counts=True)
>>> dict(zip(unique, counts))
{0: 7, 1: 4, 2: 1, 3: 2, 4: 1}

Не бесшумный способ :

Использование collections.Counter;

>> import collections, numpy

>>> a = numpy.array([0, 3, 0, 1, 0, 1, 2, 1, 0, 0, 0, 0, 1, 3, 4])
>>> collections.Counter(a)
Counter({0: 7, 1: 4, 3: 2, 2: 1, 4: 1})

3
Это было бы `` `уникально, countts = numpy.unique (a, return_counts = True) dict (zip (уникально, рассчитывает))` ``
уничтожение

25
Если вы хотите словарь,dict(zip(*numpy.unique(a, return_counts=True)))
Сеппо Энарви

2
Что делать, если я хочу получить доступ к числу экземпляров каждого уникального элемента массива без присваивания переменной - рассчитывает. Есть намеки на это?
sajis997

У меня та же цель, что и у @ sajis997. Я хочу использовать 'count' в качестве функции агрегирования в groupby
p_sutherland

1
Пробовал использовать оба метода для очень большого массива (~ 30Gb). Метод Numpy исчерпал память, тогда как collections.Counterработал просто отлично
Иван Новиков

252

Как насчет использования numpy.count_nonzero, что-то вроде

>>> import numpy as np
>>> y = np.array([1, 2, 2, 2, 2, 0, 2, 3, 3, 3, 0, 0, 2, 2, 0])

>>> np.count_nonzero(y == 1)
1
>>> np.count_nonzero(y == 2)
7
>>> np.count_nonzero(y == 3)
3

20
Этот ответ кажется лучше, чем тот, у кого больше голосов.
Алекс

1
Я не думаю, что это сработает, numpy.ndarrayкак первоначально просили OP.
LYu

5
@LYu - y является np.ndarray в этом ответе. Кроме того - большинство, если не все функции np.something работают на ndarrays без проблем.
Магнуски

132

Лично я бы пошел на: (y == 0).sum()и(y == 1).sum()

Например

import numpy as np
y = np.array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])
num_zeros = (y == 0).sum()
num_ones = (y == 1).sum()

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

Может быть меньше места, чем numpy.count_nonzero (y == 0), так как он оценивает вектор (y == 0)
Шридхар Тиагараджан

Мне это нравится, потому что похоже на matlab / octavesum( vector==value )
ePi272314

39

Для вашего случая вы также можете посмотреть в numpy.bincount

In [56]: a = np.array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])

In [57]: np.bincount(a)
Out[57]: array([8, 4])  #count of zeros is at index 0 : 8
                        #count of ones is at index 1 : 4

Этот код может быть одним из самых быстрых решений для больших массивов, которые я экспериментировал. Получение результата в виде списка также является бонусом. Thanx!
Янгсуп Ким

И если 'a' является n-мерным массивом, мы можем просто использовать: np.bincount (np.reshape (a, a.size))
Ари

21

Преобразовать ваш массив yв список, lа затем сделать l.count(1)иl.count(0)

>>> y = numpy.array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])
>>> l = list(y)
>>> l.count(1)
4
>>> l.count(0)
8 

19
y = np.array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])

Если вы знаете, что они справедливы 0и 1:

np.sum(y)

дает вам количество единиц. np.sum(1-y)дает нули.

Для небольшой общности, если вы хотите считать, 0а не ноль (но, возможно, 2 или 3):

np.count_nonzero(y)

дает число ненулевое.

Но если вам нужно что-то более сложное, я не думаю, что numpy предоставит хороший countвариант. В этом случае перейдите в коллекции:

import collections
collections.Counter(y)
> Counter({0: 8, 1: 4})

Это ведет себя как диктат

collections.Counter(y)[0]
> 8

13

Если вы точно знаете, какой номер вы ищете, вы можете использовать следующее;

lst = np.array([1,1,2,3,3,6,6,6,3,2,1])
(lst == 2).sum()

возвращает, сколько раз 2 произошло в вашем массиве.


8

Честно говоря, мне проще всего преобразовать его в серию панд или DataFrame:

import pandas as pd
import numpy as np

df = pd.DataFrame({'data':np.array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])})
print df['data'].value_counts()

Или этот хороший однострочный текст, предложенный Робертом Мюилом:

pd.Series([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1]).value_counts()

4
Просто примечание: не нужен DataFrame или NumPy, можно перейти непосредственно из списка в серии: pd.Series([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1]).value_counts()
Роберт

Круто, это хорошо, один вкладыш. Big up
слова для этого

8

Никто не предложил использовать numpy.bincount(input, minlength)с minlength = np.size(input), но это, кажется, хорошее решение, и, безусловно, самое быстрое :

In [1]: choices = np.random.randint(0, 100, 10000)

In [2]: %timeit [ np.sum(choices == k) for k in range(min(choices), max(choices)+1) ]
100 loops, best of 3: 2.67 ms per loop

In [3]: %timeit np.unique(choices, return_counts=True)
1000 loops, best of 3: 388 µs per loop

In [4]: %timeit np.bincount(choices, minlength=np.size(choices))
100000 loops, best of 3: 16.3 µs per loop

Это безумное ускорение между numpy.unique(x, return_counts=True)и numpy.bincount(x, minlength=np.max(x))!


как это по сравнению с гистограммой?
Джон Ктехик

@johnktejik np.histogramне вычисляет то же самое. Нет смысла сравнивать три подхода, которые я предлагаю, с histogramфункцией, извините.
Næreen

1
@ Næreen bincountработает только для целых чисел, поэтому он работает для задачи ОП, но, возможно, не для общей проблемы, описанной в заголовке. Также вы пытались использовать bincountс массивами с очень большими целыми числами?
Нетленная ночь

@ImperishableNight Нет, я не пробовал с большими целыми, но любой желающий может сделать это и опубликовать свой собственный тест :-)
Næreen

Спасибо за этот недооцененный трюк! На моей машине bincountпримерно в четыре раза быстрее чем unique.
Бьорн Линдквист


6

y.tolist().count(val)

с val 0 или 1

Поскольку список Python имеет встроенную функцию count, преобразование в список перед использованием этой функции является простым решением.


5

Еще одним простым решением может быть использование numpy.count_nonzero () :

import numpy as np
y = np.array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])
y_nonzero_num = np.count_nonzero(y==1)
y_zero_num = np.count_nonzero(y==0)
y_nonzero_num
4
y_zero_num
8

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


5

Для подсчета количества вхождений вы можете использовать np.unique(array, return_counts=True):

In [75]: boo = np.array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])

# use bool value `True` or equivalently `1`
In [77]: uniq, cnts = np.unique(boo, return_counts=1)
In [81]: uniq
Out[81]: array([0, 1])   #unique elements in input array are: 0, 1

In [82]: cnts
Out[82]: array([8, 4])   # 0 occurs 8 times, 1 occurs 4 times

4

Я бы использовал np.where:

how_many_0 = len(np.where(a==0.)[0])
how_many_1 = len(np.where(a==1.)[0])


2

Общий и простой ответ:

numpy.sum(MyArray==x)   # sum of a binary list of the occurence of x (=0 or 1) in MyArray

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

import numpy
MyArray=numpy.array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])  # array we want to search in
x=0   # the value I want to count (can be iterator, in a list, etc.)
numpy.sum(MyArray==0)   # sum of a binary list of the occurence of x in MyArray

Теперь, если MyArray находится в нескольких измерениях, и вы хотите посчитать возникновение распределения значений в строке (= образец в дальнейшем)

MyArray=numpy.array([[6, 1],[4, 5],[0, 7],[5, 1],[2, 5],[1, 2],[3, 2],[0, 2],[2, 5],[5, 1],[3, 0]])
x=numpy.array([5,1])   # the value I want to count (can be iterator, in a list, etc.)
temp = numpy.ascontiguousarray(MyArray).view(numpy.dtype((numpy.void, MyArray.dtype.itemsize * MyArray.shape[1])))  # convert the 2d-array into an array of analyzable patterns
xt=numpy.ascontiguousarray(x).view(numpy.dtype((numpy.void, x.dtype.itemsize * x.shape[0])))  # convert what you search into one analyzable pattern
numpy.sum(temp==xt)  # count of the searched pattern in the list of patterns

2

Вы можете использовать словарное понимание, чтобы создать аккуратную однострочную строку. Больше о понимании словаря можно найти здесь

>>>counts = {int(value): list(y).count(value) for value in set(y)}
>>>print(counts)
{0: 8, 1: 4}

Это создаст словарь со значениями в вашем ndarray в качестве ключей и подсчетом значений в качестве значений для ключей соответственно.

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



1

Это можно легко сделать следующим способом

y = np.array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])
y.tolist().count(1)

1

Поскольку ваш ndarray содержит только 0 и 1, вы можете использовать sum () для получения вхождения 1s и len () - sum () для получения вхождения 0s.

num_of_ones = sum(array)
num_of_zeros = len(array)-sum(array)

1

У вас есть специальный массив только с 1 и 0 здесь. Так что хитрость заключается в использовании

np.mean(x)

который дает вам процент 1 с в вашем массиве. В качестве альтернативы используйте

np.sum(x)
np.sum(1-x)

даст вам абсолютное число 1 и 0 в вашем массиве.


1
dict(zip(*numpy.unique(y, return_counts=True)))

Просто скопировал здесь комментарий Сеппо Энарви, который заслуживает правильного ответа.


0

Это включает еще один шаг, но более гибкое решение, которое также будет работать для 2d-массивов и более сложных фильтров, состоит в создании логической маски и последующем использовании .sum () для маски.

>>>>y = np.array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])
>>>>mask = y == 0
>>>>mask.sum()
8

0

Если вы не хотите использовать numpy или модуль коллекций, вы можете использовать словарь:

d = dict()
a = [0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1]
for item in a:
    try:
        d[item]+=1
    except KeyError:
        d[item]=1

результат:

>>>d
{0: 8, 1: 4}

Конечно, вы также можете использовать оператор if / else. Я думаю, что функция Counter делает почти то же самое, но это более прозрачно.


0

Для общих записей:

x = np.array([11, 2, 3, 5, 3, 2, 16, 10, 10, 3, 11, 4, 5, 16, 3, 11, 4])
n = {i:len([j for j in np.where(x==i)[0]]) for i in set(x)}
ix = {i:[j for j in np.where(x==i)[0]] for i in set(x)}

Будет выводить счет:

{2: 2, 3: 4, 4: 2, 5: 2, 10: 2, 11: 3, 16: 2}

И показатели:

{2: [1, 5],
3: [2, 4, 9, 14],
4: [11, 16],
5: [3, 12],
10: [7, 8],
11: [0, 10, 15],
16: [6, 13]}

0

здесь у меня есть кое-что, благодаря чему вы можете посчитать количество появлений определенного числа: по вашему коду

count_of_zero = лист (у [у == 0]). кол (0)

печать (count_of_zero)

// в соответствии с совпадением будут булевы значения, а в соответствии со значением True будет возвращаться число 0


0

Если вас интересует самое быстрое выполнение, вы заранее знаете, какое значение (а) нужно искать, и ваш массив является 1D, или вы иным образом интересуетесь результатом для уплощенного массива (в этом случае ввод функции должен будь np.flatten(arr)скорее чем просто arr) тогда Нумба твой друг

import numba as nb


@nb.jit
def count_nb(arr, value):
    result = 0
    for x in arr:
        if x == value:
            result += 1
    return result

или для очень больших массивов, где распараллеливание может быть полезным:

@nb.jit(parallel=True)
def count_nbp(arr, value):
    result = 0
    for i in nb.prange(arr.size):
        if arr[i] == value:
            result += 1
    return result

Сравнительный анализ их np.count_nonzero()(который также имеет проблему создания временного массива, которого можно избежать) и np.unique()решения на основе

import numpy as np


def count_np(arr, value):
    return np.count_nonzero(arr == value)
import numpy as np


def count_np2(arr, value):
    uniques, counts = np.unique(a, return_counts=True)
    counter = dict(zip(uniques, counts))
    return counter[value] if value in counter else 0 

для ввода, созданного с помощью:

def gen_input(n, a=0, b=100):
    return np.random.randint(a, b, n)

получены следующие графики (второй ряд графиков - это увеличение при более быстром подходе):

bm_full bm_zoom

Показано, что решение на основе Numba заметно быстрее, чем аналоги NumPy, и для очень больших входов параллельный подход быстрее, чем наивный.


Полный код доступен здесь .


0

Если вы имеете дело с очень большими массивами, возможно использование генераторов. Приятно то, что этот подход хорошо работает как для массивов, так и для списков, и вам не нужно никаких дополнительных пакетов. Кроме того, вы не используете столько памяти.

my_array = np.array([0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1])
sum(1 for val in my_array if val==0)
Out: 8

-1

У Numpy есть модуль для этого. Просто небольшой взлом. Поместите ваш входной массив в качестве бинов.

numpy.histogram(y, bins=y)

На выходе есть 2 массива. Один с самими значениями, другой с соответствующими частотами.


разве «мусорное ведро» не должно быть числом?
Джон Ктехик

1
Да, @johnktejik, ты прав. Этот ответ не работает.
Næreen

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.