raggruppare i dati in Python con scipy / numpy


108

esiste un modo più efficiente per prendere una media di un array in contenitori prespecificati? ad esempio, ho una matrice di numeri e una matrice corrispondente alle posizioni di inizio e fine del cestino in quella matrice e voglio solo prendere la media in quei raccoglitori? Ho un codice che lo fa di seguito, ma mi chiedo come possa essere ridotto e migliorato. Grazie.

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

Risposte:


181

Probabilmente è più veloce e più facile da usare 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))]

Un'alternativa a questo è usare numpy.histogram():

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

Prova tu stesso quale è più veloce ... :)


1
Non vedo un diff - che è più veloce?

4
@user: non so quale sia più veloce per i tuoi dati e parametri. Entrambi i metodi dovrebbero essere più veloci del tuo e mi aspetto che il histogram()metodo sia più veloce per un gran numero di contenitori. Ma dovrai creare il tuo profilo, non posso farlo per te.
Sven Marnach

39

La funzione Scipy (> = 0.11) scipy.stats.binned_statistic affronta specificamente la domanda precedente.

Per lo stesso esempio delle risposte precedenti, la soluzione di Scipy sarebbe

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]

16

Non sono sicuro del motivo per cui questo thread è stato bloccato; ma ecco una risposta approvata per il 2014, che dovrebbe essere molto più veloce:

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

3
stai rispondendo a una domanda diversa. Ad esempio il tuo mean[0] = np.mean(data[0:10]), mentre la risposta giusta dovrebbe esserenp.mean(data[data < 10])
Ruggero Turra

5

Il pacchetto numpy_indexed (disclaimer: sono il suo autore) contiene funzionalità per eseguire in modo efficiente operazioni di questo tipo:

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

Questa è essenzialmente la stessa soluzione di quella che ho pubblicato in precedenza; ma ora avvolto in una bella interfaccia, con test e tutto :)


3

Aggiungerei, e anche per rispondere alla domanda, trova i valori medi dei bin usando histogram2d python che lo scipy ha anche una funzione appositamente progettata per calcolare una statistica binata bidimensionale per uno o più set di dati

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 funzione scipy.stats.binned_statistic_dd è una generalizzazione di questa funzione per dataset di dimensioni superiori


1

Un'altra alternativa è usare ufunc.at. Questo metodo applica sul posto un'operazione desiderata a indici specificati. Possiamo ottenere la posizione del bin per ogni datapoint utilizzando il metodo searchsorted. Quindi possiamo usare at per incrementare di 1 la posizione dell'istogramma nell'indice dato da bin_indexes, ogni volta che incontriamo un indice in 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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.