如何归一化HHMM格式循环时间戳数值数据并移除异常值?
处理循环时间数据:异常值移除与归一化方案
一、先把数值型HHMM转成标准循环时间格式
你的数据是数值型且缺失前导零,第一步需要将其转换为可识别的循环时间结构:
- 将数值转为字符串,补前导零至4位:
# 先简化列名方便操作 colnames(sleepDiary_1)[3:4] <- c("bed_time", "sleep_attempt_time") # 补前导零到4位,统一格式 sleepDiary_1$bed_time_str <- sprintf("%04d", sleepDiary_1$bed_time) sleepDiary_1$sleep_attempt_time_str <- sprintf("%04d", sleepDiary_1$sleep_attempt_time) - 拆分小时和分钟,转换为总分钟数(范围0-1439,1440分钟等价于0,符合循环特性):
# 提取小时、分钟并计算总分钟数 sleepDiary_1$bed_time_mins <- as.integer(substr(sleepDiary_1$bed_time_str, 1, 2))*60 + as.integer(substr(sleepDiary_1$bed_time_str, 3, 4)) sleepDiary_1$sleep_attempt_time_mins <- as.integer(substr(sleepDiary_1$sleep_attempt_time_str, 1, 2))*60 + as.integer(substr(sleepDiary_1$sleep_attempt_time_str, 3, 4))
二、异常值移除
睡眠时间的异常值需结合领域知识和循环数据特性处理:
- 领域知识筛选:如果研究对象是普通白班人群,就寝/入睡时间通常集中在18:00(1080分钟)至次日08:00(480分钟),可先剔除不在该区间的数据:
# 定义有效时间范围:18:00之后 或 08:00之前 valid_bed <- sleepDiary_1$bed_time_mins >= 1080 | sleepDiary_1$bed_time_mins <= 480 valid_sleep <- sleepDiary_1$sleep_attempt_time_mins >= 1080 | sleepDiary_1$sleep_attempt_time_mins <= 480 # 筛选有效数据 sleep_clean <- sleepDiary_1[valid_bed & valid_sleep, ] - 圆形统计法检测异常值:针对循环数据,用圆形均值和标准差判断偏离度,避免误判接近0/1440的点:
- 将分钟数转换为弧度(0到2π,对应1440分钟):
sleep_clean$bed_rad <- (sleep_clean$bed_time_mins / 1440) * 2 * pi sleep_clean$sleep_rad <- (sleep_clean$sleep_attempt_time_mins / 1440) * 2 * pi - 用
circular包计算圆形统计量并剔除异常值:library(circular) # 转换为circular对象 bed_circ <- circular(sleep_clean$bed_rad, type="angles", units="radians") # 计算圆形均值和标准差 bed_mean <- mean(bed_circ) bed_sd <- sd(bed_circ) # 计算每个点到均值的循环距离,剔除超过3倍标准差的点 bed_dist <- circular::distance(bed_circ, bed_mean) sleep_clean <- sleep_clean[bed_dist <= 3*bed_sd, ]
- 将分钟数转换为弧度(0到2π,对应1440分钟):
三、循环数据的归一化
普通的min-max、Z-score归一化不适合循环数据(会错误放大23:59和00:01的距离),推荐两种适配方案:
方案1:转换为正弦/余弦分量(最常用)
将时间映射为二维的正弦、余弦值,完整保留循环特性,适合多数后续分析:
# 基于总分钟数计算正弦、余弦值 sleep_clean$bed_sin <- sin(sleep_clean$bed_time_mins / 1440 * 2 * pi) sleep_clean$bed_cos <- cos(sleep_clean$bed_time_mins / 1440 * 2 * pi) sleep_clean$sleep_sin <- sin(sleep_clean$sleep_attempt_time_mins / 1440 * 2 * pi) sleep_clean$sleep_cos <- cos(sleep_clean$sleep_attempt_time_mins / 1440 * 2 * pi)
比如23:59和00:01的正弦/余弦值非常接近,能正确反映实际时间差。
方案2:循环归一化到[-0.5, 0.5]
如果需要一维归一化值,可通过循环映射实现:
# 将0-1440分钟映射到[-0.5, 0.5] sleep_clean$bed_norm <- ifelse(sleep_clean$bed_time_mins / 1440 > 0.5, (sleep_clean$bed_time_mins / 1440) - 1, sleep_clean$bed_time_mins / 1440) sleep_clean$sleep_norm <- ifelse(sleep_clean$sleep_attempt_time_mins / 1440 > 0.5, (sleep_clean$sleep_attempt_time_mins / 1440) - 1, sleep_clean$sleep_attempt_time_mins / 1440)
此时23:00(1380分钟)会转为-0.0417,01:00(60分钟)转为0.0417,两者的距离符合实际2小时差距。
内容的提问来源于stack exchange,提问作者driver
相关产品推荐
相关产品推荐

