Pandas groupBy с условной группировкой

У меня есть два кадра данных, и мне нужно сгруппировать первый на основе некоторых критериев из второго df.

df1= 
     summary  participant_id response_date
0        2.0              11    2016-04-30
1        3.0              11    2016-05-01
2        3.0              11    2016-05-02
3        3.0              11    2016-05-03
4        3.0              11    2016-05-04
5        3.0              11    2016-05-05
6        3.0              11    2016-05-06
7        4.0              11    2016-05-07
8        4.0              11    2016-05-08
9        3.0              11    2016-05-09
10       3.0              11    2016-05-10
11       3.0              11    2016-05-11
12       3.0              11    2016-05-12
13       3.0              11    2016-05-13
14       3.0              11    2016-05-14
15       3.0              11    2016-05-15
16       3.0              11    2016-05-16
17       4.0              11    2016-05-17
18       3.0              11    2016-05-18
19       3.0              11    2016-05-19
20       3.0              11    2016-05-20
21       4.0              11    2016-05-21
22       4.0              11    2016-05-22
23       4.0              11    2016-05-23
24       3.0              11    2016-05-24
25       3.0              11    2016-05-25
26       3.0              11    2016-05-26
27       3.0              11    2016-05-27
28       3.0              11    2016-05-28
29       3.0              11    2016-05-29
..       ...             ...           ... 

df2 =
    summary  participant_id response_date
0      12.0              11    2016-04-30
1      12.0              11    2016-05-14
2      14.0              11    2016-05-28
.       ...             ...           ...     

Мне нужно сгруппировать (получить блоки) df1 между датами в столбце df2. А именно:

df1= 
         summary  participant_id response_date
             2.0              11    2016-04-30

             3.0              11    2016-05-01
             3.0              11    2016-05-02
             3.0              11    2016-05-03
             3.0              11    2016-05-04
             3.0              11    2016-05-05
             3.0              11    2016-05-06
             4.0              11    2016-05-07
             4.0              11    2016-05-08
             3.0              11    2016-05-09
             3.0              11    2016-05-10
             3.0              11    2016-05-11
             3.0              11    2016-05-12
             3.0              11    2016-05-13
             3.0              11    2016-05-14

             3.0              11    2016-05-15
             3.0              11    2016-05-16
             4.0              11    2016-05-17
             3.0              11    2016-05-18
             3.0              11    2016-05-19
             3.0              11    2016-05-20
             4.0              11    2016-05-21
             4.0              11    2016-05-22
             4.0              11    2016-05-23
             3.0              11    2016-05-24
             3.0              11    2016-05-25
             3.0              11    2016-05-26
             3.0              11    2016-05-27
             3.0              11    2016-05-28

             3.0              11    2016-05-29
    ..       ...             ...           ... 

Есть ли элегантное решение с groupby?


person Arnold Klein    schedule 18.06.2017    source источник


Ответы (2)


Может быть более элегантное решение, но вы можете пройтись по значениям response_date в df2 и создать логическую серию значений, сверившись со всеми значениями response_date в df1 и просто просуммировав их все.

df1['group'] = 0
for rd in df2.response_date.values:
    df1['group'] += df1.response_date > rd

Выход:

   summary  participant_id response_date  group
0      2.0              11    2016-04-30      0
1      3.0              11    2016-05-01      1
2      3.0              11    2016-05-02      1
3      3.0              11    2016-05-03      1
4      3.0              11    2016-05-04      1

Создание ответа @Scott:

Вы можете использовать pd.cut, но вам нужно будет добавить дату до самой ранней даты и после самой последней даты в response_date из df2.

dates = [pd.Timestamp('2000-1-1')] + 
         df2.response_date.sort_values().tolist() + 
        [pd.Timestamp('2020-1-1')]
df1['group'] = pd.cut(df1['response_date'], dates)
person Ted Petrou    schedule 18.06.2017
comment
спасибо, но я намеренно хотел бы избежать использования циклов при работе с Pandas. - person Arnold Klein; 18.06.2017
comment
@ArnoldKlein Я согласен, что лучше избегать циклов, хотя вы не должны исключать их без разбора. Мне нравится решение @Scott, и я немного изменил его, чтобы охватить больше случаев. Судя по опубликованным вами данным, response_date относится к типу datetime. Вы должны убедиться, что оба столбца DataFrames имеют одинаковый тип данных. - person Ted Petrou; 18.06.2017
comment
Я закончил с вашим решением с циклом. Я не получил хорошего ответа, используя предложение Скотта, изучу его позже, но в то же время циклы времени служат хорошо. - person Arnold Klein; 19.06.2017

Вам нужен метод .cut. Это позволяет вам группировать даты по какому-то другому списку дат.

df1['cuts'] = pd.cut(df1['response_date'], df2['response_date'])
grouped = df1.groupby('cuts')
print grouped.max()  #for example
person Him    schedule 18.06.2017
comment
не получилось: TypeError: can't compare datetime.timedelta to int - person Arnold Klein; 18.06.2017
comment
Это умно, но я думаю, вам нужна минимальная дата и максимальная дата, чтобы избежать пропущенных значений для тех, кто находится вне диапазона от df2.response_date - person Ted Petrou; 18.06.2017