Сумма истинных значений за последние n дат в пандах

У меня есть фрейм данных из нескольких тысяч строк со столбцами географии, response_dates и True/False для in_compliance.

df = pd.DataFrame( { 
"geography" : ["Baltimore", "Frederick", "Annapolis", "Hagerstown", "Rockville" , "Salisbury","Towson","Bowie"] , 
"response_date" : ["2018-03-31", "2018-03-30", "2018-03-28", "2018-03-28", "2018-04-02", "2018-03-30","2018-04-07","2018-04-02"],
"in_compliance" : [True, True, False, True, False, True, False, True]})

Я хочу добавить столбец, представляющий количество значений True для последних четырех дат в столбце response_date, включая response_date для этой строки. Пример желаемого результата:

 geography  response_date   in_compliance   Past
    df1 = df.groupby(['city']).apply(lambda x: x.set_index('response_date').resample('1D').first())
    df2 = df1.groupby(level=0)['in_compliance']\
         .apply(lambda x: x.shift().rolling(min_periods=1,window=4).count())\
         .reset_index(name='Past_4_dates_sum_of_true')
dates_sum_of_true Baltimore 2018-03-24 True 1 Baltimore 2018-03-25 False 1 Baltimore 2018-03-26 False 1 Baltimore 2018-03-27 False 1 Baltimore 2018-03-30 False 0 Baltimore 2018-03-31 True 1 Baltimore 2018-04-01 True 2 Baltimore 2018-04-02 True 3 Baltimore 2018-04-03 False 3 Baltimore 2018-04-06 True 3 Baltimore 2018-04-07 True 3 Baltimore 2018-04-08 False 2

Я пробовал разные подходы к группировке и прокатке. Но я получаю результаты, которые не являются теми, которые я ожидаю и которые мне нужны.

df.groupby('city').resample('d').sum().fillna(0).groupby('city').rolling(4,min_periods=1).sum()

Это был еще один подход, который я использовал:

    df1 = df.groupby(['city']).apply(lambda x: x.set_index('response_date').resample('1D').first())
    df2 = df1.groupby(level=0)['in_compliance']\
         .apply(lambda x: x.shift().rolling(min_periods=1,window=4).count())\
         .reset_index(name='Past_4_dates_sum_of_true')

person JamesMiller    schedule 01.12.2019    source источник


Ответы (2)


Я думаю, вы можете использовать rolling с 4days с 4d:

df = df.sort_values(['city','response_date'])
df = df.set_index('response_date')

df['new'] = (df.groupby('city')['in_compliance']
               .rolling('4d',min_periods=1)
               .sum()
               .astype(int)
               .reset_index(level=0, drop=True))
df = df.reset_index()
print (df)
   response_date       city  in_compliance  Past_4_dates_sum_of_true  new
0     2018-03-24  Baltimore           True                         1    1
1     2018-03-25  Baltimore          False                         1    1
2     2018-03-26  Baltimore          False                         1    1
3     2018-03-27  Baltimore          False                         1    1
4     2018-03-30  Baltimore          False                         0    0
5     2018-03-31  Baltimore           True                         1    1
6     2018-04-01  Baltimore           True                         2    2
7     2018-04-02  Baltimore           True                         3    3
8     2018-04-03  Baltimore          False                         3    3
9     2018-04-06  Baltimore           True                         3    1 <-difference because 2018-04-05 missing
10    2018-04-07  Baltimore           True                         3    2
11    2018-04-08  Baltimore          False                         2    2
person jezrael    schedule 01.12.2019
comment
Спасибо - это хорошо работает для последовательностей дат ответов, в которых нет пропусков response_date. Спасибо, что заметили разницу, вызванную пробелом. Есть ли способ заставить скользящий 4d отсчитывать четыре даты в df, а не четыре дня? - person JamesMiller; 02.12.2019
comment
@JamesMiller - вы думаете, как ваш последний абзац? Я нахожу только упростить ваше решение df1 = df.groupby(['city']).apply(lambda x: x.set_index('response_date').resample('1D').first()) до df1 = df.groupby(['city']).resample('1D').first() (до создания DatetimeIndex) - person jezrael; 02.12.2019
comment
Каков наилучший подход к созданию DatetimeIndex? Я пробовал это несколькими способами и получаю сообщение об ошибке, что response_date является экземпляром «Int64Index». - person JamesMiller; 02.12.2019
comment
@JamesMiller использует df = df.set_index('response_date') - person jezrael; 02.12.2019
comment
Спасибо @jezrael. В итоге я добавил столбец количества рабочих дней между датой ответа и началом проверки. Затем я немного изменил ваш код, чтобы получить сумму in_compliance для предыдущих четырех строк, независимо от пробелов в датах. Этот пост помог. - person JamesMiller; 03.12.2019

Это намного проще:

df['Past_4_dates_sum_of_true'] = df.rolling(4, min_periods=1)['in_compliance'].sum().astype(int)

Выход:

       geography response_date  in_compliance  Past_4_dates_sum_of_true
0   Baltimore    2018-03-24           True                         1
1   Baltimore    2018-03-25          False                         1
2   Baltimore    2018-03-26          False                         1
3   Baltimore    2018-03-27          False                         1
4   Baltimore    2018-03-30          False                         0
5   Baltimore    2018-03-31           True                         1
6   Baltimore    2018-04-01           True                         2
7   Baltimore    2018-04-02           True                         3
8   Baltimore    2018-04-03          False                         3
9   Baltimore    2018-04-06           True                         3
10  Baltimore    2018-04-07           True                         3
11  Baltimore    2018-04-08          False                         2
person Aryerez    schedule 01.12.2019