Эффективный способ подсчета уникальных элементов в массиве numpy/scipy в Python

У меня есть scipy массив, например.

a = array([[0, 0, 1], [1, 1, 1], [1, 1, 1], [1, 0, 1]])

Я хочу подсчитать количество вхождений каждого уникального элемента в массиве. Например, для вышеупомянутого массива a, я хочу получить, что есть 1 появление [0, 0, 1], 2 вхождения [1, 1, 1] и 1 появление [1, 0, 1].

Один из способов, который я думал сделать:

from collections import defaultdict
d = defaultdict(int)

for elt in a:
  d[elt] += 1

есть лучший/более эффективный способ?

спасибо.

Ответ 1

Если придерживаться Python 2.7 (или 3.1) не проблема, и любая из этих двух версий Python доступна вам, возможно, новая collections.Counter может быть чем-то для вас, если вы будете придерживаться хешируемых элементов, таких как кортежи:

>>> from collections import Counter
>>> c = Counter([(0,0,1), (1,1,1), (1,1,1), (1,0,1)])
>>> c
Counter({(1, 1, 1): 2, (0, 0, 1): 1, (1, 0, 1): 1})

Тем не менее, я не тестировал эти два подхода.

Ответ 2

Вы можете сортировать массив лексикографически по строкам и искать точки, где изменяются строки:

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

In [2]: b = a[lexsort(a.T)]

In [3]: b
Out[3]: 
array([[0, 0, 1],
       [1, 0, 1],
       [1, 1, 1],
       [1, 1, 1]])

...


In [5]: (b[1:] - b[:-1]).any(-1)
Out[5]: array([ True,  True, False], dtype=bool)

Последний массив говорит, что первые три строки отличаются, а третья строка повторяется дважды.

Для массивов единиц и нулей вы можете кодировать значения:

In [6]: bincount(dot(a, array([4,2,1])))
Out[6]: array([0, 1, 0, 0, 0, 1, 0, 2])

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

Ответ 3

для python 2.6 <

import itertools

data_array = [[0, 0, 1], [1, 1, 1], [1, 1, 1], [1, 0, 1]]

dict_ = {}

for list_, count in itertools.groupby(data_array):
    dict_.update({tuple(list_), len(list(count))})

Ответ 4

Пакет numpy_indexed (отказ от ответственности: я являюсь его автором) предоставляет решение, подобное тому, которое выкладывает патрон; который является красиво векторизованным. Но с проверками, приятным интерфейсом и многими другими полезными функциями:

import numpy_indexed as npi
npi.count(a)