如何用Pandas数据集计算员工在岗时长与缺勤时长?
解决方案:Pandas处理打卡数据计算在岗/缺勤时长
先指出你原代码里的几个问题:
df.iteritems()是遍历列的方法,不是遍历行,遍历行应该用df.iterrows(),但更推荐用Pandas的向量化操作替代手动遍历- 条件判断逻辑错误:
if (df['Addr'] == 'Main Entry-In' or 'Prod. Entry-In')会始终返回True,因为'Prod. Entry-In'是真值,正确写法是判断Addr是否包含Entry/Exit关键词 - 语法错误:
entries.['Time']多了个点,应该是entries['Time']
下面是高效的向量化解决方案,无需手动逐行遍历:
步骤1:数据预处理
先合并日期时间为可计算的datetime类型,标记打卡类型,并按用户和时间排序(这是时间计算的基础,确保打卡顺序正确):
import pandas as pd # 假设数据已读取到df中 df['Datetime'] = pd.to_datetime(df['Date'] + ' ' + df['Time'], format='%Y.%m.%d %H:%M') # 标记打卡类型:Entry/Exit df['CheckType'] = df['Addr'].apply(lambda x: 'Entry' if 'Entry In' in x else 'Exit' if 'Exit' in x else 'Unknown') # 按用户ID和时间排序,保证打卡顺序正确 df = df.sort_values(by=['User ID', 'Datetime']).reset_index(drop=True)
步骤2:计算在岗时长(Present Time)
同一用户的Exit时间减去对应的Entry时间,通过分组偏移行来配对:
# 分组后获取下一条记录的时间和打卡类型 df['Next_Datetime'] = df.groupby('User ID')['Datetime'].shift(-1) df['Next_CheckType'] = df.groupby('User ID')['CheckType'].shift(-1) # 仅当当前是Entry且下一条是Exit时,计算在岗时长 df['present_time'] = df.apply( lambda row: (row['Next_Datetime'] - row['Datetime']) if row['CheckType'] == 'Entry' and row['Next_CheckType'] == 'Exit' else pd.NaT, axis=1 )
步骤3:计算缺勤时长(Absence Time)
下一次Entry时间减去上一次Exit时间:
# 分组后获取上一条记录的时间和打卡类型 df['Prev_Datetime'] = df.groupby('User ID')['Datetime'].shift(1) df['Prev_CheckType'] = df.groupby('User ID')['CheckType'].shift(1) # 仅当当前是Entry且上一条是Exit时,计算缺勤时长 df['absence_time'] = df.apply( lambda row: (row['Datetime'] - row['Prev_Datetime']) if row['CheckType'] == 'Entry' and row['Prev_CheckType'] == 'Exit' else pd.NaT, axis=1 )
步骤4:处理异常打卡(连续打卡/漏打卡)
过滤连续打卡(同一类型连续出现)
# 标记连续相同类型的打卡为异常 df['Is_Abnormal'] = df.groupby('User ID')['CheckType'].apply(lambda x: x == x.shift()) # 过滤掉连续异常的打卡(保留第一个连续项) df = df[~df['Is_Abnormal']].reset_index(drop=True)
处理漏打卡(Entry/Exit不配对)
可以统计每个用户的Entry和Exit数量,标记不匹配的记录:
# 统计每个用户的Entry/Exit数量 check_counts = df.groupby(['User ID', 'CheckType']).size().unstack(fill_value=0) # 找出Entry和Exit数量不等的用户 unmatched_users = check_counts[check_counts['Entry'] != check_counts['Exit']].index # 标记这些用户的最后一条记录为漏打卡 df['Is_Missing'] = df.apply( lambda row: True if row['User ID'] in unmatched_users and row.name == df[df['User ID'] == row['User ID']].index[-1] else False, axis=1 )
最终结果整理
保留需要的列,或按用户汇总结果:
# 选择需要的列展示 result = df[['User ID', 'User Name', 'Datetime', 'CheckType', 'present_time', 'absence_time', 'Is_Abnormal', 'Is_Missing']] print(result)
关键说明
- 用向量化操作替代手动遍历,效率远高于逐行循环(Pandas的核心优势就是向量化)
- 必须先按用户和时间排序,否则时间差计算会完全错误
- 异常处理规则可根据业务调整,比如连续打卡的时间阈值(比如10分钟内连续打卡算异常)、漏打卡的补全规则(比如默认补全当天22:00为下班时间)
内容的提问来源于stack exchange,提问作者Nikobitan
相关产品推荐
相关产品推荐

