如何用Python检测员工在不同地点的重叠打卡记录
识别同一员工跨门店重叠考勤记录的Python实现方案
下面提供两种可行的实现方案,分别基于你熟悉的Pandas工具,以及你考虑使用的DateTimeRange库:
方案一:用Pandas原生工具实现
步骤1:数据读取与预处理
先把考勤数据转换成DataFrame,再将日期和时间字段合并成完整的datetime时间戳,这是判断时间重叠的基础:
import pandas as pd # 加载考勤数据(实际场景可替换为pd.read_csv读取文件) data = { 'store': ['store1', 'store1', 'store2', 'store2'], 'name': ['reese', 'john', 'reese', 'bill'], 'day': ['Mon', 'Mon', 'Mon', 'Mon'], 'date': ['8/8/2022', '8/8/2022', '8/8/2022', '8/8/2022'], 'clockin': ['8:55:32 AM', '8:55:27 AM', '9:00:00 AM', '12:36:35 PM'], 'clockout': ['11:12:35 AM', '11:59:34 AM', '12:00:00 PM', '5:02:31 PM'] } df = pd.DataFrame(data) # 合并日期与打卡时间,转换为datetime类型 df['start_time'] = pd.to_datetime(df['date'] + ' ' + df['clockin']) df['end_time'] = pd.to_datetime(df['date'] + ' ' + df['clockout'])
步骤2:分组检查时间重叠
按「员工姓名+日期」分组,对每组内的记录两两比对:判断是否属于不同门店,且时间区间存在重叠(重叠逻辑为区间A的开始时间 < 区间B的结束时间,且区间B的开始时间 < 区间A的结束时间):
def check_overlapping(group): # 遍历组内所有记录对 for i in range(len(group)): row1 = group.iloc[i] for j in range(i+1, len(group)): row2 = group.iloc[j] # 不同门店+时间重叠触发标记 if row1['store'] != row2['store']: if row1['start_time'] < row2['end_time'] and row2['start_time'] < row1['end_time']: return pd.Series([True, row1, row2]) return pd.Series([False, None, None]) # 分组执行检查 result = df.groupby(['name', 'date']).apply(check_overlapping) result.columns = ['has_overlap', 'record1', 'record2'] # 筛选出有重叠的异常记录 overlapping_records = result[result['has_overlap'] == True] print(overlapping_records)
方案二:用DateTimeRange库实现
DateTimeRange封装了时间区间的常用操作,无需自己写重叠判断逻辑,代码更直观:
步骤1:安装并导入库
pip install DateTimeRange
步骤2:数据预处理+重叠检查
预处理逻辑和Pandas方案一致,用DateTimeRange对象封装时间区间,调用内置的is_intersection方法判断重叠:
import pandas as pd from datetimerange import DateTimeRange # 同样先加载并预处理数据 data = { 'store': ['store1', 'store1', 'store2', 'store2'], 'name': ['reese', 'john', 'reese', 'bill'], 'day': ['Mon', 'Mon', 'Mon', 'Mon'], 'date': ['8/8/2022', '8/8/2022', '8/8/2022', '8/8/2022'], 'clockin': ['8:55:32 AM', '8:55:27 AM', '9:00:00 AM', '12:36:35 PM'], 'clockout': ['11:12:35 AM', '11:59:34 AM', '12:00:00 PM', '5:02:31 PM'] } df = pd.DataFrame(data) df['start_time'] = pd.to_datetime(df['date'] + ' ' + df['clockin']) df['end_time'] = pd.to_datetime(df['date'] + ' ' + df['clockout']) def check_overlap_with_dtr(group): # 为每条记录创建DateTimeRange对象 range_list = [] for idx, row in group.iterrows(): time_range = DateTimeRange(row['start_time'], row['end_time']) range_list.append((row, time_range)) # 遍历检查记录对 for i in range(len(range_list)): row1, range1 = range_list[i] for j in range(i+1, len(range_list)): row2, range2 = range_list[j] if row1['store'] != row2['store'] and range1.is_intersection(range2): return pd.Series([True, row1, row2]) return pd.Series([False, None, None]) # 分组执行并筛选结果 result = df.groupby(['name', 'date']).apply(check_overlap_with_dtr) result.columns = ['has_overlap', 'record1', 'record2'] overlapping_records = result[result['has_overlap'] == True] print(overlapping_records)
额外注意事项
- 如果实际考勤数据的日期时间格式有差异,可以给
pd.to_datetime指定format参数(比如format="%m/%d/%Y %I:%M:%S %p"),避免解析错误。 - 若数据量很大,双重循环效率较低,可以先对每组的
start_time排序,只检查相邻记录,能大幅提升性能。
内容的提问来源于stack exchange,提问作者user19695340
相关产品推荐
相关产品推荐

