You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pandas DataFrame重复行求和去重问题排查(Python)

Pandas DataFrame重复行求和保留最后一行异常排查

需求描述

对DataFrame中ID、RecordingType、Date完全相同的行,将Sleep字段求和后保留组内最后一行,删除其余行。当前代码运行后,17/01/2022对应的Sleep值仅为220(预期250),且当重复行超过3行时功能失效。

原始DataFrame

ID  RecordingType Date        Sleep
 1   MinsAsleep    17/01/2022  30
 1   MinsAsleep    17/01/2022  40
 1   MinsAsleep    17/01/2022  50
 1   MinsAsleep    17/01/2022  60
 1   MinsAsleep    17/01/2022  70
 1   MinsAsleep    19/01/2022  100

预期结果

ID  RecordingType Date        Sleep
 1   MinsAsleep    17/01/2022  250
 1   MinsAsleep    19/01/2022  100

现有代码

temp_physdata = # dataframe I inserted above 
temp_physdata = temp_physdata.sort_values(by=['ID', 'RecordingType', 'Date','Sleep'], ascending=True).reset_index().drop(columns=["index"])
temp_physdata["ToRemove"] = False
dict_temp_physdata = temp_physdata.to_dict("records")

outputcolnames = {'MinsAsleep':'Sleep'}
    

for i in range(temp_physdata.shape[0]-1):
        
        curr_recording_type = dict_temp_physdata[i]["RecordingType"]
        next_recording_type = dict_temp_physdata[i+1]["RecordingType"]
        
        # Check if current and next row have the same ID and RecordingType and Date
        
        if (dict_temp_physdata[i]["ID"] == dict_temp_physdata[i+1]["ID"] and curr_recording_type == next_recording_type and dict_temp_physdata[i]["Date"] == dict_temp_physdata[i+1]["Date"]):

            # Check if current row is MinsAsleep recording
            
            if curr_recording_type == 'MinsAsleep':
                
                # Check if the next row is the last row with the same ID, RecordingType, and Date values
                
                if i+1 == temp_physdata.shape[0]-1 or (dict_temp_physdata[i+1]["ID"] != dict_temp_physdata[i+2]["ID"] or 
                                              next_recording_type != dict_temp_physdata[i+2]["RecordingType"] or 
                                              dict_temp_physdata[i+1]["Date"] != dict_temp_physdata[i+2]["Date"]):
                    
                    temp_physdata.at[i,"ToRemove"] = True
                    
                    # Update the last row with the sum of all the MinsAsleep Recording values
                    temp_physdata.at[i+1, outputcolnames[next_recording_type]] += dict_temp_physdata[i][outputcolnames[curr_recording_type]]
                    
                    # Mark all the other rows to be removed
                    
                     for j in range(i, temp_physdata.shape[0]-1):
                        
                         if dict_temp_physdata[j]["ID"] == dict_temp_physdata[i]["ID"] and curr_recording_type == dict_temp_physdata[j]["RecordingType"] and dict_temp_physdata[i]["Date"] == dict_temp_physdata[j]["Date"]:
                            
                             temp_physdata.at[j, "ToRemove"] = True
                          
            else:
            
                temp_physdata.at[i,"ToRemove"] = True
                temp_physdata.at[i+1,outputcolnames[next_recording_type]] = max(dict_temp_physdata[i][outputcolnames[curr_recording_type]], dict_temp_physdata[i+1][outputcolnames[next_recording_type]])

temp_physdata = temp_physdata[temp_physdata["ToRemove"] == False].reset_index().drop(columns=["index"])

实际结果

ID  RecordingType Date        Sleep
 1   MinsAsleep    17/01/2022  220
 1   MinsAsleep    19/01/2022  100

问题原因分析

  1. 求和逻辑不完整:现有代码仅当i+1是同组最后一行时,才将当前行Sleep值累加到下一行,导致组内前面的多行(如示例中前3行的30、40、50)未被纳入求和,最终仅累加了40+50+60+70=220,遗漏了首行的30。
  2. 字典数据未同步更新:dict_temp_physdata是基于初始DataFrame转换的字典,后续修改temp_physdata的Sleep值不会同步到字典中,后续循环判断和计算依赖的仍是原始数据,逻辑易出错。
  3. 删除标记逻辑混乱:内层循环标记删除时,错误将同组最后一行也标记为待删除(如示例中第4行的ToRemove会被设为True),仅因循环范围未覆盖最后一行才侥幸保留,但逻辑存在严重漏洞。
  4. 循环条件限制:仅处理相邻行的判断,无法应对超过3行的重复组,导致前面的行完全未被处理。

修复方案

直接使用Pandas内置的groupby功能实现需求,代码简洁且可靠:

方案1:仅保留求和后的Sleep值

temp_physdata = temp_physdata.groupby(
    ['ID', 'RecordingType', 'Date'], 
    as_index=False
).agg(Sleep=('Sleep', 'sum'))

方案2:保留组内最后一行的其他字段(若存在)

如果需要保留组内最后一行的其他列信息,同时更新Sleep为求和值:

temp_physdata = temp_physdata.groupby(
    ['ID', 'RecordingType', 'Date'], 
    as_index=False
).apply(lambda x: x.assign(Sleep=x['Sleep'].sum()).tail(1)).reset_index(drop=True)

以上两种方案均能正确计算出17/01/2022的Sleep值为250,且无论重复行数量多少都能稳定运行。

内容的提问来源于stack exchange,提问作者mariant

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 01:48:28