| Title: | Scalable Two-Tier Causal Discovery for Multi-Omic Data |
|---|---|
| Description: | Causal ASCEND (Ancestral Scalable Causal discovEry via iNherited Descent) infers ancestral relations among downstream molecular variables, such as gene expression, when a layer of upstream variables, such as genotypes or methylation sites, is known to precede them. Each conditional independence test conditions only on a small, iteratively refined set of nearest ancestors instead of the whole upstream layer, so the number of tests grows polynomially with the number of variables. The package accepts matrices, SummarizedExperiment objects or a MultiAssayExperiment, and returns the ancestral relations with the rule and evidence behind each one. |
| Authors: | Stephen Asiedu [aut, cre] (ORCID: <https://orcid.org/0009-0005-8885-5381>), David Watson [aut] |
| Maintainer: | Stephen Asiedu <[email protected]> |
| License: | GPL-3 |
| Version: | 0.99.0 |
| Built: | 2026-10-14 22:31:27 UTC |
| Source: | https://github.com/BiocStaging/causalASCEND |
ascend() infers which foreground variables (for example genes) are
causal ancestors of which, using a background layer (for example
genotypes) that is known to precede the foreground. It tests
conditional independence with a Fisher-z partial-correlation test,
conditioning each test only on the nearest ancestors of the two
variables, which it learns and refines over successive sweeps.
ascend( background, foreground, alpha = 0.05, alphaMB = 0.05, fdr = TRUE, minVotes = 1, prescreen = 0.3, maxIter = 10, verbose = FALSE, backgroundAssay = 1, foregroundAssay = 1 )ascend( background, foreground, alpha = 0.05, alphaMB = 0.05, fdr = TRUE, minVotes = 1, prescreen = 0.3, maxIter = 10, verbose = FALSE, backgroundAssay = 1, foregroundAssay = 1 )
background |
Background (upstream) data: a numeric matrix or
data frame with features in rows and samples in columns, or a
|
foreground |
Foreground (downstream) data, in the same form as
|
alpha |
Significance level of the orientation rules. |
alphaMB |
Significance level of the Markov-blanket (nearest ancestor) searches. |
fdr |
Logical: correct the pairwise tests of each sweep with the Benjamini-Hochberg procedure. |
minVotes |
Number of agreeing witnesses required before a direction is committed. Values above 1 trade coverage for precision. |
prescreen |
Benjamini-Hochberg threshold of the marginal prescreen that limits each foreground variable's background candidates. Set to 1 to keep every background variable. |
maxIter |
Maximum number of sweeps. |
verbose |
Logical: report progress with |
backgroundAssay, foregroundAssay
|
Assay name or index used when
|
The method assumes that no foreground variable causes a background variable, and that the data are Markov and faithful to a directed acyclic graph. Not every background variable needs to be measured, but missing background variables act as hidden confounders and lower accuracy. Missing values are handled with pairwise-complete correlations.
Each pair of foreground variables is resolved by one of three rules, with background or already ordered foreground variables as witnesses: R1 (deactivation) gives an ancestor relation, R2 (activation) a non-descendant relation and R3 (conditional independence) an "unrelated" call. Relations are then closed transitively, the nearest ancestors are re-learned and the sweep repeats until nothing changes.
An AscendResult object. See ancestralMatrix(),
ancestralEdges(), pairPValues() and ascendStats().
Asiedu S, Watson D. Causal ASCEND: scalable two-tier causal discovery on high-dimensional multi-omics data.
ascendMultiAssay() for a MultiAssayExperiment,
simulateTwoTier() for test data.
sim <- simulateTwoTier(n = 1000, nBackground = 20, nForeground = 8, r2 = 0.7, sparsity = 0.7, pCross = 0.1, effect = 1.5, seed = 123) res <- ascend(sim$background, sim$foreground) res head(ancestralEdges(res)) evaluateAncestral(res, trueAncestral(sim))sim <- simulateTwoTier(n = 1000, nBackground = 20, nForeground = 8, r2 = 0.7, sparsity = 0.7, pCross = 0.1, effect = 1.5, seed = 123) res <- ascend(sim$background, sim$foreground) res head(ancestralEdges(res)) evaluateAncestral(res, trueAncestral(sim))
Extracts the background and foreground experiments, keeps the samples
(primary units) present in both and passes them to ascend().
ascendMultiAssay( mae, background, foreground, backgroundAssay = 1, foregroundAssay = 1, ... )ascendMultiAssay( mae, background, foreground, backgroundAssay = 1, foregroundAssay = 1, ... )
mae |
A
|
background, foreground
|
Names (or indices) of the background and
foreground experiments in |
backgroundAssay, foregroundAssay
|
Assay used when an experiment
is a |
... |
Further arguments passed to |
An AscendResult object.
if (requireNamespace("MultiAssayExperiment", quietly = TRUE)) { sim <- simulateTwoTier(n = 300, nBackground = 10, nForeground = 5, seed = 2) ids <- paste0("s", seq_len(300)) colnames(sim$background) <- colnames(sim$foreground) <- ids mae <- MultiAssayExperiment::MultiAssayExperiment( experiments = list(snp = sim$background, rna = sim$foreground)) ascendMultiAssay(mae, background = "snp", foreground = "rna") }if (requireNamespace("MultiAssayExperiment", quietly = TRUE)) { sim <- simulateTwoTier(n = 300, nBackground = 10, nForeground = 5, seed = 2) ids <- paste0("s", seq_len(300)) colnames(sim$background) <- colnames(sim$foreground) <- ids mae <- MultiAssayExperiment::MultiAssayExperiment( experiments = list(snp = sim$background, rna = sim$foreground)) ascendMultiAssay(mae, background = "snp", foreground = "rna") }
Extract the inferred ancestral relations and the evidence behind them
from the result of ascend().
ancestralMatrix(x) ## S4 method for signature 'AscendResult' ancestralMatrix(x) pairPValues(x) ## S4 method for signature 'AscendResult' pairPValues(x) ascendStats(x) ## S4 method for signature 'AscendResult' ascendStats(x) ancestralEdges(x, include = c("ancestor", "non-descendant")) ## S4 method for signature 'AscendResult' ancestralEdges(x, include = c("ancestor", "non-descendant")) ## S4 method for signature 'AscendResult' show(object)ancestralMatrix(x) ## S4 method for signature 'AscendResult' ancestralMatrix(x) pairPValues(x) ## S4 method for signature 'AscendResult' pairPValues(x) ascendStats(x) ## S4 method for signature 'AscendResult' ascendStats(x) ancestralEdges(x, include = c("ancestor", "non-descendant")) ## S4 method for signature 'AscendResult' ancestralEdges(x, include = c("ancestor", "non-descendant")) ## S4 method for signature 'AscendResult' show(object)
x |
An AscendResult object. |
include |
Which relations |
object |
An AscendResult object. |
ancestralMatrix(): the numeric ancestrality matrix (1 ancestor,
0.5 non-descendant, 0 unrelated, NA unresolved), row variable
relative to column variable.
pairPValues(): matrix of the p-value of the last pairwise
independence test of each pair, a continuous score for ranking pairs.
ascendStats(): list with the numbers of conditional independence
tests by type (nPairTests, nWitnessTests, nBlanketTests), the
time spent in them, conditioning-set sizes and the number of sweeps.
ancestralEdges(): data frame with one row per relation, with
columns from, to, relation, rule, sweep, votes and
pValue. Ancestor and non-descendant relations are listed once, from
the earlier to the later variable; unrelated and unresolved pairs are
listed once with from before to in the matrix order.
sim <- simulateTwoTier(n = 500, nBackground = 10, nForeground = 6, seed = 1) res <- ascend(sim$background, sim$foreground) ancestralMatrix(res) ancestralEdges(res) ascendStats(res)$nPairTestssim <- simulateTwoTier(n = 500, nBackground = 10, nForeground = 6, seed = 1) res <- ascend(sim$background, sim$foreground) ancestralMatrix(res) ancestralEdges(res) ascendStats(res)$nPairTests
AscendResult holds the ancestral relations that ascend() inferred
among the foreground variables, together with the evidence behind each
one and an account of the work done. Use the accessors listed below
rather than the slots.
An object of class AscendResult.
ancestralNumeric matrix, foreground by foreground. Entry [a, b]
is 1 if a is an ancestor of b, 0.5 if a is not a descendant of
b, 0 if neither is an ancestor of the other, and NA if the pair is
unresolved. Rows and columns follow a topological order of the claims.
pValuesNumeric matrix: p-value of the last pairwise (rule R3) independence test of each pair.
ruleCharacter matrix: the step that committed each pair ("R1",
"R2", "R3", "transitivity", "symmetry", "R3_final"), or
NA if unresolved.
ruleSweepInteger matrix: the sweep in which the pair was committed.
ruleVotesInteger matrix: for R1 and R2 commits, the number of agreeing witnesses.
statsList: numbers of conditional independence tests by type, time spent in them, conditioning-set sizes and number of sweeps.
paramsList: the arguments of the call and the data dimensions.
ascend(), ancestralMatrix(), ancestralEdges(),
pairPValues(), ascendStats()
sim <- simulateTwoTier(n = 500, nBackground = 10, nForeground = 6, seed = 1) res <- ascend(sim$background, sim$foreground) ressim <- simulateTwoTier(n = 500, nBackground = 10, nForeground = 6, seed = 1) res <- ascend(sim$background, sim$foreground) res
Each ordered pair (a, b) is a claim that a precedes b (entry 1
or 0.5) or not (entry 0). A reversed claim therefore counts as both a
false positive and a false negative. Unresolved pairs are counted
separately and are not scored.
evaluateAncestral(estimate, truth)evaluateAncestral(estimate, truth)
estimate |
An AscendResult object or an ancestrality matrix. |
truth |
The output of |
A list with tp, fp, fn, tn, unresolved, precision,
recall, f1, orientationAccuracy (fraction of claims on truly
related pairs that point the right way) and coverage (fraction of
pairs with any verdict).
sim <- simulateTwoTier(n = 1000, nBackground = 20, nForeground = 8, r2 = 0.7, sparsity = 0.7, pCross = 0.1, effect = 1.5, seed = 123) res <- ascend(sim$background, sim$foreground) evaluateAncestral(res, trueAncestral(sim))sim <- simulateTwoTier(n = 1000, nBackground = 20, nForeground = 8, r2 = 0.7, sparsity = 0.7, pCross = 0.1, effect = 1.5, seed = 123) res <- ascend(sim$background, sim$foreground) evaluateAncestral(res, trueAncestral(sim))
Draws data from a linear-Gaussian structural equation model with a background layer that precedes a foreground layer: background variables form a random DAG, each foreground variable receives edges from random background variables, and foreground variables form a random DAG among themselves. No foreground variable affects a background variable.
simulateTwoTier( n, nBackground, nForeground, r2 = 0.5, sparsity = 0.9, pCross = 0.05, effect = 0.8, linear = 1, scaleBackground = TRUE, seed = NULL )simulateTwoTier( n, nBackground, nForeground, r2 = 0.5, sparsity = 0.9, pCross = 0.05, effect = 0.8, linear = 1, scaleBackground = TRUE, seed = NULL )
n |
Number of samples. |
nBackground, nForeground
|
Numbers of background and foreground variables. |
r2 |
Fraction of each foreground variable's variance explained by its parents. |
sparsity |
Sparsity of the within-layer DAGs (larger is sparser;
an edge appears with probability |
pCross |
Probability of each background-to-foreground edge. |
effect |
Size of the foreground-to-foreground effects, relative to the parent's standard deviation. |
linear |
Fraction of parent effects that are linear; the rest are squared, square-root, softplus or ReLU transforms of the parent. |
scaleBackground |
Logical: standardise each background variable to unit variance as it is drawn. Without it, background variances grow along background paths and swamp the foreground signal when the background is large. |
seed |
Optional random seed. |
A list with background and foreground (numeric matrices,
features in rows, samples in columns), adjacency (the true
foreground graph, adjacency[parent, child] = 1) and params.
sim <- simulateTwoTier(n = 200, nBackground = 5, nForeground = 4, seed = 1) dim(sim$background) sim$adjacencysim <- simulateTwoTier(n = 200, nBackground = 5, nForeground = 4, seed = 1) dim(sim$background) sim$adjacency
True ancestral relations of a simulated graph
trueAncestral(x)trueAncestral(x)
x |
The output of |
A 0/1 matrix with entry [a, b] = 1 if a is an ancestor of
b (diagonal NA), in a topological order.
sim <- simulateTwoTier(n = 100, nBackground = 5, nForeground = 5, sparsity = 0.5, seed = 3) trueAncestral(sim)sim <- simulateTwoTier(n = 100, nBackground = 5, nForeground = 5, sparsity = 0.5, seed = 3) trueAncestral(sim)