如何调整R代码筛选每日最接近午夜的GPS点位
问题描述
我有一批包含日期时间信息的夜间(16:00-次日09:00)GPS点位数据,已将跨午夜的点位统一标记为首个夜间的NightDate,数据包含多个个体ID,且每晚有多条记录。需要按ID和NightDate分组,筛选出每组中最接近午夜的单条点位。
数据格式(含ID列)
| NightDate | timeofday | time | NightDateTime |
|---|---|---|---|
| 2019-03-22 | 19.47 | 19:28:00 | 22/03/2019 19:28:00 |
| 2019-03-22 | 21.88 | 21:53:00 | 22/03/2019 21:53:00 |
| 2019-03-22 | 2.77 | 02:46:00 | 22/03/2019 02:46:00 |
| 2019-03-23 | 20.53 | 20:32:00 | 23/03/2019 20:32:00 |
| 2019-03-23 | 23.07 | 23:04:00 | 23/03/2019 23:04:00 |
| 2019-03-23 | 1.67 | 01:40:00 | 23/03/2019 01:40:00 |
| 2019-03-23 | 4.28 | 04:17:00 | 23/03/2019 04:17:00 |
| 2019-03-24 | 19.57 | 19:34:00 | 24/03/2019 19:34:00 |
| 2019-03-24 | 23.37 | 23:22:00 | 24/03/2019 23:22:00 |
| 2019-03-24 | 2.05 | 02:03:00 | 24/03/2019 02:03:00 |
| 2019-03-24 | 4.8 | 04:48:00 | 24/03/2019 04:48:00 |
尝试的代码及问题
最初尝试用timeofday计算与0点的距离,但只保留了凌晨数据,忽略了23点左右接近午夜的记录:
Night <- read.csv("night_redate.csv") Night.filter <- Night %>% group_by(ID, NightDate) %>% slice(which.min(abs(timeofday - 0))) write.csv(Night.filter, "filtered_night.csv", row.names = FALSE)
也曾尝试用标准日期时间处理,但筛选逻辑有误:
# Remove rows with NA in 'NightDate' column and group by ID and NightDateTime Night.edit <- Night %>% filter(!is.na(DateTime)) %>% group_by(ID, NightDateTime) %>% mutate( # Convert NightDateTime to Date night_date = as.Date(NightDateTime), # Create Midnight Time midnight_time = as.POSIXct(paste(night_date, "00:00:00")), # Calculate Time Difference time_diff = difftime(midnight_time, NightDateTime), # Take Absolute Value time_diff_absolute = abs(as.numeric(time_diff)) ) %>% # Filter for the observation closest to midnight filter(time_diff_absolute == min(time_diff_absolute))
需要调整代码,让23:00和01:00这类时间都被纳入接近午夜的考量范围。
解决方案
方法1:修正timeofday的距离计算逻辑
问题出在直接用abs(timeofday - 0)计算距离,23点左右的timeofday值(如23.07)与0的距离是23.07,但实际上它到午夜的距离只有约0.93小时。正确的计算应该是:对于timeofday >= 16的时段(夜间前半段),计算到24点的距离;对于timeofday < 9的时段(夜间后半段,次日凌晨),计算到0点的距离,然后取两者中的最小值对应的记录。
修正后的代码:
library(dplyr) Night <- read.csv("night_redate.csv") Night.filter <- Night %>% group_by(ID, NightDate) %>% mutate( # 计算到午夜的小时数距离 midnight_dist = case_when( timeofday >= 16 ~ 24 - timeofday, # 23:04到24:00的距离是0.93小时 timeofday < 9 ~ timeofday # 02:46到00:00的距离是2.77小时 ) ) %>% slice(which.min(midnight_dist)) %>% select(-midnight_dist) # 移除临时计算列 write.csv(Night.filter, "filtered_night.csv", row.names = FALSE)
方法2:使用标准日期时间计算(推荐)
利用NightDateTime转换成POSIXct格式,结合NightDate确定对应的午夜时间(即NightDate的次日0点),然后计算每条记录到该午夜的时间差绝对值,取最小值对应的记录。
正确的代码:
library(dplyr) library(lubridate) Night <- read.csv("night_redate.csv") # 先转换NightDateTime为标准日期时间格式,注意匹配你的日期格式 Night <- Night %>% mutate(NightDateTime = dmy_hms(NightDateTime)) # 根据你的数据格式调整,这里是日/月/年 时分秒 Night.filter <- Night %>% group_by(ID, NightDate) %>% mutate( # 确定当前NightDate对应的午夜时间:NightDate的次日0点 midnight_time = as.POSIXct(paste(as.Date(NightDate) + 1, "00:00:00")), # 计算到午夜的时间差绝对值(单位:秒) time_diff_abs = abs(as.numeric(difftime(midnight_time, NightDateTime, units = "secs"))) ) %>% slice(which.min(time_diff_abs)) %>% select(-midnight_time, -time_diff_abs) write.csv(Night.filter, "filtered_night.csv", row.names = FALSE)
说明:
- 用
lubridate的dmy_hms函数可以更便捷地转换日期时间格式,避免手动拼接出错。 - 因为
NightDate是跨午夜数据的首个夜间日期,对应的午夜是NightDate + 1的0点(比如NightDate为2019-03-22,对应的午夜是2019-03-23 00:00:00),这样不管是22日23点还是23日凌晨的记录,计算到这个午夜的时间差都是正确的。
内容的提问来源于stack exchange,提问作者CjC
相关产品推荐
相关产品推荐

