在DataFrame中为每个组枚举每一行

23
在 pandas 中,如何根据给定的分组方式为行添加一个新列来进行编号?例如,假设有以下 DataFrame:
import pandas as pd
import numpy as np

a_list = ['A', 'B', 'C', 'A', 'A', 'C', 'B', 'B', 'A', 'C']
df = pd.DataFrame({'col_a': a_list, 'col_b': range(10)})
df
  col_a  col_b
0     A      0
1     B      1
2     C      2
3     A      3
4     A      4
5     C      5
6     B      6
7     B      7
8     A      8
9     C      9

我想添加一个名为col_c的列,它根据col_a的分组和col_b的排序给出第N行所属的“组”的值。

期望输出:

  col_a  col_b  col_c
0     A      0      1
3     A      3      2
4     A      4      3
8     A      8      4
1     B      1      1
6     B      6      2
7     B      7      3
2     C      2      1
5     C      5      2
9     C      9      3

我正在努力访问col_c。你可以使用.sort_index(by=['col_a', 'col_b'])进行适当的分组和排序,现在的问题是如何到达新列并为每一行打上标签。

3个回答

40

对于这种情况,有一个名为cumcount的函数可以精确实现:

df['col_c'] = g.cumcount()

正如文档中所述:
对于每个组,将其中的每个项目从0到该组长度减1进行编号。
在定义cumcount之前的原始答案。

您可以创建一个辅助函数来完成此操作:

def add_col_c(x):
    x['col_c'] = np.arange(len(x))
    return x

首先按照列 col_a 进行排序:

In [11]: df.sort('col_a', inplace=True)

然后将此函数应用于每个组:
In [12]: g = df.groupby('col_a', as_index=False)

In [13]: g.apply(add_col_c)
Out[13]:
  col_a  col_b  col_c
3     A      3      0
8     A      8      1
0     A      0      2
4     A      4      3
6     B      6      0
1     B      1      1
7     B      7      2
9     C      9      0
2     C      2      1
5     C      5      2

为了获得 1,2,...,您可以使用 np.arange(1, len(x) + 1)

这基本上回答了问题 - 尽管 gjreda 要求按 col_b 排序并枚举 col_a。 - gabe
太棒了,谢谢@AndyHayden。我知道这将是一个groupby,但是无法正确应用它。 - Greg Reda

4
给出的两个答案都涉及为每个组调用一个Python函数,如果你有很多组,向量化方法应该更快(我还没有检查过)。
以下是我的纯NumPy建议:
In [5]: df.sort(['col_a', 'col_b'], inplace=True, ascending=(False, False))
In [6]: sizes = df.groupby('col_a', sort=False).size().values
In [7]: df['col_c'] = np.arange(sizes.sum()) - np.repeat(sizes.cumsum() - sizes, sizes)
In [8]: print df
  col_a  col_b  col_c
9     C      9      0
5     C      5      1
2     C      2      2
7     B      7      0
6     B      6      1
1     B      1      2
8     A      8      0
4     A      4      1
3     A      3      2
0     A      0      3

2
你可以定义自己的函数来处理这个问题:
In [58]: def func(x):
   ....:     x['col_c'] = x['col_a'].argsort() + 1 
   ....:     return x
   ....: 

In [59]: df.groupby('col_a').apply(func)
Out[59]: 
  col_a  col_b  col_c
0     A      0      1   
3     A      3      2   
4     A      4      3   
8     A      8      4   
1     B      1      1   
6     B      6      2   
7     B      7      3   
2     C      2      1   
5     C      5      2   
9     C      9      3

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