web-dev-qa-db-fra.com

Pandas définition multi-index sur les lignes, puis transposition en colonnes

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.

13
sheridp

Oui! Cela s'appelle la transposition.

a.set_index(['one', 'two']).T

enter image description here


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

enter image description here

5
piRSquared

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
4
Nickil Maveli

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)
1
ragesz