Pandas: перекрестная таблица на нескольких столбцах, затем Groupby

#python #pandas

#python #pandas

Вопрос:

У меня есть фрейм данных, который выглядит следующим образом.

 df

    visit_date sex region status
0   2019-04-01   m     as   pass
1   2019-04-02   m     as   pass
2   2019-04-02   f     na   pass
3   2019-04-03   f     na   fail
4   2019-04-08   f     na   pass
5   2019-04-09   f     as   pass
6   2019-04-09   m     na   pass
7   2019-04-10   m     as   fail
8   2019-04-15   f     as   fail
9   2019-04-15   m     na   pass
10  2019-04-16   f     na   pass
11  2019-04-17   f     na   fail
 

visit_date есть datetime , а остальные есть categorical (объект).

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

Ожидаемый результат.

             f  m  as  na  fail  pass
visit_date                          
2019-04-07  2  2   2   2     1     3
2019-04-14  2  2   2   2     1     3
2019-04-21  3  1   1   3     2     2
 

Я использовал pd.crosstab и groupby .

 df.visit_date = pd.to_datetime(df.visit_date)

cols = ['sex', 'region', 'status']

df2 = pd.crosstab(df['visit_date'], df[cols[0]])

for i in range(1, len(cols)):
    df2 = df2.join(pd.crosstab(df['visit_date'], df[cols[i]]))

df2.groupby([pd.Grouper(level='visit_date', freq='1W')]).sum()

            f  m  as  na  fail  pass
visit_date                          
2019-04-07  2  2   2   2     1     3
2019-04-14  2  2   2   2     1     3
2019-04-21  3  1   1   3     2     2
 

Проблема в том, что мне нужно снова groupby, чтобы суммировать их еженедельно, и это слишком медленно. Мои фактические данные содержат более 100 столбцов и более 1 миллиона строк.

Есть ли более быстрый способ получить тот же результат?

Ответ №1:

Используйте DataFrame.melt с DataFrameGroupBy.size и Series.unstack для изменения формы:

 cols = ['sex', 'region', 'status']
df1 = (df.melt(id_vars='visit_date', value_vars=cols)
         .groupby([pd.Grouper(key='visit_date', freq='1W'),'value'])
         .size()
         .unstack(fill_value=0))
print (df1)
value       as  f  fail  m  na  pass
visit_date                          
2019-04-07   2  2     1  2   2     3
2019-04-14   2  2     1  2   2     3
2019-04-21   1  3     2  1   3     2
 

Также возможно создать Multiindex :

 cols = ['sex', 'region', 'status']
df2 = (df.melt(id_vars='visit_date', value_vars=cols)
         .groupby([pd.Grouper(key='visit_date', freq='1W'),'variable', 'value'])
         .size()
         .unstack(level=[1,2], fill_value=0))
print (df2)
variable   region    sex    status     
value          as na   f  m   fail pass
visit_date                             
2019-04-07      2  2   2  2      1    3
2019-04-14      2  2   2  2      1    3
2019-04-21      1  3   3  1      2    2
 

Таким образом, возможен выбор по первому уровню:

 print (df2['region'])
value       as  na
visit_date        
2019-04-07   2   2
2019-04-14   2   2
2019-04-21   1   3
 

Или сгладить значения:

 df2.columns = df2.columns.map('_'.join)
print (df2)
            region_as  region_na  sex_f  sex_m  status_fail  status_pass
visit_date                                                              
2019-04-07          2          2      2      2            1            3
2019-04-14          2          2      2      2            1            3
2019-04-21          1          3      3      1            2            2
 

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

1. @ResidentSleeper — Добро пожаловать! Также добавлено еще одно немного измененное решение — может быть, тоже поможет 🙂

2. MultiIndex Решение также помогает мне упростить мою работу. Большое вам спасибо.