Данные биннинга в python с scipy/numpy

существует ли более эффективный способ получить среднее значение массива в заранее определенных ячейках? например, у меня есть массив чисел и массив, соответствующий начальным и конечным позициям bin в этом массиве, и я хочу просто взять среднее значение в этих ячейках? У меня есть код, который делает это ниже, но мне интересно, как его можно сократить и улучшить. спасибо.

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

Ответ 1

Вероятно, быстрее и проще использовать 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))]

Альтернативой этому является использование numpy.histogram():

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

Попробуйте сами, какой из них быстрее...:)

Ответ 2

Функция Scipy ( >= 0.11) scipy.stats.binned_statistic специально рассматривает вышеуказанный вопрос.

В том же примере, что и в предыдущих ответах, решение Scipy было бы

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]

Ответ 3

Не знаю, почему эта нить стала некротической; но вот одобренный ответ 2014 года, который должен быть намного быстрее:

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

Ответ 4

Пакет numpy_indexed (отказ от ответственности: я являюсь его автором) содержит функциональные возможности для эффективного выполнения операций этого типа:

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

Это по существу то же самое решение, что и предыдущее; но теперь завернутый в приятный интерфейс, с тестами и все:)

Ответ 5

Я бы добавил, а также, чтобы ответить на вопрос найти средние значения bin с помощью python histogram2d, что scipy также имеет функцию, специально разработанную для вычислить двумерную бинную статистику для одного или нескольких наборов данных

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

функция scipy.stats.binned_statistic_dd является обобщением этой функции для наборов данных более высоких размеров

Ответ 6

Другой альтернативой является использование ufunc.at. Этот метод применяет на месте желаемую операцию по указанным индексам. Мы можем получить позицию бина для каждого datapoint, используя метод searchsorted. Затем мы можем использовать at для увеличения на 1 позиции гистограммы в индексе, заданном bin_indexes, каждый раз, когда встречаем индекс в 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)