Comment ajouter plusieurs colonnes à pandas dataframe en une seule affectation?


122

Je suis nouveau sur les pandas et j'essaie de comprendre comment ajouter plusieurs colonnes aux pandas simultanément. Toute aide ici est appréciée. Idéalement, je voudrais faire cela en une seule étape plutôt qu'en plusieurs étapes répétées ...

import pandas as pd

df = {'col_1': [0, 1, 2, 3],
        'col_2': [4, 5, 6, 7]}
df = pd.DataFrame(df)

df[[ 'column_new_1', 'column_new_2','column_new_3']] = [np.nan, 'dogs',3]  #thought this would work here...

Vous devez indiquer quelle erreur vous avez. Quand j'essaye ceci sur pandas 1.0 je reçoisKeyError: "None of [Index(['column_new_1', 'column_new_2', 'column_new_3'], dtype='object')] are in the [columns]"
smci

Réponses:


186

Je me serais attendu à ce que votre syntaxe fonctionne aussi. Le problème se pose car lorsque vous créez de nouvelles colonnes avec la syntaxe de la liste de colonnes ( df[[new1, new2]] = ...), les pandas exigent que le côté droit soit un DataFrame (notez que cela n'a pas vraiment d'importance si les colonnes du DataFrame ont les mêmes noms que les colonnes vous créez).

Votre syntaxe fonctionne bien pour attribuer des valeurs scalaires aux colonnes existantes , et pandas est également heureux d'attribuer des valeurs scalaires à une nouvelle colonne en utilisant la syntaxe à colonne unique ( df[new1] = ...). La solution consiste donc soit à convertir cela en plusieurs affectations à une seule colonne, soit à créer un DataFrame approprié pour le côté droit.

Voici plusieurs approches qui vont travailler:

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'col_1': [0, 1, 2, 3],
    'col_2': [4, 5, 6, 7]
})

Puis l'un des éléments suivants:

1) Trois affectations en une, en utilisant le déballage de liste:

df['column_new_1'], df['column_new_2'], df['column_new_3'] = [np.nan, 'dogs', 3]

2) DataFramedéveloppe commodément une seule ligne pour correspondre à l'index, vous pouvez donc le faire:

df[['column_new_1', 'column_new_2', 'column_new_3']] = pd.DataFrame([[np.nan, 'dogs', 3]], index=df.index)

3) Créez un bloc de données temporaire avec de nouvelles colonnes, puis combinez-le avec le bloc de données d'origine plus tard:

df = pd.concat(
    [
        df,
        pd.DataFrame(
            [[np.nan, 'dogs', 3]], 
            index=df.index, 
            columns=['column_new_1', 'column_new_2', 'column_new_3']
        )
    ], axis=1
)

4) Similaire au précédent, mais en utilisant à la joinplace de concat(peut être moins efficace):

df = df.join(pd.DataFrame(
    [[np.nan, 'dogs', 3]], 
    index=df.index, 
    columns=['column_new_1', 'column_new_2', 'column_new_3']
))

5) Utiliser un dict est une façon plus "naturelle" de créer le nouveau bloc de données que les deux précédents, mais les nouvelles colonnes seront triées par ordre alphabétique (au moins avant Python 3.6 ou 3.7 ):

df = df.join(pd.DataFrame(
    {
        'column_new_1': np.nan,
        'column_new_2': 'dogs',
        'column_new_3': 3
    }, index=df.index
))

6) À utiliser .assign()avec plusieurs arguments de colonne.

J'aime beaucoup cette variante sur la réponse de @ zero, mais comme la précédente, les nouvelles colonnes seront toujours triées par ordre alphabétique, du moins avec les premières versions de Python:

df = df.assign(column_new_1=np.nan, column_new_2='dogs', column_new_3=3)

7) C'est intéressant (basé sur https://stackoverflow.com/a/44951376/3830997 ), mais je ne sais pas quand cela en vaudrait la peine:

