Pandas 由另一个 df 的 "join" fillna?

Pandas fillna by "join" of another df?

我有 2 个 DF,一个有 ID,一个有条目。基本上:

ID    Val
1     111
2     222
3     333

还有一个缺少一些 Val:

ID    Val   Other
1     111   123
1     NaN   3
1     111   5
2     222   3553
2     NaN   58
2     222   321
3     NaN   456

我想做的是用第一个 df 中的 Val 值填充第二个 df 中 Val 中的缺失值。所以结果应该是:

ID    Val   Other
1     111   123
1     111   3
1     111   5
2     222   3553
2     222   58
2     222   321
3     NaN   456

我怎样才能做到这一点?我见过一个类似的用例,但来自同一个 df。当我尝试这个时,我得到一个错误 bc。我的 df 的尺寸当然不匹配。

所以问题是,如何通过“连接”填充我的 NaN 值?

假设您的数据帧是 df1 和 df2:

df2.groupby('ID').apply(lambda s: s.fillna(df1.set_index('ID')['Val'][s.name]))

输出:

     Val  Other
0  111.0    123
1  111.0      3
2  111.0      5
3  222.0   3553
4  222.0     58
5  222.0    321

解决方案一:

可以合并使用 np.where:

vals = df2.merge(df1, on=['ID'], how='left', suffixes=['_',''])['Val']
df2['Val'] = np.where(df2['Val'].isna(), vals, df2['Val'])

速度:每个循环 1.87 毫秒 ± 29.4 微秒(7 次运行的平均值 ± 标准偏差,每次 100 次循环)


方案二:

创建映射字典并将值映射到 ID。

dct = df1.set_index('ID').to_dict()['Val']
df2.loc[df2['Val'].isna(), 'Val'] = df2['ID'].map(dct)

速度:每个循环 1.94 毫秒 ± 66.2 微秒(7 次运行的平均值 ± 标准偏差,每次 100 次循环)



示例数据:

import pandas as pd
import numpy as np
df1 = pd.DataFrame({'ID': {0: 1, 1: 2}, 'Val': {0: 111, 1: 222}})
df2 = pd.DataFrame({'ID': {0: 1, 1: 1, 2: 1, 3: 2, 4: 2, 5: 2}, 'Val': {0: 111.0, 1: np.nan, 2: 111.0, 3: 222.0, 4: np.nan, 5: 222.0}, 'Other': {0: 123, 1: 3, 2: 5, 3: 3553, 4: 58, 5: 321}})

输出:

   ID    Val  Other
0   1  111.0    123
1   1  111.0      3
2   1  111.0      5
3   2  222.0   3553
4   2  222.0     58
5   2  222.0    321

您可以 merge 第一个 df 到第二个,然后使用 combine_first 填充 NaNs:

df2 = df2.merge(df1.rename({'Val': 'TrueVal'}, axis=1), how='left')
df2['Val'] = df2['Val'].combine_first(df2['TrueVal'])
df2.drop('TrueVal', inplace=True)

测试数据:

import numpy as np
import pandas as pd

    a = pd.DataFrame({"ID": {0: 1, 1: 2}, "Val": {0: 111, 1: 222}})
    b = pd.DataFrame(
        {
            "ID": {0: 1, 1: 1, 2: 1, 3: 2, 4: 2, 5: 2},
            "Val": {0: 111.0, 1: np.NaN, 2: 111.0, 3: 222.0, 4: np.NaN, 5: 222.0},
            "Other": {0: 123, 1: 3, 2: 5, 3: 3553, 4: 58, 5: 321},
        }
    )

那我就用merge:

b.merge(a, on="ID")

输出:

   ID  Val_x  Other  Val_y
0   1  111.0    123    111
1   1    NaN      3    111
2   1  111.0      5    111
3   2  222.0   3553    222
4   2    NaN     58    222
5   2  222.0    321    222

使用以下方法清理数据框:

c = b.merge(a, on="ID").drop("Val_x", axis=1).rename(columns={"Val_y": "Val"})