如何用Pandas移除复合键重复行并保留分散非缺失值?
预期结果
我有如下格式的数据表:
希望将其转换为如下格式:
问题描述
ID和Event #字段构成复合键,代表表中的唯一条目。
条目可能存在两次或多次重复,但部分行值分散在重复行中,且无法确定有效数据位于重复行的首行、末行还是中间行。
我希望移除重复条目,同时保留所有已填充的行值,无论其分布在哪些重复行中。
如何用Pandas实现这一需求?
查阅相关内容后,我认为需要使用groupby和fillna或ffill/bfill方法,但作为Pandas新手,我不清楚如何在以下条件下实现:
- 行通过复合键区分
- 存在超过1条重复行的情况
- 有效数据分散在重复行的多个字段中
- 无法确定有效数据位于重复行的首行、末行还是中间行
以下是示例DataFrame:
df = pd.DataFrame([['ABC111', 1, '1/1/23 12:00:00', None, '1/1/23 13:30:00', None], ['ABC111', 2, '1/2/23 00:00:00', None, '1/2/23 13:30:00', None], ['ABC111', 3, '1/3/23 00:00:00', None, '1/3/23 13:30:00', None], ['ABC112', 1, '1/1/23 00:00:00', None, '1/1/23 13:30:00', None], ['ABC112', 2, '1/2/23 00:00:00', 'Test Value A', None, None], ['ABC112', 2, '1/2/23 00:00:00', 'Test Value A', None, None], ['ABC112', 2, None, None, '1/2/23 13:30:00', 'Test Value B'], ['ABC113', 1, '1/1/23 00:00:00', None, '1/1/23 13:30:00', None], ['ABC113', 2, '1/2/23 00:00:00', None, '1/2/23 13:30:00', None], ['ABC113', 3, None, None, '1/3/23 13:30:00', 'Test Value B'], ['ABC113', 3, '1/3/23 00:00:00', 'Test Value A', None, None], ['ABC114', 1, '1/1/23 00:00:00', 'Test Value A', None, None], ['ABC114', 1, None, None, '1/1/23 13:30:00', 'Test Value B'], ['ABC114', 1, None, None, '1/1/23 13:30:00', 'Test Value B'], ['ABC114', 1, None, None, '1/1/23 13:30:00', 'Test Value B'], ['ABC114', 1, None, None, '1/1/23 13:30:00', 'Test Value B'], ['ABC114', 2, '1/2/23 00:00:00', None, '1/2/23 13:30:00', None], ['ABC114', 3, '1/3/23 00:00:00', None, '1/3/23 13:30:00', None]], columns=['ID', 'Event #', 'Start Date', 'Start Value', 'End Date', 'End Value'])
解决方案
你可以通过分组+聚合非空值的方式实现需求,核心逻辑是按复合键ID和Event #分组,将每组内分散在不同行的有效数据合并到一行,同时移除重复条目。以下是几种可行方法:
方法1:分组后聚合取第一个非空值
该方法直接对每组的每个字段提取第一个非空值,确保保留有效数据,是最稳妥的方案:
# 按复合键分组,对每个列取第一个非空值;若列全为空则保留None result = df.groupby(['ID', 'Event #'], as_index=False).agg( lambda x: x.dropna().iloc[0] if not x.dropna().empty else None )
方法2:分组填充缺失值后去重
先对每组进行前向+后向填充,让同一组内所有行都补全有效数据,再去重保留每组唯一一行:
# 分组后用前向+后向填充补全缺失值 filled_df = df.groupby(['ID', 'Event #']).apply(lambda x: x.ffill().bfill()) # 去重保留每组唯一条目 result = filled_df.drop_duplicates(['ID', 'Event #'])
方法3:利用first聚合(适用于非空值唯一场景)
如果同一组内每个字段的非空值都是唯一的,直接用first聚合就能快速得到结果,它会取每组内第一个出现的非空值:
result = df.groupby(['ID', 'Event #'], as_index=False).first()
以上三种方法都能满足你的需求,可根据实际数据场景选择:方法1通用性最强;方法2适合需要保留原表部分行结构的情况;方法3最简单高效。
内容的提问来源于Stack Exchange,提问作者Nick
相关产品推荐
相关产品推荐

