Package 'zeitR'

Title: Actigraphy Data Parsing and Analysis for R
Description: Provides tools for importing, parsing, and analysing raw actigraphy data from wrist-worn devices. Implements a full sleep analysis pipeline — off-wrist detection, main sleep period detection, nap detection, and WASO computation — validated epoch-for-epoch against the Condor circadiaBase Python reference pipeline using ActTrust hardware. Also computes standard non-parametric circadian rhythm variables (interdaily stability, intradaily variability, relative amplitude, L5, M10). Device-specific parameter presets can be swapped to adapt the pipeline to other actigraph models. Designed to complement slumbR in the Circadia Lab ecosystem.
Authors: Lucas França [aut, cre] (ORCID: <https://orcid.org/0000-0003-0853-1319>), Mario Leocadio-Miguel [aut] (ORCID: <https://orcid.org/0000-0002-7248-3529>), Julia Ribeiro da Silva Vallim [aut] (ORCID: <https://orcid.org/0000-0001-8708-8479>)
Maintainer: Lucas França <[email protected]>
License: MIT + file LICENSE
Version: 0.1.8
Built: 2026-10-23 11:18:55 UTC
Source: https://github.com/circadia-bio/zeitR

Help Index


Default state colour palette for actogram plots

Description

Returns a named character vector of hex colours keyed on the four state labels produced by label_states(). Pass the output to the colours argument of any actogram function to override individual colours.

Usage

actogram_colours()

Value

Named character vector with elements "wake", "sleep", "nap", and "off-wrist".

Examples

actogram_colours()
# wake        sleep        nap    off-wrist
# "#C25E2A" "#3B2F6B" "#F0A500" "#D9C8A0"

ActTrust device parameter preset

Description

Returns a named list of all device- and study-specific parameter values used by the zeitR pipeline when processing ActTrust recordings. Passing this list (or a modified copy) to run_pipeline() via the params argument fully controls which values each detector stage receives, without having to touch the individual function calls.

Usage

acttrust_params()

Details

The list is organised into four named sections that map directly to the four pipeline stages:

offwrist

Parameters for detect_offwrist_bimodal(). These values were validated against the Condor circadiaBase pipeline using ActTrust hardware. The refinement stage (.bimodal_refine_acttrust) is device-specific and currently only validated for ActTrust.

sleep

Parameters for detect_sleep_crespo(). sleep_quantile (0.365) is the ActTrust CSPD wrapper value; the original Crespo (2012) algorithm uses 1/3.

nap

Parameters for detect_naps_crespo(), passed as the params argument. Equivalent to .cspd_nap_params().

waso

Parameters for compute_waso().

To adapt the pipeline for a different device, copy the list, modify the relevant values, and pass it to run_pipeline():

p <- acttrust_params()
p$sleep$sleep_quantile <- 1/3   # use the original Crespo threshold
result <- run_pipeline("recording.txt", params = p)

Value

A named list with elements offwrist, sleep, nap, and waso.

See Also

run_pipeline() for the pipeline entry point.


Check actigraphy timestamps for consistency issues

Description

Scans a recording tibble for three classes of timestamp problem:

Usage

check_consistency(x, gap_s = 120, datetime_col = "datetime")

Arguments

x

A tibble as returned by read_acttrust() or prepare_actigraphy(), containing a datetime column.

gap_s

numeric(1). Gap threshold in seconds. Intervals longer than this are flagged. Default is 120 (2 minutes).

datetime_col

character(1). Name of the datetime column. Default is "datetime".

Details

  • Gaps — intervals between consecutive epochs longer than gap_s seconds.

  • Backward jumps — timestamps that go backwards in time.

  • Year artefacts — timestamps in the years 1970 or 2000, which typically indicate firmware epoch-counter rollover bugs.

Value

A tibble with one row per detected issue and columns:

row

integer — row index in x where the issue occurs.

datetime

POSIXct — timestamp at that row.

issue

character — one of "gap", "backward_jump", or "year_artefact".

detail

character — human-readable description.

Returns a zero-row tibble if no issues are found.

Examples

## Not run: 
rec    <- read_acttrust("recordings/P001.txt")
issues <- check_consistency(rec)
issues

## End(Not run)

Circular mean of clock times in decimal hours

Description

Computes the mean of times on the 0-24 h clock using the unit-circle method: convert each time to a unit-vector angle, average the sin and cos components, and back-project via atan2. This correctly handles midnight wrap (e.g. times at 23:30 and 00:30 average to 00:00, not 12:00).

Usage

circ_mean_h(x)

Arguments

x

numeric. Decimal hours in [0, 24). NA values are silently dropped.

Details

The older threshold approach (shift values >= 12 h by -24 h before averaging) produces badly biased estimates when times straddle noon – critically for sleep offset in late sleepers – and is superseded by this function (Fix 20 in the JRSV pipeline, ICC for sleep offset 0.502 -> 0.897 on N=404).

Value

A single numeric in [0, 24), or NA_real_ if x is empty after NA removal.

References

Pewsey, A., Neuhauser, M., & Ruxton, G. D. (2013). Circular Statistics in R. Oxford University Press.

Examples

circ_mean_h(c(23.5, 0.5))   # midnight wrap -> 0
circ_mean_h(c(23.0, 1.0))   # -> 0
circ_mean_h(c(7.0, 9.0))    # -> 8

Circular standard deviation of clock times in decimal hours

Description

Computes the circular SD using the mean resultant length formula: sqrt(-2 * log(R_bar)) * 24 / (2*pi), where R_bar is the mean resultant length of the unit-circle representation of the times. Linear SD inflates when times straddle midnight; this measure is invariant to the wrap point (Fix 24 in the JRSV pipeline).

Usage

circ_sd_h(x)

Arguments

x

numeric. Decimal hours in [0, 24). NA values are silently dropped. Returns NA_real_ for fewer than 2 values.

Value

A single non-negative numeric (hours), or NA_real_.

References

Mardia, K. V., & Jupp, P. E. (2000). Directional Statistics (2nd ed.). Wiley.

Examples

circ_sd_h(c(23.5, 0.5))   # small SD for times close to midnight
circ_sd_h(c(6.0, 18.0))   # large SD for times 12 h apart

Classify physical activity intensity directly from activity counts

Description

Convenience wrapper combining estimate_ee() and classify_pa_intensity() in one call: converts raw ActTrust(R)/GT3X+ activity counts straight to a PA intensity factor via the published "batista2026" (or future) equation set, without needing to handle the intermediate MET values.

Usage

classify_pa_counts(
  counts,
  device = c("ACTT", "GT3X+"),
  placement = c("hip", "wrist"),
  equation_set = "batista2026"
)

Arguments

counts

Numeric vector of activity counts (counts/min, over the same epoch length used by the source equation set – 1-minute for "batista2026"). Negative values are treated as 0 with a warning, since the underlying square-root transform is undefined below zero.

device

character(1). "ACTT" (default) or "GT3X+".

placement

character(1). "hip" (default) or "wrist".

equation_set

character(1), passed to pa_equations(). Default "batista2026".

Value

An ordered factor, same length as counts and same levels as classify_pa_intensity().

See Also

estimate_ee(), classify_pa_intensity(), pa_equations() for the coefficients and their generalisability caveats.

Examples

classify_pa_counts(c(0, 5000, 25000, 50000), device = "ACTT", placement = "hip")

Classify physical activity intensity from METs

Description

Bins metabolic equivalent (MET) values into the four PA intensity classes used throughout Batista et al. (2026) and consistent with WHO PA intensity conventions: light, moderate, vigorous, very vigorous.

Usage

classify_pa_intensity(mets)

Arguments

mets

Numeric vector of MET values, e.g. from estimate_ee() or measured directly (indirect calorimetry, published equations, etc.). MET-based, not device-specific – any source of METs can be classified.

Value

An ordered factor with levels c("light", "moderate", "vigorous", "very_vigorous"), same length as mets. Bands: ⁠[0,3)⁠ light, ⁠[3,6)⁠ moderate, ⁠[6,9)⁠ vigorous, ⁠[9,Inf)⁠ very vigorous.

See Also

estimate_ee(), pa_equations()

Examples

mets <- estimate_ee(c(0, 5000, 25000, 50000), device = "ACTT", placement = "hip")
classify_pa_intensity(mets)

Classify sleep episodes as main or secondary (native pipeline)

Description

Applies the JRSV classification rule set to a raw episode table from extract_sleep_episodes(), assigning each episode a sleep_type of "main" or "secondary". The execution order follows Fix 27:

Usage

classify_sleep_episodes(
  episodes,
  data,
  max_tib_h = 16,
  max_main_tib_h = 14,
  min_main_tib_h = 4,
  nocturnal_onset_start = 18,
  nocturnal_onset_end = 6,
  temp_thresh = 28,
  light_thresh_window = 5,
  light_thresh_recovery = 10,
  min_fragment_tib_h = 1,
  rolling_window_min = 15L,
  max_split_iterations = 3L,
  collision_gap_h = 4,
  verbose = FALSE
)

Arguments

episodes

A tibble as returned by extract_sleep_episodes().

data

A tibble as returned by detect_sleep_crespo(), containing at minimum datetime, state, activity. The ActTrust channels int_temp (wrist temperature) and light (ambient lux) must be present for Fix 26a/c; the function falls back to defaults when absent.

max_tib_h

numeric(1). Episodes with TBT > this value are excluded directly. Default 16.

max_main_tib_h

numeric(1). Episodes with TBT in ⁠(max_main_tib_h, max_tib_h]⁠ are split first. Default 14.

min_main_tib_h

numeric(1). Minimum TBT (hours) for a main episode. Default 4.

nocturnal_onset_start

numeric(1). Default nocturnal window start (decimal hours). Overridden by Fix 26a. Default 18.

nocturnal_onset_end

numeric(1). Default nocturnal window end. Overridden by Fix 26a. Default 6.

temp_thresh

numeric(1). Minimum wrist temperature (degC) for candidate episodes in Fix 26a/c. Default 28.

light_thresh_window

numeric(1). Maximum ambient light (lux) for the nocturnal window inference (Fix 26a). Default 5.

light_thresh_recovery

numeric(1). Maximum ambient light (lux) for gap merging in fragment recovery (Fix 26c). Default 10.

min_fragment_tib_h

numeric(1). Minimum TBT (hours) for each fragment produced by an episode split (Fix 29). Default 1.

rolling_window_min

integer(1). Smoothing window (epochs) for the activity signal used to locate episode split points. Default 15L.

max_split_iterations

integer(1). Maximum recursive splits per episode. Default 3L.

collision_gap_h

numeric(1). Minimum gap (hours) between two same-date main episodes to trigger sleep-date reassignment (Fix 26b). Default 4.

verbose

logical(1). Print step-by-step diagnostics. Default FALSE.

Details

  1. Fix 29f – exclude episodes starting at/after noon on the latest classified night's calendar date (max(gts)), before any other classification step. Prevents a spurious end-of-recording artifact (device removed mid-day, not a real night) from being counted as a valid main night.

  2. Fix 26a – infer adaptive nocturnal window from int_temp and light. Falls back to nocturnal_onset_start/nocturnal_onset_end if fewer than 2 candidate episodes pass the temperature and light filter.

  3. Fix 29 / Rule 1 – split TBT 14–16 h episodes at their activity peak (recursive, up to max_split_iterations). Fragments still exceeding max_main_tib_h after splitting are excluded.

  4. Rule 2 – exclude episodes with TBT > max_tib_h (16 h) directly, without attempting a split.

  5. Fix 26c – recover sleep nights missed by the scorer for dates with no classified main episode. Sleep and wake within the candidate window are determined by Cole-Kripke epoch scoring on ZCMn (not the period-level CSPD state). Adjacent sleep runs are merged when the intervening gap has wrist temperature >= temp_thresh and ambient light <= light_thresh_recovery.

  6. Rules 3–5 – classify each episode as "main" or "secondary" using the nocturnal window and min_main_tib_h.

  7. Fix 26b – resolve sleep-date collisions: when two main episodes share a noon-threshold sleep date and are separated by >= collision_gap_h hours, reassign the later one to the next calendar date.

  8. Rule 6 – keep the longest main episode per sleep date; demote all others to "secondary".

  9. Rule 7 – exclude all episodes (main and secondary) on dates that have no valid main sleep after Rule 6.

Value

A tibble with the same columns as episodes plus sleep_type ("main" or "secondary") and is_nap (logical, TRUE when sleep_type == "secondary", for backwards compatibility with the zeitr_result$nights schema).

See Also

extract_sleep_episodes(), run_pipeline_native()


Compute PIM, TAT, and ZCM activity counts from raw triaxial acceleration

Description

Reproduces the general wrist-actigraph processing chain – per-axis DC removal and band-pass filter, vector norm, epoch-level integration – that converts raw acceleration samples into the epoch-level activity metrics used throughout the rest of zeitR (e.g. read_acttrust()'s activity/ZCMn columns) and the wider PA-intensity literature (see pa_equations()).

Usage

compute_activity_counts(
  x,
  y,
  z,
  sampling_rate,
  epoch_sec = 60,
  filter_low = 0.5,
  filter_high = 2.7,
  zcm_threshold = 0.01,
  tat_threshold = 0.05,
  metrics = c("PIM", "TAT", "ZCM")
)

Arguments

x, y, z

Numeric vectors of raw triaxial acceleration samples (same units – typically g – and length, uniformly sampled at sampling_rate).

sampling_rate

numeric(1). Sampling rate of x/y/z in Hz.

epoch_sec

numeric(1). Epoch length in seconds. Default 60 (1-minute epochs, the ActTrust standard used elsewhere in zeitR).

filter_low, filter_high

numeric(1). Band-pass cutoffs in Hz, passed to mrpheus::bandpass_filter(). Default 0.5/2.7 (ActTrust-style, per Batista et al. 2026); use 0.25/2.5 for GT3X+-style processing instead.

zcm_threshold

numeric(1). Dead-band around zero (same units as x/y/z) a filtered axis must clear to register a zero crossing. Default 0.01.

tat_threshold

numeric(1). Amplitude threshold (same units) the filtered norm must exceed to count toward TAT. Default 0.05.

metrics

character(). Which metrics to compute. Default c("PIM", "TAT", "ZCM") (all three).

Value

A tibble with one row per epoch: epoch (integer index, 1-based) plus one column per requested metric.

What this is (and isn't)

Filtering reuses mrpheus::remove_dc() and mrpheus::bandpass_filter() – the same zero-phase Butterworth implementation already validated as part of mrpheus's YASA-parity PSG pipeline – rather than a new, unvalidated filter written from scratch. This makes mrpheus a required package for this function specifically (a Suggests dependency of zeitR, checked at runtime; the rest of zeitR does not need it).

Reusing a validated filter primitive does not make the whole function validated, though: the PIM/TAT/ZCM epoch-aggregation logic below (what happens after filtering) is still an original implementation with nothing to check it against – no raw-to-counts converter exists to compare it to, and Condor's/ActiGraph's exact onboard threshold constants are proprietary and unpublished. Built from the general processing description in Batista et al. (2026, PLoS ONE) and standard actigraphy literature. Treat its output as a reasonable approximation of device-computed counts, not a validated reproduction – and prefer device-computed counts (read_acttrust()) over this function whenever they're available.

Processing steps

  1. Each of x, y, z has its DC offset removed (mrpheus::remove_dc()) – important for the z-axis in particular, which typically carries a ~1 g gravity offset – then is independently band-pass filtered with a zero-phase (mrpheus::bandpass_filter()) Butterworth filter (filter_low-filter_high Hz).

  2. A vector norm is computed per sample: sqrt(x^2 + y^2 + z^2) on the filtered axes, matching the order described for ActTrust in Batista et al. (2026): filter first, then combine axes.

  3. Samples are grouped into non-overlapping epochs of epoch_sec * sampling_rate samples. Trailing samples that don't fill a complete final epoch are dropped, with a warning.

  4. Per epoch:

    • PIM (proportional integration mode) – sum of the absolute filtered norm across the epoch.

    • TAT (time above threshold) – seconds within the epoch where the absolute filtered norm exceeds tat_threshold.

    • ZCM (zero crossing mode) – number of sign changes beyond a zcm_threshold dead-band, summed across the three filtered axes, within the epoch.

zcm_threshold and tat_threshold have no published reference value – they are exposed as tunable parameters rather than given a false-precision default. The values shipped here are small, physically motivated starting points (see Arguments), not calibrated constants.

See Also

pa_equations(), estimate_ee(), classify_pa_counts() to go from PIM counts to METs and PA intensity bands; read_acttrust() for device-computed counts (preferred over this function when available); mrpheus::bandpass_filter(), mrpheus::remove_dc() for the underlying filter primitives.

Examples

set.seed(1)
n  <- 25 * 60 * 5  # 5 minutes at 25 Hz
x  <- rnorm(n, sd = 0.05)
y  <- rnorm(n, sd = 0.05)
z  <- rnorm(n, sd = 0.05) + 1  # gravity on the z-axis

## Not run: 
compute_activity_counts(x, y, z, sampling_rate = 25, epoch_sec = 60)

## End(Not run)

Cosinor rhythmometry (Cornelissen 2014)

Description

Fits a single-harmonic cosine model to an activity/light/temperature signal with a FIXED period (default 24h), returning the acrophase (peak time), MESOR (rhythm-adjusted mean), and amplitude.

Usage

compute_cosinor(x, col = "activity", period_min = 1440)

Arguments

x

A zeitr_recording/zeitr_result, or a data frame / tibble with at least a datetime column and the column named in col.

col

character(1). Name of the signal column to fit (e.g. "activity", "light", "int_temp"). Default "activity".

period_min

numeric(1). Fixed period in minutes. Default 1440 (24h), matching the reference notebook's locked-period usage.

Details

Ports pyActigraphy's actual Cosinor class (pyActigraphy/analysis/ cosinor.py) as it is really used in the reference notebook (Cell 16 of vs_condor_py_pipeline_fix30_jrsv.ipynb's ⁠_fit_cosinor()⁠), not reconstructed from documentation. The model is

y=M+Acos⁡(ωx+ϕ)y = M + A \cos(\omega x + \phi)

with ω=2π/T\omega = 2\pi / T, xx the integer epoch position (⁠0, 1, 2, ...⁠, matching Cosinor._convert_timestamp_to_index()'s (index - index[0]) / index.freq, not clock time or elapsed seconds), MM the MESOR, AA the amplitude, ϕ\phi the acrophase.

Cell 16's wrapper explicitly locks Period (vary=False) rather than fitting it – its own comment explains why: leaving Period free let the optimizer converge anywhere from 0.23h to 91.75h, making the acrophase/period meaningless. With the period fixed, the model is linear in disguise: expanding Acos⁡(ωx+ϕ)=Acos⁡ϕcos⁡(ωx)−Asin⁡ϕsin⁡(ωx)A\cos(\omega x+\phi) = A\cos\phi\cos(\omega x) - A\sin\phi\sin(\omega x) and substituting β1=Acos⁡ϕ\beta_1 = A\cos\phi, β2=−Asin⁡ϕ\beta_2 = -A\sin\phi gives y=M+β1cos⁡(ωx)+β2sin⁡(ωx)y = M + \beta_1\cos(\omega x) + \beta_2\sin(\omega x), an ordinary linear regression in (M,β1,β2)(M, \beta_1, \beta_2) – solved here via qr.solve(), not a nonlinear optimizer. Verified by direct execution that this closed-form OLS solution matches pyActigraphy's actual lmfit (Levenberg-Marquardt) fit to within floating-point precision when the period is locked, which it always is in the notebook's actual usage – this is not an approximation.

acrophase_time/acrophase_time_neg reproduce the notebook's own sign-flip fix exactly: the model's peak occurs at t_peak = -phi / omega = -phi * T / (2*pi), minutes since the first epoch – the notebook's own comment documents a prior bug where the unnegated +phi * T/(2*pi) gave the ANTI-peak (trough), offset ~12h from the true peak. t_peak is then wrapped to a 24h clock time via round(t_peak) %% 1440 – hardcoded to 1440 regardless of the actual period_min used for the fit, matching the source exactly (the wrap-to-clock-time step assumes a 24h day, since it's meant to report a wall-clock peak time, not a literal fit-period position). acrophase_time_neg maps this to ⁠[-12, 12]⁠ (values ⁠>= 12:00⁠ shifted by -24), matching the ⁠_om10_neg⁠/⁠_ol5_neg⁠ convention used elsewhere in the same notebook for circular-safe comparison of clock times.

Value

A tibble with columns participant_id, acrophase_time ("HH:MM" string), acrophase_time_neg (numeric, ⁠[-12, 12]⁠), MESOR, amplitude, period_min, n_epochs.

References

Cornelissen, G. (2014). Cosinor-based rhythmometry. Theoretical Biology and Medical Modelling, 11(1), 16. doi:10.1186/1742-4682-11-16

See Also

compute_npcra(), compute_sri()

Examples

## Not run: 
result <- run_pipeline_native("recordings/P001.txt", tz = "America/Sao_Paulo")
compute_cosinor(result)
compute_cosinor(result, col = "light")

## End(Not run)

Compute CPD, MSF, MSW, MSFsc, SJL, and SJLa

Description

Calculates chronobiological phenotyping metrics from classified nightly sleep data. Mirrors compute_cpd_metrics() and nights_to_cpd_df() from Julia Ribeiro da Silva Vallim's pipeline_functions_fix27.py, with two updates to match the fix29 notebook:

  • MSF and MSW are computed with the circular mean to correctly handle mid-sleep times that wrap midnight.

  • Truncated episodes starting after noon on the last recording day are excluded before metric computation.

Usage

compute_cpd_metrics(x, ...)

## S3 method for class 'zeitr_result'
compute_cpd_metrics(
  x,
  min_tib_h = 3,
  min_tib_eve_h = 3,
  tz = "UTC",
  holidays = x$holidays,
  free_days = x$free_days,
  ...
)

## Default S3 method:
compute_cpd_metrics(
  x,
  min_tib_h = 3,
  min_tib_eve_h = 3,
  tz = "UTC",
  holidays = NULL,
  free_days = c("Saturday", "Sunday"),
  ...
)

Arguments

x

A zeitr_result object or a tibble of nightly sleep statistics as returned by run_pipeline_native() or run_pipeline().

...

Not used; reserved for forward compatibility with future methods.

min_tib_h

numeric(1). Minimum TBT (hours) for inclusion. Default is 3.0.

min_tib_eve_h

numeric(1). Minimum TBT (hours) for a night to qualify as a free-day-eve night. Default is 3.0.

tz

character(1). Time zone for extracting clock hours. Default is "UTC".

holidays

Holidays to treat as free days in addition to the days in free_days. Accepts three forms, which can be mixed in the same vector:

  • Date objects or "YYYY-MM-DD" strings for year-specific dates (e.g. as.Date("2019-03-04") for one Carnival day).

  • "DD-MM" strings for dates that recur every year (e.g. "25-12" for Christmas, "01-01" for New Year). Default is NULL. When x is a zeitr_result, defaults to x$holidays automatically. A warning is emitted when NULL; suppress with options(zeitR.no_holidays_warn = FALSE).

free_days

A character vector of day names ("Monday" through "Sunday", case-insensitive) or ISO integers (1 = Monday ... 7 = Sunday) identifying which days of the week are unconditionally treated as free days. Default is c("Saturday", "Sunday"). When x is a zeitr_result, defaults to x$free_days automatically.

Details

Dispatches on the class of x:

  • data.frame / tibble – treated as a nights table (same behaviour as the original function signature).

  • zeitr_result – extracts x$nights and inherits x$holidays and x$free_days automatically.

Mid-sleep is computed per night as:

MS=(SO+offset−onset2) mod 24\text{MS} = \left(\text{SO} + \frac{\text{offset} - \text{onset}}{2}\right) \bmod 24

where onset = bts + SOL and offset = gts - SOI (both in decimal hours).

MSW and MSF use the circular mean of per-night mid-sleep values, treating clock hours as angles on a 24-hour circle so values near midnight from opposite sides average correctly, matching calculate_msf() / calculate_msw() from the fix29 notebook. MSFsc adjusts MSF by the free-day-eve sleep onset and the weighted weekly mean sleep duration when free-day duration exceeds weekday duration. CPD is the RMS distance of each night's mid-sleep from MSFsc in the time x sequence plane.

Value

A named list with n_nights_cpd, n_free_days, n_workdays, msw_h, msw_hms, msf_h, msf_hms, msfsc_h, msfsc_hms, sjl_h, sjl_min, sjla_h, sjla_min, cpd_s, cpd_min, cpd_h.

See Also

compute_sleep_metrics(), run_pipeline_native()


Compute LIDS ultradian-rhythm parameters for every sleep bout

Description

The main entry point for the LIDS pipeline: extracts sleep bouts, applies lids_transform() and fit_lids() to each, and returns one row per bout with cosine-fit parameters and a quality-filter flag. Ports the full pipeline described in Winnebeck et al. (2018) and Hammad et al. (2026).

Usage

compute_lids(
  x,
  bout_source = c("auto", "state", "roenneberg"),
  activity_col = "activity",
  method = c("gaussian", "mva"),
  win_min = 30,
  sigma_min = 5,
  period_range = c(30, 180),
  period_step = 2,
  duration_range = c(3, 12),
  min_r = 0.4,
  max_p = 0.05,
  offset_bounds = c(1, 99),
  bout_args = list()
)

Arguments

x

A zeitr_result, zeitr_recording, or a data frame / tibble with at least datetime and activity columns (and, for bout_source = "state"/"auto", a state column).

bout_source

character(1). "auto" (default), "state", or "roenneberg". See Details.

activity_col

character(1). Name of the activity column in x. Default "activity".

method, win_min, sigma_min

Forwarded to lids_transform().

period_range, period_step

Forwarded to fit_lids().

duration_range

numeric(2), hours. Bout duration bounds (both bout_source paths). Default c(3, 12).

min_r, max_p, offset_bounds

Quality-filter thresholds; see Details.

bout_args

Named list of additional arguments forwarded to detect_lids_bouts() when bout_source = "roenneberg" (e.g. relative_threshold, main_window). Default list().

Value

A tibble with one row per bout: participant_id, bout_id, bout_start, bout_end, duration_h, period_min, amplitude, offset, slope_per_60min, phase_rad, pearson_r, p_value, mri, passes_quality_filter.

Where bouts come from (bout_source)

  • "state" – uses the epoch-level state column already produced by zeitR's own pipelines (run_pipeline() / run_pipeline_native()): contiguous state == 1 runs are treated as bouts, filtered by duration_range. Off-wrist (state == 4) epochs break a run rather than being bridged.

  • "roenneberg" – ignores any existing state column and runs the independent detect_lids_bouts() relative-immobility detector directly on the activity signal. Use this for standalone recordings that haven't been run through zeitR's Crespo/Vallim pipelines, or to reproduce Winnebeck/Hammad's own bout-detection method rather than zeitR's.

  • "auto" (the default) – "state" if a state column is present in x, otherwise "roenneberg".

Quality filtering

Following Winnebeck et al. (2018) and Hammad et al. (2026), a bout passes quality filtering when all of:

  • pearson_r >= min_r (default 0.4 – a soft data-quality threshold, not a hard significance test; ~75% of adult bouts cleared this bar in Winnebeck et al. 2018),

  • p_value <= max_p (default 0.05),

  • ⁠offset_bounds[1] < offset < offset_bounds[2]⁠ (default ⁠1 < offset < 99⁠), excluding spuriously flat bouts (e.g. a lost/removed device).

Bouts failing quality filtering are still returned (with passes_quality_filter = FALSE) rather than dropped, so callers can inspect what was excluded.

References

Winnebeck, E. C., Fischer, D., Leise, T., & Roenneberg, T. (2018). Dynamics and Ultradian Structure of Human Sleep in Real Life. Current Biology, 28(1), 49-59.e5. doi:10.1016/j.cub.2017.11.063

Hammad, G., Schoch, S. F., Engelmann, M., Spock, Z., Kurth, S., & Winnebeck, E. C. (2026). Charting infant sleep cycle development using actigraphy: Longitudinal evidence for ultradian cycle lengthening within the first year of life. SLEEP.

See Also

lids_transform(), fit_lids(), detect_lids_bouts(), study_lids_metrics()

Examples

## Not run: 
result <- run_pipeline_native("recordings/P001.txt", tz = "America/Sao_Paulo")
compute_lids(result)

## End(Not run)

Light Regularity Index (LRI)

Description

Computes the Light Regularity Index (Hand et al. 2023): a measure of day-to-day consistency in light exposure timing, analogous to the Sleep Regularity Index but applied to a binarized light-exposure signal.

Usage

compute_lri(x, col = "light", threshold = NULL, log_transform = FALSE)

Arguments

x

A zeitr_recording/zeitr_result, or a data frame / tibble with at least a datetime column and the column named in col.

col

character(1). Name of the light channel column. Default "light".

threshold

numeric(1) or NULL. Threshold applied to col (col > threshold = "light exposure"). Default NULL, which computes all five of the reference notebook's default thresholds (10, 20, 50, 100, 300, applied as 1, 1.301, 1.699, 2, 2.4771 – see Details on why these numbers don't mean lux against raw data) and returns one row with columns LRI_10...LRI_300. Pass a single number for one custom threshold instead (returns a single LRI column).

log_transform

logical(1). If TRUE, applies log10(col + 1) before thresholding – the probably-originally-intended behaviour implied by the reference notebook's (incorrect, per Details) comment. Default FALSE, matching what the reference actually computes.

Details

Ports the artvalencio/pyActigraphy fork's actual LRI() method (pyActigraphy/light/light_metrics.py) exactly – found there after an earlier investigation confirmed it does not exist anywhere in the official ghammad/pyActigraphy repository (any branch, any commit in history). LRI()'s own inner prob_stability()/lri_profile() functions are structurally identical to compute_sri()'s "sadeh"/ "ck"/"scripps"/"roenneberg" paths' aggregation (.sri_pyactigraphy()) – verified by direct execution that LRI()'s own code and a direct call to sri() (pyActigraphy/sleep/scoring/ sri.py) give IDENTICAL results on the same real light data, to full floating-point precision. compute_lri() reuses .sri_pyactigraphy() directly rather than duplicating the same formula under a different name.

A real discrepancy worth knowing about, not a guess: the reference notebook's own comment (Cell 16 of vs_condor_py_pipeline_fix30_jrsv.ipynb) claims "pyActigraphy stores light data in log10-transformed lux internally," and picks its default thresholds accordingly (⁠10/20/50/100/300 lux -> log10 -> 1.0/1.301/1.699/2.0/2.4771⁠). This is not what the code actually does: read directly, RawATR's constructor (pyActigraphy/io/atr/atr.py) passes the LIGHT column straight into LightRecording with no log10() call anywhere, and real light values from actual ActTrust recordings confirm this empirically – raw LIGHT ranges from 0 to over 20,000 in real data checked this session, nowhere near the ~0-5 range log10(lux) would produce. So the reference Python's own LRI_10/LRI_20/etc. columns almost certainly don't measure what their names claim – thresholds of ~1-2.5 applied to raw, untransformed light amount to "any detectable light vs. darkness," not "50 lux vs. below." This function replicates the reference's actual behaviour (raw threshold, no transform) for parity purposes – matching what the real pipeline computes, not what its comments say it computes. log_transform = TRUE is available if you want the – probably originally intended – log10(lux) behaviour instead; it is NOT the default, since the default should match the real reference output.

Value

A tibble with participant_id, either LRI_10, LRI_20, LRI_50, LRI_100, LRI_300 (default, threshold = NULL) or a single LRI column (custom threshold), and n_epochs.

References

Hand, A. J., Lane, J. M., Payne, A. C., ... & Barker, D. H. (2023). Measuring light regularity: sleep regularity is associated with regularity of light exposure in adolescents. Sleep, 46(8), zsad001. doi:10.1093/sleep/zsad001

See Also

compute_sri(), compute_cosinor()

Examples

## Not run: 
result <- run_pipeline_native("recordings/P001.txt", tz = "America/Sao_Paulo")
compute_lri(result)
compute_lri(result, threshold = 50)

## End(Not run)

Non-parametric circadian rhythm analysis (NPCRA)

Description

Computes the standard non-parametric circadian rhythm analysis variables from an actigraphy recording. IS and IV are computed from the hourly-mean activity profile (p = 24), matching pyActigraphy's actual ⁠_interdaily_stability()⁠/⁠_intradaily_variability()⁠ (not the population-variance formula in Gonçalves et al. 2014 or Van Someren et al. 1999's own text – the real implementation uses sample variance, ddof = 1; see Details). L5/M10 and their onsets follow a different convention – see below – matching the notebook this package's Vallim-pipeline comparisons were validated against.