new_cols = ['column_new_1', 'column_new_2', 'column_new_3']
new_vals = [np.nan, 'dogs', 3]
df = df.reindex(columns=df.columns.tolist() + new_cols)   # add empty cols
df[new_cols] = new_vals  # multi-column assignment works for existing cols

8) En fin de compte, il est difficile de battre trois missions distinctes:

df['column_new_1'] = np.nan
df['column_new_2'] = 'dogs'
df['column_new_3'] = 3

Remarque: beaucoup de ces options ont déjà été couvertes dans d'autres réponses: ajoutez plusieurs colonnes à DataFrame et définissez-les égales à une colonne existante , est-il possible d'ajouter plusieurs colonnes à la fois à un pandas DataFrame? , Ajoutez plusieurs colonnes vides à pandas DataFrame


L'approche # 7 ( .reindex) ne modifierait-elle pas l'index du dataframe? Pourquoi quelqu'un voudrait-il modifier inutilement l'index lors de l'ajout de colonnes à moins que ce ne soit un objectif explicite ...
Acumenus

1
.reindex()est utilisé avec l' columnsargument, donc il ne change que la colonne "index" (noms). Cela ne modifie pas l'index de ligne.
Matthias Fripp

pour certaines approches, vous pouvez utiliser OrderedDict: par exemple,df.join(pd.DataFrame( OrderedDict([('column_new_2', 'dogs'),('column_new_1', np.nan),('column_new_3', 3)]), index=df.index ))
hashmuke

