如何合并与拆分DataFrame行?处理同名不同用户及重复数据
解决思路与实现代码
核心问题分析
你的需求本质是区分"同名同用户"和"同名不同用户",并合并同一用户的分散信息:
- 同名同用户:不同行属于同一个人,需合并所有非缺失信息
- 同名不同用户:姓名相同但属于不同个体(如原数据中的两个"C"),需拆分为独立行
邮箱是最可靠的用户唯一标识,因此我们以邮箱为核心分组依据,结合姓名关联无邮箱的行,最终合并信息。
实现步骤与代码
1. 构造示例数据
import pandas as pd from pandas import NaT # 还原你的原DataFrame df = pd.DataFrame({ 'Name': ['A', 'B', 'C', 'D', 'B', 'D', 'B', 'C'], 'Mail': ['A@gmail.com', None, 'C@gmail.com', None, None, 'D@gmail.com', 'B@gmail.com', 'C2@gmail.com'], 'Birthday': ['1-1-1990', NaT, '1-1-1985', '1-1-1980', '1-1-1995', NaT, NaT, '1-1-1970'], 'Genre': ['M', 'F', 'M', 'I', 'I', 'M', 'I', 'F'], 'Subscription': ['Y', 'N', 'Y', 'N', 'N', 'Y', 'Y', 'N'], 'Age': [33, None, 38, 43, 27, None, None, 53], 'Comments': ['-'] * 8 })
2. 关联无邮箱行到对应用户
通过姓名匹配已有邮箱,填充无邮箱行的邮箱字段:
# 创建「姓名→邮箱」映射(仅保留每个姓名对应的非空邮箱) name_mail_map = df[df['Mail'].notna()].drop_duplicates('Name').set_index('Name')['Mail'].to_dict() # 填充无邮箱行的邮箱 df['Mail'] = df['Mail'].fillna(df['Name'].map(name_mail_map)) # 处理仍无邮箱的行(生成临时唯一标识,避免丢失) mask = df['Mail'].isna() df.loc[mask, 'Mail'] = df.loc[mask, 'Name'] + '_' + df.loc[mask, 'Birthday'].astype(str)
3. 定义合并规则函数
合并同一用户的多行,保留所有非缺失值(优先用前后填充补全有效信息,最后一行即为合并结果):
def merge_user_group(group): # 前后填充补全缺失值,取最后一行作为合并结果 return group.ffill().bfill().iloc[-1]
4. 按邮箱分组合并
# 按邮箱分组合并用户信息 merged_df = df.groupby('Mail').apply(merge_user_group).reset_index(drop=True) # 将邮箱前缀作为新姓名(区分同名不同用户) merged_df['Name'] = merged_df['Mail'].str.split('@').str[0] # 调整列顺序与期望结果一致 merged_df = merged_df[['Name', 'Mail', 'Birthday', 'Genre', 'Subscription', 'Age', 'Comments']]
5. 最终结果
Name Mail Birthday Genre Subscription Age Comments 0 A A@gmail.com 1-1-1990 M Y 33 - 1 B B@gmail.com 1-1-1995 F Y 27 - 2 C C@gmail.com 1-1-1985 M Y 38 - 3 C2 C2@gmail.com 1-1-1970 F N 53 - 4 D D@gmail.com 1-1-1980 M Y 43 -
自定义调整说明
如果需要针对特定列修改合并规则(如Age取最大值、Genre取出现次数最多的值),可以修改merge_user_group函数:
def merge_user_group(group): merged = group.copy() # 示例:Age取最大值 merged['Age'] = merged['Age'].max() # 示例:Genre取出现次数最多的值 merged['Genre'] = merged['Genre'].mode()[0] # 其他列保留非空值 for col in merged.columns.drop(['Age', 'Genre']): merged[col] = merged[col].ffill().bfill().iloc[-1] return merged.iloc[-1]
内容的提问来源于stack exchange,提问作者Pablo Moreira Garcia
相关产品推荐
相关产品推荐

