简体   繁体   中英

Pandas pivot table Nested Sorting

Given this data frame and pivot table:

import pandas as pd
df=pd.DataFrame({'A':['x','y','z','x','y','z'],
                 'B':['one','one','one','two','two','two'],
                 'C':[7,5,3,4,1,6]})
df


    A   B       C
0   x   one     7
1   y   one     5
2   z   one     3
3   x   two     4
4   y   two     1
5   z   two     6

table = pd.pivot_table(df, index=['A', 'B'],aggfunc=np.sum)

table
A  B  
x  one    7
   two    4
y  one    5
   two    1
z  one    3
   two    6
Name: C, dtype: int64

I want to sort the pivot table such that the order of 'A' is z, x, y and the order of 'B' is based on the descendingly-sorted values from data frame column 'C'.

Like this:

A  B  
z  two    6
   one    3
x  one    7
   two    4
y  one    5
   two    1

    Name: C, dtype: int64

Thanks in advance!

I don't believe there is an easy way to accomplish your objective. The following solution first sorts your table is descending order based on the values of column C . It then concatenates each slice based on your desired order.

order = ['z', 'x', 'y']
table = table.reset_index().sort_values('C', ascending=False)
>>> pd.concat([table.loc[table.A == val, :].set_index(['A', 'B']) for val in order])
       C
A B     
z two  6
  one  3
x one  7
  two  4
y one  5
  two  1

Solution

custom_order = ['z', 'x', 'y']
kwargs = dict(axis=0, level=0, drop_level=False)

new_table = pd.concat(
    [table.xs(idx_v, **kwargs).sort_values(ascending=False) for idx_v in custom_order]
)

Alternate one liner

pd.concat([table.xs(i, drop_level=0).sort_values(ascending=0) for i in list('zxy')]

Explanation

custom_order is your desired order. kwargs is a convenient way to improve readability (in my opinion). Key elements to note, axis=0 and level=0 might be important for you if you want to leverage this further. However, those are also the default values and can be left out. drop_level=False is the key argument here and is necessary to keep the idx_v we are taking a xs of such that the pd.concat puts it all together in the way we'd like.

I use a list comprehension in almost the exact same manner as Alexander within the pd.concat call.

Demonstration

print new_table

A  B  
z  two    6
   one    3
x  one    7
   two    4
y  one    5
   two    1
Name: C, dtype: int64

If you can read in column A as categorical data, then it becomes much more straightforward. Setting your categories as list('zxy') and specifying ordered=True uses your custom ordering.

You can read in your data using something similar to:

'A':pd.Categorical(['x','y','z','x','y','z'], list('zxy'), ordered=True)

Alternatively, you can read in the data as you currently are, then use astype to convert A to categorical:

df['A'] = df['A'].astype('category', categories=list('zxy'), ordered=True)

Once A is categorical, you can pivot the same as before, and then sort with:

table = table.sort_values(ascending=False).sortlevel(0, sort_remaining=False)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM