在Pythonpandas现有的DataFrame中添加新的列

我有以下索引的DataFrame与命名的列和行不连续的数字:

abcd 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 

我想添加一个新的列'e'到现有的数据框,并不想改变数据框中的任何东西(即,新列的长度始终与DataFrame的长度相同)。

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

我尝试了不同版本的joinappendmerge ,但我没有得到我想要的结果,只有最多的错误。 我如何添加列e到上面的例子?

使用原始的df1索引创build系列:

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


编辑2015年
有人报告用这个代码得到SettingWithCopyWarning
但是,目前的pandas版本0.16.1仍然运行良好。

 >>> sLength = len(df1['a']) >>> df1 abcd 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 abcde 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' 

SettingWithCopyWarning旨在通知Dataframe副本上可能无效的分配。 它不一定说你做错了(它可以触发误报),但从0.13.0它让你知道有更多适合同一目的的方法。 然后,如果您收到警告,请按照其build议: 尝试使用.loc [row_index,col_indexer] = value

 >>> df1.loc[:,'f'] = p.Series(np.random.randn(sLength), index=df1.index) >>> df1 abcdef 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 >>> 

实际上,这是目前pandas文档中描述的更高效的方法



编辑2017年

如注释和@Alexander所示,目前将Series的值添加为DataFrame的新列的最好方法是使用assign

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

这是添加新列的简单方法: df['e'] = e

我想添加一个新的列'e'到现有的数据框,并且不要改变数据框中的任何东西。 (该系列的长度与dataframe相同。)

我假定e中的索引值与df1中的索引值相匹配。

启动一个名为e的新列的最简单的方法,并将它从系列e分配给它:

 df['e'] = e.values 

分配(pandas0.16.0+)

从Pandas 0.16.0开始,您也可以使用assign ,它将新列分配给DataFrame,并返回一个新对象(副本)以及所有原始列。

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

按照这个例子 (其中还包括assign函数的源代码),你也可以包含多个列:

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

在你的例子的上下文中:

 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 abcd 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 abcde 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 

首次介绍这个新function的描述可以在这里find。

直接通过NumPy来做到这一点是最有效率的:

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

注意我的原始(非常古老的)build议是使用map (这是慢得多):

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

似乎在最近的Pandas版本中,要走的路是使用df.assign :

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

它不会产生SettingWithCopyWarning。

我得到了可怕的SettingWithCopyWarning ,并没有通过使用iloc语法来解决。 我的DataFrame是由来自ODBC源的read_sql创build的。 使用上面的低技巧的build议,以下为我工作:

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

这工作得很好,最后插入列。 我不知道这是否是最有效的,但我不喜欢警告消息。 我认为有一个更好的解决办法,但我找不到,我认为这取决于索引的某些方面。
注意 。 这只能工作一次,并会提供一个错误消息,如果试图覆盖和现有的列。
注意如上所述,从0.16.0分配是最好的解决scheme。 请参阅文档http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.assign.html#pandas.DataFrame.assign适用于不覆盖中间值的数据streamtypes。;

超级简单的列分配

pandas数据框是作为列的有序字典实现的。

这意味着__getitem__ []不仅可以用来获取某一列,而且__setitem__ [] =可以用来分配一个新的列。

例如,通过简单地使用[]访问器,该数据框可以添加一个列

  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 

请注意,即使数据框的索引closures,这也是有效的。

 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 

[] =是要走的路,但要小心!

但是,如果您有一个pd.Series并尝试将其分配给索引处于closures状态的数据pd.Series ,则会遇到麻烦。 看例子:

 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 

这是因为pd.Series默认情况下有一个从0到n枚举的索引。 pandas[] =方法试图 成为“聪明”

究竟是怎么回事

当你使用[] =方法时,pandas正在静静地执行一个使用左边数据框索引和右边系列索引的外部联接或外部合并。 df['column'] = series

边注

这很快导致认知失调,因为[]=方法试图根据input做很多不同的事情,除非你知道pandas如何工作的,否则结果是不可预测的。 因此,我会build议在代码库中使用[]= ,但是当在笔记本上浏览数据的时候,就没有问题了。

