Si j'ai une simple trame de données:
print(a)
one two three
0 A 1 a
1 A 2 b
2 B 1 c
3 B 2 d
4 C 1 e
5 C 2 f
Je peux facilement créer un multi-index sur les lignes en émettant:
a.set_index(['one', 'two'])
three
one two
A 1 a
2 b
B 1 c
2 d
C 1 e
2 f
Existe-t-il un moyen similaire de créer un multi-index sur les colonnes?
J'aimerais terminer avec:
one A B C
two 1 2 1 2 1 2
0 a b c d e f
Dans ce cas, il serait assez simple de créer le multi-index de ligne, puis de le transposer, mais dans d'autres exemples, je souhaiterai créer un multi-index sur les lignes et les colonnes.
Oui! Cela s'appelle la transposition.
a.set_index(['one', 'two']).T
Empruntons-nous du billet de @ ragesz car ils ont utilisé un bien meilleur exemple pour démontrer avec.
df = pd.DataFrame({'a':['foo_0', 'bar_0', 1, 2, 3], 'b':['foo_0', 'bar_1', 11, 12, 13],
'c':['foo_1', 'bar_0', 21, 22, 23], 'd':['foo_1', 'bar_1', 31, 32, 33]})
df.T.set_index([0, 1]).T
Vous pouvez utiliser pivot_table
suivi d'une série de manipulations sur la trame de données pour obtenir la forme souhaitée:
df_pivot = pd.pivot_table(df, index=['one', 'two'], values='three', aggfunc=np.sum)
def rename_duplicates(old_list): # Replace duplicates in the index with an empty string
seen = {}
for x in old_list:
if x in seen:
seen[x] += 1
yield " "
else:
seen[x] = 0
yield x
col_group = df_pivot.unstack().stack().reset_index(level=-1)
col_group.index = rename_duplicates(col_group.index.tolist())
col_group.index.name = df_pivot.index.names[0]
col_group.T
one A B C
two 1 2 1 2 1 2
0 a b c d e f
Je pense que la réponse courte est NON . Pour avoir des colonnes multi-index, la trame de données doit avoir deux (ou plus) lignes à convertir en en-têtes (comme les colonnes pour les lignes multi-index). Si vous disposez de ce type de trame de données, la création d'un en-tête multi-index n'est pas si difficile. Cela peut être fait dans une très longue ligne de code, et vous pouvez le réutiliser dans n'importe quelle autre trame de données, seuls les numéros de ligne des en-têtes doivent être gardés à l'esprit et changer s'ils diffèrent:
df = pd.DataFrame({'a':['foo_0', 'bar_0', 1, 2, 3], 'b':['foo_0', 'bar_1', 11, 12, 13],
'c':['foo_1', 'bar_0', 21, 22, 23], 'd':['foo_1', 'bar_1', 31, 32, 33]})
La trame de données:
a b c d
0 foo_0 foo_0 foo_1 foo_1
1 bar_0 bar_1 bar_0 bar_1
2 1 11 21 31
3 2 12 22 32
4 3 13 23 33
Création d'un objet multi-index:
arrays = [df.iloc[0].tolist(), df.iloc[1].tolist()]
tuples = list(Zip(*arrays))
index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second'])
df.columns = index
Résultat d'en-tête multi-index:
first foo_0 foo_1
second bar_0 bar_1 bar_0 bar_1
0 foo_0 foo_0 foo_1 foo_1
1 bar_0 bar_1 bar_0 bar_1
2 1 11 21 31
3 2 12 22 32
4 3 13 23 33
Enfin, nous devons supprimer 0-1 lignes, puis réinitialiser l'index de ligne:
df = df.iloc[2:].reset_index(drop=True)
La version "une ligne" (la seule chose que vous devez changer est de spécifier les index d'en-tête et le dataframe lui-même):
idx_first_header = 0
idx_second_header = 1
df.columns = pd.MultiIndex.from_tuples(list(Zip(*[df.iloc[idx_first_header].tolist(),
df.iloc[idx_second_header].tolist()])), names=['first', 'second'])
df = df.drop([idx_first_header, idx_second_header], axis=0).reset_index(drop=True)