agrupando datos en python con scipy / numpy

108

¿Existe una forma más eficiente de tomar un promedio de una matriz en contenedores preespecificados? por ejemplo, tengo una matriz de números y una matriz correspondiente a las posiciones inicial y final de la bandeja en esa matriz, ¿y solo quiero tomar la media en esas bandejas? Tengo un código que lo hace a continuación, pero me pregunto cómo se puede reducir y mejorar. Gracias.

from scipy import *
from numpy import *

def get_bin_mean(a, b_start, b_end):
    ind_upper = nonzero(a >= b_start)[0]
    a_upper = a[ind_upper]
    a_range = a_upper[nonzero(a_upper < b_end)[0]]
    mean_val = mean(a_range)
    return mean_val


data = rand(100)
bins = linspace(0, 1, 10)
binned_data = []

n = 0
for n in range(0, len(bins)-1):
    b_start = bins[n]
    b_end = bins[n+1]
    binned_data.append(get_bin_mean(data, b_start, b_end))

print binned_data

fuente

Respuestas:

181

Probablemente sea más rápido y fácil de usar numpy.digitize():

import numpy
data = numpy.random.random(100)
bins = numpy.linspace(0, 1, 10)
digitized = numpy.digitize(data, bins)
bin_means = [data[digitized == i].mean() for i in range(1, len(bins))]

Una alternativa a esto es usar numpy.histogram():

bin_means = (numpy.histogram(data, bins, weights=data)[0] /
             numpy.histogram(data, bins)[0])

Pruebe usted mismo cuál es más rápido ... :)

Sven Marnach
fuente
1
No veo una diferencia, ¿cuál es más rápido?
4
@usuario: No sé cuál es más rápido para sus datos y parámetros. Ambos métodos deberían ser más rápidos que el suyo, y espero que el histogram()método sea más rápido para una gran cantidad de bins. Pero tendrás que perfilarte, no puedo hacer esto por ti.
Sven Marnach
39

La función Scipy (> = 0.11) scipy.stats.binned_statistic aborda específicamente la pregunta anterior.

Para el mismo ejemplo que en las respuestas anteriores, la solución de Scipy sería

import numpy as np
from scipy.stats import binned_statistic

data = np.random.rand(100)
bin_means = binned_statistic(data, data, bins=10, range=(0, 1))[0]
divenex
fuente
16

No estoy seguro de por qué este hilo fue negado; pero aquí hay una respuesta aprobada en 2014, que debería ser mucho más rápida:

import numpy as np

data = np.random.rand(100)
bins = 10
slices = np.linspace(0, 100, bins+1, True).astype(np.int)
counts = np.diff(slices)

mean = np.add.reduceat(data, slices[:-1]) / counts
print mean
Eelco Hoogendoorn
fuente
3
estás respondiendo a una pregunta diferente. Por ejemplo mean[0] = np.mean(data[0:10]), su , aunque la respuesta correcta debería sernp.mean(data[data < 10])
Ruggero Turra
5

El paquete numpy_indexed (descargo de responsabilidad: soy su autor) contiene funciones para realizar operaciones de este tipo de manera eficiente:

import numpy_indexed as npi
print(npi.group_by(np.digitize(data, bins)).mean(data))

Esta es esencialmente la misma solución que la que publiqué anteriormente; pero ahora envuelto en una bonita interfaz, con pruebas y todo :)

Eelco Hoogendoorn
fuente
3

Yo agregaría, y también para responder a la pregunta, encuentre los valores medios de bin usando histogram2d python que el scipy también tiene una función especialmente diseñada para calcular una estadística binned bidimensional para uno o más conjuntos de datos

import numpy as np
from scipy.stats import binned_statistic_2d

x = np.random.rand(100)
y = np.random.rand(100)
values = np.random.rand(100)
bin_means = binned_statistic_2d(x, y, values, bins=10).statistic

la función scipy.stats.binned_statistic_dd es una generalización de esta función para conjuntos de datos de mayores dimensiones

Chmeul
fuente
1

Otra alternativa es utilizar ufunc.at. Este método aplica en el lugar una operación deseada en índices específicos. Podemos obtener la posición del contenedor para cada punto de datos utilizando el método searchsorted. Entonces podemos usar at para incrementar en 1 la posición del histograma en el índice dado por bin_indexes, cada vez que encontremos un índice en bin_indexes.

np.random.seed(1)
data = np.random.random(100) * 100
bins = np.linspace(0, 100, 10)

histogram = np.zeros_like(bins)

bin_indexes = np.searchsorted(bins, data)
np.add.at(histogram, bin_indexes, 1)
Kostas
fuente