биннинг данных в Python с помощью scipy / numpy


108

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

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

Ответы:


181

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

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


1
Я не вижу разницы - что быстрее?

4
@user: я не знаю, какой из них быстрее для ваших данных и параметров. Оба метода должны быть быстрее, чем ваш, и я ожидаю, что этот histogram()метод будет быстрее для большого количества ящиков. Но тебе придется профилировать себя, я не могу сделать это за тебя.
Sven Marnach

39

Функция 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]

16

Не уверен, почему эта нить была некротизирована; но вот одобренный в 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

3
вы отвечаете на другой вопрос. Например, ваш mean[0] = np.mean(data[0:10]), хотя правильный ответ должен бытьnp.mean(data[data < 10])
Руджеро Турра

5

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

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

По сути, это то же решение, что я опубликовал ранее; но теперь завернутый в приятный интерфейс, с тестами и всем :)


3

Я бы добавил, а также чтобы ответить на вопрос, найти средние значения ячеек с использованием histogram2d python, что у 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 является обобщением этой функции для наборов данных более высоких измерений


1

Другой альтернативой является использование ufunc.at. Этот метод применяет на месте желаемую операцию по указанным индексам. Мы можем получить позицию корзины для каждой точки данных, используя метод 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)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.