Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
0ae3f99
updated funding sources
JZauner Oct 6, 2025
8c0c88d
Merge branch 'dev' of https://github.com/tscnlab/LightLogR into dev
JZauner Oct 6, 2025
24cae13
added MiEye
JZauner Oct 6, 2025
e6344ea
update to course series
JZauner Nov 12, 2025
80dd66f
update to ActLumus and MiEye import expression
JZauner Nov 12, 2025
b018f5a
Increment version number to 0.10.0
JZauner Nov 12, 2025
24e422b
changed argument order
JZauner Nov 12, 2025
596b0d8
changed argument order and behavior when only length is provided
JZauner Nov 12, 2025
46dd07c
new devices
JZauner Nov 12, 2025
2cea214
general updates
JZauner Nov 12, 2025
064fc51
rename gg_state to gg_states
JZauner Nov 24, 2025
57aacb7
updated citation
JZauner Nov 24, 2025
f0e0f97
Brown_check takes factors as well
JZauner Nov 24, 2025
e91fd26
added facetting argument to gg_heatmap
JZauner Nov 24, 2025
6fefbbd
updated navigation with additional resources
JZauner Nov 24, 2025
2a3a784
removed unnecessary argument
JZauner Nov 24, 2025
8db8eaf
adjusted y.axis.label default
JZauner Nov 24, 2025
9131023
adjusted standard x.axis.label
JZauner Nov 24, 2025
4e0902a
make add_states() work with intervals
JZauner Nov 24, 2025
2a56a57
update news for add_states()
JZauner Nov 24, 2025
5cab618
improved gg_states()
JZauner Nov 24, 2025
de9adbc
updates to gg_photoperiod
JZauner Nov 25, 2025
e25ebf4
warning about partial matching
JZauner Nov 26, 2025
c97bf65
bugfix in sleepint2Brown
JZauner Nov 26, 2025
6a3fe7f
allow for minimum duration in `remove_partial_data`
JZauner Nov 26, 2025
b8eec69
improved VEET import. closes #66
JZauner Nov 26, 2025
1d3245d
remove unnecessary package + small bug fix
JZauner Nov 26, 2025
d7b6457
added a version argument to import functions to facilitaty different …
JZauner Nov 26, 2025
ba464ee
restricted test_coverage calculation
JZauner Nov 26, 2025
18f0e05
Preserve numeric sample indices in sampling
JZauner Nov 26, 2025
9dce714
Merge branch 'dev' into codex/add-flexible-sampling-function-bblt8v
JZauner Nov 26, 2025
e47196f
Merge pull request #73 from tscnlab/codex/add-flexible-sampling-funct…
JZauner Nov 26, 2025
3e0c338
added sample_groups function
JZauner Nov 27, 2025
83a042b
adjusted import properties for VEET. closes #65
JZauner Nov 27, 2025
9bc458d
added the option for count days since start
JZauner Nov 27, 2025
66a328c
various bugfixes
JZauner Nov 27, 2025
f449330
moved plotly to suggests
JZauner Nov 27, 2025
43df2c9
added function style_time
JZauner Nov 27, 2025
37317cb
Add tests for style_time helper
JZauner Nov 27, 2025
7ae4025
Merge pull request #74 from tscnlab/codex/add-test-file-for-style_time
JZauner Nov 27, 2025
a577dc0
Add coordinate formatting helper
JZauner Nov 27, 2025
6bc157d
Merge pull request #75 from tscnlab/codex/create-function-for-coordin…
JZauner Nov 27, 2025
dfee871
added format_coordinates
JZauner Nov 27, 2025
62bdd4e
added summary_table
JZauner Nov 27, 2025
43a78b6
Rename summary helper functions
JZauner Nov 28, 2025
7dde752
Merge pull request #76 from tscnlab/codex/refactor-summary_table.r-in…
JZauner Nov 28, 2025
c9d375e
Fix summary table formatting rows
JZauner Nov 28, 2025
b15af95
Merge branch 'dev' into codex/refactor-summary_table.r-into-package-f…
JZauner Nov 28, 2025
ef876a8
Merge pull request #77 from tscnlab/codex/refactor-summary_table.r-in…
JZauner Nov 28, 2025
3f42cbf
add summary_table adjustments
JZauner Nov 28, 2025
d18d16c
Replace gtExtras histograms with ggplot
JZauner Nov 28, 2025
8eee747
Merge pull request #78 from tscnlab/codex/remove-reliance-on-ggextras…
JZauner Nov 28, 2025
ac7e479
adjusted summary table
JZauner Nov 28, 2025
1a642de
added circular daytime option
JZauner Nov 28, 2025
699c210
Use circular times and add Circular2Time helper
JZauner Nov 28, 2025
c800cdb
Merge pull request #79 from tscnlab/codex/refactor-datetime2time-usin…
JZauner Nov 28, 2025
633f6a1
adjustments to Circular2Time, Datetime2Time and upstream functions
JZauner Nov 28, 2025
76db0a7
minor adjustments
JZauner Nov 28, 2025
64d31fb
Use paste0 for summary strings
JZauner Nov 28, 2025
1946812
Merge pull request #81 from tscnlab/codex/remove-glue-dependency-from…
JZauner Nov 28, 2025
f948322
Fix histogram plotting for summary_table
JZauner Nov 28, 2025
c052271
Merge pull request #82 from tscnlab/codex/fix-summary_table-histogram…
JZauner Nov 28, 2025
c9346a4
small updates
JZauner Nov 28, 2025
5b880e0
minor adjustments
JZauner Nov 28, 2025
8c246f2
update to news
JZauner Nov 28, 2025
b73faf1
Merge branch 'main' into dev
JZauner Nov 28, 2025
4777228
adjustment to summary_table behavior
JZauner Nov 28, 2025
e724123
removed test coverage action
JZauner Nov 28, 2025
cc09066
minor updates
JZauner Nov 28, 2025
87c9477
cran comments update
JZauner Nov 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 0 additions & 62 deletions .github/workflows/test-coverage.yaml

