Я заметил, что вы хотите, чтобы startDate был изменен до конца соответствующего 30-дневного периода, начиная с вашей даты начала.
Другая деталь заключается в том, что вам нужен результат сгруппировано по INDEX_250 - сколько предыдущих транзакций (в обоих соответствующих периодах) было с одинаковым значением INDEX_250 .
ПримечаниеКроме того, скользящие вычисления могут выполняться в окне, содержащем строки из числа будущих периодов, тогда как вы хотите количество транзакций за предыдущих 30 или 60 днейи Rolling не допускает отрицательного числа периодов.
Вот почему я выбрал иной подход, чем "обычный" Rolling .
Начать свспомогательные переменные:
td30 = pd.Timedelta('30D')
dRng = pd.date_range(start='2013-09-17', end=df.startDate.max() + td30,
freq='30D', closed='left')
Затем определите следующую функцию, вычисляющую обе цели:
def targets(grp):
grp['Prd'] = grp.startDate.apply(lambda x: dRng.asof(x) + td30)
grp.set_index('Prd', inplace=True)
trg30 = grp.groupby(level=0).INDEX_250.count()\
.rename('nrTargets_gr_250_30').reindex(dRng, fill_value=0)
trg60 = trg30.rolling(2).sum().rename('nrTargets_gr_250_60')\
.fillna(0, downcast='infer')
trg30 = trg30[trg30 > 0]
trg60 = trg60[trg60 > 0]
return trg30.to_frame().join(trg60, how='outer')\
.fillna(0, downcast='infer').rename_axis('startDate')
Примените ее и сбросьте индексы (только в этом порядке, чтобы иметь правильное расположение столбцов):
df2 = df[df.startDate >= '2013-09-17'].groupby('INDEX_250')\
.apply(targets).reset_index(level=[0]).reset_index()
Примечаниеs:
- Я взял только строки с startDate в или после указанной вами даты начала ( 2013-09-17 ).
- Тип обоих target столбцов равен int . Я думаю, что это более естественно, так как эти столбцы содержат число транзакций, которое по своей природе является просто целым числом .
И последнее, чтоизменить тип INDEX_250 на int :
df2.INDEX_250 = df2.INDEX_250.astype(int)
Результат для группы INDEX_250 такой же, как вы указали, за исключением строк результатаот 2016 и 2017 , которые не были включены в ваши данные выборки.
Расширенная версия - со средними ценами
Чтобы увеличить результат в среднемцены для каждой «конечной» даты и обеих целей, требуются два изменения.
Сначала определите другую функцию для «переформатирования» target DataFrame:
def trgReformat(trg):
trg = trg[trg.nrTargets_gr_250 > 0].copy()
trg['avgPrice'] = trg.sm / trg.nrTargets_gr_250
return trg.drop(columns='sm')
Secondопределить цели функцию как:
def targets(grp):
grp['Prd'] = grp.startDate.apply(lambda x: dRng.asof(x) + td30)
grp.set_index('Prd', inplace=True)
trg30 = grp.groupby(level=0).agg(
nrTargets_gr_250=('INDEX_250', 'count'), sm=('priceDeal', 'sum'))\
.reindex(dRng, fill_value=0)
trg60 = trg30.rolling(2).sum().fillna(0, downcast='infer')
trg30 = trgReformat(trg30)
trg60 = trgReformat(trg60)
return trg30.join(trg60, how='outer', lsuffix='_30', rsuffix='_60')\
.fillna(0, downcast='infer').rename_axis('startDate')
Эта функция использует именованных агрегатов , для вычисления:
- nrTargets_gr_250 - количество строк,
- см - сумма цен.
Причина в том, чтовычисление trg60 выполняется с использованием скользящего (для 2 последовательных 30-дневных периодов), поэтому одного среднего здесь будет недостаточно.
Вычисление средней цены может быть выполнено так же поздно, как и при переформатировании каждой цели .
Применение этой функции такое же, как и раньше.