@hashmuke Cela a du sens pour les premières versions de Python. Cela peut particulièrement plaire aux personnes utilisant des dictionnaires pour plusieurs choses dans Pandas, par exemple, df = pd.DataFrame({'before': [1, 2, 3], 'after': [4, 5, 6]})vsdf = pd.DataFrame(OrderedDict([('before', [1, 2, 3]), ('after', [4, 5, 6])])
Matthias Fripp

2
Si vous utilisez l'option avec join, assurez-vous qu'il n'y a pas de doublons dans votre index (ou utilisez un reset_indexpremier). Cela pourrait vous faire gagner quelques heures de débogage.
Guido le

40

Vous pouvez utiliser assignavec un dict de noms de colonnes et de valeurs.

In [1069]: df.assign(**{'col_new_1': np.nan, 'col2_new_2': 'dogs', 'col3_new_3': 3})
Out[1069]:
   col_1  col_2 col2_new_2  col3_new_3  col_new_1
0      0      4       dogs           3        NaN
1      1      5       dogs           3        NaN
2      2      6       dogs           3        NaN
3      3      7       dogs           3        NaN

Existe-t-il un moyen de faire de même qui maintient un ordre spécifique des colonnes?
user48956

1
Vous pouvez maintenir un ordre spécifique avec les versions antérieures de Python en appelant assign plusieurs fois: df.assign(**{'col_new_1': np.nan}).assign(**{'col2_new_2': 'dogs'}).assign(**{'col3_new_3': 3})
skasch

Si les noms de colonnes ne contiennent que des chaînes qui sont des noms de variables juridiques: df.assign(col_new_1=np.nan, col2_new_2='dogs', col3_new_3=3). Cela maintient l'ordre.
Tobias Bergkvist il y a

9

Avec l'utilisation de concat :

In [128]: df
Out[128]: 
   col_1  col_2
0      0      4
1      1      5
2      2      6
3      3      7

In [129]: pd.concat([df, pd.DataFrame(columns = [ 'column_new_1', 'column_new_2','column_new_3'])])
Out[129]: 
   col_1  col_2 column_new_1 column_new_2 column_new_3
0    0.0    4.0          NaN          NaN          NaN
1    1.0    5.0          NaN          NaN          NaN
2    2.0    6.0          NaN          NaN          NaN
3    3.0    7.0          NaN          NaN          NaN

Pas très sûr de ce que vous vouliez faire [np.nan, 'dogs',3]. Peut-être maintenant les définir comme valeurs par défaut?

In [142]: df1 = pd.concat([df, pd.DataFrame(columns = [ 'column_new_1', 'column_new_2','column_new_3'])])
In [143]: df1[[ 'column_new_1', 'column_new_2','column_new_3']] = [np.nan, 'dogs', 3]

In [144]: df1
Out[144]: 
   col_1  col_2  column_new_1 column_new_2  column_new_3
0    0.0    4.0           NaN         dogs             3
1    1.0    5.0           NaN         dogs             3
2    2.0    6.0           NaN         dogs             3
3    3.0    7.0           NaN         dogs             3

s'il y avait un moyen de faire votre 2ème partie en une seule étape - oui des valeurs constantes dans les colonnes à titre d'exemple.
runningbirds

3

utilisation de la compréhension de liste, pd.DataFrameetpd.concat

pd.concat(
    [
        df,
        pd.DataFrame(
            [[np.nan, 'dogs', 3] for _ in range(df.shape[0])],
            df.index, ['column_new_1', 'column_new_2','column_new_3']
        )
    ], axis=1)

entrez la description de l'image ici


3

si vous ajoutez beaucoup de colonnes manquantes (a, b, c, ....) avec la même valeur, ici 0, j'ai fait ceci:

    new_cols = ["a", "b", "c" ] 
    df[new_cols] = pd.DataFrame([[0] * len(new_cols)], index=df.index)

Il est basé sur la deuxième variante de la réponse acceptée.


0

Je veux juste souligner cette option2 dans la réponse de @Matthias Fripp

(2) Je ne m'attendrais pas nécessairement à ce que DataFrame fonctionne de cette façon, mais il le fait

df [['column_new_1', 'column_new_2', 'column_new_3']] = pd.DataFrame ([[np.nan, 'dogs', 3]], index = df.index)

est déjà documenté dans la propre documentation de pandas http://pandas.pydata.org/pandas-docs/stable/indexing.html#basics

Vous pouvez transmettre une liste de colonnes à [] pour sélectionner les colonnes dans cet ordre. Si une colonne n'est pas contenue dans le DataFrame, une exception sera déclenchée. Plusieurs colonnes peuvent également être définies de cette manière. Vous pouvez trouver cela utile pour appliquer une transformation ( sur place ) à un sous-ensemble de colonnes.


Je pense que c'est assez standard pour l'affectation multi-colonnes. La partie qui m'a surpris est que pd.DataFrame([[np.nan, 'dogs', 3]], index=df.index)réplique la ligne qui lui est donnée pour créer un dataframe entier de la même longueur que l'index.
Matthias Fripp

0

Si vous souhaitez simplement ajouter de nouvelles colonnes vides, la réindexation fera le travail

df
   col_1  col_2
0      0      4
1      1      5
2      2      6
3      3      7

df.reindex(list(df)+['column_new_1', 'column_new_2','column_new_3'], axis=1)
   col_1  col_2  column_new_1  column_new_2  column_new_3
0      0      4           NaN           NaN           NaN
1      1      5           NaN           NaN           NaN
2      2      6           NaN           NaN           NaN
3      3      7           NaN           NaN           NaN

exemple de code complet

import numpy as np
import pandas as pd

df = {'col_1': [0, 1, 2, 3],
        'col_2': [4, 5, 6, 7]}
df = pd.DataFrame(df)
print('df',df, sep='\n')
print()
df=df.reindex(list(df)+['column_new_1', 'column_new_2','column_new_3'], axis=1)
print('''df.reindex(list(df)+['column_new_1', 'column_new_2','column_new_3'], axis=1)''',df, sep='\n')

sinon aller pour des zéros répondent avec assign


0

Je ne suis pas à l'aise avec "Index" et ainsi de suite ... pourrait apparaître comme ci-dessous

df.columns
Index(['A123', 'B123'], dtype='object')

df=pd.concat([df,pd.DataFrame(columns=list('CDE'))])

df.rename(columns={
    'C':'C123',
    'D':'D123',
    'E':'E123'
},inplace=True)


df.columns
Index(['A123', 'B123', 'C123', 'D123', 'E123'], dtype='object')
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.