绕过这个问题

如果你有一个pd.Series并且希望它自上而下分配,或者如果你正在编码有效的代码,并且你不确定索引顺序,那么值得保护这种问题。

你可以将pd.Series到一个np.ndarray或者一个list ,这可以做到这一点。

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

要么

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

但这不是很明确。

一些编码员可能会说:“嘿,这看起来多余,我会优化这个”。

明确的方式

pd.Series的索引设置为df的索引是明确的。

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

或者更现实一些,你可能已经有了一个pd.Series

 protected_series = pd.Series(['no', 'no', 'no', 'yes']) protected_series.index = df.index 3 no 2 no 1 no 0 yes 

现在可以分配

 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()替代方法

既然索引不和谐是问题,如果你觉得数据框的索引不应该规定的东西,你可以简单地删除索引,这应该是更快,但它不是很干净,因为你的function现在可能做两件事情。

 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

虽然df.assign使得它更清楚你正在做什么,但它实际上具有与上面[]=相同的问题

 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 

只要注意与df.assign你的专栏不称为self 。 这将导致错误。 这使得df.assign 臭味 ,因为在这个函数中有这样的工件。

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

你可能会说,“那么,我就不用self ”。 但谁知道这个function将来如何变化,以支持新的论点。 也许你的专栏名称将成为大pandas新的更新的一个参数,导致升级的问题。

让我补充一点,就像对于hum3 , .loc没有解决SettingWithCopyWarning ,我不得不求助于df.insert() 。 在我的案例中,假“正”是由“假”链索引dict['a']['e'] ,其中'e'是新列,而dict['a']是来自字典的DataFrame。

另请注意,如果您知道自己在做什么,则可以使用pd.options.mode.chained_assignment = None切换警告,并使用此处给出的其他解决scheme之一。

在分配新列之前,如果您有索引数据,则需要对索引进行sorting。 至less在我的情况下,我不得不:

 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]) 

有一点需要注意的是,如果你这样做的话

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

这将实际上是df1.index上的连接。 所以如果你想要有一个连接的效果,我可能不完美的解决scheme是创build一个索引值覆盖你的数据的宇宙的数据框,然后使用上面的代码。 例如,

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

以下是我所做的…但是我对pandas很陌生,一般来说真的是Python,所以没有任何承诺。

 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) 

如果你得到了SettingWithCopyWarning ,一个简单的修复就是复制你要添加列的DataFrame。

 df = df.copy() df['col_name'] = values 

万全:

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

例:

 df = pd.DataFrame(data=np.random.randn(20, 4), columns=['A', 'B', 'C', 'D']) df ABCD 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 ABCD 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 ABCD 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”添加到现有数据框中

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

我正在寻找一种将numpy.nan列添加到数据numpy.nan的一般方法,而没有得到愚蠢的SettingWithCopyWarning

从以下方面来看:

  • 这里的答案
  • 这个关于传递variables作为关键字参数的问题
  • 这种方法用于在线生成一个numpy的NaN数组

我想出了这个:

 col = 'column_name' df = df.assign(**{col:numpy.full(len(df), numpy.nan)}) 

为了完整性 – 使用DataFrame.eval()方法的另一个解决scheme:

数据:

 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]: abcd 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 

解:

 In [46]: df1.eval("e = @e.values", inplace=True) In [47]: df1 Out[47]: abcde 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 

如果数据框和Series对象具有相同的索引pandas.concat也可以在这里工作:

 import pandas as pd df # abcd #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 # abcde #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 

如果他们没有相同的索引:

 e.index = df.index df = pd.concat([df, e.rename("e")], axis=1) 
  1. 首先创build一个具有相关数据的Python的list_of_e。
  2. 使用这个:df ['e'] = list_of_e

如果你想把整个新列设置为一个初始基值(例如None ),你可以这样做: df1['e'] = None

如果你试图添加的列是一个系列variables,那么只是:

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

即使您要replace现有的列,也可以正常工作。只需键入与要replace的列相同的new_columns_name,它将只用新的系列数据覆盖现有的列数据。