Internal engine that performs Over-Representation Analysis with a
hypergeometric test computed directly on phyper.
Returns a result table with one row per tested chemical.
Column renaming, multiple-testing correction, chemical-name join,
canonical ordering and sort are applied downstream by
.format_enrichment_result so the engine stays close
to the vocabulary of the test itself.
Usage
ora(
ChemicalName_GeneSymbols,
gene_symbols,
pAdjustMethod = "BH",
universe = NULL,
minGSSize = 2,
maxGSSize = Inf
)Arguments
- ChemicalName_GeneSymbols
A data frame with two columns (
term,gene) mapping CTD chemical IDs to HGNC gene symbols. The first two columns are used, whatever their names.- gene_symbols
Character vector of HGNC gene symbols to test for enrichment. Duplicates and
NAare removed.- pAdjustMethod
Character. Method for multiple testing correction (default
"BH"). Passed top.adjust.- universe
Vector of background gene identifiers, or
NULL(default) to use every gene inChemicalName_GeneSymbols. Set it torownames(expr)or to the full tested gene list to restrict the background to measured genes only. Coerced withas.character(), so an integer column of Entrez IDs works as it does forgene_symbols.The default is a fallback, not a recommendation, and the function says so when it uses it. A hypergeometric test asks how many of \(n\) genes drawn from \(N\) would land in a set. Genes that your experiment could never have detected still sit in \(N\), filling the urn with balls that cannot be drawn, so the draw looks more selective than it was and the p-value comes out too small. The error is anti-conservative: it manufactures significance.
The right universe is every gene that entered your test, not every gene you sequenced and not only the significant ones: a gene filtered out for low expression could not have been selected, so it does not belong in the urn either. On the RNA-seq example bundled with this package the difference is 32 significant chemicals against 19.
- minGSSize
Integer. Minimum gene set size after intersection with the background (default 2). The default is chosen for CTD, where the median chemical has 4 target genes: a one-gene set is degenerate, since its p-value equals the ratio of input genes to background whichever gene it contains, so it measures membership rather than enrichment.
- maxGSSize
Maximum gene set size after intersection with the background. The default is
Inf, that is no upper limit, and that too is a choice made for CTD rather than inherited.The reasoning mirrors the one for
minGSSize, and reaches the opposite conclusion. A set of \(M\) genes cannot, even when every one of the \(m\) input genes falls inside it, produce a p-value below \(C(M,m)/C(N,m) \approx (M/N)^m\). That floor rises with \(M\), so a large enough set becomes untestable. Measured on CTD, with 28,571 genes in the background and an input list of 169, the largest still-testable set is about 26,600 genes, 93% of the universe. The largest chemical in CTD has 16,536. No chemical is untestable from above, so an upper cut removes sets that could have been declared significant.What it removes is not marginal. A cut at 500 excludes 265 chemicals, among them benzo(a)pyrene, valproic acid, sodium arsenite, bisphenol A, aflatoxin B1 and particulate matter. Their sets are large because the literature on them is large: in CTD size tracks how well studied a chemical is, where in GO a large term is one that has stopped meaning anything. Keeping them costs 3% more tests, 8,235 against 7,970.
Set it to a finite value if you have a reason of your own; ctdR does not impose one.
One consequence to be aware of: without a cap the significant results skew towards large sets. This is not the fold enrichment talking, which moves the other way, since \(M\) sits in its denominator. It is that a p-value measures how unlikely an excess is, and the excess is counted in genes. With 154 input genes from a background of 27,444, a fold of 1.5 means 0.4 genes above expectation for a set of 100 (p = 0.43) and 45 genes above for a set of 16,000 (p = 1.4e-15). Sort by
p.adjustto rank by evidence, and readfoldEnrichmentbeside it to see how sharp the association is; neither answers the question alone.
Value
A data frame with columns ChemicalID, GeneRatio,
BgRatio, pvalue, p.adjust, geneID,
Count and foldEnrichment, sorted by pvalue
ascending. Returns an empty data frame with the same structure when
no gene set can be tested. Emits a message reporting how many
chemicals the size filter left untested, since those are absent from
the result rather than present with a large p-value.
Details
Earlier versions delegated this step to the clusterProfiler
package. That pulled in 59 packages, and a visualization layer this
package never used, to reach a single call. The test itself is one
line of stats, so it is computed here and that dependency is
gone. The p-values are
unchanged: the two implementations were compared over 24
configurations of input list, minimum set size and background
universe, and the largest absolute difference was exactly 0.
The background is every gene appearing in ChemicalName_GeneSymbols,
optionally narrowed by universe. Gene sets are intersected with
that background before the size filter, so minGSSize and
maxGSSize always refer to the set as actually tested rather than
to its nominal size.