如何用Pandas对比DataFrame识别已完成与待完成实验任务?
Pandas合并操作异常问题排查
问题背景
我有两个DataFrame:
df_ref(从YAML生成):包含所有计划运行的实验配置df_data(从CSV读取):记录已完成的实验
需求:
- 返回已完成和待完成的实验任务列表(字典格式)
- YAML的列是CSV列的严格子集
- 可移除对比中无关的列
现有代码
def get_incomplete_configs(full_config, project, user='', drop_cols=[]): # load in csv (downloaded from wandb) configsrun_df = pd.read_csv('completed_runs.csv') # proposed config dataframe full_config = pd.DataFrame(full_config).drop_duplicates() # which attributes do we care about merge_on = list(set(full_config.columns.values) - set(drop_cols)) # pick the relevant ones configsrun_df = configsrun_df[merge_on].dropna() full_config = full_config[merge_on].dropna() # type matching mismatches = [(i, configsrun_df[col].dtype, full_config[col].dtype) for i, col in enumerate(merge_on) if configsrun_df[col].dtype != full_config[col].dtype] if len(mismatches): for m in mismatches: if (m[1]=='int64') or (m[2]=='int64'): configsrun_df[merge_on[m[0]]] = configsrun_df[merge_on[m[0]]].astype(int) full_config[merge_on[m[0]]] = full_config[merge_on[m[0]]].astype(int) else: raise Exception # merging operation which should work completed_runs = pd.merge(full_config, configsrun_df, indicator=True, how='outer').query('_merge=="left_only"').drop('_merge', axis=1).drop_duplicates() # convert to list of dicts completed_runs = completed_runs.T.to_dict().values() print('this job is {}% complete'.format(round(len(completed_runs)/len(full_config),2))) # return return completed_runs
核心疑问
当前merge操作返回结果异常,数据量不匹配,对合并操作的理解哪里有问题?
问题分析与修正
1. 合并结果含义完全搞反
你当前代码中:
completed_runs = pd.merge(...).query('_merge=="left_only"')
outer join后的_merge字段含义:
left_only:仅存在于full_config(计划任务)中的记录 → 待完成任务both:同时存在于计划和已完成列表中的记录 → 已完成任务right_only:仅存在于已完成列表中的记录 → 计划外的额外任务
你把left_only的结果命名为completed_runs,完全颠倒了含义,这是数据量不匹配的核心原因。
2. 列顺序丢失导致隐性问题
用集合操作生成merge_on会打乱原始列顺序:
merge_on = list(set(full_config.columns.values) - set(drop_cols))
虽然merge不依赖列顺序,但后续类型转换、数据校验可能出现隐性错误,建议改为列表推导式保持顺序:
merge_on = [col for col in full_config.columns if col not in drop_cols]
3. 类型转换逻辑过于激进
原代码仅处理int64类型不匹配的情况,其他直接抛异常,无法处理常见的类型兼容问题(比如CSV中是float但实际是整数、字符串含空格等)。建议采用更通用的转换逻辑:优先转为数值类型,失败则转为字符串并去除首尾空格。
4. 提前dropna丢失有效记录
merge前对两个DataFrame执行dropna(),会直接丢失存在缺失值的计划任务或已完成任务,应该先处理类型匹配,再根据业务需求决定是否过滤缺失值。
修正后的代码
def get_task_status(full_config, project, user='', drop_cols=[]): # 加载已完成任务CSV configsrun_df = pd.read_csv('completed_runs.csv') # 转换计划任务为DataFrame并去重 full_config = pd.DataFrame(full_config).drop_duplicates() # 确定用于匹配的列(保持原始顺序) merge_on = [col for col in full_config.columns if col not in drop_cols] # 筛选相关列 configsrun_df = configsrun_df[merge_on] full_config = full_config[merge_on] # 类型匹配优化:优先统一为数值类型,失败则转为字符串 for col in merge_on: try: # 尝试转为整数(自动处理CSV中的float整数) configsrun_df[col] = pd.to_numeric(configsrun_df[col], downcast='integer') full_config[col] = pd.to_numeric(full_config[col], downcast='integer') except (ValueError, TypeError): # 转为字符串并去除首尾空格,避免格式差异导致匹配失败 configsrun_df[col] = configsrun_df[col].astype(str).str.strip() full_config[col] = full_config[col].astype(str).str.strip() # 执行外连接,标记匹配状态 merged = pd.merge(full_config, configsrun_df, on=merge_on, indicator=True, how='outer') # 分离待完成、已完成任务 incomplete_runs = merged[merged['_merge'] == 'left_only'].drop('_merge', axis=1).drop_duplicates() completed_runs = merged[merged['_merge'] == 'both'].drop('_merge', axis=1).drop_duplicates() # 转换为字典列表(更简洁的方式) incomplete_list = incomplete_runs.to_dict('records') completed_list = completed_runs.to_dict('records') # 计算并打印完成率 completion_rate = round(len(completed_list) / len(full_config) * 100, 2) print(f'任务完成率:{completion_rate}%') # 返回已完成和待完成任务列表 return {'completed': completed_list, 'incomplete': incomplete_list}
内容的提问来源于stack exchange,提问作者Outstretched Pupil
相关产品推荐
相关产品推荐

