numpy: tần số hiệu quả nhất tính cho các giá trị duy nhất trong một mảng


244

Trong numpy/ scipy, có một cách nào hiệu quả để lấy số lượng tần số cho các giá trị duy nhất trong một mảng không?

Một cái gì đó dọc theo những dòng này:

x = array( [1,1,1,2,2,2,5,25,1,1] )
y = freq_count( x )
print y

>> [[1, 5], [2,3], [5,1], [25,1]]

(Đối với bạn, người dùng R ngoài kia, về cơ bản tôi đang tìm kiếm table()chức năng)


5
collections.Counter(x)đủ không?
pylang

1
Sẽ tốt hơn nếu tôi đánh dấu vào câu trả lời này là chính xác cho câu hỏi của bạn: stackoverflow.com/a/25943480/9024698 .
Bị ruồng bỏ

Bộ sưu tập. Cuộc gặp gỡ khá chậm. Xem bài đăng của tôi: stackoverflow.com/questions/41594940/ Cách
Sembei Norimaki

Câu trả lời:


161

Hãy xem np.bincount:

http://docs.scipy.org/doc/numpy/reference/generated/numpy.bincount.html

import numpy as np
x = np.array([1,1,1,2,2,2,5,25,1,1])
y = np.bincount(x)
ii = np.nonzero(y)[0]

Và sau đó:

zip(ii,y[ii]) 
# [(1, 5), (2, 3), (5, 1), (25, 1)]

hoặc là:

np.vstack((ii,y[ii])).T
# array([[ 1,  5],
         [ 2,  3],
         [ 5,  1],
         [25,  1]])

hoặc tuy nhiên bạn muốn kết hợp số lượng và các giá trị duy nhất.


42
Xin chào, Điều này sẽ không hoạt động nếu các phần tử của x có một dtype khác với int.
Manoj

7
Nó sẽ không hoạt động nếu chúng là bất cứ thứ gì khác ngoài ints không âm và sẽ rất không hiệu quả nếu các int được đặt cách nhau.
Erik

Với phiên bản numpy 1.10 tôi thấy rằng, để đếm số nguyên, nó nhanh hơn khoảng 6 lần so với np.unique. Ngoài ra, lưu ý rằng nó cũng tính ints âm, nếu tham số đúng được đưa ra.
Jihun

@Manoj: Các phần tử của tôi x là mảng. Tôi đang thử nghiệm giải pháp của jme.
Catalina Chircu

508

Kể từ Numpy 1.9, phương pháp dễ nhất và nhanh nhất là sử dụng đơn giản numpy.unique, giờ đây có một return_countsđối số từ khóa:

import numpy as np

x = np.array([1,1,1,2,2,2,5,25,1,1])
unique, counts = np.unique(x, return_counts=True)

print np.asarray((unique, counts)).T

Cung cấp cho:

 [[ 1  5]
  [ 2  3]
  [ 5  1]
  [25  1]]

So sánh nhanh với scipy.stats.itemfreq:

In [4]: x = np.random.random_integers(0,100,1e6)

In [5]: %timeit unique, counts = np.unique(x, return_counts=True)
10 loops, best of 3: 31.5 ms per loop

In [6]: %timeit scipy.stats.itemfreq(x)
10 loops, best of 3: 170 ms per loop

22
Cảm ơn đã cập nhật! Đây là bây giờ, IMO, câu trả lời chính xác.
Erve1879

1
BAM! đây là lý do tại sao chúng tôi cập nhật ... khi chúng tôi tìm thấy câu trả lời như thế này. Numpy dài 1.8. Làm thế nào chúng ta có thể đưa điều này lên đầu danh sách?
1269942

Nếu bạn gặp lỗi: TypeError: unique () có một đối số từ khóa không mong muốn 'return_counts', chỉ cần làm: unique, Counts = np.unique (x, True)
NumeSanguis

3
@NumeSanguis Bạn đang sử dụng phiên bản nào của numpy? Trước v1.9, return_countsđối số từ khóa không tồn tại, điều này có thể giải thích ngoại lệ. Trong trường hợp đó, các tài liệu cho thấy np.unique(x, True)tương đương với np.unique(x, return_index=True), không trả về số lượng.
jme

1
Trong các phiên bản cũ hơn, thành ngữ điển hình để có được điều tương tự là unique, idx = np.unique(x, return_inverse=True); counts = np.bincount(idx). Khi tính năng này được thêm vào (xem tại đây ), một số thử nghiệm không chính thức đã sử dụng return_countsxung nhịp nhanh hơn 5x.
Jaime

133

Cập nhật: Phương pháp được đề cập trong câu trả lời ban đầu không được dùng nữa, chúng ta nên sử dụng cách mới thay thế:

