Скользящее окно Pandas с настраиваемой длиной просмотра назад на основе суммы столбцов

Имея фрейм данных pandas с двумя столбцами, «atbats» и «hits», индексированными по дате, возможно ли получить самое последнее историческое среднее значение (среднее количество совпадений на atbat)? Например, историческое среднее значение может быть таким, что наименьшее количество атлетов превышает 10. Это похоже на скользящее окно с условным количеством периодов ретроспективного анализа. Например, учитывая:

      date, atbats, hits, 
2017-01-01,      5,    2,
2017-01-02,      6,    3,
2017-01-03,      1,    1,
2017-01-04,      12,   3,
2017-01-04,      1,    0,

В первый день исторических атлетов не было. Во второй день только 6. Поскольку оба меньше 10, они могут быть NaN или просто 0.

На третий день мы оглянемся на последние два дня и увидим 5 + 6 битов со средним значением (2 + 3) / (5 + 6) = 0,45 хитов на бит.

На третий день мы оглянемся на последние три дня и получим (2 + 3 + 1) / (5 + 6 + 1) = 0,5 хитов / ат.

На четвертый день мы оглянемся только на последний день и получим 4/16 = 0,25 хитов на бит. Поскольку в последний день их больше 10 (16), нам не нужно искать дальше.

Окончательный фрейм данных будет выглядеть так:

      date, atbats, hits,  pastAtbats, pastHits, avg,
2017-01-01,      5,    2,           0,       0,   0,
2017-01-02,      6,    3,           0,       0,   0,
2017-01-03,      1,    1,          11,       5,   0.45,
2017-01-04,      16,   4,          12,       6,   0.50,
2017-01-04,      1,    0,          16,       4,   0.25,

Возможен ли такой расчет в пандах?

Единственное решение, которое я могу придумать, - это чистая грубая сила: разделите попадания на биты в каждой строке, реплицируйте каждую строку x раз, где x = atbats, а затем просто сделайте скользящее окно из 10. Но в моем фреймворке "atbats" "в среднем около 80 в день, поэтому это значительно увеличит размер фрейма данных и общее количество окон для расчета.


person Albeit    schedule 14.09.2017    source источник


Ответы (1)


Используйте iterrows, чтобы добиться того, что вам нужно. См. ниже:

Исходный фрейм данных:

index atbats  hits
1       5     2
2       6     3
3       1     1
4      16     4
4       1     0
5       1     0
6      14     2
7       5     1

Код:

data = []
last = [0,0]
past_atbats = 0
past_hits = 0
for i, row in df.iterrows():
    if( last[0] >= 10):
        data.append(last.copy())
    else:
        data.append([0,0])

    if(row['atbats'] >= 10):
        last[0] = row['atbats']
        last[1] = row['hits']
    else:
        last[0] += row['atbats']
        last[1] += row['hits']

df_past = pd.DataFrame(data=data,index=df.index,columns=['past_atbats','past_hits'])
df = df.merge(df_past,left_index=True,right_index=True)
df['avg'] = df['past_hits'].divide(df['past_atbats'])

Результат в:

index atbats  hits  past_atbats  past_hits       avg
1       5     2            0          0       NaN
2       6     3            0          0       NaN
3       1     1           11          5  0.454545
4      16     4           12          6  0.500000
4      16     4           16          4  0.250000
4       1     0           12          6  0.500000
4       1     0           16          4  0.250000
5       1     0           17          4  0.235294
6      14     2           18          4  0.222222
7       5     1           14          2  0.142857

Возможно, оптимизацию можно провести, но я думаю, она вам поможет.

person David Leon    schedule 14.09.2017
comment
Я надеялся на что-то более производительное ... использование iterrows эффективно просто вручную перебирает каждую строку. - person Albeit; 26.09.2017