library(PhysioDevices)
#> Loading required package: PhysioCoreThis vignette takes an Apple Health export (Health
app → profile → Export All Health Data →
export.xml, plus any ECG .csv files) all the
way through to per-modality analysis, using PhysioDevices
for ingestion and PhysioWearable / PhysioECG
for the analysis.
1. Ingest the export
ah <- readAppleHealth("apple_health_export/export.xml", tz = "Asia/Tokyo")
ah
appleHealthTypes(ah) # what modalities are present, with counts and spansreadAppleHealth() streams the file, so multi-hundred-MB
exports are fine. Every <Record> becomes a row (type,
source, unit, start/end, value); <Workout> elements
become ah$workouts.
2. Heart rate and heart-rate variability
The watch already stores HRV as
HeartRateVariabilitySDNN; heart rate is
HeartRate. For deeper HRV from the raw beats, use the
single-lead ECG (below).
hr <- appleHealthSeries(ah, "HeartRate") # bpm, irregular
hrv <- appleHealthSeries(ah, "HeartRateVariabilitySDNN") # ms (SDNN)
summary(hr$value_num)
summary(hrv$value_num)
# as a PhysioExperiment (irregular; timestamps in metadata) for the ecosystem
pe_hr <- appleHealthExperiment(ah, "HeartRate")3. Blood oxygen (SpO2)
ox <- appleHealthSeries(ah, "OxygenSaturation") # fraction 0-1
PhysioWearable::spo2Metrics(ox$value_num, time = ox$start)spo2Metrics() reports mean, nadir, time-below-90%
(T90/CT90) and an oxygen desaturation index (ODI). Note the Apple Watch
takes periodic spot readings, not a continuous trace,
so ODI/T90 are approximate unless you feed continuous overnight
oximetry.
4. Sleep
The watch scores sleep stages itself;
summarizeAppleSleep() turns them into per-night clinical
metrics (TST, efficiency, WASO, latency, stage minutes).
stages <- appleHealthSeries(ah, "SleepAnalysis") # AsleepCore/Deep/REM, Awake, InBed
PhysioWearable::summarizeAppleSleep(stages)If you instead have raw activity counts (from another wearable), score sleep/wake with the Cole-Kripke algorithm and summarise it:
sw <- PhysioWearable::coleKripke(counts) # counts = per-minute activity
PhysioWearable::summarizeSleep(sw)5. Activity
steps <- appleHealthSeries(ah, "StepCount")
energy <- appleHealthSeries(ah, "ActiveEnergyBurned")
tapply(steps$value_num, as.Date(steps$start), sum) # steps per day
ah$workouts # per-workout summariesFor epoch-level free-living metrics (ENMO, intensity gradient,
fragmentation, HAR → ICF), export raw accelerometry from a Core-Motion
app and use PhysioWearable::readAccelCSV() →
summarizeFreeLiving() (Apple Health itself stores only
derived activity, not raw acceleration).
6. ECG
Apple Watch ECG recordings export as one CSV each;
readAppleECG() reads the single-lead trace into a
PhysioExperiment that feeds PhysioECG:
ecg <- readAppleECG("ecg_2023-05-01.csv")
samplingRate(ecg) # ~512 Hz
# baseline-correct + Pan-Tompkins R-peaks, then HRV/rhythm, via PhysioECG
proc <- PhysioECG::ecgProcess(ecg)
PhysioECG::ecgAnalyze(proc) # time/freq/geometric HRV + rhythmScope and limitations
-
Raw accelerometry and ECG voltage are not in
export.xml— the ECG is a separate CSV (readAppleECG()); raw motion needs a Core-Motion capture app. - SpO2 is periodic spot data → ODI/T90 are approximate.
-
Sleep stages come from Apple’s own on-device
scoring;
coleKripke()is for raw-count actigraphy, not a re-scoring of Apple’s stages. - Timestamps carry the export’s timezone offset; set
tzto your local zone.