Pandas 分层排序

Pie*_*rce 5 python sorting hierarchical pandas

我有一个类别和数量的数据框。可以使用冒号分隔的字符串将类别嵌套到无限级别的子类别中。我希望按降序对它进行排序。但是以分层类型的方式显示。

我需要如何排序

CATEGORY                            AMOUNT
Transport                           5000
Transport : Car                     4900
Transport : Train                   100
Household                           1100
Household : Utilities               600
Household : Utilities : Water       400
Household : Utilities : Electric    200
Household : Cleaning                100
Household : Cleaning : Bathroom     75
Household : Cleaning : Kitchen      25
Household : Rent                    400
Living                              250
Living : Other                      150
Living : Food                       100
Run Code Online (Sandbox Code Playgroud)

编辑: 数据框:

pd.DataFrame({
    "category": ["Transport", "Transport : Car", "Transport : Train", "Household", "Household : Utilities", "Household : Utilities : Water", "Household : Utilities : Electric", "Household : Cleaning", "Household : Cleaning : Bathroom", "Household : Cleaning : Kitchen", "Household : Rent", "Living", "Living : Other", "Living : Food"],
    "amount": [5000, 4900, 100, 1100, 600, 400, 200, 100, 75, 25, 400, 250, 150, 100]
})
Run Code Online (Sandbox Code Playgroud)

注意:这是我想要的顺序。在排序之前,它可以是任意顺序。

EDIT2: 如果有人在寻找类似的解决方案,我在这里发布了一个解决方案:如何按层次类别结构中的值对熊猫中的数据框进行排序

Ben*_*n.T 5

一种方法可能是首先选择str.split类别列。

df_ = df['category'].str.split(' : ', expand=True)
print (df_.head())
           0          1     2
0  Transport       None  None
1  Transport        Car  None
2  Transport      Train  None
3  Household       None  None
4  Household  Utilities  None
Run Code Online (Sandbox Code Playgroud)

然后获取列金额,您想要的是根据以下条件获取每组的最大金额:

  • 仅第一列,
  • 然后是第一列和第二列
  • 然后是第一、二、三列,...

您可以使用 with 来执行此操作groupby.transformmax然后连接创建的每个列。

s = df['amount']
l_cols = list(df_.columns)
dfa = pd.concat([s.groupby([df_[col] for col in range(0, lv+1)]).transform('max')
                  for lv in l_cols], keys=l_cols, axis=1)
print (dfa)
       0       1      2
0   5000     NaN    NaN
1   5000  4900.0    NaN
2   5000   100.0    NaN
3   1100     NaN    NaN
4   1100   600.0    NaN
5   1100   600.0  400.0
6   1100   600.0  200.0
7   1100   100.0    NaN
8   1100   100.0   75.0
9   1100   100.0   25.0
10  1100   400.0    NaN
11   250     NaN    NaN
12   250   150.0    NaN
13   250   100.0    NaN
Run Code Online (Sandbox Code Playgroud)

现在您只需按sort_values正确的顺序对所有列进行排序,先是 0,然后是 1,然后是 2...,获取索引并使用 loc 以预期的方式对 df 进行排序

dfa = dfa.sort_values(l_cols, na_position='first', ascending=False)
dfs = df.loc[dfa.index] #here you can reassign to df directly
print (dfs)
                            category  amount
0                          Transport    5000
1                    Transport : Car    4900
2                  Transport : Train     100
3                          Household    1100
4              Household : Utilities     600
5      Household : Utilities : Water     400
6   Household : Utilities : Electric     200
10                  Household : Rent     400 #here is the one difference with this data
7               Household : Cleaning     100
8    Household : Cleaning : Bathroom      75
9     Household : Cleaning : Kitchen      25
11                            Living     250
12                    Living : Other     150
13                     Living : Food     100
Run Code Online (Sandbox Code Playgroud)

  • 这看起来更平易近人,谢谢!我假设这个列表理解:“[df_[col] for col in range(0, lv+1)]”与“groupby()”接受的特定类型有关?(也很高兴您纠正了 OP 的顺序:)) (2认同)
  • @Noah所以是的,在groupby中,如果它们是您使用它的数据框或系列的一部分,您可以传递列名称或索引级别。但这里的`s`没有这个。因此,您可以做的是使用相同长度的可迭代列表(在本例中,我一次使用 df_ 中的一列)来查看组的位置。对于 lv=0,则相当于 `s.groupby([df_[0]])`;对于 lv=1,则相当于 `s.groupby([df_[0], df_[1]])`如果你没有很多子级别,可以手动完成,但是带有循环“for col ...”的版本更灵活 (2认同)

Pie*_*rce 0

回答我自己的问题:我找到了一种方法。有点啰嗦,但就是这样。

import numpy as np
import pandas as pd


def sort_tree_df(df, tree_column, sort_column):
    sort_key = sort_column + '_abs'
    df[sort_key] = df[sort_column].abs()
    df.index = pd.MultiIndex.from_frame(
        df[tree_column].str.split(":").apply(lambda x: [y.strip() for y in x]).apply(pd.Series))
    sort_columns = [df[tree_column].values, df[sort_key].values] + [
        df.groupby(level=list(range(0, x)))[sort_key].transform('max').values
        for x in range(df.index.nlevels - 1, 0, -1)
    ]
    sort_indexes = np.lexsort(sort_columns)
    df_sorted = df.iloc[sort_indexes[::-1]]
    df_sorted.reset_index(drop=True, inplace=True)
    df_sorted.drop(sort_key, axis=1, inplace=True)
    return df_sorted


sort_tree_df(df, 'category', 'amount')

Run Code Online (Sandbox Code Playgroud)