numpy: bir dizideki benzersiz değerler için en verimli frekans sayımı


244

numpy/ İçinde scipy, bir dizideki benzersiz değerler için frekans sayımları almanın etkili bir yolu var mı?

Bu çizgiler boyunca bir şey:

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]]

(Sizin için, R kullanıcıları orada, temelde table()işlevi arıyorum )


5
collections.Counter(x)yeterli?
pylang

1
Bu cevabı şimdi sorunuz için doğru olarak işaretlerseniz daha iyi olur: stackoverflow.com/a/25943480/9024698 .
Dışında

Koleksiyonlar. Sayaç oldukça yavaş.
Yazımı görüntüle

Yanıtlar:


161

Şuna bir göz atın 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]

Ve sonra:

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

veya:

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

ancak sayıları ve benzersiz değerleri birleştirmek istersiniz.


42
Merhaba, x öğelerinin int dışında bir türü varsa, bu işe yaramaz.
Manoj

7
Negatif olmayan intlerden başka bir şeylerse işe yaramazlar ve intlar aralıklıysa çok alan verimsiz olur.
Erik

Numpy sürüm 1.10 ile tamsayı saymak için np.unique'den yaklaşık 6 kat daha hızlı olduğunu buldum. Ayrıca, doğru parametreler verilirse negatif ints saydığını da unutmayın.
Jihun

@Manoj: x öğelerim diziler. Ben jme çözümünü test ediyorum.
Catalina Chircu

508

Numpy 1.9'dan itibaren, en kolay ve en hızlı yöntem, numpy.uniqueşimdi bir return_countsanahtar kelime argümanı olan basitçe kullanmaktır :

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

Hangi verir:

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

Şunlarla hızlı bir karşılaştırma 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
Güncelleme için teşekkürler! Bu şimdi, IMO, doğru cevap.
Erve1879

1
BAM! bu yüzden güncelleme yapıyoruz ... böyle cevaplar bulduğumuzda. Çok uzun numpy 1.8. Bunu listenin başına nasıl getirebiliriz?
user1269942

Hatayı alırsanız: TypeError: unique () 'return_counts' beklenmeyen bir anahtar kelime bağımsız değişkenine sahipse, şunu yapın: unique, counts = np.unique (x, True)
NumesSanguis

3
@NumesSanguis Hangi numpy sürümünü kullanıyorsunuz? V1.9'dan önce return_counts, istisnayı açıklayabilecek anahtar kelime argümanı yoktu. Bu durumda dokümanlar , sayıları döndürmeyen np.unique(x, True)eşdeğer olduğunu önerir np.unique(x, return_index=True).
jme

1
Eski numpy sürümlerinde aynı şeyi elde etmek için tipik deyim oldu unique, idx = np.unique(x, return_inverse=True); counts = np.bincount(idx). Bu özellik eklendiğinde ( buraya bakın ) bazı gayri resmi testlerde return_counts5 katın üzerinde saat kullanımı daha hızlıdır.
Jaime

133

Güncelleme: Orijinal yanıtta belirtilen yöntem kullanımdan kaldırıldı, bunun yerine yeni yolu kullanmalıyız:

>>> 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]])

Orijinal cevap:

scipy.stats.itemfreq kullanabilirsiniz

>>> 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
Şimdiye kadarki en pitonik yaklaşım gibi görünüyor. Ayrıca, 100k x 100k matrislerde np.bincount ile "istenen dizi için çok derin nesne" sorunları ile karşılaştım.
metasequoia

1
Ben orijinal soru pozer ilk cevap bu cevap, görünürlüğünü artırmak için değiştirilmesini öneririz
wiswit

Yine de 0.14 öncesi sürümler için yavaş.
Jason S

dizi dizelerle doluysa, döndürülen öğelerin her birindeki her iki öğenin de dize olduğunu unutmayın.
user1269942

Görünüşe göre itemfreq kullanımdan kaldırıldı
Terence Parr

48

Ben de bununla ilgileniyordum, bu yüzden biraz performans karşılaştırması yaptım ( bir evcil hayvan projesi olan perfplot kullanarak ). Sonuç:

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

en hızlısı. (Günlük ölçeklendirmeye dikkat edin.)

resim açıklamasını buraya girin


Grafiği oluşturmak için kod:

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
Arsa oluşturmak için kodu gönderdiğiniz için teşekkür ederiz. Perfplot hakkında daha önce bilmiyordum . Kullanışlı görünüyor.
ruffsl

Ben seçeneği ekleyerek kod çalıştırmasına başardı equality_check=array_sorteqiçinde perfplot.show(). Bir hataya neden olan (Python 2'de) pd.value_counts(sort = False ile bile ) idi .
user2314737

33

Panda modülünü kullanarak:

>>> 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 () gerekli değildir. Aksi takdirde, iyi bir örnek. Numpy de. Pandalar girdi olarak basit bir liste alabilirler.
Yohan Obadia

1
@YohanObadia - dizinin boyutuna bağlı olarak, önce bir seriye dönüştürmek son işlemi benim için daha hızlı hale getirdi. Yaklaşık 50.000 değerin olduğunu tahmin ediyorum.
n1k31t4

1
@YohanObadia
ivankeller'dan

19

Bu, en genel ve en performanslı çözümdür; henüz gönderilmedi şaşırttı.

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))

