Suncalc R包:如何基于日照时间创建分类变量?逻辑判断失效求助
问题:时间分类(ToD)赋值逻辑失效,结果大量NA且不准确
已加载的库
library(lubridate) library(dplyr) library(suncalc)
数据样例(前6行)
structure(list(Date.of.Capture = structure(c(18383, 18393, 18395, 18395, 18402, 18815), class = "Date"), Month = c(5L, 5L, 5L, 5L, 5L, 7L), Day = c(1L, 11L, 13L, 13L, 20L, 7L), Year = c(2020L, 2020L, 2020L, 2020L, 2020L, 2021L), Time.of.Capture = c("6:24", "6:27", "8:55", "8:55", "20:22", "6:26"), Time = structure(c(1588314240, 1589178420, 1589360100, 1589360100, 1590006120, 1625639160), class = c("POSIXct", "POSIXt"), tzone = "UTC"), ToD = c(NA, NA, NA, NA, "Daytime", NA), lat = c(40.75336, 40.75336, 40.75336, 40.75336, 40.75336, 40.75336), lon = c(-111.624088, -111.624088, -111.624088, -111.624088, -111.624088, -111.624088), sunriseEnd = structure(c(1588336111, 1589199430, 1589372111, 1589372111, 1589976545, 1625659614), class = c("POSIXct", "POSIXt"), tzone = "MST"), sunsetStart = structure(c(1588386056, 1589250673, 1589423593, 1589423593, 1590028795, 1625713092), class = c("POSIXct", "POSIXt"), tzone = "MST"), dawn = structure(c(1588334143, 1589197398, 1589370065, 1589370065, 1589974454, 1625657442), class = c("POSIXct", "POSIXt"), tzone = "MST"), dusk = structure(c(1588388024, 1589252706, 1589425639, 1589425639, 1590030886, 1625715264), class = c("POSIXct", "POSIXt"), tzone = "MST")), class = "data.frame", row.names = c(NA, 6L))
此前的处理代码
时间格式转换与日照时间获取
md$Time <- paste0(md$Year, "-", md$Month, "-", md$Day, " ", md$Time.of.Capture,":00") md$Time <- ymd_hms(md$Time) md$Date.of.Capture <- paste0(md$Year, "-", md$Month, "-", md$Day) md$Date.of.Capture <- as.Date(md$Date.of.Capture, format = "%Y-%m-%d", tz = "MST") timesofday <- getSunlightTimes(date = md$Date.of.Capture, lat = 40.753360, lon = -111.624088, tz="MST", keep=c("sunriseEnd", "sunsetStart", "dawn", "dusk")) md <- left_join(md, timesofday, by = c("Date.of.Capture" = "date"))
ToD赋值代码
md$ToD<-NA for(i in 1: nrow(md)){ if (md$Time[i] > md$dawn[i] & md$Time[i] < md$sunriseEnd[i]){ md$ToD[i] <- "Dawn" } else if (md$Time[i] > md$sunsetStart[i] & md$Time[i] < md$dusk[i]){ md$ToD[i] <- "Dusk" } else if (md$Time[i] > md$dusk[i] & md$Time[i] < md$dawn[i]){ md$ToD[i] <- "Nighttime" } else if (md$Time[i] > md$sunriseEnd[i] & md$Time[i] < md$sunsetStart[i]){ md$ToD[i] <- "Daytime" } } unique(md$ToD)
问题原因分析
- 时区不匹配:
Time列是UTC时区,而日照时间列(dawn、sunriseEnd等)是MST时区,直接比较会导致时间数值错位。比如第一行Time是UTC 2020-05-01 06:24,转换为MST是2020-05-01 00:24,远早于当天dawn的MST时间05:15:43,直接比较会完全不符合预期逻辑。 - 夜间逻辑错误:
md$Time[i] > md$dusk[i] & md$Time[i] < md$dawn[i]这个条件永远无法成立,因为同一天的dawn时间早于dusk,没有时间能同时满足大于dusk且小于dawn,夜间应该覆盖当天dusk到次日dawn的时间段,用逻辑或而非逻辑与。
解决方案
步骤1:统一时区
将Time列转换为MST时区,确保与日照时间列时区一致:
md$Time <- with_tz(md$Time, tzone = "MST")
步骤2:修正ToD赋值逻辑(改用向量式操作,避免循环)
使用dplyr::case_when处理更清晰,同时修正夜间判断逻辑,覆盖边界时间点:
md <- md %>% mutate( ToD = case_when( # 黎明:dawn到sunriseEnd之间(含边界) Time >= dawn & Time <= sunriseEnd ~ "Dawn", # 白天:sunriseEnd到sunsetStart之间(含边界) Time >= sunriseEnd & Time <= sunsetStart ~ "Daytime", # 黄昏:sunsetStart到dusk之间(含边界) Time >= sunsetStart & Time <= dusk ~ "Dusk", # 夜间:dusk之后 或者 dawn之前(涵盖跨天情况) Time > dusk | Time < dawn ~ "Nighttime", # 兜底处理异常情况 TRUE ~ NA_character_ ) )
验证样例结果
运行上述代码后,样例数据的ToD结果如下:
- 第1行:2020-05-01 00:24(早于dawn)→ Nighttime
- 第2行:2020-05-11 00:27(早于dawn)→ Nighttime
- 第3行:2020-05-13 02:55(早于dawn)→ Nighttime
- 第5行:2020-05-20 13:22(处于sunriseEnd和sunsetStart之间)→ Daytime
- 第6行:2021-07-07 00:26(早于dawn)→ Nighttime
该逻辑可正确覆盖所有时间区间,不会出现大量NA值。
内容的提问来源于stack exchange,提问作者BlakeLedbet
相关产品推荐
相关产品推荐

