#python #pandas #date #pandas-groupby
#питон #pandas #Дата #панды-группби
Вопрос:
Минимальный воспроизводимый пример
import numpy as np
import pandas as pd
np.random.seed(0)
dates = pd.date_range(start='1/1/2021', end='3/15/2021')
df = pd.DataFrame({'date': np.random.choice(dates, 1000),
'label': np.random.choice(['a', 'b', 'c'], 1000)})
Результат
date label
0 2021-02-14 a
1 2021-02-17 c
2 2021-03-06 a
3 2021-03-09 c
4 2021-03-09 b
... ... ...
995 2021-03-06 c
996 2021-01-14 b
997 2021-01-02 a
998 2021-01-03 c
999 2021-03-08 b
Я пытаюсь группировать столбец даты каждые 4 недели, начиная с последней наблюдаемой даты (в данном случае, df['date'].max()
дает '3/15/2021'
, поэтому я хочу, чтобы последняя дата при группировке по дате и метке была '3/15/2021'
и для других дат корректировалась соответствующим образом (за 28 дней до 3/15, за 56 дней до 3/15 и т. Д.).
Однако я не смог этого сделать pd.Grouper
. Согласно документам, pd.Grouper
принимает origin
параметр, который корректирует группировку, но нет возможности использовать его на основе даты окончания.
Есть ли способ использовать pd.Grouper
аналогично следующему:
df.groupby([pd.Grouper(key='date', freq='28d', label='right'), 'label'])['label'].count()
date label
2021-01-29 a 114
b 135
c 134
2021-02-26 a 125
b 133
c 123
2021-03-26 a 83
b 81
c 72
Name: label, dtype: int64
но вместо этого установите для последнего сгруппированного по дате конца значение 3/15 (и пусть эта последняя группа содержит все данные за 28 дней, начиная с 3/15)?
Ответ №1:
Мы можем попробовать создать счетчик дат с помощью div
df_sub = df.assign(v = ((df.date-df.date.max()).dt.days.sub(1)//28))
s = df_sub.groupby(['v','label']).agg({'label':'count'})
s = s.join(df_sub.groupby('v').date.max())
Out[41]:
label date
v label
-3 a 76 2021-01-18
b 87 2021-01-18
c 91 2021-01-18
-2 a 120 2021-02-15
b 138 2021-02-15
c 126 2021-02-15
-1 a 126 2021-03-15
b 124 2021-03-15
c 112 2021-03-15
Комментарии:
1. Я тоже думал об этом, но последнее соединение
date.max()
может не сработать, если2021-02-15
оно не отображается в данных.
Ответ №2:
По-видимому pd.Grouper
, не поддерживает отрицательную частоту. Я бы решил группировать по Timedelta
:
out = (df.groupby((max_date-df['date'])//pd.Timedelta('28d'))
['label'].value_counts()
)
# relabel the index
out.index = pd.MultiIndex.from_tuples([
(max_date - pd.to_timedelta(x*28, unit='D'),y) for x,y in out.index
], names=['date','label'])
Вывод:
date label
2021-03-15 a 126
b 124
c 112
2021-02-15 b 138
c 126
a 120
2021-01-18 c 91
b 87
a 76
Name: label, dtype: int64