Биннинг статистика с нерегулярными и чередующимися бинами - PullRequest
0 голосов
/ 08 января 2019

Это короткий, полный пример более сложного реального приложения.

Используемые библиотеки :

import numpy as np
import scipy as sp
import scipy.stats as scist
import matplotlib.pyplot as plt
from itertools import zip_longest

Данные :

У меня есть массив с нерегулярными ячейками, определенными с началом и концом, например, вот так ( в реальном случае этот формат является заданным, поскольку он является выходом другого процесса ):

bin_starts = np.array([0, 93, 184, 277, 368])
bin_ends = np.array([89, 178, 272, 363, 458])

который я объединяю с:

bns = np.stack(zip_longest(bin_starts, bin_ends)).flatten()
bns
>>> array([  0,  89,  93, 178, 184, 272, 277, 363, 368, 458])

давая регулярно чередующиеся последовательности длинных и коротких интервалов, все неправильной длины Это схематичное представление заданных длинных и коротких интервалов: Bins_sketch

У меня есть куча данных временных рядов, аналогичных случайным данным, созданным ниже:

# make some random example data to bin
np.random.seed(45)
x = np.arange(0,460)
y = 5+np.random.randn(460).cumsum()
plt.plot(x,y);

Data to bin

Цель

Я хотел бы использовать последовательность интервалов для сбора статистики (среднее значение, процентили, и т. Д. ) на данных - но только с использованием длинных интервалов, то есть желтых на эскизе.

Допущения и уточнения:

Края длинных интервалов никогда не пересекаются; другими словами, между длинными интервалами всегда есть короткий интервал. Кроме того, первый интервал всегда длинный.

Текущее решение:

Один из способов сделать это - использовать scipy.stats.binned_statistic на всех интервалах, а затем нарезать результат, чтобы сохранить только все остальные (т. Е. [::2]), как это (отличная помощь для некоторой статистики, например, np.percentile, читал этот SO ответ от @ ali_m ):

ave = scist.binned_statistic(x, y, 
                         statistic = np.nanmean, 
                         bins=bns)[0][::2]

, что дает мне желаемый результат:

plt.plot(np.arange(0,5), ave);

binned_data

Вопрос : Есть ли более Pythonic способ сделать это (используя любой из Numpy, Scipy или Pandas)?

1 Ответ

0 голосов
/ 08 января 2019

Я думаю, использование комбо из IntervalIndex, pd.cut, groupby и agg - это относительно простой и легкий способ получить то, что вы хотите.

Сначала я бы сделал DataFrame (не уверен, что это лучший способ перейти из массивов np):

df = pd.DataFrame()
df['x'], df['y'] = x, y

Затем вы можете определить свои корзины как список кортежей:

bins = list(zip(bin_starts, bin_ends))

Используйте pandas IntervalIndex , который имеет метод from_tuples(), чтобы создать ячейки для последующего использования в cut. Это полезно, потому что вам не нужно полагаться на нарезку массива bns, чтобы распутать «регулярно чередующиеся последовательности длинных и коротких интервалов» - вместо этого вы можете явно определить интересующие вас ячейки:

ii = pd.IntervalIndex.from_tuples(bins, closed='both')

kwarg closed указывает, следует ли включать номера конечных членов в интервал. Например, для кортежа (0, 89), с closed='both' интервал будет включать как 0, так и 89 (в отличие от left, right или neither).

Затем создайте столбец категории в кадре данных, используя pd.cut(), который представляет собой метод для объединения значений в интервалы. Объект IntervalIndex можно указать с помощью bin kwarg:

df['bin'] = pd.cut(df.x, bins=ii)

Наконец, используйте df.groupby() и .agg(), чтобы получить любую статистику, которую вы хотите:

df.groupby('bin')['y'].agg(['mean', np.std])

который выводит:

                 mean       std
bin                            
[0, 89]     -4.814449  3.915259
[93, 178]   -7.019151  3.912347
[184, 272]   7.223992  5.957779
[277, 363]  15.060402  3.979746
[368, 458]  -0.644127  3.361927
...