Pandas:计算中间In/Out记录的总timedelta(total_seconds())
解决方案
步骤1:数据预处理
首先将日期和时间合并为可计算的datetime类型,并按日期、姓名、时间排序,确保打卡序列的正确性:
import pandas as pd # 加载示例数据 example_df = pd.DataFrame([ ['2024-01-01', 'Homer', 'in', '07:30'], ['2024-01-01', 'Homer', 'out' ,'09:00'], ['2024-01-01', 'Homer', 'in' ,'09:30'], ['2024-01-01', 'Homer', 'out' ,'16:00'], ['2024-01-01', 'Marge', 'in' , '06:20'], ['2024-01-01', 'Marge', 'out' ,'16:00'], ['2024-01-01', 'Bart', 'in' ,'07:10'], ['2024-01-01', 'Bart', 'out' ,'08:00'], ['2024-01-01', 'Bart', 'in' ,'08:20'], ['2024-01-01', 'Bart', 'out' ,'17:00'], ['2024-01-01', 'Barney', 'in' ,'08:10'], ['2024-01-01', 'Lisa', 'in' ,'08:05'], ['2024-01-01', 'Lisa', 'out' ,'14:00'], ['2024-01-01', 'Lisa', 'in' ,'14:15'], ['2024-01-01', 'Lisa', 'out' ,'18:10'], ['2024-01-01', 'Millhouse', 'out' ,'19:10'], ['2024-02-01', 'Homer', 'in', '07:30'], ['2024-02-01', 'Homer', 'out' ,'09:00'], ['2024-02-01', 'Marge', 'in' , '06:30'], ['2024-02-01', 'Marge', 'out' ,'09:10'], ['2024-02-01', 'Marge', 'in' ,'10:10'], ['2024-02-01', 'Marge', 'out' ,'16:10'], ['2024-02-01', 'Bart', 'in' ,'07:10'], ['2024-02-01', 'Bart', 'out' ,'15:00'], ['2024-02-01', 'Barney', 'in' ,'08:10'], ['2024-02-01', 'Lisa', 'in' ,'08:05'], ['2024-02-01', 'Lisa', 'out' ,'16:00'], ['2024-02-01', 'Millhouse', 'in' ,'08:10'], ['2024-02-01', 'Millhouse', 'in' ,'08:10'], ['2024-02-01', 'Millhouse', 'in' ,'16:15']], columns=['Date', 'Name', 'In/Out', 'Time']) # 合并日期与时间为datetime列 example_df['Datetime'] = pd.to_datetime(example_df['Date'] + ' ' + example_df['Time']) # 按日期、姓名、时间排序 example_df = example_df.sort_values(by=['Date', 'Name', 'Datetime']).reset_index(drop=True)
步骤2:计算中间记录的总时间差
这里针对用户离岗后再返回的间隔时长(即out后接in的时间段)进行计算,这与你示例中Homer对应1800秒的逻辑匹配:
def calculate_middle_interval(group): if len(group) < 2: return 0 # 获取下一条记录的时间和打卡状态 group['next_datetime'] = group['Datetime'].shift(-1) group['next_inout'] = group['In/Out'].shift(-1) # 筛选出out后接in的有效间隔,计算总秒数 valid_rows = group[(group['In/Out'] == 'out') & (group['next_inout'] == 'in')] total_seconds = (valid_rows['next_datetime'] - valid_rows['Datetime']).dt.total_seconds().sum() return total_seconds # 按日期和姓名分组计算 result = example_df.groupby(['Date', 'Name']).apply(calculate_middle_interval).reset_index(name='Total_Seconds') # 过滤无中间记录的条目 result = result[result['Total_Seconds'] > 0].reset_index(drop=True)
最终结果
运行上述代码后,result将只包含有中间打卡记录的用户,输出格式如下:
Date Name Total_Seconds 0 2024-01-01 Bart 1200.0 1 2024-01-01 Homer 1800.0 2 2024-01-01 Lisa 900.0 3 2024-02-01 Marge 3600.0
调整说明
如果你的“中间记录”指的是除首次入岗和末次离岗外的工作时长(即中间in-out对的工作时间总和),可替换为以下分组处理函数:
def calculate_middle_work_time(group): # 提取有效的in-out工作配对 valid_pairs = [] prev_in = None for _, row in group.iterrows(): if row['In/Out'] == 'in': prev_in = row['Datetime'] elif row['In/Out'] == 'out' and prev_in is not None: valid_pairs.append((prev_in, row['Datetime'])) prev_in = None # 仅保留中间的工作配对(排除首尾) if len(valid_pairs) < 2: return 0 middle_pairs = valid_pairs[1:-1] if len(valid_pairs) > 2 else valid_pairs[1:] total_seconds = sum((end - start).total_seconds() for start, end in middle_pairs) return total_seconds
内容的提问来源于stack exchange,提问作者Brunch1012
相关产品推荐
相关产品推荐