Usage

compute_npcra(
  x,
  epoch_s = NULL,
  L5_hours = 5,
  M10_hours = 10,
  window_days = NULL,
  trim_to_d1 = TRUE,
  exclude_offwrist = FALSE
)

Arguments

x

A zeitr_recording as returned by read_actigraphy(), or a data frame / tibble with at least datetime and activity columns. If a state column is present, off-wrist epochs (state == 4) are used as-is by default – see exclude_offwrist.

epoch_s

numeric(1). Epoch duration in seconds. If NULL (default), estimated automatically from the median inter-epoch interval.

L5_hours

numeric(1). Width of the least-active window in hours. Default is 5.

M10_hours

numeric(1). Width of the most-active window in hours. Default is 10.

window_days

numeric(1) or NULL. If supplied, the recording is split into non-overlapping windows of this length (in days) and NPCRA variables are computed for each window. A window_start column is added to the output. Partial final windows (shorter than window_days) are included but flagged via a lower n_days value. Default NULL computes a single estimate over the full recording.

trim_to_d1

logical(1). If TRUE (default), the recording is trimmed to start at 00:00 of D+1 – the first full calendar day after recording onset – before any NPCRA variable is computed, matching the Python reference pipeline's convention (it always starts its NPCRA window at D+1 00:00 rather than spanning the raw, typically fractional, recording length). Set to FALSE for the full untrimmed recording (the pre-trim_to_d1 behaviour). If trimming would leave fewer than 2 epochs, a warning is emitted and the untrimmed recording is used instead. Off-wrist exclusion (state == 4, if exclude_offwrist = TRUE) still applies either way; this does not replicate the Python pipeline's separate 30-min-threshold off-wrist-run rule for the M10/L5 windows specifically (see exclude_offwrist) – only the D+1 window start.

