Панды - сортировка многоуровневых данных по столбцам, но сохранение порядка групп уровней - PullRequest
0 голосов
/ 08 января 2019

У меня есть многоуровневый Dataframe со столбцом под названием name. Я хотел бы сохранить группировку данных на уровне родитель-потомок, но отсортировать каждый уровень A-Z по этому столбцу name.

Другими словами, для каждой строки, если отсортирован родительский элемент, все дочерние элементы на более низком уровне также будут отсортированы в виде блока.

Вот пример моего текущего кадра данных:

df = pd.DataFrame(columns=['level', 'name'],                        
                  data=[['1','AAA'],                                  
                        ['1.1', 'ZZZ'],                               
                        ['1.2', 'XXX'],                               
                        ['1.3', 'YYY'],                               
                        ['1.3.1', 'GGG'],                             
                        ['1.3.1.1', 'XXX'],                           
                        ['1.3.2', 'EEE'],                             
                        ['1.3.3', 'FFF'],                             
                        ['1.3.3.1', 'TTT'],                           
                        ['1.3.3.2', 'SSS'],                           
                        ['2', 'CCC'],                                 
                        ['3', 'BBB'],                                 
                        ['3.1', 'AAA']])                              

И как должен выглядеть отсортированный Dataframe:

sorted_df = pd.DataFrame(columns=['level', 'name'],                 
                  data=[['1','AAA'],           # No Change            
                        ['1.1', 'XXX'],        # Was 1.2              
                        ['1.2', 'YYY'],        # Was 1.3              
                        ['1.2.1', 'EEE'],      # Was 1.3.2            
                        ['1.2.2', 'FFF'],      # Was 1.3.3            
                        ['1.2.3', 'GGG'],      # Was 1.3.1            
                        ['1.2.3.1', 'XXX'],    # Was 1.3.1.1          
                        ['1.2.3.1', 'SSS'],    # Was 1.3.3.2          
                        ['1.2.3.2', 'TTT'],    # was 1.3.3.1          
                        ['1.3', 'ZZZ'],        # Was 1.1              
                        ['2', 'BBB'],          # Was 3                
                        ['2.1', 'AAA'],        # Was 3.1              
                        ['3', 'CCC']])         # Was 2

Разбиваем это на шаги:

  1. Сортировка самого глубокого уровня (т.е. X.X.X.X) по имени. В оригинальном DF выше, 1.3.3.1 (SSS) и 1.3.3.2 (TTT) поменялись местами. 1.3.1.1 (XXX) остается прежним, поскольку в группе 1.3.1.X нет других элементов.

  2. Посмотрите на следующий уровень вверх (т.е. X.X.X - GGG, EEE, FFF). 1.3.1 (GGG) и все, что находится ниже (т. Е. 1.3.1.1), должны двигаться ниже EEE и FFF (и их детей). EEE и FFF (и их дети) уже находятся в правильном положении.

  3. Повторите этот процесс на следующих уровнях, сортируя родителей и всех их детей.

Я попытался разбить Dataframe на несколько индексов, разделив столбец уровня по точкам:

df = pd.concat([df['level'].str.split('.', expand=True), df], axis=1) \
      .set_index([0,1,2,3])

Как только я дошел до этой точки, я как-то застрял. Я пробовал разные вещи (sort_value, sort_index, reset_index и т. Д.), Но не смог разобраться (каламбур). Чтобы еще больше усложнить ситуацию, 'level' может быть произвольной длины с моими реальными данными (например, 1.2.2.1.2.3.1 ...), а столбец имени также является произвольным (см., Как AAA повторяется на другом уровне в примере данные).

Это кажется простым делом, но я потратил пару часов на исследования и стучу головой, пытаясь понять это. Любая помощь будет принята с благодарностью !!

1 Ответ

0 голосов
/ 08 января 2019

Попробуйте использовать fillna:

df.join(df.level.str.split('.', expand=True).fillna(-1))\
  .sort_values([0,1,2,3])[['level','name']]

Выход:

      level name
0         1  AAA
1       1.1  ZZZ
2       1.2  XXX
3       1.3  YYY
4     1.3.1  GGG
5   1.3.1.1  XXX
6     1.3.2  EEE
7     1.3.3  FFF
8   1.3.3.1  TTT
9   1.3.3.2  SSS
10        2  CCC
11        3  BBB
12      3.1  AAA

Неизвестная глубина:

df.join(df.level.str.split('.', expand=True)).fillna(-1)\
  .pipe(lambda x: x.sort_values(x.filter(regex='\d+').columns.tolist()))[['level','name']]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...