>>> import numpy as np
>>> x = [1,1,1,2,2,2,5,25,1,1]
>>> np.array(np.unique(x, return_counts=True)).T
    array([[ 1,  5],
           [ 2,  3],
           [ 5,  1],
           [25,  1]])

Câu trả lời gốc:

bạn có thể sử dụng scipy.stats.itemfreq

>>> from scipy.stats import itemfreq
>>> x = [1,1,1,2,2,2,5,25,1,1]
>>> itemfreq(x)
/usr/local/bin/python:1: DeprecationWarning: `itemfreq` is deprecated! `itemfreq` is deprecated and will be removed in a future version. Use instead `np.unique(..., return_counts=True)`
array([[  1.,   5.],
       [  2.,   3.],
       [  5.,   1.],
       [ 25.,   1.]])

1
Có vẻ như cách tiếp cận pythonic nhất cho đến nay. Ngoài ra, tôi gặp phải các vấn đề với "vấn đề quá sâu đối với mảng mong muốn" với np.bincount trên ma trận 100k x 100k.
metasequoia

1
Tôi thay vào đó đề nghị người đặt câu hỏi ban đầu thay đổi câu trả lời bị mắc kẹt từ câu hỏi đầu tiên sang câu hỏi này, để tăng khả năng hiển thị của nó
khôn ngoan

Mặc dù vậy, nó chậm cho các phiên bản trước 0.14.
Jason S

lưu ý rằng nếu mảng chứa đầy chuỗi, cả hai phần tử trong mỗi mục được trả về cũng là chuỗi.
1269942

Có vẻ như itemfreq đã không được chấp nhận
Terence Parr

48

Tôi cũng quan tâm đến điều này, vì vậy tôi đã làm một so sánh hiệu suất nhỏ (sử dụng perfplot , một dự án thú cưng của tôi). Kết quả:

y = np.bincount(a)
ii = np.nonzero(y)[0]
out = np.vstack((ii, y[ii])).T

cho đến nay là nhanh nhất. (Lưu ý chia tỷ lệ log.)

nhập mô tả hình ảnh ở đây


Mã để tạo cốt truyện:

import numpy as np
import pandas as pd
import perfplot
from scipy.stats import itemfreq


def bincount(a):
    y = np.bincount(a)
    ii = np.nonzero(y)[0]
    return np.vstack((ii, y[ii])).T


def unique(a):
    unique, counts = np.unique(a, return_counts=True)
    return np.asarray((unique, counts)).T


def unique_count(a):
    unique, inverse = np.unique(a, return_inverse=True)
    count = np.zeros(len(unique), np.int)
    np.add.at(count, inverse, 1)
    return np.vstack((unique, count)).T


def pandas_value_counts(a):
    out = pd.value_counts(pd.Series(a))
    out.sort_index(inplace=True)
    out = np.stack([out.keys().values, out.values]).T
    return out


perfplot.show(
    setup=lambda n: np.random.randint(0, 1000, n),
    kernels=[bincount, unique, itemfreq, unique_count, pandas_value_counts],
    n_range=[2 ** k for k in range(26)],
    logx=True,
    logy=True,
    xlabel="len(a)",
)

1
Cảm ơn đã đăng mã để tạo ra cốt truyện. Không biết về perfplot trước đây. Có vẻ tiện dụng.
ruffsl

Tôi đã có thể chạy mã của bạn bằng cách thêm tùy chọn equality_check=array_sorteqvào perfplot.show(). Điều gây ra lỗi (trong Python 2) là pd.value_counts(ngay cả với sort = false).
dùng2314737

33

Sử dụng mô-đun gấu trúc:

>>> import pandas as pd
>>> import numpy as np
>>> x = np.array([1,1,1,2,2,2,5,25,1,1])
>>> pd.value_counts(x)
1     5
2     3
25    1
5     1
dtype: int64

5
pd.Series () là không cần thiết. Nếu không, ví dụ tốt. Numpy là tốt. Gấu trúc có thể lấy một danh sách đơn giản làm đầu vào.
Yohan Obadia

1
@YohanObadia - tùy thuộc vào kích thước f của mảng, lần đầu tiên chuyển đổi nó thành một chuỗi đã giúp cho thao tác cuối cùng nhanh hơn đối với tôi. Tôi đoán ở khoảng 50.000 giá trị.
n1k31t4

1
Tôi đã chỉnh sửa câu trả lời của mình để xem xét nhận xét có liên quan từ @YohanObadia
ivankeller

19

Đây là giải pháp tổng quát và hiệu quả nhất; ngạc nhiên là nó chưa được đăng.

import numpy as np

def unique_count(a):
    unique, inverse = np.unique(a, return_inverse=True)
    count = np.zeros(len(unique), np.int)
    np.add.at(count, inverse, 1)
    return np.vstack(( unique, count)).T

print unique_count(np.random.randint(-10,10,100))

