J'ai le DataFrame indexé suivant avec des colonnes nommées et des lignes non continues:
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
J'aimerais ajouter une nouvelle colonne, 'e'
, au cadre de données existant et je ne souhaite rien modifier dans le cadre 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, mais des erreurs tout au plus. Comment puis-je ajouter la colonne e
à l'exemple ci-dessus?
Utilisez les index df1 d'origine pour créer la série:
df1['e'] = pd.Series(np.random.randn(sLength), index=df1.index)
Éditer 2015
Certains ont rapporté avoir obtenu la SettingWithCopyWarning
avec ce code.
Cependant, le code fonctionne toujours parfaitement avec la version actuelle de pandas version 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'] = pd.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'
La SettingWithCopyWarning
a pour but d'informer d'une assignation éventuellement non valide sur une copie du Dataframe. Cela ne signifie pas nécessairement que vous vous êtes trompé (cela peut déclencher des faux positifs), mais à partir de 0.13.0, cela vous indique qu'il existe des méthodes plus adéquates pour le même objectif. Ensuite, si vous recevez cet avertissement, suivez simplement ses conseils: Essayez d’utiliser .loc [index_ligne, index_col] = valeur
>>> df1.loc[:,'f'] = pd.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 en tant que décrite dans pandas docs
Éditer 2017
Comme indiqué dans les commentaires et par @Alexander, la meilleure méthode pour ajouter les valeurs d'une série en tant que nouvelle colonne d'un DataFrame pourrait être d'utiliser assign
:
df1 = df1.assign(e=pd.Series(np.random.randn(sLength)).values)
C'est le moyen simple d'ajouter une nouvelle colonne: df['e'] = e
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 image de données.)
Je suppose que les valeurs d'index dans e
correspondent à celles de df1
.
La méthode la plus simple pour créer une nouvelle colonne nommée e
et lui attribuer les valeurs de votre série e
:
df['e'] = e.values
assigner (Pandas 0.16.0 +)
À partir de Pandas 0.16.0, vous pouvez également utiliser assign
, qui attribue de nouvelles colonnes à un DataFrame et renvoie un nouvel objet (une copie) avec toutes les colonnes originales, en plus de les 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 a été trouvée ici .
Faire cela directement via NumPy sera le plus efficace:
df1['e'] = np.random.randn(sLength)
Notez ma suggestion (très ancienne) originale d'utiliser map
(qui est beaucoup plus lent):
df1['e'] = df1['a'].map(lambda x: np.random.random())
Il semble que dans les récentes versions de Pandas, la solution consiste à utiliser df.assign :
df1 = df1.assign(e=np.random.randn(sLength))
Il ne produit pas SettingWithCopyWarning
.
Un pandas dataframe est implémenté comme un dict ordonné 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, une colonne peut être ajoutée à cette image de données simplement à l'aide de 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
Cependant, si vous avez un pd.Series
et essayez de l'assigner à une trame de données où les index sont désactivés, vous rencontrerez des problèmes. Voir 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
par défaut a un index énuméré de 0 à n. Et la pandas [] =
méthode tente d'être "intelligente"
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 la série de droite. df['column'] = series
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 pas être prédit à moins que vous ne sachiez que comment pandas fonctionne. Je déconseillerais donc le []=
dans les bases de code, mais lors de l'exploration de données dans un cahier, tout va bien.
Si vous avez un pd.Series
et que vous voulez l’affecter de haut en bas, ou si vous codez du code productif et que vous n'êtes pas sûr de l’ordre d’index, il vaut la peine de le protéger pour ce type de problème.
Vous pouvez downcast le pd.Series
en un np.ndarray
ou un 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.
Certains codeurs peuvent venir et dire "Hé, ça a l'air redondant, je vais juste l'optimiser".
Définir l'index du pd.Series
pour qu'il soit l'index du df
est explicite.
df['protected'] = pd.Series(['no', 'no', 'no', 'yes'], index=df.index)
De manière plus réaliste, vous avez probablement déjà un pd.Series
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 assigné
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
df.reset_index()
Puisque la dissonance de l’index est le problème, si vous estimez que l’index de la structure de données ne devrait pas dicter , vous pouvez simplement supprimer l’index. plus rapide, mais ce n’est pas très propre, puisque votre fonction effectue maintenant probablement 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
df.assign
Bien que df.assign
rende plus explicite ce que vous faites, il présente en fait tous les mêmes problèmes que ceux décrits 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 juste attention avec df.assign
que votre colonne ne s'appelle pas self
. Cela causera des erreurs. Cela rend 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 ne vais tout simplement pas utiliser 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, ce qui posera des problèmes de mise à niveau.
Si vous souhaitez définir la nouvelle colonne entière sur une valeur de base initiale (par exemple, None
), procédez comme suit: df1['e'] = None
En réalité, cela assignerait le type "objet" à la cellule. Vous êtes donc libre de placer ultérieurement des types de données complexes, tels que list, dans des cellules individuelles.
Moyens les plus simples: -
data['new_col'] = list_of_values
data.loc[ : , 'new_col'] = list_of_values
J'ai eu le redouté SettingWithCopyWarning
, et cela 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, les éléments suivants ont 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 cela dépend d'un aspect 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, attribuer 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 ne le faites pas. t écrasez vos valeurs intermédiaires.
list_of_e
de python contenant des données pertinentes.df['e'] = list_of_e
Si la colonne que vous essayez d'ajouter est une variable de série, alors simplement:
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 new_columns_name identique à la colonne que vous souhaitez remplacer. Il remplacera simplement les données de colonne existantes par les nouvelles données de série.
À 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
e = [ -0.335485, -1.166658, -0.385571]
moyen simple et facile
df['e'] = e
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 index:
e.index = df.index
df = pd.concat([df, e.rename("e")], axis=1)
Permettez-moi simplement d’ajouter que, tout comme pour hum , .loc
n’a pas résolu le problème SettingWithCopyWarning
et j’ai dû recourir à df.insert()
. Dans mon cas, les faux positifs ont été générés par la chaîne "fictive" d'indexation 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 utiliser l'une des solutions présentées ici.
Avant d’affecter une nouvelle colonne, si vous avez des données indexées, vous devez trier l’index. Au moins dans mon cas je devais:
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])
Une chose à noter, cependant, est que si vous le faites
df1['e'] = Series(np.random.randn(sLength), index=df1.index)
ce sera effectivement un à gauche rejoindre sur le df1.index. Donc, si vous voulez avoir un effet de jointure externe, ma solution probablement imparfaite est de créer un cadre de données avec des valeurs d'index couvrant l'univers de vos données, puis d'utiliser le code ci-dessus. Par exemple,
data = pd.DataFrame(index=all_possible_values)
df1['e'] = Series(np.random.randn(sLength), index=df1.index)
Je cherchais un moyen général d’ajouter une colonne de numpy.nan
s à un cadre de données sans obtenir le dumb SettingWithCopyWarning
.
De ce qui suit:
numpy
de NaN en ligneJe suis venu avec ceci:
col = 'column_name'
df = df.assign(**{col:numpy.full(len(df), numpy.nan)})
Par souci d’exhaustivité - encore une autre solution utilisant la méthode DataFrame.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
Pour ajouter une nouvelle colonne, 'e', au bloc de données existant
df1.loc[:,'e'] = Series(np.random.randn(sLength))
Ce qui suit est ce que j’ai fait ... Mais je suis assez nouveau pour pandas et vraiment Python en général, donc aucune promesse.
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)
pour insérer une nouvelle colonne à un emplacement donné (0 <= loc <= nombre de colonnes) dans un cadre 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 utiliser:
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 semblable à un tableau
https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.insert.html
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