| Title: | Replicate-Aware Epigenomic Domain Profiling |
|---|---|
| Description: | Provides methods for replicate-aware quantitative phenotyping and comparison of epigenomic domains from normalized continuous signal tracks and per-sample native peak/domain calls (histone-mark ChIP-seq / CUT&Tag as the primary use case; also applicable to ATAC-seq-derived domains). It characterizes each shared genomic domain by integrated signal magnitude (Intensity), sample-specific native peak breadth geometry, and continuous within-domain signal architecture (SignalDispersion). Replicate-level evidence is retained and aggregated with specified support rules, enabling condition-specific classification of Intensity-Super, Breadth-Super, Dual-Super, Typical, or Uncertain domain states without using the consensus-domain width as the breadth caller. The package further supports condition transitions, continuous domain-width remodeling, signal quality control, genome-aware domain annotation with a three-level export (per-domain summary, per-domain-gene pair, and raw relationship detail), optional BEDPE contact evidence, expression-aware candidate-gene prioritization, differential domain analysis on continuous BigWig signal using limma, and result visualization. |
| Authors: | Ying ZHANG [aut, cre] (ORCID: <https://orcid.org/0009-0005-9644-7062>), Xingze HUANG [ctb] (ORCID: <https://orcid.org/0009-0002-9286-1344>), Ye CHEN [ctb], Liang XU [ctb, fnd] (This work is funded by the Fundamental Research Funds for the Central Universities (226-2025-00101) and the National Natural Science Foundation of China (32270746, 82203247, 82203415).) |
| Maintainer: | Ying ZHANG <[email protected]> |
| License: | GPL (>= 3) + file LICENSE |
| Version: | 0.99.3 |
| Built: | 2026-10-04 10:11:13 UTC |
| Source: | https://github.com/BiocStaging/epiPortrait |
Performs a group-wise differential analysis of a continuous
domain assay (default Intensity, the integrated BigWig signal within
each candidate domain) using limma's empirical-Bayes framework.
This complements the Super-domain state calls: where compare_superdomains()
reports relative state transitions between discrete phenotypes,
analyze_differential_domains() reports a statistical P-value for a
continuous signal change between condition groups.
analyze_differential_domains( se, feature = "Intensity", group_var = "Condition", ref_group = NULL, target_group = NULL, design = NULL, contrast = NULL, transform = c("log2"), min_signal = 0, logFC_cutoff = 1, fdr_cutoff = 0.05, trend = TRUE, robust = FALSE )analyze_differential_domains( se, feature = "Intensity", group_var = "Condition", ref_group = NULL, target_group = NULL, design = NULL, contrast = NULL, transform = c("log2"), min_signal = 0, logFC_cutoff = 1, fdr_cutoff = 0.05, trend = TRUE, robust = FALSE )
se |
A SummarizedExperiment from |
feature |
Character. Continuous assay to test (default "Intensity"). |
group_var |
Character. colData column holding the groups
(default "Condition"). Only used when |
ref_group, target_group
|
Character. Reference and target group labels for
the convenience two-group contrast (used when |
design |
A model formula (e.g. |
contrast |
A contrast specification: NULL (uses ref/target), a length-2
character vector of column names to subtract (coef1 - coef2), or a numeric
matrix from |
transform |
Character. "log2" (only; v1.0). Transform applied to the (non-negative) signal before limma. |
min_signal |
Numeric. Filter: domains whose mean transformed signal is below this are dropped from the fit (NA results), to avoid fitting noise. Default 0 (no filtering). |
logFC_cutoff |
Numeric. |log2-fold-change| threshold (on log2 scale) used
only for the descriptive |
fdr_cutoff |
Numeric. FDR threshold for the |
trend |
Logical. Passed to |
robust |
Logical. Passed to |
Quantitative unit. epiPortrait extracts continuous signal
magnitude (e.g. Intensity) from normalized BigWig tracks rather than
integer read counts. limma is applied to log-transformed continuous intensity;
unlike DESeq2 or edgeR, this is not a negative-binomial count model. Valid
inference therefore requires quantitatively comparable normalized tracks and
an appropriate experimental design.
Explicit caveat (recorded in provenance). The test is on the integrated BigWig signal, not an exact read-count model. If the user requires DESeq2/DiffBind-style count statistics, the recommended path is to obtain per-domain counts externally and run those tools; epiPortrait provides the coordinate universe and continuous phenotype layer.
Design and contrasts. design defines the linear model on the
samples (e.g. ~ Condition or ~ 0 + Condition + batch); contrast
is set either via the convenience ref_group/target_group pair
(default two-group comparison on a Condition column) or via a
contrast matrix / limma::makeContrasts-style specification.
Transform offset caveat. The variance-stabilizing transform is
log(x + 1); the pseudo-count 1 is expressed in the units of the input
BigWigs, so its relative weight depends on the upstream normalization scheme
(CPM vs RPGC vs spike-in). With CPM-scale tracks the offset is negligible for
enriched domains but dominates near-background ones; set min_signal
accordingly to exclude domains where the fit would be driven by the offset.
Mean-variance trend. Integrated intensity shows a strong
mean-variance dependence across domains; by default a limma mean-variance
trend is fitted (trend = TRUE), which prevents low-signal domains from
being over-called as significant. The automatic fallback (fewer than 20
tested domains) and robust = TRUE outlier-resistant moderation follow
limma's recommendations for non-count genomic data.
se with rowData columns:
<feature>_logFC, <feature>_AveExpr,
<feature>_t, <feature>_P.Value, <feature>_adj.P.Val,
<feature>_DiffStatus (Gain / Loss / NS / NA);
and metadata(se)$differential_domains with the limma fit summary,
design, contrast and provenance.
data(example_se) if (requireNamespace("limma", quietly = TRUE)) { se <- analyze_differential_domains( example_se, group_var = "Condition", ref_group = "Control", target_group = "Treatment") table(SummarizedExperiment::rowData(se)$Intensity_DiffStatus) }data(example_se) if (requireNamespace("limma", quietly = TRUE)) { se <- analyze_differential_domains( example_se, group_var = "Condition", ref_group = "Control", target_group = "Treatment") table(SummarizedExperiment::rowData(se)$Intensity_DiffStatus) }
Annotates the final shared domains (not individual peaks) with
genome-aware linear evidence plus optional BEDPE 3D-contact and RNA-seq
expression evidence. Adds 7 compact rowData columns and stores a long-format
domain-gene link table in metadata(se)$domain_gene_links.
annotate_epi_domains( se, genome = "hg38", txdb = NULL, anno_db = NULL, promoter_upstream = 3000, promoter_downstream = 3000, bedpe = NULL, bedpe_score_col = NULL, min_anchor_overlap_bp = NULL, expression = NULL, expression_type = NULL, aggregate_fun = "median", gene_id_keytype = NULL )annotate_epi_domains( se, genome = "hg38", txdb = NULL, anno_db = NULL, promoter_upstream = 3000, promoter_downstream = 3000, bedpe = NULL, bedpe_score_col = NULL, min_anchor_overlap_bp = NULL, expression = NULL, expression_type = NULL, aggregate_fun = "median", gene_id_keytype = NULL )
se |
A SummarizedExperiment from |
genome |
Character. Built-in shortcut ("hg38","hg19","mm10","mm9") or a
TxDb object. For non-built-in species supply |
txdb |
A TxDb object or NULL (resolved from genome shortcut). |
anno_db |
Character. OrgDb package name, or NULL (resolved). |
promoter_upstream |
Numeric. Promoter window upstream of the TSS (default 3000 bp). |
promoter_downstream |
Numeric. Promoter window downstream of the TSS (default 3000 bp). |
bedpe |
Character path to a BEDPE file, a data.frame, or NULL. Only
|
bedpe_score_col |
Numeric or character or NULL. Optional column holding
a per-record contact STRENGTH (e.g. the FitHiChIP TMM-normalized contact
frequency in column 8). Integer column index (>= 7; columns 1-6 are the
coordinates) or a column name (file inputs expose V1..Vn names; a
data.frame input can carry arbitrary names). When given, quantitative
outputs are produced IN ADDITION to the count-based evidence:
|
min_anchor_overlap_bp |
Numeric or NULL. Minimum base pairs of overlap
required between a domain anchor and a BEDPE anchor for the 3D contact to
be counted (default NULL = any overlap, i.e. >= 1 bp). A value of
|
expression |
A gene-level expression object: a wide matrix (genes x samples), a long data.frame (gene_id, SampleID, Condition, expression), or NULL. |
expression_type |
Character. "TPM", "CPM", "normalized_counts", "VST", "rlog". Must be declared; never auto-guessed. |
aggregate_fun |
Character. Per-condition aggregation over RNA replicates (default "median"). |
gene_id_keytype |
Character or NULL. OrgDb keytype for the gene IDs (default "ENTREZID" for built-in UCSC genomes; required for custom TxDbs using other ID systems such as ENSEMBL / TAIR / FlyBase). |
Representative gene-level TSS. epiPortrait uses one strand-aware
5' boundary per gene derived from GenomicFeatures::genes(txdb) (start
for +, end for -) as the representative TSS for compact domain annotation. It
does not enumerate every alternative-transcript TSS of a gene; the compact
nearest_tss_distance_bp is therefore a gene-level proximity measure.
Promoter models. filter_promoter_peaks() uses
GenomicFeatures::promoters(txdb) (transcript-level promoter set), while
annotate_epi_domains() uses a gene-level representative-TSS promoter
window. The two are not the same promoter universe; in an enhancer workflow,
promoter exclusion and promoter_overlap_gene_count should not be
assumed to use identical definitions.
se with rowData columns: primary_genomic_context,
nearest_tss_gene_id, nearest_tss_gene_symbol, nearest_tss_distance_bp,
promoter_overlap_gene_count, gene_body_overlap_gene_count,
fully_contained_gene_count, n_bedpe_contact_gene, bedpe_contact_score,
bedpe_contact_score_max, bedpe_contact_score_mean, bedpe_contact_score_n
(BEDPE score-summary columns are always present: values are NA for
supported contacts without a usable score, and 0 for domains with no
BEDPE evidence, so downstream schemas are stable), and
top_candidate_gene_symbol; the annotation result is also exposed as a
three-level structure in metadata:
metadata(se)$annotation_summary — ONE ROW PER DOMAIN master
table (nearest gene, per-evidence overlap/gene counts, number of
BEDPE-contacted genes, and a representative top candidate gene).
metadata(se)$domain_gene_links_dedup — ONE ROW PER
domain-gene pair, with summarised relation_types,
evidence_sources and a best_relation label.
metadata(se)$domain_gene_links — raw per-relationship
detail (for auditing).
metadata(se)$annotation_provenance, and (if provided)
metadata(se)$bedpe_provenance / metadata(se)$expression_provenance.
data(example_se) if (requireNamespace("TxDb.Hsapiens.UCSC.hg38.knownGene", quietly = TRUE)) { se <- annotate_epi_domains(example_se, genome = "hg38") head(SummarizedExperiment::rowData(se)$primary_genomic_context) }data(example_se) if (requireNamespace("TxDb.Hsapiens.UCSC.hg38.knownGene", quietly = TRUE)) { se <- annotate_epi_domains(example_se, genome = "hg38") head(SummarizedExperiment::rowData(se)$primary_genomic_context) }
Extracts domain-level quantitative features from BigWig files
across consensus or stitched peaks: Intensity (integrated signal
magnitude) and SignalDispersion (signal-weighted genomic dispersion).
When sample-specific native peak files (peak_path) are provided,
additional sample-specific breadth geometry is added as
NativeMaxPeakWidth, NativeOccupiedWidth and
NativePeakCount. This is the core engine of epiPortrait.
build_portrait_matrix( sample_sheet, consensus_peaks, workers = 1, on_disk = FALSE, h5_dir = "epiPortrait_h5", cache_dir = NULL, custom_features = NULL, negative_policy = c("error", "clip_zero", "allow"), fail_action = c("stop", "drop") )build_portrait_matrix( sample_sheet, consensus_peaks, workers = 1, on_disk = FALSE, h5_dir = "epiPortrait_h5", cache_dir = NULL, custom_features = NULL, negative_policy = c("error", "clip_zero", "allow"), fail_action = c("stop", "drop") )
sample_sheet |
A |
consensus_peaks |
A |
workers |
Integer. Number of threads for parallel processing (default: 1). |
on_disk |
Logical. If TRUE, stores assays as HDF5 files on disk to save RAM, which is highly recommended for mammalian-scale BigWig processing. |
h5_dir |
Character. Directory to store HDF5 files if |
cache_dir |
Character or NULL. If provided, caches per-sample RAW coverage views as RDS files in this directory. Subsequent calls with the same BigWig + consensus peaks combination skip BigWig I/O entirely (default: NULL). The cache stores raw (un-sanitized) coverage so that negative_policy and custom features can change between runs without cache pollution. |
custom_features |
A named list of functions. Each function must accept a
single |
negative_policy |
Character. How to handle negative signal values in the BigWig tracks:
|
fail_action |
Character. Behavior when a BigWig cannot be imported
|
A SummarizedExperiment object. Core assays (domain x sample):
Intensity: integrated BigWig signal within the shared domain.
SignalDispersion: signal-weighted genomic SD within the
domain (secondary within-domain architecture descriptor).
NativeMaxPeakWidth: max width of native peaks overlapping the
domain (NA when peak_path missing).
NativeOccupiedWidth: sum of reduced native peak widths
inside the domain (NA when peak_path missing).
NativePeakCount: number of native peaks overlapping the
domain (NA when peak_path missing).
The static shared interval length is stored in rowData(se)$IntervalWidth.
Imported native peaks (if provided) are stored in
metadata(se)$native_peaks for downstream peak-level Breadth-Super
calling.
if (.Platform$OS.type != "windows") { extdata <- system.file("extdata", package = "epiPortrait") samples <- data.frame( SampleID = c("C1", "C2", "T1", "T2"), Condition = c("Control", "Control", "Treatment", "Treatment"), bw_path = file.path(extdata, c("C1.bw", "C2.bw", "T1.bw", "T2.bw")), peak_path = file.path(extdata, c("C1_peaks.bed", "C2_peaks.bed", "T1_peaks.bed", "T2_peaks.bed"))) peaks <- rtracklayer::import(file.path(extdata, "peaks.bed")) build_portrait_matrix(samples, consensus_peaks = peaks, workers = 1) }if (.Platform$OS.type != "windows") { extdata <- system.file("extdata", package = "epiPortrait") samples <- data.frame( SampleID = c("C1", "C2", "T1", "T2"), Condition = c("Control", "Control", "Treatment", "Treatment"), bw_path = file.path(extdata, c("C1.bw", "C2.bw", "T1.bw", "T2.bw")), peak_path = file.path(extdata, c("C1_peaks.bed", "C2_peaks.bed", "T1_peaks.bed", "T2_peaks.bed"))) peaks <- rtracklayer::import(file.path(extdata, "peaks.bed")) build_portrait_matrix(samples, consensus_peaks = peaks, workers = 1) }
Identifies super-domains by ranking domains on a feature and separating extreme ("super") from typical domains via a hockey-stick inflection, with replicate-aware support aggregation. Supports three modes:
"global_consensus" (default; alias "consensus"):
calls per-sample, computes replicate support per condition group, and
requires the support rule to be met in EVERY group. Identifies domains
reproducibly super across all conditions (persistent/global consensus).
"per_group": EACH replicate is called independently using its
own ranked feature distribution; replicate-level calls are then
aggregated within each condition using the selected support rule.
Group-mean ranks are stored for visualization only and do not determine
the group call. Stores one call column per group. Recommended for the
main analysis of multi-condition studies. The per-domain x per-replicate
call matrix is available via get_replicate_calls().
"per_sample": calls each sample independently, stores all
call columns.
call_super_domains( se, feature = "Intensity", mode = c("global_consensus", "consensus", "per_group", "per_sample"), group_var = "Condition", method = "elbow", log_transform = NULL, quantile_cutoff = NULL, min_replicate_support = 0.5, support_rule = c("majority", "all", "fraction"), min_valid_replicates = NULL, min_quality = 0.1, tie_policy = c("strict", "inclusive"), n_bootstrap = NULL, seed = NULL, min_peak_overlap_fraction = 0.5, valid_chroms = NULL, verbose = TRUE )call_super_domains( se, feature = "Intensity", mode = c("global_consensus", "consensus", "per_group", "per_sample"), group_var = "Condition", method = "elbow", log_transform = NULL, quantile_cutoff = NULL, min_replicate_support = 0.5, support_rule = c("majority", "all", "fraction"), min_valid_replicates = NULL, min_quality = 0.1, tie_policy = c("strict", "inclusive"), n_bootstrap = NULL, seed = NULL, min_peak_overlap_fraction = 0.5, valid_chroms = NULL, verbose = TRUE )
se |
A SummarizedExperiment from |
feature |
Character. Ranking feature.
The static genomic interval length is available as |
mode |
Character. "global_consensus" (default), "per_group", or "per_sample". The alias "consensus" is also accepted. |
group_var |
Character. Column in colData for grouping (used when mode = "per_group" or "global_consensus"). |
method |
Character. Inflection-detection heuristic:
Ignored when |
log_transform |
Logical or NULL. Whether to |
quantile_cutoff |
Numeric or NULL. If provided (e.g. 0.95), bypasses
inflection detection and uses an explicit top fraction instead. For
|
min_replicate_support |
Numeric. Minimum support fraction used when
|
support_rule |
Character. Replicate-support rule for per-group / consensus calls:
no-call replicates never count as Super and never inflate support. |
min_valid_replicates |
Integer or NULL. Minimum number of valid
(non-NA) replicates required before a domain can be called Super in a
group. When |
min_quality |
Numeric. Minimum inflection quality for a reliable call
(see |
tie_policy |
Character. How to classify domains whose value equals the
cutoff. |
n_bootstrap |
Integer or NULL. Bootstrap resamples for the cutoff
stability interval and success rate (default NULL; set e.g. 200 to
enable). The interval reflects resampling stability over the fixed
ranked set, not a classical iid confidence interval. Its information
content shrinks as the ranked set grows (interval width ~ 1/sqrt(n)); at
genome-wide scale the primary cutoff-stability check is per-replicate
cutoff consistency via |
seed |
Integer or NULL. Random seed for the bootstrap (default NULL). A NULL seed leaves the resampling truly random (recommended for general use); pass a fixed integer to make the bootstrap reproducible. |
min_peak_overlap_fraction |
Numeric. Only used when
|
valid_chroms |
Character or NULL. Only used when
|
verbose |
Logical. Print messages (default TRUE). |
Optional H3K27ac / ROSE benchmark. If the goal is a head-to-head
comparison with the original ROSE method (Whyte et al., 2013, Cell
153:307-319), use method = "tangent" with
log_transform = FALSE (raw signal, ROSE-style scale). Because the
tangent implementation is a geometric variant rather than a bit-exact port
of ROSE's calculate_cutoff(), validate any ROSE-reproduction numbers
against the reference implementation before drawing quantitative
conclusions. This is an optional benchmark setting, not the package's primary
workflow; the core value of epiPortrait is the Intensity x native-Breadth
decomposition with replicate support. The log-transformed default is retained
as the epiPortrait convention because it stabilises the skewed intensity
distribution for the elbow heuristic and downstream analysis.
A SummarizedExperiment with rowData columns for each call.
Call provenance (feature, method, requested/used log_transform, cutoff,
cutoff_stability_interval, bootstrap_success_rate, quality_score,
call_status, n_super) is stored in
metadata(se)$superdomain_calls.
data(example_se) se <- call_super_domains(example_se, feature = "Intensity", method = "tangent", log_transform = FALSE) table(SummarizedExperiment::rowData(se)$Intensity_Domain_Type)data(example_se) se <- call_super_domains(example_se, feature = "Intensity", method = "tangent", log_transform = FALSE) table(SummarizedExperiment::rowData(se)$Intensity_Domain_Type)
Performs pre-analysis QC on a sample sheet of BigWig tracks Checks file existence, signal ranges, negative/zero fractions, chromosome naming, and cross-sample global scale consistency. Returns a per-sample summary with a PASS / WARNING / FAIL status and emits warnings for global incompatibilities.
check_signal_compatibility( sample_sheet, regions = NULL, max_windows = 2000, seed = NULL, verbose = TRUE )check_signal_compatibility( sample_sheet, regions = NULL, max_windows = 2000, seed = NULL, verbose = TRUE )
sample_sheet |
A |
regions |
A |
max_windows |
Integer. Number of windows to sample when |
seed |
Integer or NULL. Optional random seed for reproducible window / region subsampling (default NULL = random draw). Pass a fixed seed to make the QC reproducible; NULL keeps the draw truly random. |
verbose |
Logical. Print a summary (default TRUE). |
A data.frame with one row per sample:
SampleID, Condition, signal-sum (QCWindowSignalSum when sampling tiles,
DomainSetSignalSum when regions are provided), MedianSignal, MeanSignal,
ZeroFraction, NegativeFraction, UpperQuantile, GlobalScaleRatio, Status.
The object carries a print method that displays the summary table
when verbose = TRUE; the returned value is otherwise an ordinary
data.frame.
if (.Platform$OS.type != "windows") { extdata <- system.file("extdata", package = "epiPortrait") ss <- data.frame( SampleID = c("C1", "C2", "T1", "T2"), Condition = c("Control", "Control", "Treatment", "Treatment"), bw_path = file.path(extdata, c("C1.bw", "C2.bw", "T1.bw", "T2.bw"))) check_signal_compatibility(ss, max_windows = 500) }if (.Platform$OS.type != "windows") { extdata <- system.file("extdata", package = "epiPortrait") ss <- data.frame( SampleID = c("C1", "C2", "T1", "T2"), Condition = c("Control", "Control", "Treatment", "Treatment"), bw_path = file.path(extdata, c("C1.bw", "C2.bw", "T1.bw", "T2.bw"))) check_signal_compatibility(ss, max_windows = 500) }
Combines the Intensity-Super and Breadth-Super calls into the
final 2D architecture taxonomy:
Typical / Intensity-Super / Breadth-Super /
Dual-Super / Uncertain. Dual-Super means the domain is
simultaneously high in integrated signal and unusually broad.
combine_superdomain_calls( se, intensity_feature = "Intensity", width_feature = "Breadth", call_col_suffix = "Domain_Type", group_var = NULL )combine_superdomain_calls( se, intensity_feature = "Intensity", width_feature = "Breadth", call_col_suffix = "Domain_Type", group_var = NULL )
se |
SummarizedExperiment after call_super_domains on Intensity and Breadth. |
intensity_feature |
Character (default "Intensity"). |
width_feature |
Character. Breadth axis feature (default "Breadth"). This is the peak-level native breadth call, NOT a static interval length. |
call_col_suffix |
"Domain_Type" (consensus) or "Call__Control" (per_group). |
group_var |
Character or NULL. If provided, the calls are combined per
condition group (requires mode = "per_group"), producing
|
se with Combined_Domain_Class column (or Combined_Class__
data(example_se) se <- call_super_domains(example_se, feature = "Intensity", method = "tangent", log_transform = FALSE) se <- call_super_domains(se, feature = "Breadth") se <- combine_superdomain_calls(se) table(SummarizedExperiment::rowData(se)$Combined_Domain_Class)data(example_se) se <- call_super_domains(example_se, feature = "Intensity", method = "tangent", log_transform = FALSE) se <- call_super_domains(se, feature = "Breadth") se <- combine_superdomain_calls(se) table(SummarizedExperiment::rowData(se)$Combined_Domain_Class)
Computes transitions of the combined taxonomy
(Intensity-Super / Breadth-Super / Dual-Super / Typical / Uncertain) between
two conditions, using the Combined_Class__<group> columns produced by
combine_superdomain_calls(se, group_var = ...). This closes the core
loop: the combined architecture taxonomy is tracked across conditions
not just a single feature.
compare_superdomain_classes(se, ref_group, target_group)compare_superdomain_classes(se, ref_group, target_group)
se |
SummarizedExperiment after combine_superdomain_calls(group_var). |
ref_group |
Reference condition. |
target_group |
Treatment condition. |
se with Combined_Relative_Class_Transition column.
data(example_se) se <- call_super_domains(example_se, feature = "Intensity", mode = "per_group", group_var = "Condition", method = "tangent", log_transform = FALSE) se <- call_super_domains(se, feature = "Breadth", mode = "per_group", group_var = "Condition") se <- combine_superdomain_calls(se, group_var = "Condition") se <- compare_superdomain_classes(se, ref_group = "Control", target_group = "Treatment") table(SummarizedExperiment::rowData(se)$Combined_Relative_Class_Transition)data(example_se) se <- call_super_domains(example_se, feature = "Intensity", mode = "per_group", group_var = "Condition", method = "tangent", log_transform = FALSE) se <- call_super_domains(se, feature = "Breadth", mode = "per_group", group_var = "Condition") se <- combine_superdomain_calls(se, group_var = "Condition") se <- compare_superdomain_classes(se, ref_group = "Control", target_group = "Treatment") table(SummarizedExperiment::rowData(se)$Combined_Relative_Class_Transition)
Compare Super-Domain Status Between Conditions
compare_superdomains( se, group_var = "Condition", ref_group, target_group, feature = "Intensity", cutoff_scope = c("relative", "reference", "pooled") )compare_superdomains( se, group_var = "Condition", ref_group, target_group, feature = "Intensity", cutoff_scope = c("relative", "reference", "pooled") )
se |
after call_super_domains(mode="per_group"). |
group_var |
Character. |
ref_group |
Reference condition. |
target_group |
Treatment condition. |
feature |
Feature name (default "Intensity"). |
cutoff_scope |
Character. How the super/non-super threshold is defined
The default
For |
se with *_Transition column.
data(example_se) se <- call_super_domains(example_se, feature = "Intensity", mode = "per_group", group_var = "Condition", method = "tangent", log_transform = FALSE) se <- compare_superdomains(se, group_var = "Condition", ref_group = "Control", target_group = "Treatment") table(SummarizedExperiment::rowData(se)$Intensity_Transition)data(example_se) se <- call_super_domains(example_se, feature = "Intensity", mode = "per_group", group_var = "Condition", method = "tangent", log_transform = FALSE) se <- compare_superdomains(se, group_var = "Condition", ref_group = "Control", target_group = "Treatment") table(SummarizedExperiment::rowData(se)$Intensity_Transition)
Computes a continuous effect descriptor for domain breadth between two condition groups. For each shared domain it summarizes the mapped native width per group (median NativeMaxPeakWidth across replicates) and reports:
WidthDelta_bp: median(target) - median(ref)
log2WidthRatio: log2(median(target)/median(ref))
WidthDirection: "Expansion" / "Contraction" / "Stable"
This is a CONTINUOUS effect descriptor, not a significance test: with a handful of replicates it reports the observed magnitude of width remodeling. Statistical testing is left to downstream / external frameworks.
compute_width_transition( se, group_var = "Condition", ref_group, target_group, effect_threshold = log2(1.5), min_replicates = 1L )compute_width_transition( se, group_var = "Condition", ref_group, target_group, effect_threshold = log2(1.5), min_replicates = 1L )
se |
A SummarizedExperiment after build_portrait_matrix() with native peaks (requires the NativeMaxPeakWidth assay). |
group_var |
Character. colData column for grouping (default "Condition"). |
ref_group, target_group
|
Character. Reference and target condition groups. |
effect_threshold |
Numeric or NULL. Minimum |
min_replicates |
Integer. Minimum number of replicates per group with a finite width for the domain to be scored (default 1; NA otherwise). |
A SummarizedExperiment with pair-specific rowData columns
WidthDelta_bp__<ref>_vs_<target> and
log2WidthRatio__<ref>_vs_<target> (and
WidthDirection__<ref>_vs_<target> when a threshold is set), so
multiple comparisons can be stored without overwriting each other.
data(example_se) se <- compute_width_transition(example_se, ref_group = "Control", target_group = "Treatment") head(SummarizedExperiment::rowData(se)$WidthDelta_bp__Control_vs_Treatment)data(example_se) se <- compute_width_transition(example_se, ref_group = "Control", target_group = "Treatment") head(SummarizedExperiment::rowData(se)$WidthDelta_bp__Control_vs_Treatment)
epiPortrait provides replicate-aware quantitative phenotyping of epigenomic domains across biological samples and conditions. Each shared genomic domain is characterized by three measurement layers: Intensity (integrated signal magnitude), native peak breadth geometry (per-replicate native peak width), and SignalDispersion (continuous within-domain signal architecture). Intensity and Breadth are the canonical classification axes; super-domain states (Intensity-Super, Breadth-Super, Dual-Super) are called with replicate-level evidence and explicit support rules, while SignalDispersion is retained as a secondary architecture descriptor. The package supports condition transitions, continuous domain-width remodeling, signal quality control, genome-aware domain annotation, optional BEDPE contact evidence, expression-aware candidate-gene prioritization, and result visualization. Primary use case: histone-mark ChIP-seq / CUT&Tag; continuous-track workflows also apply to ATAC-seq-derived domains.
get_consensus_peaks — Build consensus domain universe
stitch_epi_peaks — Stitch proximal peaks into macro-domains
build_portrait_matrix — Extract portrait matrix from BigWigs
normalize_portrait — Normalize Intensity while preserving SignalDispersion
call_super_domains — Intensity / peak-level Breadth calling
annotate_epi_domains — Domain-aware annotation & candidate genes
plot_portrait_pca — PCA of quantitative domain features
plot_portrait_correlation — Sample correlation heatmap
plot_peak_track — Raw BigWig coverage track
plot_hockey_stick — Super-domain ranking plot
combine_superdomain_calls — Combine Intensity/Breadth super calls
compare_superdomains — Condition-aware super-domain transitions
filter_promoter_peaks — Exclude promoter-proximal peaks
filter_blacklist — Filter blacklisted regions
Maintainer: Ying ZHANG [email protected] (ORCID)
Other contributors:
Xingze HUANG [email protected] (ORCID) [contributor]
Ye CHEN [email protected] [contributor]
Liang XU [email protected] (This work is funded by the Fundamental Research Funds for the Central Universities (226-2025-00101) and the National Natural Science Foundation of China (32270746, 82203247, 82203415).) [contributor, funder]
Useful links:
Report bugs at https://github.com/zying106/epiPortrait/issues
data(example_se) se <- call_super_domains(example_se, feature = "Intensity", verbose = FALSE) table(SummarizedExperiment::rowData(se)$Intensity_Domain_Type)data(example_se) se <- call_super_domains(example_se, feature = "Intensity", verbose = FALSE) table(SummarizedExperiment::rowData(se)$Intensity_Domain_Type)
A simulated SummarizedExperiment containing 500 genomic
domains on chr21 (hg38) with quantitative features (Intensity,
SignalDispersion, NativeMaxPeakWidth, NativeOccupiedWidth, NativePeakCount)
measured across 6 samples (3 Control, 3 Treatment). The synthetic data
deliberately mixes replicate-support patterns (strong 3/3, boundary 2/3,
weak 1/3, and a few no-call domains) so replicate-aware calling is
instructive; see metadata(example_se)$simulation_provenance.
data("example_se")data("example_se")
A SummarizedExperiment object with:
5 matrices (Intensity, SignalDispersion, NativeMaxPeakWidth, NativeOccupiedWidth, NativePeakCount), each 500 x 6
GRanges on chr21 (hg38, all intervals in-bounds)
includes Simulation_Scenario (the generative
mechanism: Typical / Intensity-high /
Breadth-contraction / Dual-with-breadth-contraction) and
IntervalWidth (static genomic interval length)
SampleID, Condition (Control/Treatment), BioReplicate, bw_path (NA), peak_path (NA)
native_peaks, native_peak_available, feature_definitions, simulation_provenance
data(example_se) example_se SummarizedExperiment::assayNames(example_se) S4Vectors::metadata(example_se)$simulation_provenancedata(example_se) example_se SummarizedExperiment::assayNames(example_se) S4Vectors::metadata(example_se)$simulation_provenance
Writes a self-contained results directory: flat TSV tables, per-assay matrices, per-call tables, an object manifest, and the complete SummarizedExperiment as RDS.
export_epiportrait_results( se, outdir = "epiPortrait_results", save_object = TRUE, group_var = "Condition" )export_epiportrait_results( se, outdir = "epiPortrait_results", save_object = TRUE, group_var = "Condition" )
se |
A SummarizedExperiment. |
outdir |
Character. Output directory (created if missing). |
save_object |
Logical. Also save |
group_var |
Character. Passed to |
Invisibly the path to the output directory.
data(example_se) se <- call_super_domains(example_se, feature = "Intensity", verbose = FALSE) out <- export_epiportrait_results(se, outdir = tempfile("epi_export"))data(example_se) se <- call_super_domains(example_se, feature = "Intensity", verbose = FALSE) out <- export_epiportrait_results(se, outdir = tempfile("epi_export"))
Filter ENCODE Blacklist Regions
filter_blacklist(gr, genome = "hg38", blacklist_path = NULL)filter_blacklist(gr, genome = "hg38", blacklist_path = NULL)
gr |
A GRanges object. |
genome |
Character. "hg38", "hg19", "mm10", "mm9". |
blacklist_path |
Custom BED path or NULL (uses built-in). |
Filtered GRanges.
gr <- GenomicRanges::GRanges("chr1", IRanges::IRanges(c(100, 5000000), width = 200)) filter_blacklist(gr, genome = "hg38")gr <- GenomicRanges::GRanges("chr1", IRanges::IRanges(c(100, 5000000), width = 200)) filter_blacklist(gr, genome = "hg38")
Filters out peaks that overlap with specified genomic regions, typically used to remove promoter-proximal peaks before Super-Element analysis.
filter_promoter_peaks(gr, genome = "hg38", upstream = 2000, downstream = 2000)filter_promoter_peaks(gr, genome = "hg38", upstream = 2000, downstream = 2000)
gr |
A GRanges object of peaks. |
genome |
A character string ("hg38", "hg19", "mm10", etc.) or a TxDb object. |
upstream |
Number of bp upstream of TSS to define promoter (default: 2000). |
downstream |
Number of bp downstream of TSS to define promoter (default: 2000). |
A filtered GRanges object.
gr <- GenomicRanges::GRanges("chr1", IRanges::IRanges(c(100, 5000000), width = 200)) if (requireNamespace("TxDb.Hsapiens.UCSC.hg38.knownGene", quietly = TRUE)) { filter_promoter_peaks(gr, genome = "hg38") }gr <- GenomicRanges::GRanges("chr1", IRanges::IRanges(c(100, 5000000), width = 200)) if (requireNamespace("TxDb.Hsapiens.UCSC.hg38.knownGene", quietly = TRUE)) { filter_promoter_peaks(gr, genome = "hg38") }
Locates the inflection point of a ranked (ascending) value curve used to separate super-domains from typical domains. Two heuristics are supported:
find_hockey_inflection( x, method = c("elbow", "tangent"), min_quality = 0.1, verbose = FALSE )find_hockey_inflection( x, method = c("elbow", "tangent"), min_quality = 0.1, verbose = FALSE )
x |
Numeric vector of feature values (e.g. per-domain intensity). |
method |
Character. |
min_quality |
Numeric. Minimum |
verbose |
Logical. Print warnings on low-quality calls (default FALSE). |
"elbow" (default): the point with the maximum perpendicular
distance to the line connecting the first and last ranked points.
Classic knee/elbow detection; cheap and robust when signals are
log-transformed.
"tangent": a ROSE-inspired tangent-optimization inflection
(Whyte et al., 2013, Cell 153:307-319). Slides a line whose slope is
(max - min)/n and minimizes the number of points below it.
Note that this is a geometric variant of ROSE's
calculate_cutoff() step — not a bit-exact reproduction — and
implements only an inflection step, not the full ROSE pipeline
(stitching, input subtraction). epiPortrait does not log-transform
the input unless log_transform is requested.
A method-agnostic quality_score (max perpendicular distance relative
to the value range) is returned. When it falls below min_quality,
call_status is "no_call" and the inflection is not reliable.
A list with elements:
inflection_idx |
Index of the inflection point in the sorted vector. |
cutoff_value |
Feature value at the inflection point. |
quality_score |
Right-tail curve-prominence quality (0-1). A left-loaded / inverse hockey-stick curve has quality 0 and is not called. |
method |
The method used. |
call_status |
|
reason |
Reason for |
x <- c(rep(1, 50), seq(1, 100, length.out = 50)) find_hockey_inflection(x, method = "elbow") find_hockey_inflection(x, method = "tangent")x <- c(rep(1, 50), seq(1, 100, length.out = 50)) find_hockey_inflection(x, method = "elbow") find_hockey_inflection(x, method = "tangent")
Returns the provenance for a feature's super-domain calls
(cutoff, stability interval, quality, per-group replicate calls), stored in
metadata(se)$superdomain_calls.
get_call_provenance(se, feature = "Intensity")get_call_provenance(se, feature = "Intensity")
se |
A SummarizedExperiment after |
feature |
Character. Feature name (default "Intensity"). |
A list (the stored provenance), or NULL if unavailable.
data(example_se) se <- call_super_domains(example_se, feature = "Intensity", verbose = FALSE) get_call_provenance(se, "Intensity")$methoddata(example_se) se <- call_super_domains(example_se, feature = "Intensity", verbose = FALSE) get_call_provenance(se, "Intensity")$method
Returns per-domain call results for a feature in long or wide form: for each condition group, the call, replicate support and rank.
get_call_results(se, feature = "Intensity", long = FALSE)get_call_results(se, feature = "Intensity", long = FALSE)
se |
A SummarizedExperiment after |
feature |
Character. Feature name (default "Intensity"). |
long |
Logical. If TRUE, returns long format (one row per domain x group); if FALSE, wide format with one column per group. |
A data.frame.
data(example_se) se <- call_super_domains(example_se, feature = "Intensity", mode = "per_group", group_var = "Condition", verbose = FALSE) head(get_call_results(se, "Intensity")) head(get_call_results(se, "Intensity", long = TRUE))data(example_se) se <- call_super_domains(example_se, feature = "Intensity", mode = "per_group", group_var = "Condition", verbose = FALSE) head(get_call_results(se, "Intensity")) head(get_call_results(se, "Intensity", long = TRUE))
Extract Combined Class Results
get_combined_class_results(se)get_combined_class_results(se)
se |
A SummarizedExperiment after |
A data.frame of combined classes (and Combined_Domain_Class if
present).
data(example_se) se <- call_super_domains(example_se, feature = "Intensity", verbose = FALSE) se <- call_super_domains(se, feature = "Breadth", verbose = FALSE) se <- combine_superdomain_calls(se) head(get_combined_class_results(se))data(example_se) se <- call_super_domains(example_se, feature = "Intensity", verbose = FALSE) se <- call_super_domains(se, feature = "Breadth", verbose = FALSE) se <- combine_superdomain_calls(se) head(get_combined_class_results(se))
Merges multiple sample peak/domain sets into a unified genomic coordinate system while applying a reproducibility filter. This is CONSENSUS / domain-universe construction, not a peak caller (epiPortrait does not call MACS2/SICER-type peaks).
get_consensus_peaks(gr_list, min_reps = 2)get_consensus_peaks(gr_list, min_reps = 2)
gr_list |
A GRangesList or list of GRanges objects containing peaks/domains from all samples. |
min_reps |
Minimum number of replicate peak sets contributing at least one overlap to a reduced consensus interval for it to be retained (default: 2). |
Union-support chaining semantics. All sample peaks are first
reduce()-merged into connected intervals, then each reduced interval
counts how many sample peak sets contribute at least one overlapping peak
(Support_Reps). Connected overlapping peaks can chain into a wider
reduced interval; Support_Reps is an interval-level replicate
contribution count, NOT base-pair-wise support across replicates. A
Support_Reps = 3 interval does not imply every base pair is covered by
all three replicates.
A filtered GRanges object representing the consensus domain universe.
gr1 <- GenomicRanges::GRanges("chr1", IRanges::IRanges(c(100, 5000), width = 200)) gr2 <- GenomicRanges::GRanges("chr1", IRanges::IRanges(c(150, 5200), width = 200)) get_consensus_peaks(list(rep1 = gr1, rep2 = gr2), min_reps = 2)gr1 <- GenomicRanges::GRanges("chr1", IRanges::IRanges(c(100, 5000), width = 200)) gr2 <- GenomicRanges::GRanges("chr1", IRanges::IRanges(c(150, 5200), width = 200)) get_consensus_peaks(list(rep1 = gr1, rep2 = gr2), min_reps = 2)
Extracts candidate (domain-associated) genes from the stored domain-gene evidence (metadata(se)$domain_gene_links), optionally applying RNA-seq expression prioritization. Ordinal prioritization is explicit: evidence relationships come first, expression is a secondary tie-breaker; no black-box composite score is computed.
get_domain_genes( se, domains = NULL, relations = c("nearest_tss", "promoter_overlap", "gene_body_overlap", "fully_contained", "bedpe_promoter_contact"), group = NULL, expression_priority = c("none", "expressed_first", "high_expression_first"), min_expression = NULL, unique_genes = TRUE, nearest_tss_cutoff_bp = getOption("epiPortrait.nearest_tss_cutoff_bp", 10000), rank_by = c("tier", "bedpe_score") )get_domain_genes( se, domains = NULL, relations = c("nearest_tss", "promoter_overlap", "gene_body_overlap", "fully_contained", "bedpe_promoter_contact"), group = NULL, expression_priority = c("none", "expressed_first", "high_expression_first"), min_expression = NULL, unique_genes = TRUE, nearest_tss_cutoff_bp = getOption("epiPortrait.nearest_tss_cutoff_bp", 10000), rank_by = c("tier", "bedpe_score") )
se |
A SummarizedExperiment after |
domains |
Logical vector (length nrow(se)) selecting which domains to consider, or NULL for all. |
relations |
Character. Evidence relation types to include (default all: nearest_tss, promoter_overlap, gene_body_overlap, fully_contained, bedpe_promoter_contact). |
group |
Character or NULL. Condition group whose expression is used for prioritization (NULL = no expression). |
expression_priority |
Character. "none" (default), "expressed_first", or "high_expression_first". |
min_expression |
Numeric or NULL. Descriptive threshold for
|
unique_genes |
Logical. Collapse to unique genes (default TRUE), retaining the highest-priority domain association per gene. |
nearest_tss_cutoff_bp |
Numeric. Distance (bp) below which a
|
rank_by |
Character. Primary ordering key for
|
A data.frame: domain_id, gene_id, gene_symbol, relation_types, bedpe_supported, bedpe_support_count, bedpe_contact_score, nearest_tss_distance_bp (when available), expression_value, expression_status, expression_rank, candidate_priority.
data(example_se) if (requireNamespace("TxDb.Hsapiens.UCSC.hg38.knownGene", quietly = TRUE)) { se <- annotate_epi_domains(example_se, genome = "hg38") head(get_domain_genes(se)) }data(example_se) if (requireNamespace("TxDb.Hsapiens.UCSC.hg38.knownGene", quietly = TRUE)) { se <- annotate_epi_domains(example_se, genome = "hg38") head(get_domain_genes(se)) }
Returns a flat data.frame of domain-level results for export or inspection. Includes genomic coordinates, IntervalWidth, and — when present — per-group calls/support/rank, combined classes, transitions and group mean quantitative values.
get_domain_results(se, group_var = "Condition", include_assay_means = TRUE)get_domain_results(se, group_var = "Condition", include_assay_means = TRUE)
se |
A SummarizedExperiment with super-domain calls. |
group_var |
Character. Column in colData for group-mean feature columns. If NULL, no group-mean columns are added. |
include_assay_means |
Logical. Add per-group mean of each dynamic assay
(default TRUE when |
A data.frame.
data(example_se) head(get_domain_results(example_se, group_var = "Condition"))data(example_se) head(get_domain_results(example_se, group_var = "Condition"))
Get Mark-Specific Preset
get_mark_preset(mark)get_mark_preset(mark)
mark |
"H3K27ac", "H3K4me3", "H3K27me3", "H3K9me3", etc. |
List of preset parameters, including:
label: human-readable domain label
stitch_distance: default stitching gap in bp. 0 / NULL is a
SENTINEL meaning "do not automatically re-stitch at the
preset-workflow level" — treat it as "skip stitch_epi_peaks()"
(e.g. broad repressive marks such as H3K27me3 / H3K9me3, whose
domains are already spatially clustered by the upstream broad-domain
caller, and unknown/generic marks). Note that
stitch_epi_peaks(x, 0) itself still merges
overlapping/adjacent ranges; the sentinel must be handled by the
caller (see the vignette preset workflow).
primary_feature: ranking feature
mark_class: "active" (H3K27ac, H3K4me3, H3K4me1) or
"broad_repressive" (H3K27me3, H3K9me3)
taxonomy_style: "super_domain" (active marks) or
"repressive_remodeling" (broad repressive marks). This controls
DISPLAY terminology only; the internal canonical classes
(Intensity-Super / Breadth-Super / Dual-Super) are unchanged.
class_labels: display aliases for the canonical taxonomy.
get_mark_preset("H3K27ac") get_mark_preset("H3K27me3")get_mark_preset("H3K27ac") get_mark_preset("H3K27me3")
Exposes the per-domain x per-replicate super-domain evidence that
underlies the group-level replicate-support call. For
mode = "per_group" the matrix is read from stored provenance; for
mode = "per_sample" it is reconstructed from the
<feature>_Call__<sample> rowData columns. Group calls are
replicate-support aggregates; this matrix is the auditable evidence trail.
get_replicate_calls(se, feature = "Intensity", group = NULL, long = FALSE)get_replicate_calls(se, feature = "Intensity", group = NULL, long = FALSE)
se |
A SummarizedExperiment after |
feature |
Character. Feature (default "Intensity"). |
group |
Character or NULL. For per_group calls, restrict to the samples of this condition group. NULL returns all samples. |
long |
Logical. If TRUE, returns long format (domain_id, SampleID, Group, call); if FALSE (default), a domain x sample matrix. |
A matrix (long = FALSE) or data.frame (long = TRUE).
data(example_se) se <- call_super_domains(example_se, feature = "Intensity", mode = "per_group", group_var = "Condition", verbose = FALSE) m <- get_replicate_calls(se, feature = "Intensity", group = "Control") head(m) head(get_replicate_calls(se, feature = "Intensity", group = "Control", long = TRUE))data(example_se) se <- call_super_domains(example_se, feature = "Intensity", mode = "per_group", group_var = "Condition", verbose = FALSE) m <- get_replicate_calls(se, feature = "Intensity", group = "Control") head(m) head(get_replicate_calls(se, feature = "Intensity", group = "Control", long = TRUE))
Extract Sample-Level Results / QC Table
get_sample_results(se)get_sample_results(se)
se |
A SummarizedExperiment. |
A data.frame with one row per sample (colData + QC).
data(example_se) get_sample_results(example_se)data(example_se) get_sample_results(example_se)
Extract Transition Results
get_transition_results(se)get_transition_results(se)
se |
A SummarizedExperiment after |
A data.frame of transition columns.
data(example_se) se <- call_super_domains(example_se, feature = "Intensity", mode = "per_group", group_var = "Condition", verbose = FALSE) se <- compare_superdomains(se, group_var = "Condition", ref_group = "Control", target_group = "Treatment") head(get_transition_results(se))data(example_se) se <- call_super_domains(example_se, feature = "Intensity", mode = "per_group", group_var = "Condition", verbose = FALSE) se <- compare_superdomains(se, group_var = "Condition", ref_group = "Control", target_group = "Treatment") head(get_transition_results(se))
For a per-group feature call, classifies the cause of each
NA / Uncertain group call so the ambiguity is auditable:
no_native_peak_in_condition (Breadth): the domain has
no native peak evidence in any replicate of the group — e.g. it is
condition-specific (present only in the other condition's universe).
no_valid_signal_in_any_replicate (signal features): no
replicate produced a usable ranked distribution.
insufficient_valid_replicates: some, but fewer than
min_valid_replicates, replicates carried evidence (e.g. 1/3
with a majority rule).
inflection_no_call_all_replicates: every replicate's
inflection was unreliable (no_call).
Domains whose group call is not NA get cause = NA.
This reads stored provenance only and never re-computes calls.
get_uncertain_cause(se, feature = "Breadth", group, group_var = NULL)get_uncertain_cause(se, feature = "Breadth", group, group_var = NULL)
se |
A SummarizedExperiment after |
feature |
Character. Feature (default "Breadth"). |
group |
Character. Condition group. |
group_var |
Character or NULL. Column used for grouping; resolved from stored provenance when NULL (default "Condition"). |
A data.frame with columns Domain_ID, Group_Call,
N_Valid_Replicates, Min_Valid_Replicates, Cause.
data(example_se) se <- call_super_domains(example_se, feature = "Breadth", mode = "per_group", group_var = "Condition", verbose = FALSE) get_uncertain_cause(se, feature = "Breadth", group = "Control")data(example_se) se <- call_super_domains(example_se, feature = "Breadth", mode = "per_group", group_var = "Condition", verbose = FALSE) get_uncertain_cause(se, feature = "Breadth", group = "Control")
Overlays the per-sample super-domain calls of object B
(se_b) onto the shared domains of object A (se_a). The two
objects may have DIFFERENT domain universes (e.g. a promoter-filtered +
12.5 kb-stitched H3K27ac universe vs a 5 kb-stitched H3K4me3 universe);
each se_a domain is classified from the se_b calls of the
se_b domains that overlap it. This enables the two-axis design:
e.g. whether promoter-proximal ARBS that an enhancer-mark pipeline
deliberately excludes are captured by the promoter-mark WIDTH axis
("boundary rescue"), or the orthogonal Intensity x Width combinations.
integrate_cross_mark( se_a, se_b, call_fmt_a = "Breadth_Call__%s", call_fmt_b = "Intensity_Call__%s", mark_a = NULL, mark_b = NULL, timepoints = NULL, aggregate_ov = c("any_super", "majority"), min_overlap_bp = 0 )integrate_cross_mark( se_a, se_b, call_fmt_a = "Breadth_Call__%s", call_fmt_b = "Intensity_Call__%s", mark_a = NULL, mark_b = NULL, timepoints = NULL, aggregate_ov = c("any_super", "majority"), min_overlap_bp = 0 )
se_a |
A |
se_b |
A |
call_fmt_a, call_fmt_b
|
Character. |
mark_a, mark_b
|
Character or NULL. Short labels (e.g. "Width",
"Intensity") prepended to the new rowData column names. When NULL they
are derived from the call column prefix (text before |
timepoints |
Character or NULL. Time points to integrate. When NULL,
the intersection of the column-derived time points of both objects is
used (falling back to |
aggregate_ov |
Character. How to collapse MULTIPLE overlapping
|
min_overlap_bp |
Numeric. Minimum base pairs of overlap required for a
|
se_a with per-time-point rowData columns:
<mark_b>_Call__<tp>: aggregated se_b call
<mark_b>_NOverlaps__<tp>: number of overlapping se_b
domains (0 = not covered by the se_b universe)
plus a long-format integration table stored in
metadata(se_a)$cross_mark_integration and a provenance list in
metadata(se_a)$cross_mark_provenance.
data(example_se) ## both objects must carry per_sample calls; see examples of ## transition_matrix_per_sample() for building a two-object setup.data(example_se) ## both objects must carry per_sample calls; see examples of ## transition_matrix_per_sample() for building a two-object setup.
Normalizes the Intensity assay while preserving SignalDispersion in its native bp-scale units. Corrects the Intensity matrix to account for differences in sequencing depth or distribution across samples.
normalize_portrait(se, method = "None", force_TMM = FALSE)normalize_portrait(se, method = "None", force_TMM = FALSE)
se |
A SummarizedExperiment object from build_portrait_matrix(). |
method |
Normalization method. Options are:
Row-wise Z-score scaling is not offered here: it is a display/clustering
transform that would destroy the cross-domain magnitude ranking used for
Super calling. Use |
force_TMM |
Logical. If |
A normalized SummarizedExperiment object.
data(example_se) normalize_portrait(example_se, method = "None")data(example_se) normalize_portrait(example_se, method = "None")
Heatmap of the combined architecture class transition between two conditions (Control rows -> Treatment columns). Displays row percentage and domain counts. This is a RELATIVE architecture-state transition (per condition-group cutoffs) and must not be read as absolute gain/loss.
plot_class_transition( se, ref_group, target_group, normalize = c("row_percent", "count", "none"), include_uncertain = TRUE, mark = NULL, ref_role = NULL, target_role = NULL, show_axis_labels = TRUE )plot_class_transition( se, ref_group, target_group, normalize = c("row_percent", "count", "none"), include_uncertain = TRUE, mark = NULL, ref_role = NULL, target_role = NULL, show_axis_labels = TRUE )
se |
A SummarizedExperiment with |
ref_group |
Character. Reference condition (rows). |
target_group |
Character. Treatment / target condition (columns). |
normalize |
Character. "row_percent" (default), "count", or "none". |
include_uncertain |
Logical. Include Uncertain class (default TRUE). |
mark |
Character or NULL. Histone mark preset for display terminology. |
ref_role |
Character or NULL. Optional semantic role label appended to the reference group name in the subtitle (e.g. "control"), so the plot states unambiguously which group is the reference. Default NULL. |
target_role |
Character or NULL. Optional semantic role label appended to the target group name in the subtitle (e.g. "tumor"). Default NULL. |
show_axis_labels |
Logical. Show the class names on the heatmap
margins (default TRUE). The axis lines and ticks are always removed, so
the plot reads as a clean heatmap that keeps the row/column class
identity; set |
A ggplot.
data(example_se) se <- call_super_domains(example_se, feature = "Intensity", mode = "per_group", group_var = "Condition", verbose = FALSE) se <- call_super_domains(se, feature = "Breadth", mode = "per_group", group_var = "Condition", verbose = FALSE) se <- combine_superdomain_calls(se, group_var = "Condition") plot_class_transition(se, ref_group = "Control", target_group = "Treatment")data(example_se) se <- call_super_domains(example_se, feature = "Intensity", mode = "per_group", group_var = "Condition", verbose = FALSE) se <- call_super_domains(se, feature = "Breadth", mode = "per_group", group_var = "Condition", verbose = FALSE) se <- combine_superdomain_calls(se, group_var = "Condition") plot_class_transition(se, ref_group = "Control", target_group = "Treatment")
Volcano plot of a differential-domain analysis performed by
analyze_differential_domains, using the stored DiffStatus
labels with the package's publication palette. Domains are coloured by
Gain / Loss / NS (grey).
plot_differential_volcano( se, feature = "Intensity", logFC_col = NULL, padj_col = NULL, label_n = 0 )plot_differential_volcano( se, feature = "Intensity", logFC_col = NULL, padj_col = NULL, label_n = 0 )
se |
A SummarizedExperiment after |
feature |
Character. Differential feature column (default "Intensity"). |
logFC_col, padj_col
|
Character. Column names for logFC and adjusted
P-value (default constructed from |
label_n |
Integer. Number of top domains to label with
|
A ggplot object.
data(example_se) if (requireNamespace("limma", quietly = TRUE)) { se <- analyze_differential_domains( example_se, group_var = "Condition", ref_group = "Control", target_group = "Treatment") plot_differential_volcano(se, label_n = 5) }data(example_se) if (requireNamespace("limma", quietly = TRUE)) { se <- analyze_differential_domains( example_se, group_var = "Condition", ref_group = "Control", target_group = "Treatment") plot_differential_volcano(se, label_n = 5) }
100% stacked bar of the combined architecture class composition per condition group.
plot_domain_class_composition( se, group_var = "Condition", mark = NULL, focus = c("super", "all"), show_counts = TRUE )plot_domain_class_composition( se, group_var = "Condition", mark = NULL, focus = c("super", "all"), show_counts = TRUE )
se |
A SummarizedExperiment with |
group_var |
Character. colData column for groups. |
mark |
Character or NULL. Histone mark preset for display terminology. |
focus |
Character. |
show_counts |
Logical. Draw absolute domain counts inside each segment (default TRUE). |
A ggplot.
data(example_se) se <- call_super_domains(example_se, feature = "Intensity", mode = "per_group", group_var = "Condition", verbose = FALSE) se <- call_super_domains(se, feature = "Breadth", mode = "per_group", group_var = "Condition", verbose = FALSE) se <- combine_superdomain_calls(se, group_var = "Condition") plot_domain_class_composition(se)data(example_se) se <- call_super_domains(example_se, feature = "Intensity", mode = "per_group", group_var = "Condition", verbose = FALSE) se <- call_super_domains(se, feature = "Breadth", mode = "per_group", group_var = "Condition", verbose = FALSE) se <- combine_superdomain_calls(se, group_var = "Condition") plot_domain_class_composition(se)
Quantitative per-domain profile across conditions: biological
replicates as points and group means as larger open points, faceted by
feature. A dashed line connects the group means so the across-condition
direction of change is read directly. When combined architecture classes
are present (combine_superdomain_calls), the domain's class in each
condition is annotated under the axis, linking the quantitative values to
its architectural identity. Replaces the qualitative radar as the
quantitative view.
plot_domain_feature_profile( se, peak_id, group_var = "Condition", features = NULL, transform = c("log10p1", "none"), annotate_class = TRUE, mark = NULL )plot_domain_feature_profile( se, peak_id, group_var = "Condition", features = NULL, transform = c("log10p1", "none"), annotate_class = TRUE, mark = NULL )
se |
A SummarizedExperiment. |
peak_id |
Character. Domain ID. |
group_var |
Character. colData column for groups. |
features |
Character vector of assays to show (default the three dynamic assays). |
transform |
Character. "log10p1" or "none". |
annotate_class |
Logical. Annotate the domain's combined architecture class under each condition group (default TRUE; silently skipped when the combined-class columns are absent). |
mark |
Character or NULL. Histone mark preset for class display terminology. |
A ggplot.
data(example_se) plot_domain_feature_profile(example_se, peak_id = rownames(example_se)[1])data(example_se) plot_domain_feature_profile(example_se, peak_id = rownames(example_se)[1])
Signature figure: each domain as a point in the Intensity x Breadth plane, coloured by combined architecture class. This is the visual that distinguishes epiPortrait from single-intensity callers (Intensity-extreme vs breadth-extreme vs dual).
plot_domain_landscape( se, group = NULL, transform = c("log10p1", "none"), label_n = 0, label_col = NULL, label_mode = c("per_class", "top_intensity"), x_feature = "Intensity", y_feature = "NativeMaxPeakWidth", mark = NULL, group_var = "Condition", show_counts = TRUE )plot_domain_landscape( se, group = NULL, transform = c("log10p1", "none"), label_n = 0, label_col = NULL, label_mode = c("per_class", "top_intensity"), x_feature = "Intensity", y_feature = "NativeMaxPeakWidth", mark = NULL, group_var = "Condition", show_counts = TRUE )
se |
A SummarizedExperiment with combined class columns
( |
group |
Character or NULL. Condition group whose combined class to
colour by (NULL uses the consensus |
transform |
Character. "log10p1" (default) or "none" (display only; classification is never recomputed). |
label_n |
Integer. Number of representative domains to label per
super class (for |
label_col |
Character. Column in rowData used for labels (default "SYMBOL" if present, else Domain_ID). |
label_mode |
Character. |
x_feature |
Character. Assay for the x axis (default "Intensity"). |
y_feature |
Character. Assay for the y axis (default "NativeMaxPeakWidth", the domain-level native breadth summary; falls back to "NativeOccupiedWidth" then "SignalDispersion" when unavailable). |
mark |
Character or NULL. Histone mark preset for display terminology (e.g. "H3K27me3" renders Extended-Domain / Dual-Extreme labels). NULL uses the canonical Intensity-Super / Breadth-Super / Dual-Super labels. |
group_var |
Character. colData column used for grouping when
|
show_counts |
Logical. Append the per-class domain count to the legend labels, e.g. "Intensity-Super (n=607)" (default TRUE). |
A ggplot.
data(example_se) se <- call_super_domains(example_se, feature = "Intensity", mode = "per_group", group_var = "Condition", verbose = FALSE) se <- call_super_domains(se, feature = "Breadth", mode = "per_group", group_var = "Condition", verbose = FALSE) se <- combine_superdomain_calls(se, group_var = "Condition") plot_domain_landscape(se, group = "Control")data(example_se) se <- call_super_domains(example_se, feature = "Intensity", mode = "per_group", group_var = "Condition", verbose = FALSE) se <- call_super_domains(se, feature = "Breadth", mode = "per_group", group_var = "Condition", verbose = FALSE) se <- combine_superdomain_calls(se, group_var = "Condition") plot_domain_landscape(se, group = "Control")
Generates a ranking plot for super-domain identification. Domains are ranked by their feature value; super-domains are highlighted and the cutoff (and, when available, the replicate-cutoff stability band) is drawn from the object's stored provenance. In per-group mode the cutoff is shown as a descriptive median replicate cutoff, never as the unique biological decision boundary.
plot_hockey_stick( se, feature = "Intensity", label_genes = FALSE, top_n_label = 10, group = NULL, group_var = "Condition", show_cutoff = TRUE, show_cutoff_band = TRUE )plot_hockey_stick( se, feature = "Intensity", label_genes = FALSE, top_n_label = 10, group = NULL, group_var = "Condition", show_cutoff = TRUE, show_cutoff_band = TRUE )
se |
A SummarizedExperiment object after running |
feature |
Character. Which feature to plot ("Intensity",
"SignalDispersion", or "Breadth"; legacy aliases accepted). For
|
label_genes |
Logical. Label top super-domains (default FALSE; the ranking curve and cutoff are the message, per-domain labels are opt-in). |
top_n_label |
Integer. Number of top domains to label (default: 10). |
group |
Character or NULL. For signal features: a condition group
(per-group call/rank columns |
group_var |
Character. colData column used for groups when
|
show_cutoff |
Logical. Draw the cutoff as a dashed horizontal guide (default TRUE). |
show_cutoff_band |
Logical. Draw a light horizontal band for the replicate cutoff stability interval (default TRUE). |
A ggplot object.
data(example_se) se <- call_super_domains(example_se, feature = "Intensity", method = "tangent", log_transform = FALSE) plot_hockey_stick(se, feature = "Intensity", label_genes = FALSE)data(example_se) se <- call_super_domains(example_se, feature = "Intensity", method = "tangent", log_transform = FALSE) plot_hockey_stick(se, feature = "Intensity", label_genes = FALSE)
Generates a track-like visualization of the raw BigWig coverage for a specific domain across all samples, for direct visual validation.
plot_peak_track( se, peak_id, group_var = "Condition", extend_bp = 1000, smooth_window = 1, free_y = FALSE, show_native_occupancy = FALSE )plot_peak_track( se, peak_id, group_var = "Condition", extend_bp = 1000, smooth_window = 1, free_y = FALSE, show_native_occupancy = FALSE )
se |
A SummarizedExperiment object from build_portrait_matrix(). |
peak_id |
Character. The ID of the peak to plot. |
group_var |
Character. The grouping variable in colData(se). |
extend_bp |
Numeric. Number of base pairs to extend the view around the peak (default: 1000). |
smooth_window |
Numeric. Window size for smoothing the coverage (default: 1, no smoothing). |
free_y |
Logical. Use independent y-scales per facet panel (default FALSE = fixed y, so cross-sample intensity differences are visually preserved rather than auto-scaled away). |
show_native_occupancy |
Logical. Overlay the per-sample NativeOccupiedWidth (sum of reduced native peak widths inside the domain) as horizontal bands (default FALSE). The band is centred on the candidate domain and is a per-sample geometry view, not a new biological call. |
A ggplot object.
if (.Platform$OS.type != "windows") { extdata <- system.file("extdata", package = "epiPortrait") samples <- data.frame( SampleID = c("C1", "C2", "T1", "T2"), Condition = c("Control", "Control", "Treatment", "Treatment"), bw_path = file.path(extdata, c("C1.bw", "C2.bw", "T1.bw", "T2.bw"))) se_tiny <- build_portrait_matrix( samples, consensus_peaks = rtracklayer::import(file.path(extdata, "peaks.bed")), workers = 1) plot_peak_track(se_tiny, peak_id = rownames(se_tiny)[1], group_var = "Condition") }if (.Platform$OS.type != "windows") { extdata <- system.file("extdata", package = "epiPortrait") samples <- data.frame( SampleID = c("C1", "C2", "T1", "T2"), Condition = c("Control", "Control", "Treatment", "Treatment"), bw_path = file.path(extdata, c("C1.bw", "C2.bw", "T1.bw", "T2.bw"))) se_tiny <- build_portrait_matrix( samples, consensus_peaks = rtracklayer::import(file.path(extdata, "peaks.bed")), workers = 1) plot_peak_track(se_tiny, peak_id = rownames(se_tiny)[1], group_var = "Condition") }
Computes and visualizes the Spearman (default, robust to heavy-tailed epigenomic signal) or Pearson correlation between samples.
plot_portrait_correlation( se, feature = "Intensity", method = c("spearman", "pearson"), limits = NULL, dynamic_mid = TRUE )plot_portrait_correlation( se, feature = "Intensity", method = c("spearman", "pearson"), limits = NULL, dynamic_mid = TRUE )
se |
A SummarizedExperiment object. |
feature |
Character. The assay to use (default: "Intensity"). |
method |
Character. "spearman" (default) or "pearson". |
limits |
Numeric of length 2 or NULL. Colour-scale limits for the
correlation. Default |
dynamic_mid |
Logical. When |
A ggplot object.
data(example_se) plot_portrait_correlation(example_se, feature = "Intensity") plot_portrait_correlation(example_se, feature = "Intensity", limits = c(-1, 1))data(example_se) plot_portrait_correlation(example_se, feature = "Intensity") plot_portrait_correlation(example_se, feature = "Intensity", limits = c(-1, 1))
Performs Principal Component Analysis (PCA) on a specific quantitative domain feature (e.g., Intensity or SignalDispersion) to visualize sample clustering and batch effects.
plot_portrait_pca( se, feature = "Intensity", group_var = "Condition", n_top = 500, features = NULL )plot_portrait_pca( se, feature = "Intensity", group_var = "Condition", n_top = 500, features = NULL )
se |
A SummarizedExperiment object. |
feature |
Character. The assay to use for PCA (default: "Intensity"). |
group_var |
Character. The grouping variable for coloring points. |
n_top |
Numeric. Use only the top N most variable peaks (default: 500). |
features |
Character vector. Additional assays to include in a
combined-feature PCA (e.g. |
A ggplot object.
data(example_se) plot_portrait_pca(example_se, feature = "Intensity", group_var = "Condition")data(example_se) plot_portrait_pca(example_se, feature = "Intensity", group_var = "Condition")
Bar plot of the fraction of domains with each replicate-support level (0/k, 1/k, ..., k/k), stacked by the final group call. Demonstrates replicate-aware calling and the effect of the support rule.
plot_replicate_support( se, feature = "Intensity", group = NULL, group_var = "Condition", min_support = 1, show_counts = TRUE )plot_replicate_support( se, feature = "Intensity", group = NULL, group_var = "Condition", min_support = 1, show_counts = TRUE )
se |
A SummarizedExperiment after |
feature |
Character. Feature (default "Intensity"). |
group |
Character. Condition group (default first group). |
group_var |
Character. colData column for groups. |
min_support |
Integer. Drop support levels with fewer than this many
supporting replicates. The 0/k bin (domains never super in any replicate)
usually dominates the bar and is grey; set |
show_counts |
Logical. Draw the domain count above each bar (default TRUE). |
A ggplot.
data(example_se) se <- call_super_domains(example_se, feature = "Intensity", mode = "per_group", group_var = "Condition", verbose = FALSE) plot_replicate_support(se, feature = "Intensity", group = "Control")data(example_se) se <- call_super_domains(example_se, feature = "Intensity", mode = "per_group", group_var = "Condition", verbose = FALSE) plot_replicate_support(se, feature = "Intensity", group = "Control")
Bar chart of why domains are Uncertain for a feature's
per-group call (see get_uncertain_cause): no native peak in the
condition / insufficient valid replicates / inflection no-call. Helps
audit whether the Uncertain fraction reflects condition-specific
loci rather than analysis artifacts.
plot_uncertain_cause( se, feature = "Breadth", group, group_var = NULL, show_counts = TRUE )plot_uncertain_cause( se, feature = "Breadth", group, group_var = NULL, show_counts = TRUE )
se |
A SummarizedExperiment after |
feature |
Character. Feature (default "Breadth"). |
group |
Character. Condition group. |
group_var |
Character or NULL. Grouping column (resolved from stored provenance when NULL). |
show_counts |
Logical. Label bars with counts (default TRUE). |
A ggplot.
data(example_se) se <- call_super_domains(example_se, feature = "Breadth", mode = "per_group", group_var = "Condition", verbose = FALSE) plot_uncertain_cause(se, feature = "Breadth", group = "Control")data(example_se) se <- call_super_domains(example_se, feature = "Breadth", mode = "per_group", group_var = "Condition", verbose = FALSE) plot_uncertain_cause(se, feature = "Breadth", group = "Control")
Save an epiPortrait Figure in Vector or Raster Format
save_epiportrait_figure(p, file, width = 3.5, height = 3.3, dpi = 600, ...)save_epiportrait_figure(p, file, width = 3.5, height = 3.3, dpi = 600, ...)
p |
A ggplot. |
file |
Output path (extension determines format: .pdf, .svg, .png, .tiff). |
width, height
|
Numeric in inches. |
dpi |
Numeric. For raster output (default 600). |
... |
Passed to the underlying ggsave / ragg device. |
Invisibly the file path.
data(example_se) p <- plot_portrait_pca(example_se, feature = "Intensity") save_epiportrait_figure(p, file = tempfile(fileext = ".png"))data(example_se) p <- plot_portrait_pca(example_se, feature = "Intensity") save_epiportrait_figure(p, file = tempfile(fileext = ".png"))
Merges closely spaced peaks into larger continuous macro-domains based on a user-defined stitching distance. This is crucial for analyzing Super-Elements or broad heterochromatin spreading in epiPortrait.
stitch_epi_peaks(gr, stitch_distance = 12500)stitch_epi_peaks(gr, stitch_distance = 12500)
gr |
A GRanges object of peaks (typically the output from get_consensus_peaks). |
stitch_distance |
Numeric. Maximum distance (in base pairs) between peaks to be stitched together. For Super-Elements, 12500 (12.5 kb) is standard. For standard broad marks, you might use 3000 to 5000. |
A GRanges object of stitched domains, with a metadata column 'Constituent_Peaks' recording how many original peaks were merged into each domain.
gr <- GenomicRanges::GRanges("chr1", IRanges::IRanges(start = c(100, 10000), end = c(200, 10100))) stitch_epi_peaks(gr, stitch_distance = 12500)gr <- GenomicRanges::GRanges("chr1", IRanges::IRanges(start = c(100, 10000), end = c(200, 10100))) stitch_epi_peaks(gr, stitch_distance = 12500)
Summarize an epiPortrait Object
summarize_epiportrait_object(se)summarize_epiportrait_object(se)
se |
A SummarizedExperiment. |
A list with a short text summary of the object contents.
data(example_se) summarize_epiportrait_object(example_se)data(example_se) summarize_epiportrait_object(example_se)
Computes super-domain / class transitions between per-sample
call columns (mode "per_sample", one sample per time point), which
compare_superdomains() / compare_superdomain_classes() do
not support (they require per-GROUP columns). Adds one transition column
per (ref, target) time-point pair and a labelled counts table to
metadata(se)$transitions.
transition_matrix_per_sample( se, feature = NULL, call_fmt = NULL, timepoints = NULL, ref = NULL, verbose = TRUE )transition_matrix_per_sample( se, feature = NULL, call_fmt = NULL, timepoints = NULL, ref = NULL, verbose = TRUE )
se |
A |
feature |
Character or NULL. Feature prefix for the call columns
(e.g. |
call_fmt |
Character or NULL. |
timepoints |
Character or NULL. Ordered time points. When NULL,
derived from the call column suffixes, preserving their discovery order
(use |
ref |
Character or NULL. Reference time point for transitions; when given, all other time points are targets. When NULL, all ordered pairs (a, b) with a before b are computed. |
verbose |
Logical. Print the transition counts (default TRUE). |
se with rowData columns
<prefix>_SampleTransition__<ref>_vs_<target> (labels:
Persistent_<value>, <from>_to_<to>, Uncertain,
matching compare_superdomain_classes() semantics) and
metadata(se)$transitions[[key]] entries with the counts tables.
data(example_se) se <- call_super_domains(example_se, feature = "Intensity", mode = "per_sample", verbose = FALSE) se <- transition_matrix_per_sample(se, feature = "Intensity", verbose = FALSE) SummarizedExperiment::rowData(se)$Intensity_SampleTransition__C1_vs_T1data(example_se) se <- call_super_domains(example_se, feature = "Intensity", mode = "per_sample", verbose = FALSE) se <- transition_matrix_per_sample(se, feature = "Intensity", verbose = FALSE) SummarizedExperiment::rowData(se)$Intensity_SampleTransition__C1_vs_T1
Verifies the internal consistency invariants of an epiPortrait object: unique row names, alignment between assays and rowRanges/colData, IntervalWidth matching rowRanges widths, presence of required assays, and consistent per-group class/support columns.
validate_epiportrait_object(se)validate_epiportrait_object(se)
se |
A SummarizedExperiment from |
Invisibly TRUE if all checks pass; otherwise stops with the
first failing invariant.
data(example_se) validate_epiportrait_object(example_se)data(example_se) validate_epiportrait_object(example_se)