如何合并Series和DataFrame

131
如果您在寻找关于如何在索引上合并DataFrameSeries的信息,请查看这个答案。原始提问者的初衷是想知道如何将系列元素分配为另一个DataFrame的列。如果您有兴趣了解答案,请查看EdChum的被接受的答案
df = pd.DataFrame({'a':[1, 2], 'b':[3, 4]})  # see EDIT below
s = pd.Series({'s1':5, 's2':6})

for name in s.index:
    df[name] = s[name]

   a  b  s1  s2
0  1  3   5   6
1  2  4   5   6

有人可以建议更好的语法/更快的方法吗?

我的尝试:

df.merge(s)
AttributeError: 'Series' object has no attribute 'columns'

df.join(s)
ValueError: Other Series must have a name

编辑 前两个回答强调了我的问题,因此请使用以下内容构建 df

df = pd.DataFrame({'a':[np.nan, 2, 3], 'b':[4, 5, 6]}, index=[3, 5, 6])

最终结果为

    a  b  s1  s2
3 NaN  4   5   6
5   2  5   5   6
6   3  6   5   6
7个回答

233

更新
从v0.24.0开始,只要Series有名称,您就可以在DataFrame和Series上合并

df.merge(s.rename('new'), left_index=True, right_index=True)
# If series is already named,
# df.merge(s, left_index=True, right_index=True)

现在,您可以使用to_frame()将Series简单地转换为DataFrame。因此(如果在索引上进行连接):

df.merge(s.to_frame(), left_index=True, right_index=True)

7
根据问题中关于dfs的定义,这个答案返回的是一个空数据框,而不是问题所要求的结果。我们不想按索引匹配;我们想要将s值广播到df的所有行。 - CPBL
2
这是解决一个不同的问题:“给定一个DataFrame和Series,如何在索引上合并它们”。 OP的问题是“将Series的每个元素分配为DataFrame中的新列”。 - cs95

31

你可以从Series构建DataFrame,然后与DataFrame合并。 因此,您将数据指定为值,但将其乘以长度,将列设置为索引,并将left_indexright_index参数设置为True:

In [27]:

df.merge(pd.DataFrame(data = [s.values] * len(s), columns = s.index), left_index=True, right_index=True)
Out[27]:
   a  b  s1  s2
0  1  3   5   6
1  2  4   5   6

编辑:如果您想使用df的索引来构建系列并使用其索引,则可以执行以下操作:

df.merge(pd.DataFrame(data = [s.values] * len(df), columns = s.index, index=df.index), left_index=True, right_index=True)

假设索引匹配长度。


10

这是一种方法:

df.join(pd.DataFrame(s).T).fillna(method='ffill')

为了解析这里发生的事情...

pd.DataFrame(s).Ts 创建一个一行的 DataFrame,它看起来像这样:

   s1  s2
0   5   6

接下来,join将新的框架与df进行连接:

   a  b  s1  s2
0  1  3   5   6
1  2  4 NaN NaN

最后,使用 fillna 函数和向前填充 (ffill) 参数来填充索引 1 上的 NaN 值,使用该列中的上一个值进行填充。
   a  b  s1  s2
0  1  3   5   6
1  2  4   5   6

为避免使用fillna,可以使用pd.concat重复由s构建的DataFrame中的行。在这种情况下,通用解决方案如下:
df.join(pd.concat([pd.DataFrame(s).T] * len(df), ignore_index=True))

这里有另一种解决编辑后问题中所提到的索引挑战的方法:

以下是需要翻译的内容:

df.join(pd.DataFrame(s.repeat(len(df)).values.reshape((len(df), -1), order='F'), 
        columns=s.index, 
        index=df.index))

s通过重复值和重新排列(指定“Fortran”顺序)的方式转换为DataFrame,并传递适当的列名称和索引。然后将这个新的DataFrame与df连接。


不错的一行代码,但需要注意的是,数据框中已经存在的任何NaN值也将被填充。 - Nathan Lloyd
@Nonth 谢谢你的建议,非常好。我已经进行了编辑,包括了一种避免填充 NaN 值的替代方法。 - Alex Riley
EdChums原始答案的问题影响了这个修订后的答案。如果我使用index=[3, 5]构建df,则在您的命令之后,新列将包含nan。 - Nathan Lloyd
@Nonth 再次编辑!现在应该符合您的新要求了。 - Alex Riley
你的答案快了20倍,但与1e5行的df相比仍有约100毫秒的差异。我的for循环非常慢。顺便提一下,在你的答案中,2应该改为len(df)以适用于一般情况。 - Nathan Lloyd
显示剩余2条评论

