Pandas按值排序,然后按索引排序

17

我有以下数据集:

import numpy as np
from pandas import DataFrame
import numpy.random as random

random.seed(12)

df = DataFrame (
    {
        "fac1" : ["a","a","a","a","b","b","b","b"] ,
        "val" : random.choice(np.arange(0,20), 8, replace=False)
    }
)
df2 = df.set_index(["fac1"])
df2

enter image description here

我希望的是在每个 fac1 组内按 val 进行排序,得到如下结果:

enter image description here

我已经仔细查阅了文档,但没有找到一个简单明了的方法。我能做到的最好办法是以下这种hack:

df3 = df2.reset_index()
df4 = df3.sort_values(["fac1","val"],ascending=[True,True],axis=0)
df5 = df4.set_index(["fac1"])
df5
# Produces the picture above

我意识到上述内容可以从多个inplace选项中受益,只是为了让中间产品清晰明了而这样做。
我找到了这篇SO帖子,它使用分组和排序函数。然而,从那篇帖子改编的以下代码产生了错误的结果。
df2.groupby("fac1",axis=1).apply(lambda x : x.sort_values("val"))

由于空间限制,输出已删除。

还有其他方法可以解决这个问题吗?

更新:解决方案

被接受的解决方案是:

df2.sort_values(by='val').sort_index(kind='mergesort')

排序算法必须为mergesort,并且必须显式指定,因为它不是默认值。正如sort_index文档中指出的那样,“mergesort是唯一的稳定算法。”如果您不为kind指定mergesort,则以下是另一个无法正确排序的示例数据集:

random.seed(12)

len = 32 

df = DataFrame (
    {
        "fac1" : ["a" for i in range(int(len/2))] + ["b" for i in range(int(len/2))] ,
        "val" : random.choice(np.arange(0,100), len, replace=False)
    }
)
df2 = df.set_index(["fac1"])
df2.sort_values(by='val').sort_index()

(由于空间原因,我省略了所有输出)


我一直在试图缩小故障发生的点,它与 len 有关 - 对于代码中的其他所有内容,提议的解决方案适用于 len <= 16,但对于更大的值则失败。 - Silenced Temporarily
1个回答

20

编辑:我查阅了文档,sort_index 的默认排序算法是快速排序。这不是一个“稳定”的算法,因为它不能保留“已排序输出中相等元素的输入顺序”(来自维基百科)。然而,sort_index 提供了选择“归并排序”的选项,这是一种稳定的排序算法。所以我的原始答案是错误的。

df2.sort_values(by='val').sort_index()

“worked”只是偶然发生的结果。这段代码应该每次都能正常运行,因为它使用了一种稳定的排序算法:

df2.sort_values(by='val').sort_index(kind = 'mergesort')

当你写最后的编辑时,我刚好遇到了同样的问题。是的,那就是答案。话虽如此,我有点惊讶于kind的默认值不是mergesort。嗯,我猜可以从两个方面来论证。总之,我认为这解决了问题。 - sparc_spread
我也很惊讶,但我认为在最坏的情况下可能需要更长时间。 - Sam
同意 - 根据文档,这一切都归结于底层的numpy ndarray实现。该库真正为速度而建;同时,在pandas层上,我的用例是常见的(我是pandas的新手,但已经从事数据科学/统计工作多年,主要使用SAS,在那里这很容易做到)。我认为通过添加一个布尔型的stable参数来改进API会更好,而不是选择算法 - 这样人们更容易注意到它。无论如何,现在我们知道了!再次感谢(并享受赏金 :-))。 - sparc_spread
3
如果你和我一样需要先按列排序,然后再按索引排序,你应该改变排序的顺序!例如:.sort_index().sort_values('A', kind='mergesort') - lucidyan

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