Pandas: групповая сортировка самого внутреннего столбца на основе другого многоуровневого столбца

#python #python-3.x #pandas #dataframe #sorting

#python #python-3.x #pandas #фрейм данных #сортировка

Вопрос:

Рассмотрим ниже df:

 In [3771]: 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': 5, 'percentage': None}, {'value': '12', 'percentage': None}, {'value': 'N/A', 'percentage': None}, {}, {'value': 19, 'percentage': None}, {'value': 12, 'percentage': None}, {'value': 11, 'percentage': None}, np.nan],
               'F':[{'value': 72, 'percentage': None}, {'value': 72, 'percentage': None}, {'value': 66, 'percentage': None}, {'value': 62, 'percentage': None}, {'value': 66, 'percentage': None}, {'value': 16,      'percentage': None}, {'value': 67, 'percentage': None}, {'value': 67, 'percentage': None}, {'value': 66, 'percentage': None}, {'value': 54, 'percentage': None}, {'value': 78, 'percentage': None}]})

In [3779]: df
Out[3898]: 
    A  B   C   D                                     E                                  F
0   a  b  C1  D1    {'value': '4', 'percentage': None}  {'value': 72, 'percentage': None}
1   a  b  C1  D2      {'value': 5, 'percentage': None}  {'value': 72, 'percentage': None}
2   a  b  C2  D1     {'value': 12, 'percentage': None}  {'value': 66, 'percentage': None}
3   a  b  C1  D3      {'value': 5, 'percentage': None}  {'value': 62, 'percentage': None}
4   a  b  C3  D3   {'value': '12', 'percentage': None}  {'value': 66, 'percentage': None}
5   a  b  C3  D2  {'value': 'N/A', 'percentage': None}  {'value': 16, 'percentage': None}
6   a  b  C2  D4                                    {}  {'value': 67, 'percentage': None}
7   a  b  C3  D4     {'value': 19, 'percentage': None}  {'value': 67, 'percentage': None}
8   a  b  C3  D1     {'value': 12, 'percentage': None}  {'value': 66, 'percentage': None}
9   a  b  C2  D2     {'value': 11, 'percentage': None}  {'value': 54, 'percentage': None}
10  a  b  C2  D3                                   NaN  {'value': 78, 'percentage': None}
 

Я pivot выше df:

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

In [3781]: x
Out[3900]: 
                                            E                                  F
A                                           a                                  a
B C  D                                                                          
b C1 D1    {'value': '4', 'percentage': None}  {'value': 72, 'percentage': None}
     D2      {'value': 5, 'percentage': None}  {'value': 72, 'percentage': None}
     D3      {'value': 5, 'percentage': None}  {'value': 62, 'percentage': None}
  C2 D1     {'value': 12, 'percentage': None}  {'value': 66, 'percentage': None}
     D2     {'value': 11, 'percentage': None}  {'value': 54, 'percentage': None}
     D3                                   NaN  {'value': 78, 'percentage': None}
     D4                                    {}  {'value': 67, 'percentage': None}
  C3 D1     {'value': 12, 'percentage': None}  {'value': 66, 'percentage': None}
     D2  {'value': 'N/A', 'percentage': None}  {'value': 16, 'percentage': None}
     D3   {'value': '12', 'percentage': None}  {'value': 66, 'percentage': None}
     D4     {'value': 19, 'percentage': None}  {'value': 67, 'percentage': None}
 

Я хочу отсортировать самый внутренний столбец, который предназначен D для каждой группы внешних столбцов B и C основан на многоуровневом столбце с индексом (E, a) в порядке убывания на основе value ключа из dict.

Редактировать:

dict Может иметь value ключ со смешанными типами данных. Это может быть int, str, NaN или просто недоступно.

Ожидаемый результат:

                                             E                                  F
A                                           a                                  a
B C  D                                                                          
b C1 D2      {'value': 5, 'percentage': None}  {'value': 72, 'percentage': None}
     D3      {'value': 5, 'percentage': None}  {'value': 62, 'percentage': None}
     D1    {'value': '4', 'percentage': None}  {'value': 72, 'percentage': None}
  C2 D1     {'value': 12, 'percentage': None}  {'value': 66, 'percentage': None}
     D2     {'value': 11, 'percentage': None}  {'value': 54, 'percentage': None}
     D4                                    {}  {'value': 67, 'percentage': None}
     D3                                   NaN  {'value': 78, 'percentage': None}
  C3 D4     {'value': 19, 'percentage': None}  {'value': 67, 'percentage': None}
     D1     {'value': 12, 'percentage': None}  {'value': 66, 'percentage': None}
     D3   {'value': '12', 'percentage': None}  {'value': 66, 'percentage': None}
     D2  {'value': 'N/A', 'percentage': None}  {'value': 16, 'percentage': None}
 


 
 

Ответ №1:

Решение с помощью помощника MultiIndex column , созданного Series.str.get , затем сортировка по DataFrame.sort_values и последнее удаление вспомогательного столбца:

 x[('new', 'a')] = pd.to_numeric(x[('E','a')].str.get('value'), errors='coerce')
lvl = x.index.names[:-1]

order = 'desc'

x = (x.sort_values(lvl   [('new', 'a')],ascending=[True] * len(lvl)   [order == 'asc'])
       .drop(('new', 'a'), axis=1))
 

 print (x)

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

                                         F  
A                                        a  
B C  D                                      
b C1 D2  {'value': 72, 'percentage': None}  
     D3  {'value': 62, 'percentage': None}  
     D1  {'value': 72, 'percentage': None}  
  C2 D1  {'value': 66, 'percentage': None}  
     D2  {'value': 54, 'percentage': None}  
     D3  {'value': 78, 'percentage': None}  
     D4  {'value': 67, 'percentage': None}  
  C3 D4  {'value': 67, 'percentage': None}  
     D1  {'value': 66, 'percentage': None}  
     D3  {'value': 66, 'percentage': None}  
     D2  {'value': 16, 'percentage': None}  
 

Комментарии:

1. знаете ли вы, почему это решение выдает ошибку? x.sort_values(by=('E', 'a'), key=lambda x: x.get('value')) или эквивалент x.sort_values(by=('E', 'a'), key=lambda x: x['value'])

2. @IoaTzimas — Возможно, новая функциональность, поэтому некоторые ошибки.

3. @jezrael Не могли бы вы, пожалуйста, сделать это решение немного общим в том смысле, что мне не нужно жестко кодировать имена столбцов B , C и D ? Мне всегда нужно сортировать самый внутренний столбец, который есть D , а остальные столбцы ( B и C ) могут быть одинаковыми.

4. @jezrael Спасибо за помощь. Есть еще один случай, должен ли я добавить для него образцы данных?

5. @jezrael Это работает очень хорошо. Спасибо за всю помощь.