【问题标题】:Pandas: Sort innermost column group-wise based on other multilevel column excluding one rowPandas:根据除一行之外的其他多级列按组对最内列进行排序
【发布时间】:2021-03-19 20:51:36
【问题描述】:

这是我的previous question 的扩展:

考虑下面df

In [68]: df = pd.DataFrame({'A': ['a'] * 11,
    ...:                'B': ['b'] * 11,
    ...:                'C': ['C1', 'C1', 'C2','C1', 'C3', 'C3', 'C2', 'C3', 'C3', 'C2', 'C2'],
    ...:                'D': ['D1', 'D2', 'D1', 'D3', 'D3', 'D2', 'D4', 'D4', 'D1', 'D2', 'D3'],
    ...:                'E': [{'value': '4', 'percentage': None}, {'value': 5, 'percentage': None}, {'value': 12, 'percentage': None}, {'value': 9, 'percentage': None}, {'value': '12', 'percentage': None}, {'value': 'N/A', 'percentage': None}, {}, {'valu
    ...: e': 24, 'percentage': None}, {'value': 12, 'percentage': None}, {'value': 33, 'percentage': None}, {'value': 11, 'percentage': None}]})
    ...: 

Pivot 以上df:

In [69]: x = df.pivot(['B', 'C', 'D'], 'A', ['E'])

In [70]: x
Out[70]: 
                                            E
A                                           a
B C  D                                       
b C1 D1    {'value': '4', 'percentage': None}
     D2      {'value': 5, 'percentage': None}
     D3      {'value': 9, 'percentage': None}
  C2 D1     {'value': 12, 'percentage': None}
     D2     {'value': 33, 'percentage': None}
     D3     {'value': 11, 'percentage': None}
     D4                                    {}
  C3 D1     {'value': 12, 'percentage': None}
     D2  {'value': 'N/A', 'percentage': None}
     D3   {'value': '12', 'percentage': None}
     D4     {'value': 24, 'percentage': None}

我想根据来自字典的value 键在asc/desc 中的索引为(E, a) 的多级列对每组外列B and C 的最内列D 进行排序。

但是,对于每个组,都会有一行包含所有其他行的 total 值。无论排序顺序如何(asc 或 desc),我总是需要将该行保留在最后。

desc 情况下的预期输出

Out[70]: 
                                            E
A                                           a
B C  D                                       
b C1 D2      {'value': 5, 'percentage': None}
     D1    {'value': '4', 'percentage': None}
     D3      {'value': 9, 'percentage': None}
  C2 D1     {'value': 12, 'percentage': None}
     D3     {'value': 11, 'percentage': None}
     D4                                    {}
     D2     {'value': 33, 'percentage': None}
  C3 D1     {'value': 12, 'percentage': None}
     D3   {'value': '12', 'percentage': None}
     D2  {'value': 'N/A', 'percentage': None}
     D4     {'value': 24, 'percentage': None}

asc 情况下的预期输出

Out[70]: 
                                            E
A                                           a
B C  D                                       
b C1 D1    {'value': '4', 'percentage': None}
     D2      {'value': 5, 'percentage': None}
     D3      {'value': 9, 'percentage': None}
  C2 D3     {'value': 11, 'percentage': None}
     D1     {'value': 12, 'percentage': None}
     D4                                    {}
     D2     {'value': 33, 'percentage': None}
  C3 D1     {'value': 12, 'percentage': None}
     D3   {'value': '12', 'percentage': None}
     D2  {'value': 'N/A', 'percentage': None}
     D4     {'value': 24, 'percentage': None}

【问题讨论】:

  • 由于您的数据结构而具有挑战性。您在数据框中有字典,通常不鼓励这样做。更不用说你想对字典中的值求和,有时是字符串,有时是整数。最好只创建一个for 循环。
  • @QuangHoang 我不想在这里总结任何东西。它只是根据一列对整个df 进行排序。排序逻辑主要在链接的question 中得到解答。请看一看。我只想为每个组保留total 的行。
  • 是的,我看错了,但排序几乎是一回事。
  • 我可以使用@jezrael 对最后一个问题的回答来解决这个问题。但它有点笨拙。所以我一直在寻找更好的方法。
  • @MayankPorwal 字典中的value 键是否可以包含负值?

标签: python python-3.x pandas dataframe sorting


【解决方案1】:

使用辅助列进行排序的解决方案 - 首先将 values 通过 Series.str.getto_numeric 转换为数字列,然后创建另一列比较布尔值的每个组的最大值:

lvls = list(x.index.names[:-1])
print (lvls)
['B', 'C']

x[('tmp', 'tmp')] = pd.to_numeric(x[('E','a')].str.get('value'), errors='coerce')

x[('max','tmp')] = x.groupby(lvls)[[('tmp','tmp')]].transform('max') == x[[('tmp','tmp')]]

升序参数中的所有值都是True,默认值:

x1 = x.sort_values(lvls + [('max','tmp'), ('tmp','tmp')])
print (x1)
                                            E   tmp    max
A                                           a   tmp    tmp
B C  D                                                    
b C1 D1    {'value': '4', 'percentage': None}   4.0  False
     D2      {'value': 5, 'percentage': None}   5.0  False
     D3      {'value': 9, 'percentage': None}   9.0   True
  C2 D3     {'value': 11, 'percentage': None}  11.0  False
     D1     {'value': 12, 'percentage': None}  12.0  False
     D4                                    {}   NaN  False
     D2     {'value': 33, 'percentage': None}  33.0   True
  C3 D1     {'value': 12, 'percentage': None}  12.0  False
     D3   {'value': '12', 'percentage': None}  12.0  False
     D2  {'value': 'N/A', 'percentage': None}   NaN  False
     D4     {'value': 24, 'percentage': None}  24.0   True
     

这里最后把True改成了False

x2 = x.sort_values(lvls + [('max','tmp'), ('tmp','tmp')],
                   ascending=[True] * len(lvls) + [True, False])
print (x2)

                                            E   tmp    max
A                                           a   tmp    tmp
B C  D                                                    
b C1 D2      {'value': 5, 'percentage': None}   5.0  False
     D1    {'value': '4', 'percentage': None}   4.0  False
     D3      {'value': 9, 'percentage': None}   9.0   True
  C2 D1     {'value': 12, 'percentage': None}  12.0  False
     D3     {'value': 11, 'percentage': None}  11.0  False
     D4                                    {}   NaN  False
     D2     {'value': 33, 'percentage': None}  33.0   True
  C3 D1     {'value': 12, 'percentage': None}  12.0  False
     D3   {'value': '12', 'percentage': None}  12.0  False
     D2  {'value': 'N/A', 'percentage': None}   NaN  False
     D4     {'value': 24, 'percentage': None}  24.0   True
     

最后删除辅助列:

x1 = x1.drop([('max','tmp'), ('tmp','tmp')], axis=1)
x2 = x2.drop([('max','tmp'), ('tmp','tmp')], axis=1)

【讨论】:

  • 这太棒了!谢谢。
  • 我这里还有一个用例,我现在可以添加示例数据吗?
  • @MayankPorwal - 真的很复杂,请提出一个新问题。
  • 当然@jezrael。我很快就会发布一个新的。给我 10 分钟。
  • 请查看new question
【解决方案2】:

您可以定义一个函数 groups 多级列 E, a 在级别 B and C 上,并返回将按照规则指定的对数据帧进行排序的索引,其中保留所有其他行的总值的行最后:

def sort_idx(s):
    idx = []
    for k, g in s.groupby(level=[0, 1], sort=False):
        i = g.idxmax()
        idx += [*g.drop(i).sort_values().index , i]
    return idx

s = pd.to_numeric(x[('E', 'a')].str['value'], errors='coerce')
x = x.loc[sort_idx(s)]

结果:

                                            E
A                                           a
B C  D                                       
b C1 D1    {'value': '4', 'percentage': None}
     D2      {'value': 5, 'percentage': None}
     D3      {'value': 9, 'percentage': None}
  C2 D3     {'value': 11, 'percentage': None}
     D1     {'value': 12, 'percentage': None}
     D4                                    {}
     D2     {'value': 33, 'percentage': None}
  C3 D1     {'value': 12, 'percentage': None}
     D3   {'value': '12', 'percentage': None}
     D2  {'value': 'N/A', 'percentage': None}
     D4     {'value': 24, 'percentage': None}

【讨论】:

  • 感谢舒巴姆的回答。但是,如果您在第二组中看到,33 的行应该最后出现,{} 的行应该在它的上方。请检查我对这两种情况的预期输出(asc 和 desc)。
  • @MayankPorwal 啊,好吧!我错过了那部分:)
  • @MayankPorwal 编辑了答案。
猜你喜欢
  • 2021-03-09
  • 1970-01-01
  • 2021-11-25
  • 1970-01-01
  • 1970-01-01
  • 2021-10-09
  • 1970-01-01
  • 2023-03-28
  • 2023-03-08
相关资源
最近更新 更多