为DataFrame按条件赋值:无精确匹配时取更大u_g对应的target值
解决方案:DF2匹配DF1的精确+近似匹配需求
首先咱们先把示例数据构建出来,方便测试验证:
import pandas as pd # 构建DF1 df1 = pd.DataFrame({ 'spec': ['G1', 'G2', 'WG2', 'WG2', 'WG2', 'WG3', 'WG3', 'SWG3'], 'u_g': [4.8, 2.1, 1.4, 1.2, 1.0, 0.8, 0.7, 0.7], 'target': [0.88, 0.76, 0.71, 0.68, 0.52, 0.65, 0.53, 0.31] }) # 构建DF2 df2 = pd.DataFrame({ 'id': range(1, 12), 'type': ['WG2', 'WG2', 'WG2', 'G1', 'G1', 'G2', 'SWG3', 'WG3', 'WG3', 'WG2', pd.NA], 'u_g_1': [1.4, 1.4, 1.0, 4.8, 4.9, 2.1, 0.7, 0.8, 0.7, 1.1, 0] })
方法一:逐行处理(适合小数据集)
这种方法逻辑直观,容易理解,适合数据量不大的场景:
步骤1:先完成精确匹配
用你之前尝试的merge方法,先拿到所有精确匹配的target,同时保留DF2的所有行:
# 精确匹配,得到初始target列并命名区分 df2_modified = df2.merge( df1, left_on=['type', 'u_g_1'], right_on=['spec', 'u_g'], how='left' ).rename(columns={'target': 'target_exact'})
步骤2:预处理DF1分组排序
把DF1按spec分组,每组按u_g升序排序,这样后续找大于u_g_1的最小值会更高效:
df1_grouped = df1.groupby('spec').apply( lambda x: x.sort_values('u_g').reset_index(drop=True) ).reset_index(drop=True)
步骤3:编写函数处理非精确匹配行
定义一个函数,给每个没有精确匹配的行找到符合要求的target:
def find_closest_target(row): # type为NaN直接返回空值 if pd.isna(row['type']): return pd.NA # 获取当前type对应的分组数据 group = df1_grouped[df1_grouped['spec'] == row['type']] if group.empty: return pd.NA # 筛选出u_g大于当前u_g_1的条目 candidates = group[group['u_g'] > row['u_g_1']] if candidates.empty: return pd.NA # 没有符合条件的,返回空 # 取u_g最小的那个对应的target return candidates.loc[candidates['u_g'].idxmin(), 'target']
步骤4:合并最终结果
把精确匹配的target和补全的target合并,得到最终的结果列:
df2_modified['target'] = df2_modified.apply( lambda row: row['target_exact'] if not pd.isna(row['target_exact']) else find_closest_target(row), axis=1 ) # 保留需要的列 df2_modified = df2_modified[['id', 'type', 'u_g_1', 'target']]
运行后你会得到符合要求的df2_modified:
id type u_g_1 target 0 1 WG2 1.4 0.71 1 2 WG2 1.4 0.71 2 3 WG2 1.0 0.52 3 4 G1 4.8 0.88 4 5 G1 4.9 NaN 5 6 G2 2.1 0.76 6 7 SWG3 0.7 0.31 7 8 WG3 0.8 0.65 8 9 WG3 0.7 0.53 9 10 WG2 1.1 0.68 10 11 <NA> 0.0 NaN
方法二:用merge_asof高效处理(适合大数据集)
如果你的数据量很大,逐行apply会很慢,这时候可以用merge_asof做向量级别的匹配,速度会快很多:
步骤1:拆分精确匹配和剩余行
先把精确匹配的行单独提取出来,剩下的行用近似匹配处理:
# 精确匹配的行 df_exact = df2.merge(df1, left_on=['type', 'u_g_1'], right_on=['spec', 'u_g'], how='inner') # 剩余需要近似匹配的行 df_remaining = df2[~df2.index.isin(df_exact.index)].copy()
步骤2:排序数据
merge_asof要求左右两边的数据都按匹配键排序,所以先对两个数据集排序:
# 按type和u_g_1排序剩余行 df_remaining_sorted = df_remaining.sort_values(['type', 'u_g_1']) # 按spec和u_g排序DF1 df1_sorted = df1.sort_values(['spec', 'u_g'])
步骤3:用merge_asof做近似匹配
direction='forward'会找第一个大于等于u_g_1的u_g,之后我们再过滤掉等于的情况,只保留大于的:
df_approx = pd.merge_asof( df_remaining_sorted, df1_sorted, left_on='u_g_1', right_on='u_g', left_by='type', right_by='spec', direction='forward' ) # 过滤掉u_g <= u_g_1的情况(确保是大于的最小值) df_approx = df_approx[df_approx['u_g'] > df_approx['u_g_1']]
步骤4:合并结果并排序
把精确匹配和近似匹配的结果合并,再按id排序:
df2_modified = pd.concat([df_exact, df_approx], ignore_index=True) df2_modified = df2_modified.sort_values('id').reset_index(drop=True) df2_modified = df2_modified[['id', 'type', 'u_g_1', 'target']]
这个方法得到的结果和方法一完全一致,但处理速度会快很多,适合十万级以上的数据量。
内容的提问来源于stack exchange,提问作者Aukru
相关产品推荐
相关产品推荐

