如何按字符串索引的自定义顺序对 pandas 数据帧进行排序

How to sort a pandas dataframe by a custom order on a string index

提问人:littleworth 提问时间:4/25/2018 最后编辑:Peter Mortensenlittleworth 更新时间:9/16/2023 访问量:43283

问:

我有以下数据帧:

import pandas as pd

df = pd.DataFrame({'id': [2967, 5335, 13950, 6141, 6169],
                   'Player': ['Cedric Hunter', 'Maurice Baker',
                              'Ratko Varda', 'Ryan Bowen', 'Adrian Caldwell'],
                   'Year': [1991, 2004, 2001, 2009, 1997],
                   'Age': [27, 25, 22, 34, 31],
                   'Tm': ['CHH', 'VAN', 'TOT', 'OKC', 'DAL'],
                   'G': [6, 7, 60, 52, 81]})


df.set_index('Player', inplace=True)

它显示:

Out[128]:

                 Age   G   Tm  Year     id
Player
Cedric Hunter     27   6  CHH  1991   2967
Maurice Baker     25   7  VAN  2004   5335
Ratko Varda       22  60  TOT  2001  13950
Ryan Bowen        34  52  OKC  2009   6141
Adrian Caldwell   31  81  DAL  1997   6169

如何使用任意顺序按索引(“玩家”)排序?例如,如下图所示。

reorderlist = ['Maurice Baker',
               'Adrian Caldwell',
               'Ratko Varda',
               'Ryan Bowen',
               'Cedric Hunter']
python pandas 排序 索引 分类数据

评论

0赞 Tenfrow 4/25/2018
所以你希望它像它在列表中排序一样有序?reorderlist
0赞 smci 4/25/2018
pandas 实现这一点的“正确”方法是允许 Categoricals 作为索引,就像 R 一样;目前 pandas 没有,它给出错误。

答:

4赞 smci 4/25/2018 #1

若要获取字符串列表的自定义排序顺序,请将其声明为分类,并在排序中手动指定该顺序:

player_order = pd.Categorical([ 'Maurice Baker', 'Adrian Caldwell','Ratko Varda' ,'Ryan Bowen' ,'Cedric Hunter'],
              ordered=True)

这是因为 pandas 还不允许 Categoricals 作为索引:df.set_index(keys=player_order, inplace=True) TypeError: unhashable type: 'Categorical'

因此,您需要使用df.sort_index(level=player_order)

评论

0赞 jean-loup 12/27/2018
请给出一个解决方案,而不是“......”点
1赞 smci 12/29/2018
@jean-loup:我已经在这里给出了解决方案,但要 200% 清楚:df.sort_index(level=player_order)
57赞 BENY 4/25/2018 #2

reindex

df.reindex(reorderlist)
Out[89]: 
                 Age   G   Tm  Year     id
Player                                    
Maurice Baker     25   7  VAN  2004   5335
Adrian Caldwell   31  81  DAL  1997   6169
Ratko Varda       22  60  TOT  2001  13950
Ryan Bowen        34  52  OKC  2009   6141
Cedric Hunter     27   6  CHH  1991   2967

更新信息,您有多个同名玩家

out = df.iloc[pd.Categorical(df.index,reorderlist).argsort()]

评论

1赞 tiru 9/24/2019
您好,我已经尝试过这个玩家正在作为记录器列表获得,但所有其他值都属于 NAN。我想要与上面完全相同的值
0赞 Diego F Medina 2/19/2022
当有同名的玩家时,这不起作用。
0赞 BENY 2/19/2022
@DiegoFMedina检查更新
11赞 totalhack 9/15/2020 #3

从 Pandas 1.1 开始,DataFrame.sort_values 有一个参数,它接受一个可调用对象来控制排序。因此,您可以使用如下方法:key

def sorter(column):
    reorder = [
        "Maurice Baker",
        "Adrian Caldwell",
        "Ratko Varda",
        "Ryan Bowen",
        "Cedric Hunter",
    ]
    # This also works:
    # mapper = {name: order for order, name in enumerate(reorder)}
    # return column.map(mapper)
    cat = pd.Categorical(column, categories=reorder, ordered=True)
    return pd.Series(cat)

df_sorted = df.sort_values(by="Player", key=sorter)

使用和我在评论中提出的替代方案之间可能存在一些实际差异。例如,请参阅这些注意事项。为了完整起见,我展示了两者。我也没有测试过这与当前公认的使用 .当您也参与其中时,最好的方法可能会有所不同。pd.Categoricalcolumn.mapdf.reindexMultiIndex

评论

5赞 madprogramer 7/13/2021
不是所有的英雄都穿斗篷!显然,有些人戴着围巾!
0赞 T. Hall 4/3/2022 #4

在不包含空白行的情况下按任意顺序排序,我发现在测试 BENYS 答案时有效。它根据需要进行排序,忽略缺少的键,例如 ,但有助于不包含没有数据的键的空行。df.filterdf.reindex

df.filter(reorderlist, axis=0)

                    id  Year  Age   Tm   G
Player                                    
Maurice Baker     5335  2004   25  VAN   7
Adrian Caldwell   6169  1997   31  DAL  81
Ratko Varda      13950  2001   22  TOT  60
Ryan Bowen        6141  2009   34  OKC  52
Cedric Hunter     2967  1991   27  CHH   6

#Extra keys dont add empty rows, missing keys ignored
reorderlist.append('LeBron James')
reorderlist.remove('Adrian Caldwell')
df.filter(reorderlist, axis=0)

                  id  Year  Age   Tm   G
Player                                  
Maurice Baker   5335  2004   25  VAN   7
Ratko Varda    13950  2001   22  TOT  60
Ryan Bowen      6141  2009   34  OKC  52
Cedric Hunter   2967  1991   27  CHH   6
1赞 PTQuoc 5/27/2022 #5

如果有多个列需要排序,根据我的经验,我习惯于将值转换为 .然后使用:mapstringnumbersort_values

# Step 1/3: create dictionary to convert any string to number
convert_dict = {'Maurice Baker':1,
                'Adrian Caldwell':2,
                'Ratko Varda':3} # You can start filling till the end

# Step 2/3: Create column `new` that mapping from `Player`:
df['new'] = df['Player'].map(convert_dict)

# Step 3/3: sort
df.sort_values(by=['new'], ignore_index=True, inplace=True)
df.drop(columns=['new'], inplace=True)
0赞 Keyub W 11/30/2023 #6

我发现的最简单的方法是将列表传递给 ,但如果索引不唯一,这将不起作用。.loc

df = df.loc[reorderlist, :]