如何将Pandas DataFrame指定行的列表型单元格拆分为多行并匹配列值
问题
现有如下结构的Pandas DataFrame:
| label | pred | gt |
|---|---|---|
| label1 | val1 | val11 |
| label2 | ['str1', 'str2'] | ['str1', 'str3', 'str4'] |
| label3 | foo | box |
需要将label2行中pred和gt列的列表型数据拆分为多行,列表长度不一致时用None补全,期望结果如下:
| label | pred | gt |
|---|---|---|
| label1 | val1 | val11 |
| label2 | 'str1' | 'str1' |
| label2 | 'str2' | 'str3' |
| label2 | None | 'str4' |
| label3 | foo | box |
尝试使用explode()方法实现,但得到的新DataFrame全为NaN值,且拆分后的行未匹配到正确label,代码如下:
df_filtered = output_df[output_df['label'] == 'label2'] # explode the list column into multiple rows while keeping other columns df_exploded = pd.concat([ df_filtered.drop(['pred', 'gt'], axis=1), df_filtered['pred'].explode().reset_index(drop=True), df_filtered['gt'].explode().reset_index(drop=True) ], axis=1) # add prefix to the existing column name (label) to differentiate each new row df_exploded = df_exploded.add_prefix('new_') # rename the columns to remove the prefix from the original column df_exploded = df_exploded.rename(columns={'new_pred': 'pred', 'new_gt': 'gt'}) # combine the exploded dataframe with the original dataframe, dropping the original list column df_combined = pd.concat([output_df.drop(['pred', 'gt'], axis=1), df_exploded], axis=1)
解决方案
问题分析
你原来的代码有两个核心问题:
reset_index(drop=True)破坏了原数据的索引关联,导致label和拆分后的pred/gt无法正确对齐;- 最后合并时直接拼接原DataFrame和拆分后的DataFrame,没有替换原
label2行,导致结构混乱出现NaN。
正确实现方法
可以通过「分离目标行→对齐拆分→合并结果」的步骤实现需求:
代码实现(兼容所有Pandas版本)
import pandas as pd # 构造示例数据(已有output_df可跳过此步) output_df = pd.DataFrame({ 'label': ['label1', 'label2', 'label3'], 'pred': ['val1', ['str1', 'str2'], 'foo'], 'gt': ['val11', ['str1', 'str3', 'str4'], 'box'] }) # 1. 拆分需要处理和不需要处理的行 non_target_df = output_df[output_df['label'] != 'label2'].copy() target_df = output_df[output_df['label'] == 'label2'].copy() # 2. 对目标行的pred/gt做对齐拆分:统一长度,不足补None def split_aligned(row): pred_list = row['pred'] gt_list = row['gt'] max_length = max(len(pred_list), len(gt_list)) # 补全短列表到最长长度 padded_pred = pred_list + [None] * (max_length - len(pred_list)) padded_gt = gt_list + [None] * (max_length - len(gt_list)) return pd.DataFrame({ 'label': [row['label']] * max_length, 'pred': padded_pred, 'gt': padded_gt }) # 应用拆分函数到目标行 split_result = target_df.apply(split_aligned, axis=1).explode('_0')['_0'].apply(pd.Series) # 3. 合并所有行 final_df = pd.concat([non_target_df, split_result], ignore_index=True) # 可选:给字符串添加引号,匹配示例格式 final_df['pred'] = final_df['pred'].apply(lambda x: f"'{x}'" if pd.notna(x) else x) final_df['gt'] = final_df['gt'].apply(lambda x: f"'{x}'" if pd.notna(x) else x) print(final_df)
简化实现(Pandas 2.0+)
如果使用Pandas 2.0及以上版本,可通过迭代行快速完成:
import pandas as pd output_df = pd.DataFrame({ 'label': ['label1', 'label2', 'label3'], 'pred': ['val1', ['str1', 'str2'], 'foo'], 'gt': ['val11', ['str1', 'str3', 'str4'], 'box'] }) # 遍历每行处理,非列表转单元素列表后补全对齐 rows = [] for _, row in output_df.iterrows(): pred = row['pred'] if isinstance(row['pred'], list) else [row['pred']] gt = row['gt'] if isinstance(row['gt'], list) else [row['gt']] max_len = max(len(pred), len(gt)) pred += [None]*(max_len - len(pred)) gt += [None]*(max_len - len(gt)) rows.append(pd.DataFrame({'label': row['label'], 'pred': pred, 'gt': gt})) final_df = pd.concat(rows, ignore_index=True) # 可选:添加引号 final_df[['pred', 'gt']] = final_df[['pred', 'gt']].applymap(lambda x: f"'{x}'" if pd.notna(x) else x) print(final_df)
内容的提问来源于stack exchange,提问作者Yana
相关产品推荐
相关产品推荐