This file was deleted.

16 changes: 9 additions & 7 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: LightLogR
Title: Process Data from Wearable Light Loggers and Optical Radiation Dosimeters
Version: 0.9.3
Version: 0.10.0
Authors@R: c(
person("Johannes", "Zauner",
email = "[email protected]", role = c("aut", "cre"),
Expand All @@ -12,6 +12,8 @@ Authors@R: c(
email = "[email protected]", role = "aut",
comment = c(ORCID = "0000-0002-2813-2668")),
person("European Partnership on Metrology", role = "fnd", comment = "The project (22NRM05 MeLiDos) has received funding from the European Partnership on Metrology, co-financed by the European Union's Horizon Europe Research and Innovation Programme, EURAMET, and the Participating States. Views and opinions expressed are those of the authors and do not necessarily reflect those of the European Union or EURAMET."),
person("Wellcome Trust", role = "fnd", comment = "LightLogR's development is supported by the Wellcome Trust (www.wellcome.org), 226787/2/22/Z."),
person("Reality Labs Research", role = "fnd", comment = "LightLogR's development is supported by the GLEE project (Global Light Exposure Engine, www.visualdiet.org) funded by Reality Labs Research."),
person("Translational Sensory and Circadian Neuroscience Unit (MPS/TUM/TUMCREATE)", comment = c(URL = "www.tscnlab.org"), role = "cph"))
Description: Import, processing, validation, and visualization of personal light exposure measurement data from wearable devices. The package implements features such as the import of data and metadata files, conversion of common file formats, validation of light logging data, verification of crucial metadata, calculation of common parameters, and semi-automated analysis and visualization.
License: MIT + file LICENSE
Expand All @@ -22,29 +24,28 @@ URL: https://github.com/tscnlab/LightLogR,
https://tscnlab.github.io/LightLogR/,
https://zenodo.org/doi/10.5281/zenodo.11562600
BugReports: https://github.com/tscnlab/LightLogR/issues
Imports:
Imports:
circular,
cowplot,
dplyr,
ggplot2,
ggsci,
ggtext,
gt,
gtExtras,
hms,
janitor,
lifecycle,
lubridate,
magrittr,
plotly,
purrr,
readr,
rlang,
scales,
slider,
stats,
stringr,
suntools,
tibble,
tidyr,
utils
tidyr
Depends:
R (>= 4.3)
LazyData: true
Expand All @@ -54,6 +55,7 @@ Suggests:
gghighlight,
gtsummary,
knitr,
plotly,
patchwork,
pkgload,
rmarkdown,
Expand Down
10 changes: 10 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export(Brown2reference)
export(Brown_check)
export(Brown_cut)
export(Brown_rec)
export(Circular2Time)
export(Datetime2Time)
export(Datetime_breaks)
export(Datetime_limits)
Expand Down Expand Up @@ -40,6 +41,7 @@ export(filter_Date)
export(filter_Datetime)
export(filter_Datetime_multiple)
export(filter_Time)
export(format_coordinates)
export(frequency_crossing_threshold)
export(gap_finder)
export(gap_handler)
Expand All @@ -53,6 +55,7 @@ export(gg_heatmap)
export(gg_overview)
export(gg_photoperiod)
export(gg_state)
export(gg_states)
export(has_gaps)
export(has_irregulars)
export(import)
Expand Down Expand Up @@ -81,16 +84,23 @@ export(photoperiod)
export(pulses_above_threshold)
export(remove_partial_data)
export(reverse2_trans)
export(sample_groups)
export(sc2interval)
export(sleep_int2Brown)
export(solar_noon)
export(spectral_integration)
export(spectral_reconstruction)
export(style_time)
export(summarise_numeric)
export(summarize_numeric)
export(summary_metrics)
export(summary_overview)
export(summary_table)
export(supported_devices)
export(supported_versions)
export(symlog_trans)
export(threshold_for_duration)
export(timing_above_threshold)
importFrom(lifecycle,deprecated)
importFrom(magrittr,"%>%")
importFrom(rlang,":=")
76 changes: 75 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,77 @@
# LightLogR 0.10.0 **High noon**

## General improvements and new functions

* `summary_table()`, `summary_overview()`, and `summary_metrics()` are a great set of **new functions** that give quick summaries across the whole dataset. Care has to be taken with these functions, as they are high level and will not perform well with a higly fragmented/irregular dataset.

* `sample_groups()` is a **new function** that makes it easy to reduce the number of groups either by random sampling, flexible ordering, or based on a condition.

* `style_time()` is a **new convenience function** that takes datetimes, times, or numeric input and outputs a clean time format (e.g., "03:45"). This is primarily used to style times in tables or plots.

* `format_coordinates()` is a **new convenience function** that takes a coordinates and formats them nicely for plots and tables.

* `Datetime2Time()` now allows for `circular` time through a sine-conversion of time of day - this is especially useful for averaging of times that (can) cross midnight, such as bed-times.

* `Circular2Time()` is a **new function** that back-converts `circular` time columns to `hms` time. See the package `{circular}` for details on how `circular` columns work.

* `remove_partial_data()` allows to specify a minimum duration of available data. Simply supply a negative duration to the `threshold.missing` argument. E.g., `"-20 hours"` will only keep groups with at least 20 hours of data. While this was easy before in case of groups of known total duration, 24 hours (e.g., simply set the threshold to 4 hours to get to 20 hours of data), it was not possible for groups of unknown total duration.

* `import` functions now support a `version` argument. If there are multiple known formats for one supported device, the version can be changed. As of now, this is the case with the `VEET` device, which changed its format slightly with `v2.1.17`. This argument is also used for the `Actiwatch Spectrum`, which requires several adjustments for a German locale (beyond simple adjustment of locale settings). #65

* `supported_versions()` is a **new function** that provides an overview which device versions are supported.

* `add_Date_col()` gained the `as.count` argument. If set to true, it will output the number of days since start. The basis are calendar days.

* `add_states()` now
- allows for `start` and `end` variables to be of class `Interval` - this makes the function ready to work with output from `sc2interal()` or `sleep_int2Brown()`.
- works as expected when `force.tz = TRUE`, i.e., the timestamp in the states dataset is forced to the timezone of the receiving time-series dataset. That is useful, e.g., when you know that the timestamp is correct, but was imported ad `UTC` by default.

* Added many new resources to the documentation webpage (accessible through the nav menu), including an interactive online course for `LightLogR`.

* `filter_Datetime()` & `filter_Date()`:
- When only `length` is provided, but not `start` nor `end`, the functions now respect grouping, i.e., the length will be taken from the first record (or last in the case of `from_start = FALSE`) within each group.
- the order of arguments now makes more sense in a typical use case, with `length` being the first argument taken, then `start` and `end`

* `gg_states()` now
- allows for an individual height of the state indicators through `ymin` and `ymax` arguments
- allows to calculate arbitrary metrics from the dataset to be used in the plot through the `extract.metrics` argument.
- combining the two above allows for powerful visualizations, where, e.g., the height of a status indicator is determined by a summary metric, like the median
- allows for a non-standard `Datetime` column through the `Datetime.colname` argument.

* `gg_photoperiod()` now
- allows for an individual height of the state indicators through `ymin` and `ymax` arguments
- allows for a non-standard `Datetime` column through the `Datetime.colname` argument.

## Bug fixes and small stuff

* The `plotly` package was moved to suggested dependency, as it only covers an edge case.

* The `janitor` package is no longer a dependency, as a simpler version to find duplicates during import was implemented.

* `VEET` devices import much faster now, thanks to an efficient way to construct the data table. Thanks to @ThomasKraft for raising this issue! #66

* `gg_states()` replaced the function `gg_state()` for more consistent naming with other `states` functions.

* `gg_day()` and `gg_days()` now have the `y.axis` variable and the `geom` as first two arguments, putting the most often used arguments to the front.

* New device import: `MiEye` from Circadian Health Innovations. There are two known datetime formats for the device: `ymd HMS`, and `dmy HMS`. Both are parsed.

* More flexible import for `ActLumus` devices: Data can start at any line. For computational efficiency, it will determine the correct starting row in the first file provided (`filenames[1]`) and use it for all files provided for import.

* `log_zero_inflated()` and `exp_zero_inflated()` have an updated reference.

* `Brown_check()` now also takes factor vectors for `state`. This affects the `Brown` upstream functions that use `Brown_check()`.

* `gg_heatmap()` has gained a `facetting` variable to remove facetting altogether. Default is `TRUE`.

* Standard `y.axis.label` of visualization functions is now `Melanopic EDI (lx)`. Affects `gg_day()`, `gg_days()`, and `gg_heatmap()`.

* `gg_day()`: standard `x.axis.label` is `Local time (HH:MM)`.

* `aggregate_Date()` and `aggregate_Datetime()` now contain a warning for `...` about partial matching of argument names.

* `sleep_int2Brown()` will sensibly fill in values for columns in the `evening`, should the state dataset contain more than `Interval` and `Sleep` columns.

# LightLogR 0.9.3

* added a Newsletter section to the package page
Expand Down Expand Up @@ -98,7 +172,7 @@ This is a huge update for `LightLogR`, bringing many new features and twenty-two

* `gg_gaps()`: visualize gaps and shows instances of irregular data.

* `gg_state()`: is an addon-function to `gg_day()` or `gg_days()`, which adds a state or cluster indicator to the plot
* `gg_states()`: is an addon-function to `gg_day()` or `gg_days()`, which adds a state or cluster indicator to the plot

* `gg_heatmap()`: visualize a condensed version of time series patterns, optionally as double plots.

Expand Down
6 changes: 3 additions & 3 deletions R/Brown.R
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ Brown2reference <- function(dataset,
#' @param value Illuminance value to check against the recommendation. needs to
#' be numeric, can be a vector.
#' @param state The state from Brown et al. (2022). Needs to be a character
#' vector with the same length as `value`.
#' vector (or factor vector) with the same length as `value`.
#' @param Brown.day,Brown.evening,Brown.night The names of the states from Brown
#' et al. (2022). These are the default values (`"day"`, `"evening"`,
#' `"night"`), but can be changed if the names in `state` are different. Needs
Expand Down Expand Up @@ -158,8 +158,8 @@ Brown_check <- function(value,

#check whether state has the same length as value, give an error if not
stopifnot(
"state needs to be a character vector with the same length as value" =
is.character(state) & length(state) == length(value)
"state needs to be a character or factor vector with the same length as value" =
(is.character(state) | is.factor(state)) & length(state) == length(value)
)

dplyr::case_when(
Expand Down
60 changes: 60 additions & 0 deletions R/Circular2Time.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#' Convert circular time columns to hms
#'
#' @param dataset A data.frame with `circular` columns representing time of day.
#' @param cols The column names to convert. Expects a `symbol`. The default will
#' convert all `circular` columns. If uncertain whether columns exist in the
#' dataset, use [dplyr::any_of()].
#' @param silent Logical on whether no message shall be shown if input and
#' output are identical. Defaults to `FALSE` (i.e., a message is shown).
#'
#' @returns The input dataset with converted circular columns as time (hms)
#' columns. With the default settings, if no circular column exists, input and
#' output will be identical.
#' @export
#'
#' @examples
#' times <- lubridate::as_datetime("2023-01-01 10:00:00") + lubridate::hours(0:2)
#' times
#' circular_times <- Datetime2Time(tibble::tibble(Timestamp = times), circular = TRUE)
#' circular_times
#' Circular2Time(circular_times)
#'
#' #if times are not circular, then an averaging can be problematic across midnight:
#' selected_times <-
#' sample.data.environment |>
#' sample_groups() |>
#' dplyr::slice(c(40:43, 51838:51840))
#' selected_times
#'
#' #a simple averaging will lead to a nonsensical value, e.g. if this should
#' #calculate average sleep timing: ~10:00 in the morning
#' selected_times |> summarize_numeric()
#'
#' #by converting it to a circular beforehand, averaging works as expected:
#' #~3 minutes after midnight
#' selected_times |>
#' summarize_numeric(Datetime2Time.circular = TRUE) |>
#' Circular2Time()

Circular2Time <- function(dataset,
cols = dplyr::where(circular::is.circular),
silent = FALSE) {
stopifnot("dataset needs to be a data.frame" = is.data.frame(dataset),
"silent needs to be a logical" = is.logical(silent))

data <- dataset |>
dplyr::mutate(
dplyr::across({{ cols }}, circular_to_hms)
)

if(identical(data, dataset) & !silent){
message("No columns were affected")
}
data
}

circular_to_hms <- function(x) {
radians <- as.numeric(circular::conversion.circular(x, units = "radians")) %% (2 * pi)
seconds <- radians / (2 * pi) * 24 * 3600
hms::hms(seconds)
}
41 changes: 32 additions & 9 deletions R/Datetime2Time.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
#' @param cols The column names to convert. Expects a `symbol`. The default will
#' convert all POSIXct columns. If uncertain whether columns exist in the
#' dataset, use [dplyr::any_of()].
#' @param circular Logical on whether the columns should be converted to a
#' circular time instead of time stamps. Uses the [circular::circular()]
#' class with a `clock24` template for a clean round-trip with
#' [Circular2Time()]. Default is `FALSE`.
#' @param silent Logical on whether no message shall be shown if input and
#' output are identical. Defaults to `FALSE` (i.e., a message is shown).
#'
Expand All @@ -26,19 +30,38 @@
#' sample.data.environment |>
#' Datetime2Time(dplyr::any_of("Datetime3"))

Datetime2Time <- function(dataset,
Datetime2Time <- function(dataset,
cols = dplyr::where(lubridate::is.POSIXct),
circular = FALSE,
silent = FALSE) {
stopifnot("dataset neets to be a data.frame" = is.data.frame(dataset))

data <-
dataset |>
dplyr::mutate(
dplyr::across({{ cols }}, hms::as_hms)
stopifnot("dataset needs to be a data.frame" = is.data.frame(dataset),
"circular needs to be a logical" = is.logical(circular),
"silent needs to be a logical" = is.logical(silent))

data <- if (!circular) {
dataset |>
dplyr::mutate(
dplyr::across({{ cols }}, hms::as_hms)
)
} else {
dataset |>
dplyr::mutate(
dplyr::across({{ cols }}, datetime_to_circular)
)

}

if(identical(data, dataset) & !silent){
message("No columns were affected")
}
data
}
}

datetime_to_circular <- function(x) {
seconds <- as.numeric(hms::as_hms(x))
circular::circular(
x = seconds / (24 * 3600) * (2 * pi),
units = "radians",
modulo = "2pi",
template = "clock24"
)
}
Loading
Loading