У меня есть многоуровневый 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
Разбивая это на шаги:
Сортировать самый глубокий уровень (то есть XXXX) по имени. В оригинальном DF выше, 1.3.3.1 (SSS) и 1.3.3.2 (TTT) поменялись местами. 1.3.1.1 (XXX) остается прежним, поскольку в группе 1.3.1.X нет других элементов.
Посмотрите на следующий уровень вверх (то есть XXX - GGG, EEE, FFF). 1.3.1 (GGG) и все, что находится ниже (т.е. 1.3.1.1), должны быть ниже EEE и FFF (и их детей). EEE и FFF (и их дети) уже находятся в правильном положении.
Повторите этот процесс на следующих уровнях, сортируя родителей и всех их детей.
Я попытался разбить 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 повторяется на другом уровне в примере данные).
Это кажется простым делом, но я потратил пару часов на исследование и бью себя по голове, пытаясь понять это. Любая помощь будет принята с благодарностью!
Попробуйте использовать 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']]
name
столбца - я не думаю, что это было совершенно ясно в исходном вопросе. Я добавил некоторые разъяснения выше.