繁体   English   中英

Pandas 按多列分组和排序

[英]Pandas Group By and Sorting by multiple columns

我有一些看起来像这样的初始数据:

code          type          value
1111          Golf     Acceptable
1111          Golf    Undesirable
1111    Basketball     Acceptable
1111    Basketball    Undesirable
1111    Basketball    Undesirable

我正在尝试将其按code分组并type列以获取出现次数最多的行。 在平局的情况下,我想 select 具有值Undesirable的行。 所以上面的例子会变成这样:

code          type          value
1111          Golf    Undesirable
1111    Basketball    Undesirable

目前我正在这样做:

df = pd.DataFrame(df.groupby(['code', 'type', 'value']).size().reset_index(name='count'))

df = df.sort_values(['type', 'count'])

df = pd.DataFrame(df.groupby(['code', 'type']).last().reset_index())

我已经对此进行了一些测试,它似乎可以满足我的要求,但我真的不喜欢信任.last()调用,并希望在平局的情况下Undesirable排在最后。 有没有更好的方法来分组这个以确保我总是得到更高的计数,或者在平局Undesirable的情况下是不受欢迎的值?

性能并不是什么大问题,因为我只处理大约 50k 行左右。

情况1

如果value列仅包含两个值,即['Acceptable', 'Undesirable']那么我们可以依赖Acceptable < Undesirable按字母顺序排列的事实。 在这种情况下,您可以使用以下简化的解决方案。

创建一个名为count的辅助列,其中包含每个codetypevalue的行数。 然后按countvaluetype进行排序,并按code删除重复项并保留最后一行。

c = ['code', 'type']
df['count'] = df.groupby([*c, 'value'])['value'].transform('count')
df.sort_values(['count', 'value']).drop_duplicates(c, keep='last')

案例2

如果value列包含其他值并且您不能依赖字母顺序,请使用以下解决方案,该解决方案类似于案例 1 中提出的解决方案,但这首先将值列转换为有序Categorical类型,然后再进行排序

c = ['code', 'type']

df['count'] = df.groupby([*c, 'value'])['value'].transform('count')
df['value'] = pd.Categorical(df['value'], categories=['Acceptable', 'Undesirable'], ordered=True)

df.sort_values(['count', 'value']).drop_duplicates(c, keep='last')

结果

   code        type        value  count
1  1111        Golf  Undesirable      1
4  1111  Basketball  Undesirable      2

另一种可能的解决方案,基于以下思路:

  1. codetype对数据进行分组。

  2. 如果一个组有不止一行( len(x) > 1 )并且它的行有相同的计数( x['count'] == x['count'].min()).all() ) ,返回带有Undesirable的行。

  3. 否则,返回计数最大的行 ( x.iloc[[x['count'].argmax()]] )。

(df.groupby(['code', 'type', 'value'])['value'].size()
 .reset_index(name='count').groupby(['code', 'type'])
 .apply(lambda x: x.loc[x['value'] == 'Undesirable'] if 
        ((len(x) > 1) and (x['count'] == x['count'].min()).all()) else
        x.iloc[[x['count'].argmax()]])
 .reset_index(drop=True)
 .drop('count', axis=1))

Output:

   code        type        value
0  1111  Basketball  Undesirable
1  1111        Golf  Undesirable

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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