使用键参数按多列对Pandas Dataframe 排序

gpnt7bae  于 2022-12-21  发布在  其他
关注(0)|答案(5)|浏览(121)

我有一个 Dataframe Pandas Dataframe 与下列:

df = pd.DataFrame([
    ['A2', 2],
    ['B1', 1],
    ['A1', 2],
    ['A2', 1],
    ['B1', 2],
    ['A1', 1]], 
  columns=['one','two'])

我希望首先按列'two'排序,然后按列'one'排序。对于第二次排序,我希望使用一个自定义排序规则,该规则将按字母字符[A-Z]排序列'one',然后按尾随数字[0-100]排序。因此,排序的结果将是:

one two
 A1   1
 B1   1
 A2   1
 A1   2
 B1   2
 A2   2

在使用如下排序规则之前,我已经对类似于列'one'的字符串列表进行了排序:

def custom_sort(value):
    return (value[0], int(value[1:]))

my_list.sort(key=custom_sort)

如果我尝试通过Pandas排序来应用这个规则,我会遇到很多问题,包括:

  1. panda DataFrame.sort_values()函数接受一个键来排序,就像sort()函数一样,但是这个键函数应该是矢量化的(根据panda文档)。如果我尝试将排序键只应用到列“one”,我会得到错误**“TypeError:无法将序列转换为〈class 'int'〉"**
    1.当使用pandas DataFrame.sort_values()方法时,它会将排序键应用于传入的所有列。这将不起作用,因为我想首先使用本机数字排序按列“two”排序。
    我应该如何按照上面提到的那样对DataFrame进行排序?
oknwwptz

oknwwptz1#

您可以将列one拆分为它的组成部分,将它们作为列添加到 Dataframe 中,然后使用列two对它们进行排序。

>>> (df.assign(lhs=df['one'].str[0], rhs=df['one'].str[1:].astype(int))
       .sort_values(['two', 'rhs', 'lhs'])
       .drop(columns=['lhs', 'rhs']))
  one  two
5  A1    1
1  B1    1
3  A2    1
2  A1    2
4  B1    2
0  A2    2
cpjpxq1n

cpjpxq1n2#

使用str.extract创建一些临时列,这些列基于1)字母表(a-zA-Z]+)和2)数字(\d+),然后删除它们:

df = pd.DataFrame([
    ['A2', 2],
    ['B1', 1],
    ['A1', 2],
    ['A2', 1],
    ['B1', 2],
    ['A1', 1]], 
  columns=['one','two'])

df['one-letter'] = df['one'].str.extract('([a-zA-Z]+)')
df['one-number'] = df['one'].str.extract('(\d+)')
df = df.sort_values(['two', 'one-number', 'one-letter']).drop(['one-letter', 'one-number'], axis=1)
df
Out[38]: 
  one  two
5  A1    1
1  B1    1
3  A2    1
2  A1    2
4  B1    2
iaqfqrcu

iaqfqrcu3#

解决方案之一是将两列都设为pd.categorical,并将期望的顺序作为参数“categories”传递。
但我有一些要求,我不能强制未知\意外的值,不幸的是,这是pd.Categorical正在做的。而且None不支持作为一个类别,并自动强制。
因此,我的解决方案是使用一个键按照自定义排序顺序对多个列进行排序:

import pandas as pd

df = pd.DataFrame([
    [A2, 2],
    [B1, 1],
    [A1, 2],
    [A2, 1],
    [B1, 2],
    [A1, 1]], 
  columns=['one','two'])

def custom_sorting(col: pd.Series) -> pd.Series:
    """Series is input and ordered series is expected as output"""
    to_ret = col
    # apply custom sorting only to column one:
    if col.name == "one":
        custom_dict = {}
        # for example ensure that A2 is first, pass items in sorted order here:
        def custom_sort(value):
            return (value[0], int(value[1:]))

        ordered_items = list(col.unique())
        ordered_items.sort(key=custom_sort)
        # apply custom order first:
        for index, item in enumerate(ordered_items):
            custom_dict[item] = index
        to_ret = col.map(custom_dict)
    # default text sorting is about to be applied
    return to_ret

# pass two columns to be sorted
df.sort_values(
    by=["two", "one"],
    ascending=True,
    inplace=True,
    key=custom_sorting,
)

print(df)

输出:

5  A1    1
3  A2    1
1  B1    1
2  A1    2
0  A2    2
4  B1    2

请注意,此解决方案可能会很慢。

cotxawn7

cotxawn74#

在panda〉= 1.1.0和natsort中,你也可以这样做:

import natsort

sorted_df = df.sort_values(["one", "two"], key=natsort.natsort_keygen())
rqmkfv5c

rqmkfv5c5#

我已经创建了一个函数来解决多列使用key参数的问题,遵循@ Alexandria 的建议。它还处理了在创建时态列时不复制名称的问题。此外,它还可以对整个 Dataframe 进行排序,包括索引(使用index.names)。
它可以改进,但使用复制粘贴应该可以:
https://github.com/DavidDB33/pandas_helpers/blob/main/pandas_helpers/helpers.py

相关问题