2013-05-02 10 views
27

Ich habe ein 2D-Array mit ganzen Zahlen (sowohl positiv als auch negativ). Jede Zeile stellt die Werte im Zeitverlauf für eine bestimmte räumliche Site dar, während jede Spalte Werte für verschiedene räumliche Sites für eine bestimmte Zeit darstellt.Der effizienteste Weg, den Modus in einem numply-Array zu finden

Also, wenn das Array wie ist:

1 3 4 2 2 7 
5 2 2 1 4 1 
3 3 2 2 1 1 

Das Ergebnis sollte sein

1 3 2 2 2 1 

anzumerken, dass, wenn mehrere Werte für Modus sind, jede eine (zufällig ausgewählt) als Modus eingestellt werden kann, .

Ich kann über die Spalten, die Modus einzeln suchen, iterieren, aber ich hatte gehofft, dass numpy irgendeine eingebaute Funktion haben könnte, um das zu tun. Oder wenn es einen Trick gibt, um das effizient ohne Schleifen zu finden.

+0

Es gibt http://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.mstats.mode.html und die Antwort hier: http://stackoverflow.com/questions/6252280/find -die-häufigste-Nummer-in-a-numpy-Vektor – tom10

+1

@ tom10: Sie meinen [scipy.stats.mode()] (http: //docs.scipy.org/doc/scipy/referenz/generierte/scipy.stats.mode.html # scipy.stats.mode), oder? Der andere scheint ein maskiertes Array auszugeben. – fgb

+0

@fgb: richtig, danke für die Korrektur (und +1 für deine Antwort). – tom10

Antwort

52

prüfen scipy.stats.mode() (inspiriert von @ tom10 Kommentar):

import numpy as np 
from scipy import stats 

a = np.array([[1, 3, 4, 2, 2, 7], 
       [5, 2, 2, 1, 4, 1], 
       [3, 3, 2, 2, 1, 1]]) 

m = stats.mode(a) 
print(m) 

Ausgang:

ModeResult(mode=array([[1, 3, 2, 2, 1, 1]]), count=array([[1, 2, 2, 2, 1, 2]])) 

Wie Sie sehen, es gibt sowohl die Mode als auch die zählt. Sie können die Modi direkt über m[0] wählen:

print(m[0]) 

Ausgang:

[[1 3 2 2 1 1]] 
+3

So numpy selbst unterstützt keine solche Funktionalität? – Nik

+1

Offenbar nicht, aber [scipy Implementierung beruht nur auf numpy] (http://stackoverflow.com/questions/12399107/alternative-to-scipy-mode-function-in-numpy), so dass Sie nur diesen Code in Ihre kopieren könnten eigene Funktion. – fgb

+5

Nur eine Anmerkung, für Leute, die dies in der Zukunft betrachten: Sie müssen 'scipy.stats' explizit importieren, es ist nicht enthalten, wenn Sie einfach einen' Import-scipy' machen. – ffledgling

10

Dies ist ein heikles Problem dar, da es nicht viel da draußen Modus entlang einer Achse zu berechnen. Die Lösung ist für 1-D-Arrays, wo numpy.bincount ist praktisch, zusammen mit numpy.unique mit dem return_counts arg wie True. Die gebräuchlichste n-dimensionale Funktion, die ich sehe, ist scipy.stats.mode, obwohl sie - besonders für große Arrays mit vielen eindeutigen Werten - sehr langsam ist. Als Lösung habe ich diese Funktion entwickelt, und es verwenden, stark:

import numpy 

def mode(ndarray, axis=0): 
    # Check inputs 
    ndarray = numpy.asarray(ndarray) 
    ndim = ndarray.ndim 
    if ndarray.size == 1: 
     return (ndarray[0], 1) 
    elif ndarray.size == 0: 
     raise Exception('Cannot compute mode on empty array') 
    try: 
     axis = range(ndarray.ndim)[axis] 
    except: 
     raise Exception('Axis "{}" incompatible with the {}-dimension array'.format(axis, ndim)) 

    # If array is 1-D and numpy version is > 1.9 numpy.unique will suffice 
    if all([ndim == 1, 
      int(numpy.__version__.split('.')[0]) >= 1, 
      int(numpy.__version__.split('.')[1]) >= 9]): 
     modals, counts = numpy.unique(ndarray, return_counts=True) 
     index = numpy.argmax(counts) 
     return modals[index], counts[index] 

    # Sort array 
    sort = numpy.sort(ndarray, axis=axis) 
    # Create array to transpose along the axis and get padding shape 
    transpose = numpy.roll(numpy.arange(ndim)[::-1], axis) 
    shape = list(sort.shape) 
    shape[axis] = 1 
    # Create a boolean array along strides of unique values 
    strides = numpy.concatenate([numpy.zeros(shape=shape, dtype='bool'), 
           numpy.diff(sort, axis=axis) == 0, 
           numpy.zeros(shape=shape, dtype='bool')], 
           axis=axis).transpose(transpose).ravel() 
    # Count the stride lengths 
    counts = numpy.cumsum(strides) 
    counts[~strides] = numpy.concatenate([[0], numpy.diff(counts[~strides])]) 
    counts[strides] = 0 
    # Get shape of padded counts and slice to return to the original shape 
    shape = numpy.array(sort.shape) 
    shape[axis] += 1 
    shape = shape[transpose] 
    slices = [slice(None)] * ndim 
    slices[axis] = slice(1, None) 
    # Reshape and compute final counts 
    counts = counts.reshape(shape).transpose(transpose)[slices] + 1 

    # Find maximum counts and return modals/counts 
    slices = [slice(None, i) for i in sort.shape] 
    del slices[axis] 
    index = numpy.ogrid[slices] 
    index.insert(axis, numpy.argmax(counts, axis=axis)) 
    return sort[index], counts[index] 

Ergebnis:

In [2]: a = numpy.array([[1, 3, 4, 2, 2, 7], 
         [5, 2, 2, 1, 4, 1], 
         [3, 3, 2, 2, 1, 1]]) 

In [3]: mode(a) 
Out[3]: (array([1, 3, 2, 2, 1, 1]), array([1, 2, 2, 2, 1, 2])) 

einige Benchmarks:

In [4]: import scipy.stats 

In [5]: a = numpy.random.randint(1,10,(1000,1000)) 

In [6]: %timeit scipy.stats.mode(a) 
10 loops, best of 3: 41.6 ms per loop 

In [7]: %timeit mode(a) 
10 loops, best of 3: 46.7 ms per loop 

In [8]: a = numpy.random.randint(1,500,(1000,1000)) 

In [9]: %timeit scipy.stats.mode(a) 
1 loops, best of 3: 1.01 s per loop 

In [10]: %timeit mode(a) 
10 loops, best of 3: 80 ms per loop 

In [11]: a = numpy.random.random((200,200)) 

In [12]: %timeit scipy.stats.mode(a) 
1 loops, best of 3: 3.26 s per loop 

In [13]: %timeit mode(a) 
1000 loops, best of 3: 1.75 ms per loop 

EDIT: Sofern mehr von einem Hintergrund und den Ansatz modifiziert, um speichereffizienter zu sein

3

Erweiterung auf this method, appl Ziel war es, den Modus der Daten zu finden, in dem Sie möglicherweise den Index des tatsächlichen Arrays benötigen, um zu sehen, wie weit der Wert vom Mittelpunkt der Verteilung entfernt ist.

(_, idx, counts) = np.unique(a, return_index=True, return_counts=True) 
index = idx[np.argmax(counts)] 
mode = a[index] 

Denken Sie daran, um den Modus zu verwerfen, wenn len (np.argmax (Counts))> 1 ist, auch zu überprüfen, ob es tatsächlich repräsentativ für die zentrale Verteilung Ihrer Daten können Sie prüfen, ob es in Ihrer Standardabweichung fällt Intervall.