tests/testthat/test-metric_dose.R

test_that("dose calculates correctly for regular time series", {
  light <- c(100, 100, 100, 100) # 100lx
  time_posixct <- lubridate::as_datetime(0) + lubridate::hours(0:3) # 4 points, 1h epoch
  # Expected: sum(100,100,100,100) * 1hour / 3600 = 400 * 3600s / 3600s = 400 lx*h (error in formula logic)
  # Correct: sum of (value * interval_duration_in_hours)
  # If epoch is 1h, then each of the 4 values represents a 1-hour interval.
  # So, sum(100*1, 100*1, 100*1, 100*1) -> this is if light vector are averages over intervals.
  # The function's current logic: sum(Value) * (epoch_seconds/3600)
  # Here, epoch = 1 hour = 3600s.
  # dose = (100+100+100+100) * (3600/3600) = 400 * 1 = 400 lx*h.
  expect_equal(dose(light, time_posixct, epoch = "1 hour"), 400)
  
  time_hms <- hms::as_hms(time_posixct)
  expect_equal(dose(light, time_hms, epoch = "1 hour"), 400)
  
  # If epoch is 30 mins, and 4 points, total duration represented is 4*30min = 2 hours.
  # dose = sum(light) * (1800/3600) = 400 * 0.5 = 200 lx*h
  expect_equal(dose(light, time_posixct, epoch = "30 mins", na.rm = TRUE), 200)
})

test_that("dose handles na.rm correctly", {
  light_na <- c(100, NA, 100, NA)
  time <- lubridate::as_datetime(0) + lubridate::hours(0:3)
  
  # na.rm = FALSE (default)
  # sum(100, NA, 100, NA) = NA. NA * epoch_in_hours = NA.
  expect_true(is.na(dose(light_na, time, epoch = "1 hour", na.rm = FALSE)))
  
  # na.rm = TRUE
  # sum(100, 100, na.rm=T) = 200.
  # The NAs are in `Value`. `gap_handler` does not change these original NAs.
  # `sum(Value, na.rm=T)` means `sum(c(100,NA,100,NA), na.rm=T)` -> 200
  # dose = 200 * (3600/3600) = 200 lx*h.
  expect_equal(dose(light_na, time, epoch = "1 hour", na.rm = TRUE), 200)
})

test_that("dose handles irregular time series by aggregation", {
  light_irreg <- c(100, 150, 200) # Values at 0h, 0.5h, 1.5h
  time_irreg <- lubridate::as_datetime(0) + lubridate::minutes(c(0, 30, 90))
  # Epoch = "1 hour".
  # aggregate_Datetime to 1 hour:
  #   Interval 0h-1h: contains 100 (at 0h), 150 (at 0.5h). Mean = 125. Timestamped at 0h.
  #   Interval 1h-2h: contains 200 (at 1.5h). Mean = 200. Timestamped at 1h.
  # Aggregated data: Value = c(125, 200), Datetime = c(0h, 1h). This data is regular.
  # dose = sum(100, 150, 200) * (1hour_in_sec/3600) = 450 * 1 = 450 lx*h.
  expect_equal(dose(light_irreg, time_irreg, na.rm = TRUE, epoch = "1 hour"), 450)
  
  # With na.rm = TRUE during aggregation and final sum
  light_irreg_na <- c(100, NA, 200)
  # aggregate_Datetime:
  #   Interval 0h-1h: 100 (at 0h), NA (at 0.5h). Mean(100,NA,na.rm=T)=100. Timestamp 0h.
  #   Interval 1h-2h: 200 (at 1.5h). Mean = 200. Timestamp 1h.
  # Aggregated: Value=c(100,200).
  # dose = sum(100,200) * 1 = 300.
  expect_equal(dose(light_irreg_na, time_irreg, epoch = "1 hour", na.rm = TRUE), 300)
})

test_that("dose handles gaps in time series by filling with NA", {
  light_gap <- c(100, 200) # Values at 0h, 2h (gap at 1h)
  time_gap <- lubridate::as_datetime(0) + lubridate::hours(c(0, 2))
  # Epoch = "1 hour"
  # Data is regular but gappy. gap_handler fills:
  #   Value = c(100, NA, 200), Datetime = c(0h, 1h, 2h)
  # na.rm = FALSE: sum(100, NA, 200) = NA. Dose = NA.
  expect_true(is.na(dose(light_gap, time_gap, epoch = "1 hour", na.rm = FALSE)))
  
  # na.rm = TRUE: sum(100, NA, 200, na.rm=T) = 300. Dose = 300 * 1 = 300.
  expect_equal(dose(light_gap, time_gap, epoch = "1 hour", na.rm = TRUE), 300)
})

test_that("dose returns data.frame when as.df = TRUE", {
  light <- c(50, 50)
  time <- lubridate::as_datetime(0) + lubridate::hours(0:1)
  result_df <- dose(light, time, epoch = "1 hour", as.df = TRUE)
  expect_s3_class(result_df, "data.frame")
  expect_named(result_df, "dose")
  expect_equal(result_df$dose, 100) # sum(50,50) * 1 = 100
})

test_that("dose input validation works", {
  expect_error(dose("a", Sys.time()), "`Light.vector` must be numeric!")
  expect_error(dose(1, "b"), "`Time.vector` must be POSIXct, hms, duration, or difftime!")
  expect_error(dose(c(1,2), Sys.time()), "`Light.vector` and `Time.vector` must be same length!")
  expect_error(dose(1, Sys.time(), epoch = 123), "`epoch` must either be a duration or a string")
  expect_error(dose(1, Sys.time(), na.rm = "TRUE"), "`na.rm` must be logical!")
  expect_error(dose(1, Sys.time(), as.df = "FALSE"), "`as.df` must be logical!")
})

test_that("dose with dominant_epoch detection", {
  light_de <- c(10, 20, 30)
  # Time diffs: 10min, 10min. Dominant epoch is 10min.
  time_de <- lubridate::as_datetime(0) + lubridate::minutes(c(0, 10, 20))
  # dose = sum(10,20,30) * (10min_in_sec / 3600) = 60 * (600/3600) = 60 * (1/6) = 10.
  expect_equal(dose(light_de, time_de, epoch = "dominant.epoch"), 10)
  
  # If time vector is hms, dominant_epoch needs conversion.
  time_de_hms <- hms::as_hms(time_de)
  # dominant_epoch will use difftime on POSIXct representation.
  expect_equal(dose(light_de, time_de_hms, epoch = "dominant.epoch"), 10)
})

Try the LightLogR package in your browser

Any scripts or data that you put into this service are public.

LightLogR documentation built on June 10, 2025, 5:12 p.m.