【问题标题】:Merge 'left', but override 'right' values where possible合并“左”,但在可能的情况下覆盖“右”值
【发布时间】:2019-09-22 10:14:57
【问题描述】:

目标

我已经查看了pandas documentation on merge,但对在“左”合并中有效地覆盖值有疑问。我可以简单地为一对值执行此操作(如 here 所示),但在尝试执行多对时会变得混乱。

设置

如果我采用以下数据框:

a = pd.DataFrame({
   'id': [0,1,2,3,4,5,6,7,8,9],
    'val': [100,100,100,100,100,100,100,100,100,100]
})

b = pd.DataFrame({
    'id':[0,2,7],
    'val': [500, 500, 500]
})

我可以合并它们:

df = a.merge(b, on=['id'], how='left', suffixes=('','_y'))

得到

   id  val  val_y
0   0  100  500.0
1   1  100    NaN
2   2  100  500.0
3   3  100    NaN
4   4  100    NaN
5   5  100    NaN
6   6  100    NaN
7   7  100  500.0
8   8  100    NaN
9   9  100    NaN

我想在不存在右值的地方保留左值,但在可能的情况下用右值覆盖。

我的想要的结果是:

   id    val
0   0  500.0
1   1  100.0
2   2  500.0
3   3  100.0
4   4  100.0
5   5  100.0
6   6  100.0
7   7  500.0
8   8  100.0
9   9  100.0

我的尝试

我知道我可以用几行代码来完成这个:

df.loc[df.val_y.notnull(), 'val'] = df[df.val_y.notnull()].val_y
df = df.drop(['val_y'], axis = 1)

或者我可以使用logic from this question

但是当我想要应用此逻辑的多个列配对时,这会变得混乱。

例如,使用下面的ab

a = pd.DataFrame({
   'id': [0,1,2,3,4,5,6,7,8,9],
    'val': [100,100,100,100,100,100,100,100,100,100],
    'val_2':[200, 200, 200, 200, 200, 200, 200, 200, 200, 200]
})
b = pd.DataFrame({
    'id':[0,2,7],
    'val': [500, 500, 500],
    'val_2': [500,500,500]
})

有没有更快、更简洁的方法来获得我想要的结果?

【问题讨论】:

标签: python pandas join


【解决方案1】:

我会使用 set_indexupdate 来做到这一点:

u = a.set_index('id')
u.update(b.set_index('id'))  # Update a's values with b's values

u.reset_index()

   id    val
0   0  500.0
1   1  100.0
2   2  500.0
3   3  100.0
4   4  100.0
5   5  100.0
6   6  100.0
7   7  500.0
8   8  100.0
9   9  100.0

更新在索引上对齐。因此,在执行更新步骤之前,我将“id”设置为两个 DataFrame 中的索引。

请注意,“id”列必须是唯一的。


另一种选择是使用concatdrop_duplicates

pd.concat([b, a]).drop_duplicates('id').sort_values('id')

   id  val
0   0  500
1   1  100
1   2  500
3   3  100
4   4  100
5   5  100
6   6  100
2   7  500
8   8  100
9   9  100

由于b 覆盖ab 必须在concat 步骤中排在第一位。

【讨论】:

  • (-; a.assign(val=[dict(b.values).get(i, dict(a.values)[i]) for i in a.id])
【解决方案2】:

numpysearchsorted 并赋值

a.iloc[np.searchsorted(a.id,b.id),1]=b.val.values
a
Out[1382]: 
   id  val
0   0  500
1   1  100
2   2  500
3   3  100
4   4  100
5   5  100
6   6  100
7   7  500
8   8  100
9   9  100

【讨论】:

  • 这是我的大脑想要但没有想到的实现代码(-:
  • @piRSquared 哈哈,我觉得很难找到不一样的东西
  • 同样的想法,但loc...a.loc[a.index[a.id.searchsorted(b.id)], 'val'] = [*b.val]
  • id实际上是多列的组合时怎么办?
【解决方案3】:

dict 偷懒

d = dict(a.values)
d.update(dict(b.values))
pd.DataFrame(dict(zip(a, zip(*d.items()))))

   id  val
0   0  500
1   1  100
2   2  500
3   3  100
4   4  100
5   5  100
6   6  100
7   7  500
8   8  100
9   9  100

【讨论】:

    【解决方案4】:

    另一种选择是按照您已经在执行的操作进行合并,然后将 NaN 值填充到右侧

    df
    
        id  val val_y
    0   0   100 500.0
    1   1   100 NaN
    2   2   100 500.0
    3   3   100 NaN
    4   4   100 NaN
    5   5   100 NaN
    6   6   100 NaN
    7   7   100 500.0
    8   8   100 NaN
    9   9   100 NaN
    
    df.fillna(method='ffill', axis=1)
    
        id  val val_y
    0   0.0 100.0   500.0
    1   1.0 100.0   100.0
    2   2.0 100.0   500.0
    3   3.0 100.0   100.0
    4   4.0 100.0   100.0
    5   5.0 100.0   100.0
    6   6.0 100.0   100.0
    7   7.0 100.0   500.0
    8   8.0 100.0   100.0
    9   9.0 100.0   100.0
    

    然后用iloc[:,-1] 只分割最后一列

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-02-25
      • 1970-01-01
      • 1970-01-01
      • 2011-04-20
      • 2012-12-14
      • 1970-01-01
      • 1970-01-01
      • 2018-05-27
      相关资源
      最近更新 更多