| Title: | Clinical Report Generation for the x-biosignal Ecosystem |
|---|---|
| Description: | Parameterized, bilingual (Japanese/English), colorblind-safe clinical reports (gait, HRV, EMG) with normative overlays and MDC/MCID-annotated change for the x-biosignal physiological signal ecosystem. |
| Authors: | Yusuke Matsui [aut, cre] |
| Maintainer: | Yusuke Matsui <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.1.2 |
| Built: | 2026-10-03 13:42:32 UTC |
| Source: | https://github.com/x-biosignal/PhysioReport |
Annotates a set of paired (pre, post) outcome scores against their Minimal
Detectable Change (MDC) and Minimal Clinically Important Difference (MCID)
thresholds, classifying each change as "no change" (within measurement
error), "detectable (>MDC)" (a real change that clears measurement
error) or "clinically meaningful (>MCID)". Thresholds are compared to
the absolute change and are boundary-inclusive (a change exactly equal to the
MDC is "detectable"). The direction of benefit is tracked
separately so decrease-is-good metrics (pain, timed tests) are handled
correctly.
annotateChange(pre, post, mdc, mcid, direction = "increase")annotateChange(pre, post, mdc, mcid, direction = "increase")
pre, post
|
Numeric vectors of pre- and post-intervention scores (one element per metric); may be named to label the metrics. |
mdc, mcid
|
Numeric MDC / MCID thresholds (positive), a scalar recycled
across metrics or a vector matching |
direction |
Benefit direction, |
A data.frame of class "change_annotation" with columns
metric, pre, post, change (signed
post - pre), improvement (signed so positive = beneficial),
mdc, mcid, exceeds_mdc, exceeds_mcid,
improved, direction and classification. A metric with a
missing pre or post yields NA change and classification:
missingness is propagated rather than silently reported as "no
change".
de Vet HCW et al. (2006). Minimally important change determined by a visual method. Qual Life Res; Shrout & Fleiss (1979) for the SEM underlying MDC.
plotChangeAnnotated(), PhysioExperiment::mdc()
annotateChange(pre = c(fma = 20, pain = 7), post = c(fma = 31, pain = 4), mdc = c(5.2, 1.0), mcid = c(9, 2), direction = c("increase", "decrease"))annotateChange(pre = c(fma = 20, pain = 7), post = c(fma = 31, pain = 4), mdc = c(5.2, 1.0), mcid = c(9, 2), direction = c("increase", "decrease"))
Converts a longitudinal-tracking result into the sessions / outcomes
data frames consumed by longitudinalTimeline.
as.timelineData(x, ...) ## Default S3 method: as.timelineData(x, ...) ## S3 method for class 'MSKLongitudinalTracker' as.timelineData(x, ...)as.timelineData(x, ...) ## Default S3 method: as.timelineData(x, ...) ## S3 method for class 'MSKLongitudinalTracker' as.timelineData(x, ...)
x |
A tracker object (e.g. a |
... |
Unused. |
A list with sessions and outcomes data frames.
# A tracker is any object carrying $metrics_table and $timepoint_labels; # build a minimal one directly so the adapter runs without PhysioMSKNet. tracker <- structure( list(metrics_table = data.frame( timepoint = rep(c("wk0", "wk6"), each = 2), muscle = rep(c("biceps", "triceps"), 2), metric_name = "rms", value = c(0.10, 0.08, 0.14, 0.12)), timepoint_labels = c("wk0", "wk6")), class = "MSKLongitudinalTracker") td <- as.timelineData(tracker) td$outcomes# A tracker is any object carrying $metrics_table and $timepoint_labels; # build a minimal one directly so the adapter runs without PhysioMSKNet. tracker <- structure( list(metrics_table = data.frame( timepoint = rep(c("wk0", "wk6"), each = 2), muscle = rep(c("biceps", "triceps"), 2), metric_name = "rms", value = c(0.10, 0.08, 0.14, 0.12)), timepoint_labels = c("wk0", "wk6")), class = "MSKLongitudinalTracker") td <- as.timelineData(tracker) td$outcomes
Renders a one-page clinical report to an editable Word document using
officer (with flextable when available) — no LaTeX
toolchain required. Called by renderClinicalReport for
format = "docx"; usable directly.
buildDocxReport( data, template = c("gait", "hrv", "emg"), lang = c("en", "ja"), out )buildDocxReport( data, template = c("gait", "hrv", "emg"), lang = c("en", "ja"), out )
data |
A report payload list (see |
template |
One of |
lang |
|
out |
Output |
out, invisibly.
# Writes an editable .docx via officer (a Suggests dependency) into tempdir(). if (requireNamespace("officer", quietly = TRUE)) { fig <- plotNormativeBand(NULL, NormativeModel( mean = sin(seq(0, pi, length.out = 101)), sd = rep(0.1, 101), time = 0:100)) out <- tempfile(fileext = ".docx") buildDocxReport(list(subject = "P01", figures = list(gait_cycle = fig)), template = "gait", out = out) }# Writes an editable .docx via officer (a Suggests dependency) into tempdir(). if (requireNamespace("officer", quietly = TRUE)) { fig <- plotNormativeBand(NULL, NormativeModel( mean = sin(seq(0, pi, length.out = 101)), sd = rep(0.1, 101), time = 0:100)) out <- tempfile(fileext = ".docx") buildDocxReport(list(subject = "P01", figures = list(gait_cycle = fig)), template = "gait", out = out) }
Composes a multi-panel clinical gait report: one waveform panel per gait
variable (a colorblind-safe normative SD corridor from
plotNormativeBand overlaid with the subject's Left and Right
mean traces and optional event markers), followed by a gait-index dashboard
(GDI / GPS / Movement Analysis Profile). Panels are laid out with
patchwork.
clinicalGaitReport( pe, norm, events = NULL, sides = c("L", "R"), indices = TRUE )clinicalGaitReport( pe, norm, events = NULL, sides = c("L", "R"), indices = TRUE )
pe |
A named list of per-variable side-split cycle waveforms:
|
norm |
A |
events |
Optional numeric vector of gait-cycle percentages at which to draw event markers (e.g. toe-off) on every waveform panel. |
sides |
Which sides to overlay (currently informational; both Left and Right are drawn). |
indices |
Logical; if |
A patchwork composite (a ggplot-compatible object).
gaitIndexDashboard(), plotNormativeBand(),
PhysioMoCap::plotSymmetry()
# clinicalGaitReport() composes panels with patchwork (a Suggests dependency); # indices = FALSE keeps the gait-index panel (and PhysioMoCap) out of the way. if (requireNamespace("patchwork", quietly = TRUE)) { np <- 101 vars <- c("knee", "hip") ref <- sin(seq(0, pi, length.out = np)) norm <- list( variables = vars, mean = matrix(rep(ref, 2), nrow = 2, byrow = TRUE, dimnames = list(vars, NULL)), sd = matrix(0.1, nrow = 2, ncol = np, dimnames = list(vars, NULL)), percent = seq(0, 100, length.out = np)) pe <- list( knee = list(left = ref + 0.05, right = ref - 0.05), hip = list(left = ref, right = ref)) clinicalGaitReport(pe, norm, indices = FALSE) }# clinicalGaitReport() composes panels with patchwork (a Suggests dependency); # indices = FALSE keeps the gait-index panel (and PhysioMoCap) out of the way. if (requireNamespace("patchwork", quietly = TRUE)) { np <- 101 vars <- c("knee", "hip") ref <- sin(seq(0, pi, length.out = np)) norm <- list( variables = vars, mean = matrix(rep(ref, 2), nrow = 2, byrow = TRUE, dimnames = list(vars, NULL)), sd = matrix(0.1, nrow = 2, ncol = np, dimnames = list(vars, NULL)), percent = seq(0, 100, length.out = np)) pe <- list( knee = list(left = ref + 0.05, right = ref - 0.05), hip = list(left = ref, right = ref)) clinicalGaitReport(pe, norm, indices = FALSE) }
A single bar panel summarising the gait indices for a clinical gait report: one bar per Gait Variable Score (the Movement Analysis Profile) plus the overall Gait Profile Score, coloured by deviation magnitude, with the Gait Deviation Index reported in the subtitle (GDI 100 = normative mean, each 10 points is one standard deviation, lower = more deviation).
gaitIndexDashboard(gdi, gps, map)gaitIndexDashboard(gdi, gps, map)
gdi |
A |
gps |
A numeric Gait Profile Score, or a
|
map |
A |
A ggplot object.
gaitIndexDashboard(gdi = 82, gps = 8.4, map = c(pelvis = 3, hip = 9, knee = 12, ankle = 6))gaitIndexDashboard(gdi = 82, gps = 8.4, map = c(pelvis = 3, hip = 9, knee = 12, ankle = 6))
Draws a longitudinal rehabilitation timeline: each outcome measure is a
stacked lane showing its trajectory across sessions, intervention periods are
shaded spans across all lanes, and points where the session-to-session change
crosses the Minimal Detectable Change / Minimal Clinically Important Difference
are marked (via annotateChange).
longitudinalTimeline( sessions, interventions = NULL, outcomes, mdc = NULL, mcid = NULL )longitudinalTimeline( sessions, interventions = NULL, outcomes, mdc = NULL, mcid = NULL )
sessions |
A |
interventions |
Optional |
outcomes |
A long |
mdc, mcid
|
Optional MDC / MCID thresholds used to flag crossings: a scalar applied to all metrics, or a value named by metric. Both must be supplied to draw crossing markers. |
A ggplot object (faceted, one lane per metric).
annotateChange(), as.timelineData()
sessions <- data.frame(time = 1:5) outcomes <- data.frame(time = rep(1:5, 2), metric = rep(c("gait_speed", "fma"), each = 5), value = c(0.6, 0.7, 0.9, 1.0, 1.1, 20, 22, 28, 30, 33)) longitudinalTimeline(sessions, outcomes = outcomes, mdc = c(gait_speed = 0.1, fma = 5), mcid = c(gait_speed = 0.15, fma = 9))sessions <- data.frame(time = 1:5) outcomes <- data.frame(time = rep(1:5, 2), metric = rep(c("gait_speed", "fma"), each = 5), value = c(0.6, 0.7, 0.9, 1.0, 1.1, 20, 22, 28, 30, 33)) longitudinalTimeline(sessions, outcomes = outcomes, mdc = c(gait_speed = 0.1, fma = 5), mcid = c(gait_speed = 0.15, fma = 9))
Builds a reusable description of a normative reference — the expected
mean and sd of a measurement, optionally stratified by covariates
such as age, sex or task. A model can be
scalar (one mean/sd), a waveform (per-timepoint mean/sd vectors,
e.g. a 101-point gait-cycle corridor), a stratified table
(a data.frame with the strata columns plus mean/sd and an
optional time column), or a lookup function
function(age, sex, task) returning a list(mean, sd, time). The
table/function forms let a normative database drive the model; when no database
is available an inline scalar/waveform/table spec degrades gracefully to the
same interface.
NormativeModel( mean, sd = NULL, by = c("age", "sex", "task"), source = NULL, time = NULL )NormativeModel( mean, sd = NULL, by = c("age", "sex", "task"), source = NULL, time = NULL )
mean |
Numeric scalar or vector (the reference mean / waveform), OR a
|
sd |
Numeric scalar or vector of the same length as |
by |
Character vector naming the stratification columns/arguments
(default |
source |
Optional character string describing the normative source (citation), used as the default plot title. |
time |
Optional numeric vector (same length as |
An object of class "NormativeModel".
normativeZScore(), plotNormativeBand()
# scalar norm (e.g. comfortable gait speed for a stratum) m <- NormativeModel(mean = 1.30, sd = 0.15, source = "Bohannon 1997") normativeZScore(1.00, m) # waveform norm (101-point knee-flexion corridor) wf <- NormativeModel(mean = sin(seq(0, pi, length.out = 101)), sd = rep(0.1, 101), time = 0:100)# scalar norm (e.g. comfortable gait speed for a stratum) m <- NormativeModel(mean = 1.30, sd = 0.15, source = "Bohannon 1997") normativeZScore(1.00, m) # waveform norm (101-point knee-flexion corridor) wf <- NormativeModel(mean = sin(seq(0, pi, length.out = 101)), sd = rep(0.1, 101), time = 0:100)
Standardizes an observed value (or waveform) against a
NormativeModel: , element-wise. A
scalar model recycles across a vector of observations; a waveform model
returns a per-timepoint z of the same length as the observation.
normativeZScore(value, model, age = NULL, sex = NULL, task = NULL)normativeZScore(value, model, age = NULL, sex = NULL, task = NULL)
value |
Numeric observation. A scalar, a vector of scalars (scored against a scalar model), or a waveform matching a waveform model's length. |
model |
|
age, sex, task
|
Optional strata forwarded to the model to select the applicable norm. |
Numeric z-score(s), matching the length of value (or the
waveform length).
NormativeModel(), plotNormativeBand()
m <- NormativeModel(mean = 1.30, sd = 0.15) normativeZScore(c(1.00, 1.30, 1.60), m)m <- NormativeModel(mean = 1.30, sd = 0.15) normativeZScore(c(1.00, 1.30, 1.60), m)
Resolves a report label key to its localized string. Unknown keys fall back
to the key itself, so partial dictionaries never break a report. Labels live
in inst/extdata/labels.csv (UTF-8), keeping the R source ASCII.
physioLabel(key, lang = c("en", "ja"))physioLabel(key, lang = c("en", "ja"))
key |
Character label key (e.g. |
lang |
Language: |
The localized label string.
physioLabel("bias") physioLabel("bias", "ja")physioLabel("bias") physioLabel("bias", "ja")
Draws a Bland-Altman plot (mean vs. difference of paired measurements) with
bias and limit-of-agreement reference lines. The statistics come from
PhysioExperiment::blandAltman(), so the plotted lines exactly match the
computed bias and limits of agreement.
plotBlandAltman(x, y, confidence = 0.95)plotBlandAltman(x, y, confidence = 0.95)
x, y
|
Numeric vectors of paired measurements (e.g. two methods, or baseline vs follow-up). |
confidence |
Confidence level for the limits of agreement (default 0.95). |
A ggplot object.
plotBlandAltman(c(1, 2, 3, 4, 5), c(1.1, 1.9, 3.2, 3.8, 5.1))plotBlandAltman(c(1, 2, 3, 4, 5), c(1.1, 1.9, 3.2, 3.8, 5.1))
Draws one row per metric showing the signed change as a point, with the
MDC and MCID thresholds as symmetric reference bands around
the no-change line, coloured by the change classification.
plotChangeAnnotated(changes)plotChangeAnnotated(changes)
changes |
A |
A ggplot object.
ch <- annotateChange(c(fma = 20, pain = 7), c(fma = 31, pain = 4), mdc = c(5.2, 1.0), mcid = c(9, 2), direction = c("increase", "decrease")) plotChangeAnnotated(ch)ch <- annotateChange(c(fma = 20, pain = 7), c(fma = 31, pain = 4), mdc = c(5.2, 1.0), mcid = c(9, 2), direction = c("increase", "decrease")) plotChangeAnnotated(ch)
Draws a normative corridor as graded SD and SD ribbons
around the reference median, overlays the observed trajectory, and annotates
the z-score. Uses the ecosystem colorblind-safe palette and report theme.
plotNormativeBand( observed = NULL, model, bands = c(1, 2), time_axis = NULL, annotate_z = TRUE, age = NULL, sex = NULL, task = NULL, ... )plotNormativeBand( observed = NULL, model, bands = c(1, 2), time_axis = NULL, annotate_z = TRUE, age = NULL, sex = NULL, task = NULL, ... )
observed |
Numeric observation (a waveform, or a scalar broadcast across
the corridor). |
model |
|
bands |
Numeric vector of SD multiples to shade (default
|
time_axis |
Optional numeric x-axis; defaults to the model's |
annotate_z |
Logical; if |
age, sex, task
|
Optional strata forwarded to the model. |
... |
Currently unused (reserved for future styling arguments). |
A ggplot object.
NormativeModel(), normativeZScore()
set.seed(1) wf <- NormativeModel(mean = sin(seq(0, pi, length.out = 101)), sd = rep(0.1, 101), time = 0:100) obs <- sin(seq(0, pi, length.out = 101)) + rnorm(101, 0, 0.05) plotNormativeBand(obs, wf)set.seed(1) wf <- NormativeModel(mean = sin(seq(0, pi, length.out = 101)), sd = rep(0.1, 101), time = 0:100) obs <- sin(seq(0, pi, length.out = 101)) + rnorm(101, 0, 0.05) plotNormativeBand(obs, wf)
Plots a rehabilitation outcome profile across domains as a radar chart, with an optional normative reference polygon and multi-timepoint overlay. Uses the ecosystem colorblind-safe palette and report theme.
plotOutcomeRadar( profile, reference = NULL, normalize = c("percent", "z"), domains = NULL )plotOutcomeRadar( profile, reference = NULL, normalize = c("percent", "z"), domains = NULL )
profile |
Domain scores: a named numeric vector (one timepoint), a
matrix or data.frame with domains as columns and timepoints as rows, or a
|
reference |
Optional normative reference: a named numeric of per-domain
means (for |
normalize |
|
domains |
Optional character vector selecting and ordering the domains. |
A ggplot object (a coord_polar radar).
plotOutcomeRadar(c(pain = 3, balance = 7, gait = 6, strength = 5))plotOutcomeRadar(c(pain = 3, balance = 7, gait = 6, strength = 5))
Plots a single-subject time series with SCED-standard overlays: shaded phase
panels, phase-change reference lines, per-phase mean lines, the baseline
2SD band, and per-phase split-middle celeration (trend) lines.
plotSCED(data, phase, overlays = c("mean", "2SD", "celeration"))plotSCED(data, phase, overlays = c("mean", "2SD", "celeration"))
data |
Numeric vector of observations, in session order. |
phase |
Vector of phase labels (e.g. |
overlays |
Which overlays to draw: any of |
A ggplot object.
Kratochwill TR et al. (2010). Single-case designs technical documentation (What Works Clearinghouse).
plotSCED(c(2, 3, 2, 3, 6, 7, 8, 7), rep(c("A", "B"), each = 4))plotSCED(c(2, 3, 2, 3, 6, 7, 8, 7), rep(c("A", "B"), each = 4))
Renders a one-page, bilingual, colorblind-safe clinical report for a gait,
HRV or EMG assessment. PDF and HTML are produced from a parameterized Quarto
template (quarto + the Quarto CLI); DOCX is produced by an editable
officer path that needs no LaTeX toolchain
(buildDocxReport).
renderClinicalReport( data, template = c("gait", "hrv", "emg"), format = c("pdf", "docx", "html"), lang = c("en", "ja"), out = NULL )renderClinicalReport( data, template = c("gait", "hrv", "emg"), format = c("pdf", "docx", "html"), lang = c("en", "ja"), out = NULL )
data |
A report payload list. Recognized elements: |
template |
Report template: |
format |
Output format: |
lang |
|
out |
Output file path; defaults to a temp file named for the template and format. |
The output path, invisibly.
buildDocxReport(), clinicalGaitReport(), annotateChange()
# The LaTeX-free docx path needs only officer (a Suggests dependency); the # pdf / html paths additionally need the quarto package and the Quarto CLI. if (requireNamespace("officer", quietly = TRUE)) { fig <- plotNormativeBand(NULL, NormativeModel(sin(seq(0, pi, length.out = 101)), rep(0.1, 101), time = 0:100)) renderClinicalReport(list(subject = "P01", figures = list(gait_cycle = fig)), template = "gait", format = "docx", lang = "ja") }# The LaTeX-free docx path needs only officer (a Suggests dependency); the # pdf / html paths additionally need the quarto package and the Quarto CLI. if (requireNamespace("officer", quietly = TRUE)) { fig <- plotNormativeBand(NULL, NormativeModel(sin(seq(0, pi, length.out = 101)), rep(0.1, 101), time = 0:100)) renderClinicalReport(list(subject = "P01", figures = list(gait_cycle = fig)), template = "gait", format = "docx", lang = "ja") }
Thin wrapper returning the shared colorblind-safe ggplot2 theme from PhysioExperiment, so every report uses consistent, accessible styling.
reportTheme()reportTheme()
A ggplot2 theme (see PhysioExperiment::theme_physio).
reportTheme()reportTheme()
Summarises the nonoverlap / trend effect sizes for a two-phase (A/B) single-
case series, delegating to the peer-reviewed estimators in
PhysioClinStats: the Percentage of Non-overlapping Data (as a
percentage), the Nonoverlap of All Pairs, and the baseline-trend-corrected
Tau-U in its bounded tau-b ("scan") form.
scedStats(data, phase, improvement = c("increase", "decrease"))scedStats(data, phase, improvement = c("increase", "decrease"))
data |
Numeric vector of observations, in session order. |
phase |
Vector of phase labels the same length as |
improvement |
|
A data.frame of class "sced_stats" with columns
metric and estimate (PND in [0, 100], NAP in [0, 1], Tau-U
in [-1, 1]).
Parker RI, Vannest KJ (2011). Tau-U. Behavior Therapy; Scruggs et al. (1987) PND; Parker & Vannest (2009) NAP.
plotSCED(), PhysioClinStats::scedTauU()
# scedStats() delegates to PhysioClinStats (a Suggests dependency) if (requireNamespace("PhysioClinStats", quietly = TRUE)) { scedStats(c(2, 3, 2, 3, 6, 7, 8, 7), rep(c("A", "B"), each = 4)) }# scedStats() delegates to PhysioClinStats (a Suggests dependency) if (requireNamespace("PhysioClinStats", quietly = TRUE)) { scedStats(c(2, 3, 2, 3, 6, 7, 8, 7), rep(c("A", "B"), each = 4)) }