J'ai le DataFrame indexé suivant avec des colonnes et des lignes nommées, des nombres non continus:

          a         b         c         d
2  0.671399  0.101208 -0.181532  0.241273
3  0.446172 -0.243316  0.051767  1.577318
5  0.614758  0.075793 -0.451460 -0.012493

Je voudrais ajouter une nouvelle colonne, 'e', au bloc de données existant et je ne veux rien changer dans le bloc de données (c'est-à-dire que la nouvelle colonne a toujours la même longueur que le DataFrame).

0   -0.335485
1   -1.166658
2   -0.385571
dtype: float64

J'ai essayé différentes versions de join, append, merge, mais je n'ai pas obtenu le résultat que je voulais, seulement des erreurs au maximum. Comment puis-je ajouter la colonne e à l'exemple ci-dessus?

934
tomasz74 23 sept. 2012 à 23:00

22 réponses

Affectation de colonne super simple

Une trame de données pandas est implémentée en tant que dictée ordonnée de colonnes.

Cela signifie que le __getitem__ [] peut non seulement être utilisé pour obtenir une certaine colonne, mais __setitem__ [] = peut être utilisé pour attribuer une nouvelle colonne.

Par exemple, cette trame de données peut avoir une colonne ajoutée en utilisant simplement l'accesseur []

    size      name color
0    big      rose   red
1  small    violet  blue
2  small     tulip   red
3  small  harebell  blue

df['protected'] = ['no', 'no', 'no', 'yes']

    size      name color protected
0    big      rose   red        no
1  small    violet  blue        no
2  small     tulip   red        no
3  small  harebell  blue       yes

Notez que cela fonctionne même si l'index de la trame de données est désactivé.

df.index = [3,2,1,0]
df['protected'] = ['no', 'no', 'no', 'yes']
    size      name color protected
3    big      rose   red        no
2  small    violet  blue        no
1  small     tulip   red        no
0  small  harebell  blue       yes

[] = est le chemin à parcourir, mais attention!

Cependant, si vous avez un pd.Series et essayez de l'affecter à une trame de données où les index sont désactivés, vous rencontrerez des problèmes. Voir l'exemple:

df['protected'] = pd.Series(['no', 'no', 'no', 'yes'])
    size      name color protected
3    big      rose   red       yes
2  small    violet  blue        no
1  small     tulip   red        no
0  small  harebell  blue        no

En effet, un pd.Series a par défaut un index énuméré de 0 à n. Et la méthode pandas [] = essaie d'être "intelligente"

Qu'est-ce qui se passe réellement.

Lorsque vous utilisez la méthode [] =, pandas effectue discrètement une jointure externe ou une fusion externe à l'aide de l'index de la trame de données de gauche et de l'index de la série de droite. df['column'] = series

Note de côté

Cela provoque rapidement une dissonance cognitive, car la méthode []= essaie de faire beaucoup de choses différentes en fonction de l'entrée, et le résultat ne peut être prédit que si vous savez simplement comment les pandas fonctionnent. Je déconseille donc le []= dans les bases de code, mais lors de l'exploration de données dans un ordinateur portable, c'est très bien.

Contourner le problème

Si vous avez un pd.Series et que vous voulez qu'il soit attribué de haut en bas, ou si vous codez du code productif et que vous n'êtes pas sûr de l'ordre des index, cela vaut la peine de le protéger pour ce type de problème.

Vous pouvez abattre le pd.Series en np.ndarray ou list, cela fera l'affaire.

df['protected'] = pd.Series(['no', 'no', 'no', 'yes']).values

Ou

df['protected'] = list(pd.Series(['no', 'no', 'no', 'yes']))

Mais ce n'est pas très explicite.

Un codeur peut venir et dire "Hé, cela semble redondant, je vais juste l'optimiser".

Manière explicite

La définition de l'index de pd.Series comme index de df est explicite.

df['protected'] = pd.Series(['no', 'no', 'no', 'yes'], index=df.index)

Ou plus réaliste, vous avez probablement déjà un pd.Series.

protected_series = pd.Series(['no', 'no', 'no', 'yes'])
protected_series.index = df.index

3     no
2     no
1     no
0    yes

Peut maintenant être attribué

df['protected'] = protected_series

    size      name color protected
3    big      rose   red        no
2  small    violet  blue        no
1  small     tulip   red        no
0  small  harebell  blue       yes

Autre moyen avec df.reset_index()

Étant donné que la dissonance d'index est le problème, si vous pensez que l'index de la trame de données ne doit pas dicter des choses, vous pouvez simplement supprimer l'index, cela devrait être plus rapide, mais ce n'est pas très propre, car votre fonctionner maintenant probablement fait deux choses.

df.reset_index(drop=True)
protected_series.reset_index(drop=True)
df['protected'] = protected_series

    size      name color protected
0    big      rose   red        no
1  small    violet  blue        no
2  small     tulip   red        no
3  small  harebell  blue       yes

Remarque sur df.assign

Alors que df.assign rend plus explicite ce que vous faites, il a en fait tous les mêmes problèmes que les []= ci-dessus

df.assign(protected=pd.Series(['no', 'no', 'no', 'yes']))
    size      name color protected
3    big      rose   red       yes
2  small    violet  blue        no
1  small     tulip   red        no
0  small  harebell  blue        no

Faites attention avec df.assign que votre colonne ne s'appelle pas self. Cela entraînera des erreurs. Cela rend df.assign malodorant , car il existe ce genre d'artefacts dans la fonction.

df.assign(self=pd.Series(['no', 'no', 'no', 'yes'])
TypeError: assign() got multiple values for keyword argument 'self'

Vous pouvez dire: «Eh bien, je n'utiliserai tout simplement pas self alors». Mais qui sait comment cette fonction évoluera à l'avenir pour prendre en charge de nouveaux arguments. Peut-être que le nom de votre colonne sera un argument dans une nouvelle mise à jour de pandas, provoquant des problèmes de mise à niveau.

42
firelynx 3 avril 2017 à 08:59

Façons les plus simples: -

data['new_col'] = list_of_values

data.loc[ : , 'new_col'] = list_of_values
21
AkshayNevrekar 5 déc. 2018 à 09:13

Avant d'attribuer une nouvelle colonne, si vous avez indexé des données, vous devez trier l'index. Au moins dans mon cas, j'ai dû:

data.set_index(['index_column'], inplace=True)
"if index is unsorted, assignment of a new column will fail"        
data.sort_index(inplace = True)
data.loc['index_value1', 'column_y'] = np.random.randn(data.loc['index_value1', 'column_x'].shape[0])
6
Dima Lituiev 16 juin 2015 à 20:27

Il semble que dans les versions récentes de Pandas, la voie à suivre consiste à utiliser df.assign:

df1 = df1.assign(e=np.random.randn(sLength))

Il ne produit pas SettingWithCopyWarning.

50
AkshayNevrekar 3 oct. 2018 à 07:39

Si vous obtenez le SettingWithCopyWarning, une solution simple consiste à copier le DataFrame auquel vous essayez d'ajouter une colonne.

df = df.copy()
df['col_name'] = values
3
Tushar 7 mars 2016 à 04:00

Pour insérer une nouvelle colonne à un emplacement donné (0 <= loc <= quantité de colonnes) dans un bloc de données, utilisez simplement Dataframe.insert:

DataFrame.insert(loc, column, value)

Par conséquent, si vous souhaitez ajouter la colonne e à la fin d'un bloc de données appelé df , vous pouvez utilisation:

e = [-0.335485, -1.166658, -0.385571]    
DataFrame.insert(loc=len(df.columns), column='e', value=e)

valeur peut être une série, un entier (auquel cas toutes les cellules sont remplies avec cette seule valeur) ou une structure de type tableau

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.insert.html

4
Nooyi 7 avril 2019 à 15:18

Je cherchais un moyen général d'ajouter une colonne de numpy.nan à une trame de données sans obtenir le stupide SettingWithCopyWarning.

Parmi les éléments suivants:

  • les réponses ici
  • cette question sur le passage d'une variable comme argument de mot clé
  • cette méthode pour générer un numpy tableau de NaN en ligne

Je suis venu avec ceci:

col = 'column_name'
df = df.assign(**{col:numpy.full(len(df), numpy.nan)})
5
Community 23 mai 2017 à 12:26

Faire cela directement via NumPy sera le plus efficace:

df1['e'] = np.random.randn(sLength)

Notez que ma suggestion originale (très ancienne) était d'utiliser map (qui est beaucoup plus lent):

df1['e'] = df1['a'].map(lambda x: np.random.random())
47
Peter Mortensen 20 oct. 2015 à 13:05

Si la colonne que vous essayez d'ajouter est une variable série, alors juste:

df["new_columns_name"]=series_variable_name #this will do it for you

Cela fonctionne bien même si vous remplacez une colonne existante. Tapez simplement le nouveau_nom_colonnes comme la colonne que vous souhaitez remplacer.

11
halfelf 3 nov. 2017 à 10:44

Si vous souhaitez définir la nouvelle colonne entière sur une valeur de base initiale (par exemple None), vous pouvez le faire: df1['e'] = None

En fait, cela attribuerait le type "objet" à la cellule. Donc, plus tard, vous êtes libre de mettre des types de données complexes, comme la liste, dans des cellules individuelles.

23
digdug 18 déc. 2017 à 20:51

Pour ajouter une nouvelle colonne, «e», au bloc de données existant

 df1.loc[:,'e'] = Series(np.random.randn(sLength))
4
Chirag 4 déc. 2016 à 21:50

Une chose à noter, cependant, est que si vous le faites

df1['e'] = Series(np.random.randn(sLength), index=df1.index)

Ce sera effectivement une gauche jointure sur le df1.index. Donc, si vous souhaitez avoir un effet de jointure externe , ma solution probablement imparfaite consiste à créer un cadre de données avec des valeurs d'index couvrant l'univers de vos données, puis à utiliser le code ci-dessus. Par exemple,

data = pd.DataFrame(index=all_possible_values)
df1['e'] = Series(np.random.randn(sLength), index=df1.index)
6
Peter Mortensen 20 oct. 2015 à 13:05

Voici le moyen simple d'ajouter une nouvelle colonne: df['e'] = e

212
Kathirmani Sukumar 10 déc. 2016 à 06:53
  1. Créez d'abord le list_of_e d'un python qui contient les données pertinentes.
  2. Utilisez ceci: df['e'] = list_of_e
13
AkshayNevrekar 5 déc. 2018 à 09:13

J'ai eu le redouté SettingWithCopyWarning, et il n'a pas été corrigé en utilisant la syntaxe iloc. Mon DataFrame a été créé par read_sql à partir d'une source ODBC. En utilisant une suggestion de lowtech ci-dessus, ce qui suit a fonctionné pour moi:

df.insert(len(df.columns), 'e', pd.Series(np.random.randn(sLength),  index=df.index))

Cela a bien fonctionné pour insérer la colonne à la fin. Je ne sais pas si c'est le plus efficace, mais je n'aime pas les messages d'avertissement. Je pense qu'il y a une meilleure solution, mais je ne la trouve pas, et je pense que cela dépend d'un certain aspect de l'index.
Remarque . Que cela ne fonctionne qu'une seule fois et donnera un message d'erreur si vous essayez d'écraser la colonne existante.
Remarque Comme ci-dessus et à partir de 0.16.0 assign est la meilleure solution. Voir la documentation http: // pandas .pydata.org / pandas-docs / stable / généré / pandas.DataFrame.assign.html # pandas.DataFrame.assign Fonctionne bien pour le type de flux de données où vous n'écrasez pas vos valeurs intermédiaires.

19
hum3 21 oct. 2016 à 11:32

À toute épreuve:

df.loc[:, 'NewCol'] = 'New_Val'

Exemple:

df = pd.DataFrame(data=np.random.randn(20, 4), columns=['A', 'B', 'C', 'D'])

df

           A         B         C         D
0  -0.761269  0.477348  1.170614  0.752714
1   1.217250 -0.930860 -0.769324 -0.408642
2  -0.619679 -1.227659 -0.259135  1.700294
3  -0.147354  0.778707  0.479145  2.284143
4  -0.529529  0.000571  0.913779  1.395894
5   2.592400  0.637253  1.441096 -0.631468
6   0.757178  0.240012 -0.553820  1.177202
7  -0.986128 -1.313843  0.788589 -0.707836
8   0.606985 -2.232903 -1.358107 -2.855494
9  -0.692013  0.671866  1.179466 -1.180351
10 -1.093707 -0.530600  0.182926 -1.296494
11 -0.143273 -0.503199 -1.328728  0.610552
12 -0.923110 -1.365890 -1.366202 -1.185999
13 -2.026832  0.273593 -0.440426 -0.627423
14 -0.054503 -0.788866 -0.228088 -0.404783
15  0.955298 -1.430019  1.434071 -0.088215
16 -0.227946  0.047462  0.373573 -0.111675
17  1.627912  0.043611  1.743403 -0.012714
18  0.693458  0.144327  0.329500 -0.655045
19  0.104425  0.037412  0.450598 -0.923387


df.drop([3, 5, 8, 10, 18], inplace=True)

df

           A         B         C         D
0  -0.761269  0.477348  1.170614  0.752714
1   1.217250 -0.930860 -0.769324 -0.408642
2  -0.619679 -1.227659 -0.259135  1.700294
4  -0.529529  0.000571  0.913779  1.395894
6   0.757178  0.240012 -0.553820  1.177202
7  -0.986128 -1.313843  0.788589 -0.707836
9  -0.692013  0.671866  1.179466 -1.180351
11 -0.143273 -0.503199 -1.328728  0.610552
12 -0.923110 -1.365890 -1.366202 -1.185999
13 -2.026832  0.273593 -0.440426 -0.627423
14 -0.054503 -0.788866 -0.228088 -0.404783
15  0.955298 -1.430019  1.434071 -0.088215
16 -0.227946  0.047462  0.373573 -0.111675
17  1.627912  0.043611  1.743403 -0.012714
19  0.104425  0.037412  0.450598 -0.923387

df.loc[:, 'NewCol'] = 0

df
           A         B         C         D  NewCol
0  -0.761269  0.477348  1.170614  0.752714       0
1   1.217250 -0.930860 -0.769324 -0.408642       0
2  -0.619679 -1.227659 -0.259135  1.700294       0
4  -0.529529  0.000571  0.913779  1.395894       0
6   0.757178  0.240012 -0.553820  1.177202       0
7  -0.986128 -1.313843  0.788589 -0.707836       0
9  -0.692013  0.671866  1.179466 -1.180351       0
11 -0.143273 -0.503199 -1.328728  0.610552       0
12 -0.923110 -1.365890 -1.366202 -1.185999       0
13 -2.026832  0.273593 -0.440426 -0.627423       0
14 -0.054503 -0.788866 -0.228088 -0.404783       0
15  0.955298 -1.430019  1.434071 -0.088215       0
16 -0.227946  0.047462  0.373573 -0.111675       0
17  1.627912  0.043611  1.743403 -0.012714       0
19  0.104425  0.037412  0.450598 -0.923387       0
10
K88 12 avril 2017 à 11:22

Ce qui suit est ce que j'ai fait ... Mais je suis assez nouveau pour les pandas et vraiment Python en général, donc pas de promesses.

df = pd.DataFrame([[1, 2], [3, 4], [5,6]], columns=list('AB'))

newCol = [3,5,7]
newName = 'C'

values = np.insert(df.values,df.shape[1],newCol,axis=1)
header = df.columns.values.tolist()
header.append(newName)

df = pd.DataFrame(values,columns=header)
3
Peter Mortensen 20 oct. 2015 à 13:07

Si le bloc de données et l'objet Series ont le même index , pandas.concat fonctionne également ici:

import pandas as pd
df
#          a            b           c           d
#0  0.671399     0.101208   -0.181532    0.241273
#1  0.446172    -0.243316    0.051767    1.577318
#2  0.614758     0.075793   -0.451460   -0.012493

e = pd.Series([-0.335485, -1.166658, -0.385571])    
e
#0   -0.335485
#1   -1.166658
#2   -0.385571
#dtype: float64

# here we need to give the series object a name which converts to the new  column name 
# in the result
df = pd.concat([df, e.rename("e")], axis=1)
df

#          a            b           c           d           e
#0  0.671399     0.101208   -0.181532    0.241273   -0.335485
#1  0.446172    -0.243316    0.051767    1.577318   -1.166658
#2  0.614758     0.075793   -0.451460   -0.012493   -0.385571

Au cas où ils n'auraient pas le même indice:

e.index = df.index
df = pd.concat([df, e.rename("e")], axis=1)
9
Psidom 7 avril 2017 à 01:46

Permettez-moi d'ajouter que, tout comme pour hum3, .loc n'a pas résolu le SettingWithCopyWarning et j'ai dû recourir à df.insert(). Dans mon cas, un faux positif a été généré par une "fausse" indexation de chaîne dict['a']['e'], où 'e' est la nouvelle colonne et dict['a'] est un DataFrame provenant du dictionnaire.

Notez également que si vous savez ce que vous faites, vous pouvez désactiver l'avertissement à l'aide de pd.options.mode.chained_assignment = None et que d'utiliser l'une des autres solutions données ici.

6
Community 23 mai 2017 à 11:47

Par souci d'exhaustivité - encore une autre solution utilisant DataFrame. Méthode eval ():

Les données:

In [44]: e
Out[44]:
0    1.225506
1   -1.033944
2   -0.498953
3   -0.373332
4    0.615030
5   -0.622436
dtype: float64

In [45]: df1
Out[45]:
          a         b         c         d
0 -0.634222 -0.103264  0.745069  0.801288
4  0.782387 -0.090279  0.757662 -0.602408
5 -0.117456  2.124496  1.057301  0.765466
7  0.767532  0.104304 -0.586850  1.051297
8 -0.103272  0.958334  1.163092  1.182315
9 -0.616254  0.296678 -0.112027  0.679112

Solution:

In [46]: df1.eval("e = @e.values", inplace=True)

In [47]: df1
Out[47]:
          a         b         c         d         e
0 -0.634222 -0.103264  0.745069  0.801288  1.225506
4  0.782387 -0.090279  0.757662 -0.602408 -1.033944
5 -0.117456  2.124496  1.057301  0.765466 -0.498953
7  0.767532  0.104304 -0.586850  1.051297 -0.373332
8 -0.103272  0.958334  1.163092  1.182315  0.615030
9 -0.616254  0.296678 -0.112027  0.679112 -0.622436
4
MaxU 14 mars 2017 à 21:49

Je voudrais ajouter une nouvelle colonne, «e», au bloc de données existant et ne rien changer dans le bloc de données. (La série a toujours la même longueur qu'une trame de données.)

Je suppose que les valeurs d'index de e correspondent à celles de df1.

La façon la plus simple de lancer une nouvelle colonne nommée e et de lui attribuer les valeurs de votre série e:

df['e'] = e.values

attribuer (Pandas 0.16.0+)

Depuis Pandas 0.16.0, vous pouvez également utiliser {{ X0}}, qui affecte de nouvelles colonnes à un DataFrame et renvoie un nouvel objet (une copie) avec toutes les colonnes d'origine en plus des nouvelles.

df1 = df1.assign(e=e.values)

Selon cet exemple (qui inclut également le code source de la fonction assign) , vous pouvez également inclure plusieurs colonnes:

df = pd.DataFrame({'a': [1, 2], 'b': [3, 4]})
>>> df.assign(mean_a=df.a.mean(), mean_b=df.b.mean())
   a  b  mean_a  mean_b
0  1  3     1.5     3.5
1  2  4     1.5     3.5

Dans le contexte de votre exemple:

np.random.seed(0)
df1 = pd.DataFrame(np.random.randn(10, 4), columns=['a', 'b', 'c', 'd'])
mask = df1.applymap(lambda x: x <-0.7)
df1 = df1[-mask.any(axis=1)]
sLength = len(df1['a'])
e = pd.Series(np.random.randn(sLength))

>>> df1
          a         b         c         d
0  1.764052  0.400157  0.978738  2.240893
2 -0.103219  0.410599  0.144044  1.454274
3  0.761038  0.121675  0.443863  0.333674
7  1.532779  1.469359  0.154947  0.378163
9  1.230291  1.202380 -0.387327 -0.302303

>>> e
0   -1.048553
1   -1.420018
2   -1.706270
3    1.950775
4   -0.509652
dtype: float64

df1 = df1.assign(e=e.values)

>>> df1
          a         b         c         d         e
0  1.764052  0.400157  0.978738  2.240893 -1.048553
2 -0.103219  0.410599  0.144044  1.454274 -1.420018
3  0.761038  0.121675  0.443863  0.333674 -1.706270
7  1.532779  1.469359  0.154947  0.378163  1.950775
9  1.230291  1.202380 -0.387327 -0.302303 -0.509652

La description de cette nouvelle fonctionnalité lors de son introduction peut être trouvée ici.

146
fantabolous 29 mai 2019 à 02:20

Pour créer une colonne vide

df['i'] = np.nan
2
JNZ 28 nov. 2019 à 06:12