functions python numpy 2d mode

python - functions - La manera más eficiente de encontrar el modo en numpy array



numpy python 3.6 windows (4)

Tengo una matriz 2D que contiene números enteros (tanto positivos como negativos). Cada fila representa los valores a lo largo del tiempo para un sitio espacial particular, mientras que cada columna representa valores para varios sitios espaciales para un tiempo dado.

Entonces, si la matriz es como:

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

El resultado debe ser

1 3 2 2 2 1

Tenga en cuenta que cuando hay varios valores para el modo, cualquiera (seleccionado al azar) puede configurarse como modo.

Puedo iterar sobre las columnas encontrando el modo uno a la vez, pero esperaba que Numpy pudiera tener alguna función incorporada para hacer eso. O si hay un truco para encontrarlo de manera eficiente sin bucles.


Ampliando este método , se aplica para encontrar el modo de los datos donde puede necesitar el índice de la matriz real para ver qué tan lejos está el valor del centro de la distribución.

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

Recuerde descartar el modo cuando len (np.argmax (counts))> 1, también para validar si es realmente representativo de la distribución central de sus datos, puede verificar si cae dentro de su intervalo de desviación estándar.


Creo que una forma muy simple sería usar la clase Counter. A continuación, puede usar la función most_common () de la instancia de contador como se menciona here .

Para arreglos de 1-d .:

import numpy as np from collections import Counter nparr = np.arange(10) nparr[2] = 6 nparr[3] = 6 #6 is now the mode mode = Counter(nparr).most_common(1) # mode will be [(6,3)] to give the count of the most occurring value, so -> print(mode[0][0])

Para matrices multidimensionales (poca diferencia):

import numpy as np from collections import Counter nparr = np.arange(10) nparr[2] = 6 nparr[3] = 6 nparr = nparr.reshape((10,2,5)) #same thing but we add this to reshape into ndarray mode = Counter(nparr.flatten()).most_common(1) # just use .flatten() method # mode will be [(6,3)] to give the count of the most occurring value, so -> print(mode[0][0])

Esto puede ser o no una implementación eficiente, pero es conveniente.


Este es un problema difícil, ya que no hay mucho por ahí para calcular el modo a lo largo de un eje. La solución es sencilla para arreglos en 1-D, donde numpy.bincount es útil, junto con numpy.unique con return_counts arg como True . La función n-dimensional más común que veo es scipy.stats.mode, aunque es prohibitivamente lenta, especialmente para matrices grandes con muchos valores únicos. Como solución, he desarrollado esta función y la uso en gran medida:

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]

Resultado:

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

Algunos puntos de referencia:

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

EDITAR: proporcionó más de un fondo y modificó el enfoque para ser más eficiente con la memoria


Marque scipy.stats.mode() (inspirado en el comentario de @ tom10):

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)

Salida:

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

Como puede ver, devuelve tanto el modo como los recuentos. Puede seleccionar los modos directamente a través de m[0] :

print(m[0])

Salida:

[[1 3 2 2 1 1]]