在pandas中,如何根据另一列的平均值创建一个排名新列?

7

I have the following pandas dataframe

+---------+-------+
| Country | value |
+---------+-------+
| UK      |    42 |
| US      |     9 |
| US      |    10 |
| France  |    15 |
| France  |    16 |
| Germany |    17 |
| Germany |    18 |
| Germany |    20 |
+---------+-------+

我想创建一个新的列,根据每个国家的值的平均值从大到小进行排名。

输出结果如下:

+---------+-------+---------+------+
| Country | value | Average | Rank |
+---------+-------+---------+------+
| UK      |    42 |      42 |    1 |
| US      |     9 |     9.5 |    4 |
| US      |    10 |     9.5 |    4 |
| France  |    15 |    15.5 |    3 |
| France  |    16 |    15.5 |    3 |
| Germany |    17 |      18 |    2 |
| Germany |    18 |      18 |    2 |
| Germany |    20 |      18 |    2 |
+---------+-------+---------+------+

请注意,我不需要平均值列,它只是为了说明而存在。
非常感谢。
3个回答

12
使用 groupbytransformmean 进行分组并使用rank进行排名:
df['Average'] = df.groupby('Country')['value'].transform('mean')
df['Rank'] = df['Average'].rank(method='dense', ascending=False)
print (df)
   Country  value    Average  Rank
0       UK     42  42.000000   1.0
1       US      9   9.500000   4.0
2       US     10   9.500000   4.0
3   France     15  15.500000   3.0
4   France     16  15.500000   3.0
5  Germany     17  18.333333   2.0
6  Germany     18  18.333333   2.0
7  Germany     20  18.333333   2.0

类似的解决方案:

a = df.groupby('Country')['value'].transform('mean')
b = a.rank(method='dense', ascending=False)

df = df.assign(Average=a, Rank=b)
print (df)
   Country  value    Average  Rank
0       UK     42  42.000000   1.0
1       US      9   9.500000   4.0
2       US     10   9.500000   4.0
3   France     15  15.500000   3.0
4   France     16  15.500000   3.0
5  Germany     17  18.333333   2.0
6  Germany     18  18.333333   2.0
7  Germany     20  18.333333   2.0

非常清晰、浓缩。虽然应用了相同的方法,但不排除您有不同的想法和见解。所以这类似的解决方案可能并没有什么意义。+1 - Anton vBR
@AntonvBR - 非常感谢你! - jezrael

1

解决方案
我使用了pd.DataFrame.joinpd.concat的组合,之前进行了groupbymean操作。

m = df.groupby('Country').value.mean()
df.join(
    pd.concat([m, m.rank(ascending=False)], axis=1, keys=['Average', 'Rank']),
    on='Country')

   Country  value    Average  Rank
0       UK     42  42.000000   1.0
1       US      9   9.500000   4.0
2       US     10   9.500000   4.0
3   France     15  15.500000   3.0
4   France     16  15.500000   3.0
5  Germany     17  18.333333   2.0
6  Germany     18  18.333333   2.0
7  Germany     20  18.333333   2.0

同样地,使用双重 join
m = df.groupby('Country').value.mean()
df.join(m.rename('Avergage'), on='Country') \
  .join(m.rank(ascending=False).rename('Rank'), on='Country')

   Country  value    Average  Rank
0       UK     42  42.000000   1.0
1       US      9   9.500000   4.0
2       US     10   9.500000   4.0
3   France     15  15.500000   3.0
4   France     16  15.500000   3.0
5  Germany     17  18.333333   2.0
6  Germany     18  18.333333   2.0
7  Germany     20  18.333333   2.0

或者使用 mapassign
m = df.groupby('Country').value.mean()
df.assign(
    Average=df.Country.map(m),
    Rank=df.Country.map(m.rank(ascending=False))
)

   Country  value    Average  Rank
0       UK     42  42.000000   1.0
1       US      9   9.500000   4.0
2       US     10   9.500000   4.0
3   France     15  15.500000   3.0
4   France     16  15.500000   3.0
5  Germany     17  18.333333   2.0
6  Germany     18  18.333333   2.0
7  Germany     20  18.333333   2.0

1
我会使用现代的方法链式编程来避免改变状态和创建新变量:
df = pd.DataFrame(
    {'Country': ['Russia', 'Russia', 'USA'], 'Value': [12, 15, 16]})

df.join(df.groupby('Country').
           mean().
           rank().
           rename(columns={'Value': 'Rank'}),
        on='Country')

有趣的是,但是在两个具有相同值的国家中无法工作。 - Anton vBR
你能提供一个例子吗?我尝试了 {'Country': ['Russia', 'USA'], 'Value': [15, 15]},它给出了 Rank: [1.5, 1.5],这似乎是正确的答案。 - Ilya V. Schurov
没事了,使用 passing rank(method="dense") 解决了我看到的“问题”。 (+1) - Anton vBR
是的,这就是排名的计算方式:如果有几个相等的值,则它们共享可能排名的平均值。在这种情况下,最大排名始终等于元素数量,这很好。 - Ilya V. Schurov
抱歉,我修改了你的帖子,本意是提出建议。 - Anton vBR

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接