exclude_offwrist

logical(1). If TRUE, off-wrist epochs (state == 4, when a state column is present) are deleted before computing any NPCRA variable. Default FALSE matches the actual production Python pipeline (Cell 16 of vs_condor_py_pipeline_fix30_jrsv.ipynb): ⁠_nonparam_metrics()⁠ is always called with mask_series=None for every variable (IS, IV, M10, L5, RA) – off-wrist periods' raw device readings are used as-is, not deleted. Set TRUE for the more conservative (but non-Python-matching) behaviour of excluding them. This is a blunter tool than Python's own off-wrist handling for M10/L5 specifically (short runs zeroed and kept, long runs excluded via NA + min_periods), which this package does not replicate – TRUE here simply deletes every off-wrist epoch outright, changing the time index rather than leaving gaps.

Details

The following variables are computed:

IS

Interdaily stability — consistency of the 24 h rest-activity pattern across days (range 0–1; higher = more stable). From the hourly-mean profile (p = 24).

IV

Intradaily variability — fragmentation of the rest-activity rhythm (>= 0; higher = more fragmented). From the hourly-mean profile (p = 24).

ISm

Mean of IS computed at every divisor of 1440 minutes between 1 and 60 min (22 resolutions total: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 30, 32, 36, 40, 45, 48, 60), matching Cell 16's ⁠_ISm_IVm_FREQS⁠ loop. Unlike IS, missing bins at each resolution are omitted (not zero-filled).

IVm

As ISm, for IV.

RA

Relative amplitude — contrast between the most active 10 h window (M10) and least active 5 h window (L5) (range 0–1).

L5

Mean activity during the least active 5-hour window, found by a rolling mean over a 10-min-resampled series, searched globally across the whole recording (not the p = 24 hourly profile used for IS/IV).

L5_onset

Wall-clock time ("HH:MM") of the end of the least-active window – the time of day, wrapped at 24 h regardless of which calendar day the window actually falls on.

M10

As L5, for the most active 10-hour window.

M10_onset

As L5_onset, for the most-active window.

IS/IV build a 1h-resampled series X first: missing hourly bins get a real zero (matching Python's ⁠s_1h = s.resample('1h').mean().fillna(0)⁠), not silent omission. X is grouped by hour-of-day into the p = 24 hourly profile Xh. Both variables then use sample variance (divide by n - 1, matching pandas' .var() default) rather than the population variance (divide by n) that the classic Witting/Van Someren/Gonçalves formulas describe on paper:

IS=∑h(Xˉh−Xˉ)2/(p−1)∑i(Xi−Xˉ)2/(N−1)IS = \frac{\sum_h(\bar{X}_h-\bar{X})^2/(p-1)}{\sum_i(X_i-\bar{X})^2/(N-1)}

IV=∑i(Xi−Xi−1)2/(N−1)∑i(Xi−Xˉ)2/(N−1)IV = \frac{\sum_i(X_i-X_{i-1})^2/(N-1)}{\sum_i(X_i-\bar{X})^2/(N-1)}

with N the number of hourly bins in the (zero-filled) recording and p the number of hour-of-day groups present (24 for any recording spanning a full day). The two formulas share the same denominator, matching pyActigraphy's d_1h = data.var() being computed once and reused for both.

ISm/IVm repeat this same computation at 22 other bin widths (every divisor of 1440 min between 1-60 min) and average the results – but missing bins at those resolutions are omitted from N (matching .dropna()), not zero-filled like the main IS/IV. A resolution is only excluded from the average if it has 1 or fewer bins; if the IS/IV formula itself degenerates (e.g. zero variance) at some other resolution, that NaN/Inf is included in the average like any other value, exactly matching the real Python (a try/except around the whole per-frequency block, not a check on the computed value).

Value

A tibble with columns participant_id, window_start (if window_days is set), IS, IV, ISm, IVm, RA, L5, L5_onset, M10, M10_onset, n_days, n_epochs.

References

Gonçalves, B. S. B., Adamowicz, T., Louzada, F. M., Moreno, C. R., & Araujo, J. F. (2014). A fresh look at the use of nonparametric analysis in actimetry. Sleep Medicine Reviews, 20, 84–91. doi:10.1016/j.smrv.2014.06.002

Van Someren, E. J. W., Swaab, D. F., Colenda, C. C., Cohen, W., McCall, W. V., & Rosenquist, P. B. (1999). Bright light therapy: Improved sensitivity to its effects on rest-activity rhythms in Alzheimer patients by application of nonparametric methods. Chronobiology International, 16(4), 505–518. doi:10.3109/07420529908998724

Examples

## Not run: 
rec   <- read_actigraphy("recordings/P001.txt")

# Single estimate over the full recording
compute_npcra(rec)

# Per-fortnight estimates
compute_npcra(rec, window_days = 14)

## End(Not run)

Compute sleep metrics split by day type

Description

Calculates mean sleep metrics separately for all nights, weekday nights, and weekend/holiday nights. Output column names and derived metrics mirror compute_sleep_metrics() from Julia Ribeiro da Silva Vallim's pipeline_functions_fix27.py.

Usage

compute_sleep_metrics(x, ...)

## S3 method for class 'zeitr_result'
compute_sleep_metrics(
  x,
  min_tib_h = 5,
  tz = "UTC",
  holidays = x$holidays,
  free_days = x$free_days,
  ...
)

## Default S3 method:
compute_sleep_metrics(
  x,
  min_tib_h = 5,
  tz = "UTC",
  holidays = NULL,
  free_days = c("Saturday", "Sunday"),
  ...
)

Arguments

x

A zeitr_result object or a tibble of nightly sleep statistics as returned by run_pipeline_native() or run_pipeline(). Must contain at minimum the columns is_nap, bed_time, get_up_time, tbt, tst, sol, soi, waso, eff.

...

Not used; reserved for forward compatibility with future methods.

min_tib_h

numeric(1). Minimum total in-bed time (hours) for a night to be included. Default is 5.0 (matching the Python reference).

tz

character(1). Time zone for extracting clock hours from timestamps. Default is "UTC".

holidays

Holidays to treat as free days in addition to the days in free_days. Accepts three forms, which can be mixed in the same vector:

  • Date objects or "YYYY-MM-DD" strings for year-specific dates (e.g. as.Date("2019-03-04") for one Carnival day).

  • "DD-MM" strings for dates that recur every year (e.g. "25-12" for Christmas, "01-01" for New Year). Default is NULL. When x is a zeitr_result, defaults to x$holidays automatically. A warning is emitted when NULL; suppress with options(zeitR.no_holidays_warn = FALSE).

free_days

A character vector of day names ("Monday" through "Sunday", case-insensitive) or ISO integers (1 = Monday ... 7 = Sunday) identifying which days of the week are unconditionally treated as free days. Default is c("Saturday", "Sunday"). When x is a zeitr_result, defaults to x$free_days automatically.

Details

Dispatches on the class of x:

  • data.frame / tibble – treated as a nights table (same behaviour as the original function signature).

  • zeitr_result – extracts x$nights and inherits x$holidays and x$free_days automatically.

A night is assigned to the free-day group when the get-up date falls on one of the days in free_days or matches an entry in holidays. Day-of-week is determined via the ISO 8601 weekday number (format(date, "%u")) rather than weekdays(), which is locale-dependent. All remaining nights are workday nights.

Both sleep_onset_h and sleep_offset_h use a circular mean, treating clock hours as angles on a 24-hour circle so values near midnight from opposite sides average correctly (e.g. 23:30 and 00:30 average to 00:00, not 12:00).

fps_h (free period sleep) equals fpr_tib_h - (latencia_min + inertia_min) / 60 – TBT net of sleep onset latency and sleep inertia.

