DataFrame按重复行排序

7

如何对DataFrame进行排序,使得重复的列被“循环使用”?

例如,我的原始DataFrame看起来像这样:

In [3]: df
Out[3]: 
    A  B
0  r1  0
1  r1  1
2  r2  2
3  r2  3
4  r3  4
5  r3  5

I would like it to turn to:

In [3]: df_sorted
Out[3]: 
    A  B
0  r1  0
2  r2  2
4  r3  4
1  r1  1
3  r2  3
5  r3  5

行按列 A 的循环方式进行排序。

我在Pandas中搜索了API,但似乎没有适当的方法来完成此操作。我可以编写一个复杂的函数来完成这个任务,但是想知道是否有更聪明的方法或现有的Pandas方法可以做到这一点?提前感谢。

更新: 对于我的实际问题,抱歉给出了错误的声明。列 B 包含字符串值。

2个回答

8
你可以使用 cumcount 来计算列 A 中的重复项,然后先按 A(在示例中不必要,在真实数据中可能很重要)排序,然后按 C 排序。最后通过 drop 删除列 C
df['C'] = df.groupby('A')['A'].cumcount()
df.sort_values(by=['C', 'A'], inplace=True)
print (df)
    A  B  C
0  r1  0  0
2  r2  2  0
4  r3  4  0
1  r1  1  1
3  r2  3  1
5  r3  5  1

df.drop('C', axis=1, inplace=True)
print (df)
    A  B
0  r1  0
2  r2  2
4  r3  4
1  r1  1
3  r2  3
5  r3  5

时间:

小 df (len(df)=6)

In [26]: %timeit (jez(df))
1000 loops, best of 3: 2 ms per loop

In [27]: %timeit (boud(df1))
100 loops, best of 3: 2.52 ms per loop

大的数据框(len(df)=6000

In [23]: %timeit (jez(df))
100 loops, best of 3: 3.44 ms per loop

In [28]: %timeit (boud(df1))
100 loops, best of 3: 2.52 ms per loop

计时代码:
df = pd.concat([df]*1000).reset_index(drop=True) 
df1 = df.copy()

def jez(df):
    df['C'] = df.groupby('A')['A'].cumcount()
    df.sort_values(by=['C', 'A'], inplace=True)
    df.drop('C', axis=1, inplace=True)
    return (df)

def boud(df):
    df['C'] = df.groupby('A')['B'].rank()
    df = df.sort_values(['C', 'A'])
    df.drop('C', axis=1, inplace=True)
    return (df)
100 loops, best of 3: 4.29 ms per loop

4
你可以将你的需求表述为,取每个组的第一行,然后是第二行,再然后是第三行等等。这相当于按行对每个组进行分组,然后按'A'进行分组。
你可以使用函数rank,对'A'键中的每个键编号。对每个分组应用此函数即可完成任务:
df['C'] = df.groupby('A')['B'].rank()

df
Out[8]: 
    A  B    C
0  r1  0  1.0
1  r1  1  2.0
2  r2  2  1.0
3  r2  3  2.0
4  r3  4  1.0
5  r3  5  2.0

df.sort_values(['C', 'A'])
Out[9]: 
    A  B    C
0  r1  0  1.0
2  r2  2  1.0
4  r3  4  1.0
1  r1  1  2.0
3  r2  3  2.0
5  r3  5  2.0

如果您不需要它,可以删除'C'


编辑以回复评论

我认为在您的示例中'B'是索引列。如果不是,则需要对索引本身进行处理:

df['C'] = df.reset_index().groupby('A')['index'].rank()

我根据标题中提到的重复问题处理 OP 问题。编辑了帖子,考虑了您宝贵的意见。谢谢。 - Zeugma
太好了,你的第二个解决方案现在运行良好(但不幸的是速度更慢了)。 - jezrael
谢谢,Boud。学到了一个非常有用的方法“rank”。然而,在我的实际问题中,列“B”实际上不是数字,所以我不能在该列上使用“rank”。我应该说明一下,抱歉。 - Xer

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