简体   繁体   中英

Pandas - Group and then sort

I would like to group and sort a DataFrame so:

  • We first have the largest Region by total count()
  • We then have each Name ordered by count()

I kind of managed to do this in multiple lines but there must be a more pythonic/pandas way.

The goal of this is not only to display the table but also be able to pass it to tools like plot.ly to do a stacked barchart with the region with more counts first and then each name from the largest to the smallest stacked.

So this:

A   B   C
Region1 Name1   1
Region1 Name1   1
Region1 Name2   1
Region2 Name3   1
Region2 Name4   1
Region2 Name4   1
Region2 Name4   1
Region3 Name5   1
Region3 Name6   1

Would become:

A   B   Count C
Region2 Name4   3
        Name3   1
Region1 Name1   2
        Name2   1
Region3 Name5   1
        Name6   1

(Sorry the format is not nice but this should be the right version so you can copy/paste tabular data)

Code to create the first table:

df = pd.DataFrame({'A': {0: 'Region1',
  1: 'Region1',
  2: 'Region1',
  3: 'Region2',
  4: 'Region2',
  5: 'Region2',
  6: 'Region2',
  7: 'Region3',
  8: 'Region3'},
 'B': {0: 'Name1',
  1: 'Name1',
  2: 'Name2',
  3: 'Name3',
  4: 'Name4',
  5: 'Name4',
  6: 'Name4',
  7: 'Name5',
  8: 'Name6'},
 'C': {0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 1}})

Umm, not sure if this is the best way but I am using sort_values and pd.Categorical :

m=(df.groupby(['A','B'])['C'].size().reset_index().sort_values('C',ascending=False)
.reset_index(drop=True))
m.A = pd.Categorical(m.A,m.A.unique(),ordered=True)
m.sort_values('A').reset_index(drop=True)

         A      B  C
0  Region2  Name4  3
1  Region2  Name3  1
2  Region1  Name1  2
3  Region1  Name2  1
4  Region3  Name5  1
5  Region3  Name6  1

The sorting seems to happen on the maximum count by region group, then by the count by region - name group.

To get the desired result, you need to calculate the max / region group & then hide this column before displaying, which you can do like this

df2 = df.groupby(['A', 'B']).agg('count')
df2['maxA'] = df2.groupby('A').C.transform('max')
df2.sort_values(['maxA', 'C'], ascending=[False, False])[['C']]

# produces the following output:

               C
A       B
Region2 Name4  3
        Name3  1
Region1 Name1  2
        Name2  1
Region3 Name5  1
        Name6  1

I would do 2 steps:
Step 1: create the mask index of sorted A by count
Step 2: use .loc to re-order the df and groupby with sort=False and call value_counts

m = df.groupby('A').A.transform('count').sort_values(ascending=False).index
df.loc[m].groupby('A', sort=False).B.value_counts().to_frame('Count')

Out[200]:
               Count
A       B
Region2 Name4      3
        Name3      1
Region1 Name1      2
        Name2      1
Region3 Name5      1
        Name6      1

Another way:

region_totals = df.groupby('A')['C'].sum()
mapping  = dict(zip(region_totals.index, region_totals.values))
df['D'] = df["A"].map(mapping)

df2 = df.groupby(['D','A','B']).sum().sort_values(['D','C'], ascending =False)
df2.index = df2.index.droplevel(0)

df2.rename(columns={'C':'Count'}

                Count
A       B   
Region2 Name4   3
        Name3   1
Region1 Name1   2
        Name2   1
Region3 Name5   1
        Name6   1

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