Skip to content

Commit 6446af4

Browse files
committed
clusterProfiler
1 parent b538415 commit 6446af4

5 files changed

Lines changed: 262 additions & 2 deletions

File tree

02-Enrichment.qmd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,6 @@ GO and KEGG are the most frequently used for functional analysis. They are typic
5454

5555
Other gene sets include but are not limited to Disease Ontology ([DO](http://disease-ontology.org/)), Disease Gene Network ([DisGeNET](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4397996/)), [wikiPathways](https://www.wikipathways.org), Molecular Signatures Database ([MSigDb](http://software.broadinstitute.org/gsea/msigdb)).
5656

57+
In the current `clusterProfiler` ecosystem, classical ORA and GSEA are now complemented by enrichit-backed topology-aware and multi-omics workflows. The algorithm engine lives in `enrichit`, while `clusterProfiler` provides high-level interfaces such as `nseGO()`, `mnseGO()`, `nseKEGG()`, and `mnseKEGG()` for biological interpretation.
58+
5759

020-enrichment-algorithms.qmd

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
```{r include=FALSE}
44
knitr::opts_chunk$set(message=FALSE, warning=FALSE, eval=FALSE)
5+
source("_common.R")
56
```
67

78
Functional enrichment analysis is a staple in bioinformatics for interpreting lists of genes identified from omics experiments. Behind the scenes of our ecosystem (`clusterProfiler`, `DOSE`, etc.), the heavy lifting is powered by the `enrichit` package.
@@ -10,6 +11,8 @@ We built `enrichit` with a clear philosophy: **minimal dependencies, maximum per
1011

1112
In this chapter, we will walk through the algorithmic backbone of enrichment analysis, moving from the standard ORA and GSEA to our newly introduced Network-based, multi-omics, and Weighted enrichment frameworks.
1213

14+
At the package architecture level, `enrichit` serves as the algorithm engine, while `clusterProfiler` provides the knowledge-base-aware high-level entry points. In practice, this means that topology-aware workflows are now available through wrappers such as `nseGO()`, `mnseGO()`, `nseKEGG()`, and `mnseKEGG()`, while multi-omics helpers such as `aggregate_omics()` and `aggregate_enrichment()` are also re-exported by `clusterProfiler` for downstream biological interpretation.
15+
1316
## Over-Representation Analysis (ORA) {#ora-algorithm}
1417

1518
Over Representation Analysis (ORA) [@boyle2004] is a widely used approach to determine whether known biological functions or processes are over-represented (= enriched) in an experimentally-derived gene list, *e.g.* a list of differentially expressed genes (DEGs).
@@ -96,6 +99,7 @@ Our core philosophy is to leverage existing strengths seamlessly:
9699
One RWR diffusion + one GSEA test. You get not only network-aware scores but also rigorous p-values and FDRs, with blazing speed.
97100

98101
```{r}
102+
#| eval: false
99103
library(clusterProfiler)
100104
101105
# 1. Fetch the full background PPI network for Human (taxID 9606)
@@ -118,6 +122,7 @@ Historically, RWR mathematically only accepts non-negative energy. This means yo
118122
To solve this, `enrichit` introduces a dual-track propagation architecture (`mode = "signed"`). When you pass signed statistics (like `-log10(p) * sign(logFC)`), the algorithm automatically splits the input into up-regulated and down-regulated vectors. It runs two independent RWRs on the same network, and then subtracts the two diffused energy vectors to reconstruct a fully signed, network-aware score vector. This seamlessly bridges bidirectional network diffusion with dual-tail GSEA.
119123

120124
```{r}
125+
#| eval: false
121126
# Using signed statistics (e.g., -log10(p) * sign(logFC))
122127
res_signed <- nsea(
123128
geneList = signed_stats,
@@ -182,6 +187,45 @@ mnsea_res <- mnsea(
182187
)
183188
```
184189

190+
The same topology-aware engine can now be accessed from `clusterProfiler` using knowledge-base-aware wrappers. For example, the following high-level workflow reuses a single shared example object and performs GO- and KEGG-aware network enrichment without manually constructing the annotation backend.
191+
192+
```{r}
193+
library(clusterProfiler)
194+
library(org.Hs.eg.db)
195+
196+
demo <- clusterprofiler_enrichit_demo()
197+
198+
go_nse <- nseGO(
199+
geneList = demo$geneList_evidence,
200+
network = demo$network,
201+
OrgDb = org.Hs.eg.db,
202+
keyType = "ENTREZID",
203+
ont = "BP",
204+
minGSSize = 5,
205+
maxGSSize = 500,
206+
threshold = 1e-6,
207+
maxIter = 50,
208+
verbose = FALSE,
209+
pvalueCutoff = 1,
210+
method = "sample",
211+
nPerm = 30
212+
)
213+
214+
kegg_nse <- nseKEGG(
215+
geneList = demo$geneList_evidence,
216+
network = demo$network,
217+
organism = demo$kegg_gson,
218+
minGSSize = 5,
219+
maxGSSize = 500,
220+
threshold = 1e-6,
221+
maxIter = 50,
222+
verbose = FALSE,
223+
pvalueCutoff = 1,
224+
method = "sample",
225+
nPerm = 30
226+
)
227+
```
228+
185229
### Explanation-ready Outputs for Multi-layer Results
186230

187231
Once you start doing topology-aware enrichment, the next question is obvious: *which layer actually drove the hit?* Instead of baking plotting logic into the engine, `enrichit` prepares explanation-ready tables that downstream packages such as `enrichplot` can consume directly.
@@ -230,6 +274,7 @@ By passing a `weight` vector (e.g., network centrality), the model calculates an
230274
For GSEA, we seamlessly fuse your ranked statistics (like $\log FC$) with your external weights before passing them to the C++ engine. This amplifies the signals of core nodes while attenuating unreliable ones, without reinventing the wheel.
231275

232276
```{r}
277+
#| eval: false
233278
library(igraph)
234279
235280
# Fetch network as an igraph object to calculate topological weights
@@ -257,6 +302,7 @@ The beauty of weighted enrichment lies in its **infinite possibilities**. Your `
257302
Finally, for ORA results, `enrichit` provides a Bayesian framework (`bayes_enrich()`) to estimate the posterior probability of explanatory terms. This helps to select the most likely active pathways while automatically penalizing redundancy among highly overlapping GO terms.
258303

259304
```{r}
305+
#| eval: false
260306
# After obtaining an ORA result object
261307
bayes_res <- bayes_enrich(ora_result)
262308
summary_res <- bayes_summary(bayes_res)
@@ -422,6 +468,8 @@ early_ora_res <- ora(
422468

423469
By decoupling ID mapping, statistical integration, and pathway enrichment, `enrichit` provides a clean, robust, and highly extensible pipeline for multi-omics functional analysis.
424470

471+
These workflow helpers are also available from `clusterProfiler`, which means the aggregated result can be sent directly to high-level functions such as `enrichGO()`, `gseGO()`, or `nseGO()` without changing the underlying multi-omics logic.
472+
425473
### 5. Late Fusion at the Pathway Level
426474

427475
Early fusion is elegant when your features align cleanly. But sometimes that assumption collapses immediately: transcriptomics might have a background of 20,000 genes, proteomics might only detect 4,000 proteins, and region-based omics may not even live in gene space before annotation. In these cases, forcing everything into one feature matrix is a great way to manufacture missing values and mapping noise.
@@ -465,6 +513,7 @@ After running a multi-omics integration, you may want to know whether a signific
465513
First, you can classify the pattern of each pathway by comparing the merged results with single-omics results:
466514

467515
```{r}
516+
#| eval: false
468517
# Run ORA for individual omics
469518
rna_ora_res <- ora(
470519
gene = omics_gene_ids[rna_pvals < 0.05],
@@ -492,6 +541,7 @@ The `Omics_Pattern` column will label the pathway as `Shared`, `RNA-Specific`, `
492541
Then, you can extract the gene-level contribution for a specific pathway to see the original statistics of the core enrichment genes:
493542

494543
```{r}
544+
#| eval: false
495545
# Get original multi-omics statistics for genes in the pathway
496546
contribution_df <- get_omics_contribution(
497547
res = classified_ora_res,

021-go.qmd

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,66 @@ ego3 <- gseGO(geneList = geneList,
145145

146146
The format of input data, `geneList`, is documented in the @sec-id-convert. Beware that only gene sets with size in `[minGSSize, maxGSSize]` will be tested.
147147

148+
## Topology-aware GO enrichment
149+
150+
In classical GO enrichment, genes are treated as independent members of a gene set. When a biological network is available, `clusterProfiler` can now call the `enrichit` engine through the high-level wrappers `nseGO()` and `mnseGO()`. The former performs single-network enrichment, while the latter extends the same idea to a multi-layer setting.
151+
152+
To keep the example reproducible and consistent with the KEGG chapter, we reuse one shared demonstration object.
153+
154+
```{r}
155+
#| label: topology-demo-go
156+
demo <- clusterprofiler_enrichit_demo()
157+
```
158+
159+
```{r}
160+
#| label: nseGO-example
161+
ego_nse <- nseGO(
162+
geneList = demo$geneList_evidence,
163+
network = demo$network,
164+
OrgDb = org.Hs.eg.db,
165+
keyType = "ENTREZID",
166+
ont = "BP",
167+
minGSSize = 5,
168+
maxGSSize = 500,
169+
threshold = 1e-6,
170+
maxIter = 50,
171+
verbose = FALSE,
172+
pvalueCutoff = 1,
173+
method = "sample",
174+
nPerm = 30
175+
)
176+
head(as.data.frame(ego_nse)[, c("ID", "Description", "NES", "p.adjust")], 3)
177+
```
178+
179+
The `mnseGO()` function keeps the same GO-aware interface, but accepts a list of layer-specific networks together with explicit inter-layer couplings.
180+
181+
```{r}
182+
#| label: mnseGO-example
183+
ego_mnse <- mnseGO(
184+
seed_list = demo$seed_list,
185+
networks = list(RNA = demo$network, PROT = demo$network_2),
186+
couplings = demo$couplings,
187+
OrgDb = org.Hs.eg.db,
188+
keyType = "ENTREZID",
189+
ont = "BP",
190+
collapse = "weighted_mean",
191+
layer_weights = c(RNA = 1, PROT = 1.2),
192+
minGSSize = 5,
193+
maxGSSize = 500,
194+
threshold = 1e-6,
195+
maxIter = 50,
196+
verbose = FALSE,
197+
pvalueCutoff = 1,
198+
method = "sample",
199+
nPerm = 30
200+
)
201+
head(as.data.frame(ego_mnse)[, c("ID", "Description", "NES", "p.adjust")], 3)
202+
```
203+
204+
These wrappers preserve the familiar GO semantics (`OrgDb`, `ont`, and `keyType`) while delegating the network propagation and enrichment engine to `enrichit`.
205+
148206

149207

150-
## GO analysis for non-model organisms {#clusterprofiler-go-non-model}
151208

152209
Both the `enrichGO()` and `gseGO()` functions require an `OrgDb` object as the background annotation. For organisms that don't have `OrgDb` provided by `Bioconductor`, users can query one (if available) online via `r Biocpkg("AnnotationHub")`. If there is no `OrgDb` available, users can obtain GO annotation from other sources, e.g. from `r Biocpkg("biomaRt")`, or annotate the genes using [Blast2GO](https://www.blast2go.com/) or the [Trinotate](https://rnabio.org/module-07-trinotate/0007/02/01/Trinotate/) pipeline. Then the `enricher()` or `GSEA()` functions can be used to perform GO analysis for these organisms, similar to the examples using WikiPathways and MSigDB. Another solution is to create an `OrgDb` on your own using the `r Biocpkg("AnnotationForge")` package.
153210

022-kegg.qmd

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ The `r Biocpkg("clusterProfiler")` package provides the `search_kegg_organism()`
2323

2424
```{r}
2525
#| label: search-kegg-organism
26+
#| eval: false
2627
library(clusterProfiler)
2728
search_kegg_organism('ece', by='kegg_code')
2829
ecoli <- search_kegg_organism('Escherichia coli', by='scientific_name')
@@ -205,12 +206,15 @@ This ensures that the analysis runs offline and is fully reproducible using the
205206

206207
The `r Biocpkg("clusterProfiler")` package provides the `bitr_kegg()` function to support ID conversion via the KEGG API. This is particularly useful for species that do not have an `OrgDb` object but are supported by KEGG.
207208

209+
The examples in this section require live KEGG REST access. To keep the book build reproducible when the KEGG API is slow or unreachable, we show them without executing them during rendering.
210+
208211
### Basic Usage
209212

210213
Here is an example of converting KEGG IDs to NCBI Protein IDs and then to UniProt IDs for human genes.
211214

212215
```{r}
213216
#| label: bitr-kegg
217+
#| eval: false
214218
library(clusterProfiler)
215219
data(gcSample)
216220
hg <- gcSample[[1]]
@@ -225,12 +229,13 @@ np2up <- bitr_kegg(eg2np[,2], fromType='ncbi-proteinid', toType='uniprot', organ
225229
head(np2up)
226230
```
227231

228-
The ID type (both `fromType` and `toType`) should be one of 'kegg', 'ncbi-geneid', 'ncbi-proteinid', or 'uniprot'. The 'kegg' ID is the primary ID used in the KEGG database. A rule of thumb is that 'kegg' ID corresponds to `entrezgene` ID for eukaryote species and `Locus` ID for prokaryotes.
232+
The ID type (both `fromType` and `toType`) should be one of 'kegg', 'ko', 'ncbi-geneid', 'ncbi-proteinid', or 'uniprot'. The 'kegg' ID is the primary gene ID used in the KEGG database, while 'ko' refers to KEGG Orthology identifiers. A rule of thumb is that 'kegg' gene ID corresponds to `entrezgene` ID for eukaryote species and `Locus` ID for prokaryotes.
229233

230234
For many prokaryote species, `entrezgene` IDs are not available. For example, `ece:Z5100` (E. coli O157:H7 EDL933) has `NCBI-ProteinID` and `UniProt` links, but not `NCBI-GeneID`. Attempting to convert `Z5100` to `ncbi-geneid` will result in an error stating that `ncbi-geneid` is not supported. However, conversion to `ncbi-proteinid` or `uniprot` is possible.
231235

232236
```{r}
233237
#| label: bitr-kegg-prokaryote
238+
#| eval: false
234239
# Example for prokaryote ID conversion
235240
bitr_kegg("Z5100", fromType="kegg", toType='ncbi-proteinid', organism='ece')
236241
bitr_kegg("Z5100", fromType="kegg", toType='uniprot', organism='ece')
@@ -242,13 +247,15 @@ A common confusion exists between `K` numbers (KEGG Orthology) and `ko` numbers
242247

243248
```{r}
244249
#| label: bitr-kegg-path-ko
250+
#| eval: false
245251
bitr_kegg("K00844", "kegg", "Path", "ko")
246252
```
247253

248254
To retrieve the names of the pathways (e.g., "Glutathione metabolism" instead of "ko00480"), we can define a simple helper function `ko2name` to query the KEGG API.
249255

250256
```{r}
251257
#| label: ko2name-example
258+
#| eval: false
252259
x <- bitr_kegg("K00799", "kegg", "Path", "ko")
253260
y <- ko2name(x$Path)
254261
merge(x, y, by.x='Path', by.y='ko')
@@ -258,6 +265,7 @@ merge(x, y, by.x='Path', by.y='ko')
258265

259266

260267
```{r}
268+
#| eval: false
261269
data(geneList, package="DOSE")
262270
gene <- names(geneList)[abs(geneList) > 2]
263271
@@ -274,6 +282,7 @@ Input ID type can be `kegg`, `ncbi-geneid`, `ncbi-proteinid` or `uniprot` (see a
274282
For GO analysis, the `readable` parameter controls whether to translate the IDs to human-readable gene names. This parameter is not available for KEGG analysis. However, the `setReadable()` function can translate input gene IDs to gene names if the corresponding `OrgDb` object is available.
275283

276284
```{r}
285+
#| eval: false
277286
library(org.Hs.eg.db)
278287
# 'kk' is the enrichKEGG result from the previous section
279288
kk <- setReadable(kk, OrgDb = org.Hs.eg.db, keyType="ENTREZID")
@@ -283,6 +292,7 @@ head(kk)
283292
## KEGG pathway gene set enrichment analysis {#clusterprofiler-kegg-pathway-gsea}
284293

285294
```{r}
295+
#| eval: false
286296
kk2 <- gseKEGG(geneList = geneList,
287297
organism = 'hsa',
288298
minGSSize = 120,
@@ -291,6 +301,58 @@ kk2 <- gseKEGG(geneList = geneList,
291301
head(kk2)
292302
```
293303

304+
## Topology-aware KEGG enrichment
305+
306+
The same high-level design is now available for topology-aware KEGG analysis. `nseKEGG()` performs single-network enrichment and `mnseKEGG()` extends the workflow to multiple network layers, while keeping the familiar `clusterProfiler` interface.
307+
308+
For a lightweight and reproducible example, we reuse the same shared demo object as in the GO chapter and pass a local KEGG-style `GSON` object through the `organism` argument.
309+
310+
```{r}
311+
#| label: topology-demo-kegg
312+
demo <- clusterprofiler_enrichit_demo()
313+
```
314+
315+
```{r}
316+
#| label: nseKEGG-example
317+
kk_nse <- nseKEGG(
318+
geneList = demo$geneList_evidence,
319+
network = demo$network,
320+
organism = demo$kegg_gson,
321+
minGSSize = 5,
322+
maxGSSize = 500,
323+
threshold = 1e-6,
324+
maxIter = 50,
325+
verbose = FALSE,
326+
pvalueCutoff = 1,
327+
method = "sample",
328+
nPerm = 30
329+
)
330+
head(as.data.frame(kk_nse)[, c("ID", "Description", "NES", "p.adjust")], 3)
331+
```
332+
333+
```{r}
334+
#| label: mnseKEGG-example
335+
kk_mnse <- mnseKEGG(
336+
seed_list = demo$seed_list,
337+
networks = list(RNA = demo$network, PROT = demo$network_2),
338+
couplings = demo$couplings,
339+
organism = demo$kegg_gson,
340+
collapse = "weighted_mean",
341+
layer_weights = c(RNA = 1, PROT = 1.2),
342+
minGSSize = 5,
343+
maxGSSize = 500,
344+
threshold = 1e-6,
345+
maxIter = 50,
346+
verbose = FALSE,
347+
pvalueCutoff = 1,
348+
method = "sample",
349+
nPerm = 30
350+
)
351+
head(as.data.frame(kk_mnse)[, c("ID", "Description", "NES", "p.adjust")], 3)
352+
```
353+
354+
In a real analysis, users can pass a KEGG organism code such as `"hsa"` directly. Here we use a local `GSON` object to keep the example self-contained and reproducible.
355+
294356
## KEGG Pathway Classification
295357

296358
KEGG pathways are organized into a hierarchical structure with top-level categories such as "Metabolism", "Genetic Information Processing", "Environmental Information Processing", "Cellular Processes", "Organismal Systems", "Human Diseases", and "Drug Development". This classification helps users interpret enrichment results by providing higher-level biological context. For instance, users might be interested specifically in signaling pathways ("Environmental Information Processing") or metabolic pathways ("Metabolism") while excluding disease-related pathways.
@@ -310,6 +372,7 @@ To prevent this cached data from becoming outdated, the package utilizes **GitHu
310372

311373
```{r}
312374
#| label: kegg-module-example
375+
#| eval: false
313376
mkk <- enrichMKEGG(gene = gene,
314377
organism = 'hsa',
315378
pvalueCutoff = 1,
@@ -321,6 +384,7 @@ head(mkk)
321384

322385

323386
```{r}
387+
#| eval: false
324388
mkk2 <- gseMKEGG(geneList = geneList,
325389
organism = 'hsa',
326390
pvalueCutoff = 1)

0 commit comments

Comments
 (0)