翻转已排序数据帧的排序顺序

tar*_*ras 3 python sorting algorithm numpy pandas

我有一堆结构如下的数据框

df = pd.DataFrame(
    [[1, 'A', 10], [2, 'A', 20], [3, 'A', 30], 
     [1, 'B', 20], [2, 'B', 20], [3, 'B', 10],
     [1, 'M', 20], [2, 'M', 30], [3, 'M', 30]], 
    columns=['foo', 'bar', 'buzz']
)
Run Code Online (Sandbox Code Playgroud)

数据框最初按列排序,bar并且foo可以从中获得

df.sort_values(['bar', 'foo'])
Run Code Online (Sandbox Code Playgroud)

我需要dffoo一个bar来排序。显而易见的解决方案是

df.sort_values(['foo', 'bar'])
Run Code Online (Sandbox Code Playgroud)

这给了我

   foo bar  buzz
0    1   A    10
3    1   B    20
6    1   M    20
1    2   A    20
4    2   B    20
7    2   M    30
2    3   A    30
5    3   B    10
8    3   M    30
Run Code Online (Sandbox Code Playgroud)

但现实世界的数据帧包含大约 500,000 行,我有大约 3,000 个单独的数据帧要处理。

我想知道是否有更好、更有效的解决方案来考虑数据框已经预先排序的事实?

use*_*203 5

您可以在这里利用稳定排序,因为bar已经排序,这意味着您只需要重新排序foo

这应该对所有大小的 DataFrame 的运行时产生一致的影响(我看到全面加速大约 2 倍)。

这是一个使用numpy's的示例解决方案argsort,指定一个稳定的排序。

df.iloc[np.argsort(df['foo'], kind="stable")]
Run Code Online (Sandbox Code Playgroud)
   foo bar  buzz
0    1   A    10
3    1   B    20
6    1   M    20
1    2   A    20
4    2   B    20
7    2   M    30
2    3   A    30
5    3   B    10
8    3   M    30
Run Code Online (Sandbox Code Playgroud)

性能和验证

df = pd.DataFrame(
    {
        "foo": np.random.randint(0, 100, 100_000),
        "bar": np.random.choice(list("ABCDEFGHIJKLMNOP"), 100_000),
        "buzz": np.random.randint(0, 100, 100_000),
    }
).sort_values(["bar", "foo"])

In [42]: %timeit df.iloc[np.argsort(df['foo'], kind="stable")]                                          
3.41 ms ± 22.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [43]: %timeit df.sort_values(["foo", "bar"])                                                         
6.95 ms ± 136 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [48]: a = df.iloc[np.argsort(df['foo'], kind="stable")]                                              

In [49]: b = df.sort_values(["foo", "bar"])                                                             

In [50]: np.all(a == b)                                                                                 
Out[50]: True
Run Code Online (Sandbox Code Playgroud)