我想按多列对pandas框架进行排序。我遇到的问题是,其中一列(第一列(需要自然排序,所以我尝试了以下方法:
sortedFrame = inFrame.sort_values(by=['Col_Arg', 'Col_Step'],
key=lambda x:np.argsort(index_natsorted(inFrame['Col_Arg'])))
但是该代码导致该帧仅被Col_ Arg排序。例如输入帧
Col_Arg | Col_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
"""