使用groupby()时,如何使用包含组号索引的新列创建DataFrame,类似于R中的dplyr::group_indices
。例如,如果我有
>>> df=pd.DataFrame({'a':[1,1,1,2,2,2],'b':[1,1,2,1,1,2]})
>>> df
a b
0 1 1
1 1 1
2 1 2
3 2 1
4 2 1
5 2 2
如何获得像
这样的DataFrame a b idx
0 1 1 1
1 1 1 1
2 1 2 2
3 2 1 3
4 2 1 3
5 2 2 4
(idx
索引的顺序无关紧要)
答案 0 :(得分:15)
一种简单的方法是连接分组列(以便它们的每个值组合代表一个独特的不同元素),然后将其转换为pandas Categorical并仅保留其标签:
df['idx'] = pd.Categorical(df['a'].astype(str) + '_' + df['b'].astype(str)).codes
df
a b idx
0 1 1 0
1 1 1 0
2 1 2 1
3 2 1 2
4 2 1 2
5 2 2 3
修改:将labels
属性更改为codes
,因为前者似乎已被弃用
Edit2:根据Authman Apatira
的建议添加了一个分隔符答案 1 :(得分:13)
以下是使用drop_duplicates
和merge
获取唯一标识符的简明方法。
group_vars = ['a','b']
df.merge( df.drop_duplicates( group_vars ).reset_index(), on=group_vars )
a b index
0 1 1 0
1 1 1 0
2 1 2 2
3 2 1 3
4 2 1 3
5 2 2 5
在这种情况下,标识符为0,2,3,5(只是原始索引的残差),但可以通过额外的reset_index(drop=True)
轻松更改为0,1,2,3。
答案 2 :(得分:10)
这是使用a comment above by Constantino中的ngroup
的解决方案,对于那些仍在寻找此功能的用户(如果您尝试使用像我这样的关键字在Google中搜索,则相当于R中的dplyr::group_indices
)。这也比根据我自己的时机最大化实现的解决方案快25%。
>>> import pandas as pd
>>> df = pd.DataFrame({'a':[1,1,1,2,2,2],'b':[1,1,2,1,1,2]})
>>> df['idx'] = df.groupby(['a', 'b']).ngroup()
>>> df
a b idx
0 1 1 0
1 1 1 0
2 1 2 1
3 2 1 2
4 2 1 2
5 2 2 3
>>> %timeit df['idx'] = create_index_usingduplicated(df, grouping_cols=['a', 'b'])
1.83 ms ± 67.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
>>> %timeit df['idx'] = df.groupby(['a', 'b']).ngroup()
1.38 ms ± 30 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
答案 3 :(得分:2)
我认为这种方式比当前接受的答案快一个数量级(下面的时间结果):
def create_index_usingduplicated(df, grouping_cols=['a', 'b']):
df.sort_values(grouping_cols, inplace=True)
# You could do the following three lines in one, I just thought
# this would be clearer as an explanation of what's going on:
duplicated = df.duplicated(subset=grouping_cols, keep='first')
new_group = ~duplicated
return new_group.cumsum()
计时结果:
a = np.random.randint(0, 1000, size=int(1e5))
b = np.random.randint(0, 1000, size=int(1e5))
df = pd.DataFrame({'a': a, 'b': b})
In [6]: %timeit df['idx'] = pd.Categorical(df['a'].astype(str) + df['b'].astype(str)).codes
1 loop, best of 3: 375 ms per loop
In [7]: %timeit df['idx'] = create_index_usingduplicated(df, grouping_cols=['a', 'b'])
100 loops, best of 3: 17.7 ms per loop
答案 4 :(得分:0)
我不确定这是一个如此微不足道的问题。这是一个有点复杂的解决方案,首先对分组列进行排序,然后检查每一行是否与前一行不同,如果是,则累加1.请在下面进一步检查字符串数据的答案。
df.sort_values(['a', 'b']).diff().fillna(0).ne(0).any(1).cumsum().add(1)
输出
0 1
1 1
2 2
3 3
4 3
5 4
dtype: int64
所以将其分解为步骤,让我们看看df.sort_values(['a', 'b']).diff().fillna(0)
的输出,它检查每行是否与前一行不同。任何非零条目都表示新组。
a b
0 0.0 0.0
1 0.0 0.0
2 0.0 1.0
3 1.0 -1.0
4 0.0 0.0
5 0.0 1.0
新组只需要有一个不同的列,这就是.ne(0).any(1)
检查的内容 - 对于任何列都不等于0。然后只是一个累积的总和来跟踪群体。
#create fake data and sort it
df=pd.DataFrame({'a':list('aabbaccdc'),'b':list('aabaacddd')})
df1 = df.sort_values(['a', 'b'])
df1
a b
0 a a
1 a a
4 a a
3 b a
2 b b
5 c c
6 c d
8 c d
7 d d
通过检查组是否已更改来采取类似的方法
df1.ne(df1.shift().bfill()).any(1).cumsum().add(1)
0 1
1 1
4 1
3 2
2 3
5 4
6 5
8 5
7 6
答案 5 :(得分:0)
定义不是最简单的解决方案,但这是我要做的(代码中的注释):
df=pd.DataFrame({'a':[1,1,1,2,2,2],'b':[1,1,2,1,1,2]})
#create a dummy grouper id by just joining desired rows
df["idx"] = df[["a","b"]].astype(str).apply(lambda x: "".join(x),axis=1)
print df
这会为a
和b
的每个组合生成唯一身份证。
a b idx
0 1 1 11
1 1 1 11
2 1 2 12
3 2 1 21
4 2 1 21
5 2 2 22
但这仍然是一个相当愚蠢的指数(考虑列a
和b
中的一些更复杂的值。所以让我们清楚索引:
# create a dictionary of dummy group_ids and their index-wise representation
dict_idx = dict(enumerate(set(df["idx"])))
# switch keys and values, so you can use dict in .replace method
dict_idx = {y:x for x,y in dict_idx.iteritems()}
#replace values with the generated dict
df["idx"].replace(dict_idx,inplace=True)
print df
这将产生所需的输出:
a b idx
0 1 1 0
1 1 1 0
2 1 2 1
3 2 1 2
4 2 1 2
5 2 2 3