Ajout d'une nouvelle colonne à la base de données existante dans les pandas de Python
j'ai la base de données indexée suivante 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'
, à la base de données existante et je ne veux rien changer dans la base de données (c.-à-d., la nouvelle colonne a toujours la même longueur que la base de données).
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 eu le résultat voulu, seulement des erreurs au plus. Comment puis-je ajouter la colonne e
à l'exemple ci-dessus?
21 réponses
utiliser les index df1 originaux pour créer la série:
df1['e'] = Series(np.random.randn(sLength), index=df1.index)
modifier 2015
Certains ont rapporté avoir le SettingWithCopyWarning
avec ce code.
Cependant, le code fonctionne toujours parfaitement avec la version actuelle de 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 cession éventuellement invalide sur une copie de la Dataframe. Il ne dit pas nécessairement que vous l'avez mal fait (il peut déclencher des faux positifs) mais de 0.13.0 il vous fait savoir qu'il existe des méthodes plus adéquates pour le même but. Ensuite, si vous obtenez l'avertissement, il suffit de suivre son conseil: essayer d'utiliser .loc[row_index, col_indexer] = valeur à la place de
>>> 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 comme décrit dans pandas docs
Edit 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'une base de données pourrait être en utilisant assign
:
df1 = df1.assign(e=p.Series(np.random.randn(sLength)).values)
c'est la façon simple d'ajouter une nouvelle colonne: df['e'] = e
je voudrais ajouter une nouvelle colonne, "e", à la base de données existante et ne rien changer dans la base de données. (La série a toujours la même longueur qu'une base de données.)
je suppose que les valeurs de l'indice de e
correspondent à celles de df1
.
la façon la plus facile de lancer une nouvelle colonne appelée e
, et lui attribuer les valeurs de votre série e
:
df['e'] = e.values
assign (Pandas 0.16.0+)
à partir de Pandas 0.16.0, vous pouvez également utiliser assign
, qui assigne de nouvelles colonnes à une base de données et renvoie un nouvel objet (une copie) avec toutes les colonnes originales en plus des nouvelles.
df1 = df1.assign(e=e.values)
comme dans cet exemple (qui inclut aussi le code source de la fonction assign
), vous pouvez aussi inclure plus d'une seule colonne:
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 caractéristique au moment de son introduction se trouve ici .
faire ceci directement via NumPy sera le plus efficace:
df1['e'] = np.random.randn(sLength)
Remarque que mon ancien (très ancien) suggestion a été d'utiliser des map
(ce qui est beaucoup plus lent):
df1['e'] = df1['a'].map(lambda x: np.random.random())
Il semble que ces derniers Pandas versions de la façon est d'utiliser df.attribuer :
df1 = df1.assign(e=np.random.randn(sLength))
il ne produit pas SettingWithCopyWarning
.
assignation de colonne Super simple
une base de données pandas est implémentée comme un dict ordonné de colonnes.
cela signifie que le __getitem__
[]
peut non seulement être utilisé pour obtenir une certaine colonne, mais __setitem__
[] =
peut être utilisé pour assigner une nouvelle colonne.
par exemple, cette base de données peut avoir une colonne ajoutée en utilisant simplement le []
accessor
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 du dataframe est éteint.
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 que vous essayez de l'affecter à une base de données où les index sont désactivés, vous allez avoir 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
c'est parce qu'un pd.Series
par défaut a un index énuméré de 0 à N. Et la méthode pandas [] =
tente être "smart"
Ce qui se passe.
lorsque vous utilisez la méthode pandas [] =
effectue tranquillement une jointure externe ou une fusion externe en utilisant l'index du dataframe de la main gauche et l'index de la main droite série. df['column'] = series
note
cela provoque rapidement la dissonance cognitive, depuis la méthode []=
est d'essayer de faire beaucoup de choses différentes selon l'entrée, et le résultat ne peut pas être prédit à moins que vous juste savoir comment fonctionne pandas. Je conseillerais donc de ne pas utiliser le []=
dans les bases de code, mais lors de l'exploration de données dans un carnet, c'est très bien.
contourner le problème
si vous avez un pd.Series
et que vous voulez qu'il soit assigné de haut en bas, ou si vous codez le code productif et vous n'êtes pas sûr de l'ordre d'index, il vaut la peine de sauvegarder pour ce genre de problème.
, Vous pouvez abattu le pd.Series
à 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.
un codeur peut venir et dire"hé, ça a l'air redondant, je vais juste optimiser ça".
voie explicite
définir l'index du pd.Series
comme étant l'index du 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 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
Alternative avec df.reset_index()
puisque la dissonance de l'index est le problème, si vous sentez que l'indice de la dataframe devrait dictent pas les choses, vous pouvez simplement supprimer l'index, cela devrait être plus rapide, mais il n'est pas très propre, depuis votre fonction 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
alors que df.assign
rend 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
faites juste attention avec df.assign
que votre colonne ne s'appelle pas self
. Il va provoquer des erreurs. Cela fait df.assign
odeur , car il ya 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 pas utiliser self
alors". Mais qui sait comment cette fonction change à l'avenir pour soutenir de nouveaux arguments. Peut-être que votre nom de colonne sera un argument dans une nouvelle mise à jour de pandas, à l'origine des problèmes avec la mise à niveau.
si vous voulez définir la nouvelle colonne entière à une valeur de base initiale (par exemple None
), vous pouvez faire ceci: df1['e'] = None
cela assignerait en fait le type" object " à la cellule. Donc plus tard, vous êtes libre de mettre des types de données complexes, comme list, dans des cellules individuelles.
j'ai eu le redoutable 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 fonctionnait très bien pour insérer la colonne à la fin. Je ne sais pas si c'est la 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 aspect de l'index.
Note . Que cela ne fonctionne qu'une fois et donnera un message d'erreur si vous essayez d'écraser et la colonne existante.
Note Comme ci-dessus et 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 n'écrasez pas votre intermédiaire valeur.
infaillible:
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
si la colonne que vous essayez d'ajouter est une variable de 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.il suffit de taper new_columns_name de la même façon que la colonne que vous voulez remplacer.Il va juste écraser les données de colonne existantes avec les nouvelles données de série.
permettez-moi juste 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, false positive a été généré par "faux" Chaîne d'indexation dict['a']['e']
, où 'e'
est la nouvelle colonne, et dict['a']
est une base de données provenant du dictionnaire.
notez aussi que si vous savez ce que vous faites, vous pouvez changer l'avertissement en utilisant
pd.options.mode.chained_assignment = None
et que d'utiliser l'un de l'autre les solutions données ici.
si le cadre de données et L'objet de série ont le même index , pandas.concat
travaille aussi 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
dans le cas où ils n'ont pas le même index:
e.index = df.index
df = pd.concat([df, e.rename("e")], axis=1)
- créez D'abord la liste_of_e d'un python qui contient des données pertinentes.
- utilisez ceci: DF['e'] = list_of_f
avant d'attribuer une nouvelle colonne, si vous avez des données indexé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])
une chose à noter, cependant ,est que si vous 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 externe join effect, ma solution probablement imparfaite est de créer une base de données avec des valeurs d'index couvrant l'univers de vos données, et 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)
pour ajouter une nouvelle colonne, "e", à la base de données existante
df1.loc[:,'e'] = Series(np.random.randn(sLength))
je cherchais un moyen général d'ajouter une colonne de numpy.nan
s à une base de données sans obtenir le muet SettingWithCopyWarning
.
de ce qui suit:
- les réponses ici
- cette question sur la transmission d'une variable comme argument mot-clé
- cette méthode pour la génération d'un
numpy
tableau de NaNs-ligne
j'ai trouvé ceci:
col = 'column_name'
df = df.assign(**{col:numpy.full(len(df), numpy.nan)})
pour des raisons d'exhaustivité - encore une autre solution utilisant DataFrame.eval() la méthode:
Data:
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
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)
Si vous obtenez le SettingWithCopyWarning
, une solution facile est de copier le DataFrame vous essayez d'ajouter une colonne.
df = df.copy()
df['col_name'] = values
les voies les plus faciles: -
data ['new_col'] = list_of_values
"151900920 des données".loc [:, 'new_col'] = list_of_values