Gibt es innumpy
/ - scipy
einen effizienten Weg, Frequenzzählungen für eindeutige Werte in einem Array zu erhalten?
Etwas in diese Richtung:
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]]
(Für Sie, R-Nutzer da draußen, suche ich grundsätzlich die Funktion table()
)
Schau dir np.bincount
an:
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]
Und dann:
Zip(ii,y[ii])
# [(1, 5), (2, 3), (5, 1), (25, 1)]
oder:
np.vstack((ii,y[ii])).T
# array([[ 1, 5],
[ 2, 3],
[ 5, 1],
[25, 1]])
oder wie Sie die Zählwerte und die eindeutigen Werte kombinieren möchten.
Ab Numpy 1.9 ist die einfachste und schnellste Methode die Verwendung von numpy.unique
, das jetzt ein Schlüsselwort return_counts
hat:
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
Was gibt:
[[ 1 5]
[ 2 3]
[ 5 1]
[25 1]]
Ein schneller Vergleich mit 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
Update: Die in der ursprünglichen Antwort erwähnte Methode ist veraltet, wir sollten stattdessen die neue Methode verwenden:
>>> 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]])
Ursprüngliche Antwort:
sie können scipy.stats.itemfreq verwenden.
>>> 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.]])
Ich war auch daran interessiert, also habe ich einen kleinen Leistungsvergleich gemacht (mit perfplot , einem meiner Lieblingsprojekte). Ergebnis:
y = np.bincount(a)
ii = np.nonzero(y)[0]
out = np.vstack((ii, y[ii])).T
ist bei weitem der schnellste. (Beachten Sie die Protokollskalierung.)
Code zum Erzeugen des Diagramms:
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)'
)
Pandas-Modul verwenden:
>>> import pandas as pd
>>> import numpy as np
>>> x = np.array([1,1,1,2,2,2,5,25,1,1])
>>> pd.value_counts(pd.Series(x))
1 5
2 3
25 1
5 1
dtype: int64
Dies ist bei weitem die allgemeinste und leistungsfähigste Lösung; überrascht, dass es noch nicht veröffentlicht wurde.
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))
Im Gegensatz zu der aktuell akzeptierten Antwort funktioniert sie für jeden sortierbaren Datentyp (nicht nur für positive Ints) und bietet eine optimale Leistung. Der einzige bedeutende Aufwand ist die Sortierung durch np.unique.
numpy.bincount
ist wahrscheinlich die beste Wahl. Wenn Ihr Array außer kleinen, dichten Ganzzahlen etwas enthält, kann es nützlich sein, es in etwa wie folgt einzuhüllen:
def count_unique(keys):
uniq_keys = np.unique(keys)
bins = uniq_keys.searchsorted(keys)
return uniq_keys, np.bincount(bins)
Zum Beispiel:
>>> 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]))
Obwohl dies bereits beantwortet wurde, schlage ich einen anderen Ansatz vor, der numpy.histogram
verwendet. Eine solche Funktion gibt bei einer Sequenz die Häufigkeit ihrer Elemente gruppiert in Bins zurück.
Achtung: In diesem Beispiel funktioniert es, weil Zahlen Ganzzahlen sind. Wenn sie reelle Zahlen waren, würde diese Lösung nicht so gut zutreffen.
>>> 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.]))
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()))
Dies gibt Ihnen: {1: 5, 2: 3, 5: 1, 25: 1}
Alte Frage, aber ich möchte meine eigene Lösung bereitstellen, die sich als die schnellste herausstellt. Verwenden Sie normallist
anstelle von np.array
als Eingabe (oder übertragen Sie zuerst auf die Liste), basierend auf meinem Test.
Schau es dir an wenn du es auch findest.
def count(a):
results = {}
for x in a:
if x not in results:
results[x] = 1
else:
results[x] += 1
return results
Zum Beispiel,
>>>timeit count([1,1,1,2,2,2,5,25,1,1]) would return:
100000 Loops, am besten von 3: 2,26 µs pro Loop
>>>timeit count(np.array([1,1,1,2,2,2,5,25,1,1]))
100000 Loops, am besten von 3: 8,8 µs pro Loop
>>>timeit count(np.array([1,1,1,2,2,2,5,25,1,1]).tolist())
100000 Loops, am besten von 3: 5,85 µs pro Loop
Die akzeptierte Antwort wäre zwar langsamer und die scipy.stats.itemfreq
-Lösung ist noch schlimmer.
Eine eingehendere Prüfung bestätigte nicht die formulierte Erwartung.
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. Kommentare zu Cache und anderen In-RAM-Nebeneffekten, die einen kleinen Datensatz massiv repetitive Testergebnisse beeinflussen.
Um - eindeutige Nicht-Ganzzahlen zu zählen ähnlich der Antwort von Eelco Hoogendoorn, aber wesentlich schneller (Faktor 5 auf meinem Rechner), habe ich weave.inline
verwendet, um numpy.unique
mit etwas C-Code zu kombinieren;
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 Information
> %timeit count_unique(data)
> 10000 loops, best of 3: 55.1 µs per loop
Eelcos reine numpy
-Version:
> %timeit unique_count(data)
> 1000 loops, best of 3: 284 µs per loop
Hinweis
Hier gibt es Redundanz (unique
führt auch eine Sortierung durch), was bedeutet, dass der Code möglicherweise weiter optimiert werden könnte, indem die unique
-Funktionalität in die C-Code-Schleife eingefügt wird.
so etwas sollte es tun:
#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
Auch dieser vorherige Post zum Effizientes Zählen einzigartiger Elemente scheint Ihrer Frage ziemlich ähnlich zu sein, es sei denn, mir fehlt etwas.
mehrdimensionale Frequenzzählung, d.h. Arrays zählen.
>>> 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,
pandas als pd importieren
importiere numpy als np
pd.Series (name_of_array) .value_counts ()