Không giống như câu trả lời hiện được chấp nhận, nó hoạt động trên bất kỳ kiểu dữ liệu nào có thể sắp xếp (không chỉ ints tích cực) và nó có hiệu suất tối ưu; chi phí đáng kể duy nhất là trong việc phân loại được thực hiện bởi np.unique.


không hoạt động:AttributeError: 'numpy.ufunc' object has no attribute 'at'
PR

Một phương pháp đơn giản hơn sẽ là gọinp.bincount(inverse)
ali_m

15

numpy.bincountcó lẽ là sự lựa chọn tốt nhất Nếu mảng của bạn chứa bất cứ thứ gì ngoài các số nguyên nhỏ dày đặc, có thể hữu ích để bọc nó như thế này:

def count_unique(keys):
    uniq_keys = np.unique(keys)
    bins = uniq_keys.searchsorted(keys)
    return uniq_keys, np.bincount(bins)

Ví dụ:

>>> x = array([1,1,1,2,2,2,5,25,1,1])
>>> count_unique(x)
(array([ 1,  2,  5, 25]), array([5, 3, 1, 1]))

8

Mặc dù nó đã được trả lời, tôi đề nghị một cách tiếp cận khác sử dụng numpy.histogram. Hàm như vậy được đưa ra một chuỗi, nó trả về tần số của các phần tử được nhóm trong các thùng .

Coi chừng : nó hoạt động trong ví dụ này vì số là số nguyên. Nếu họ có số thực, thì giải pháp này sẽ không áp dụng tốt.

>>> from numpy import histogram
>>> y = histogram (x, bins=x.max()-1)
>>> y
(array([5, 3, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       1]),
 array([  1.,   2.,   3.,   4.,   5.,   6.,   7.,   8.,   9.,  10.,  11.,
        12.,  13.,  14.,  15.,  16.,  17.,  18.,  19.,  20.,  21.,  22.,
        23.,  24.,  25.]))

5
import pandas as pd
import numpy as np
x = np.array( [1,1,1,2,2,2,5,25,1,1] )
print(dict(pd.Series(x).value_counts()))

Điều này mang lại cho bạn: {1: 5, 2: 3, 5: 1, 25: 1}


1
collections.Counter(x)cũng cho kết quả tương tự. Tôi tin rằng OP muốn một đầu ra giống với tablechức năng R. Giữ Seriescó thể hữu ích hơn.
pylang

Xin lưu ý rằng sẽ cần phải chuyển sang pd.Series(x).reshape(-1)nếu đó là một mảng nhiều chiều.
natsuapo

4

Để đếm số không nguyên duy nhất - tương tự như câu trả lời của Eelco Hoogendoorn nhưng nhanh hơn đáng kể (hệ số 5 trên máy của tôi), tôi đã sử dụng weave.inlineđể kết hợp numpy.uniquevới một chút mã c;

import numpy as np
from scipy import weave

def count_unique(datain):
  """
  Similar to numpy.unique function for returning unique members of
  data, but also returns their counts
  """
  data = np.sort(datain)
  uniq = np.unique(data)
  nums = np.zeros(uniq.shape, dtype='int')

  code="""
  int i,count,j;
  j=0;
  count=0;
  for(i=1; i<Ndata[0]; i++){
      count++;
      if(data(i) > data(i-1)){
          nums(j) = count;
          count = 0;
          j++;
      }
  }
  // Handle last value
  nums(j) = count+1;
  """
  weave.inline(code,
      ['data', 'nums'],
      extra_compile_args=['-O2'],
      type_converters=weave.converters.blitz)
  return uniq, nums

Thông tin hồ sơ

> %timeit count_unique(data)
> 10000 loops, best of 3: 55.1 µs per loop

numpyPhiên bản thuần túy của Eelco :

> %timeit unique_count(data)
> 1000 loops, best of 3: 284 µs per loop

Ghi chú

Có sự dư thừa ở đây (cũng uniquethực hiện một loại), có nghĩa là mã có thể được tối ưu hóa hơn nữa bằng cách đặt uniquechức năng bên trong vòng lặp mã c.


4

Câu hỏi cũ, nhưng tôi muốn cung cấp giải pháp của riêng mình, hóa ra là nhanh nhất, sử dụng bình thường listthay vì np.arraylàm đầu vào (hoặc chuyển sang danh sách trước tiên), dựa trên bài kiểm tra của tôi.

Kiểm tra xem nếu bạn gặp nó là tốt.

def count(a):
    results = {}
    for x in a:
        if x not in results:
            results[x] = 1
        else:
            results[x] += 1
    return results

Ví dụ,

>>>timeit count([1,1,1,2,2,2,5,25,1,1]) would return:

100000 vòng, tốt nhất là 3: 2,26 lượt mỗi vòng

>>>timeit count(np.array([1,1,1,2,2,2,5,25,1,1]))