dp_midsleep_min is the circular SD (R-bar mean-resultant-length formula) of per-night mid-sleep, in minutes – matching production Python's ⁠_std_circular_h()⁠ exactly, not a plain SD. dp_tst_min is a plain SD of per-night TST, in minutes.

Value

A named list with metrics for three groups (overall, wd = workday, fd = free day):

n_overall, n_wd, n_fd

Night counts.

sleep_onset_h, sleep_offset_h

Circular mean onset and offset in decimal hours.

fpr_tib_h

Mean TBT in hours.

fps_h

Mean free period sleep (TBT - SOL - SOI) in hours.

tst_h

Mean TST in hours.

latencia_min, inertia_min

Mean SOL and SOI in minutes.

waso_min

Mean WASO in minutes.

sleep_eff_pct

Mean sleep efficiency in percent (0-100).

tst_24h_h

Same as tst_h (24-h TST for main sleep only).

dp_midsleep_min

Circular SD of mid-sleep, in minutes.

dp_tst_min

Plain SD of TST, in minutes.

Workday and free-day metrics carry the suffix ⁠_wd⁠ and ⁠_fd⁠.

See Also

compute_cpd_metrics(), run_pipeline_native()

Examples

## Not run: 
# From a zeitr_result: holidays and free_days forwarded automatically
result <- run_pipeline_native("recordings/P001.txt",
                              tz        = "America/Sao_Paulo",
                              holidays  = my_holidays,
                              free_days = c("Saturday", "Sunday"))
sm <- compute_sleep_metrics(result, tz = "America/Sao_Paulo")

# Non-standard schedule: Friday + Saturday as free days
sm <- compute_sleep_metrics(result$nights,
                            tz        = "America/Sao_Paulo",
                            holidays  = my_holidays,
                            free_days = c("Friday", "Saturday"))

sm$tst_h            # mean TST in hours
sm$sleep_onset_h    # mean sleep onset (circular, decimal hours)
sm$dp_midsleep_min  # within-person SD of mid-sleep in minutes

## End(Not run)

Sleep Regularity Index (SRI)

Description

Computes the Sleep Regularity Index (Phillips et al. 2017): a measure of day-to-day consistency in the sleep/wake pattern, based on the probability that an epoch's sleep/wake state matches the state at the same clock time exactly 24 h later (or earlier), averaged across the whole recording. Ranges from -100 (perfectly inverted day-to-day) to +100 (perfectly regular); 0 corresponds to chance-level agreement.

Usage

compute_sri(x, epoch_s = NULL, max_gap_min = 30, algo = "vallim")

Arguments

x

A zeitr_recording/zeitr_result, or a data frame / tibble with at least datetime and state columns (algo = "vallim") or datetime and activity columns (algo = "sadeh", "ck", "scripps", or "roenneberg"). For state: state == 1 or state == 7 is treated as sleep, state == 4 as off-wrist (missing), and any other value as wake – matching the coding already used across zeitR's pipeline output (run_pipeline(), run_pipeline_native(), export_hypnogram()).

epoch_s

numeric(1). Epoch duration in seconds. If NULL (default), estimated automatically from the median inter-epoch interval.

max_gap_min

numeric(1). Off-wrist gaps of this many minutes or less are interpolated rather than excluded. Default 30, matching Fix 30 and Fix 14's 30-minute threshold elsewhere in the pipeline. Only used when algo = "vallim".

algo

