Skip to content

Commit b05f38a

Browse files
authored
Merge branch 'main' into dev
2 parents feb1f84 + dfa68b3 commit b05f38a

12 files changed

+154
-21
lines changed

LightLogR.Rproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
Version: 1.0
2-
ProjectId: 6b298e31-e359-4202-b3a9-ee65538e1c99
32

43
RestoreWorkspace: Default
54
SaveWorkspace: Default

R/metric_interdaily_stability.R

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
#'
1010
#' @param Light.vector Numeric vector containing the light data.
1111
#' @param Datetime.vector Vector containing the time data. Must be POSIXct.
12+
#' @param use.samplevar Logical. Should the sample variance be used (divide by N-1)?
13+
#' By default (`FALSE`), the population variance (divide by N) is used, as described
14+
#' in Van Someren et al. (1999).
1215
#' @param na.rm Logical. Should missing values be removed? Defaults to `FALSE`.
1316
#' @param as.df Logical. Should the output be returned as a data frame? If `TRUE`, a data
1417
#' frame with a single column named `interdaily_stability` will be returned.
@@ -31,8 +34,7 @@
3134
#' \doi{10.1177/14771535231170500}
3235
#'
3336
#' @examples
34-
#'
35-
#' set.seed(1)
37+
#'set.seed(1)
3638
#' N <- 24 * 7
3739
#' # Calculate metric for seven 24 h days with two measurements per hour
3840
#' dataset1 <-
@@ -45,17 +47,19 @@
4547
#' dplyr::summarise(
4648
#' "Interdaily stability" = interdaily_stability(MEDI, Datetime)
4749
#' )
48-
#'
4950
interdaily_stability <- function(Light.vector,
5051
Datetime.vector,
52+
use.samplevar = FALSE,
5153
na.rm = FALSE,
52-
as.df = FALSE) {
54+
as.df = FALSE
55+
) {
5356
# Initial checks
5457
stopifnot(
5558
"`Light.vector` must be numeric!" = is.numeric(Light.vector),
5659
"`Datetime.vector` must be POSIXct!" = lubridate::is.POSIXct(Datetime.vector),
5760
"`Light.vector` and `Datetime.vector` must be same length!" =
5861
length(Light.vector) == length(Datetime.vector),
62+
"`use.samplevar` must be logical!" = is.logical(use.samplevar),
5963
"`na.rm` must be logical!" = is.logical(na.rm),
6064
"`as.df` must be logical!" = is.logical(as.df)
6165
)
@@ -114,19 +118,34 @@ interdaily_stability <- function(Light.vector,
114118
if (na.rm) {
115119
df <- df %>% tidyr::drop_na(Light)
116120
}
121+
122+
# Subtract 1 if `use.samplevar == TRUE`
123+
c = as.numeric(use.samplevar)
117124

118125
# Hourly averages for each day
119-
total_hourly <- df %>%
126+
hourly_data <- df %>%
120127
dplyr::group_by(Datetime = lubridate::floor_date(Datetime, unit = "1 hour")) %>%
121128
dplyr::summarise(Light = mean(Light))
129+
130+
# N hourly data
131+
n <- length(hourly_data$Light)
132+
133+
# Overall variance
134+
var_total <- sum((hourly_data$Light-mean(hourly_data$Light))^2) / (n-c)
122135

123136
# Hourly average across all days
124-
avg_hourly <- total_hourly %>%
137+
avg_hourly <- hourly_data %>%
125138
dplyr::group_by(Hour = lubridate::hour(Datetime)) %>%
126139
dplyr::summarise(Light = mean(Light))
140+
141+
# N per day
142+
p <- length(avg_hourly$Light)
143+
144+
# Variance across average day
145+
var_avg_day <- sum((avg_hourly$Light-mean(hourly_data$Light))^2) / (p-c)
127146

128147
# Variance across average day / variance across all days
129-
is <- stats::var(avg_hourly$Light) / stats::var(total_hourly$Light)
148+
is <- var_avg_day / var_total
130149

131150
# Return data frame or numeric vector
132151
if (as.df) {

R/metric_intradaily_variability.R

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
#'
88
#' @param Light.vector Numeric vector containing the light data.
99
#' @param Datetime.vector Vector containing the time data. Must be POSIXct.
10+
#' @param use.samplevar Logical. Should the sample variance be used (divide by N-1)?
11+
#' By default (`FALSE`), the population variance (divide by N) is used, as described
12+
#' in Van Someren et al. (1999).
1013
#' @param na.rm Logical. Should missing values be removed? Defaults to `FALSE`.
1114
#' @param as.df Logical. Should the output be returned as a data frame? If `TRUE`, a data
1215
#' frame with a single column named `intradaily_variability` will be returned.
@@ -46,6 +49,7 @@
4649
#'
4750
intradaily_variability <- function(Light.vector,
4851
Datetime.vector,
52+
use.samplevar = FALSE,
4953
na.rm = FALSE,
5054
as.df = FALSE) {
5155
# Initial checks
@@ -54,6 +58,7 @@ intradaily_variability <- function(Light.vector,
5458
"`Datetime.vector` must be POSIXct!" = lubridate::is.POSIXct(Datetime.vector),
5559
"`Light.vector` and `Datetime.vector` must be same length!" =
5660
length(Light.vector) == length(Datetime.vector),
61+
"`use.samplevar` must be logical!" = is.logical(use.samplevar),
5762
"`na.rm` must be logical!" = is.logical(na.rm),
5863
"`as.df` must be logical!" = is.logical(as.df)
5964
)
@@ -112,18 +117,26 @@ intradaily_variability <- function(Light.vector,
112117
if (na.rm) {
113118
df <- df %>% tidyr::drop_na(Light)
114119
}
120+
121+
# Subtract 1 if `use.samplevar == TRUE`
122+
c = as.numeric(use.samplevar)
115123

116124
# Hourly averages for each day
117-
total_hourly <- df %>%
125+
hourly_data <- df %>%
118126
dplyr::group_by(lubridate::floor_date(Datetime, unit = "1 hour")) %>%
119127
dplyr::summarise(Light = mean(Light))
128+
129+
# N hourly data
130+
n <- length(hourly_data$Light)
120131

121-
# Variance of consecutive hourly differences
122-
var_hourly_diff <-
123-
sum(diff(total_hourly$Light)^2) / (length(total_hourly$Light) - 1)
132+
# Overall variance
133+
var_total <- sum((hourly_data$Light-mean(hourly_data$Light))^2) / (n-c)
134+
135+
# Sum of squares of consecutive hourly differences
136+
var_hourly_diff <- sum(diff(hourly_data$Light)^2) / (n-1)
124137

125-
# Variance of consecutive differences / variance across all days
126-
iv <- var_hourly_diff / stats::var(total_hourly$Light)
138+
# Sum of squares of consecutive differences / variance across all days
139+
iv <- var_hourly_diff / var_total
127140

128141
# Return data frame or numeric vector
129142
if (as.df) {

man/import_Dataset.Rd

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/interdaily_stability.Rd

Lines changed: 5 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/intradaily_variability.Rd

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/helper.R

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
trunc_decimal = function(x, accuracy){base::trunc(x * 10^accuracy) / 10^accuracy}

tests/testthat/test-metric_duration_above_threshold.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ test_that("Calculation works", {
1212
duration_above_threshold(MEDI, datetime, "below", 100), lubridate::dminutes(7)
1313
)
1414
expect_equal(
15-
duration_above_threshold(MEDI, datetime, threshold = c(0,99)), lubridate::dminutes(4)
15+
duration_above_threshold(MEDI, datetime, threshold = c(0,100)), lubridate::dminutes(5)
1616
)
1717
})
1818

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
test_that("Population variance works", {
2+
MEDI = sample.data.environment$MEDI
3+
Datetime = sample.data.environment$Datetime
4+
expect_equal(trunc_decimal(interdaily_stability(MEDI, Datetime),2), 0.77)
5+
})
6+
7+
test_that("Sample variance works", {
8+
MEDI = sample.data.environment$MEDI
9+
Datetime = sample.data.environment$Datetime
10+
expect_equal(trunc_decimal(interdaily_stability(MEDI, Datetime, use.samplevar = TRUE),2), 0.79)
11+
})
12+
13+
test_that("IS for Gaussian noise near 2", {
14+
N = 24*1000
15+
MEDI = rnorm(N)
16+
Datetime = lubridate::as_datetime(lubridate::dhours(0:(N-1)), tz = "UTC")
17+
expect_equal(round(interdaily_stability(MEDI, Datetime),1), 0)
18+
})
19+
20+
test_that("IS equals 1 for perfect IS", {
21+
MEDI = rep(c(rep(1, 6), rep(250, 4), rep(500, 5), rep(750, 4), rep(1, 5)),7)
22+
Datetime = lubridate::as_datetime(lubridate::dhours(0:(24*7-1)), tz = "UTC")
23+
expect_equal(interdaily_stability(MEDI, Datetime), 1)
24+
})
25+
26+
test_that("Handling missing values works", {
27+
MEDI = rep(c(as.numeric(NA), rep(1, 5), rep(250, 4), rep(500, 5), rep(750, 4), rep(1, 5)),7)
28+
Datetime = lubridate::as_datetime(lubridate::dhours(0:(24*7-1)), tz = "UTC")
29+
expect_equal(suppressWarnings({interdaily_stability(MEDI, Datetime)}), as.numeric(NA))
30+
expect_equal(suppressWarnings({interdaily_stability(MEDI, Datetime, na.rm=TRUE)}), 1)
31+
})
32+
33+
test_that("Return data frame works", {
34+
MEDI = sample.data.environment$MEDI
35+
Datetime = sample.data.environment$Datetime
36+
expect_equal(
37+
round(interdaily_stability(MEDI, Datetime, as.df = TRUE), 1),
38+
tibble::tibble("interdaily_stability" = 0.8)
39+
)
40+
})
41+
42+
43+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
test_that("Population variance works", {
2+
MEDI = sample.data.environment$MEDI
3+
Datetime = sample.data.environment$Datetime
4+
expect_equal(trunc_decimal(intradaily_variability(MEDI, Datetime),3), 0.166)
5+
})
6+
7+
test_that("Sample variance works", {
8+
MEDI = sample.data.environment$MEDI
9+
Datetime = sample.data.environment$Datetime
10+
expect_equal(trunc_decimal(intradaily_variability(MEDI, Datetime, use.samplevar = TRUE),3), 0.165)
11+
})
12+
13+
test_that("IV for Gaussian noise near 2", {
14+
N = 24*1000
15+
MEDI = rnorm(N)
16+
Datetime = lubridate::as_datetime(lubridate::dhours(0:(N-1)), tz = "UTC")
17+
expect_equal(round(intradaily_variability(MEDI, Datetime)), 2)
18+
})
19+
20+
test_that("IV for Sine wave near 0", {
21+
MEDI = sin(seq(0,7*pi,length.out = 24*7))
22+
Datetime = lubridate::as_datetime(lubridate::dhours(0:(24*7-1)), tz = "UTC")
23+
expect_equal(round(intradaily_variability(MEDI, Datetime),1), 0)
24+
})
25+
26+
test_that("Handling missing values works", {
27+
MEDI = rep(c(as.numeric(NA), rep(1, 5), rep(250, 4), rep(500, 5), rep(750, 4), rep(1, 5)),7)
28+
Datetime = lubridate::as_datetime(lubridate::dhours(0:(24*7-1)), tz = "UTC")
29+
expect_equal(suppressWarnings({intradaily_variability(MEDI, Datetime)}),as.numeric(NA))
30+
expect_equal(suppressWarnings({round(intradaily_variability(MEDI, Datetime, na.rm=TRUE),1)}), 0.4)
31+
})
32+
33+
test_that("Return data frame works", {
34+
MEDI = sample.data.environment$MEDI
35+
Datetime = sample.data.environment$Datetime
36+
expect_equal(
37+
round(intradaily_variability(MEDI, Datetime, as.df = TRUE), 2),
38+
tibble::tibble("intradaily_variability" = 0.17)
39+
)
40+
})

tests/testthat/test-metric_period_above_threshold.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ test_that("Calculation works", {
99
period_above_threshold(MEDI, datetime, "below", 10), lubridate::dminutes(5)
1010
)
1111
expect_equal(
12-
period_above_threshold(MEDI, datetime, threshold = c(12,13)), lubridate::dminutes(1)
12+
period_above_threshold(MEDI, datetime, threshold = c(11,13)), lubridate::dminutes(2)
1313
)
1414
expect_equal(
1515
period_above_threshold(MEDI, datetime, threshold = 10, loop = TRUE), lubridate::dminutes(7)

tests/testthat/test-metric_timing_above_threshold.R

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
test_that("Calculation works", {
1+
test_that("Above threshold works", {
22
MEDI = c(rep(1, 6), rep(250, 4), rep(500, 5), rep(750, 4), rep(1, 5))
33
Datetime = lubridate::as_datetime(lubridate::dhours(0:23), tz = "UTC")
44
expect_equal(
@@ -9,6 +9,11 @@ test_that("Calculation works", {
99
"last" = lubridate::as_datetime(lubridate::dhours(18), tz = "UTC")
1010
)
1111
)
12+
})
13+
14+
test_that("Below threshold works", {
15+
MEDI = c(rep(1, 6), rep(250, 4), rep(500, 5), rep(750, 4), rep(1, 5))
16+
Datetime = lubridate::as_datetime(lubridate::dhours(0:23), tz = "UTC")
1217
expect_equal(
1318
timing_above_threshold(MEDI, Datetime, comparison = "below", threshold = 250),
1419
list(
@@ -17,6 +22,11 @@ test_that("Calculation works", {
1722
"last" = lubridate::as_datetime(lubridate::dhours(23), tz = "UTC")
1823
)
1924
)
25+
})
26+
27+
test_that("Between thresholds works", {
28+
MEDI = c(rep(1, 6), rep(250, 4), rep(400, 5), rep(750, 4), rep(1, 5))
29+
Datetime = lubridate::as_datetime(lubridate::dhours(0:23), tz = "UTC")
2030
expect_equal(
2131
timing_above_threshold(MEDI, Datetime, threshold = c(250, 500)),
2232
list(

0 commit comments

Comments
 (0)