Скользящая средняя на объекте pandas.groupby, который учитывает время - PullRequest
0 голосов
/ 04 декабря 2018

Для данного кадра данных pandas в следующем формате:

toy = pd.DataFrame({
'id': [1,2,3,
       1,2,3,
       1,2,3],
'date': ['2015-05-13', '2015-05-13', '2015-05-13', 
         '2016-02-12', '2016-02-12', '2016-02-12', 
         '2018-07-23', '2018-07-23', '2018-07-23'],
'my_metric': [395, 634, 165, 
              144, 305, 293, 
              23, 395, 242]
})
# Make sure 'date' has datetime format
toy.date = pd.to_datetime(toy.date)

enter image description here

Столбец my_metric содержит некоторую (случайную) метрику, которую я хочувычислить зависящее от времени скользящее среднее, условно для столбца id и в течение некоторого заданного промежутка времени, который я определяю сам.Я буду называть этот промежуток времени «временем просмотра»;что может быть 5 минут или 2 года.Чтобы определить, какие наблюдения следует включить в расчет обратного просмотра, мы используем столбец date (который может быть индексом, если вы предпочитаете).

К моему разочарованию, я обнаружил, что такую ​​процедуру нелегко выполнить с помощью встроенных панд, поскольку мне нужно выполнить вычисления условно на id, и в то же время вычисление должно быть сделано только на наблюдениях.в течение времени просмотра (проверяется с помощью столбца date).Следовательно, выходной фрейм данных должен состоять из одной строки для каждой комбинации id - date, причем столбец my_metric теперь является средним значением всех наблюдений, связанных в течение времени просмотра (например, 2 года, включая сегодняшнюю дату).

Для ясности я включил рисунок с желаемым выходным форматом (извинения за увеличенный рисунок) при использовании двухлетнего времени просмотра:

Desired result using a 2 year lookback time

У меня есть решение, но оно не использует определенные встроенные функции панд и, вероятно, неоптимально (комбинация понимания списка и одного цикла for).Решение, которое я ищу, не будет использовать цикл for и, следовательно, будет более масштабируемым / эффективным / быстрым.

Спасибо!

Ответы [ 2 ]

0 голосов
/ 10 декабря 2018

Итак, после некоторой переделки я нашел ответ, который будет адекватно обобщен.Я использовал немного другой «игрушечный» фрейм данных (чуть более подходящий для моего случая).Для полноты картины вот данные:

enter image description here

Рассмотрим теперь следующий код:

# Define a custom function which groups by time (using the index)
def rolling_average(x, dt):
    xt = x.sort_index().groupby(lambda x: x.time()).rolling(window=dt).mean()
    xt.index = xt.index.droplevel(0)
    return xt

dt='730D' # rolling average window: 730 days = 2 years

# Group by the 'id' column
g = toy.groupby('id')

# Apply the custom function
df = g.apply(rolling_average, dt=dt)

# Massage the data to appropriate format
df.index = df.index.droplevel(0)
df = df.reset_index().drop_duplicates(keep='last', subset=['id', 'date'])

Результат, как и ожидалось:

enter image description here

0 голосов
/ 04 декабря 2018

Расчет времени просмотра: (Текущий год - 2 года)

from dateutil.relativedelta import relativedelta
from dateutil import parser
import datetime

In [1691]: dt = '2018-01-01'

In [1695]: dt = parser.parse(dt)

In [1696]: lookback_time = dt - relativedelta(years=2)

Теперь отфильтруйте кадр данных по времени просмотра и рассчитайте скользящее среднее

In [1722]: toy['new_metric'] = ((toy.my_metric + toy[toy.date > lookback_time].groupby('id')['my_metric'].shift(1))/2).fillna(toy.my_metric)

In [1674]: toy.sort_values('id')
Out[1674]: 
        date  id  my_metric  new_metric
0 2015-05-13   1        395       395.0
3 2016-02-12   1        144       144.0
6 2018-07-23   1         23        83.5
1 2015-05-13   2        634       634.0
4 2016-02-12   2        305       305.0
7 2018-07-23   2        395       350.0
2 2015-05-13   3        165       165.0
5 2016-02-12   3        293       293.0
8 2018-07-23   3        242       267.5
...