| Title: | Wearable and Lab-System Device Ingestion for PhysioExperiment Objects |
|---|---|
| Description: | Readers that ingest consumer-wearable and wired laboratory device exports into PhysioExperiment / MultiRatePhysioExperiment objects. Wearables: Empatica E4 CSV sessions and EmbracePlus Avro containers (multi-rate ACC / BVP / EDA / TEMP / HR / IBI streams), Shimmer GSR3+ Consensys exports (GSR, PPG, accelerometer), and Polar heart-rate / R-R interval exports. Lab systems: BIOPAC AcqKnowledge '.acq' files (per-channel rates and markers), Delsys Trigno EMG + IMU exports (multi-rate streams with per-sensor metadata), and Xsens MVNX motion-capture XML (segment kinematics, joint angles, centre of mass). Each import records a provenance entry and the correct per-signal sampling rates. |
| Authors: | Yusuke Matsui [aut, cre] |
| Maintainer: | Yusuke Matsui <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.9.6 |
| Built: | 2026-10-03 13:43:26 UTC |
| Source: | https://github.com/x-biosignal/PhysioDevices |
Apple Health samples are irregularly spaced, so the returned experiment is
event-like: a single channel holding the values, samplingRate NA, and the
per-sample timestamps stored in metadata(pe)$times.
appleHealthExperiment(x, type)appleHealthExperiment(x, type)
x |
An |
type |
A numeric record type (e.g. |
A PhysioExperiment with one channel of the modality's values.
rec <- function(v, t) sprintf(paste0( '<Record type="HKQuantityTypeIdentifierHeartRate" sourceName="Watch" ', 'unit="count/min" startDate="%s" endDate="%s" value="%d"/>'), t, t, v) p <- tempfile(fileext = ".xml") writeLines(c("<HealthData>", rec(61, "2023-05-01 07:00:00 +0000"), rec(64, "2023-05-01 07:01:00 +0000"), "</HealthData>"), p) pe <- appleHealthExperiment(readAppleHealth(p), "HeartRate") dim(pe)rec <- function(v, t) sprintf(paste0( '<Record type="HKQuantityTypeIdentifierHeartRate" sourceName="Watch" ', 'unit="count/min" startDate="%s" endDate="%s" value="%d"/>'), t, t, v) p <- tempfile(fileext = ".xml") writeLines(c("<HealthData>", rec(61, "2023-05-01 07:00:00 +0000"), rec(64, "2023-05-01 07:01:00 +0000"), "</HealthData>"), p) pe <- appleHealthExperiment(readAppleHealth(p), "HeartRate") dim(pe)
Extract one modality's samples from an Apple Health export
appleHealthSeries(x, type)appleHealthSeries(x, type)
x |
An |
type |
A (prefix-stripped) record type, e.g. |
A data frame of that type's records (start, end, value,
value_num, unit, source), time-ordered.
appleHealthTypes(), appleHealthExperiment()
rec <- function(v, t) sprintf(paste0( '<Record type="HKQuantityTypeIdentifierHeartRate" sourceName="Watch" ', 'unit="count/min" startDate="%s" endDate="%s" value="%d"/>'), t, t, v) p <- tempfile(fileext = ".xml") writeLines(c("<HealthData>", rec(61, "2023-05-01 07:00:00 +0000"), rec(64, "2023-05-01 07:01:00 +0000"), "</HealthData>"), p) appleHealthSeries(readAppleHealth(p), "HeartRate")rec <- function(v, t) sprintf(paste0( '<Record type="HKQuantityTypeIdentifierHeartRate" sourceName="Watch" ', 'unit="count/min" startDate="%s" endDate="%s" value="%d"/>'), t, t, v) p <- tempfile(fileext = ".xml") writeLines(c("<HealthData>", rec(61, "2023-05-01 07:00:00 +0000"), rec(64, "2023-05-01 07:01:00 +0000"), "</HealthData>"), p) appleHealthSeries(readAppleHealth(p), "HeartRate")
Summarise the record types in an Apple Health export
appleHealthTypes(x)appleHealthTypes(x)
x |
An |
A data frame with one row per type: n, unit, and the time from/to.
readAppleHealth(), appleHealthSeries()
rec <- function(v, t) sprintf(paste0( '<Record type="HKQuantityTypeIdentifierHeartRate" sourceName="Watch" ', 'unit="count/min" startDate="%s" endDate="%s" value="%d"/>'), t, t, v) p <- tempfile(fileext = ".xml") writeLines(c("<HealthData>", rec(61, "2023-05-01 07:00:00 +0000"), rec(64, "2023-05-01 07:01:00 +0000"), "</HealthData>"), p) appleHealthTypes(readAppleHealth(p))rec <- function(v, t) sprintf(paste0( '<Record type="HKQuantityTypeIdentifierHeartRate" sourceName="Watch" ', 'unit="count/min" startDate="%s" endDate="%s" value="%d"/>'), t, t, v) p <- tempfile(fileext = ".xml") writeLines(c("<HealthData>", rec(61, "2023-05-01 07:00:00 +0000"), rec(64, "2023-05-01 07:01:00 +0000"), "</HealthData>"), p) appleHealthTypes(readAppleHealth(p))
Extract one modality from a Fitbit archive
fitbitSeries(x, modality)fitbitSeries(x, modality)
x |
A |
modality |
One of the modality names present in |
The modality's data frame.
# readFitbit() guards on the jsonlite package (a Suggests dependency). if (requireNamespace("jsonlite", quietly = TRUE)) { d <- tempfile("fitbit") dir.create(d) writeLines(paste0('[{"dateTime":"05/01/23 07:00:00","value":{"bpm":61}},', '{"dateTime":"05/01/23 07:01:00","value":{"bpm":64}}]'), file.path(d, "heart_rate.json")) fb <- readFitbit(d, what = "heart_rate") fitbitSeries(fb, "heart_rate") }# readFitbit() guards on the jsonlite package (a Suggests dependency). if (requireNamespace("jsonlite", quietly = TRUE)) { d <- tempfile("fitbit") dir.create(d) writeLines(paste0('[{"dateTime":"05/01/23 07:00:00","value":{"bpm":61}},', '{"dateTime":"05/01/23 07:01:00","value":{"bpm":64}}]'), file.path(d, "heart_rate.json")) fb <- readFitbit(d, what = "heart_rate") fitbitSeries(fb, "heart_rate") }
Is an Avro backend available?
hasAvroBackend()hasAvroBackend()
TRUE when the Python fastavro module is reachable via
reticulate.
hasAvroBackend()hasAvroBackend()
Is a BIOPAC (bioread) backend available?
hasBioread()hasBioread()
TRUE when the Python bioread module is reachable via
reticulate.
hasBioread()hasBioread()
Parses a Philips/Respironics Actiwatch .awd file into per-epoch activity
counts with timestamps. The start date/time is read from the header; the
activity counts are the longest run of numeric lines (the data block).
readActiwatch(path, epoch = 60, tz = "UTC")readActiwatch(path, epoch = 60, tz = "UTC")
path |
Path to a |
epoch |
Epoch length in seconds (default 60). The |
tz |
Time zone for the timestamps (default |
A data frame with time (POSIXct) and activity (counts), with the
epoch length in attr(, "epoch_sec"). Feeds
PhysioWearable::coleKripke() (which expects 1-minute epochs).
Philips Respironics Actiwatch / Actiware AWD format.
# An .awd is a short text header (date on line 2, time on line 3) then one # activity count per line; write a tiny one and read it back (base-R parser). p <- tempfile(fileext = ".awd") writeLines(c("SUBJECT01", "01-May-2023", "23:00", "V", "M", "A", "W", as.character(c(12, 0, 5, 40, 3, 0, 0, 8, 22, 15, 7, 2, 0, 0, 33))), p) aw <- readActiwatch(p, epoch = 60) head(aw)# An .awd is a short text header (date on line 2, time on line 3) then one # activity count per line; write a tiny one and read it back (base-R parser). p <- tempfile(fileext = ".awd") writeLines(c("SUBJECT01", "01-May-2023", "23:00", "V", "M", "A", "W", as.character(c(12, 0, 5, 40, 3, 0, 0, 8, 22, 15, 7, 2, 0, 0, 33))), p) aw <- readActiwatch(p, epoch = 60) head(aw)
Parses the single-lead ECG CSV that the Apple Watch ECG app exports into a
PhysioExperiment (one ECG channel). The classification, recorded date and
sample rate from the file header are kept in metadata(). The result feeds
the PhysioECG beat-detection and heart-rate-variability functions.
readAppleECG(path, default_rate = 512)readAppleECG(path, default_rate = 512)
path |
Path to an Apple Watch ECG |
default_rate |
Sample rate (Hz) to assume if the header has none (default 512, the Apple Watch ECG rate). |
A PhysioExperiment with a raw assay of the ECG voltage
(microvolts) and samplingRate set from the header.
Apple Inc. Taking an ECG with the ECG app on Apple Watch.
# The ECG app writes a "key,value" header then one voltage sample per line; # write a tiny export and read it back (base-R parser, no toolchain needed). p <- tempfile(fileext = ".csv") writeLines(c("Name,Jane Doe", "Sample Rate,512 Hz", "Unit,uV", "Classification,Sinus Rhythm", format(round(sin(seq(0, 20 * pi, length.out = 1024)) * 300, 2))), p) ecg <- readAppleECG(p) samplingRate(ecg)# The ECG app writes a "key,value" header then one voltage sample per line; # write a tiny export and read it back (base-R parser, no toolchain needed). p <- tempfile(fileext = ".csv") writeLines(c("Name,Jane Doe", "Sample Rate,512 Hz", "Unit,uV", "Classification,Sinus Rhythm", format(round(sin(seq(0, 20 * pi, length.out = 1024)) * 300, 2))), p) ecg <- readAppleECG(p) samplingRate(ecg)
Parses an Apple Health export.xml into a tidy set of records and workout
summaries. Every sample Apple stores as a <Record> – heart rate, heart-rate
variability (HeartRateVariabilitySDNN), blood oxygen (OxygenSaturation),
respiratory rate, step count, active/basal energy, resting and walking heart
rate, VO2 max, and SleepAnalysis sleep stages – becomes one row; <Workout>
elements become the workouts table. The reader streams the file, so multi-
hundred-MB exports are handled without loading the whole document into memory.
readAppleHealth(path, types = NULL, tz = "UTC")readAppleHealth(path, types = NULL, tz = "UTC")
path |
Path to the Apple Health |
types |
Optional character vector of (prefix-stripped) record types to
keep; |
tz |
Time zone for the parsed timestamps (default |
The Apple type prefix (HKQuantityTypeIdentifier / HKCategoryTypeIdentifier)
is stripped, so type reads e.g. "HeartRate", "OxygenSaturation",
"SleepAnalysis". Numeric samples are parsed into value_num; category
samples (sleep stages) keep their (prefix-stripped) label in value.
An apple_health object: a list with records (a data frame with
type, source, unit, start, end, value, value_num), workouts
(or NULL), path and tz.
Apple Inc. HealthKit data types and the Health app XML export.
appleHealthSeries(), appleHealthTypes(), appleHealthExperiment(),
readAppleECG()
# Apple exports each sample as a self-closing <Record> line; write a tiny # export.xml and read it back (the parser is base-R, so no toolchain is needed). p <- tempfile(fileext = ".xml") writeLines(c( '<?xml version="1.0" encoding="UTF-8"?>', '<HealthData locale="en_US">', paste0('<Record type="HKQuantityTypeIdentifierHeartRate" sourceName="Watch"', ' unit="count/min" startDate="2023-05-01 07:00:00 +0000"', ' endDate="2023-05-01 07:00:00 +0000" value="61"/>'), paste0('<Record type="HKQuantityTypeIdentifierHeartRate" sourceName="Watch"', ' unit="count/min" startDate="2023-05-01 07:01:00 +0000"', ' endDate="2023-05-01 07:01:00 +0000" value="64"/>'), '</HealthData>'), p) ah <- readAppleHealth(p) appleHealthTypes(ah) appleHealthSeries(ah, "HeartRate")# Apple exports each sample as a self-closing <Record> line; write a tiny # export.xml and read it back (the parser is base-R, so no toolchain is needed). p <- tempfile(fileext = ".xml") writeLines(c( '<?xml version="1.0" encoding="UTF-8"?>', '<HealthData locale="en_US">', paste0('<Record type="HKQuantityTypeIdentifierHeartRate" sourceName="Watch"', ' unit="count/min" startDate="2023-05-01 07:00:00 +0000"', ' endDate="2023-05-01 07:00:00 +0000" value="61"/>'), paste0('<Record type="HKQuantityTypeIdentifierHeartRate" sourceName="Watch"', ' unit="count/min" startDate="2023-05-01 07:01:00 +0000"', ' endDate="2023-05-01 07:01:00 +0000" value="64"/>'), '</HealthData>'), p) ah <- readAppleHealth(p) appleHealthTypes(ah) appleHealthSeries(ah, "HeartRate")
Parses a BIOPAC AcqKnowledge .acq file (via the Python bioread module
through reticulate; see hasBioread()) into a
PhysioExperiment::MultiPhysioExperiment. Each acquisition channel becomes a
stream at its own sample rate (channels may be acquired at different rates via
frequency dividers), carrying its label and unit. Event markers become
PhysioExperiment::PhysioEvents on the fastest stream.
readBIOPAC(path)readBIOPAC(path)
path |
Path to a |
A PhysioExperiment::MultiPhysioExperiment.
bioread: https://github.com/uwmadison-chm/bioread.
hasBioread(), readDelsysTrigno(), readXsensMVNX()
# .acq is a binary format: the package ships a small sample and the parse needs # the Python 'bioread' backend, so both are checked before reading. f <- system.file("extdata", "biopac_sample.acq", package = "PhysioDevices") if (nzchar(f) && hasBioread()) { mr <- readBIOPAC(f) PhysioExperiment::streamNames(mr) }# .acq is a binary format: the package ships a small sample and the parse needs # the Python 'bioread' backend, so both are checked before reading. f <- system.file("extdata", "biopac_sample.acq", package = "PhysioDevices") if (nzchar(f) && hasBioread()) { mr <- readBIOPAC(f) PhysioExperiment::streamNames(mr) }
Reads a raw Axivity .cwa file (the accelerometer format used by UK Biobank
and other large cohorts) into a tri-axial-acceleration PhysioExperiment via
the GGIRread package, in the same shape as readGT3X() /
PhysioWearable::accelToPhysioExperiment().
readCWA(path, start = 1, end = NULL)readCWA(path, start = 1, end = NULL)
path |
Path to a |
start, end
|
Block range to read (1-based). |
A PhysioExperiment with an acceleration assay (x/y/z, g) at the
device sample rate.
Axivity AX3/AX6; GGIRread::readAxivity.
readGT3X(), PhysioWearable::readAccelCSV()
## Not run: # .cwa is a bit-packed Axivity binary with no R writer and no bundled sample, # so this needs a real device file plus the GGIRread backend; not runnable # offline. pe <- readCWA("subject.cwa") acc <- SummarizedExperiment::assay(pe, "acceleration") enmo <- PhysioWearable::computeENMO(acc, sampling_rate = samplingRate(pe)) ## End(Not run)## Not run: # .cwa is a bit-packed Axivity binary with no R writer and no bundled sample, # so this needs a real device file plus the GGIRread backend; not runnable # offline. pe <- readCWA("subject.cwa") acc <- SummarizedExperiment::assay(pe, "acceleration") enmo <- PhysioWearable::computeENMO(acc, sampling_rate = samplingRate(pe)) ## End(Not run)
Parses a Delsys Trigno File Utility / EMGworks CSV export into a
PhysioExperiment::MultiPhysioExperiment. EMG channels (high rate) and the IMU
channels (accelerometer / gyroscope / magnetometer, lower rate) are separated
into their own streams, each at the rate implied by its per-channel X[s]
time column, with per-sensor column metadata (sensor number and modality).
readDelsysTrigno(path, sep = ",")readDelsysTrigno(path, sep = ",")
path |
Path to a Trigno |
sep |
Field separator (default: comma). |
A PhysioExperiment::MultiPhysioExperiment with an "emg" stream and,
when present, an "imu" stream.
Delsys. "Trigno File Utility / EMGworks export."
f <- system.file("extdata", "delsys_trigno.csv", package = "PhysioDevices") if (nzchar(f)) { mr <- readDelsysTrigno(f) PhysioExperiment::streamNames(mr) }f <- system.file("extdata", "delsys_trigno.csv", package = "PhysioDevices") if (nzchar(f)) { mr <- readDelsysTrigno(f) PhysioExperiment::streamNames(mr) }
Decodes an EmbracePlus .avro file into a
PhysioExperiment::MultiPhysioExperiment. Each signal in the record's
rawData (EDA, BVP, temperature, accelerometer) becomes a stream at its
samplingFrequency, aligned by its timestampStart (microseconds). Requires
an Avro backend (the Python fastavro module via reticulate; see
hasAvroBackend()).
readEmbracePlusAvro(path)readEmbracePlusAvro(path)
path |
Path to an EmbracePlus |
A PhysioExperiment::MultiPhysioExperiment.
Empatica. "EmbracePlus — Avro data format."
readEmpaticaE4(), hasAvroBackend()
# .avro is a binary container: the package ships a small sample and decoding # needs the Python 'fastavro' backend, so both are checked before reading. f <- system.file("extdata", "embraceplus_sample.avro", package = "PhysioDevices") if (nzchar(f) && hasAvroBackend()) { mr <- readEmbracePlusAvro(f) PhysioExperiment::streamNames(mr) }# .avro is a binary container: the package ships a small sample and decoding # needs the Python 'fastavro' backend, so both are checked before reading. f <- system.file("extdata", "embraceplus_sample.avro", package = "PhysioDevices") if (nzchar(f) && hasAvroBackend()) { mr <- readEmbracePlusAvro(f) PhysioExperiment::streamNames(mr) }
Reads an Empatica E4 session (a directory of per-signal CSV files) into a
PhysioExperiment::MultiPhysioExperiment. Each present signal (EDA at 4 Hz,
BVP at 64 Hz, ACC at 32 Hz, TEMP at 4 Hz, HR at 1 Hz) becomes its own stream
at its native rate, and the per-signal UTC start times are aligned on a
common clock via per-stream offsets. Inter-beat intervals (IBI.csv) and
event tags (tags.csv) are attached as events.
readEmpaticaE4(dir)readEmpaticaE4(dir)
dir |
Path to an E4 session directory. |
A PhysioExperiment::MultiPhysioExperiment with one stream per signal.
Empatica. "E4 wristband data — exported CSV file format."
readEmbracePlusAvro(), readShimmer()
d <- system.file("extdata", "e4_sample", package = "PhysioDevices") if (nzchar(d)) { mr <- readEmpaticaE4(d) PhysioExperiment::streamNames(mr) }d <- system.file("extdata", "e4_sample", package = "PhysioDevices") if (nzchar(d)) { mr <- readEmpaticaE4(d) PhysioExperiment::streamNames(mr) }
Reads a .fit file (Garmin, Wahoo, Coros, ...) into a tidy trackpoint data
frame, in the same shape as readTCX() / readGPX(). The binary decode is
delegated to the FITfileR package (not on CRAN – install with
install.packages("FITfileR", repos = "https://grimbough.r-universe.dev")).
readFIT(path, tz = "UTC")readFIT(path, tz = "UTC")
path |
Path to a |
tz |
Time zone for the parsed timestamps (default |
A data frame of trackpoints (time, lat, lon, altitude_m,
distance_m, hr, cadence, speed, power), time-ordered.
Garmin FIT SDK; grimbough/FITfileR.
## Not run: # .fit is a binary Garmin/ANT+ format with no R writer and no bundled sample, # so this needs a real device file plus the FITfileR backend (install from # the grimbough r-universe); not runnable offline. tp <- readFIT("activity.fit") ## End(Not run)## Not run: # .fit is a binary Garmin/ANT+ format with no R writer and no bundled sample, # so this needs a real device file plus the FITfileR backend (install from # the grimbough r-universe); not runnable offline. tp <- readFIT("activity.fit") ## End(Not run)
Parses a Fitbit account-export (or Google Takeout) directory into tidy
per-modality series. Files are discovered by name and both layouts are
handled: the account archive's per-day files (heart_rate-YYYY-MM-DD.json,
steps-*, sleep-*, ...) and the Web-API / research extraction's aggregated
single files (heart_rate.json, steps.json, sleep.json; e.g. the PMData
dataset). Modalities: heart rate (bpm), steps, resting_heart_rate, SpO2
(spo2*/*oxygen*, JSON or minute CSV), and sleep (Fitbit's own
wake/light/deep/rem stages). Google Pixel Watch health data is stored
in Fitbit, so the same reader covers it.
readFitbit(path, what = NULL, tz = "UTC")readFitbit(path, what = NULL, tz = "UTC")
path |
Directory of the extracted archive (searched recursively), or a file / vector of files. |
what |
Optional character vector restricting which modalities to read
(any of |
tz |
Time zone for the parsed timestamps (default |
A fitbit object: a list with any of heart_rate, steps, spo2,
resting_heart_rate (data frames of time, value), sleep (stage
intervals start, end, stage, log_id) and sleep_summary (Fitbit's
per-log efficiency, minutes_asleep, time_in_bed, ...), plus path/tz.
The sleep stages feed PhysioWearable::summarizeSleepStages(); spo2 feeds
PhysioWearable::spo2Metrics().
Fitbit Web API data types; Fitbit account data export.
readAppleHealth(), fitbitSeries()
# Reading Fitbit needs the jsonlite package (a Suggests dependency); write a # tiny aggregated heart_rate.json into a temp dir and read it back. if (requireNamespace("jsonlite", quietly = TRUE)) { d <- tempfile("fitbit") dir.create(d) writeLines(paste0('[{"dateTime":"05/01/23 07:00:00","value":{"bpm":61}},', '{"dateTime":"05/01/23 07:01:00","value":{"bpm":64}}]'), file.path(d, "heart_rate.json")) fb <- readFitbit(d, what = "heart_rate") fb }# Reading Fitbit needs the jsonlite package (a Suggests dependency); write a # tiny aggregated heart_rate.json into a temp dir and read it back. if (requireNamespace("jsonlite", quietly = TRUE)) { d <- tempfile("fitbit") dir.create(d) writeLines(paste0('[{"dateTime":"05/01/23 07:00:00","value":{"bpm":61}},', '{"dateTime":"05/01/23 07:01:00","value":{"bpm":64}}]'), file.path(d, "heart_rate.json")) fb <- readFitbit(d, what = "heart_rate") fb }
Reads a raw GENEActiv .bin file (another research accelerometer used in
large cohorts) into a tri-axial-acceleration PhysioExperiment, in the same
shape as readGT3X() / readCWA(), via the GGIRread package.
readGENEActiv(path, start = 1, end = NULL, sampling_rate = NULL)readGENEActiv(path, start = 1, end = NULL, sampling_rate = NULL)
path |
Path to a |
start, end
|
Page range to read (1-based). |
sampling_rate |
Optional sample-rate override (Hz), used when the header does not report it; otherwise taken from the header or the timestamps. |
A PhysioExperiment with an acceleration assay (x/y/z, g).
GENEActiv; GGIRread::readGENEActiv.
## Not run: # .bin is a bit-packed GENEActiv binary with no R writer and no bundled sample, # so this needs a real device file plus the GGIRread backend; not runnable # offline. pe <- readGENEActiv("subject.bin") ## End(Not run)## Not run: # .bin is a bit-packed GENEActiv binary with no R writer and no bundled sample, # so this needs a real device file plus the GGIRread backend; not runnable # offline. pe <- readGENEActiv("subject.bin") ## End(Not run)
Parses the "Fit" folder of a Google Takeout archive. From "All Data" it reads
the granular per-type JSON points – heart rate (heart_rate.bpm) and steps
(step_count.delta) – decoding their nanosecond timestamps; from "Daily
activity metrics" it reads the daily-summary CSVs (steps, calories, distance,
average/max/min heart rate).
readGoogleFit(path, what = NULL, tz = "UTC")readGoogleFit(path, what = NULL, tz = "UTC")
path |
Directory of the extracted Takeout "Fit" folder (searched recursively), or a file / vector of files. |
what |
Optional restriction, any of |
tz |
Time zone for parsed timestamps (default |
A google_fit object: a list with any of heart_rate, steps
(time, value) and daily (per-day summary), plus path/tz.
Google Takeout "Fit" export; Google Fit data types.
readHealthConnect(), readFitbit()
# readGoogleFit() guards on the jsonlite package (a Suggests dependency). Its # "Daily activity metrics" CSV path is a self-contained round trip: write one # daily-summary CSV under that folder name and read it back. if (requireNamespace("jsonlite", quietly = TRUE)) { d <- tempfile("Fit") dir.create(file.path(d, "Daily activity metrics"), recursive = TRUE) write.csv(data.frame("Date" = "2023-05-01", "Step count" = 8210, "Calories (kcal)" = 2110, "Distance (m)" = 6400, check.names = FALSE), file.path(d, "Daily activity metrics", "2023-05-01.csv"), row.names = FALSE) gf <- readGoogleFit(d, what = "daily") gf$daily }# readGoogleFit() guards on the jsonlite package (a Suggests dependency). Its # "Daily activity metrics" CSV path is a self-contained round trip: write one # daily-summary CSV under that folder name and read it back. if (requireNamespace("jsonlite", quietly = TRUE)) { d <- tempfile("Fit") dir.create(file.path(d, "Daily activity metrics"), recursive = TRUE) write.csv(data.frame("Date" = "2023-05-01", "Step count" = 8210, "Calories (kcal)" = 2110, "Distance (m)" = 6400, check.names = FALSE), file.path(d, "Daily activity metrics", "2023-05-01.csv"), row.names = FALSE) gf <- readGoogleFit(d, what = "daily") gf$daily }
Parses the track points of a .gpx file (a widely shared GPS/workout export)
into a tidy data frame: time, position, altitude, and heart rate / cadence
when the Garmin TrackPointExtension is present.
readGPX(path, tz = "UTC")readGPX(path, tz = "UTC")
path |
Path to a |
tz |
Time zone for the parsed timestamps (default |
A data frame of track points (time, lat, lon, altitude_m,
hr, cadence), time-ordered.
GPX 1.1 schema; Garmin TrackPointExtension.
# Reading GPX needs the xml2 package (a Suggests dependency); write a tiny # two-point .gpx and read it back. if (requireNamespace("xml2", quietly = TRUE)) { p <- tempfile(fileext = ".gpx") writeLines(c( '<gpx version="1.1"><trk><trkseg>', paste0('<trkpt lat="35.17" lon="136.96"><ele>12</ele>', '<time>2023-05-01T07:00:00Z</time></trkpt>'), paste0('<trkpt lat="35.1701" lon="136.9601"><ele>13</ele>', '<time>2023-05-01T07:00:05Z</time></trkpt>'), '</trkseg></trk></gpx>'), p) readGPX(p) }# Reading GPX needs the xml2 package (a Suggests dependency); write a tiny # two-point .gpx and read it back. if (requireNamespace("xml2", quietly = TRUE)) { p <- tempfile(fileext = ".gpx") writeLines(c( '<gpx version="1.1"><trk><trkseg>', paste0('<trkpt lat="35.17" lon="136.96"><ele>12</ele>', '<time>2023-05-01T07:00:00Z</time></trkpt>'), paste0('<trkpt lat="35.1701" lon="136.9601"><ele>13</ele>', '<time>2023-05-01T07:00:05Z</time></trkpt>'), '</trkseg></trk></gpx>'), p) readGPX(p) }
Reads a raw ActiGraph .gt3x file (the common US physical-activity /
actigraphy device format) into a tri-axial-acceleration PhysioExperiment.
The binary decode is done by the read.gt3x package; the result is the
same shape as PhysioWearable::accelToPhysioExperiment(), so it feeds the
free-living accelerometry pipeline (computeENMO(), summarizeFreeLiving()).
readGT3X(path, imputeZeroes = TRUE)readGT3X(path, imputeZeroes = TRUE)
path |
Path to a |
imputeZeroes |
If |
A PhysioExperiment with an acceleration assay (x/y/z, g) at the
device sample rate; device metadata in metadata().
ActiGraph GT3X format; Neishabouri A et al. read.gt3x.
readCWA(), PhysioWearable::readAccelCSV()
## Not run: # .gt3x is a bit-packed binary format with no R writer and no bundled sample, # so this needs a real ActiGraph device file plus the read.gt3x backend and # cannot be run offline. pe <- readGT3X("subject.gt3x") acc <- SummarizedExperiment::assay(pe, "acceleration") PhysioWearable::computeENMO(acc, sampling_rate = samplingRate(pe)) ## End(Not run)## Not run: # .gt3x is a bit-packed binary format with no R writer and no bundled sample, # so this needs a real ActiGraph device file plus the read.gt3x backend and # cannot be run offline. pe <- readGT3X("subject.gt3x") acc <- SummarizedExperiment::assay(pe, "acceleration") PhysioWearable::computeENMO(acc, sampling_rate = samplingRate(pe)) ## End(Not run)
Parses a directory of Health Connect record-type CSVs (one file per type) into
tidy per-modality series. Recognises HeartRate, Steps, OxygenSaturation,
RespiratoryRate and SleepSession (its stages) by filename; the time and
value columns are found by name heuristics, so the common exporter layouts
work without configuration.
readHealthConnect(path, tz = "UTC")readHealthConnect(path, tz = "UTC")
path |
Directory of the CSV export (searched recursively), or a file / vector of files. |
tz |
Time zone for parsed timestamps (default |
A health_connect object: a list with any of heart_rate, steps,
spo2, respiratory_rate (time, value), and sleep (stage intervals
start, end, stage), plus path/tz. spo2 feeds
PhysioWearable::spo2Metrics(), sleep feeds
PhysioWearable::summarizeSleepStages().
Android Health Connect data types and export.
# Health Connect exports one CSV per record type (type taken from the # filename); write a tiny HeartRate.csv into a temp dir and read it back. d <- tempfile("healthconnect") dir.create(d) write.csv(data.frame(Time = c("2023-05-01T07:00:00Z", "2023-05-01T07:01:00Z"), BeatsPerMinute = c(61, 64), check.names = FALSE), file.path(d, "HeartRate.csv"), row.names = FALSE) hc <- readHealthConnect(d) hc$heart_rate# Health Connect exports one CSV per record type (type taken from the # filename); write a tiny HeartRate.csv into a temp dir and read it back. d <- tempfile("healthconnect") dir.create(d) write.csv(data.frame(Time = c("2023-05-01T07:00:00Z", "2023-05-01T07:01:00Z"), BeatsPerMinute = c(61, 64), check.names = FALSE), file.path(d, "HeartRate.csv"), row.names = FALSE) hc <- readHealthConnect(d) hc$heart_rate
Parses an Oura sleep export/JSON (API v2 sleep collection, or the older API
v1 sleep) into tidy per-night data: sleep-stage intervals (from the 5-minute
phase string), the 5-minute heart-rate and HRV (RMSSD) series, and Oura's own
sleep summary. Both API versions are recognised.
readOura(path, tz = "UTC")readOura(path, tz = "UTC")
path |
Path to the Oura sleep JSON (a v2 object with a |
tz |
Time zone for the parsed timestamps (default |
An oura object: a list with sleep (stage intervals start, end,
stage, day), sleep_summary (per-day efficiency,
total_sleep_min, time_in_bed_min), heart_rate (time, bpm) and
hrv (time, rmssd), plus path/tz. The sleep stages feed
PhysioWearable::summarizeSleepStages().
Oura API v2 sleep data.
readFitbit(), readHealthConnect()
# Reading Oura needs the jsonlite package (a Suggests dependency); write a tiny # API-v1 sleep JSON and read it back. if (requireNamespace("jsonlite", quietly = TRUE)) { p <- tempfile(fileext = ".json") rec <- list(day = "2023-05-01", bedtime_start = "2023-05-01T23:00:00+00:00", hypnogram_5min = "1223334444", efficiency = 90, total_sleep_duration = 27000, time_in_bed = 30000, hr_5min = c(58, 57, 56, 55, 60, 62, 59, 58, 57, 61), rmssd_5min = c(40, 42, 45, 50, 48, 30, 35, 38, 41, 39)) jsonlite::write_json(list(sleep = list(rec)), p, auto_unbox = TRUE) ou <- readOura(p) ou$sleep_summary }# Reading Oura needs the jsonlite package (a Suggests dependency); write a tiny # API-v1 sleep JSON and read it back. if (requireNamespace("jsonlite", quietly = TRUE)) { p <- tempfile(fileext = ".json") rec <- list(day = "2023-05-01", bedtime_start = "2023-05-01T23:00:00+00:00", hypnogram_5min = "1223334444", efficiency = 90, total_sleep_duration = 27000, time_in_bed = 30000, hr_5min = c(58, 57, 56, 55, 60, 62, 59, 58, 57, 61), rmssd_5min = c(40, 42, 45, 50, 48, 30, 35, 38, 41, 39)) jsonlite::write_json(list(sleep = list(rec)), p, auto_unbox = TRUE) ou <- readOura(p) ou$sleep_summary }
Reads a Polar (Flow / H10) export into a PhysioExperiment. A heart-rate
column (bpm) becomes the "raw" assay; R-R intervals (ms) are attached as
PhysioExperiment::PhysioEvents (one event per beat, with onset at the cumulative
R-R time and the interval in the value). When only R-R intervals are present,
instantaneous heart rate (60000 / R-R) is used as the signal.
readPolar(path, sep = ",")readPolar(path, sep = ",")
path |
Path to a Polar |
sep |
Field separator (default: comma). |
A PhysioExperiment; R-R intervals are available via
PhysioExperiment::getEvents().
Polar. "Flow / H10 heart-rate and R-R export."
readEmpaticaE4(), readShimmer()
f <- system.file("extdata", "polar_hr.csv", package = "PhysioDevices") if (nzchar(f)) { pe <- readPolar(f) PhysioExperiment::nEvents(PhysioExperiment::getEvents(pe)) }f <- system.file("extdata", "polar_hr.csv", package = "PhysioDevices") if (nzchar(f)) { pe <- readPolar(f) PhysioExperiment::nEvents(PhysioExperiment::getEvents(pe)) }
Parses a Samsung Health CSV export directory into tidy per-modality series –
heart rate, blood oxygen (SpO2) and steps – detecting the record type from
the com.samsung... filename and the time/value columns heuristically (each
file has a one-line metadata comment above its header). Galaxy Watch health
data is stored in Samsung Health, so this covers it.
readSamsungHealth(path, tz = "UTC")readSamsungHealth(path, tz = "UTC")
path |
Directory of the export (searched recursively), or a file / vector of files. |
tz |
Time zone for the parsed timestamps (default |
A samsung_health object: a list with any of heart_rate (bpm),
spo2, steps (time, value), plus path/tz. spo2 feeds
PhysioWearable::spo2Metrics().
Samsung Health data export.
readHealthConnect(), readFitbit()
# Samsung Health CSVs carry a one-line metadata comment above the header (so # they are read with skip = 1); write a tiny heart_rate file and read it back. d <- tempfile("samsung") dir.create(d) writeLines(c("com.samsung.shealth.tracker.heart_rate,2,...", "start_time,heart_rate", "2023-05-01 07:00:00,61", "2023-05-01 07:01:00,64"), file.path(d, "com.samsung.shealth.tracker.heart_rate.202305.csv")) sh <- readSamsungHealth(d) sh$heart_rate# Samsung Health CSVs carry a one-line metadata comment above the header (so # they are read with skip = 1); write a tiny heart_rate file and read it back. d <- tempfile("samsung") dir.create(d) writeLines(c("com.samsung.shealth.tracker.heart_rate,2,...", "start_time,heart_rate", "2023-05-01 07:00:00,61", "2023-05-01 07:01:00,64"), file.path(d, "com.samsung.shealth.tracker.heart_rate.202305.csv")) sh <- readSamsungHealth(d) sh$heart_rate
Reads a Shimmer GSR3+ Consensys export (GSR / skin conductance, PPG,
accelerometer) into a PhysioExperiment. The calibrated (_CAL) columns are
read in their physical units; the timestamp column determines the sampling
rate; and each channel's unit (from the export's unit row) is stored in the
column metadata.
readShimmer(path, sep = NULL)readShimmer(path, sep = NULL)
path |
Path to a Shimmer Consensys |
sep |
Field separator. By default it is taken from a leading
|
A PhysioExperiment with one channel per calibrated signal.
Shimmer. "Consensys — data export format."
f <- system.file("extdata", "shimmer_gsr3.csv", package = "PhysioDevices") if (nzchar(f)) { pe <- readShimmer(f) channelNames(pe) }f <- system.file("extdata", "shimmer_gsr3.csv", package = "PhysioDevices") if (nzchar(f)) { pe <- readShimmer(f) channelNames(pe) }
Parses the trackpoints of a .tcx workout (the common Garmin Connect / Strava
export) into a tidy data frame: time, position, altitude, distance, heart rate
and cadence/speed where present.
readTCX(path, tz = "UTC")readTCX(path, tz = "UTC")
path |
Path to a |
tz |
Time zone for the parsed timestamps (default |
A data frame of trackpoints (time, lat, lon, altitude_m,
distance_m, hr, cadence, speed), time-ordered, with the activity
sport in attr(, "sport").
Garmin Training Center XML (TCX) schema.
# Reading TCX needs the xml2 package (a Suggests dependency); write a tiny # two-trackpoint .tcx and read it back. if (requireNamespace("xml2", quietly = TRUE)) { p <- tempfile(fileext = ".tcx") writeLines(c( '<TrainingCenterDatabase><Activities><Activity Sport="Running"><Lap><Track>', paste0('<Trackpoint><Time>2023-05-01T07:00:00Z</Time><Position>', '<LatitudeDegrees>35.17</LatitudeDegrees>', '<LongitudeDegrees>136.96</LongitudeDegrees></Position>', '<DistanceMeters>0</DistanceMeters>', '<HeartRateBpm><Value>96</Value></HeartRateBpm></Trackpoint>'), paste0('<Trackpoint><Time>2023-05-01T07:00:05Z</Time><Position>', '<LatitudeDegrees>35.1701</LatitudeDegrees>', '<LongitudeDegrees>136.9601</LongitudeDegrees></Position>', '<DistanceMeters>9</DistanceMeters>', '<HeartRateBpm><Value>99</Value></HeartRateBpm></Trackpoint>'), '</Track></Lap></Activity></Activities></TrainingCenterDatabase>'), p) readTCX(p) }# Reading TCX needs the xml2 package (a Suggests dependency); write a tiny # two-trackpoint .tcx and read it back. if (requireNamespace("xml2", quietly = TRUE)) { p <- tempfile(fileext = ".tcx") writeLines(c( '<TrainingCenterDatabase><Activities><Activity Sport="Running"><Lap><Track>', paste0('<Trackpoint><Time>2023-05-01T07:00:00Z</Time><Position>', '<LatitudeDegrees>35.17</LatitudeDegrees>', '<LongitudeDegrees>136.96</LongitudeDegrees></Position>', '<DistanceMeters>0</DistanceMeters>', '<HeartRateBpm><Value>96</Value></HeartRateBpm></Trackpoint>'), paste0('<Trackpoint><Time>2023-05-01T07:00:05Z</Time><Position>', '<LatitudeDegrees>35.1701</LatitudeDegrees>', '<LongitudeDegrees>136.9601</LongitudeDegrees></Position>', '<DistanceMeters>9</DistanceMeters>', '<HeartRateBpm><Value>99</Value></HeartRateBpm></Trackpoint>'), '</Track></Lap></Activity></Activities></TrainingCenterDatabase>'), p) readTCX(p) }
Parses a Whoop CSV export directory into tidy daily-summary tables: sleep
(per-night stage durations, efficiency, HRV, respiratory rate) and recovery
(recovery score, resting heart rate, HRV, blood oxygen, skin temperature).
Whoop exports summaries, not epoch-level stages.
readWhoop(path)readWhoop(path)
path |
Directory of the Whoop export (searched recursively), or a file / vector of files. |
A whoop object: a list with sleep and/or recovery data frames,
plus path.
Whoop data export.
# Whoop exports a folder of summary CSVs; write a tiny physiological_cycles.csv # into a temp dir and read it back (base-R read.csv, no toolchain needed). d <- tempfile("whoop") dir.create(d) write.csv(data.frame("Cycle start time" = "2023-05-01", "Recovery score %" = 66, "Resting heart rate (bpm)" = 54, "Heart rate variability (ms)" = 48, "Blood oxygen %" = 97, "Skin temp (celsius)" = 33.2, check.names = FALSE), file.path(d, "physiological_cycles.csv"), row.names = FALSE) w <- readWhoop(d) w$recovery# Whoop exports a folder of summary CSVs; write a tiny physiological_cycles.csv # into a temp dir and read it back (base-R read.csv, no toolchain needed). d <- tempfile("whoop") dir.create(d) write.csv(data.frame("Cycle start time" = "2023-05-01", "Recovery score %" = 66, "Resting heart rate (bpm)" = 54, "Heart rate variability (ms)" = 48, "Blood oxygen %" = 97, "Skin temp (celsius)" = 33.2, check.names = FALSE), file.path(d, "physiological_cycles.csv"), row.names = FALSE) w <- readWhoop(d) w$recovery
Parses a Withings "Download my data" export directory into tidy per-modality
tables: heart_rate (bpm), spo2, blood_pressure (systolic,
diastolic) and weight (kg). The modality is taken from the filename and
the time/value columns are detected heuristically. Covers Withings ScanWatch /
Sleep / BPM / Body devices.
readWithings(path, tz = "UTC")readWithings(path, tz = "UTC")
path |
Directory of the export (searched recursively), or a file / vector of files. |
tz |
Time zone for the parsed timestamps (default |
A withings object: a list with any of heart_rate, spo2,
blood_pressure, weight, plus path/tz. spo2 feeds
PhysioWearable::spo2Metrics().
Withings Health Mate data export.
# Withings exports per-modality CSVs (modality taken from the filename); write # a tiny weight.csv into a temp dir and read it back (base-R read.csv). d <- tempfile("withings") dir.create(d) write.csv(data.frame(date = c("2023-05-01", "2023-05-08"), "Weight (kg)" = c(70.2, 69.8), check.names = FALSE), file.path(d, "weight.csv"), row.names = FALSE) w <- readWithings(d) w$weight# Withings exports per-modality CSVs (modality taken from the filename); write # a tiny weight.csv into a temp dir and read it back (base-R read.csv). d <- tempfile("withings") dir.create(d) write.csv(data.frame(date = c("2023-05-01", "2023-05-08"), "Weight (kg)" = c(70.2, 69.8), check.names = FALSE), file.path(d, "weight.csv"), row.names = FALSE) w <- readWithings(d) w$weight
Parses an Xsens MVN Analyze .mvnx XML file into a
PhysioExperiment::MultiPhysioExperiment. Each kinematic signal present in the
"normal" frames (segment position, orientation, velocity, acceleration,
angular velocity/acceleration, per-joint angles, and centre of mass) becomes
a stream at the subject frame rate, with per-channel labels built from the
segment/joint names.
readXsensMVNX(path)readXsensMVNX(path)
path |
Path to a |
A PhysioExperiment::MultiPhysioExperiment.
Xsens. "MVNX file format (MVN Analyze)."
readBIOPAC(), readDelsysTrigno()
f <- system.file("extdata", "xsens_sample.mvnx", package = "PhysioDevices") if (nzchar(f) && requireNamespace("xml2", quietly = TRUE)) { mr <- readXsensMVNX(f) PhysioExperiment::streamNames(mr) }f <- system.file("extdata", "xsens_sample.mvnx", package = "PhysioDevices") if (nzchar(f) && requireNamespace("xml2", quietly = TRUE)) { mr <- readXsensMVNX(f) PhysioExperiment::streamNames(mr) }