Bartek Nowakowski
Bartek Nowakowski

Reputation: 271

Python - pivot DataFrame with multiple indexes on columns

I have a simple df like below:

           ID     Provider  Single_Cost    Bundle_ID  Bundle_Cost
0      L_0001         P_01       1075.0          NaN          NaN
1      L_0002         P_02        590.0          NaN          NaN
2      L_0003         P_02       6900.0          NaN          NaN
3      L_0004         P_02        625.0          NaN          NaN
4      L_0005         P_02       5775.0          NaN          NaN
5      L_0006         P_02        495.0          NaN          NaN
6      L_0007         P_02        570.0          NaN          NaN
7      L_0008         P_02       1250.0          NaN          NaN
8      L_0009         P_03       2940.0       P_03_1       1470.0
9      L_0010         P_03       7608.0       P_03_1       7308.0

And I need to transform it to get 2 levels of columns:

     Provider  P_01                                 P_02                                 P_03
           ID  Single_Cost  Bundle_ID  Bundle_Cost  Single_Cost  Bundle_ID  Bundle_Cost  Single_Cost  Bundle_ID  Bundle_Cost
0      L_0001      1075.0         NaN          NaN          NaN       NaN           NaN          NaN       NaN           NaN
1      L_0002         NaN         NaN          NaN        590.0       NaN           NaN          NaN       NaN           NaN
2      L_0003         NaN         NaN          NaN       6900.0       NaN           NaN          NaN       NaN           NaN
3      L_0004         NaN         NaN          NaN        625.0       NaN           NaN          NaN       NaN           NaN
4      L_0005         NaN         NaN          NaN       5775.0       NaN           NaN          NaN       NaN           NaN
5      L_0006         NaN         NaN          NaN        495.0       NaN           NaN          NaN       NaN           NaN
6      L_0007         NaN         NaN          NaN        570.0       NaN           NaN          NaN       NaN           NaN
7      L_0008         NaN         NaN          NaN       1250.0       NaN           NaN          NaN       NaN           NaN
8      L_0009         NaN         NaN          NaN          NaN       NaN           NaN       2940.0    P_03_1        1470.0
9      L_0010         NaN         NaN          NaN          NaN       NaN           NaN       7608.0    P_03_1        7308.0

I think it can be done somehow with merging 2 dataframes after groupby, but I'm not sure how to start. Can you help?

Upvotes: 1

Views: 50

Answers (1)

jezrael
jezrael

Reputation: 863711

Use modified another solution with pass 2 columns to set_index, last reset ID column and rename it to correct MultiIndex labels, here (Provider, ID):

df = (df.set_index(['ID','Provider'], append=True)
        .unstack()
        .swaplevel(1, 0, axis=1)
        .sort_index(axis=1)
        .reset_index(level=1)
        .rename_axis((None, None), axis=1)
        .rename(columns={'':'ID'})
        .rename(columns={'ID':'Provider'}, level=0))
print (df)
  Provider        P_01                              P_02            \
        ID Bundle_Cost Bundle_ID Single_Cost Bundle_Cost Bundle_ID   
0   L_0001         NaN       NaN      1075.0         NaN       NaN   
1   L_0002         NaN       NaN         NaN         NaN       NaN   
2   L_0003         NaN       NaN         NaN         NaN       NaN   
3   L_0004         NaN       NaN         NaN         NaN       NaN   
4   L_0005         NaN       NaN         NaN         NaN       NaN   
5   L_0006         NaN       NaN         NaN         NaN       NaN   
6   L_0007         NaN       NaN         NaN         NaN       NaN   
7   L_0008         NaN       NaN         NaN         NaN       NaN   
8   L_0009         NaN       NaN         NaN         NaN       NaN   
9   L_0010         NaN       NaN         NaN         NaN       NaN   

                     P_03                        
  Single_Cost Bundle_Cost Bundle_ID Single_Cost  
0         NaN         NaN       NaN         NaN  
1       590.0         NaN       NaN         NaN  
2      6900.0         NaN       NaN         NaN  
3       625.0         NaN       NaN         NaN  
4      5775.0         NaN       NaN         NaN  
5       495.0         NaN       NaN         NaN  
6       570.0         NaN       NaN         NaN  
7      1250.0         NaN       NaN         NaN  
8         NaN      1470.0    P_03_1      2940.0  
9         NaN      7308.0    P_03_1      7608.0  

Another idea is create tuples and assign new columns, here MultiIndex by MultiIndex.from_tuples:

df = (df.set_index(['ID','Provider'], append=True)
        .unstack()
        .swaplevel(1, 0, axis=1)
        .sort_index(axis=1)
        .reset_index(level=1)
        .rename_axis((None, None), axis=1))

mux = [('Provider', 'ID')] + df.columns.tolist()[1:]
df.columns = pd.MultiIndex.from_tuples(mux)

print (df)
  Provider        P_01                              P_02            \
        ID Bundle_Cost Bundle_ID Single_Cost Bundle_Cost Bundle_ID   
0   L_0001         NaN       NaN      1075.0         NaN       NaN   
1   L_0002         NaN       NaN         NaN         NaN       NaN   
2   L_0003         NaN       NaN         NaN         NaN       NaN   
3   L_0004         NaN       NaN         NaN         NaN       NaN   
4   L_0005         NaN       NaN         NaN         NaN       NaN   
5   L_0006         NaN       NaN         NaN         NaN       NaN   
6   L_0007         NaN       NaN         NaN         NaN       NaN   
7   L_0008         NaN       NaN         NaN         NaN       NaN   
8   L_0009         NaN       NaN         NaN         NaN       NaN   
9   L_0010         NaN       NaN         NaN         NaN       NaN   

                     P_03                        
  Single_Cost Bundle_Cost Bundle_ID Single_Cost  
0         NaN         NaN       NaN         NaN  
1       590.0         NaN       NaN         NaN  
2      6900.0         NaN       NaN         NaN  
3       625.0         NaN       NaN         NaN  
4      5775.0         NaN       NaN         NaN  
5       495.0         NaN       NaN         NaN  
6       570.0         NaN       NaN         NaN  
7      1250.0         NaN       NaN         NaN  
8         NaN      1470.0    P_03_1      2940.0  
9         NaN      7308.0    P_03_1      7608.0  

Upvotes: 3

Related Questions