Question Ajout d'une nouvelle colonne au DataFrame existant dans les pandas Python


J'ai le DataFrame indexé suivant avec des colonnes nommées et des lignes non-nombres 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', à la trame de données existante et ne veulent rien changer dans la trame 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 une colonne e à l'exemple ci-dessus?


568
2017-09-23 19:00


origine


Réponses:


Utilisez les index df1 d'origine pour créer la série:

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


Modifier 2015
Certains ont rapporté obtenir le SettingWithCopyWarning avec ce code.
Cependant, le code fonctionne toujours parfaitement avec la version actuelle des pandas 0.16.1.

>>> sLength = len(df1['a'])
>>> df1
          a         b         c         d
6 -0.269221 -0.026476  0.997517  1.294385
8  0.917438  0.847941  0.034235 -0.448948

>>> df1['e'] = p.Series(np.random.randn(sLength), index=df1.index)
>>> df1
          a         b         c         d         e
6 -0.269221 -0.026476  0.997517  1.294385  1.757167
8  0.917438  0.847941  0.034235 -0.448948  2.228131

>>> p.version.short_version
'0.16.1'

le SettingWithCopyWarning vise à informer d'une affectation éventuellement invalide sur une copie de la base de données. Cela ne veut pas nécessairement dire que vous l'avez fait mal (cela peut déclencher des faux positifs), mais à partir de 0.13.0, vous savez qu'il existe des méthodes plus adéquates dans le même but. Ensuite, si vous obtenez l'avertissement, suivez simplement son conseil: Essayez d'utiliser .loc [row_index, col_indexer] = valeur à la place

>>> df1.loc[:,'f'] = p.Series(np.random.randn(sLength), index=df1.index)
>>> df1
          a         b         c         d         e         f
6 -0.269221 -0.026476  0.997517  1.294385  1.757167 -0.050927
8  0.917438  0.847941  0.034235 -0.448948  2.228131  0.006109
>>> 

En fait, c'est actuellement la méthode la plus efficace décrit dans les docs pandas



Modifier 2017

Comme indiqué dans les commentaires et par @Alexander, actuellement la meilleure méthode pour ajouter les valeurs d'une série comme une nouvelle colonne d'un DataFrame pourrait utiliser assign:

df1 = df1.assign(e=p.Series(np.random.randn(sLength)).values)

647
2017-09-23 19:24



C'est la manière simple d'ajouter une nouvelle colonne: df['e'] = e


142
2017-12-12 16:04



Je voudrais ajouter une nouvelle colonne, 'e', ​​à la trame de données existante et ne change rien dans la trame de données. (La série a toujours la même longueur qu'une base de données.)

Je suppose que les valeurs d'index dans e correspondre à ceux de df1.

Le moyen le plus simple d'initier une nouvelle colonne nommée eet attribuez-lui les valeurs de votre série e:

df['e'] = e.values

affecter (Pandas 0.16.0+)

A partir de Pandas 0.16.0, vous pouvez également utiliser assign, qui assigne 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 comprend également le code source du assign fonction), 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.


88
2018-02-14 00:49



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 (ce qui est beaucoup plus lent):

df1['e'] = df1['a'].map(lambda x: np.random.random())

33
2017-09-23 19:22



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

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

Il ne produit pas SettingWithCopyWarning.


27
2017-07-21 17:35



J'ai eu la redoutée SettingWithCopyWarninget il n'a pas été résolu 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 existe une meilleure solution, mais je ne la trouve pas, et je pense que cela dépend de certains aspects de l'indice.
Remarque. Cela ne fonctionne qu'une fois et donnera un message d'erreur si vous essayez d'écraser une 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/generated/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.


14
2018-06-11 09:45



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__  [] ne peut pas seulement être utilisé pour obtenir une certaine colonne, mais __setitem__  [] = peut être utilisé pour affecter une nouvelle colonne.

Par exemple, cette base de données peut avoir une colonne ajoutée en utilisant simplement le [] 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 base 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'assigner à une base 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

C'est parce qu'un pd.Series Par défaut a un index énuméré de 0 à n. Et les pandas [] = méthode essais  être intelligent"

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

Lorsque vous utilisez le [] = méthode pandas exécute tranquillement une jointure externe ou une fusion externe en utilisant l'index de la trame de données de gauche et l'index de la série de droite. df['column'] = series

Note latérale

Cela provoque rapidement une dissonance cognitive, puisque []= méthode tente de faire beaucoup de choses différentes en fonction de l'entrée, et le résultat ne peut être prédit à moins que vous il suffit de savoir comment les pandas fonctionnent. Je conseillerais donc de ne pas []= dans les bases de code, mais lors de l'exploration des données dans un cahier, c'est bien.

Faire le tour du problème

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

Vous pourriez baisser le pd.Series à un np.ndarray ou un list, Ça 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.

Certains codeurs peuvent venir et dire "Hey, ça a l'air redondant, je vais juste optimiser ça".

Manière explicite

Définir l'index de la pd.Series être l'indice de la df est explicite.

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

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

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

3     no
2     no
1     no
0    yes

Peut maintenant être affecté

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

Moyen alternatif avec df.reset_index()

Puisque la dissonance d'index est le problème, si vous sentez que l'index du dataframe devrait pas dicter les choses, vous pouvez simplement laisser tomber l'index, cela devrait être plus rapide, mais ce n'est pas très propre, puisque votre fonction 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

Note sur df.assign

Tandis que df.assign rendre plus explicite ce que vous faites, il a en fait tous les mêmes problèmes que le 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

Il suffit de regarder avec df.assign que votre colonne n'est pas appelée self. Cela provoquera des erreurs. Cela fait df.assign  malodorant, car il y a 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 pas self alors ".Mais qui sait comment cette fonction changera à l'avenir pour supporter de nouveaux arguments.Peut-être que votre nom de colonne sera un argument dans une nouvelle mise à jour de pandas, causant des problèmes avec la mise à niveau.


14
2018-04-03 08:59