对Pandas框架进行排序,使用是通过多个列标签进行自然排序



我想按多列对pandas框架进行排序。我遇到的问题是,其中一列(第一列(需要自然排序,所以我尝试了以下方法:

sortedFrame = inFrame.sort_values(by=['Col_Arg', 'Col_Step'],
key=lambda x:np.argsort(index_natsorted(inFrame['Col_Arg'])))

但是该代码导致该帧仅被Col_ Arg排序。例如输入帧

Col_ArgCol_Step
1第一个20
2秒10
1第一个10

因此,代码的问题在于,当您在pandas.DataFrame.sort_values中使用多个列以及key参数时,panda会按照您在参数by=["col1", "col2"]中定义的顺序获取每个序列,并调用该函数,将列值按调用sort_values之前的出现顺序传递给它

例如,让我们定义一个简单的函数,它只打印它接收的参数,并将其用作key参数:

import pandas as pd
import numpy as np
from natsort import index_natsorted, natsort_keygen, natsorted

# == Sample DataFrame ============================
df = pd.DataFrame(
{
'Col_Arg': ['First', 'Second', 'First', 'Third', 'Fourth', 'Tenth'],
'Col_Step': [20, 10, 10, 30, 20, 5]
}
)

# == Simple Key Function ============================
def print_values(x):
print(x)
return x

df.sort_values(
by=["Col_Arg", "Col_Step"],
key=lambda x: print_values(x)
)
# Returns:
"""
0     1 First
1    2 Second
2     1 First
3     3 Third
4    4 Fourth
5    10 Tenth
Name: Col_Arg, dtype: object
0    20
1    10
2    10
3    30
4    20
5     5
Name: Col_Step, dtype: int64
"""

因此,基本上pandas.DataFrame.sort_values将每一列作为一个序列传递给您的函数,它希望您的函数进行一些转换,使列";可排序";。以下是Panda文档中的参数描述:

key:callable,可选

描述:

在排序之前对值应用key函数。这类似于内置sorted()函数中的键参数,但显著的区别是该键函数应该向量化。它应该期望一个系列,并返回一个与输入形状相同的系列它将由独立地应用于中的每一列

换句话说,如果要在同一个pandas.DataFrame.sort_values操作中对两列进行排序,则需要传入一个函数,该函数能够将'Col_Arg'转换为数字形式,同时返回未修改的'Col_Step'。此外,通过在key=lambda x:np.argsort(index_natsorted(inFrame['Col_Arg']))中使用inFrame而不是传递x,键函数将根据inFrame索引按调用sort_values函数之前存在的顺序对值进行排序。这里有一个例子:


df.sort_values(
by=["Col_Arg", "Col_Step"],
key=lambda x: print_values(np.argsort(index_natsorted(df["Col_Step"])))
)
# Prints:
"""
[3 1 2 5 4 0]
[3 1 2 5 4 0]
"""

因此,第一次调用key函数时,它使用[3 1 2 5 4 0]对数据帧索引进行排序,然后它应用与以前相同的顺序,但现在所有索引都已经被移动,因此它最终破坏了排序操作。

快速修复

如前所述,key函数按照排序操作之前的存在顺序获取每个列值。因此,我们需要创建一个将'Col_Arg'值转换为数字的函数,而不是尝试在键函数内部进行排序。有一个名为数字解析器的软件包可以为您做到这一点。要安装它,请运行以下代码:

pip install number-parser

然后,您可以创建一个函数在key中使用,如下所示:


import numpy as np
import pandas as pd
from number_parser import parse_ordinal

def custom_sort(col: pd.Series) -> pd.Series:
if col.name == "Col_Arg":
return col.apply(parse_ordinal)
return col
df.sort_values(
by=["Col_Arg", "Col_Step"],
key=custom_sort
)
# Returns:
"""
Col_Arg  Col_Step
2   First        10
0   First        20
1  Second        10
3   Third        30
4  Fourth        20
5   Tenth         5
"""

解决方案2:另一种选择是这样做:

import pandas as pd
import numpy as np
from natsort import index_natsorted, natsort_keygen, natsorted

df.sort_values(
by="Col_Arg", key=lambda col: np.argsort(index_natsorted(col))
).groupby("Col_Arg", as_index=False).apply(
lambda grp: grp.sort_values("Col_Step")
).reset_index(
drop=True
)
# Returns:
"""
Col_Arg  Col_Step
0   First        10
1   First        20
2  Fourth        20
3  Second        10
4   Tenth         5
5   Third        30
"""

最新更新