Convertir la ligne en en-tête de colonne pour Pandas DataFrame,

111

Les données avec lesquelles je dois travailler sont un peu désordonnées. Il a des noms d'en-tête à l'intérieur de ses données. Comment puis-je choisir une ligne dans un dataframe pandas existant et en faire (la renommer en) un en-tête de colonne?

Je veux faire quelque chose comme:

header = df[df['old_header_name1'] == 'new_header_name1']

df.columns = header
EK
la source

Réponses:

196
In [21]: df = pd.DataFrame([(1,2,3), ('foo','bar','baz'), (4,5,6)])

In [22]: df
Out[22]: 
     0    1    2
0    1    2    3
1  foo  bar  baz
2    4    5    6

Définissez les étiquettes de colonne pour qu'elles correspondent aux valeurs de la deuxième ligne (emplacement d'index 1):

In [23]: df.columns = df.iloc[1]

Si l'index a des libellés uniques, vous pouvez supprimer la deuxième ligne en utilisant:

In [24]: df.drop(df.index[1])
Out[24]: 
1 foo bar baz
0   1   2   3
2   4   5   6

Si l'index n'est pas unique, vous pouvez utiliser:

In [133]: df.iloc[pd.RangeIndex(len(df)).drop(1)]
Out[133]: 
1 foo bar baz
0   1   2   3
2   4   5   6

L'utilisation df.drop(df.index[1])supprime toutes les lignes avec le même libellé que la deuxième ligne. Parce que les index non uniques peuvent conduire à des pierres d'achoppement (ou des bogues potentiels) comme celui-ci, il est souvent préférable de veiller à ce que l'index soit unique (même si Pandas n'en a pas besoin).

unutbu
la source
Merci beaucoup pour votre réponse rapide! Comment puis-je choisir une ligne par valeur au lieu de l'emplacement de l'index pour en faire un en-tête? Donc, pour votre exemple, quelque chose comme .. df.columns = df [df [0] == 'foo']
EK
Le problème avec cela est qu'il pourrait y avoir plus d'une ligne qui a la valeur "foo". Une façon de contourner ce problème est de choisir explicitement la première telle ligne: df.columns = df.iloc[np.where(df[0] == 'foo')[0][0]].
unutbu
Ah je vois pourquoi tu as fait ça. Pour mon cas, je sais qu'il n'y a qu'une seule ligne qui a la valeur "foo". Alors ça va. Je viens de faire de cette façon, je suppose que c'est le même que celui que vous m'avez donné ci-dessus. idx_loc = df [df [0] == 'toto']. index.tolist () [0] df.columns = df.iloc [idx_loc]
EK
63

Cela fonctionne (pandas v'0.19.2 '):

df.rename(columns=df.iloc[0])
Zachary Wilson
la source
22
Vous pouvez supprimer la ligne "en-tête" en ajoutant.drop(df.index[0])
ostrokach
J'aime mieux cela que la réponse acceptée. J'adore les solutions courtes en ligne.
Javier
13

Il serait plus facile de recréer la trame de données. Cela interpréterait également les types de colonnes à partir de zéro.

headers = df.iloc[0]
new_df  = pd.DataFrame(df.values[1:], columns=headers)
shahar_m
la source
4

Vous pouvez spécifier l'index de ligne dans les constructeurs read_csv ou read_html via le headerparamètre qui représente Row number(s) to use as the column names, and the start of the data. Cela a l'avantage de supprimer automatiquement toutes les lignes précédentes qui sont supposées être indésirables.

import pandas as pd
from io import StringIO

In[1]
    csv = '''junk1, junk2, junk3, junk4, junk5
    junk1, junk2, junk3, junk4, junk5
    pears, apples, lemons, plums, other
    40, 50, 61, 72, 85
    '''

    df = pd.read_csv(StringIO(csv), header=2)
    print(df)

Out[1]
       pears   apples   lemons   plums   other
    0     40       50       61      72      85
ccpizza
la source