binning des données en python avec scipy / numpy

108

existe-t-il un moyen plus efficace de prendre une moyenne d'un tableau dans des bacs prédéfinis? par exemple, j'ai un tableau de nombres et un tableau correspondant aux positions de début et de fin du bac dans ce tableau, et je veux simplement prendre la moyenne de ces bacs? J'ai un code qui le fait ci-dessous mais je me demande comment il peut être réduit et amélioré. Merci.

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

la source

Réponses:

181

C'est probablement plus rapide et plus facile à utiliser 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))]

Une alternative à cela consiste à utiliser numpy.histogram():

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

Essayez par vous-même lequel est le plus rapide ... :)

Sven Marnach
la source
1
Je ne vois pas de différence - qui est plus rapide?
4
@user: Je ne sais pas lequel est le plus rapide pour vos données et paramètres. Les deux méthodes devraient être plus rapides que les vôtres, et je m'attendrais à ce que la histogram()méthode soit plus rapide pour un grand nombre de bacs. Mais vous devrez vous profiler, je ne peux pas faire ça pour vous.
Sven Marnach
39

La fonction Scipy (> = 0,11) scipy.stats.binned_statistic répond spécifiquement à la question ci-dessus.

Pour le même exemple que dans les réponses précédentes, la solution Scipy serait

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
la source
16

Je ne sais pas pourquoi ce fil a été nécrosé; mais voici une réponse approuvée en 2014, qui devrait être beaucoup plus rapide:

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
la source
3
vous répondez à une autre question. Par exemple votre mean[0] = np.mean(data[0:10]), alors que la bonne réponse devrait êtrenp.mean(data[data < 10])
Ruggero Turra
5

Le package numpy_indexed (clause de non-responsabilité: je suis son auteur) contient des fonctionnalités permettant d'effectuer efficacement des opérations de ce type:

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

C'est essentiellement la même solution que celle que j'ai publiée plus tôt; mais maintenant enveloppé dans une belle interface, avec des tests et tout :)

Eelco Hoogendoorn
la source
3

J'ajouterais, et aussi pour répondre à la question, trouver les valeurs moyennes de bin en utilisant histogram2d python que le scipy a également une fonction spécialement conçue pour calculer une statistique bidimensionnelle regroupée pour un ou plusieurs ensembles de données

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 fonction scipy.stats.binned_statistic_dd est une généralisation de cette fonction pour les jeux de données de dimensions supérieures

Chmeul
la source
1

Une autre alternative consiste à utiliser ufunc.at. Cette méthode applique en place une opération souhaitée à des indices spécifiés. Nous pouvons obtenir la position de chaque point de données en utilisant la méthode searchsorted. Ensuite, nous pouvons utiliser at pour incrémenter de 1 la position de l'histogramme à l'index donné par bin_indexes, chaque fois que nous rencontrons un index à 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
la source