| Title: | Chronological, Gestational and Biological DNAm Age Estimation with Methylation-Based Clocks |
|---|---|
| Description: | Estimates chronological, gestational and biological DNA methylation (DNAm) age, trait scores and epigenetic age acceleration from methylation beta values. Implements 44 published clocks, selectable by name, family or array platform: chronological (Horvath, Hannum, BNN, skin+blood, PedBE, Wu, AltumAge and others, including the cross-platform Garma clock native to EPICv2), gestational (Knight, Bohlin, Mayne, Lee, NEOage), phenotypic and pace-of-aging (Levine, DunedinPACE), telomere length, mitotic counters (epiTOC1/epiTOC2, HypoClock, stemTOC, RepliTali), causal (CausAge, DamAge, AdaptAge) and trait EpiScores. Accepts matrices, data frames, Bioconductor containers or HDF5 files, computing block-wise out-of-core through BigDataStatMeth when the data exceed memory. Provides configurable missing-value imputation (mean, reference or KNN), cell-type deconvolution, intrinsic and extrinsic epigenetic age acceleration (IEAA, EEAA), quality-control reports and plotting functions. |
| Authors: | Dolors Pelegri-Siso [aut, cre] (ORCID: <https://orcid.org/0000-0002-5993-3003>), Juan R. Gonzalez [aut] (ORCID: <https://orcid.org/0000-0003-3267-2146>) |
| Maintainer: | Dolors Pelegri-Siso <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 1.99.2 |
| Built: | 2026-09-14 14:44:43 UTC |
| Source: | https://github.com/bioc/methylclock |
For each age clock, how much older (or younger) a sample looks than its chronological age. Three measures per clock:
ageAccThe raw difference, predicted minus chronological age.
residualThe classic age-acceleration residual: predicted
regressed on chronological age. (The published EEAA of Chen and
colleagues is a different, cell-weighted measure; EEAA
returns it under its usual name.)
residualCellsThe residual further adjusted for blood cell
composition (whatever panel you supply), so it reflects ageing
independent of shifts in cell proportions. Only returned when
cell_counts is supplied. This is the same adjustment idea as
the canonical IEAA of the literature, but that measure is defined
with a specific set of seven immune covariates —
IEAA computes it as defined.
Estimate the cell proportions with cellCounts.
ageAcceleration(x, age, cell_counts = NULL, clocks = NULL)ageAcceleration(x, age, cell_counts = NULL, clocks = NULL)
x |
A |
age |
Numeric vector of chronological ages, one per sample in |
cell_counts |
Optional samples-by-cell-types matrix (from
|
clocks |
Optional clock names. Default: the age clocks in |
A data frame with columns id, clock, ageAcc,
residual and (with cell_counts) residualCells.
cellCounts, plotAgeAcceleration
data(methylclock_demo) head(ageAcceleration(methylclock_demo, age = methylclock_demo$age))data(methylclock_demo) head(ageAcceleration(methylclock_demo, age = methylclock_demo$age))
An immune-aware measure of epigenetic age acceleration in the spirit of
Chen et al. (2016): a Klemera–Doubal weighted average of the Hannum clock
with immune cell types that track age (by default naive CD8+ T cells,
exhausted CD8+ T cells and plasmablasts) is formed, then regressed on
chronological age, and the returned chenAcc is that residual.
ageAccelerationChen( hannum, age, cells, immune = c("CD8nv", "CD8exhausted", "PlasmaBlast") )ageAccelerationChen( hannum, age, cells, immune = c("CD8nv", "CD8exhausted", "PlasmaBlast") )
hannum |
Numeric vector of Hannum-clock estimated ages, one per sample. May be named by sample id. |
age |
Numeric vector of chronological ages, one per sample. |
cells |
Samples-by-cell-types matrix with the immune cell measures;
it must contain every column named in |
immune |
Column names in |
The defining trait of this function is that the Klemera–Doubal weights
are re-estimated from YOUR data, and the immune inputs are whatever cell
measures you supply. The published EEAA instead fixes both: its weights
were fixed once in the Women's Health Initiative cohorts and its cell
inputs are three specific CpG-based estimators (the parameters of that
fixed recipe are published in European patent EP 3 494 210 B1, and
EEAA implements it). The patent documents the re-estimated
flavour as well (its "dynamic" variant) and reports the two to be highly
correlated; still, the two are numerically different measures, which is
why this function's output column is named chenAcc and not EEAA.
A data frame with columns id, age and
chenAcc.
The subsets this measure was defined on — naive CD8+ T cells, exhausted
CD8+ T cells (CD28-CD45RA-) and plasmablasts — are not resolved by the
reference panels bundled here (see listCellReferences), which
report total CD8+ T cells. Two external routes are commonly used, and both
produce values you can pass straight to cells:
the advanced analysis of the online epigenetic-age calculator of Horvath (2013), which reports these subsets directly;
an extended immune deconvolution such as the EPIC IDOL-Ext library of Salas et al. (2022), distributed as the FlowSorted.BloodExtended.EPIC package under its own licence from the authors' institution — with the caveat that it resolves naive and memory CD8+ T cells and B cells but NOT exhausted CD8+ T cells or plasmablasts, so proportions from it are stand-ins: the result is a measure in the spirit of Chen's, not the one defined on the original subsets.
Users who obtain proportions this way should cite the source they used. Note that the rarer subsets (plasmablasts, exhausted CD8+ T cells) are reported by some algorithms as scores linearly related to abundance rather than as cell-type proportions, which is worth keeping in mind when comparing values across sources.
Chen BH, Marioni RE, Colicino E, et al. (2016). DNA methylation-based measures of biological age. Aging 8(9):1844–1865. Klemera P, Doubal S (2006). A new approach to the concept and computation of biological age. Mech Ageing Dev 127(3):240–248. European patent EP 3 494 210 B1 (2024). DNA methylation based biological age prediction.
EEAA for the canonical fixed-weight measure,
IEAA, ageAcceleration,
cellCounts
## Not run: # `subsets` holding naive CD8+, exhausted CD8+ and plasmablast measures # per sample, e.g. from the Horvath calculator's advanced analysis: chen <- ageAccelerationChen(res$Hannum, age = pheno$age, cells = subsets) ## End(Not run)## Not run: # `subsets` holding naive CD8+, exhausted CD8+ and plasmablast measures # per sample, e.g. from the Horvath calculator's advanced analysis: chen <- ageAccelerationChen(res$Hannum, age = pheno$age, cells = subsets) ## End(Not run)
Several clocks are trained on an age variable that is compressed on a log scale below a reference adult age. This maps a raw prediction back to years.
anti_trafo(x, adult_age = 20)anti_trafo(x, adult_age = 20)
x |
Numeric vector of raw predictions. |
adult_age |
Reference adult age used by the transformation. Default
|
Numeric vector of ages in years.
anti_trafo(c(-0.5, 0, 0.5, 1.5))anti_trafo(c(-0.5, 0, 0.5, 1.5))
Returns the estimates as a data frame, materialising them from disk first if they are not already held in memory. The backend is invisible to the caller.
## S3 method for class 'methylclock' as.data.frame(x, ...)## S3 method for class 'methylclock' as.data.frame(x, ...)
x |
A |
... |
Unused. |
A data frame of estimates.
Reference-based deconvolution: given a methylation matrix, it estimates the proportion of each blood cell type per sample, by fitting the sample's profile as a non-negative mix of purified-cell reference profiles (Houseman et al. 2012). These proportions are what the intrinsic age acceleration adjusts for.
cellCounts( betas, reference = "blood gse35069 complete", normalize = TRUE, array = c("EPIC", "450k") )cellCounts( betas, reference = "blood gse35069 complete", normalize = TRUE, array = c("EPIC", "450k") )
betas |
A methylation matrix (or data frame, HDF5-backed matrix, or an accepted Bioconductor container) with CpG identifiers as row names. |
reference |
Reference panel name (see |
normalize |
Quantile-normalise the CpGs to a shipped reference before
fitting (ignored for the FlowSorted panels). Default |
array |
|
The panels named in listCellReferences are shipped via
the data layer. Two higher-resolution panels are also supported but not
shipped, because of their licence: "FlowSorted.Blood.EPIC" (6 cells,
GPL-3) and "FlowSorted.BloodExtended.EPIC" (12 immune cells, Salas et
al. 2022, a restrictive non-commercial academic licence). Install the
package yourself and it is used at run time; nothing licensed is
redistributed by methylclock.
A matrix of samples (rows) by cell types (columns), each row summing to approximately one.
Houseman EA et al. (2012) BMC Bioinformatics 13:86. Salas LA et al. (2022) Nature Communications 13:761.
## Not run: cellCounts(betas) cellCounts(betas, reference = "FlowSorted.BloodExtended.EPIC") ## End(Not run)## Not run: cellCounts(betas) cellCounts(betas, reference = "FlowSorted.BloodExtended.EPIC") ## End(Not run)
A one-row-per-clock overview meant to be read first: what each clock
estimates, which arrays it was built for, how many CpGs it uses, and a short
reference to the paper. It is a frozen snapshot of the registry (regenerated
when clocks are added), so it can be displayed without resolving any
coefficients. For the live registry — to filter or query programmatically —
use clock_list.
data(clock_catalog)data(clock_catalog)
A data frame with one row per clock and columns:
Clock name (pass to methylclock(clocks = )).
What the clock reports, with units (e.g. "Age").
The tissue the clock was trained on (whole blood, cord blood, placenta, buccal, skin, multi-tissue...): a clock reads best the tissue it came from.
Illumina platforms the clock supports.
Number of CpGs the clock uses.
Clock generation, where defined.
The registry target family the clock belongs to.
Citation key for the clock's paper.
Short human-readable reference (author and year).
data(clock_catalog) clock_catalog[clock_catalog$target == "chronological", c("clock", "estimates", "arrays", "n_cpgs", "reference")]data(clock_catalog) clock_catalog[clock_catalog$target == "chronological", c("clock", "estimates", "arrays", "n_cpgs", "reference")]
Look up one clock's registry entry
clock_info(name)clock_info(name)
name |
Clock name. |
The registry entry (class clock_entry); errors if the clock is
not registered.
clock_info("Horvath")clock_info("Horvath")
List and filter registered clocks
clock_list( target = NULL, generation = NULL, predictor = NULL, platform = NULL, status = NULL )clock_list( target = NULL, generation = NULL, predictor = NULL, platform = NULL, status = NULL )
target |
Optional filter by biological target. |
generation |
Optional filter by generation. |
predictor |
Optional filter by prediction engine. |
platform |
Optional filter by native platform: keeps clocks whose native
platform includes any of the given arrays (e.g. |
status |
Optional filter by availability status. |
A data frame with one row per matching clock.
clock_list(target = "gestational") clock_list(platform = "450K")clock_list(target = "gestational") clock_list(platform = "450K")
The predictor families a registered clock can use. A clock's predictor
must be one of these values.
clock_predictorsclock_predictors
An object of class character of length 7.
intercept plus a weighted sum of features (most clocks).
project features onto a PCA rotation, then a linear model on the principal components.
two-stage model (methylation surrogates then a combining model); typically requires chronological age and sex.
a mean or quantile over a defined set of CpGs.
a counter estimated per sample by numerical optimisation.
a neural network.
a model with categorical output.
A character vector of the supported engine names.
clock_predictorsclock_predictors
Adds (or replaces) one clock in the package registry. A registration is a single declarative description of a clock; the estimation engine reads it to compute the clock's value.
clock_register( name, target, predictor = "linear", generation = NA_integer_, platform = character(0), compatible = character(0), tissue = NA_character_, resource = NULL, aux = NULL, coef_col = "CoefficientTraining", intercept = TRUE, standardize = FALSE, normalize = NA_character_, agg = "mean", transform = identity, inputs = character(0), feature_type = "cpg", output_type = "numeric", license = NA_character_, source = NA_character_, citation = NA_character_, status = "ok", notes = NA_character_ )clock_register( name, target, predictor = "linear", generation = NA_integer_, platform = character(0), compatible = character(0), tissue = NA_character_, resource = NULL, aux = NULL, coef_col = "CoefficientTraining", intercept = TRUE, standardize = FALSE, normalize = NA_character_, agg = "mean", transform = identity, inputs = character(0), feature_type = "cpg", output_type = "numeric", license = NA_character_, source = NA_character_, citation = NA_character_, status = "ok", notes = NA_character_ )
name |
Clock name (unique key), e.g. |
target |
Biological target; one of |
predictor |
Prediction engine; one of |
generation |
Clock generation (e.g. 1, 2, 3) or |
platform |
Character vector of the clock's native arrays — the
platform(s) it was built and validated on, e.g. |
compatible |
Character vector of additional arrays the clock can be used with through probe conversion or adaptation, i.e. not its native platform. |
tissue |
The tissue the clock was trained on (e.g. |
resource |
Resource identifier (resolved by |
aux |
Character vector of auxiliary resource identifiers (e.g.
normalization means, a PCA rotation, a cell-type reference), or |
coef_col |
Name of the coefficient column in the resource. Default
|
intercept |
Logical: is the first coefficient row the intercept? |
standardize |
Logical: z-score each sample across the CpGs of the input
matrix before applying the coefficients (as some elastic-net clocks
require). Default |
normalize |
Optional input normalization applied before the
coefficients. |
agg |
For |
transform |
Function applied to the raw prediction (e.g. an
anti-transform). Default |
inputs |
Character vector of inputs required beyond the beta matrix
(e.g. |
feature_type |
Feature space of the predictor. Default |
output_type |
One of |
license |
Short license/provenance tag for the weights. Default
|
source |
Where the weights come from (URL, package, or resource id). |
citation |
Short citation key. |
status |
Availability status; one of |
notes |
Free-text notes. |
Invisibly, the registry entry (a list of class clock_entry).
clock_list, clock_info,
mcd_resource
clock_register("MyClock", target = "chronological", resource = "coefMyClock") clock_info("MyClock")clock_register("MyClock", target = "chronological", resource = "coefMyClock") clock_info("MyClock")
The clock registry
clock_registry()clock_registry()
A named list of registry entries (objects of class
clock_entry), ordered by name.
reg <- clock_registry() length(reg) names(reg)[seq_len(5)] reg[["Horvath"]]reg <- clock_registry() length(reg) names(reg)[seq_len(5)] reg[["Horvath"]]
Removes all registered clocks. Mainly useful in tests, or when building a registry from scratch: the built-in clocks are registered when the package is loaded, so save them first if you want them back.
clock_reset()clock_reset()
Invisibly NULL.
clock_register, clock_registry
saved <- clock_registry() clock_reset() length(clock_registry()) # put the built-in clocks back for (entry in saved) do.call(clock_register, unclass(entry)) length(clock_registry())saved <- clock_registry() clock_reset() length(clock_registry()) # put the built-in clocks back for (entry in saved) do.call(clock_register, unclass(entry)) length(clock_registry())
A coarse status for a registered clock. "ok" is fully available;
"license-risk" is usable but carries licensing constraints on its
weights; "out-of-scope" does not apply to the package's target tissue;
"not-implementable" has no usable published model; "todo" is
planned.
clock_statusesclock_statuses
An object of class character of length 5.
A character vector of the supported status labels.
clock_statusesclock_statuses
The quantity a clock estimates. A clock's target must be one of these.
clock_targetsclock_targets
An object of class character of length 10.
A character vector of the supported target names.
clock_targetsclock_targets
A quick quality-control summary for a dataset with known ages: for every age
clock, how closely its estimate follows chronological age. It reports the
number of samples, the Pearson correlation, the R-squared and the median
absolute error (in years). Pass by to compute the same summary within
groups — sex, case/control, cohort, ethnicity — which is the usual way to
check that a clock behaves across subgroups rather than only on average.
clockAccuracy(x, age, by = NULL, clocks = NULL)clockAccuracy(x, age, by = NULL, clocks = NULL)
x |
A |
age |
Numeric vector of chronological ages, one per sample in |
by |
Optional categorical vector (one value per sample, or named by id): the summary is computed separately within each group. |
clocks |
Optional clock names to restrict to. Default: the clocks that estimate an age in years (a comparison with chronological age is only meaningful for those). |
A high correlation with a large error means a clock *follows* age but is *miscalibrated* for your data (a constant offset), which the two columns surface separately.
A data frame with one row per clock (per group), and columns
clock, optionally group, n, r, R2 and
MAE.
data(methylclock_demo) clockAccuracy(methylclock_demo, age = methylclock_demo$age, by = methylclock_demo$sex)data(methylclock_demo) clockAccuracy(methylclock_demo, age = methylclock_demo$age, by = methylclock_demo$sex)
For every clock, how many of the CpGs it needs are present in your data. A clock built from only a fraction of its CpGs should be read with caution (and below the coverage threshold it is not computed at all). This needs the methylation matrix, not the estimates, so it can look up which CpGs you have.
clockCoverage(betas, clocks = NULL)clockCoverage(betas, clocks = NULL)
betas |
A methylation matrix (or data frame, HDF5-backed matrix, or one of the accepted Bioconductor containers): only its CpG identifiers (row names) are used. |
clocks |
Optional clock names to restrict to. Default: all clocks. |
A data frame with one row per clock and columns clock,
n_cpgs (CpGs the clock uses), present (how many are in your
data) and pct_present.
data(methylclock_betas) clockCoverage(methylclock_betas, clocks = c("Horvath", "Hannum", "Levine"))data(methylclock_betas) clockCoverage(methylclock_betas, clocks = c("Horvath", "Hannum", "Levine"))
When the same subjects were sampled at several ages, this pairs their
samples into trajectories: one row per subject, visit and clock, ordered
in time. It is the table the longitudinal plots and rates are built from,
and the one to hand to a mixed-model package for formal analysis — for
example lme4::lmer(estimate ~ age + (age | subject), data = tr)
fits a population slope with per-subject deviations. This package shapes
and displays longitudinal data; the inference belongs to those tools.
clockTrajectories(x, subject, age, clocks = NULL)clockTrajectories(x, subject, age, clocks = NULL)
x |
A |
subject |
Subject identifier (character or factor), one per sample in
|
age |
Numeric chronological age AT EACH SAMPLE, one per sample in
|
clocks |
Optional character vector of clock names. Default: the age
clocks present in |
Samples with a missing subject or age are dropped; repeated measurements of a subject at the same recorded age are averaged (whether they are technical replicates or distinct samples whose ages rounded to the same value is for the caller to know — pass more precise ages to keep them apart); and any subject-and-clock combination seen at fewer than two distinct ages is excluded — one point has no trajectory. Each exclusion is reported in a message.
A data frame with columns subject, visit (1, 2, ...
in age order), age, clock and estimate.
data(methylclock_longitudinal) d <- methylclock_longitudinal tr <- clockTrajectories(d, subject = d$subject, age = d$age, clocks = "Wu") head(tr)data(methylclock_longitudinal) d <- methylclock_longitudinal tr <- clockTrajectories(d, subject = d$subject, age = d$age, clocks = "Wu") head(tr)
Computes the selected clocks over a methylation beta matrix and returns a
new_methylclock result. This is the engine shared by
methylclock, DNAmAge and DNAmGA.
compute_clocks( x, clocks = "all", target = NULL, generation = NULL, platform = NULL, min.perc = 0.8, impute = c("mean", "reference", "none", "knn"), min.perc.sample = 0, k = 10, maxp = 1500, storage = c("auto", "hdf5", "memory"), file = NULL, compression = NULL, ... )compute_clocks( x, clocks = "all", target = NULL, generation = NULL, platform = NULL, min.perc = 0.8, impute = c("mean", "reference", "none", "knn"), min.perc.sample = 0, k = 10, maxp = 1500, storage = c("auto", "hdf5", "memory"), file = NULL, compression = NULL, ... )
x |
A methylation matrix with CpG identifiers as row names and samples as columns (a data frame with a CpG-identifier column is also accepted). A large matrix may be supplied on disk as an HDF5-backed matrix, in which case each clock reads only the CpGs it needs rather than the whole array. |
clocks |
Either |
target |
Optional biological target to restrict to (e.g.
|
generation |
Optional generation to restrict to (e.g. |
platform |
Optional native platform to restrict to (e.g. |
min.perc |
Minimum fraction of a clock's CpGs that must be present. A
clock is only computed when its coverage is strictly greater than this;
otherwise it is returned as |
impute |
How missing values among the present CpGs are filled before
the weighted sum. |
min.perc.sample |
Per-sample coverage floor. For each clock, a sample
with fewer than this fraction of the clock's present CpGs observed is
returned |
k |
Neighbours averaged when |
maxp |
Largest block imputed by exact KNN when |
storage |
How the estimates are held: |
file |
HDF5 file to write the estimates to. When |
compression |
HDF5 compression level (0-9); |
... |
Reserved for future inputs (e.g. age, sex, cell composition). |
A "methylclock" object.
methylclock, persist,
clock_list
data(methylclock_betas) res <- compute_clocks(methylclock_betas, clocks = c("Horvath", "Levine"), storage = "memory") head(as.data.frame(res))data(methylclock_betas) res <- compute_clocks(methylclock_betas, clocks = c("Horvath", "Levine"), storage = "memory") head(as.data.frame(res))
Convenience wrapper around methylclock restricted to
non-gestational clocks.
DNAmAge( x, clocks = "all", generation = NULL, platform = NULL, min.perc = 0.8, storage = "auto", ... )DNAmAge( x, clocks = "all", generation = NULL, platform = NULL, min.perc = 0.8, storage = "auto", ... )
x |
A methylation matrix with CpG identifiers as row names and samples as columns (a data frame with a CpG-identifier column is also accepted), or a large matrix held on disk as an HDF5-backed matrix. |
clocks |
Either |
generation |
Optional generation to restrict to (e.g. |
platform |
Optional native platform to restrict to (e.g. |
min.perc |
Minimum fraction of a clock's CpGs that must be present for
it to be computed (otherwise |
storage |
How the estimates are held: |
... |
Passed to |
A "methylclock" object.
data(methylclock_betas) head(as.data.frame(DNAmAge(methylclock_betas, clocks = "Horvath")))data(methylclock_betas) head(as.data.frame(DNAmAge(methylclock_betas, clocks = "Horvath")))
Convenience wrapper around methylclock restricted to
gestational clocks.
DNAmGA( x, clocks = "all", generation = NULL, platform = NULL, min.perc = 0.8, storage = "auto", ... )DNAmGA( x, clocks = "all", generation = NULL, platform = NULL, min.perc = 0.8, storage = "auto", ... )
x |
A methylation matrix with CpG identifiers as row names and samples as columns (a data frame with a CpG-identifier column is also accepted), or a large matrix held on disk as an HDF5-backed matrix. |
clocks |
Either |
generation |
Optional generation to restrict to (e.g. |
platform |
Optional native platform to restrict to (e.g. |
min.perc |
Minimum fraction of a clock's CpGs that must be present for
it to be computed (otherwise |
storage |
How the estimates are held: |
... |
Passed to |
A "methylclock" object.
The EEAA of the literature (Chen et al. 2016): the Hannum clock blended
with three immune cell estimators — plasmablasts, exhausted CD8+ T cells
(CD28-CD45RA-) and naive CD8+ T cells — with weights fixed once in the
Women's Health Initiative, then regressed on chronological age; EEAA is
the residual. By construction it rises with the immune shifts of ageing,
which is what makes it "extrinsic", and it is the specific measure
researchers mean by "EEAA" (its intrinsic sibling is IEAA).
EEAA(x, age, betas)EEAA(x, age, betas)
x |
A |
age |
Numeric vector of chronological ages, one per sample in
|
betas |
The methylation data the estimate came from: a matrix (or data frame, HDF5-backed matrix, or accepted Bioconductor container) with CpG identifiers as row names and one column per sample. Only the rows of the estimator CpGs are read, so an on-disk matrix is not loaded whole. |
The three cell estimators are computed here from the methylation data
itself, as linear combinations of published CpG coefficients — the same
route the original software takes — so no external deconvolution is
needed; betas should be blood 450K/EPIC data. All numeric
parameters (the estimators' CpG coefficients and the fixed
centers, scales and weights of the blend) are the ones published in
European patent EP 3 494 210 B1. The estimators' additive intercepts are
not published; they shift every sample equally and cancel in the residual,
which is why this function returns EEAA and not the intermediate
biological-age value. If some of the estimator CpGs are absent from your
array a warning reports it: the scores then deviate from the published
estimators and comparability with other cohorts' EEAA weakens.
A data frame with columns id and EEAA.
Chen BH, Marioni RE, Colicino E, et al. (2016). DNA methylation-based measures of biological age. Aging 8(9):1844–1865. European patent EP 3 494 210 B1 (2024). DNA methylation based biological age prediction.
IEAA, ageAcceleration,
ageAccelerationChen
## Not run: res <- DNAmAge(betas, clocks = "Hannum") head(EEAA(res, age = pheno$age, betas = betas)) ## End(Not run)## Not run: res <- DNAmAge(betas, clocks = "Hannum") head(EEAA(res, age = pheno$age, betas = betas)) ## End(Not run)
The IEAA of the literature (Chen et al. 2016): the Horvath clock's
estimate regressed on chronological age and seven blood immune cell
covariates — naive CD8+ T cells, exhausted CD8+ T cells
(CD28-CD45RA-), plasmablasts, CD4+ T cells, natural killer cells,
monocytes and granulocytes — whose residual reflects cell-intrinsic
ageing independent of shifts in blood cell composition. This is the
specific measure researchers mean by "IEAA" (its extrinsic sibling is
EEAA). The same adjustment idea for any clock and any cell
panel is available as the residualCells column of
ageAcceleration, under that plainer name.
IEAA(x, age, betas, cell_counts = NULL)IEAA(x, age, betas, cell_counts = NULL)
x |
A |
age |
Numeric vector of chronological ages, one per sample in
|
betas |
The methylation data the estimate came from: a matrix (or data frame, HDF5-backed matrix, or accepted Bioconductor container) with CpG identifiers as row names and one column per sample; blood 450K/EPIC data. The rare-subset estimators read only their CpG rows. |
cell_counts |
Optional samples-by-cell-types matrix (for example
from |
The three rare subsets are computed from the methylation data itself, as
the published linear combinations of CpGs the original software uses
(parameters from European patent EP 3 494 210 B1; their additive
intercepts are not published, but constants are absorbed by the
regression, so the residual is unaffected). The four common cell types
are taken from cell_counts if you provide them, or estimated with
cellCounts otherwise; granulocytes are used directly when
a Gran column exists, and otherwise formed as neutrophils plus
eosinophils. Reference-based deconvolution estimates are not numerically
identical to the original calculator's own estimates of these four, so
the measure is canonical in structure and covariates while small
numerical differences with that software remain possible. If some of the
estimator CpGs are absent from your array a warning reports it.
A data frame with columns id and IEAA.
Chen BH, Marioni RE, Colicino E, et al. (2016). DNA methylation-based measures of biological age. Aging 8(9):1844–1865. European patent EP 3 494 210 B1 (2024). DNA methylation based biological age prediction.
EEAA, ageAcceleration,
cellCounts
## Not run: res <- DNAmAge(betas, clocks = "Horvath") head(IEAA(res, age = pheno$age, betas = betas)) ## End(Not run)## Not run: res <- DNAmAge(betas, clocks = "Horvath") head(IEAA(res, age = pheno$age, betas = betas)) ## End(Not run)
Fills the missing values of a methylation matrix from each CpG's nearest
CpGs – those that co-vary with it across the observed samples – rather than
from its marginal mean, so co-methylation is respected. A row set within
maxp is imputed exactly; a larger one is first split into blocks of
similar CpGs (through a BigDataStatMeth SVD embedding) and each block is
imputed exactly. This is the same imputation compute_clocks
applies with impute = "knn"; use it to impute a matrix once and reuse
it across analyses.
imputeKNN(x, k = 10, maxp = 1500, rowmax = 0.5, colmax = 0.8)imputeKNN(x, k = 10, maxp = 1500, rowmax = 0.5, colmax = 0.8)
x |
A methylation matrix with CpG identifiers as row names and samples as columns (a data frame with a CpG-identifier column is also accepted). |
k |
Number of neighbouring CpGs averaged for each imputed value. |
maxp |
Largest block of CpGs imputed exactly; larger row sets are split into similar blocks first. |
rowmax |
If a CpG has a greater fraction of samples missing than this, it is filled with the per-sample mean instead of by KNN. |
colmax |
If a sample has a greater fraction of CpGs missing than this, a warning is raised (the sample is very sparse); imputation still proceeds. |
The matrix with missing values filled, same shape and dimnames.
data(methylclock_betas) m <- methylclock_betas[seq_len(200), ] m[5, 2] <- NA m[40, c(1, 3)] <- NA filled <- imputeKNN(m) filled[c(5, 40), seq_len(3)]data(methylclock_betas) m <- methylclock_betas[seq_len(200), ] m[5, 2] <- NA m[40, c(1, 3)] <- NA filled <- imputeKNN(m) filled[c(5, 40), seq_len(3)]
The reference panels shipped with the package (via the data layer), each a
name accepted by cellCounts's reference argument.
cellCounts() also accepts the higher-resolution
"FlowSorted.Blood.EPIC" and "FlowSorted.BloodExtended.EPIC"
panels, which are not shipped and are used only from your own installation of
those packages (see cellCounts).
listCellReferences()listCellReferences()
A character vector of the shipped reference-panel names.
listCellReferences()listCellReferences()
Writes a methylation matrix to an HDF5 file and returns it as an on-disk
matrix that methylclock can estimate block by block, without
holding the whole array in memory. Useful for arrays too large to work with
comfortably in RAM: convert once, then pass the result (and let the in-memory
copy go).
mc_to_hdf5( x, file = NULL, overwrite = FALSE, sep = NULL, header = TRUE, row.names = TRUE, compression = NULL )mc_to_hdf5( x, file = NULL, overwrite = FALSE, sep = NULL, header = TRUE, row.names = TRUE, compression = NULL )
x |
A methylation matrix or data frame (CpG identifiers as row names / a CpG-identifier column, samples as columns), or the path to a delimited text file laid out the same way. A text file is imported without being loaded into memory first. |
file |
Destination HDF5 file. Pass a real path to keep it; a session
temporary file is used when |
overwrite |
Overwrite |
sep |
For a text file, the field separator (default: auto by extension,
comma for |
header, row.names
|
For a text file, whether it has a header row / a
leading column of CpG identifiers. Both default to |
compression |
HDF5 compression level (0-9); |
An on-disk (HDF5-backed) matrix, open for reading; pass it to
methylclock and close() it when done.
data(methylclock_betas) f <- tempfile(fileext = ".h5") hm <- mc_to_hdf5(methylclock_betas, f) res <- methylclock(hm, clocks = "Horvath") head(as.data.frame(res)) close(hm) unlink(f)data(methylclock_betas) f <- tempfile(fileext = ".h5") hm <- mc_to_hdf5(methylclock_betas, f) res <- methylclock(hm, clocks = "Horvath") head(as.data.frame(res)) close(hm) unlink(f)
Sets or returns the ordered list of sources consulted to resolve a resource.
The chain is stored in options(methylclockData.backend=).
mcd_backends(backends)mcd_backends(backends)
backends |
Optional character vector to set, e.g. |
Order matters: the first backend that resolves an identifier wins. Supported backends:
"local"a directory of .rda files with an
accompanying MANIFEST.csv (set its location with
options(methylclockData.local_root=) or the
METHYLCLOCKDATA_MIRROR environment variable).
"eh"ExperimentHub; resolves the resource by the manifest's
eh_id column. Requires the ExperimentHub package.
"zenodo"downloads and caches the resource for the record in
the manifest's zenodo_doi column.
A backend returns nothing for a resource whose manifest row has no id for that source, so the chain falls through to the next backend.
The current backend chain; returned invisibly when setting.
mcd_backends() # the current chain old <- mcd_backends() mcd_backends(c("local", "eh")) # set a chain mcd_backends(old) # and restore itmcd_backends() # the current chain old <- mcd_backends() mcd_backends(c("local", "eh")) # set a chain mcd_backends(old) # and restore it
Empties the private cache of resolved resources. Resources are re-resolved through the backend chain the next time they are needed.
mcd_cache_clear()mcd_cache_clear()
Invisibly NULL.
mcd_cache_clear()mcd_cache_clear()
Reads and caches the manifest that describes the available resources
(identifier, stored object name, class, dimensions, columns, size, checksum
and source). A copy ships with the package; a configured local data root
(see mcd_backends) takes precedence when present.
mcd_manifest()mcd_manifest()
A data frame, one row per resource identifier.
man <- mcd_manifest() head(man$id)man <- mcd_manifest() head(man$id)
The single access point for coefficient objects. Resolves id through
the backend chain (see mcd_backends), caches the result in a
private environment and returns it.
mcd_resource(id, verify = FALSE, cache = TRUE)mcd_resource(id, verify = FALSE, cache = TRUE)
id |
Resource identifier, e.g. |
verify |
Logical; verify the object's checksum when possible. Default
|
cache |
Logical; use and populate the private cache. Default
|
The resource object in canonical form (see normalize_coef).
mcd_manifest, mcd_backends,
clock_register
coef <- mcd_resource("coefHorvath") head(coef)coef <- mcd_resource("coefHorvath") head(coef)
Some resources are files read directly by the C++ layer (e.g. an HDF5 bundle
of neural-network weights) rather than objects loaded into R. This resolves
id through the backend chain and returns a local path: the file on
disk for the local backend, or a downloaded-and-cached copy for a
remote backend.
mcd_resource_file(id)mcd_resource_file(id)
id |
Resource identifier (a row of |
A path to the resource file.
Unified entry point: estimates any registered clocks from a methylation
matrix. DNAmAge and DNAmGA are convenience
wrappers that restrict the selection to non-gestational and gestational
clocks respectively.
methylclock( x, clocks = "all", generation = NULL, platform = NULL, min.perc = 0.8, impute = c("mean", "reference", "none", "knn"), min.perc.sample = 0, storage = "auto", ... )methylclock( x, clocks = "all", generation = NULL, platform = NULL, min.perc = 0.8, impute = c("mean", "reference", "none", "knn"), min.perc.sample = 0, storage = "auto", ... )
x |
A methylation matrix with CpG identifiers as row names and samples as columns (a data frame with a CpG-identifier column is also accepted), or a large matrix held on disk as an HDF5-backed matrix. |
clocks |
Either |
generation |
Optional generation to restrict to (e.g. |
platform |
Optional native platform to restrict to (e.g. |
min.perc |
Minimum fraction of a clock's CpGs that must be present for
it to be computed (otherwise |
impute |
How missing values among the present CpGs are filled:
|
min.perc.sample |
Per-sample coverage floor: a sample with fewer than
this fraction of a clock's present CpGs observed is returned |
storage |
How the estimates are held: |
... |
Passed to |
A "methylclock" object.
DNAmAge, DNAmGA, persist,
clock_list
data(methylclock_betas) res <- methylclock(methylclock_betas, clocks = c("Horvath", "Levine")) head(as.data.frame(res))data(methylclock_betas) res <- methylclock(methylclock_betas, clocks = c("Horvath", "Levine")) head(as.data.frame(res))
A small, complete methylation beta matrix (real data) provided so the
vignette can demonstrate the impute= options on an actual matrix:
compute the clocks on the full data, blank out a fraction of the values, and
compare mean and K-nearest-neighbour imputation against the truth. Unlike the
other bundled objects, this one is the model *input* (CpGs and samples), not
the estimates. It is a subset of the example matrix shipped with earlier
versions of the package, restricted to the CpGs of the clocks it fully covers
(Horvath, Levine, Wu) to keep it small and let those
clocks run without missing CpGs.
data(methylclock_betas)data(methylclock_betas)
A numeric matrix with 919 rows (CpGs, as row names) and 16 columns (samples). Values are methylation betas in \[0, 1]; there are no missing values.
Subset of MethylationDataExample55 from earlier releases of
this package; a public human methylation series.
data(methylclock_betas) methylclock_betas[1:3, 1:3] # complete-data estimates, then the effect of imputing blanked-out values truth <- methylclock(methylclock_betas, clocks = c("Horvath", "Levine", "Wu"))data(methylclock_betas) methylclock_betas[1:3, 1:3] # complete-data estimates, then the effect of imputing blanked-out values truth <- methylclock(methylclock_betas, clocks = c("Horvath", "Levine", "Wu"))
Clock estimates together with estimated blood cell proportions for an adult
whole-blood cohort (795 samples, EPIC), so the intrinsic (cell-adjusted) age
acceleration can be computed live. The cell proportions were estimated with
cellCounts from the methylation matrix (not shipped).
data(methylclock_cells)data(methylclock_cells)
A data frame with 795 rows (samples) and columns:
Sample identifier.
Chronological age in years.
Reported sex (F/M).
Estimated age in years.
Estimated proportion of each blood cell type.
Grady Trauma Project, GEO accession https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE132203.
data(methylclock_cells) cells <- c("Bcell", "CD4T", "CD8T", "Eos", "Mono", "Neu", "NK") head(ageAcceleration(methylclock_cells, age = methylclock_cells$age, cell_counts = as.matrix(methylclock_cells[, cells])))data(methylclock_cells) cells <- c("Bcell", "CD4T", "CD8T", "Eos", "Mono", "Neu", "NK") head(ageAcceleration(methylclock_cells, age = methylclock_cells$age, cell_counts = as.matrix(methylclock_cells[, cells])))
Per-sample epigenetic clock estimates for a public whole-blood methylation series with known chronological age (GEO accession GSE40279, 656 adult blood samples aged 19–101). The raw methylation matrix is not shipped; this table holds only the computed estimates, chronological age and sex, which is what the plotting functions need. It lets the vignette and the plot examples run on real data without downloading anything.
data(methylclock_demo)data(methylclock_demo)
A data frame with 656 rows (samples) and columns:
Sample identifier.
Chronological age in years.
Reported sex, a factor with levels F and M.
Reported ethnicity, a factor with levels
Caucasian (426, "Caucasian - European" in the source) and
Hispanic (230, "Hispanic - Mexican"). It lets the cohort be
used as two population-specific references in
plotReferenceRange.
Estimated age in years from each clock.
Estimated telomere length.
Hannum et al. (2013), GEO accession https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE40279.
data(methylclock_demo) plotDNAmAge(methylclock_demo, age = methylclock_demo$age)data(methylclock_demo) plotDNAmAge(methylclock_demo, age = methylclock_demo$age)
Per-sample clock estimates for a public cohort in which the same children
were sampled at several ages — the shape the longitudinal tools
(clockTrajectories, trajectoryRates,
plotTrajectories) work on. 67 children from Goma, DR Congo,
followed from birth to about age 3 (venous blood, EPIC; GEO accession
GSE224573), 164 observations in all, technical replicates flagged rather
than removed. The cohort carries substantial peripartum trauma exposure,
so it illustrates the mechanics of longitudinal analysis; it does not
stand for children in general.
data(methylclock_longitudinal)data(methylclock_longitudinal)
A data frame with 164 rows (observations) and columns id,
subject (the child), age (decimal age at the visit),
sex, replicate, and one numeric column per computed clock.
GEO accession https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE224573.
data(methylclock_longitudinal) with(methylclock_longitudinal, table(table(subject)))data(methylclock_longitudinal) with(methylclock_longitudinal, table(table(subject)))
Per-sample clock estimates, chronological age and sex for public blood
cohorts shipped as selectable references for
plotReferenceRange (the raw methylation is not shipped).
Each is a single, specific cohort and its bands describe it alone:
Swedish (GSE87571), 729 adults aged 14–94 from a population-based
cohort recorded as disease state: normal (3 source samples ship no
age and are excluded); GENOA (GSE210255), 1394 African American
adults recruited through hypertensive sibships (enriched for hypertension,
with related samples); Han Chinese controls (GSE116379), the 79
non-schizophrenia controls, all aged 45–51, of a schizophrenia and
prenatal-famine study in Changchun; and DRC mothers (GSE224363),
97 mothers aged 14–42 sampled around delivery in Goma, DR Congo, in a
cohort with substantial trauma exposure.
data(methylclock_references)data(methylclock_references)
A data frame with 2299 rows (samples) and columns cohort
(factor), id, age, sex, and one numeric column per
computed clock.
GEO accessions https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE87571, https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE210255, https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE116379 and https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE224363.
data(methylclock_references) table(methylclock_references$cohort)data(methylclock_references) table(methylclock_references$cohort)
Estimates for a public whole-blood cohort with recorded smoking status (464 adults, 450K), used to show a non-age (trait) clock at work: the methylation smoking predictor separates never, former and current smokers. As with the other bundled data, only the estimates and phenotype are shipped, not the methylation matrix.
data(methylclock_smoking)data(methylclock_smoking)
A data frame with 464 rows (samples) and columns:
Sample identifier.
Smoking status, a factor with levels never,
former and current.
Chronological age in years.
Reported sex (F/M).
The methylation smoking-predictor score.
Estimated age in years, for context.
Tsaprouni et al. (2014), GEO accession https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE50660.
data(methylclock_smoking) tapply(methylclock_smoking$McCartney.Smoking, methylclock_smoking$smoking, median)data(methylclock_smoking) tapply(methylclock_smoking$McCartney.Smoking, methylclock_smoking$smoking, median)
Per-sample clock estimates for two public blood cohorts that were not used to
train these clocks, so they read the clocks' accuracy out of sample: an adult
cohort and a paediatric one. It is what the vignette's validation section runs
on. As with methylclock_demo, only the estimates and age/sex are
shipped, not the methylation matrices.
data(methylclock_validation)data(methylclock_validation)
A data frame with 929 rows (samples) and columns:
Which cohort the sample belongs to, a factor:
"adult (GSE132203)" (795 adults, whole blood, EPIC) or
"pediatric (GSE36054)" (134 healthy children aged 1–17, blood,
450K).
Sample identifier.
Chronological age in years.
Reported sex, a factor with levels F and M (may be
NA for the paediatric cohort).
Estimated age in years from each clock.
Grady Trauma Project, GEO accession https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE132203; and Alisch et al. (2012), GEO accession https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE36054.
data(methylclock_validation) adult <- subset(methylclock_validation, cohort == "adult (GSE132203)") plotDNAmAge(adult, age = adult$age, clocks = c("Horvath", "BLUP"))data(methylclock_validation) adult <- subset(methylclock_validation, cohort == "adult (GSE132203)") plotDNAmAge(adult, age = adult$age, clocks = c("Horvath", "BLUP"))
Low-level constructor for the object returned by the clock estimation
functions. Most users obtain one from methylclock,
DNAmAge or DNAmGA rather than calling this
directly.
new_methylclock( values, clocks, storage = "memory", handle = NULL, persistent = FALSE, meta = list() )new_methylclock( values, clocks, storage = "memory", handle = NULL, persistent = FALSE, meta = list() )
values |
A data frame of estimates (one row per sample, one column per
clock) when it is held in memory; |
clocks |
Character vector of the clocks that were computed. |
storage |
Where the authoritative estimates live: |
handle |
For |
persistent |
|
meta |
Named list of metadata (e.g. sample count, call, warnings). |
An object of class "methylclock".
est <- data.frame(id = c("s1", "s2"), Horvath = c(41.2, 55.8)) res <- new_methylclock(est, clocks = "Horvath") as.data.frame(res)est <- data.frame(id = c("s1", "s2"), Horvath = c(41.2, 55.8)) res <- new_methylclock(est, clocks = "Horvath") as.data.frame(res)
Coerces a coefficient object to the single canonical layout used by the
estimation engine (a data frame with a character CpGmarker column and
numeric coefficient columns), so that downstream code does not handle the
shape differences between clocks. Called by mcd_resource when a
resource is loaded. Objects that are not coefficient tables (e.g. cell-type
references) are returned unchanged.
normalize_coef(obj, id = NULL, row = NULL)normalize_coef(obj, id = NULL, row = NULL)
obj |
The raw resource object as loaded by a backend. |
id |
The resource identifier (unused; kept for dispatch). |
row |
The manifest row for |
The object in canonical form.
# a table whose CpG column is not called CpGmarker raw <- data.frame(probeID = c("(Intercept)", "cg00075967"), weight = c(0.5, -0.02)) normalize_coef(raw) # a bare CpG vector becomes a one-column table normalize_coef(c("cg00075967", "cg00374717"))# a table whose CpG column is not called CpGmarker raw <- data.frame(probeID = c("(Intercept)", "cg00075967"), weight = c(0.5, -0.02)) normalize_coef(raw) # a bare CpG vector becomes a one-column table normalize_coef(c("cg00075967", "cg00374717"))
Save an object to a file so it outlives the current session.
persist(x, file, ...) ## S3 method for class 'methylclock' persist(x, file, overwrite = FALSE, ...)persist(x, file, ...) ## S3 method for class 'methylclock' persist(x, file, overwrite = FALSE, ...)
x |
The object to persist. |
file |
Destination file path. |
... |
Further arguments passed to methods. |
overwrite |
Overwrite |
For a "methylclock" result the estimates already live in an
HDF5 file; when that file is a session temporary, persisting copies it to
file (no recomputation) and verifies the copy. A result held only in
memory is written out to file. The returned object references the
persistent file.
The object, updated to reference the persistent file (invisibly).
data(methylclock_betas) res <- methylclock(methylclock_betas, clocks = "Horvath") f <- tempfile(fileext = ".h5") res <- persist(res, f) file.exists(f) unlink(f)data(methylclock_betas) res <- methylclock(methylclock_betas, clocks = "Horvath") f <- tempfile(fileext = ".h5") res <- persist(res, f) file.exists(f) unlink(f)
Splits the per-clock age acceleration (the residual of predicted age on chronological age) by a categorical sample variable, such as sex or a case/control label. A group whose boxes sit consistently above zero looks epigenetically older than its chronological age across clocks, which is the association these plots are meant to surface.
plotAccelerationByGroup(x, age, group, clocks = NULL)plotAccelerationByGroup(x, age, group, clocks = NULL)
x |
A |
age |
Numeric vector of chronological ages, one per sample in |
group |
Categorical vector (factor or character), one value per sample
in |
clocks |
Optional character vector of clock names. Default: the age
clocks present in |
A ggplot object.
data(methylclock_demo) d <- methylclock_demo plotAccelerationByGroup(d, age = d$age, group = d$sex, clocks = c("Horvath", "Hannum", "Levine"))data(methylclock_demo) d <- methylclock_demo plotAccelerationByGroup(d, age = d$age, group = d$sex, clocks = c("Horvath", "Hannum", "Levine"))
Age acceleration is the residual of the linear fit of predicted age on
chronological age: a positive value is a sample that looks older than its
years for it. Two views: "box" shows the spread per clock;
"heatmap" shows every sample and clock, so a
sample accelerated across all clocks (a consistent outlier) stands out.
plotAgeAcceleration( x, age, clocks = NULL, type = c("box", "heatmap"), color_by = c("auto", "target", "none") )plotAgeAcceleration( x, age, clocks = NULL, type = c("box", "heatmap"), color_by = c("auto", "target", "none") )
x |
A |
age |
Numeric vector of chronological ages, one per sample in |
clocks |
Optional character vector of clock names. Default: the age
clocks present in |
type |
|
color_by |
For |
A ggplot object.
data(methylclock_demo) d <- methylclock_demo plotAgeAcceleration(d, age = d$age, clocks = c("Horvath", "Hannum", "Levine"))data(methylclock_demo) d <- methylclock_demo plotAgeAcceleration(d, age = d$age, clocks = c("Horvath", "Hannum", "Levine"))
A method-agreement view that complements the scatter in plotDNAmAge:
for each age clock, the difference (predicted minus real age) against the mean
of the two. The solid line is the mean difference (the clock's bias) and the
dashed lines are the 95% limits of agreement (bias +/- 1.96 SD). A bias away
from zero is a miscalibrated clock; wide limits mean noisy predictions.
plotBlandAltman(x, age, clocks = NULL, ncol = 3)plotBlandAltman(x, age, clocks = NULL, ncol = 3)
x |
A |
age |
Numeric vector of chronological ages, one per sample in |
clocks |
Optional clock names. Default: the age clocks in |
ncol |
Number of facet columns. Default 3. |
A ggplot object.
data(methylclock_demo) d <- methylclock_demo plotBlandAltman(d, age = d$age, clocks = c("Horvath", "Hannum", "Levine"))data(methylclock_demo) d <- methylclock_demo plotBlandAltman(d, age = d$age, clocks = c("Horvath", "Hannum", "Levine"))
Correlation of the per-sample estimates across every pair of clocks, as a heatmap. Clocks that measure similar things correlate highly; this is the clearest view of how much the clocks agree (and where they are redundant).
plotClockCorrelation(x, clocks = NULL, method = "pearson", cluster = TRUE)plotClockCorrelation(x, clocks = NULL, method = "pearson", cluster = TRUE)
x |
A |
clocks |
Optional character vector of clock names to include. Default:
all numeric clocks in |
method |
Correlation method passed to |
cluster |
Reorder clocks by hierarchical clustering so correlated ones
sit together. Default |
A ggplot object.
data(methylclock_demo) plotClockCorrelation(methylclock_demo, clocks = c("Horvath", "Hannum", "Levine", "TL"))data(methylclock_demo) plotClockCorrelation(methylclock_demo, clocks = c("Horvath", "Hannum", "Levine", "TL"))
A smoothed density curve of the per-sample values of every clock. With
group, the curves of the groups are drawn together in each panel, so
a shift, a widening or an extra mode between groups shows up as a visible
change of shape.
plotClockDensities( x, group = NULL, clocks = NULL, what = c("estimate", "acceleration"), style = c("overlay", "mirror", "ridge"), age = NULL, ncol = 4, min_n = 5L )plotClockDensities( x, group = NULL, clocks = NULL, what = c("estimate", "acceleration"), style = c("overlay", "mirror", "ridge"), age = NULL, ncol = 4, min_n = 5L )
x |
A |
group |
Optional categorical vector (factor or character), one value
per sample in |
clocks |
Optional character vector of clock names. Default: all numeric
clocks in |
what |
Plot the raw |
style |
|
age |
Numeric vector of chronological ages, one per sample in
|
ncol |
Facet columns for |
min_n |
Minimum number of values a clock/group needs for a curve; below it, the values are drawn as ticks. Default 5. |
Three layouts are available through style. "overlay" (the
default) superimposes the group curves in one panel per clock.
"mirror" needs exactly two groups and draws one upwards and the
other downwards from a shared baseline, so any asymmetry between the two
shapes is the figure itself. "ridge" stacks all the clocks in a
single column, one ridge per clock, giving a one-look overview across
clocks; because that puts every clock on the same axis, the estimates are
z-scored per clock there (accelerations are already in shared units).
With what = "acceleration" the curves are drawn over the age
acceleration (the residual of predicted age regressed on chronological age)
instead of the raw estimates. The distinction matters when the groups differ
in chronological age: the raw estimates then differ with it, so their
densities separate for that reason alone, while the acceleration removes
that trend and leaves the scales on which the group shapes are comparable.
A density smoothed from a handful of points takes its shape mostly from the
smoothing, not from the data, so any clock/group with fewer than
min_n values is drawn as ticks along its baseline rather than as a
curve.
A ggplot object.
data(methylclock_demo) d <- methylclock_demo cl <- c("Horvath", "Hannum", "Levine") plotClockDensities(d, group = d$sex, clocks = cl) plotClockDensities(d, group = d$sex, clocks = cl, what = "acceleration", age = d$age, style = "mirror") plotClockDensities(d, group = d$sex, clocks = cl, style = "ridge")data(methylclock_demo) d <- methylclock_demo cl <- c("Horvath", "Hannum", "Levine") plotClockDensities(d, group = d$sex, clocks = cl) plotClockDensities(d, group = d$sex, clocks = cl, what = "acceleration", age = d$age, style = "mirror") plotClockDensities(d, group = d$sex, clocks = cl, style = "ridge")
A violin plus box of every clock's per-sample values, to see the spread and
any skew or outliers at a glance. Because clocks live on different scales
(years, counts, scores), scale = TRUE z-scores each clock to
one axis; scale = FALSE facets them on their own scales.
plotClockDistributions( x, clocks = NULL, scale = FALSE, ncol = 4, color_by = c("auto", "target", "none") )plotClockDistributions( x, clocks = NULL, scale = FALSE, ncol = 4, color_by = c("auto", "target", "none") )
x |
A |
clocks |
Optional character vector of clock names. Default: all numeric
clocks in |
scale |
z-score each clock onto one axis ( |
ncol |
Facet columns when |
color_by |
Colour the violins by clock family (its registry
|
A ggplot object.
data(methylclock_demo) plotClockDistributions(methylclock_demo, clocks = c("Horvath", "Hannum", "Levine"))data(methylclock_demo) plotClockDistributions(methylclock_demo, clocks = c("Horvath", "Hannum", "Levine"))
For each age clock, a scatter of the predicted age against the samples'
chronological age, with the identity line (predicted equal to
chronological, drawn dashed), a linear fit and its confidence band, and
the fit's in the title. Clocks that do not output an age
(mitotic counts, trait scores) are dropped unless named in clocks.
plotDNAmAge(x, age, clocks = NULL, group = NULL, ncol = 3, outlier_sd = 3)plotDNAmAge(x, age, clocks = NULL, group = NULL, ncol = 3, outlier_sd = 3)
x |
A |
age |
Numeric vector of chronological ages, one per sample in the same
order as |
clocks |
Optional character vector of clock names to plot. Default: the
age clocks present in |
group |
Optional categorical vector (factor or character), one value
per sample in |
ncol |
Number of facet columns. Default 3. |
outlier_sd |
Points whose residual from the clock's linear fit exceeds
this many standard deviations are highlighted, to flag predictions that fall
well off the trend. |
With group, the points and the fitted lines are drawn per group: a
group whose line runs above the other's is predicted older at the same
chronological age, and lines with different slopes mean that gap changes
along the age range. The outlier highlight is turned off in that case, so
its colour cannot be mistaken for a group.
A ggplot object.
data(methylclock_demo) d <- methylclock_demo plotDNAmAge(d, age = d$age, clocks = c("Horvath", "Hannum", "Levine")) plotDNAmAge(d, age = d$age, group = d$sex, clocks = "Horvath")data(methylclock_demo) d <- methylclock_demo plotDNAmAge(d, age = d$age, clocks = c("Horvath", "Hannum", "Levine")) plotDNAmAge(d, age = d$age, group = d$sex, clocks = "Horvath")
One row per clock, in one of two layouts. The default forest draws the
difference between the two groups' means with its confidence interval, all
on one shared axis — a one-look answer to which clocks separate the
groups and by how much. The dashed line at zero means "no difference": an
interval that reaches it is compatible with none, and the points of
intervals that leave zero out are drawn filled. style = "dumbbell"
draws instead the two group means joined by a segment, so both levels are
visible rather than only their difference — one group being high tells a
different story than the other being low, even when the gap is the same.
plotGroupDifference( x, group, clocks = NULL, what = c("acceleration", "estimate"), style = c("forest", "dumbbell"), age = NULL, conf = 0.95 )plotGroupDifference( x, group, clocks = NULL, what = c("acceleration", "estimate"), style = c("forest", "dumbbell"), age = NULL, conf = 0.95 )
x |
A |
group |
Categorical vector (factor or character) with exactly two
levels, one value per sample in |
clocks |
Optional character vector of clock names. Default: the age
clocks present in |
what |
Compare the |
style |
|
age |
Numeric vector of chronological ages, one per sample in
|
conf |
Confidence level of the intervals (Welch t). Default 0.95. |
By default the comparison is on the age acceleration (the residual of
predicted age regressed on chronological age), which requires age.
When the groups differ in chronological age, the raw estimates differ with
it, so the acceleration is the scale on which a group difference means
something beyond age. When no ages are known, what = "estimate"
compares the raw estimates instead; any difference in chronological age
between the groups is then part of the number.
A ggplot object.
data(methylclock_demo) d <- methylclock_demo cl <- c("Horvath", "Hannum", "Levine") plotGroupDifference(d, group = d$sex, age = d$age, clocks = cl) plotGroupDifference(d, group = d$sex, what = "estimate", clocks = cl, style = "dumbbell")data(methylclock_demo) d <- methylclock_demo cl <- c("Horvath", "Hannum", "Levine") plotGroupDifference(d, group = d$sex, age = d$age, clocks = cl) plotGroupDifference(d, group = d$sex, what = "estimate", clocks = cl, style = "dumbbell")
The growth-chart idea applied to epigenetic age: for each clock, the percentile bands of a reference cohort along chronological age (5th–95th light, 25th–75th darker, median line), with the user's samples drawn on top. A sample inside the central band is where most of the reference sits at that age; one outside the light band is where fewer than one in ten of the reference samples fall. Where the reference has too few samples near an age, no band is drawn — there is no rule to measure against there.
plotReferenceRange( x, age, reference = "auto", ref_label = NULL, clocks = NULL, window = 5, min_n = 20L, ncol = 2 )plotReferenceRange( x, age, reference = "auto", ref_label = NULL, clocks = NULL, window = 5, min_n = 20L, ncol = 2 )
x |
A |
age |
Numeric vector of chronological ages, one per sample in
|
reference |
|
ref_label |
Label for the reference shown in the subtitle. Defaults to
the shipped cohort's name, or |
clocks |
Optional character vector of clock names. Default: the age
clocks present in both |
window |
Half-width, in years, of the sliding age window the percentiles are computed in. Default 5. |
min_n |
Minimum reference samples a window needs for its band to be drawn. Default 20. |
ncol |
Facet columns. Default 2. |
The bands describe the named reference cohort, nothing more: a
reference measures well what resembles it (population, tissue, array,
processing), so the most meaningful reference is your own control samples,
passed as a data frame. When none is given, the function falls back to a
cohort shipped with the package, says so in a message, and the subtitle
always states which reference was used and its size. The shipped options
(see Details) are offered so that something can be seen with a
stated caveat rather than nothing without data; they are single, specific
cohorts and none of them stands for people in general.
"GSE40279": 656 adults aged 19–101 (Hannum 2013, blood,
450K). This is the training cohort of the Hannum, BLUP and EN clocks,
so its bands for those clocks read tighter than a general cohort's
would. Its reported ethnicity splits it into two further references,
"GSE40279-Caucasian" (426) and "GSE40279-Hispanic"
(230): a reference measures best the population it comes from.
"GSE132203": 795 adults aged 18–77 (Grady Trauma Project,
blood, EPIC), a cohort selected for trauma exposure.
"GSE36054": 134 children aged 1–17 (blood, 450K).
"GSE87571": 729 Swedish adults aged 14–94 (blood, 450K), a
population-based cohort recorded as disease state: normal.
"GSE210255": 1394 African American adults (GENOA, blood,
EPIC), recruited through hypertensive sibships — enriched for
hypertension, with related samples, and spanning roughly ages 25–90
with most samples between 40 and 75.
"GSE116379": 79 Han Chinese adults from Changchun (blood,
450K) — the non-schizophrenia controls of a schizophrenia and
prenatal-famine study, all aged 45–51, so its bands exist only in
that narrow window.
"GSE224363": 97 mothers from Goma, DR Congo (venous blood,
EPIC), sampled around delivery in a cohort with substantial trauma
exposure, aged 14–42.
A ggplot object.
data(methylclock_demo) pred <- methylclock_demo[seq_len(10), ] plotReferenceRange(pred, age = pred$age)data(methylclock_demo) pred <- methylclock_demo[seq_len(10), ] plotReferenceRange(pred, age = pred$age)
One point per sample, placed by two summaries of its clock estimates once
each clock is z-scored across samples (so clocks on different scales become
comparable): the consensus — the median z-score, where the clocks jointly
place the sample relative to the rest — and the discordance — the
standard deviation of those z-scores, how much the clocks contradict each
other on that sample. Most samples form a low band; a sample far above it
is one the clocks genuinely disagree about, which can be a technical
problem with that sample or a biology worth a second look. The
label_n most discordant samples are highlighted and labelled by id.
plotSampleDiscordance( x, clocks = NULL, what = c("estimate", "acceleration"), age = NULL, label_n = 5L )plotSampleDiscordance( x, clocks = NULL, what = c("estimate", "acceleration"), age = NULL, label_n = 5L )
x |
A |
clocks |
Optional character vector of clock names. Default: the clocks
in |
what |
Summarise the raw |
age |
Numeric vector of chronological ages, one per sample in
|
label_n |
How many of the most discordant samples to highlight and label. Default 5; 0 turns the highlight off. |
This complements sampleQC, which scores how jointly
extreme a sample is: a sample can be extreme with every clock in
agreement, or unremarkable on average while the clocks disagree.
With what = "acceleration" (requires age) the same two
summaries are computed on the age acceleration instead, so the consensus
reads as how jointly accelerated the sample is rather than how old.
A ggplot object.
data(methylclock_demo) plotSampleDiscordance(methylclock_demo)data(methylclock_demo) plotSampleDiscordance(methylclock_demo)
Runs a principal component analysis on the per-sample clock estimates (a samples-by-clocks matrix) and plots the first two components, optionally coloured by a per-sample variable such as age or sex. It shows how samples arrange themselves across the whole panel of clocks: for a healthy age series the leading component typically tracks chronological age.
plotSamplePCA(x, color = NULL, color_label = "group", clocks = NULL)plotSamplePCA(x, color = NULL, color_label = "group", clocks = NULL)
x |
A |
color |
Optional per-sample variable to colour points by (numeric for a
continuous scale, factor/character for a discrete one), in |
color_label |
Legend title for |
clocks |
Optional character vector of clock names to include in the PCA. Default: all numeric clock columns. |
A ggplot object.
data(methylclock_demo) d <- methylclock_demo plotSamplePCA(d, color = d$age, color_label = "age", clocks = c("Horvath", "Hannum", "Levine"))data(methylclock_demo) d <- methylclock_demo plotSamplePCA(d, color = d$age, color_label = "age", clocks = c("Horvath", "Hannum", "Levine"))
The longitudinal view: for each clock, one thin line per subject joining
its visits (chronological age on the x axis, the clock's estimate on the
y axis), with the identity line dashed — a subject moving parallel to it
ages one epigenetic year per calendar year, and a steeper path runs
faster. With group, lines are coloured by group and a thicker line
follows each group's median trajectory across the age range it covers.
plotTrajectories(x, subject, age, group = NULL, clocks = NULL, ncol = 3)plotTrajectories(x, subject, age, group = NULL, clocks = NULL, ncol = 3)
x |
A |
subject |
Subject identifier (character or factor), one per sample in
|
age |
Numeric chronological age AT EACH SAMPLE, one per sample in
|
group |
Optional categorical vector (factor or character), one value
per sample in |
clocks |
Optional character vector of clock names. Default: the age
clocks present in |
ncol |
Facet columns. Default 3. |
The lines join the observed visits without smoothing: the wobble of a
trajectory is a faithful display of how noisy repeated clock estimates
are, which is part of what this plot is for. Per-subject rates with their
caveats are computed by trajectoryRates.
A ggplot object.
data(methylclock_longitudinal) d <- methylclock_longitudinal plotTrajectories(d, subject = d$subject, age = d$age, clocks = c("Wu", "PedBE"))data(methylclock_longitudinal) d <- methylclock_longitudinal plotTrajectories(d, subject = d$subject, age = d$age, clocks = c("Wu", "PedBE"))
Bundles the checks worth running before trusting clock estimates into one call: sample descriptives, how many clocks were computable, how well each age clock tracks chronological age (overall and within groups), and — if you pass the methylation matrix — how many of each clock's CpGs your data carry. The chronological ages are optional: without them the accuracy and acceleration cannot be computed, and the report falls back to coverage, agreement and distributions.
qcReport( x, age = NULL, by = NULL, clocks = NULL, betas = NULL, impute = NULL, output = c("object", "html"), file = NULL )qcReport( x, age = NULL, by = NULL, clocks = NULL, betas = NULL, impute = NULL, output = c("object", "html"), file = NULL )
x |
A |
age |
Optional numeric vector of chronological ages (one per sample in
|
by |
Optional categorical vector (one value per sample, or named by id): the accuracy is broken down within each group. |
clocks |
Optional clock names to restrict to. Default: all clocks in
|
betas |
Optional methylation matrix (or accepted container): if given,
the report adds each clock's CpG coverage (see |
impute |
Optional label of the imputation method used to compute
|
output |
|
file |
Where to write the HTML report when |
An object of class methylclock_qc: a list with samples,
groups, computed, accuracy (or NULL without
age), coverage and missing_by_clock (or NULL
without betas), impute (the recorded method, if any),
notes (a written reading of each section, in plain language) and
commentary (the one-line-per-section summary). With
output = "html" the path to the rendered report is returned
invisibly.
data(methylclock_demo) qcReport(methylclock_demo, age = methylclock_demo$age, by = methylclock_demo$sex)data(methylclock_demo) qcReport(methylclock_demo, age = methylclock_demo$age, by = methylclock_demo$sex)
Flags samples worth a second look: those whose clock estimates are jointly extreme in clock space (a robust sign of a technical outlier), and — if you pass the methylation matrix — those missing a large fraction of the clock CpGs (values that had to be imputed). A sample high on either count carries estimates that are closer to an approximation than a measurement.
sampleQC(x, betas = NULL, clocks = NULL, outlier_sd = 3)sampleQC(x, betas = NULL, clocks = NULL, outlier_sd = 3)
x |
A |
betas |
Optional methylation matrix (or accepted container): adds the per-sample percentage of clock CpGs that were missing (and so imputed). |
clocks |
Optional clock names to restrict to. |
outlier_sd |
A sample is flagged an outlier when its mean absolute z-score across clocks exceeds this. Default 3. |
A data frame with one row per sample: id, outlier_score
(mean absolute z-score across clocks), is_outlier,
discordance (standard deviation of the z-scores across clocks —
how much the clocks contradict each other on that sample; drawn by
plotSampleDiscordance), and — with betas —
pct_missing.
data(methylclock_demo) head(sampleQC(methylclock_demo))data(methylclock_demo) head(sampleQC(methylclock_demo))
The theme the package's plotting functions use by default: a light background,
restrained gridlines and clear facet labels. It returns a standard
ggplot2 theme, so you can add it to your own plots or override any part
of it — every plotting function returns a ggplot object, so the
defaults are a starting point, not a constraint.
theme_methylclock(base_size = 12, base_family = "")theme_methylclock(base_size = 12, base_family = "")
base_size |
Base font size in points. Default 12. |
base_family |
Base font family. Default "" (the device default). |
A ggplot2 theme object.
library(ggplot2) ggplot(mtcars, aes(wt, mpg)) + geom_point() + theme_methylclock()library(ggplot2) ggplot(mtcars, aes(wt, mpg)) + geom_point() + theme_methylclock()
For every subject and clock, the within-subject slope of the estimate:
how many epigenetic years it advances per chronological year across that
subject's visits. A slope of 1 tracks the calendar. This is a property of
the estimate's trajectory, not a validated measure of biological ageing
pace — of the shipped clocks, only DunedinPACE was designed and
validated to estimate such a pace, from a single sample.
With two visits the slope is the difference quotient and carries no
per-subject uncertainty (se is NA: read it as orientative);
with three or more it is the slope of a least-squares line with its
standard error.
trajectoryRates(x, subject, age, clocks = NULL)trajectoryRates(x, subject, age, clocks = NULL)
x |
A |
subject |
Subject identifier (character or factor), one per sample in
|
age |
Numeric chronological age AT EACH SAMPLE, one per sample in
|
clocks |
Optional character vector of clock names. Default: the age
clocks present in |
Read individual slopes with care: the technical replication error of many
clocks spans several years (see Higgins-Chen et al. 2022, Nature Aging,
on the reliability of clock estimates), and the difference of two
measurements carries the noise of both, so over a short follow-up a
subject's slope can be mostly measurement. The steadier signal is in the
aggregate — the distribution of slopes in a group, or their group
difference (plotGroupDifference accepts these rates: one
row per subject).
A data frame with one row per subject and clock: subject,
clock, n_visits, span_years (age range covered),
slope and se (NA with two visits).
data(methylclock_longitudinal) d <- methylclock_longitudinal rates <- trajectoryRates(d, subject = d$subject, age = d$age, clocks = "Wu") head(rates)data(methylclock_longitudinal) d <- methylclock_longitudinal rates <- trajectoryRates(d, subject = d$subject, age = d$age, clocks = "Wu") head(rates)