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

如何在R中按条件保留最早打卡与最晚下班记录并去重?

解决考勤数据提取最早上班/最晚下班记录的R方案

嘿,这个需求在考勤数据处理里太常见了,我来给你一步步搞定它。首先得先把数据的格式理清楚,因为你用cbind创建的数据集会把所有列都转成字符类型,这会影响后续的时间比较逻辑。核心思路就是按员工唯一标识分组,然后提取每组里最早的上班打卡和最晚的下班打卡记录。

第一步:正确加载原始数据

先把你的数据重新整理下,避免cbind导致的全字符列问题:

id <- c(1,1,1,1,2,3,4)
name <- c("sam","sam","sam","sam","jack","john","jude")
sex <- c("M","M","M","M","M","M","F")
punch_in <- c("8/6/2015 8:00:00","8/6/2015 8:05:00","8/6/2015 8:00:00","8/6/2015 8:05:00","8/6/2015 8:06:00","8/6/2015 7:59:00","8/6/2015 8:00:00")
punch_out <- c("8/6/2015 16:00:00","8/6/2015 16:00:00","8/6/2015 16:05:00","8/6/2015 16:05:00","8/6/2015 16:00:00","8/6/2015 16:05:00","8/6/2015 16:05:00")
# 直接用data.frame创建,保留各列原始类型
data <- data.frame(id, name, sex, punch_in, punch_out, stringsAsFactors = FALSE)

第二步:转换时间列格式

原始的punch_in和punch_out是字符串,没法直接比较早晚,所以得转成时间类型。这里推荐两种方法,你可以选自己顺手的:

方法一:用dplyr包(代码更直观,推荐)

dplyr是数据处理的利器,先安装并加载它:

install.packages("dplyr")
library(dplyr)

然后链式处理数据:

clean_data <- data %>%
  # 把字符串转成POSIX时间格式,注意匹配你的日期格式"%m/%d/%Y %H:%M:%S"
  mutate(
    punch_in = as.POSIXct(punch_in, format = "%m/%d/%Y %H:%M:%S"),
    punch_out = as.POSIXct(punch_out, format = "%m/%d/%Y %H:%M:%S")
  ) %>%
  # 按员工的唯一标识分组(id、name、sex)
  group_by(id, name, sex) %>%
  # 提取每组最早的上班打卡和最晚的下班打卡
  summarise(
    punch_in = min(punch_in),  # 保留最早的上班记录
    punch_out = max(punch_out), # 保留最晚的下班记录
    .groups = "drop" # 处理完取消分组状态,避免后续操作报错
  )

运行后clean_data就是你要的结果,比如sam的记录就是8/6/2015 8:00:00上班,8/6/2015 16:05:00下班,完美符合你的需求。

方法二:用Base R(无需额外安装包)

如果你不想用第三方包,用Base R的aggregate函数也能实现:

# 先转换时间格式
data$punch_in <- as.POSIXct(data$punch_in, format = "%m/%d/%Y %H:%M:%S")
data$punch_out <- as.POSIXct(data$punch_out, format = "%m/%d/%Y %H:%M:%S")

# 分组聚合,对punch_in取min,punch_out取max
clean_data_base <- aggregate(
  cbind(punch_in, punch_out) ~ id + name + sex,
  data = data,
  FUN = function(x) {
    # 判断当前处理的是哪一列,应用对应的函数
    col_name <- deparse(substitute(x))
    if (col_name == "punch_in") min(x) else max(x)
  }
)

这样得到的结果和dplyr方法完全一致,只是写法稍微繁琐一点。

小提示

如果你想让结果列名更清晰,比如改成earliest_punch_in和latest_punch_out,只需要在summarise里修改列名就行:

clean_data <- data %>%
  mutate(
    punch_in = as.POSIXct(punch_in, format = "%m/%d/%Y %H:%M:%S"),
    punch_out = as.POSIXct(punch_out, format = "%m/%d/%Y %H:%M:%S")
  ) %>%
  group_by(id, name, sex) %>%
  summarise(
    earliest_punch_in = min(punch_in),
    latest_punch_out = max(punch_out),
    .groups = "drop"
  )

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:55:00