Şu anda kabul edilen cevabın aksine, sıralanabilir (sadece pozitif ints değil) herhangi bir veri türü üzerinde çalışır ve optimum performansa sahiptir; tek önemli gider np.unique tarafından yapılan sıralamadadır.


çalışmıyor:AttributeError: 'numpy.ufunc' object has no attribute 'at'
PR

Daha basit bir yöntem aramak olacaktırnp.bincount(inverse)
ali_m

15

numpy.bincountmuhtemelen en iyi seçimdir. Diziniz küçük yoğun tamsayıların yanı sıra herhangi bir şey içeriyorsa, bunu şöyle bir şeyle sarmak yararlı olabilir:

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

Örneğin:

>>> 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

Zaten yanıtlanmış olmasına rağmen, yararlanan farklı bir yaklaşım öneriyorum numpy.histogram. Bu tür bir fonksiyon, bir dizi verildiğinde , bidonlarda gruplanmış elemanlarının frekansını döndürür .

Yine de dikkat edin : sayılar tamsayı olduğu için bu örnekte çalışır. Eğer gerçek rakamlar varsa, bu çözüm o kadar güzel olmazdı.

>>> 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()))

Bu size şunu verir: {1: 5, 2: 3, 5: 1, 25: 1}


1
collections.Counter(x)aynı sonucu verir. OP'nin R tablefonksiyonuna benzeyen bir çıktı istediğine inanıyorum . Tutmak Seriesdaha faydalı olabilir.
pylang

pd.Series(x).reshape(-1)Çok boyutlu bir dizi ise aktarımın gerekli olacağını lütfen unutmayın .
natsuapo

4

Benzersiz olmayan tam sayıları saymak için - Eelco Hoogendoorn'un cevabına benzer ancak oldukça daha hızlı (makinemdeki 5 faktörü), biraz c kodu ile weave.inlinebirleşirdim numpy.unique;

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

Profil bilgisi

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

Eelco'nun saf numpyversiyonu:

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

Not

Burada fazlalık var ( uniquebir sıralama da gerçekleştiriyor), yani kodun uniqueişlevsellikleri c-kod döngüsünün içine koyarak daha da optimize edilebilir .


4

Eski soru, ama ben kendi test sunmak için, en hızlı olduğu ortaya çıktı, giriş olarak normal listkullanın np.array(veya öncelikle listeye transfer) kullanın.

Siz de karşılaşırsanız kontrol edin .

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

Örneğin,

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

100000 döngü, en iyisi 3: döngü başına 2,26 µs

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

100000 döngü, en iyisi 3: döngü başına 8,8 µs

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

100000 döngü, en iyisi 3: döngü başına 5,85 µs

Kabul edilen cevap daha yavaş olur ve scipy.stats.itemfreqçözüm daha da kötüdür.


Daha ayrıntılı bir test , formüle edilmiş beklentiyi doğrulamamıştır .

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

Ref. küçük bir veri kümesini büyük ölçüde tekrarlayan test sonuçlarını etkileyen önbellek ve diğer RAM içi yan etkiler hakkında aşağıdaki yorumlar.


Bu cevap gerçekten iyidir, çünkü gösterilmesi numpyzorunlu değildir.
Mehdi

@Rain Lee ilginç. Liste hipotezini önbelleklenemeyen bazı veri kümesi boyutlarında da çapraz doğruladınız mı? Her iki gösterimde de 150.000 rastgele öğe olduğunu varsayalım ve aZmqStopwatch.start () örneğinde olduğu gibi tek seferde biraz daha doğru ölçüldü ; count (aRepresentation); aZmqStopwatch.stop () ?
user3666197

Bazı testler yaptık ve evet, gerçek veri seti performansında büyük farklılıklar var . Test etmek, sadece kaba kuvvet ölçekli döngüler çalıştırmak ve gerçekçi olmayan in vitro nanosaniye alıntı yapmaktan ziyade python iç mekaniği hakkında biraz daha fazla bilgi gerektirir . Test edildiği gibi - önceden dönüştürülmüş bir liste
sunumunda

Evet, benim üstünlüğü başparmak olan numpy çok büyük olması gecikme küçük miktarlarda işlemek ama potansiyeline sahiptir gelebilecek her şey için listeleri gecikme kritik küçük veri setleri için, ve tabii ki bir gerçek benchmarking :) FTW
David

1

böyle bir şey yapmalı:

#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

Ayrıca, benzersiz öğeleri verimli bir şekilde saymakla ilgili bu önceki yazı, bir şey kaçırmadıkça sorunuza oldukça benziyor.


Bağlantılı soru biraz benzer, ancak daha karmaşık veri türleriyle çalışıyor gibi görünüyor.
Abe

1

çok boyutlu frekans sayımı, yani sayım dizileri.

>>> 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]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.