character(1). Which sleep/wake source and SRI aggregation to use: "vallim" (default, uses the pipeline's own state column), "sadeh", "ck", "scripps", or "roenneberg" (all four score raw activity). See Details for the precise differences beyond just the scoring algorithm.

Details

algo = "vallim" (default) ports Fix 30 of the Python reference pipeline (SRI_vallim): sleep/wake comes directly from the epoch-level state column already produced by zeitR's own pipelines (run_pipeline() / run_pipeline_native()) – the same classification compute_sleep_metrics() and compute_cpd_metrics() already treat as ground truth for this recording. Off-wrist handling mirrors Fix 30 exactly: off-wrist epochs (state == 4) are treated as missing. Gaps of max_gap_min minutes or less are interpolated (forward-filled from the last valid epoch, or back-filled from the next valid epoch when the gap starts at the very beginning of the recording); longer gaps are left as missing and excluded from the day-to-day comparison entirely, rather than being counted as a non-match. The aggregation is a single flat average over every valid 24h-apart epoch pair, pooled across the whole recording:

SRI=−100+200×1M∑tΨ(t,t+24h)SRI = -100 + 200 \times \frac{1}{M}\sum_{t} \Psi(t, t + 24h)

where Ψ(t,t+24h)=1\Psi(t, t+24h) = 1 if the sleep/wake state at epoch tt matches the state 24 h later, 00 otherwise, and MM is the number of epoch pairs where both epochs have a valid (non-missing) state. This matches Julia's own compute_sri_vallim() replica (also a flat pooled ratio, not a per-time-of-day average – see below).

algo = "sadeh" instead derives sleep/wake from the raw activity signal via the Sadeh et al. (1994) algorithm, matching pyActigraphy's actual Sadeh()/SleepRegularityIndex() (Cell 16 of vs_condor_py_pipeline_fix30_jrsv.ipynb) exactly – both sourced from pyActigraphy's real code (pyActigraphy/sleep/scoring_base.py, ⁠_sadeh()⁠; pyActigraphy/sleep/scoring/sri.py, sri()), not reconstructed from documentation. Two precise details that differ from the "vallim" path above:

  • Aggregation is a two-step average, not a flat pooled one. pyActigraphy's sri() first groups epochs by time-of-day (hour, minute, second) across all days, averages day-to-day stability within each time-of-day slot, and only then averages across slots. This is mathematically different from a flat pooled average whenever slots have unequal numbers of valid day-pairs (e.g. a partial first/last day) – both "vallim" and "sadeh" are ported faithfully to their own respective real source, not reconciled to use the same aggregation.

  • No off-wrist handling. Sadeh scores whatever raw activity it is given; there is no off-wrist masking/interpolation step in pyActigraphy's own code for this path. max_gap_min has no effect when algo = "sadeh".

Sadeh's own edge behaviour is also reproduced exactly: mean_W5/NAT need a full centered 11-epoch window (5 before, self, 5 after) and are NA for the first/last 5 epochs; sd_Last6 needs a full trailing 6-epoch window (self + 5 before) and is NA for the first 5 epochs; logAct uses the following epoch's activity (shift(-1)) and is NA for the last epoch. Where the resulting PS score is NA, pandas' NaN > threshold evaluates to False (not propagated as missing), so that epoch is scored wake (0), not excluded – reproduced here explicitly, since R's NA > threshold gives NA, not FALSE.

algo = "ck" derives sleep/wake via pyActigraphy's native Cole-Kripke implementation (.CK(), default settings = "30sec_max_non_overlap") – a DIFFERENT weight set from the Condor-native ColeKripke class used elsewhere in zeitR's pipeline (R/cole_kripke.R); the two share an algorithm family name but are otherwise unrelated. Shares "sadeh"'s two-step SRI aggregation and lack of off-wrist handling (see above). Despite the reference notebook resampling to 30-second bins before calling this, that round-trip is a mathematical no-op on data that was only ever 1-minute resolution (verified by direct execution – see ?.ck_native_score), so this operates directly on native-resolution activity with no resampling needed. Also applies Webster's (1982) rescoring rules afterward, matching pyActigraphy's default. Uses the opposite threshold polarity from Sadeh (D < threshold = sleep here, vs PS > threshold = sleep for Sadeh) – both faithful to their own respective source.

algo = "scripps" derives sleep/wake via pyActigraphy's native Scripps Clinic implementation (Scripps()/⁠_scripps()⁠). Structurally identical to algo = "ck" – same centered rolling weighted dot product, same D < threshold = sleep polarity, same two-step SRI aggregation and lack of off-wrist handling – just a different scale/window/threshold, and no Webster rescoring step (pyActigraphy's Scripps() doesn't call rescore(), unlike CK()).

algo = "roenneberg" derives sleep/wake via pyActigraphy's native Roenneberg et al. algorithm (roenneberg()) – by far the most involved of the four: trend extraction (a 24h centered rolling mean, allowing a partial window down to 12h at the recording's edges), a 15%-of-trend threshold categorization, seed-finding (candidate sleep-onset runs at least 30 min long), then an iterative correlation-based bout-cleaning loop (each candidate onset is tested against a family of triangular "sleep bout ending at position i" templates over the following 12h, accepting the HIGHEST correlation peak as the bout's offset – not simply the first qualifying one; see ?.find_highest_peak_idx for a real version mismatch this caught). Shares the two-step SRI aggregation and lack of off-wrist handling with the other three raw-activity algorithms above. No rescoring step (rescoring is specific to CK()).

Value

A tibble with columns participant_id, sri, n_pairs (number of valid 24h-apart epoch comparisons used; NA for algo != "vallim", whose aggregation isn't a single pooled pair count), and n_epochs. sri is NA if the recording is shorter than 24 h, or (for algo = "vallim") if no valid pairs remain after off-wrist exclusion.

References

Phillips, A. J. K., Clerx, W. M., O'Brien, C. S., Sano, A., Barger, L. K., Picard, R. W., Lockley, S. W., Klerman, E. B., & Czeisler, C. A. (2017). Irregular sleep/wake patterns are associated with poorer academic performance and delayed circadian and sleep/wake timing. Scientific Reports, 7, 3216. doi:10.1038/s41598-017-03171-4

Sadeh, A., Sharkey, M., & Carskadon, M. A. (1994). Activity-Based Sleep-Wake Identification: An Empirical Test of Methodological Issues. Sleep, 17(3), 201-207. doi:10.1093/sleep/17.3.201

See Also

compute_npcra(), compute_sleep_metrics(), run_pipeline_native()

Examples

## Not run: 
result <- run_pipeline_native("recordings/P001.txt", tz = "America/Sao_Paulo")
compute_sri(result)
compute_sri(result, algo = "sadeh")

## End(Not run)

Compute WASO and nightly sleep statistics

Description

Scores each epoch within detected sleep periods as wake or sleep using score_epochs_cole_kripke(), then computes per-night statistics. This is a faithful port of the Python condor_pipeline detect_waso: night boundaries are built by .nights_df() (the search_gap = FALSE path of the reference nights_df), each night's ZCM is scored with Cole-Kripke, and the epoch-level state is rebuilt from a zero (wake) base so that within-night WASO-wake epochs and all epochs outside detected nights are scored as wake.

Usage

compute_waso(x, wake_thresh = 60L, search_gap = FALSE)

Arguments

x

A tibble as returned by detect_naps_crespo() (or detect_sleep_crespo() if nap detection is skipped), containing columns datetime, ZCMn, and state.

wake_thresh

integer(1). Minimum duration in epochs of a wake bout required to delimit a new sleep period boundary. Default is 60.

search_gap

logical(1). Reserved for API compatibility with the reference pipeline. The reference detect_waso always builds boundaries with search_gap = FALSE, so this argument currently has no effect.

Details

The following metrics are computed for each detected night / nap:

Metric Definition
tbt Total Bed Time — epochs from bed time to get-up time
tst Total Sleep Time — tbt - waso - sol - soi
waso Wake After Sleep Onset — wake epochs between sleep onset and final wake
sol Sleep Onset Latency — epochs from bed time to first sleep epoch
soi Sleep Offset Inertia — trailing wake epochs at end of sleep period
nw Number of awakenings — count of wake-onset transitions
eff Sleep efficiency — tst / tbt

Value

A list with two elements:

nights

A tibble with one row per detected night/nap and columns night, is_nap, bed_time, get_up_time, tbt, tst, waso, sol, soi, nw, eff.

data

The input tibble x with state and sleep updated with epoch-level Cole-Kripke wake/sleep scores.

References

Cole, R. J., Kripke, D. F., Gruen, W., Mullaney, D. J., & Gillin, J. C. (1992). Automatic sleep/wake identification from wrist activity. Sleep, 15(5), 461–469. doi:10.1093/sleep/15.5.461

Examples

## Not run: 
rec    <- read_acttrust("recordings/P001.txt")
prep   <- prepare_actigraphy(rec)
prep   <- detect_offwrist_bimodal(prep)
prep   <- detect_sleep_crespo(prep)
prep   <- detect_naps_crespo(prep)
result <- compute_waso(prep)
result$nights

## End(Not run)

Detect nighttime sleep bouts via the Roenneberg relative-immobility method

Description

Standalone sleep-bout detector for raw actigraphy, independent of zeitR's main Crespo-based pipeline (detect_sleep_crespo()) or the Vallim native pipeline (run_pipeline_native()). Intended for LIDS analysis on activity recordings that haven't been run through either of those. If you already have a zeitr_result, pass it straight to compute_lids() instead (bout_source = "state", the default there) and skip this function.

Usage

detect_lids_bouts(
  datetime,
  activity,
  relative_threshold = 0.15,
  ma_window_h = 24,
  bridge_min = 5,
  min_bout_min = 30,
  max_gap_min = 15,
  main_window = c("18:00", "08:00"),
  duration_range = c(3, 12),
  one_per_night = TRUE
)

Arguments

datetime

POSIXct vector of epoch timestamps, regularly spaced.

activity

Numeric vector of activity counts, same length as datetime.

relative_threshold

numeric(1). Default 0.15.

ma_window_h

numeric(1). Moving-average window in hours. Default 24.

bridge_min

numeric(1). Maximum active-blip length (minutes) to bridge during consolidation. Default 5.

min_bout_min

numeric(1). Minimum consolidated-run length (minutes) to keep as a candidate bout. Default 30.

max_gap_min

numeric(1). Maximum gap (minutes) between candidate bouts to fuse them into one. Default 15.

main_window

character(2). c(start, end) clock times ("HH:MM") defining the window a bout must start within. Default c("18:00", "08:00") (wraps midnight).

duration_range

numeric(2). Final bout duration bounds, in hours. Default c(3, 12) (Winnebeck et al. 2018).

one_per_night

logical(1). If TRUE (default), keep only the longest surviving bout per calendar night (the "main sleep episode").

Details

Ported (with one bug fixed – see below) from a prototype R notebook translation of Mario Leocadio-Miguel's method, itself adapted from the relative-immobility algorithm used in Winnebeck et al. (2018) and Hammad et al. (2026):

  1. Compute a ma_window_h-centered moving average of activity (the recording's own baseline).

  2. Flag epochs where activity < relative_threshold times that moving average as candidate sleep.

  3. Consolidate: brief active blips (<= bridge_min minutes), surrounded on both sides by candidate-sleep epochs, are relabelled as sleep.

  4. Keep only consolidated runs lasting >= min_bout_min.

  5. Fuse bouts separated by a gap <= max_gap_min. (This is the step truncated mid-statement in the source notebook, fused.a... – reimplemented in full here as the internal .fuse_bouts(). Unlike the notebook, which pads fused gaps with NaN to preserve a regular time axis, the fused bout here simply spans start to end over the original activity values – fine for bout timing, but if you need the gap epochs excluded from the LIDS fit itself, mask them to NA in activity beforehand.)

  6. Restrict to bouts starting within main_window (default 18:00-08:00), excluding daytime naps / recording artefacts.

  7. Filter by total duration (duration_range) and, if one_per_night = TRUE, keep only the longest bout per calendar night.

The moving-average step uses zeitR's border-replicated rolling_mean_cpp() rather than pandas' min_periods=1 edge behaviour (which shrinks the window near the recording boundary instead of replicating the edge value) – a minor difference confined to the first/ last ma_window_h/2 hours of the recording.

Value

A tibble with one row per detected bout: bout_id, bout_start, bout_end, duration_h, n_epochs.

References

Winnebeck, E. C., Fischer, D., Leise, T., & Roenneberg, T. (2018). Dynamics and Ultradian Structure of Human Sleep in Real Life. Current Biology, 28(1), 49-59.e5. doi:10.1016/j.cub.2017.11.063

See Also

compute_lids(), lids_transform(), fit_lids()


Detect secondary sleep periods (naps) using the Crespo nap algorithm

Description

Faithful port of the Python nap_wrapper: runs the full CSPD model in nap mode (detect_naps = TRUE) on the currently-awake epochs (state == 0) and merges the detected naps into the sleep state. Must be run after detect_sleep_crespo().

Usage

detect_naps_crespo(x, epoch_h = NULL, params = .cspd_nap_params())

Arguments

x

A tibble as returned by detect_sleep_crespo(), containing columns datetime, activity, and state. Nap detection runs on the wake (state == 0) subset only, mirroring the Python nap_bool mask.

epoch_h

numeric(1). Number of epochs per hour. If NULL (default), derived from the wake-subsequence epoch duration as 3600 / duration.

params

CSPD nap configuration list (default .cspd_nap_params()), the port of nap_wrapper's parameter set.

Details

Nap detection uses a nap-mode MSP (a high zero-proportion combined with a low adaptive-median activity, .crespo_nap_msp()) followed by the same bed-time / get-up-time refiners as the main sleep detection, with the nap parameter set (.cspd_nap_params()) and nap-specific minimum-length post-processing. Detected naps are written as state == 1 (merged into "sleep"), matching nap_wrapper, which assigns state[wake] = 1 - refined_output (i.e. naps are not a distinct state).

Value

The input tibble x with state and sleep columns updated. Nap epochs become state == 1 and sleep == 1; off-wrist (state == 4) and existing main-sleep epochs are preserved.

References

Crespo, C., Aboy, M., Fernández, J. R., & Mojón, A. (2012). Automatic identification of activity-rest periods based on actigraphy. Journal of Medical and Biological Engineering, 32(4), 249–256. doi:10.5405/jmbe.1033

See Also

detect_sleep_crespo() for main sleep period detection.

Examples

## Not run: 
rec  <- read_acttrust("recordings/P001.txt")
prep <- prepare_actigraphy(rec)
prep <- detect_offwrist_bimodal(prep)
prep <- detect_sleep_crespo(prep)
prep <- detect_naps_crespo(prep)

## End(Not run)

Off-wrist detection using the Condor bimodal activity/temperature model

Description

Detects periods where the actigraph was not worn using the bimodal algorithm developed by Condor Instruments. The algorithm proceeds in three stages:

Usage

detect_offwrist_bimodal(
  x,
  hws = 10L,
  activity_quantile = 0.15,
  min_norm_activity = 0.015,
  nbins = 100L,
  min_offwrist_length = 10L,
  min_temp_threshold = 0.35
)

Arguments

x

A tibble as returned by prepare_actigraphy(), containing columns datetime, activity (PIM), int_temp, ext_temp, state, and offwrist.

hws

integer(1). Half-window size (in epochs) for rolling feature extraction. Default is 10 (matching the Python pipeline).

activity_quantile

numeric(1). Quantile used to define "low activity". Default is 0.15.

min_norm_activity

numeric(1). Minimum normalised activity threshold below which the low-activity cutoff is clamped. Default is 0.015.

nbins

integer(1). Number of histogram bins used when fitting the GMM threshold. Default is 100.

min_offwrist_length

integer(1). Minimum number of consecutive off-wrist epochs required to retain a detected period. Shorter runs are discarded. Default is 10.

min_temp_threshold

numeric(1). Minimum normalised temperature threshold; the fitted GMM threshold is clamped to this value if it falls below it. Default is 0.35.

Details

  1. Feature extraction — rolling median of PIM activity and rolling signal/variance of internal temperature are computed over a symmetric window of half-width hws epochs.

  2. Bimodal temperature threshold — among epochs with low activity median (below the activity_quantile quantile of normalised activity), a 2-component Gaussian Mixture Model is fitted to the normalised temperature distribution. The threshold between the two components is taken as the minimum of the GMM density between the two means (or the minimum of the smoothed histogram if that is lower). Ashman's D is computed as a bimodality quality metric.

  3. Initial classification & refinement — epochs simultaneously exhibiting low activity median AND low temperature are marked as off-wrist. Short spurious off-wrist runs shorter than min_offwrist_length epochs are removed.

Off-wrist epochs are encoded as state == 4 (matching the Python pipeline convention). The offwrist column is set to 0.25 for off-wrist epochs for actogram overlay plotting.

Value

The input tibble x with state and offwrist columns updated. Off-wrist epochs have state == 4 and offwrist == 0.25.

References

The bimodal off-wrist algorithm was developed by Julius A. P. P. de Paula at Condor Instruments (2023). It is not published in peer-reviewed literature but the source code is available in the circadiaBase pipeline repository. The Ashman D statistic is described in:

Ashman, K. M., Bird, C. M., & Zepf, S. E. (1994). Detecting bimodality in astronomical datasets. The Astronomical Journal, 108, 2348. doi:10.1086/117248

Examples

## Not run: 
rec  <- read_acttrust("recordings/P001.txt")
prep <- prepare_actigraphy(rec)
prep <- detect_offwrist_bimodal(prep)
sum(prep$state == 4)  # number of off-wrist epochs

## End(Not run)

Detect main sleep periods using the Crespo algorithm

Description

Identifies the main sleep period(s) in an actigraphy recording using the algorithm described in Crespo et al. (2012). The method applies an adaptive median filter to the activity signal, mitigates spuriously long zero runs, and thresholds the result at a quantile of the filtered signal. Morphological closing and opening operations are then used to smooth the binary sleep/wake estimate.

Usage

detect_sleep_crespo(
  x,
  epoch_h = NULL,
  median_filter_h = 8,
  pad_h = 1,
  sleep_quantile = 0.365,
  morph_size = 61L,
  consec_zeros_thr = 15L,
  awake_zeros_thr = 2L,
  sleep_zeros_thr = 30L,
  zero_mitigation_q = 0.33,
  min_short_window_thr = 1,
  refine = TRUE,
  condition = 0L
)

Arguments

x

A tibble as returned by detect_offwrist_bimodal() (or prepare_actigraphy() if off-wrist detection is skipped), containing columns datetime, activity, and state. The detector runs on the on-wrist subset (state != 4) only, mirroring the Python cspd_wrapper.

epoch_h

numeric(1). Number of epochs per hour. If NULL (default), derived from the epoch duration (the mode of the on-wrist inter-epoch interval), as 3600 / duration.

median_filter_h

numeric(1). Length of the preprocessing median filter window in hours. Default is 8.

pad_h

numeric(1). Padding length in hours added before the adaptive median filter. Default is 1.

sleep_quantile

numeric(1). Quantile of the filtered activity used as the MSP sleep/wake threshold. Default is 0.365 (the ActTrust CSPD value used by cspd_wrapper; the standalone Crespo algorithm uses 1/3).

morph_size

integer(1). Size of the structuring element used in morphological closing/opening. Default is 61 epochs.

consec_zeros_thr

integer(1). Runs of zeros longer than this threshold are treated as invalid (zero mitigation). Default is 15.

awake_zeros_thr

integer(1). Threshold for consecutive zeros within wake periods. Default is 2.

sleep_zeros_thr

integer(1). Threshold for consecutive zeros within sleep periods. Default is 30.

zero_mitigation_q

numeric(1). Quantile of activity used to determine the mitigation level for invalid zero runs. Default is 0.33.

min_short_window_thr

numeric(1). Minimum value of the adaptive median threshold; if the fitted quantile falls below this, the threshold is clamped here. Default is 1.0.

refine

logical(1). If TRUE (default), the MSP detection is refined into final sleep periods by the CSPD bed-time / get-up-time refiners (.cspd_refine_periods), reproducing the Python refined_output. If FALSE, the raw MSP detection is used directly.

condition

integer(1). Initial condition flag (default 0). The MSP stage bumps it to 2 when its activity-median threshold clamps to min_short_window_thr; the refiner uses that effective condition. Affects only refine = TRUE.

Value

The input tibble x with state and sleep columns updated. Sleep epochs have state == 1 and sleep == 1; off-wrist epochs (state == 4) are preserved and excluded from the sleep column. With refine = TRUE the sleep epochs delimit the refined sleep PERIODS (the Python refined_output); per-epoch wake/sleep within them is scored later by compute_waso().

References

Crespo, C., Aboy, M., Fernández, J. R., & Mojón, A. (2012). Automatic identification of activity-rest periods based on actigraphy. Journal of Medical and Biological Engineering, 32(4), 249–256. doi:10.5405/jmbe.1033

See Also

detect_naps_crespo() for secondary sleep period (nap) detection.

Examples

## Not run: 
rec  <- read_acttrust("recordings/P001.txt")
prep <- prepare_actigraphy(rec)
prep <- detect_offwrist_bimodal(prep)
prep <- detect_sleep_crespo(prep)

## End(Not run)

Estimate energy expenditure (METs) from activity counts

Description

Applies the published ActTrust(R)/GT3X+ regression equation (sqrt(MET) = b0 + b1 * sqrt(activity_counts)) from pa_equations() to convert raw activity counts into estimated metabolic equivalents (METs).

Usage

estimate_ee(
  counts,
  device = c("ACTT", "GT3X+"),
  placement = c("hip", "wrist"),
  equation_set = "batista2026"
)

Arguments

counts

Numeric vector of activity counts (counts/min, over the same epoch length used by the source equation set – 1-minute for "batista2026"). Negative values are treated as 0 with a warning, since the underlying square-root transform is undefined below zero.

device

character(1). "ACTT" (default) or "GT3X+".

placement

character(1). "hip" (default) or "wrist".

equation_set

character(1), passed to pa_equations(). Default "batista2026".

Value

Numeric vector of estimated METs, same length as counts.

See Also

pa_equations() for the underlying coefficients and their limitations, classify_pa_intensity() to convert METs into intensity bands.

Examples

estimate_ee(c(0, 5000, 25000, 50000), device = "ACTT", placement = "hip")

Export a zeitR pipeline result as a hypnoR-compatible hypnogram

Description

Converts the epoch-level data tibble from run_pipeline() or run_pipeline_native() into the tidy hypnogram format expected by hypnoR metric functions.

Usage

export_hypnogram(
  result,
  subject_id = NULL,
  source = "zeitR",
  drop_offwrist = FALSE,
  epoch_sec = NULL
)

Arguments

result

A zeitr_result list as returned by run_pipeline() or run_pipeline_native(), or a tibble with at minimum the columns datetime and state.

subject_id

character(1) or NULL. Written to the subject_id column and always takes precedence. When NULL (default), result$subject_id is used if present; the column is omitted entirely when no ID is available from either source.

source

character(1). Label written to the source column. Default "zeitR".

drop_offwrist

logical(1). Remove off-wrist epochs (state == 4) from the output and re-index epochs. Default FALSE.

epoch_sec

numeric(1). When supplied, warns if the observed epoch duration differs from this value. Default NULL.

Details

The coarse (3-state) stage mapping used by zeitR is:

state ZCMn Stage
0 any "W"
1, 7 ⁠> 0⁠ "Sleep"
1, 7 ⁠== 0⁠ "Quiet sleep"
4 any "W"

Zero-count epochs within sleep (ZCMn == 0) are mapped to "Quiet sleep" as the standard actigraphy proxy for quiet/deep sleep. When ZCMn is not present in the data, all sleep epochs are mapped to "Sleep".

The stage column is an ordered factor with levels c("W", "Sleep", "Quiet sleep"), matching hypnoR's coarse resolution contract. "Quiet sleep" is not produced by actigraphy but the level is present so downstream hypnoR functions do not throw factor-level errors.

subject_id is taken from result$subject_id when not explicitly supplied. Both run_pipeline() and run_pipeline_native() derive this from the input filename stem automatically, so in most cases no manual override is needed.

Value

A tibble with columns:

epoch

Integer epoch index, 1-based.

time

POSIXct timestamp for the start of each epoch.

stage

Ordered factor: c("W", "Sleep", "Quiet sleep").

subject_id

Character subject identifier (omitted when not available).

source

Character scorer label.

See Also

run_pipeline(), run_pipeline_native(), label_states()

Examples

## Not run: 
result <- run_pipeline("recordings/P001.txt", tz = "America/Sao_Paulo")

# subject_id inferred from filename automatically
hyp <- export_hypnogram(result)
hyp$subject_id  # "P001"

# Override when filename does not match study code
hyp <- export_hypnogram(result, subject_id = "STUDY_001")

# Batch: subject_id inferred for every file automatically
results <- run_pipeline_batch("recordings/", tz = "America/Sao_Paulo")
hyps    <- lapply(results, export_hypnogram)

# Drop off-wrist epochs
hyp_clean <- export_hypnogram(result, drop_offwrist = TRUE)

## End(Not run)

Extract sleep episodes from a CSPD-scored epoch table

Description

Converts the epoch-level state vector produced by detect_sleep_crespo() into a per-episode tibble analogous to the Python SleepPipeline's results.nights. Contiguous on-wrist sleep periods are delimited by .nights_df(); per-epoch wake/sleep within each period is scored by score_epochs_cole_kripke() to derive WASO, SOL, SOI, TST, EFF, and NW. All time metrics are returned in minutes.

Usage

extract_sleep_episodes(data, wake_thresh = 60L)

Arguments

data

A tibble as returned by detect_sleep_crespo(), containing columns datetime, ZCMn, and state.

wake_thresh

integer(1). Minimum wake-bout length (epochs) used by .nights_df() to separate episode boundaries. Default 60L.

Value

A tibble with one row per detected episode and columns bts (POSIXct), gts (POSIXct), tbt, tst, sol, soi, waso (minutes), nw (integer), eff (0-1), nap (logical, always FALSE – classification into main/secondary happens in classify_sleep_episodes()).

See Also

classify_sleep_episodes(), run_pipeline_native()


Fit a sloped cosine to a LIDS profile, scanning candidate periods

Description

For a fixed period TT, the sloped-cosine model

f(t)=αcos⁡(2πt/T)+βsin⁡(2πt/T)+b+s tf(t) = \alpha\cos(2\pi t/T) + \beta\sin(2\pi t/T) + b + s\,t

is linear in (α,β,b,s)(\alpha,\beta,b,s), so it is solved by ordinary least squares rather than a non-linear optimiser (Hammad et al. 2026). Candidate periods are scanned over period_range in steps of period_step, and for each one the fit's Munich Rhythmicity Index (MRI=2×amplitude×r\text{MRI} = 2 \times \text{amplitude} \times r, Winnebeck et al. 2018) is computed. The period with the highest MRI is returned as the bout's estimated ultradian cycle length – the same selection rule used by pyActigraphy.analysis.LIDS.lids_fit(), generalised here to include the linear slope term.

Usage

fit_lids(lids, epoch_min = 1, period_range = c(30, 180), period_step = 2)

Arguments

lids

Numeric vector of (smoothed) LIDS values for one sleep bout, as returned by lids_transform(). Must not contain NA.

epoch_min

numeric(1). Epoch duration in minutes. Default 1.

period_range

numeric(2). Candidate period bounds in minutes. Default c(30, 180) (Hammad et al. 2026, tuned for infant/ultradian cycles); use c(60, 180) with period_step = 5 to match Winnebeck et al. (2018)'s adult/adolescent scan.

period_step

numeric(1). Step size in minutes for the period scan. Default 2.

Value

A named list:

period_min

Estimated cycle length (minutes) at peak MRI.

amplitude

α2+β2\sqrt{\alpha^2+\beta^2}.

phase_rad

−atan2(β,α)-\mathrm{atan2}(\beta,\alpha), radians; 0 = LIDS peak at bout start.

offset

Inactivity level at bout start (bb).

slope_per_60min

Linear trend, rescaled to LIDS units per hour.

pearson_r

Correlation between fitted and observed LIDS.

p_value

Two-sided p-value for pearson_r (stats::cor.test()).

mri

Munich Rhythmicity Index at the selected period.

References

Winnebeck, E. C., Fischer, D., Leise, T., & Roenneberg, T. (2018). Dynamics and Ultradian Structure of Human Sleep in Real Life. Current Biology, 28(1), 49-59.e5. doi:10.1016/j.cub.2017.11.063

Hammad, G., Schoch, S. F., Engelmann, M., Spock, Z., Kurth, S., & Winnebeck, E. C. (2026). Charting infant sleep cycle development using actigraphy. SLEEP.

See Also

lids_transform(), compute_lids()

Examples

set.seed(1)
t <- seq(0, 300, by = 1)
lids <- 85 + 15 * cos(2 * pi * t / 60) - 0.05 * t + rnorm(length(t), sd = 2)
fit_lids(lids)

Convert integer epoch states to a labelled factor

Description

Converts the integer state column produced by the zeitR pipeline into a human-readable factor. Useful for display, plotting, and export — the internal state column always stays integer to preserve Python reference parity.

Usage

label_states(x)

Arguments

x

integer (or numeric) vector of epoch states, as found in result$data$state.

Details

Integer Label
0 "wake"
1 "sleep"
4 "off-wrist"
7 "nap"

Any value not in the table above is silently converted to NA.

Value

An ordered factor with levels c("wake", "sleep", "nap", "off-wrist"), the same length as x.

Examples

label_states(c(0L, 1L, 0L, 4L, 1L, 7L))
# [1] wake  sleep wake  off-wrist sleep nap
# Levels: wake < sleep < nap < off-wrist

## Not run: 
result <- run_pipeline("recordings/P001.txt")
result$data$state_label <- label_states(result$data$state)

## End(Not run)

Apply the LIDS (Locomotor Inactivity During Sleep) transform

Description

Converts an activity signal into "inactivity" via the LIDS non-linear transform, then smooths it – the first step of the LIDS ultradian-rhythm pipeline (Winnebeck et al. 2018; Hammad et al. 2026). LIDS is only ever computed inside an already-identified sleep bout; extract bouts first via compute_lids() or detect_lids_bouts().

Usage

lids_transform(
  activity,
  epoch_min = 1,
  method = c("gaussian", "mva"),
  win_min = 30,
  sigma_min = 5
)

Arguments

activity

Numeric vector of activity counts for a single sleep bout, in chronological order at a constant epoch length.

epoch_min

numeric(1). Epoch duration in minutes. Default 1.

method

character(1). "gaussian" (default) or "mva".

win_min

numeric(1). Smoothing window width in minutes (full width for "mva"; +/- 3 sigma width for "gaussian"). Default 30.

sigma_min

numeric(1). Gaussian kernel standard deviation in minutes, used only when method = "gaussian". Default 5 (Hammad et al. 2026); ignored for "mva".

Details

LIDSi=1001+xi\text{LIDS}_i = \frac{100}{1+x_i}

where xix_i is the raw activity count at epoch ii. A LIDS value of 100 means zero movement; it falls toward 0 as movement increases.

Two smoothing methods are available, both over a nominal 30-min window:

  • "gaussian" (default) – Gaussian kernel, standard deviation sigma_min (default 5 min), truncated at +/- 3 sigma (Hammad et al. 2026).

  • "mva" – centered moving average (Winnebeck et al. 2018; pyActigraphy's default). Uses zeitR's border-replicated internal rolling_mean_cpp(), which replicates the edge value rather than shrinking the window near the bout boundary (pandas' min_periods=1 behaviour) – a minor difference confined to the first/ last ~15 min of each bout.

Any NA in activity (e.g. a brief off-wrist gap inside an otherwise valid bout) is linearly interpolated first – fit_lids() cannot handle missing values.

Value

Numeric vector of smoothed LIDS values, same length as activity.

References

Winnebeck, E. C., Fischer, D., Leise, T., & Roenneberg, T. (2018). Dynamics and Ultradian Structure of Human Sleep in Real Life. Current Biology, 28(1), 49-59.e5. doi:10.1016/j.cub.2017.11.063

Hammad, G., Schoch, S. F., Engelmann, M., Spock, Z., Kurth, S., & Winnebeck, E. C. (2026). Charting infant sleep cycle development using actigraphy: Longitudinal evidence for ultradian cycle lengthening within the first year of life. SLEEP.

See Also

fit_lids(), detect_lids_bouts(), compute_lids()

Examples

set.seed(1)
activity <- pmax(0, 20 + 15 * sin(seq(0, 6 * pi, length.out = 360)) +
                    rnorm(360, sd = 5))
lids <- lids_transform(activity, method = "gaussian")

Published activity-count equations for estimating METs and PA intensity

Description

Returns the regression coefficients and count-based cut-points for estimating energy expenditure (METs) and classifying physical activity (PA) intensity from ActTrust(R) (Condor Instruments) or ActiGraph(R) GT3X+ activity counts, hip or wrist placement.

Usage

pa_equations(equation_set = "batista2026")

Arguments

equation_set

character(1). Currently only "batista2026" is available.

Value

A tibble with one row per device x placement combination and columns:

device

"ACTT" or "GT3X+".

placement

"hip" or "wrist".

b0, b1

Intercept and slope of sqrt(MET) = b0 + b1 * sqrt(activity_counts).

cut3, cut6, cut9

Published count/min cut-points (95% CI midpoints) at the 3, 6, and 9 MET thresholds – i.e. the count value at which the fitted equation crosses that MET level. Provided for reference; classify_pa_intensity() classifies on estimated METs directly rather than re-deriving these.

Important caveats

These equations come from a single controlled-laboratory validation study (Batista et al. 2026; N = 56 healthy adults aged 18-35; treadmill walking/running at 3-9 km/h only). Treat them as one available equation set, not a universal standard:

  • The paper's own Discussion section compares its GT3X+ (hip) cut-points against two other published GT3X+ studies (Sasaki et al. 2011; Santos-Lozano et al. 2013) and finds differences of 2-65% depending on the MET threshold – and those two reference studies differ from each other by 16-39%. The paper attributes that spread mainly to sample-level characteristics rather than device or methodological artefacts, but that reading applies to the comparison between Sasaki and Santos-Lozano – it doesn't fully carry over to comparisons against this paper's own equations, since all three studies used different modelling approaches (Sasaki et al.: ActiGraph's two-regression model; Santos-Lozano et al.: an artificial neural network; this paper: a simple sqrt-transformed linear model with a device x placement interaction). Cross-study cut-point differences therefore reflect model choice as well as sample, not sample alone – and that's checkable to different degrees: this paper's linear coefficients (b0/b1 below) are transparent enough to compare term-by-term against Sasaki et al.'s two-regression model, so a discrepancy there is at least diagnosable. Santos-Lozano et al.'s cut-points come from an ANN, which has no inspectable coefficients – there is no way to say why it disagrees with the equations here, only that it does.

  • The ACTT (hip)/ACTT (wrist) equations are the first published cut-points for ActTrust(R) at all, so there is nothing yet to cross-check them against.

  • Validated only for laboratory treadmill walking/running in healthy young adults. Applying these equations to free-living data, other age groups (children, older adults), clinical populations, or other activity types is an extrapolation the source study explicitly flags as untested.

equation_set is exposed as an explicit argument (rather than hard-coding a single table) so a future validation study covering a different population or device can be added as an alternative set without changing the estimate_ee() / classify_pa_intensity() API.

See Also

estimate_ee(), classify_pa_intensity()

Examples

pa_equations()

Plot a single-column actogram

Description

Renders epoch-level sleep/wake states as a raster with one row per calendar day and time-of-day (00:00 to 24:00) on the x-axis. The oldest day is at the top, following standard chronobiology convention.

Usage

plot_actogram(
  result,
  tz = NULL,
  title = NULL,
  colours = NULL,
  date_label_every = 7L,
  epoch_min = 1,
  base_size = 13
)

Arguments

result

A zeitr_result list (from run_pipeline() or run_pipeline_native()), or a tibble with at least datetime and state columns.

tz

character(1) or NULL. Timezone for date and time-of-day extraction. NULL (default) auto-detects from the timezone attribute embedded in the datetime POSIXct column; falls back to "UTC" when absent.

title

character(1) or NULL. Plot title. NULL constructs "Actogram \u2014 <subject_id>" from result$subject_id when available.

colours

Named character vector mapping state labels ("wake", "sleep", "nap", "off-wrist") to hex colours. NULL uses actogram_colours().

date_label_every

integer(1). Label every Nth row on the y-axis. Default 7 (weekly ticks).

epoch_min

numeric(1). Epoch duration in minutes. Used as the tile width in ggplot2::geom_tile(). Default 1 (ActTrust standard).

base_size

numeric(1). Base font size passed to ggplot2::theme_minimal(). Default 13.

Value

A ggplot object.

See Also

plot_actogram_double(), plot_actogram_activity(), actogram_colours(), label_states()

Examples

## Not run: 
result <- run_pipeline("recordings/P001.txt", tz = "America/Sao_Paulo")

# Default colours
plot_actogram(result)

# Custom colours
plot_actogram(result, colours = c(wake = "#E8D5B0", sleep = "#1C1A2E",
                                  nap = "#F0A500", "off-wrist" = "#C25E2A"))

# Label every 14 days
plot_actogram(result, date_label_every = 14L)

## End(Not run)

Plot a double-plotted actogram with activity bars

Description

Same double-plot layout as plot_actogram_double() — each day appears in both the left and right column of adjacent rows — but each epoch is rendered as a vertical bar whose height is proportional to the raw activity count (default: ZCMn, the zero-crossing mean from ActTrust). Bars are coloured by sleep/wake state, so the plot simultaneously conveys activity intensity and state classification.

Usage

plot_actogram_activity(
  result,
  tz = NULL,
  title = NULL,
  colours = NULL,
  activity_col = "ZCMn",
  activity_cap_quantile = 0.99,
  log_scale = FALSE,
  date_label_every = 7L,
  epoch_min = 1,
  base_size = 13
)

Arguments

result

A zeitr_result list (from run_pipeline() or run_pipeline_native()), or a tibble with at least datetime and state columns.

tz

character(1) or NULL. Timezone for date and time-of-day extraction. NULL (default) auto-detects from the timezone attribute embedded in the datetime POSIXct column; falls back to "UTC" when absent.

title

character(1) or NULL. Plot title. NULL constructs "Actogram \u2014 <subject_id>" from result$subject_id when available.

colours

Named character vector mapping state labels ("wake", "sleep", "nap", "off-wrist") to hex colours. NULL uses actogram_colours().

activity_col

character(1). Name of the column in result$data to use as the activity signal. Default "ZCMn" (zero-crossing mean; present in all ActTrust recordings processed by zeitR).

activity_cap_quantile

numeric(1) in (0, 1]. Quantile of non-zero activity values used to cap bar heights before normalising. Default 0.99 (top 1 % of active epochs are clipped to full bar height).

log_scale

logical(1). If TRUE, applies a log1p() transform to the activity signal before capping and normalising, compressing the dynamic range so lower-activity variation is easier to see. Default FALSE (linear scale).

date_label_every

integer(1). Label every Nth row on the y-axis. Default 7 (weekly ticks).

epoch_min

numeric(1). Epoch duration in minutes. Used as the tile width in ggplot2::geom_tile(). Default 1 (ActTrust standard).

base_size

numeric(1). Base font size passed to ggplot2::theme_minimal(). Default 13.

Details

Bar heights are capped at the activity_cap_quantile quantile of non-zero epochs to prevent outlier activity bursts from compressing the visible range for the rest of the recording. A thin baseline stub (2 % of row height) is drawn for zero-activity epochs so that sleep periods and off-wrist blocks remain faintly visible.

Actigraphy activity counts are typically right-skewed, with occasional bursts far above the typical waking level. On the default linear scale this compresses most of the meaningful variation among low-to-moderate activity epochs into a thin sliver near the baseline. Set log_scale = TRUE to apply a log1p() transform (log(1 + x), so zero-activity epochs map to 0 rather than -Inf) before capping and normalising – this expands the low-activity range at the cost of visually compressing the difference between already-high activity bursts.

Value

A ggplot object.

See Also

plot_actogram(), plot_actogram_double(), actogram_colours()

Examples

## Not run: 
result <- run_pipeline("recordings/P001.txt", tz = "America/Sao_Paulo")
plot_actogram_activity(result)

# Cap at 95th percentile to highlight moderate activity
plot_actogram_activity(result, activity_cap_quantile = 0.95)

# Log scale: reveals structure among low-activity epochs that a linear
# scale would otherwise compress near the baseline
plot_actogram_activity(result, log_scale = TRUE)

## End(Not run)

Plot a double-plotted actogram

Description

The classic chronobiology double-plot format. Each recording day is drawn twice: in the left column of its own row (x = 00:00 to 24:00) and in the right column of the row above (x = 24:00 to 48:00). This means consecutive pairs of days share a row, making circadian phase drift visible as a diagonal band across rows.

Usage

plot_actogram_double(
  result,
  tz = NULL,
  title = NULL,
  colours = NULL,
  date_label_every = 7L,
  epoch_min = 1,
  base_size = 13
)

Arguments

result

A zeitr_result list (from run_pipeline() or run_pipeline_native()), or a tibble with at least datetime and state columns.

tz

character(1) or NULL. Timezone for date and time-of-day extraction. NULL (default) auto-detects from the timezone attribute embedded in the datetime POSIXct column; falls back to "UTC" when absent.

title

character(1) or NULL. Plot title. NULL constructs "Actogram \u2014 <subject_id>" from result$subject_id when available.

colours

Named character vector mapping state labels ("wake", "sleep", "nap", "off-wrist") to hex colours. NULL uses actogram_colours().

date_label_every

integer(1). Label every Nth row on the y-axis. Default 7 (weekly ticks).

epoch_min

numeric(1). Epoch duration in minutes. Used as the tile width in ggplot2::geom_tile(). Default 1 (ActTrust standard).

base_size

numeric(1). Base font size passed to ggplot2::theme_minimal(). Default 13.

Details

Row ii shows day ii on the left and day i+1i+1 on the right. Every day therefore appears twice in the plot (except the first, which has no left-column predecessor, and the last, which has no right-column successor). A dashed vertical line marks the 24 h boundary between the two columns.

Value

A ggplot object.

See Also

plot_actogram(), plot_actogram_activity(), actogram_colours()

Examples

## Not run: 
result <- run_pipeline("recordings/P001.txt", tz = "America/Sao_Paulo")
plot_actogram_double(result)

## End(Not run)

Prepare a raw actigraphy tibble for analysis

Description

Transforms the output of read_acttrust() (or any reader that returns a compatible tibble) into the working data frame expected by all detection functions. Specifically:

Usage

prepare_actigraphy(x)

Arguments

x

A tibble as returned by read_acttrust(), containing at minimum int_temp and ext_temp columns.

Details

  1. Clamps int_temp and ext_temp to the physiological range [0, 42] °C.

  2. Adds min-max scaled temperature columns int_temp_ and ext_temp_ (range [0, 1]) for plotting.

  3. Ensures state, offwrist, and sleep columns are present and initialised to 0.

The original tibble is never modified; a copy is returned.

Value

A tibble of the same dimensions as x with additional or updated columns: state, offwrist, sleep, int_temp_, ext_temp_.

Examples

## Not run: 
rec  <- read_acttrust("recordings/P001.txt")
prep <- prepare_actigraphy(rec)

## End(Not run)

Read an actigraphy file into a zeitr_recording object

Description

A device-agnostic wrapper that reads a raw actigraphy file and returns a zeitr_recording object with ⁠$epochs⁠ (a tidy tibble) and ⁠$metadata⁠ (a named list of device and recording information).

Usage

read_actigraphy(path, device = "acttrust", tz = "UTC", ...)

Arguments

path

character(1). Path to the raw actigraphy file.

device

character(1). Device type. One of "acttrust" (default) or "axivity". Additional devices will be added in future versions.

tz

character(1). Recording time zone passed to the underlying reader. Default is "UTC".

...

Additional arguments forwarded to the device-specific reader (e.g. encoding for read_acttrust()).

Details

Currently supported devices:

  • "acttrust" — Condor Instruments ActTrust / ActTrust2 (.txt)

  • "axivity" — Axivity AX3/AX6 (.cwa), via read_axivity() (requires the axR package; converts raw acceleration to epoch-level counts with compute_activity_counts() – see read_axivity()'s Details for validation caveats)

Value

A zeitr_recording S3 object — a named list with:

⁠$epochs⁠

A tibble with one row per epoch and columns datetime, activity, int_temp, ext_temp, ZCMn, state, offwrist, sleep.

⁠$metadata⁠

A named list with subject, device_id, device_model, firmware_version, interval_s, source_file, and participant_id (derived from the filename stem).

See Also

read_actigraphy_dir() to read a whole directory at once.

Examples

## Not run: 
rec <- read_actigraphy("recordings/P001.txt")
rec$epochs
rec$metadata

## End(Not run)

Read all actigraphy files in a directory

Description

Applies read_actigraphy() to every file matching pattern in folder. Returns a zeitr_study object — a named list of zeitr_recording objects, one per file. Files that fail to parse are skipped with a warning.

Usage

read_actigraphy_dir(
  folder,
  device = "acttrust",
  pattern = "*.txt",
  tz = "UTC",
  ...
)

Arguments

folder

character(1). Path to a directory containing actigraphy files.

device

character(1). Device type passed to read_actigraphy(). Default is "acttrust".

pattern

character(1). Glob pattern for file discovery. Default is "*.txt".

tz

character(1). Recording time zone. Default is "UTC".

...

Additional arguments forwarded to read_actigraphy().

Value

A zeitr_study S3 object — a named list of zeitr_recording objects. Names are participant IDs (filename stems).

See Also

study_summary() to summarise a zeitr_study.

Examples

## Not run: 
study <- read_actigraphy_dir("recordings/", tz = "America/Sao_Paulo")
study_summary(study)

## End(Not run)

Read a Condor Instruments ActTrust actigraphy file

Description

Parses a Condor ActTrust .txt export into a tidy tibble. The file format consists of a variable-length key-value header block followed by semicolon-delimited epoch rows. The header ends at the line beginning with DATE/TIME.

Usage

read_acttrust(path, tz = "UTC", encoding = "latin1")

Arguments

path

character(1) or fs::path. Path to the ActTrust .txt file.

tz

character(1). Time zone string passed to lubridate::parse_date_time(). Defaults to "UTC". Set to the local recording time zone for correct circadian alignment.

encoding

character(1). File encoding. Defaults to "latin1", which matches Condor's default export encoding.

Value

A tibble with one row per epoch and the following columns:

datetime

POSIXct — epoch timestamp.

activity

double — PIM activity count.

int_temp

double — internal (on-body) temperature, degC.

ext_temp

double — external (ambient) temperature, degC. NA if unavailable.

ZCMn

double — normalised zero-crossing mode count. NA if unavailable.

light

double — total light intensity (lux). NA if unavailable.

state

double — state column, initialised to 0.

offwrist

double — off-wrist indicator, initialised to 0.

sleep

double — sleep indicator, initialised to 0.

The tibble carries a "zeitr_acttrust" class and a metadata attribute (a named list with subject, device_id, device_model, firmware_version, interval_s, source_file).

Examples

## Not run: 
rec <- read_acttrust("recordings/P001.txt")
rec
attr(rec, "metadata")

## End(Not run)

Read an Axivity AX3/AX6 .cwa file into a zeitR-standard epoch tibble

Description

Bridges axR::axivity_read_cwa()'s raw per-sample output (triaxial acceleration at the device's native sampling rate) into the same epoch-level, 9-column shape read_acttrust() produces – so an Axivity recording can flow through the rest of zeitR (run_pipeline(), compute_npcra(), compute_sri(), etc.) exactly like an ActTrust one.

Usage

read_axivity(
  path,
  tz = "UTC",
  epoch_sec = 60,
  filter_low = 0.25,
  filter_high = 2.5,
  zcm_threshold = 0.01,
  tat_threshold = 0.05
)

Arguments

path

character(1). Path to a .cwa/AX6 file, forwarded to axR::axivity_read_cwa().

tz

character(1). Time zone the device's clock was set to. Default "UTC".

epoch_sec

numeric(1). Epoch length in seconds, forwarded to compute_activity_counts(). Default 60, matching the rest of zeitR.

filter_low, filter_high

numeric(1). Band-pass cutoffs in Hz, forwarded to compute_activity_counts(). Default 0.25/2.5 (GT3X+-style preset – see Details; no validated Axivity-specific preset exists).

zcm_threshold, tat_threshold

numeric(1). Forwarded to compute_activity_counts(). Defaults 0.01/0.05.

Value

A tibble with one row per epoch and the same columns as read_acttrust() (datetime, activity, int_temp, ext_temp, ZCMn, light, state, offwrist, sleep), plus one extra column not present there: TAT (time above threshold, seconds/epoch, from compute_activity_counts()). activity is PIM. ext_temp is always NA – Axivity devices have a single on-body temperature sensor, no separate ambient sensor. The tibble carries a "zeitr_axivity" class and a metadata attribute (a named list with device_id, session_id, sample_rate, epoch_sec, filter_low, filter_high, cwa_metadata (axR's raw device metadata string), source_file).

What this does (and doesn't) validate

The raw-to-counts conversion itself is compute_activity_counts() – already documented there as an unvalidated approximation of onboard device processing (no reference converter exists to check it against). Axivity devices additionally have no published or validated filter/threshold preset at all (unlike ActTrust and GT3X+, which at least have a documented processing description to approximate). The filter_low/filter_high defaults here (0.25/2.5 Hz) are compute_activity_counts()'s GT3X+-style preset, reused because it's the closer starting point of the two existing options for a research-grade wrist accelerometer like the AX3 – not because it has been checked against real Axivity output. Treat activity/ZCMn from this function as a rough approximation only; validate against a reference (e.g. GGIR) before relying on it for any published analysis.

ZCMn is compute_activity_counts()'s raw ZCM count with no additional normalisation applied – named ZCMn only for column-name compatibility with read_acttrust()'s CK-scoring input, not because a normalisation step has actually been performed.

Sampling rate

axivity_read_cwa() reports sample_rate per sample (block-level, as stored in the .cwa file). This function takes the single most common value across the whole recording and uses it for the entire conversion; if any samples report a different rate (a genuine rate change mid recording, or a corrupt block), a warning names the discrepancy but the dominant rate is still used throughout. Epoch boundaries and grouping for light/int_temp averaging are derived from this same dominant rate, matching compute_activity_counts()'s own epoch grouping exactly (including which trailing samples get dropped).

Time zone

axivity_read_cwa() tags timestamp as UTC by convention (the device's own real-time clock, not a true UTC source) – exactly like read_acttrust() does for ActTrust's DATE/TIME column. tz here re-labels the same clock reading under the recording's actual local time zone (via lubridate::force_tz(); no shift in wall-clock value), rather than converting it – set it to the time zone the device's clock was actually set to for correct circadian alignment downstream.

See Also

read_actigraphy() (device = "axivity"), compute_activity_counts() for the underlying raw-to-counts conversion and its validation caveats, read_acttrust() for the column shape this matches.

Examples

## Not run: 
rec <- read_axivity("recordings/P001.cwa", tz = "America/Sao_Paulo")
rec
attr(rec, "metadata")

## End(Not run)

Run the full actigraphy sleep analysis pipeline

Description

Orchestrates the complete pipeline for a single ActTrust recording:

Usage

run_pipeline(
  path,
  tz = "UTC",
  gap_s = 120,
  params = acttrust_params(),
  offwrist_args = list(),
  sleep_args = list(),
  nap_args = list(),
  quiet = FALSE
)

Arguments

path

character(1). Path to the ActTrust .txt file.

tz

character(1). Recording time zone. Passed to read_acttrust(). Default is "UTC".

gap_s

numeric(1). Gap threshold (seconds) for check_consistency(). Default is 120.

params

Device parameter preset, as returned by acttrust_params(). When supplied, values from params are used as defaults for each detector stage, with any explicit offwrist_args, sleep_args, or nap_args taking precedence. Defaults to acttrust_params().

offwrist_args

list. Additional arguments passed to detect_offwrist_bimodal(). Default is an empty list.

sleep_args

list. Additional arguments passed to detect_sleep_crespo(). Default is an empty list.

nap_args

list. Additional arguments passed to detect_naps_crespo(). Default is an empty list.

quiet

logical(1). If TRUE, suppresses the timestamp-issue warning emitted when check_consistency() finds problems. Useful in batch or testing contexts where the warning is expected. Default is FALSE.

Details

  1. Read — read_acttrust()

  2. Consistency check — check_consistency()

  3. Prepare — prepare_actigraphy()

  4. Off-wrist detection — detect_offwrist_bimodal()

  5. Main sleep period detection — detect_sleep_crespo()

  6. Nap detection — detect_naps_crespo()

  7. WASO + nightly statistics — compute_waso()

Value

A zeitr_result S3 object — a named list with:

subject_id

character — derived from the input filename stem.

source_file

character — absolute path to the input file.

data

tibble — final epoch-level data frame with all state columns populated.

nights

tibble — per-night sleep statistics.

issues

tibble — timestamp consistency issues (0 rows if none).

metadata

list — device and subject metadata from the file header.

See Also

run_pipeline_batch() for processing a directory of files.

Examples

## Not run: 
result <- run_pipeline("recordings/P001.txt", tz = "America/Sao_Paulo")
result$nights
result$data

## End(Not run)

Run the pipeline on all files in a directory

Description

Applies run_pipeline() to every file matching pattern in folder, returning a list of zeitr_result objects. Files that fail are skipped with a warning.

Usage

run_pipeline_batch(folder, pattern = "*.txt", parallel = FALSE, ...)

Arguments

folder

character(1). Path to a directory containing ActTrust files.

pattern

character(1). Glob pattern for file discovery. Default is "*.txt".

parallel

logical(1). If TRUE, processes files in parallel using future.apply::future_lapply() (a Suggests-only dependency). Requires a future::plan() to be set beforehand, e.g. future::plan(future::multisession(workers = 4)); falls back to sequential processing with a warning if future.apply is not installed. Default FALSE.

...

Additional arguments forwarded to run_pipeline().

Value

A named list of zeitr_result objects, one per successfully processed file. Names are the file stem (subject IDs).

Examples

## Not run: 
results <- run_pipeline_batch("recordings/", tz = "America/Sao_Paulo")
lapply(results, function(r) r$nights)

# Parallel: process a large cohort across 4 workers
future::plan(future::multisession(workers = 4))
results <- run_pipeline_batch("recordings/", tz = "America/Sao_Paulo",
                               parallel = TRUE)

## End(Not run)

Run the Vallim native actigraphy sleep analysis pipeline

Description

Orchestrates the vendor-independent (Vallim) sleep analysis pipeline for a single ActTrust recording. Shares the read / prepare / off-wrist / CSPD epoch-scorer stages with run_pipeline(), then replaces Condor's nights_df classification with the rule set developed by Julia Ribeiro da Silva Vallim and ported from her condor pipeline (Fixes 25, 26a/b/c, 27, 29).

Usage

run_pipeline_native(
  path,
  tz = "UTC",
  gap_s = 120,
  params = acttrust_params(),
  offwrist_args = list(),
  sleep_args = list(),
  classify_args = list(),
  holidays = NULL,
  free_days = c("Saturday", "Sunday"),
  quiet = FALSE
)

Arguments

path

character(1). Path to the ActTrust .txt file.

tz

character(1). Recording time zone. Default "UTC".

gap_s

numeric(1). Gap threshold (seconds) for check_consistency(). Default 120.

params

Device parameter preset, as returned by acttrust_params().

offwrist_args

list. Additional arguments for detect_offwrist_bimodal().

sleep_args

list. Additional arguments for detect_sleep_crespo().

classify_args

list. Additional arguments for classify_sleep_episodes().

holidays

Holidays to treat as free days in addition to the days in free_days. Accepts three forms, which can be mixed in the same vector:

  • Date objects or "YYYY-MM-DD" strings for year-specific dates (e.g. as.Date("2019-03-04") for one Carnival day).

  • "DD-MM" strings for dates that recur every year (e.g. "25-12" for Christmas, "07-09" for Brazilian Independence Day). Stored in result$holidays and auto-forwarded by the zeitr_result S3 methods of compute_sleep_metrics() and compute_cpd_metrics(). Default NULL.

free_days

A character vector of day names ("Monday" through "Sunday", case-insensitive) or ISO integers (1 = Monday ... 7 = Sunday) identifying which days of the week are unconditionally treated as free days. Stored in result$free_days and auto-forwarded by the zeitr_result S3 methods of compute_sleep_metrics() and compute_cpd_metrics(). Default c("Saturday", "Sunday").

quiet

logical(1). Suppress timestamp warnings. Default FALSE.

Details

Steps:

  1. Read – read_acttrust()

  2. Consistency check – check_consistency()

  3. Prepare – prepare_actigraphy()

  4. Off-wrist detection – detect_offwrist_bimodal()

  5. Epoch scoring – detect_sleep_crespo() (CSPD; same scorer as run_pipeline())

  6. Episode extraction – extract_sleep_episodes()

  7. Episode classification – classify_sleep_episodes() (JRSV rules)

  8. Fine-grained state – compute_waso() (Cole-Kripke within periods, for result$data)

Value

A zeitr_result S3 object with the same structure as run_pipeline(), except nights additionally contains:

sleep_type

character – "main" or "secondary".

bed_time, get_up_time

POSIXct – bed_time is the first sleep epoch, get_up_time is the first wake epoch (matches the Python reference's bts/gts convention exactly).

The is_nap column is retained for backwards compatibility (is_nap == (sleep_type == "secondary")).

See Also

run_pipeline() for the vendor (Condor) pipeline, extract_sleep_episodes(), classify_sleep_episodes()

Examples

## Not run: 
result <- run_pipeline_native("recordings/P001.txt", tz = "America/Sao_Paulo")
result$nights

## End(Not run)

Run the native pipeline on all files in a directory

Description

Applies run_pipeline_native() to every file matching pattern in folder, returning a named list of zeitr_result objects. Files that fail are skipped with a warning.

Usage

run_pipeline_native_batch(folder, pattern = "*.txt", parallel = FALSE, ...)

Arguments

folder

character(1). Path to a directory of ActTrust files.

pattern

character(1). Glob pattern. Default "*.txt".

parallel

logical(1). If TRUE, processes files in parallel using future.apply::future_lapply() (a Suggests-only dependency). Requires a future::plan() to be set beforehand, e.g. future::plan(future::multisession(workers = 4)); falls back to sequential processing with a warning if future.apply is not installed. Default FALSE.

...

Additional arguments forwarded to run_pipeline_native().

Value

A named list of zeitr_result objects.

Examples

## Not run: 
results <- run_pipeline_native_batch("recordings/", tz = "America/Sao_Paulo")
lapply(results, function(r) r$nights)

# Parallel: process a large cohort across 4 workers
future::plan(future::multisession(workers = 4))
results <- run_pipeline_native_batch("recordings/", tz = "America/Sao_Paulo",
                                     parallel = TRUE)

## End(Not run)

Score actigraphy epochs as wake or sleep using the Cole-Kripke algorithm

Description

Applies the Cole-Kripke algorithm to a vector of zero-crossing mode (ZCM) activity counts, scoring each epoch as wake (1) or sleep (0) using a weighted sum of activity in a surrounding window.

Usage

score_epochs_cole_kripke(
  zcm,
  P = 0.000464,
  weights_before = c(34.5, 133, 529, 375, 408, 400.5, 1074, 2048.5, 2424.5),
  weights_after = c(1920, 149.5, 257.5, 125, 111.5, 120, 69, 40.5)
)

Arguments

zcm

numeric vector of ZCM activity counts, one value per epoch.

P

numeric(1). Scaling factor. Default is 0.000464 (Cole et al., 1992).

weights_before

numeric(9). Weights applied to the 9 epochs before the current epoch. Defaults to the values from Cole et al. (1992), Table 2.

weights_after

numeric(8). Weights applied to the 8 epochs after the current epoch. Defaults to the values from Cole et al. (1992), Table 2.

Details

Each epoch's score is computed as:

Di=P∑j=19Wj−⋅Ai−j+P∑j=18Wj+⋅Ai+jD_i = P \sum_{j=1}^{9} W_j^{-} \cdot A_{i-j} + P \sum_{j=1}^{8} W_j^{+} \cdot A_{i+j}

where AiA_i is the ZCM count at epoch ii, W−W^{-} and W+W^{+} are the before and after weight vectors from Cole et al. (1992), and P=0.000464P = 0.000464. Epochs with Di≥1D_i \ge 1 are scored as wake.

Value

An integer vector the same length as zcm, with 1 indicating wake and 0 indicating sleep.

References

Cole, R. J., Kripke, D. F., Gruen, W., Mullaney, D. J., & Gillin, J. C. (1992). Automatic sleep/wake identification from wrist activity. Sleep, 15(5), 461–469. doi:10.1093/sleep/15.5.461

Examples

## Not run: 
rec    <- read_acttrust("recordings/P001.txt")
scores <- score_epochs_cole_kripke(rec$ZCMn)
table(scores)  # 0 = sleep, 1 = wake

## End(Not run)

Batch LIDS ultradian-rhythm summary across a study

Description

Computes compute_lids() for every participant in a batch of pipeline results and summarises each participant's quality-filtered bouts into a single row – the LIDS counterpart to study_sleep_metrics() and study_summary(), making per-participant median cycle length, amplitude, offset, and slope database-ready for tools like syncR::sync().

Usage

study_lids_metrics(results, min_bouts = 3, ...)

Arguments

results

A named list of zeitr_result objects, as returned by run_pipeline_batch() or run_pipeline_native_batch().

min_bouts

integer(1). Minimum number of quality-filtered bouts required for a participant's summary metrics to be reported (rather than NA, with n_bouts/n_bouts_passing still populated). Default 3.

...

Forwarded to compute_lids() for every participant.

Value

A tibble with one row per participant: participant_id, n_bouts, n_bouts_passing, and (computed only over passes_quality_filter == TRUE bouts, and only when there are at least min_bouts of them) period_min_median, amplitude_median, offset_median, slope_per_60min_median, plus an ⁠_iqr⁠ variant of each.

See Also

compute_lids(), study_sleep_metrics()

Examples

## Not run: 
results <- run_pipeline_native_batch("recordings/", tz = "America/Sao_Paulo")
study_lids_metrics(results)

## End(Not run)

Batch sleep-timing and chronotype metrics across a study

Description

Computes compute_sleep_metrics() and compute_cpd_metrics() for every participant in a batch of pipeline results and stacks them into a single tibble with one row per participant – the sleep-timing/chronotype counterpart to study_summary(), which covers NPCRA activity-rhythm variables instead.

Usage

study_sleep_metrics(
  results,
  min_tib_h = 5,
  min_tib_eve_h = 3,
  tz = "UTC",
  holidays = NULL,
  free_days = NULL
)

Arguments

results

A named list of zeitr_result objects, as returned by run_pipeline_batch() or run_pipeline_native_batch(). participant_id is taken from each result's own ⁠$subject_id⁠, falling back to the list name if that is unavailable.

min_tib_h

numeric(1). Minimum total in-bed time (hours) for a night to be included in compute_sleep_metrics(). Default 5.0.

min_tib_eve_h

numeric(1). Minimum TBT (hours) for a night to qualify as a free-day-eve night in compute_cpd_metrics(). Default 3.0.

tz

character(1). Time zone for extracting clock hours. Default "UTC".

holidays, free_days

Forwarded to both compute_sleep_metrics() and compute_cpd_metrics() for every participant. Default NULL for both, in which case each participant's own result$holidays/result$free_days (set when the pipeline was run) are used instead. Supplying either here overrides that per-participant default for the whole study.

Details

compute_sleep_metrics() and compute_cpd_metrics() each return a single named list per participant with no participant identifier, and there was previously no batch wrapper analogous to study_summary() for them. This is that wrapper – intended to make these chronobiological phenotyping metrics database-ready for tools like syncR::sync(), which expect one row per participant with a shared participant_id column across sources.

If either metric computation fails for a participant (e.g. no free days found, or no nights pass the min_tib_h filter), that participant's row is filled with NA for the affected metrics and a warning is emitted – the rest of the study is unaffected.

Value

A tibble with one row per participant: participant_id, all compute_sleep_metrics() columns (n_overall/n_wd/n_fd and the twelve sleep-timing metrics with ⁠_wd⁠/⁠_fd⁠ suffixes), and all compute_cpd_metrics() columns (n_nights_cpd, n_free_days, n_workdays, msw_h/msf_h/msfsc_h and their ⁠_hms⁠ forms, sjl_h, sjla_h, cpd_s/cpd_min/cpd_h).

See Also

study_summary() for the NPCRA (activity-rhythm) analogue, compute_sleep_metrics(), compute_cpd_metrics(), run_pipeline_native_batch()

Examples

## Not run: 
results <- run_pipeline_native_batch("recordings/", tz = "America/Sao_Paulo")
study_sleep_metrics(results)

# Feed straight into syncR::sync()
sync(zeit = study_sleep_metrics(results))

## End(Not run)

Summarise a zeitr_study across participants

Description

Computes per-participant summary statistics from a zeitr_study object (as returned by read_actigraphy_dir()). For each recording, the function computes NPCRA variables (IS, IV, RA, L5, M10) and basic recording quality metrics.

Usage

study_summary(study, epoch_s = NULL, L5_hours = 5, M10_hours = 10)

Arguments

study

A zeitr_study object as returned by read_actigraphy_dir(), or a named list of zeitr_recording objects.

epoch_s

numeric(1). Epoch duration in seconds. If NULL (default), estimated separately for each recording.

L5_hours

numeric(1). Width of the L5 window in hours. Default is 5.

M10_hours

numeric(1). Width of the M10 window in hours. Default is 10.

Value

A tibble with one row per participant and columns:

participant_id

Participant identifier (filename stem).

n_epochs

Total number of epochs in the recording.

n_days

Recording duration in days.

start

POSIXct — first epoch timestamp.

end

POSIXct — last epoch timestamp.

IS

Interdaily stability.

IV

Intradaily variability.

RA

Relative amplitude.

L5

Mean activity in the least active 5 h window.

L5_onset

Clock time of L5 midpoint.

M10

Mean activity in the most active 10 h window.

M10_onset

Clock time of M10 midpoint.

See Also

compute_npcra() for single-recording NPCRA, read_actigraphy_dir() to create a zeitr_study, study_sleep_metrics() for the sleep-timing/ chronotype analogue (CPD, MSF/MSW, social jetlag).

Examples

## Not run: 
study <- read_actigraphy_dir("recordings/", tz = "America/Sao_Paulo")
study_summary(study)

## End(Not run)