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

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)

问题原因分析

  1. 时区不匹配:Time列是UTC时区,而日照时间列(dawn、sunriseEnd等)是MST时区,直接比较会导致时间数值错位。比如第一行Time是UTC 2020-05-01 06:24,转换为MST是2020-05-01 00:24,远早于当天dawn的MST时间05:15:43,直接比较会完全不符合预期逻辑。
  2. 夜间逻辑错误: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 16:44:55