Pandas按标识符分组聚合字符串变量并处理多值与空单元格
解决DataFrame按ID合并并处理多值列的问题
我明白你现在需要处理一个带重复ID和大量空值的DataFrame,核心需求是把同一ID的所有非空值整合到一行,同时对多出来的条目生成带后缀的标识来区分。下面给你提供一套基于Pandas的实用解决方案,完全贴合你的场景。
首先,先把你给出的示例数据转换成可复现的测试DataFrame,方便你直接验证:
import pandas as pd import numpy as np # 构造和示例匹配的测试数据 data = [ ["Tom", "sarah", "mike", np.nan, np.nan, np.nan, np.nan, np.nan, np.nan], ["Tom", np.nan, np.nan, "john", "john", np.nan, np.nan, np.nan, np.nan], ["Gen", "paul", np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan], ["Gen", np.nan, np.nan, "sandra", np.nan, np.nan, np.nan, np.nan, np.nan], ["Gen", np.nan, np.nan, "lara", "lara", "lara", "lara", np.nan, np.nan], ["Gen", np.nan, np.nan, "mike", "mike", np.nan, np.nan, np.nan, np.nan], ["Lara", "bill", "bill", "bill", np.nan, np.nan, np.nan, np.nan, np.nan], ["Lara", np.nan, np.nan, "josh", "josh", np.nan, np.nan, np.nan, np.nan], ["Lara", np.nan, np.nan, "kevin", "kevin", np.nan, np.nan, np.nan, np.nan] ] df = pd.DataFrame(data, columns=["ID", "name1", "name2", "name3", "name4", "name5", "name6", "name7", "name8"])
方案一:把同一ID的所有非空值合并到一行,生成带序号后缀的列
这个方案适合你需要把同一ID的所有非空内容集中到一行,按顺序用name_1、name_2这样的后缀区分的场景:
def process_single_id_group(group): # 收集当前ID分组下所有name列的非空值,按顺序排列 all_non_null_values = group.filter(like='name').stack().tolist() # 生成带序号的新列名 new_col_names = [f"name_{i+1}" for i in range(len(all_non_null_values))] # 返回包含ID和所有非空值的Series,用于后续合并 return pd.Series([group['ID'].iloc[0]] + all_non_null_values, index=["ID"] + new_col_names) # 按ID分组处理,合并结果并重置索引 final_result = df.groupby('ID', group_keys=False).apply(process_single_id_group).reset_index(drop=True)
代码逻辑说明:
group.filter(like='name'):只保留当前分组中所有以name开头的列,过滤掉ID列.stack():把列维度的数据转成行维度,自动跳过空值,快速集中所有非空的name值- 生成
name_1、name_2这类后缀列名,保证每个非空值都有唯一对应的列 - 通过
groupby.apply把每个ID的处理结果合并成完整的DataFrame
处理后的结果示例:
| ID | name_1 | name_2 | name_3 | name_4 | name_5 | name_6 | name_7 | name_8 |
|---|---|---|---|---|---|---|---|---|
| Gen | paul | sandra | lara | lara | lara | lara | mike | mike |
| Lara | bill | bill | bill | josh | josh | kevin | kevin | NaN |
| Tom | sarah | mike | john | john | NaN | NaN | NaN | NaN |
方案二:保留原始列名,仅对同一列的多值生成带后缀的新列
如果你的需求是保留原始列的对应关系,只有当同一ID的同一原始列(比如name1)有多个非空值时,才生成name1_2这类后缀列,用下面的代码:
# 把宽表转成窄表,仅保留非空值 melted_df = df.melt(id_vars='ID', var_name='original_col', value_name='value').dropna() # 给每个ID的同一原始列的条目加序号后缀 melted_df['suffix'] = melted_df.groupby(['ID', 'original_col']).cumcount() + 1 # 生成新列名:单值用原始列名,多值加后缀 melted_df['new_col'] = melted_df.apply( lambda x: f"{x['original_col']}_{x['suffix']}" if x['suffix'] > 1 else x['original_col'], axis=1 ) # 转回宽表得到最终结果 final_result = melted_df.pivot(index='ID', columns='new_col', values='value').reset_index()
这个方案更适合需要追溯原始列对应关系的场景,不会打乱原有列的命名逻辑。
内容的提问来源于stack exchange,提问作者user8880321
相关产品推荐
相关产品推荐

