【问题标题】:Pandas merge dataframes with shared column, fillna in left with rightPandas 将数据框与共享列合并,左右填充
【发布时间】: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() 的用途。

标签: python pandas dataframe merge


【解决方案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”。
  • 这很公平。有机会我会补充说明
  • 我编辑了一些解释。您提供了这么多备选方案,这令人困惑,请编辑简洁的一行说明,说明您何时需要每一个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-09
  • 2021-11-14
  • 2023-03-13
  • 1970-01-01
  • 1970-01-01
  • 2018-07-10
  • 2021-12-14
相关资源
最近更新 更多