Как создать сводную таблицу с двумя различными агрегатами - PullRequest
1 голос
/ 03 октября 2019

У меня есть набор данных, для которого я хотел бы выполнить несколько шагов агрегации, используя. Этот код создает данные:

import pandas as pd
import numpy as np

df1 = pd.DataFrame({'Name': ['A', 'A', 'B', 'B'],
                    'S': [200, 100, 300, 400],
                    'Date': pd.to_datetime(['2019-01-01', '2019-01-01', '2019-02-01', '2019-03-01']).date,
                    'Value': [5, 10, 30, 40]})

выход:

df1: 
  Name    S        Date  Value
0    A  200  2019-01-01      5
1    A  100  2019-01-01     10
2    B  300  2019-02-01     30
3    B  400  2019-03-01     40

Конечный результат агрегации должен выглядеть следующим образом:

                2019-01-01  2019-02-01  2019-03-01
A   100, 200            15      
B   300 - 400                       30          40

Первый шаг Idid is

df2 = df.groupby(by=['Name', 'Date']).agg({'S': lambda x: ', '
                             .join(pd.DataFrame([str(s) for s in x]).drop_duplicates()
                                                                    .sort_values(by=0)
                                                                    .iloc[:, 0]
                                                                    .map(str)),
                            'Value': np.sum,})

Часть .join(...) немного запутана, но принимает числа в S, отбрасывает дубликаты, сортирует и объединяет их в строку.

В результате получаетсяэто:

df2: 
                        S  Value
Name Date                       
A    2019-01-01  100, 200     15
B    2019-02-01       300     30
     2019-03-01       400     40

и вот я застрял. Я могу сгенерировать следующее:

df3 = (df2.pivot_table('Value', index=['Name', 'S'], columns=['Date'], 
                      aggfunc={'Value': np.sum})
                    .fillna(0)
                    .reset_index()
                    )
df3: 
Date Name         S  2019-01-01  2019-02-01  2019-03-01
0       A  100, 200        15.0         0.0         0.0
1       B       300         0.0        30.0         0.0
2       B       400         0.0         0.0        40.0

Однако я бы хотел, чтобы две последние строки были объединены, с S, становящимся 300 - 400 (аналогично соединению для df2). Я не выяснил, как я могу объединить эти агрегаты в один шаг (смешивая groupby и pivot_table).

Спасибо за помощь.

Ответы [ 2 ]

1 голос
/ 03 октября 2019

Попробуйте:

val=df1.groupby(['Name','Date'])['Value'].sum().reset_index() # get aggregate sum of values
ind=df1.groupby('Name').apply(lambda x: '-'.join([str(i) for i in x.S.values])).reset_index() # Prepare index for target dataframe
target_df=ind.merge(val, on=['Name']).pivot_table(index=['Name', 0], columns=['Date'], values='Value').fillna(0) # Merge both and pivot to get desired output

Затем print(target_df) даст желаемый результат:

Date          2019-01-01  2019-02-01  2019-03-01
Name 0                                          
A    200-100        15.0         0.0         0.0
B    300-400         0.0        30.0        40.0
0 голосов
/ 03 октября 2019

@ Парт дал правильное понимание того, что невозможно выполнить оба шага (агрегировать числа и pivot_table данные, и одновременно запустить groupby), но вы должны отделить создание будущегоИндекс, а затем поворот данных.

Вот мой окончательный код (небольшое отличие от @ parth):

import pandas as pd
import numpy as np

df1 = pd.DataFrame({'Name': ['A', 'A', 'B', 'B'],
                    'S': [200, 100, 300, 400],
                    'Date': pd.to_datetime(['2019-01-01', '2019-01-01', '2019-02-01', '2019-03-01']).date,
                    'Value': [5, 10, 30, 40]})

print('df1: \n', df1, '\n')

df2 = df.groupby(by=['Name', 'Date']).agg({'S': lambda x: ', '
                             .join(pd.DataFrame([str(s) for s in x]).drop_duplicates()
                                                                    .sort_values(by=0)
                                                                    .iloc[:, 0]
                                                                    .map(str)),
                            'Value': np.sum,})
print('df2:\n ', df2, '\n')

ind = (df2.groupby(by=['Name']).agg({'S': lambda x: ' - '.join(pd.DataFrame([str(s) for s in x])
                                                          .drop_duplicates()
                                                          .sort_values(by=0)
                                                          .iloc[:, 0]
                                                          .map(str))})
                             .reset_index())
print('ind:\n', ind, '\n')

val = df1.groupby(['Name','Date'])['Value'].sum().reset_index() # get aggregate sum of values
print('val:\n', val, '\n')

df3 = (ind.merge(val, on=['Name'])
          .pivot_table(index=['Name', 'S'], columns=['Date'], values='Value')
          .fillna(0)
          )
print('df3\n', df3)

...