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

1

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

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

Вот пример моего текущего Dataframe:

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. Сортировать самый глубокий уровень (то есть XXXX) по имени. В оригинальном DF выше, 1.3.3.1 (SSS) и 1.3.3.2 (TTT) поменялись местами. 1.3.1.1 (XXX) остается прежним, поскольку в группе 1.3.1.X нет других элементов.

  2. Посмотрите на следующий уровень вверх (то есть XXX - 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 повторяется на другом уровне в примере данные).

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

Теги:
pandas
dataframe

1 ответ

1

Попробуйте использовать 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']]
  • 0
    @mattlapointe Не уверен, если я что-то пропустил в столбце имен. В вашем вопросе имена изменяют уровни с входного фрейма данных на отсортированный фрейм данных. Я пропустил какую-то логику здесь?
  • 1
    Спасибо Скотту за ответ, но не совсем то, что я хочу сделать. Мне нужно отсортировать по name столбца - я не думаю, что это было совершенно ясно в исходном вопросе. Я добавил некоторые разъяснения выше.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню