binowanie danych w Pythonie za pomocą scipy / numpy

108

czy istnieje bardziej efektywny sposób obliczania średniej tablicy w określonych wcześniej pojemnikach? na przykład mam tablicę liczb i tablicę odpowiadającą pozycjom początkowym i końcowym bin w tej tablicy i chcę po prostu wziąć średnią w tych koszach? Mam kod, który to robi, ale zastanawiam się, jak można go zmniejszyć i ulepszyć. dzięki.

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

źródło

Odpowiedzi:

181

Prawdopodobnie jest szybszy i łatwiejszy w użyciu 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))]

Alternatywą jest użycie numpy.histogram():

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

Spróbuj sam, który jest szybszy ... :)

Sven Marnach
źródło
1
Nie widzę różnicy - która jest szybsza?
4
@user: Nie wiem, który jest szybszy dla twoich danych i parametrów. Obie metody powinny być szybsze niż twoja i spodziewam się, że histogram()metoda będzie szybsza dla dużej liczby pojemników. Ale musisz się profilować, nie mogę tego dla ciebie zrobić.
Sven Marnach
39

Funkcja Scipy (> = 0.11) scipy.stats.binned_statistic odnosi się konkretnie do powyższego pytania.

W przypadku tego samego przykładu, co w poprzednich odpowiedziach, rozwiązaniem Scipy byłoby

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
źródło
16

Nie jestem pewien, dlaczego ta nić została nekroć; ale oto odpowiedź zatwierdzona w 2014 roku, która powinna być znacznie szybsza:

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
źródło
3
odpowiadasz na inne pytanie. Na przykład twoja mean[0] = np.mean(data[0:10]), chociaż prawidłowa odpowiedź powinna byćnp.mean(data[data < 10])
Ruggero Turra
5

Numpy_indexed pakiet (disclaimer: Jestem jego autorem) zawiera funkcjonalności do efektywnego wykonywania operacji tego typu:

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

Jest to zasadniczo to samo rozwiązanie, które zamieściłem wcześniej; ale teraz zapakowany w ładny interfejs, z testami i wszystkim :)

Eelco Hoogendoorn
źródło
3

Dodałbym, a także aby odpowiedzieć na pytanie, znajdź średnie wartości bin za pomocą histogram2d python, że scipy ma również funkcję specjalnie zaprojektowaną do obliczania dwuwymiarowej statystyki binowanej dla jednego lub więcej zestawów danych

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

funkcja scipy.stats.binned_statistic_dd jest uogólnieniem tej funkcji dla zbiorów danych o wyższych wymiarach

Chmeul
źródło
1

Inną alternatywą jest użycie ufunc.at. Ta metoda stosuje w miejscu żądaną operację przy określonych indeksach. Możemy uzyskać pozycję bin dla każdego punktu danych za pomocą metody wyszukiwania z sortowaniem. Następnie możemy użyć at, aby zwiększyć o 1 pozycję histogramu w indeksie podanym przez bin_indexes, za każdym razem, gdy napotkamy indeks w 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
źródło