100000 vòng lặp, tốt nhất là 3: 8,8.

>>>timeit count(np.array([1,1,1,2,2,2,5,25,1,1]).tolist())

100000 vòng, tốt nhất là 3: 5,85 lượt mỗi vòng

Trong khi câu trả lời được chấp nhận sẽ chậm hơn, và scipy.stats.itemfreqgiải pháp thậm chí còn tồi tệ hơn.


Một thử nghiệm sâu sắc hơn đã không xác nhận kỳ vọng được xây dựng.

from zmq import Stopwatch
aZmqSTOPWATCH = Stopwatch()

aDataSETasARRAY = ( 100 * abs( np.random.randn( 150000 ) ) ).astype( np.int )
aDataSETasLIST  = aDataSETasARRAY.tolist()

import numba
@numba.jit
def numba_bincount( anObject ):
    np.bincount(    anObject )
    return

aZmqSTOPWATCH.start();np.bincount(    aDataSETasARRAY );aZmqSTOPWATCH.stop()
14328L

aZmqSTOPWATCH.start();numba_bincount( aDataSETasARRAY );aZmqSTOPWATCH.stop()
592L

aZmqSTOPWATCH.start();count(          aDataSETasLIST  );aZmqSTOPWATCH.stop()
148609L

Tham chiếu nhận xét bên dưới về bộ đệm và các tác dụng phụ trong RAM khác ảnh hưởng đến một tập dữ liệu nhỏ ồ ạt kết quả kiểm tra lặp đi lặp lại.


Câu trả lời này thực sự tốt, vì nó cho thấy numpykhông nhất thiết phải đi.
Mahdi

@Rain Lee thú vị. Bạn đã xác nhận chéo giả thuyết danh sách cũng trên một số kích thước tập dữ liệu không có bộ nhớ cache chưa? Giả sử 150.000 mục ngẫu nhiên trong cả hai cách biểu diễn và được đo chính xác hơn một chút trong một lần chạy như ví dụ về aZmqStopwatch.start (); Count (aRepftimeation); aZmqStopwatch.stop () ?
user3666197 04/08/2015

Đã thực hiện một số thử nghiệm và có, có sự khác biệt rất lớn trong hiệu suất dữ liệu thực. Việc kiểm tra đòi hỏi một cái nhìn sâu sắc hơn về cơ học bên trong của trăn hơn là chỉ chạy một vòng có tỷ lệ vũ phu và trích dẫn các nano giây trong ống nghiệm không thực tế . Như thử nghiệm - một np.bincount () có thể được thực hiện để xử lý 150,000 mảng trong vòng chưa đầy 600 [chúng tôi] trong khi trên def -ed count () trên một đại diện danh sách trước khi chuyển đổi đó mất hơn 122,000 [chúng ta]
user3666197

Yeah, quy tắc-of-thumb của tôi là NumPy cho bất cứ điều gì có thể xử lý một lượng nhỏ của độ trễ nhưng có tiềm năng rất lớn, danh sách cho các tập dữ liệu nhỏ mà độ trễ rất quan trọng, và tất nhiên thực benchmarking FTW :)
David

1

một số thứ như thế này nên làm điều đó:

#create 100 random numbers
arr = numpy.random.random_integers(0,50,100)

#create a dictionary of the unique values
d = dict([(i,0) for i in numpy.unique(arr)])
for number in arr:
    d[j]+=1   #increment when that value is found

Ngoài ra, bài viết trước về tính hiệu quả các yếu tố độc đáo này có vẻ khá giống với câu hỏi của bạn, trừ khi tôi thiếu một cái gì đó.


Câu hỏi được liên kết tương tự nhau, nhưng có vẻ như anh ta đang làm việc với các loại dữ liệu phức tạp hơn.
Abe

1

đếm tần số đa chiều, tức là đếm mảng.

>>> print(color_array    )
  array([[255, 128, 128],
   [255, 128, 128],
   [255, 128, 128],
   ...,
   [255, 128, 128],
   [255, 128, 128],
   [255, 128, 128]], dtype=uint8)


>>> np.unique(color_array,return_counts=True,axis=0)
  (array([[ 60, 151, 161],
    [ 60, 155, 162],
    [ 60, 159, 163],
    [ 61, 143, 162],
    [ 61, 147, 162],
    [ 61, 162, 163],
    [ 62, 166, 164],
    [ 63, 137, 162],
    [ 63, 169, 164],
   array([     1,      2,      2,      1,      4,      1,      1,      2,
         3,      1,      1,      1,      2,      5,      2,      2,
       898,      1,      1,  

1
import pandas as pd
import numpy as np

print(pd.Series(name_of_array).value_counts())

0
from collections import Counter
x = array( [1,1,1,2,2,2,5,25,1,1] )
mode = counter.most_common(1)[0][0]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.