Binning-Daten in Python mit scipy / numpy

106

Gibt es eine effizientere Möglichkeit, einen Durchschnitt eines Arrays in vorgegebenen Behältern zu ermitteln? Zum Beispiel habe ich ein Array von Zahlen und ein Array, das den Start- und Endpositionen der Bins in diesem Array entspricht, und ich möchte nur den Mittelwert in diesen Bins nehmen. Ich habe Code, der es unten tut, aber ich frage mich, wie es reduziert und verbessert werden kann. Vielen Dank.

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

quelle

Antworten:

180

Es ist wahrscheinlich schneller und einfacher zu bedienen 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))]

Eine Alternative dazu ist numpy.histogram():

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

Probieren Sie selbst aus, welches schneller ist ... :)

Sven Marnach
quelle
1
Ich sehe keinen Unterschied - welcher ist schneller?
4
@user: Ich weiß nicht, welches für Ihre Daten und Parameter schneller ist. Beide Methoden sollten schneller sein als Ihre, und ich würde erwarten, dass die histogram()Methode für eine große Anzahl von Behältern schneller ist. Aber du musst dich profilieren, das kann ich nicht für dich tun.
Sven Marnach
39

Die Scipy (> = 0.11) -Funktion scipy.stats.binned_statistic speziell mit der obigen Frage.

Für das gleiche Beispiel wie in den vorherigen Antworten wäre die Scipy-Lösung

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
quelle
16

Ich bin mir nicht sicher, warum dieser Thread nekrotisch wurde. Aber hier ist eine 2014 genehmigte Antwort, die viel schneller sein sollte:

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
quelle
3
Sie beantworten eine andere Frage. Zum Beispiel Ihre mean[0] = np.mean(data[0:10]), während die richtige Antwort sein solltenp.mean(data[data < 10])
Ruggero Turra
5

Das Paket numpy_indexed (Haftungsausschluss: Ich bin sein Autor) enthält Funktionen zur effizienten Ausführung von Operationen dieses Typs:

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

Dies ist im Wesentlichen die gleiche Lösung wie die, die ich zuvor veröffentlicht habe. aber jetzt in eine schöne Oberfläche gewickelt, mit Tests und allem :)

Eelco Hoogendoorn
quelle
3

Ich würde hinzufügen und auch die Frage beantworten , mittlere Bin-Werte mit histogram2d Python zu finden, dass der Scipy auch eine Funktion hat, die speziell entwickelt wurde, um eine zweidimensionale Binned-Statistik für einen oder mehrere Datensätze zu berechnen

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

Die Funktion scipy.stats.binned_statistic_dd ist eine Verallgemeinerung dieser Funktion für Datensätze mit höheren Dimensionen

Chmeul
quelle
1

Eine andere Alternative ist die Verwendung von ufunc.at. Diese Methode wendet an Ort und Stelle eine gewünschte Operation an bestimmten Indizes an. Wir können die Bin-Position für jeden Datenpunkt mithilfe der suchsortierten Methode ermitteln. Dann können wir at verwenden, um die Position des Histogramms am Index von bin_indexes jedes Mal um 1 zu erhöhen, wenn wir auf einen Index bei bin_indexes stoßen.

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
quelle