activerse is a lightweight helper package that attaches actibase, actiplot, actiread, actimetrics, actisensorlog, actisomni, actiwalkability, and actiquantiles together.
It is designed to behave a bit like tidyverse: load one package, then use the functions from the actigraphy packages directly.
Packages
| Package | Repository | Version / status |
|---|---|---|
actibase |
jhuwit/actibase |
|
actiplot |
jhuwit/actiplot |
|
actiread |
jhuwit/actiread |
|
actimetrics |
jhuwit/actimetrics |
|
actisensorlog |
jhuwit/actisensorlog |
|
actisomni |
jhuwit/actisomni |
|
actiwalkability |
jhuwit/actiwalkability |
|
actiquantiles |
jhuwit/actiquantiles |
|
Related packages
The following packages are part of the activerse ecosystem but are not attached by activerse.
| Package | Repository | Version / status |
|---|---|---|
actinet |
jhuwit/actinet |
|
asleep |
jhuwit/asleep |
|
mapnhanespa |
jhuwit/mapnhanespa |
|
sleeper |
jhuwit/sleeper |
|
stepcount |
jhuwit/stepcount |
|
walking |
jhuwit/walking |
|
activerse Norms and Expectations
One of the goals of the activerse is standardization and modularization of code. We aim for some norms and expectation for integration of new packages into the activerse ecosystem. These are not hard rules, but rather guidelines for consistency and maintainability.
- When possible, user-exposed (e.g. in
exportsin NAMESPACE) functions should have the prefixacti_. - Optional arguments are initialized in the function using
NULLand/or use amatch.argcase when options are presented. - Functions should use
actibasehelpers where they can, especiallyget_sample_rateandget_dynamic_range. In cases wheresample_ratecan be passed as well as data,sample_ratewill be initialized asNULLorget_sample_rate(data). - Python dependencies are in
R/zzz.Rand wrapped usingreticulate::py_requirein the.onLoadfunction. The.onLoadshould call an exportedpy_require_PACKAGEwherePACKAGEis usually the package or python module. - For functions that call python modules, it is encouraged for a
callr::rwrapper to be included (this cause acallrdependency, so it can be Suggests. These wrappers should have the prefixpy_acti, for example seeactimetrics::py_acti_calculate_stepcount, which callsactimetrics::acti_calculate_stepcount.
- Packages have vignettes where possible. They can use files from
activersepackages usingsystem.fileif they want to use the data.
- Individual packages can be depended on with their own system, but functions should be wrapped using the above norms into an
activersepackage to be “included”. - Use
Roxygen: list(markdown = TRUE)inDESCRIPTION, use theusethispackage to write your package anddevtools::release()to submit to CRAN.
Usage
Load the packages
library(activerse)
# activerse-load:start
search()[grepl("^package:(actibase|actiplot|actiread|actimetrics|actisensorlog|actisomni|actiwalkability|actiquantiles)$", search())]
#> [1] "package:actiquantiles" "package:actiwalkability"
#> [3] "package:actisomni" "package:actisensorlog"
#> [5] "package:actimetrics" "package:actiread"
#> [7] "package:actibase"
# activerse-load:endExample data
The packages ship with small example datasets that are useful for quick tests.
data(acti_raw_data, package = "actibase")
data(acti_count_data, package = "actimetrics")
head(acti_raw_data)
#> # A tibble: 6 × 4
#> time X Y Z
#> <dttm> <dbl> <dbl> <dbl>
#> 1 2019-09-17 18:40:00 0 0.008 0.996
#> 2 2019-09-17 18:40:00 0.016 0 1.01
#> 3 2019-09-17 18:40:00 0.02 -0.008 1.00
#> 4 2019-09-17 18:40:00 0.016 -0.012 1.01
#> 5 2019-09-17 18:40:00 0.016 -0.008 1.01
#> 6 2019-09-17 18:40:00 0.008 -0.008 1.01
head(acti_count_data)
#> # A tibble: 6 × 5
#> time axis1 axis2 axis3 counts
#> <dttm> <dbl> <dbl> <dbl> <dbl>
#> 1 2019-09-17 18:40:00 5435 9659 8253 13818
#> 2 2019-09-17 18:41:00 9125 9197 4131 13598
#> 3 2019-09-17 18:42:00 4404 4367 3494 7119
#> 4 2019-09-17 18:43:00 3267 3170 2543 5214
#> 5 2019-09-17 18:44:00 1405 896 894 1891
#> 6 2019-09-17 18:45:00 0 0 0 0Use actimetrics
Once activerse is attached, functions from the activerse packages are available directly. You can also call them with the relevant pkg:: prefix if you prefer being explicit.
Calculate ENMO
enmo <- acti_calculate_enmo(data = acti_raw_data)
head(enmo)
#> # A tibble: 6 × 2
#> HEADER_TIME_STAMP ENMO_t
#> <dttm> <dbl>
#> 1 2019-09-17 18:40:00 0.688
#> 2 2019-09-17 18:41:00 0.708
#> 3 2019-09-17 18:42:00 0.183
#> 4 2019-09-17 18:43:00 0.150
#> 5 2019-09-17 18:44:00 0.0278
#> 6 2019-09-17 18:45:00 0.0139Calculate activity index
activity_index <- acti_calculate_activity_index(
acti_raw_data,
unit = "1 min",
ensure_all_time = TRUE,
verbose = FALSE
)
head(activity_index)
#> # A tibble: 6 × 2
#> HEADER_TIME_STAMP AI
#> <dttm> <dbl>
#> 1 2019-09-17 18:40:00 23.2
#> 2 2019-09-17 18:41:00 22.9
#> 3 2019-09-17 18:42:00 11.9
#> 4 2019-09-17 18:43:00 10.5
#> 5 2019-09-17 18:44:00 2.35
#> 6 2019-09-17 18:45:00 0Calculate MAD
mad <- acti_calculate_mad(data = acti_raw_data)
head(mad)
#> # A tibble: 6 × 8
#> HEADER_TIME_STAMP SD SD_t AI_DEFINED MAD MEDAD mean_r ENMO_t
#> <dttm> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 2019-09-17 18:40:00 1.87 1.85 1.33 1.09 0.630 1.65 0.688
#> 2 2019-09-17 18:41:00 1.54 1.53 1.08 0.853 0.552 1.69 0.708
#> 3 2019-09-17 18:42:00 0.253 0.204 0.278 0.207 0.188 1.14 0.183
#> 4 2019-09-17 18:43:00 0.228 0.175 0.228 0.191 0.178 1.10 0.150
#> 5 2019-09-17 18:44:00 0.0815 0.0637 0.309 0.0300 0.00330 1.02 0.0278
#> 6 2019-09-17 18:45:00 0 0 0 0 0 1.01 0.0139More examples
actimetrics also includes functions for wear/non-wear detection, step count estimation, and other processing helpers:
acti_process(acti_raw_data)
acti_calculate_wear(acti_raw_data)
acti_calculate_nonwear(acti_raw_data)
acti_calculate_stepcount(acti_raw_data)Running Walking with Different Python Environments
If you want to use both walking estimation from forest and stepcount, you can run them in separate R processes using callr to avoid Python package conflicts. Here’s how you can do it:
library(callr)
library(activerse)
gt3x_path = acti_example_gt3x()
data <- acti_read_gt3x(gt3x_path, cleanup = TRUE)
stepcount_callr = function(data,
...) {
reticulate::py_require("stepcount==3.11.0", python_version = "3.10")
sc <- reticulate::import("stepcount")
stepcount::stepcount_check()
res = actimetrics::acti_calculate_stepcount(data, ...)
return(res)
}
# 2. Run the isolated background R process
result <- callr::r(
func = stepcount_callr,
show = TRUE,
args = list(data = data) # Safely injects data into the process
)
#> Loading model...
#> Downloading https://wearables-files.ndph.ox.ac.uk/files/models/stepcount/ssl-20230208.joblib.lzma...
#> Checking Data
#> Writing file to CSV...
#> Reading in Data for Stepcount
#> Predicting from Model
#> Running step counter...
#> Gravity calibration...Gravity calibration... Done! (0.04s)
#> Nonwear detection...Nonwear detection... Done! (0.05s)
#> Resampling...Resampling... Done! (0.05s)
#> Defining windows...
#> 0%| | 0/241 [00:00<?, ?it/s]100%|██████████| 241/241 [00:00<00:00, 2300.90it/s]
#> Using local /Users/johnmuschelli/Library/Caches/org.R-project.R/R/reticulate/uv/cache/archive-v0/12AzDesQwp07_KC6/lib/python3.10/site-packages/stepcount/torch_hub_cache/OxWearables_ssl-wearables_v1.0.0
#> Classifying windows...
#> 0%| | 0/1 [00:00<?, ?it/s]100%|██████████| 1/1 [00:02<00:00, 2.04s/it]
#> Processing Result
#> /usr/local/Cellar/python@3.10/3.10.20_1/Frameworks/Python.framework/Versions/3.10/lib/python3.10/multiprocessing/resource_tracker.py:224: UserWarning: resource_tracker: There appear to be 1 leaked semaphore objects to clean up at shutdown
#> warnings.warn('resource_tracker: There appear to be %d '
head(result)
#> # A tibble: 6 × 3
#> time steps walking
#> <dttm> <dbl> <lgl>
#> 1 2019-09-17 18:40:00 34 TRUE
#> 2 2019-09-17 18:41:00 82 TRUE
#> 3 2019-09-17 18:42:00 104 TRUE
#> 4 2019-09-17 18:43:00 105 TRUE
#> 5 2019-09-17 18:44:00 15 TRUE
#> 6 2019-09-17 18:45:00 0 FALSE
forest_callr = function(data,
...) {
reticulate::py_require(
"git+https://github.com/onnela-lab/forest@45fb41038bd46c25d9e6a4442aa74fa03b501317",
python_version = "3.11")
fr = reticulate::import("forest")
oak = fr$oak$base
oak
res = actimetrics::acti_calculate_forest(data, ...)
return(res)
}
# 2. Run the isolated background R process
fresult <- callr::r(
func = forest_callr,
show = TRUE,
args = list(data = data) # Safely injects data into the process
)
#> Preprocessing Bout
#> Bout is Preprocessed
#> OAK: Find walking is done
head(fresult)
#> # A tibble: 6 × 2
#> time steps
#> <dttm> <dbl>
#> 1 2019-09-17 18:40:00 12.3
#> 2 2019-09-17 18:41:00 53.9
#> 3 2019-09-17 18:42:00 105.
#> 4 2019-09-17 18:43:00 105.
#> 5 2019-09-17 18:44:00 6.75
#> 6 2019-09-17 18:45:00 0In the previous example we use reticulate::py_require to install python dependencies on demand, but recommend an environment (e.g. conda) with the required packages pre-installed for better performance and use on multiple subjects.
Updating activerse
When a new package is added to activerse, update the package list, the README, and the attach helper by running:
If the new package lives under a different GitHub owner, pass --owner=:
The script updates DESCRIPTION, R/activerse.R, and both README files in one pass.
To list an ecosystem package in the README without making it an activerse dependency or attaching it, use --related: