Pandas 将数据框与共享列合并,左右填充

Posted

技术标签:

【中文标题】Pandas 将数据框与共享列合并,左右填充【英文标题】:Pandas merge dataframes with shared column, fillna in left with right 【发布时间】:2019-11-12 11:48:42 【问题描述】:

我正在尝试合并两个数据帧并将左侧 df 中的 nan 替换为右侧 df,我可以使用如下三行代码来完成,但我想知道是否有更好/更短的方法?

# Example data (my actual df is ~500k rows x 11 cols)
df1 = pd.DataFrame('a': [1,2,3,4], 'b': [0,1,np.nan, 1], 'e': ['a', 1, 2,'b'])
df2 = pd.DataFrame('a': [1,2,3,4], 'b': [np.nan, 1, 0, 1])

# Merge the dataframes...
df = df1.merge(df2, on='a', how='left')

# Fillna in 'b' column of left df with right df...
df['b'] = df['b_x'].fillna(df['b_y'])

# Drop the columns no longer needed
df = df.drop(['b_x', 'b_y'], axis=1)

【问题讨论】:

我改写了您的问题以说明所需的行为:“用右 df 填充左 df 的 'b' 列”。你真的想跳过左合并,首先给你两个不需要的列'b_x,b_y',然后不得不操纵它们。这就是update() 的用途。 【参考方案1】:

短版

df1.b.fillna(df1.a.map(df2.set_index('a').b),inplace=True)
df1
Out[173]: 
   a    b  e
0  1  0.0  a
1  2  1.0  1
2  3  0.0  2
3  4  1.0  b

既然你提到会有多个列

df = df1.combine_first(df1[['a']].merge(df2, on='a', how='left'))
df
Out[184]: 
   a    b  e
0  1  0.0  a
1  2  1.0  1
2  3  0.0  2
3  4  1.0  b

我们也可以用 df 传递给fillna

df1.fillna(df1[['a']].merge(df2, on='a', how='left'))
Out[185]: 
   a    b  e
0  1  0.0  a
1  2  1.0  1
2  3  0.0  2
3  4  1.0  b

【讨论】:

在数据帧 df1 上操作,没有任何就地突变...如果你想:df1.fillna('b': df1.a.map(df2.set_index('a').b)) 如果你看起来不错,这可以在merge上变体?或者更好combine_first @piRSquared 是的,已经 + 了,先生,这是一个很好的答案:-)【参考方案2】:

只有索引对齐(重要说明),我们才能使用update

df1['b'].update(df2['b'])


   a    b  e
0  1  0.0  a
1  2  1.0  1
2  3  0.0  2
3  4  1.0  b

或者干脆fillna:

df1['b'].fillna(df2['b'], inplace=True)

如果您的索引未对齐,请参阅下面的WenNYoBen's 答案或comment。

【讨论】:

df1.set_index('a',inplace=True); df1.update(df2.set_index('a')); df1.reset_index() 【参考方案3】:

您可以屏蔽数据。

原始数据:

print(df)
   one  two  three
0    1  1.0    1.0
1    2  NaN    2.0
2    3  3.0    NaN

print(df2)
   one  two  three
0    4    4      4
1    4    2      4
2    4    4      3

见下文,蒙版只是根据条件填充。

# mask values where isna()
df1[['two','three']] = df1[['two','three']]\
        .mask(df1[['two','three']].isna(),df2[['two','three']])

输出:

   one  two  three
0    1  1.0    1.0
1    2  2.0    2.0
2    3  3.0    3.0

【讨论】:

注意我的例子有 3 列,所以你必须以某种方式合并 我对此进行了编辑。您可以一次屏蔽多个列。无需合并。仍然要求顺序相同。【参考方案4】:

混淆合并的问题是两个数据框都有一个'b'列,但左右版本在不匹配的地方有NaN。您首先要避免从merge 获得不需要的多个“b”列“b_x”、“b_y”

从 df1 分割非共享列 'a','e' 做merge(df2, 'left'),这将从正确的数据帧中获取“b”(因为它只存在于正确的df中) 最后做df1.update(...),这将用df1['b']更新从df2获取的'b'列中的NaN

解决方案:

df1.update(df1[['a', 'e']].merge(df2, 'left'))

df1

   a    b  e
0  1  0.0  a
1  2  1.0  1
2  3  0.0  2
3  4  1.0  b

注意:因为我使用了merge(..., how='left'),所以我保留了调用数据帧的行顺序。如果我的 df1a 值不正常

   a    b  e
0  1  0.0  a
1  2  1.0  1
2  4  1.0  b
3  3  NaN  2

结果是

df1.update(df1[['a', 'e']].merge(df2, 'left'))

df1

   a    b  e
0  1  0.0  a
1  2  1.0  1
2  4  1.0  b
3  3  0.0  2

正如预期的那样。


进一步...

如果您想在可能涉及更多列时更明确

df1.update(df1.drop('b', 1).merge(df2, 'left', 'a'))

更进一步...

如果你不想update数据框,我们可以使用combine_first

快速

df1.combine_first(df1[['a', 'e']].merge(df2, 'left'))

显式

df1.combine_first(df1.drop('b', 1).merge(df2, 'left', 'a'))

更进一步!...

'left'merge 可以保留顺序,但索引。这是极端保守的方法:

df3 = df1.drop('b', 1).merge(df2, 'left', on='a').set_index(df1.index)
df1.combine_first(df3)

【讨论】:

update 函数对我不起作用;但是combine_first 完全符合我的要求,谢谢 kslookall: update 工作正常,请在干净的 pandas 0.24 会话中重试,并确认它工作正常。 @piRSquared 你真的需要解释一下update 在左合并之前填充了 NA,因此它们不会导致不需要的多个“b”列“b_x”、“b_y”。 这很公平。有机会我会补充说明 我编辑了一些解释。您提供了这么多备选方案,这令人困惑,请编辑简洁的一行说明,说明您何时需要每一个。

以上是关于Pandas 将数据框与共享列合并,左右填充的主要内容,如果未能解决你的问题,请参考以下文章

pandas 将数据框与 NaN(或“未知”)合并以查找缺失值

将具有两个日期列的一个数据框与另一个具有两个日期列的数据框合并

将两个数据框与一些公共列合并,其中公共的组合需要是自定义函数

将数据框与系列合并

合并 Pandas 数据框中的 2 列,用前一个值填充 NaN [重复]

合并和填充 Pandas DataFrame