注册 登录
Office中国论坛/Access中国论坛 返回首页

roych的个人空间 http://www.office-cn.net/?179386 [收藏] [复制] [分享] [RSS]

日志

使用pandas处理考勤表

热度 1已有 307 次阅读2023-8-20 16:36 |个人分类:随便说说

代码如下,复制后保存为扩展名为py的文件即可。
  1. 如未安装pandas包,请使用pip install pandas进行安装。
  2. 代码请结合附件,在python环境下使用。
  3. 确保附件保存在C盘根目录下,否则请将代码中的路径改为正确的路径。
由于代码注释已经很详细了,所以我这里就不再解释了。
处理流程如下:
  1. 先处理系统错误(空值/数据类型等问题)。
  2. 然后处理逻辑错误(漏打卡,多打卡等问题)。
  3. 根据逻辑计算。
  4. 输出结果。
----------------------------
import pandas as pd
df = pd.read_excel(r'C:\考勤记录模板.xlsx')
# 预览数据
print(df.head(10))
# 查看空值
print(df.isnull().sum())
# 删除空值
df.dropna(subset='时间', inplace=True)
print(df.isnull().sum())
# 修改日期格式
df['日期'] = df['日期'].apply(
    lambda x: pd.to_datetime('1900-1-1')
    + pd.DateOffset(x))
df['时间'] = pd.to_datetime(
    df['时间'], format='%H:%M:%S')
print(df.head(20))
# 添加打卡次数,以便检查多打卡或漏打卡的情况。
df['打卡次数'] = df.groupby(['工号', '日期'])['时间'].rank()
print(df.head(20))
df_times = df.groupby(['工号', '日期'])['打卡次数'].agg(
    ['min', 'max'])
df_times = df_times.reset_index()
print(df_times.head())
df_new = pd.merge(df, df_times, on=['工号', '日期'])
print(df_new.head())
# 多打卡的位置介乎最小值和最大值之间,因此将两者与打卡次数相减,必然大于0
df_new['多打卡'] = (df_new['打卡次数'] - df_new['min']) * (df_new['max'] - df_new['打卡次数'])
df_new = df_new[df_new['多打卡'] == 0]
print(df_new.head(10))
# 漏打卡的最大值和最小值相同,相减得0
df_new['打卡次数差'] = df_new['max'] - df_new['min']
df_new['漏打卡'] = df_new['打卡次数差'].apply(lambda x: '是' if x == 0 else '否')
print(df_new.head(10))
# 漏打卡没法算迟到,补卡后再处理
'''
基于以上表格,给我算出每个人,每天的迟到,早退,加班时长。
具体逻辑如下:
迟到:时间大于8:35:01算迟到
早退:时间小于16:29:59算早退;周六11:30:00之前算早退.
加班时长:工作日:16:30:00后算加班,未达到17:30:00不算加班,17:30:00后满半小时都算加班;
周六:12:30:00以后算加班,周日:8:35:01后算加班
'''
df_new['星期'] = df_new['日期'].dt.strftime('%a')
print(df_new.head(10))


# 判断迟到/早退/加班。
def get_work_type(df_time, df_weekday, is_mark=True):
    start_time = pd.to_datetime('1900-01-01 8:35:01')
    work_type = None
    work_time = pd.Timedelta(0)
    if df_weekday == 'Sat':
        end_time = pd.to_datetime('1900-01-01 11:30:00')
        ot_start_time = pd.to_datetime('1900-01-01 12:30:00')
    elif df_weekday == 'Sun':
        end_time = pd.to_datetime('1900-01-01 8:35:01')
        ot_start_time = pd.to_datetime('1900-01-01 8:35:01')
    else:
        end_time = pd.to_datetime('1900-01-01 16:30:00')
        ot_start_time = pd.to_datetime('1900-01-01 18:00:00')
    # 打卡时间介乎下班和上班之间
    if start_time < df_time < end_time:
        # 打卡时间接近上班则算迟早,否则算早退
        if df_time - start_time <= end_time - df_time:
            work_type = '迟到'
            work_time = df_time - start_time
        else:
            work_type = '早退'
            work_time = end_time - df_time
    # 如果下班时间大于加班起始时间
    elif df_time > ot_start_time:
        work_type = '加班'
        work_time = df_time - ot_start_time
    # 根据传入值选择输出时间或者标签。
    if is_mark:
        return work_type
    else:
        return work_time


# 生成类型和时长
df_new['类型'] = df_new.apply(
    lambda x: get_work_type(x['时间'], x['星期']), axis=1)
df_new[df_new['类型'].notnull()]
df_new['时长'] = df_new.apply(
    lambda x: get_work_type(x['时间'], x['星期'], False), axis=1)
df_new[df_new['类型'].notnull()]
columns = ['工号', '姓名', '部门', '日期', '时间', '漏打卡', '类型', '时长']
print(df_new.columns)
df_result = df_new[columns]
df_result.to_excel(r'C:\考勤记录.xlsx', '处理结果', index=False)
----------------------------

发表评论 评论 (2 个评论)

回复 tmtony 2023-10-12 10:25
    你是博学多才啊
回复 roych 2023-10-12 21:28
tmtony:      你是博学多才啊
改天我丢一个调用阿里的通义千问模型来回答问题的脚本上来,岂不吓坏你?

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

QQ|站长邮箱|小黑屋|手机版|Office中国/Access中国 ( 粤ICP备10043721号-1 )  

GMT+8, 2024-4-27 20:39 , Processed in 0.067202 second(s), 18 queries .

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

返回顶部