Завершите группировку pandas на максимальную дату в столбце

#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