QT/QTc central tendency against the ICH E14 threshold, outlier scatter, and categorical summaries of participants crossing the 450/480/500 ms boundaries.
QT/QTc cardiac safety across three linked views. The chart below is
live — rendered by the safety.viz qtExplorer module on the
package’s example ECG data, with the full control sidebar.
placebo_arm is set, the
placebo-corrected change (ΔΔ) with confidence intervals against the ICH
E14 10 ms threshold of regulatory concern.The bundled adeg dataset uses the ADaM ADEG column names
the module expects (TEST, STRESN,
BASE, ARM, plus
VISIT/VISITNUM/ABLFL), so the
chart initializes with no settings at all; the settings below name the
placebo arm to unlock the ΔΔ view and add subgroup filters.
This is safety.viz QT Explorer Phase 1. QTcI, PR/QRS intervals, participant drill-down, and hysteresis plots are Phase 2 work, tracked upstream.
A note on the demo data. adeg comes
from the CDISC Pilot 01 ECG data, which — like plenty of real trial data
— is internally inconsistent: it collects RR and heart rate as separate
measurements, and the two contradict each other (they were generated
independently, so only 0.8% of readings agree). The QTc values here are
therefore cleaned at ingestion rather than taken from the source’s own
pre-derived parameters: QTcF = QT / (RR/1000)^(1/3) and
QTcB = QT / (RR/1000)^(1/2), correcting against the RR
implied by the recorded heart rate, which is the more credible of the
two inputs. See safety.viz#79
for the full working.
Even so, the pilot’s measured QT intervals are long for a resting population (median 444 ms), so QTcF centres near 468 ms and a majority of participants cross the 450 / 480 / 500 ms categories below. No correction formula can address that — it is a property of the synthetic source, not of the chart. The views exercise every threshold, but the crossing rates here should not be read as clinically typical.
The same chart ships as a gsm-idiom report workflow. Run it with
gsm.core::RunWorkflow() to produce a standalone HTML
report, or use the matching runner script:
lWorkflow <- yaml::read_yaml(
system.file("workflow", "3_reports", "qt_explorer.yaml", package = "gsm.safety")
)
gsm.core::RunWorkflow(
lWorkflow = lWorkflow,
lData = list(dfResults = ExampleData("adeg"))
)
Rscript inst/examples/qt-explorer.R <output-dir>
qt_explorer.yaml workflow
meta:
Type: Report
ID: qt_explorer
Output: html
Name: QT Safety Explorer Report
Description: Interactive safety.viz QT/QTc safety explorer with central tendency against the ICH E14 threshold, outlier scatter, and categorical summaries.
lSettings:
placebo_arm: Placebo
filters:
- value_col: SEX
label: Sex
- value_col: RACE
label: Race
- value_col: SITE
label: Site
spec:
dfResults:
USUBJID:
type: character
TEST:
type: character
STRESN:
type: numeric
BASE:
type: numeric
ARM:
type: character
steps:
- output: strOutputDir
name: getwd
- output: lWidget
name: gsm.safety::Widget_QtExplorer
params:
dfResults: dfResults
lSettings: lSettings
- output: strReportPath
name: gsm.safety::SaveWidgetReport
params:
widget: lWidget
strOutputDir: strOutputDir
strOutputFile: ID
library(gsm.safety)
dfResults <- ExampleData("adeg")
Widget_QtExplorer(
dfResults,
lSettings = list(
placebo_arm = "Placebo",
filters = list(
list(value_col = "SEX", label = "Sex"),
list(value_col = "RACE", label = "Race"),
list(value_col = "SITE", label = "Site")
)
)
)