3
现在,更简单、更简洁的解决方案可以实现相同的任务。利用 DataFrame.apply() 的功能将 Series 转换为其所属 DataFrame 的列,我们可以使用以下代码:
df.join(df.apply(lambda x: s, axis=1))

结果:

     a  b  s1  s2
3  NaN  4   5   6
5  2.0  5   5   6
6  3.0  6   5   6

在这里,我们使用了DataFrame.apply()和一个简单的lambda函数作为应用于axis=1的函数。所应用的lambda函数只是简单地返回Series s:
df.apply(lambda x: s, axis=1)

结果:

   s1  s2
3   5   6
5   5   6
6   5   6

结果已经继承了原始数据框 df 的行索引。 因此,我们可以通过 DataFrame.join()df 与这个中间结果简单地连接起来,以获得所需的最终结果(因为它们具有相同的行索引)。 DataFrame.apply() 将Series转换为其所属DataFrame的列的能力在 官方文档 中有详细记录,如下所示:

默认情况下(result_type=None),最终返回类型是从应用函数的返回类型推断出来的。


默认行为(result_type=None)取决于应用函数的返回值:类似列表的结果将作为Series返回。但是,如果应用函数返回Series,则这些结果会扩展为列。官方文档还包括了这种用法的示例:
在函数内部返回一个Series类似于传递result_type='expand'。生成的列名将是Series索引。
df.apply(lambda x: pd.Series([1, 2], index=['foo', 'bar']), axis=1)   

   foo  bar
0    1    2
1    1    2
2    1    2

@jezrael 完全同意apply主要是一个方便的工具,而不是一个性能高效的工具,特别是在axis=1上,尽管在某些用例中有一些合法的apply使用方法。 - SeaBean
1
当然,我不认为apply是坏的,只是要小心使用它 ;) - jezrael

1
你可以轻松地将pandas.DataFrame列设置为常量。这个常量可以是一个整数,就像你的例子一样。如果你指定的列不在df中,那么pandas会创建一个新的列,列名由你指定。因此,在构建数据帧之后(来自你的问题):
df = pd.DataFrame({'a':[np.nan, 2, 3], 'b':[4, 5, 6]}, index=[3, 5, 6])

你只需要运行:

df['s1'], df['s2'] = 5, 6

您可以编写循环或推导式,以使其对列表中的所有元素或字典中的键和值执行此操作,具体取决于您的实际数据存储方式。

1
如果我可以建议您设置数据框(自动索引)的方式如下:
df = pd.DataFrame({'a':[np.nan, 1, 2], 'b':[4, 5, 6]})

然后您可以这样设置s1和s2的值(使用shape()从df返回行数):
s = pd.DataFrame({'s1':[5]*df.shape[0], 's2':[6]*df.shape[0]})

那么你想要的结果很容易:

display (df.merge(s, left_index=True, right_index=True))

或者,只需将新值添加到您的数据框 df 中:

df = pd.DataFrame({'a':[nan, 1, 2], 'b':[4, 5, 6]})
df['s1']=5
df['s2']=6
display(df)

两者均返回:

     a  b  s1  s2
0  NaN  4   5   6
1  1.0  5   5   6
2  2.0  6   5   6

如果您有另一个数据列表(而不仅仅是单个值),并且您知道它与df具有相同的顺序,例如:
s1=['a','b','c']

然后您可以以同样的方式附加它:
df['s1']=s1

返回:
     a  b s1
0  NaN  4  a
1  1.0  5  b
2  2.0  6  c

0
如果df是一个pandas.DataFrame,那么df['new_col']= Series list_object of length len(df)将把Series list_object作为名为'new_col'的列添加到DataFrame中。df['new_col']= scalar(例如您的情况中的5或6)也可以工作,并且等同于df['new_col']= [scalar]*len(df)
因此,两行代码就可以实现目的:
df = pd.DataFrame({'a':[1, 2], 'b':[3, 4]})
s = pd.Series({'s1':5, 's2':6})
for x in s.index:    
    df[x] = s[x]

Output: 
   a  b  s1  s2
0  1  3   5   6
1  2  4   5   6

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接