группировка, подсчет по времени, а затем сортировка внутри группы с использованием панд

#python #pandas

Вопрос:

У меня есть следующие панды dataframe

                  EventID    Institution_Name
TimeCreated     
2021-03-22 15:34:46 40  H1
2021-03-22 18:17:19 40  H2
2021-03-22 20:37:47 40  H2
2021-03-22 20:40:20 40  H2
2021-03-22 21:37:32 40  H2
2021-03-22 22:16:32 40  H2
2021-03-22 23:19:49 40  H2
2021-03-22 23:26:40 40  H2
2021-03-23 00:26:03 40  H3
2021-03-23 01:25:43 40  H4
2021-03-23 04:00:24 40  H5
2021-03-23 13:09:42 40  H6
2021-03-23 13:13:23 40  H1
2021-03-23 15:49:33 40  H7
2021-03-23 17:22:30 40  H8
2021-03-23 17:22:37 40  H8
2021-03-23 17:23:49 40  H9
2021-03-23 18:19:56 40  H2
2021-03-23 18:22:14 40  H2
2021-03-23 18:52:36 40  H10
 

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

 TimeCreated     Institution_Name EventID_count
2021-03-22       H2                7
2021-03-22       H1                1
....
2021-03-23       H2                2
and so on
 

Я использую следующее —

 grouper = df.groupby([pd.Grouper(freq='1D'), 'Institution_Name'])
grouper['EventID'].count().reset_index().sort_values(['TimeCreated'],ascending=True).sort_values('EventID', ascending=False).head(5)

 but this does not give the desired result.
 

Ответ №1:

  1. Сгруппируйте по 2 столбцам
 grouper = df.groupby([pd.Grouper(key='TimeCreated', freq='1D'), 'Institution_Name'])

grouper = grouper.count().groupby('TimeCreated', group_keys=False)
 
  1. Отсортируйте элементы(количество) в каждой группе дат
 grouper_count_desc = grouper.apply(lambda x: x.sort_values(by='EventID', ascending=False))
 
 In[65]: grouper_count_desc
Out[65]: 
                              EventID
TimeCreated Institution_Name         
2021-03-22  H2                      7
            H1                      1
2021-03-23  H2                      2
            H8                      2
            H1                      1
            H10                     1
            H3                      1
            H4                      1
            H5                      1
            H6                      1
            H7                      1
            H9                      1
 
  1. Отсортируйте группы по дате. Порядок элементов в каждой группе не изменится
 grouper_date_asc = grouper_count_desc.sort_values(by='TimeCreated', ascending=True)
 
 In[70]: grouper_date_desc = grouper_count_desc.sort_values(by='TimeCreated', ascending=False) # to show result, I used descending
In[71]: grouper_date_desc
Out[71]: 
                              EventID
TimeCreated Institution_Name         
2021-03-23  H2                      2
            H8                      2
            H1                      1
            H10                     1
            H3                      1
            H4                      1
            H5                      1
            H6                      1
            H7                      1
            H9                      1
2021-03-22  H2                      7
            H1                      1

 
  1. Сбросить индекс и показать результат
 print(grouper_date_asc.reset_index())
 

Ответ №2:

Вы можете использовать dt.normalize() , чтобы получить дату для группировки. Агрегируйте подсчет по .GroupBy.agg() столбцам, а затем отсортируйте их следующим образом:

 (df.groupby([df['TimeCreated'].dt.normalize(),
             'Institution_Name'])
   .agg(EventID_count=('EventID', 'count'))
   .reset_index()
   .sort_values(['TimeCreated', 'Institution_Name'], ascending=[True, False], ignore_index=True)
)
 

Если у вас TimeCreated есть индекс, вы можете использовать df.index.normalize() его следующим образом:

 (df.groupby([df.index.normalize(),
             'Institution_Name'])
   .agg(EvenetID_count=('EventID', 'count'))
   .reset_index()
   .sort_values(['TimeCreated', 'Institution_Name'], ascending=[True, False], ignore_index=True)
)
 

Результат:

    TimeCreated Institution_Name  EventID_count
0   2021-03-22               H2              7
1   2021-03-22               H1              1
2   2021-03-23               H9              1
3   2021-03-23               H8              2
4   2021-03-23               H7              1
5   2021-03-23               H6              1
6   2021-03-23               H5              1
7   2021-03-23               H4              1
8   2021-03-23               H3              1
9   2021-03-23               H2              2
10  2021-03-23              H10              1
11  2021-03-23               H1              1
 

Ваш код на самом деле довольно близок (для случая TimeCreated это индекс), просто нужно изменить способ сортировки столбцов, как показано ниже:

 grouper = df.groupby([pd.Grouper(freq='1D'), 'Institution_Name'])
grouper['EventID'].count().reset_index().sort_values(['TimeCreated', 'Institution_Name'], ascending=[True, False], ignore_index=True)
 

Результат этих кодов такой же, как и выше, за исключением того, что имя столбца для EventID остается как EventID вместо EventID_count .

Ответ №3:

Вы можете использовать pandas.Series.dt.floor :

 (df.groupby([df['TimeCreated'].dt.floor('d'),
             'Institution_Name'])
 [['EventID']].count()
 .add_suffix('_count')
 .sort_values(['TimeCreated', 'Institution_Name'], ascending=[True, False])
 .reset_index()
)
 

выход:

    TimeCreated Institution_Name  EventID_count
0   2021-03-22               H2              7
1   2021-03-22               H1              1
2   2021-03-23               H9              1
3   2021-03-23               H8              2
4   2021-03-23               H7              1
5   2021-03-23               H6              1
6   2021-03-23               H5              1
7   2021-03-23               H4              1
8   2021-03-23               H3              1
9   2021-03-23               H2              2
10  2021-03-23              H10              1
11  2021-03-23               H1              1
 

Ваша первоначальная попытка не сработала, так как Grouper не знал, где найти ваши даты (по умолчанию он использует индекс). Вот два способа исправить это.

определите имя столбца:

 (df.groupby([pd.Grouper(freq='1D', key='TimeCreated'),
             'Institution_Name'])
   [['EventID']].count()
   .add_suffix('_count')
   .sort_values(['TimeCreated', 'Institution_Name'], ascending=[True, False])
   .reset_index()
)

 

используйте столбец в качестве индекса:

 (df.set_index('TimeCreated')
   .groupby([pd.Grouper(freq='1D'),
             'Institution_Name'])
   [['EventID']].count()
   .add_suffix('_count')
   .sort_values(['TimeCreated', 'Institution_Name'], ascending=[True, False])
   .reset_index()
)

 

Комментарии:

1. EventID_count должен быть в порядке убывания, в то время как дата должна быть в порядке возрастания одновременно. Посмотрите на мои результаты.