Я пытаюсь найти простой способ переименовать индекс плоского столбца в иерархический набор столбцов с несколькими индексами. Я сталкивался в одном направлении, но, похоже, это немного глупо, - есть ли лучший способ сделать это в Пандах?
#!/usr/bin/env python
import pandas as pd
import numpy as np
flat_df = pd.DataFrame(np.random.randint(0,100,size=(4, 4)), columns=list('ACBD'))
print flat_df
# A C B D
# 0 27 67 35 36
# 1 80 42 93 20
# 2 64 9 18 83
# 3 85 69 60 84
nested_columns = {'A': ('One', 'a'),
'C': ('One', 'c'),
'B': ('Two', 'b'),
'D': ('Two', 'd'),
}
tuples = sorted(nested_columns.values(), key=lambda x: x[1]) # Sort by second value
nested_df = flat_df.sort_index(axis=1) # Sort dataframe by column name
nested_df.columns = pd.MultiIndex.from_tuples(tuples)
nested_df = nested_df.sort_index(level=0, axis=1) # Sort to group first level
print nested_df
# One Two
# a c b d
# 0 27 67 35 36
# 1 80 42 93 20
# 2 64 9 18 83
# 3 85 69 60 84
Кажется немного хрупким, чтобы сортировать как иерархическую спецификацию столбца, так и блок данных и предполагать, что они выстраиваются в линию. Также сортировка три раза кажется смешной. Альтернативой, которую я бы предпочел, было бы нечто вроде nested_df = flat_df.rename(columns=nested_columns)
, но кажется, что rename
не может перейти от индексации плоских столбцов к столбцам multiindex. Я что-то пропустил?
Изменение: реализовано, что это сломается, если кортежи, отсортированные по второму значению, не будут сортироваться так же, как имена плоских столбцов. Определенно неправильный подход.
Edit2: В ответ на @wen ответ:
nested_df = flat_df.rename(columns=nested_columns)
print nested_df
# (One, a) (One, c) (Two, b) (Two, d)
# 0 18 0 51 48
# 1 69 68 78 24
# 2 2 20 99 46
# 3 1 80 11 11
Edit3:
Основываясь на ответе @ScottBoston, вот работающее решение, в котором учитываются плоские столбцы, не упомянутые во вложенных столбцах:
#!/usr/bin/env python
import pandas as pd
import numpy as np
flat_df = pd.DataFrame(np.random.randint(0,100,size=(4, 5)), columns=list('ACBDE'))
print flat_df
# A C B D E
# 0 27 68 4 98 16
# 1 0 9 9 72 68
# 2 91 17 19 54 99
# 3 14 96 54 79 28
nested_columns = {'A': ('One', 'e'),
'C': ('One', 'h'),
'B': ('Two', 'f'),
'D': ('Two', 'g'),
}
nested_df = flat_df.rename(columns=nested_columns)
nested_df.columns = [c if isinstance(c, tuple) else ('', c) for c in nested_df.columns]
nested_df.columns = pd.MultiIndex.from_tuples(nested_df.columns)
print nested_df
# One Two
# e h f g E
# 0 27 68 4 98 16
# 1 0 9 9 72 68
# 2 91 17 19 54 99
# 3 14 96 54 79 28
Вы можете попробовать:
df.columns = pd.MultiIndex.from_tuples(df.rename(columns = nested_columns).columns)
df
Выход:
One Two
a c b d
0 27 67 35 36
1 80 42 93 20
2 64 9 18 83
3 85 69 60 84
tuple
из df.rename
, но я всегда могу предварительно обработать столбцы после переименования, чтобы убедиться, что они все кортежи.
flat_df = pd.DataFrame(np.random.randint(0,100,size=(4, 5)), columns=list('ACBDE'))
, я использовал flat_df = pd.DataFrame(np.random.randint(0,100,size=(4, 5)), columns=list('ACBDE'))
, это решение ломается.
IIUC, rename
flat_df.rename(columns=nested_columns)
Out[224]:
One Two
a c b d
0 36 19 53 46
1 17 85 63 36
2 40 80 75 86
3 31 83 75 16
обновленный
flat_df.columns.map(nested_columns.get)
Out[15]:
MultiIndex(levels=[['One', 'Two'], ['a', 'b', 'c', 'd']],
labels=[[0, 0, 1, 1], [0, 2, 1, 3]])