Module 4: Enrichment analysis

Lecture

Module 4 - Practical Exercise

In this tutorial you will perform functional enrichment analysis using:

  1. Over-Representation Analysis (ORA) with gProfiler and clusterProfiler
  2. Gene Set Enrichment analysis (GSEA) with fgsea and MSigDB

What is enrichment analysis?

Enrichment analysis tests whether specific pathways or biological functions are overrepresented in our DEG list.

Examples:

  • immune response
  • metabolism
  • cell cycle
  • apoptosis

Required packages

# base R packages
library(data.table)
library(dplyr)
library(ggplot2)
library(gprofiler2)
library(msigdbr)

# bioconductor packages
library(org.Hs.eg.db)
library(enrichplot)
library(fgsea)
library(KEGGREST)
library(pathview)
library(clusterProfiler)

Get DESeq2 Differentially Enriched Genes from database

library(DESeq2)
url <- "https://www.ncbi.nlm.nih.gov/geo/download/?type=rnaseq_counts&acc=GSE114360&format=file&file=GSE114360_raw_counts_GRCh38.p13_NCBI.tsv.gz"

raw_counts <- fread(url)

# convert the data.table to matrix format
raw_counts = as.matrix(raw_counts)
class(raw_counts)
## [1] "matrix" "array"
# set the gene ID values to be the row names for the matrix
rownames(raw_counts) = raw_counts[, "GeneID"]

# now that the gene IDs are the row names, remove the redundant column that contains them
raw_counts = raw_counts[, colnames(raw_counts) != "GeneID"]

# convert the count values from strings (with spaces) to integers, because originally the gene column contained characters, the entire matrix was set to character
class(raw_counts) = "integer"

# view the first few lines of the gene count matrix
#head(raw_counts)

# create a simple one column dataframe to start
metaData <- data.frame("Condition" = c("shRNA", "shRNA", "shRNA", "CTRL", "CTRL", "CTRL"))

# convert the "Condition" column to a factor data type
# the arbitrary order of these factors will determine the direction of log2 fold-changes for the genes (i.e. up or down regulated)
metaData$Condition = factor(metaData$Condition, levels = c("CTRL", "shRNA"))

# set the row names of the metaData dataframe to be the names of our sample replicates from the read counts matrix
rownames(metaData) = colnames(raw_counts)

# view the metadata dataframe
#head(metaData)

# check that names of htseq count columns match the names of the meta data rows
# use the "all" function which tests whether an entire logical vector is TRUE
all(rownames(metaData) == colnames(raw_counts))
## [1] TRUE
dds = DESeqDataSetFromMatrix(countData = raw_counts, colData = metaData, design = ~Condition)

# run the DESeq2 analysis on the "dds" object
dds = DESeq(dds)

# view the first 5 lines of the DE results
res = results(dds)

#define DEGs
deg=subset(res,res$padj<0.01)

Over-Representation Analysis (ORA)

Enrichment analysis with gProfiler

Documentation : gProfiler

gProfiler is a popular web tool and R package used to identify biological pathways, Gene Ontology terms, and molecular functions enriched in a list of genes.

It compares our differentially expressed genes (DEGs) against known biological annotations from databases such as GO, KEGG, Reactome, and WikiPathways to help interpret the biological meaning of RNA-seq results.

gost.res = gost(

  # List of differentially expressed genes
  rownames(deg),

  # Organism used for the enrichment analysis
  # hsapiens = Homo sapiens (human)
  organism = "hsapiens",

  # Return only statistically significant enriched terms
  significant = TRUE,

  # Do not test for underrepresented pathways
  # FALSE means we only look for overrepresented terms
  measure_underrepresentation = FALSE,

  # Significance threshold for enrichment analysis
  user_threshold = 0.05,

  # Multiple testing correction method
  # FDR = False Discovery Rate correction
  correction_method = "fdr",

  # Include evidence codes for GO annotations
  evcodes = TRUE
)
# Extract Results
gostres = gost.res$result
head(gostres)

Filter Enrichment Results

We remove:

  • very small terms (term_size)
  • extremely large terms (term_size)
  • weakly represented terms (intersection_size)
# filter results to reduce to less specialized or general terms with at least 2 genes from our list
gostres_filt = gostres[gostres$term_size > 15 & gostres$term_size < 2000 & gostres$intersection_size > 2,]

#transform score with log10, higher is better
gostres_filt$log10=-1 * log10(gostres_filt$p_value)

#order results by decreasing score value
gostres_filt=gostres_filt[order(gostres_filt$log10,decreasing=T),]

Manhattan plot for all the results

#Manhattan plot for all results
gostplot(gost.res)

Interpretation

Each point represents:

  • one biological term/pathway

Higher points indicate:

  • stronger enrichment
  • lower adjusted p-values

Colors correspond to different databases:

  • GO
  • KEGG
  • Reactome
  • WikiPathways

Barplot results for each database

# barplots for top 10 KEGG pathways
res_table = gostres_filt[gostres_filt$source=="KEGG",][1:10, ]
 
#modify margins for long terms name
par(mar = c(5, 20, 4, 2))

barplot(
  res_table$log10,
  horiz = TRUE,
  names.arg = res_table$term_name,
  col = "steelblue",
  xlab = "-log10(padj)",
  xlim = c(0, ceiling(max(res_table$log10))),
  main = "Top 10 enriched terms",
  las = 1,      # horizontal labels
  cex.names = 0.8  # text size
)

Enrichment analysis with clusterProfiler

clusterProfiler is a widely used Bioconductor package for statistical analysis and visualization of functional profiles from genomic data.

It allows users to perform enrichment analysis using Gene Ontology (GO), KEGG, Reactome, and Gene Set Enrichment Analysis (GSEA), while providing powerful visualization tools such as dotplots, enrichment maps, and pathway networks.

  • GO Biological Process Enrichment
ego_bp = enrichGO(
  
  # List of differentially expressed genes
  gene          = rownames(deg),
  
  # Annotation database for human genes
  OrgDb         = org.Hs.eg.db,
  
  
  # Type of gene identifiers used in the gene list
  keyType       = "ENTREZID",

  ont           = "BP",
  
  #Method used to adjust p-values for multiple testing
  #BH = Benjamini-Hochberg False Discovery Rate correction
  pAdjustMethod = "BH",
  
  # Maximum raw p-value threshold
  pvalueCutoff  = 0.05,
  
  # Maximum adjusted p-value threshold (FDR/q-value)
  qvalueCutoff  = 0.05,
  
  
  # Convert gene IDs into readable gene symbols
  readable      = TRUE
)

head(as.data.frame(ego_bp))

Question

What does “BP” stand for?

Try changing:

ont = "CC"

or

ont = "MF"

What changes?

Visualize GO Results

GO Barplot

barplot(  ego_bp,  showCategory = 15)

GO Dotplot

dotplot(  ego_bp,  showCategory = 20)

Interpretation of Dotplots

Dot size represents:

  • number of genes

Dot color represents:

  • statistical significance

KEGG Pathway Enrichment

ekegg = enrichKEGG(
  gene = rownames(deg),
  organism = "hsa",
  pvalueCutoff = 0.05
)

head(as.data.frame(ekegg))

Visualize KEGG Results

  • Barplot
barplot(  ekegg,  showCategory = 15)

  • Dotplot
dotplot(  ekegg,  showCategory = 20)

Change the code to include only the top10 KEGG pathways.

Show solution
dotplot(  ekegg,  showCategory = 10)

  • Pathview

The pathview package allows us to visualize gene expression changes directly on KEGG pathway diagrams.

Genes are colored according to their expression levels or fold changes, making it easier to identify activated or repressed biological pathways and interpret RNA-seq results in a biological context.

library(pathview)

# Create named vector of log2 fold changes
gene_list <- deg$log2FoldChange

# Names must be Entrez IDs
names(gene_list) <- rownames(deg)

# Visualize pathway
pathview(
  gene.data = names(gene_list),
  pathway.id = "hsa05205",
  species = "hsa",
  out.suffix = "init"
)

What is the difference with this code?

# Visualize pathway
pathview(
  gene.data = gene_list,
  pathway.id = "hsa05205",
  species = "hsa",
  out.suffix = "logFC"

)

To customize the map, we need to identify the range of expression of the DEGs involved in this pathway:

library(KEGGREST)

# Download KEGG pathway information
pathway_info = keggGet("hsa05205")

# Extract Entrez gene IDs
pathway_genes = pathway_info[[1]]$GENE

# Keep only Entrez IDs
pathway_entrez = pathway_genes[seq(1, length(pathway_genes), 2)]

head(pathway_entrez)
## [1] "10000"     "1026"      "10451"     "10818"     "10855"     "110117499"
pathway_fc = gene_list[names(gene_list) %in% pathway_entrez]

summary(pathway_fc)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
## -0.4775 -0.2462 -0.2124 -0.1964 -0.1854  0.2699

Redraw the map with an appropriate logFC range:

Show solution
# Visualize pathway
pathview(
  gene.data = gene_list,
  pathway.id = "hsa05205",
  species = "hsa",
  out.suffix = "logFCx",
  limit = list(gene = 0.5) #logFC from -0.5 to 0.5
)

Now the scale color is correct, but from green to red. We usually avoid using these colors in figures. Change the scale to: yellow - blue

Show solution
# Visualize pathway
pathview(
  gene.data = gene_list,
  pathway.id = "hsa05205",
  species = "hsa",
  limit = list(gene = 0.5), #logFC from -0.5 to 0.5
  out.suffix = "logFCx_YB",

  # Low values (downregulated genes)
  low = list(gene = "yellow"),

  # High values (upregulated genes)
  high = list(gene = "blue")

)

Gene Set Enrichment Analysis (GSEA)

Unlike classical enrichment analysis that uses only significant DEGs, GSEA uses ALL genes ranked by a statistic such as:

  • log2FoldChange
  • adjusted p-value

This approach avoids arbitrary DEG thresholds and can detect subtle but coordinated biological changes.

Prepare Ranked Gene List

We will rank genes using both:

  • adjusted p-value (padj)
  • direction of change (log2FoldChange)

Genes with: - strong fold-changes - low adjusted p-values

will obtain the highest scores.

# Remove genes with missing adjusted p-values
res_gsea = as.data.frame(res)

res_gsea = res_gsea[!is.na(res_gsea$padj),]

Create Ranking Score

We combine:

  • statistical significance
  • fold-change direction

using:

-log10(padj) * sign(log2FoldChange)
gene_rank = -log10(res_gsea$padj) * sign(res_gsea$log2FoldChange)

# Add gene names
names(gene_rank) = rownames(res_gsea)

# Sort decreasingly
gene_rank = sort(gene_rank,decreasing = TRUE)

head(gene_rank)
##      9636      8638      3433     29968        16      7803 
## 129.14920  61.96901  36.31050  34.86004  27.44421  25.28048

Visualize Ranking Distribution

hist(
  gene_rank,
  breaks = 100,
  col = "steelblue",
  main = "Distribution of GSEA Ranking Scores",
  xlab = "Ranking score"
)

Run GSEA with Gene Sets from MSigDB

MSigDB (Molecular Signatures Database) is a curated collection of annotated gene sets commonly used for pathway and enrichment analysis.

It contains multiple categories of biological signatures, including Hallmark pathways, Gene Ontology terms, immune signatures, and curated pathways from databases such as KEGG and Reactome.

We will use the Hallmark gene sets.

Hallmark pathways summarize major biological processes such as:

  • inflammation
  • hypoxia
  • apoptosis
  • MYC signaling
hallmark_df = msigdbr(
  species = "Homo sapiens",
  collection = "H"
)

head(hallmark_df)

Create Pathway List

fgsea requires a list format:

  • pathway name
  • vector of genes
hallmark_list = hallmark_df %>%
  split(x = .$ncbi_gene,
        f = .$gs_name)

head(hallmark_list)
## $HALLMARK_ADIPOGENESIS
##   [1] "19"     "11194"  "10449"  "33"     "34"     "35"     "47"     "50"    
##   [9] "51"     "112"    "149685" "9370"   "79602"  "56894"  "9131"   "204"   
##  [17] "217"    "226"    "284"    "51129"  "334"    "348"    "369"    "10124" 
##  [25] "64225"  "483"    "539"    "11176"  "593"    "23786"  "604"    "718"   
##  [33] "847"    "284119" "8436"   "901"    "977"    "9936"   "948"    "1031"  
##  [41] "400916" "1147"   "1149"   "134147" "51727"  "1306"   "1282"   "51805" 
##  [49] "84274"  "57017"  "1337"   "1349"   "1351"   "1376"   "1384"   "1431"  
##  [57] "1537"   "1580"   "1629"   "1652"   "1666"   "8694"   "1717"   "51635" 
##  [65] "25979"  "1737"   "1738"   "4189"   "29103"  "128338" "1891"   "1892"  
##  [73] "84173"  "79071"  "5168"   "2053"   "2101"   "23344"  "2109"   "2167"  
##  [81] "2184"   "8322"   "9908"   "1647"   "2632"   "27069"  "57678"  "137964"
##  [89] "2820"   "10243"  "2878"   "2879"   "80273"  "3033"   "26275"  "26353" 
##  [97] "3417"   "3419"   "3421"   "3459"   "10989"  "3679"   "80760"  "6453"  
## [105] "84522"  "3910"   "3952"   "3977"   "3991"   "10162"  "4023"   "4056"  
## [113] "8491"   "56922"  "4191"   "4199"   "11343"  "4259"   "84895"  "56246" 
## [121] "29088"  "54996"  "23788"  "4638"   "64859"  "4698"   "4706"   "4713"  
## [129] "4722"   "28512"  "4836"   "4958"   "5004"   "27250"  "10400"  "5195"  
## [137] "5209"   "5211"   "5236"   "23187"  "5264"   "415116" "123"    "5447"  
## [145] "5468"   "5495"   "84919"  "10935"  "10113"  "55037"  "5733"   "5860"  
## [153] "83871"  "7905"   "92840"  "56729"  "54884"  "8780"   "55177"  "26994" 
## [161] "6239"   "10313"  "25813"  "949"    "6342"   "6390"   "6391"   "6573"  
## [169] "6510"   "6576"   "1468"   "376497" "8884"   "130814" "6623"   "6647"  
## [177] "10580"  "65124"  "8404"   "58472"  "8082"   "6776"   "2040"   "8802"  
## [185] "6817"   "6888"   "10010"  "7086"   "10140"  "7263"   "7316"   "29979" 
## [193] "83549"  "7351"   "29796"  "10975"  "7384"   "27089"  "7423"   "7532"  
## 
## $HALLMARK_ALLOGRAFT_REJECTION
##   [1] "16"     "6059"   "10006"  "43"     "92"     "207"    "322"    "567"   
##   [9] "586"    "8915"   "602"    "672"    "717"    "822"    "9607"   "6356"  
##  [17] "6357"   "6363"   "6347"   "6367"   "6351"   "6352"   "6354"   "894"   
##  [25] "896"    "1230"   "729230" "1234"   "912"    "914"    "919"    "940"   
##  [33] "915"    "916"    "917"    "920"    "958"    "959"    "961"    "924"   
##  [41] "972"    "973"    "941"    "942"    "925"    "926"    "10225"  "1029"  
##  [49] "5199"   "56253"  "1435"   "1445"   "1520"   "10563"  "4283"   "2833"  
##  [57] "1615"   "8560"   "8444"   "1956"   "8661"   "8664"   "8669"   "8672"  
##  [65] "1984"   "1991"   "2000"   "2069"   "2113"   "2147"   "2149"   "355"   
##  [73] "356"    "2213"   "2268"   "2316"   "2533"   "2589"   "2634"   "2650"  
##  [81] "11146"  "8477"   "3001"   "3002"   "3059"   "9734"   "3091"   "3105"  
##  [89] "3108"   "3109"   "3111"   "3112"   "3117"   "3122"   "3133"   "3135"  
##  [97] "3383"   "23308"  "3455"   "3458"   "3459"   "3460"   "10261"  "3551"  
## [105] "3586"   "3589"   "3592"   "3593"   "3594"   "3596"   "3600"   "3603"  
## [113] "3606"   "8807"   "3553"   "3558"   "9466"   "3559"   "3560"   "3561"  
## [121] "3565"   "3566"   "3569"   "3574"   "3578"   "3624"   "3625"   "3662"  
## [129] "3665"   "3394"   "3683"   "3689"   "3702"   "3717"   "3824"   "3848"  
## [137] "3932"   "3937"   "3976"   "4050"   "4065"   "9450"   "4067"   "6885"  
## [145] "11184"  "4153"   "4318"   "11222"  "4528"   "4689"   "4690"   "9437"  
## [153] "114548" "4830"   "4843"   "4869"   "5196"   "5551"   "5579"   "5582"  
## [161] "5699"   "5777"   "5788"   "5917"   "8767"   "6170"   "6123"   "6133"  
## [169] "6223"   "6189"   "6203"   "27240"  "8651"   "9655"   "6688"   "5552"  
## [177] "7903"   "23166"  "6772"   "6775"   "6890"   "6891"   "6892"   "7040"  
## [185] "7042"   "7070"   "7076"   "7096"   "7097"   "7098"   "10333"  "7124"  
## [193] "7163"   "7186"   "50852"  "7321"   "7334"   "7453"   "7454"   "7535"  
## 
## $HALLMARK_ANDROGEN_RESPONSE
##   [1] "10257"  "11057"  "2181"   "87"     "9510"   "11047"  "9590"   "207"   
##   [9] "220"    "56172"  "10513"  "84159"  "563"    "567"    "2683"   "658"   
##  [17] "10645"  "595"    "896"    "8555"   "1021"   "55839"  "1622"   "1718"  
##  [25] "4189"   "2005"   "22936"  "60481"  "3992"   "2289"   "2773"   "23171" 
##  [33] "2936"   "2982"   "3005"   "8916"   "3156"   "3157"   "9455"   "3248"  
##  [41] "51171"  "3422"   "8821"   "3638"   "10788"  "3685"   "3817"   "354"   
##  [49] "3880"   "3856"   "3977"   "3998"   "4094"   "4117"   "9053"   "10461" 
##  [57] "10627"  "8031"   "10397"  "55768"  "4824"   "5036"   "10611"  "5238"  
##  [65] "8554"   "8611"   "56937"  "2185"   "11099"  "5867"   "6197"   "23223" 
##  [73] "6303"   "6319"   "9871"   "6414"   "6446"   "1836"   "54407"  "6611"  
##  [81] "6652"   "60559"  "25803"  "6722"   "6728"   "79689"  "27347"  "445347"
##  [89] "23585"  "7113"   "25816"  "7163"   "8848"   "6675"   "7329"   "51465" 
##  [97] "9218"   "7520"   "2547"   "65986"  "57178" 
## 
## $HALLMARK_ANGIOGENESIS
##  [1] "350"   "351"   "894"   "1281"  "1290"  "6372"  "2260"  "11167" "3685" 
## [10] "182"   "3714"  "3764"  "4023"  "4043"  "4060"  "4487"  "8829"  "4973" 
## [19] "5154"  "5196"  "8993"  "10631" "5553"  "5747"  "6275"  "5104"  "6578" 
## [28] "6696"  "6781"  "7056"  "7076"  "27242" "7410"  "1462"  "7422"  "7448" 
## 
## $HALLMARK_APICAL_JUNCTION
##   [1] "58"     "60"     "70"     "71"     "72"     "87"     "88"     "89"    
##   [9] "81"     "8751"   "8745"   "8754"   "11096"  "147"    "208"    "10000" 
##  [17] "247"    "268"    "57463"  "347902" "9459"   "10109"  "478"    "2683"  
##  [25] "10458"  "649"    "253559" "57863"  "794"    "10487"  "30835"  "29126" 
##  [33] "80381"  "947"    "942"    "4267"   "999"    "1009"   "1013"   "1001"  
##  [41] "1002"   "1004"   "1006"   "1024"   "1041"   "51148"  "5010"   "23562" 
##  [49] "24146"  "51208"  "149461" "1364"   "7122"   "9074"   "1366"   "9073"  
##  [57] "9080"   "1265"   "1272"   "1307"   "1308"   "1297"   "1384"   "92359" 
##  [65] "1495"   "1500"   "6376"   "8449"   "1739"   "1758"   "1823"   "1825"  
##  [73] "1956"   "2037"   "51466"  "60412"  "2200"   "2318"   "6624"   "2533"  
##  [81] "2593"   "2770"   "2771"   "2886"   "2962"   "3033"   "3265"   "3383"  
##  [89] "3384"   "3386"   "7087"   "8517"   "3636"   "3638"   "3667"   "8515"  
##  [97] "3673"   "3675"   "3680"   "3688"   "3691"   "83700"  "3728"   "3757"  
## [105] "3881"   "3909"   "3914"   "3918"   "143903" "26119"  "51474"  "8174"  
## [113] "51776"  "5871"   "5600"   "5603"   "1432"   "4192"   "4313"   "4318"  
## [121] "9019"   "10205"  "4478"   "4597"   "4628"   "4627"   "103910" "10398" 
## [129] "5818"   "5819"   "25945"  "81607"  "257194" "91624"  "4763"   "4771"  
## [137] "23114"  "79849"  "57555"  "54413"  "4892"   "4902"   "9379"   "64398" 
## [145] "84552"  "55742"  "5089"   "5097"   "5175"   "5216"   "5291"   "8503"  
## [153] "5310"   "5335"   "5522"   "5728"   "5747"   "5788"   "5880"   "5921"  
## [161] "54509"  "6237"   "6251"   "9672"   "8910"   "6464"   "357"    "140885"
## [169] "8935"   "7781"   "9353"   "10174"  "10290"  "6714"   "6810"   "6850"  
## [177] "8189"   "9344"   "7045"   "7059"   "7070"   "7073"   "7082"   "51754" 
## [185] "4982"   "7185"   "7216"   "7248"   "7106"   "7283"   "7408"   "7410"  
## [193] "7412"   "1462"   "7414"   "7450"   "8976"   "65266"  "7533"   "7791"  
## 
## $HALLMARK_APICAL_SURFACE
##  [1] "102"    "79602"  "84632"  "9465"   "351"    "50617"  "5205"   "2683"  
##  [9] "672"    "11126"  "9696"   "202"    "6376"   "131566" "1946"   "2050"  
## [17] "2319"   "2619"   "2625"   "51738"  "2947"   "3315"   "3560"   "3561"  
## [25] "4067"   "27076"  "4118"   "266727" "23054"  "22854"  "255738" "5314"  
## [33] "5329"   "51458"  "146760" "80274"  "357"    "116085" "6517"   "142680"
## [41] "8406"   "55959"  "7070"   "51754"

Run fgsea

fgsea_res = fgsea(

  # Pathway list
  pathways = hallmark_list,

  # Ranked gene list
  stats = gene_rank,

  # Minimum pathway size
  minSize = 15,

  # Maximum pathway size
  maxSize = 500,

  # Number of permutations
  nperm = 1000
)

Sort and Explore Results

fgsea_res <- fgsea_res %>%arrange(padj)
head(fgsea_res)

Question

What does the NES (Normalized Enrichment Score) represent?

Interpretation

  • Positive NES: pathway enriched among upregulated genes

  • Negative NES: pathway enriched among downregulated genes

  • Large absolute NES: stronger enrichment signal

Visualize Top Pathways

topPathwaysUp <- fgsea_res %>%
  filter(NES > 0) %>%
  arrange(desc(NES)) %>%
  head(10)

ggplot(
  topPathwaysUp,
  aes(x = reorder(pathway, NES),
      y = NES)
) +
  geom_col(fill = "steelblue") +
  coord_flip() +
  theme_bw() +
  xlab("Pathway") +
  ylab("Normalized Enrichment Score") +
  ggtitle("Top Positively Enriched Pathways")

Visualize Downregulated Pathways

topPathwaysDown <- fgsea_res %>%
  filter(NES < 0) %>%
  arrange(NES) %>%
  head(10)

ggplot(
  topPathwaysDown,
  aes(x = reorder(pathway, NES),
      y = NES)
) +
  geom_col(fill = "tomato") +
  coord_flip() +
  theme_bw() +
  xlab("Pathway") +
  ylab("Normalized Enrichment Score") +
  ggtitle("Top Negatively Enriched Pathways")

Enrichment Plot for One Pathway

plotEnrichment(
  hallmark_list[["HALLMARK_INTERFERON_GAMMA_RESPONSE"]],
  gene_rank
) +
  labs(
    title = "GSEA Enrichment Plot: Apoptosis"
  )

Interpretation of Enrichment Plot

The curve shows where pathway genes appear in the ranked gene list.

  • Genes concentrated at the top: pathway activated

  • Genes concentrated at the bottom: pathway repressed

Visualize Multiple Pathways

top_pathways <- fgsea_res$pathway[1:5]

plotGseaTable(
  pathways = hallmark_list[top_pathways],
  stats = gene_rank,
  fgseaRes = fgsea_res,
  gseaParam = 1
)

Volcano-style NES Plot

ggplot(
  fgsea_res,
  aes(x = NES,
      y = -log10(padj))
) +
  geom_point(aes(color = padj < 0.05),
             size = 3) +
  theme_bw() +
  scale_color_manual(
    values = c("grey70", "red")
  ) +
  xlab("Normalized Enrichment Score") +
  ylab("-log10 adjusted p-value") +
  ggtitle("fgsea Pathway Enrichment")

Biological Interpretation Questions

  1. Which pathways are activated?
  2. Which pathways are repressed?
  3. Are immune pathways enriched?
  4. Are proliferation pathways activated?
  5. Which biological processes dominate the dataset?
Show answer
#Which pathways are activated?
  
activated <- fgsea_res[fgsea_res$NES > 0 & fgsea_res$padj < 0.05, ]
dim(activated)
## [1] 0 8
activated 
## Empty data.table (0 rows and 8 cols): pathway,pval,padj,ES,NES,nMoreExtreme...
#Which pathways are repressed?

repressed <- fgsea_res[fgsea_res$NES < 0 & fgsea_res$padj < 0.05, ]
dim(repressed)
## [1] 2 8
repressed
##                                       pathway        pval       padj         ES
##                                        <char>       <num>      <num>      <num>
## 1: HALLMARK_EPITHELIAL_MESENCHYMAL_TRANSITION 0.001251564 0.03101266 -0.8540225
## 2:                        HALLMARK_MYOGENESIS 0.001265823 0.03101266 -0.7570904
##          NES nMoreExtreme  size                            leadingEdge
##        <num>        <num> <int>                                 <list>
## 1: -1.845140            0   120 960,5054,11167,3673,56937,5768,...[44]
## 2: -1.608063            0    97   351,3693,3691,1266,6319,1191,...[21]
#Are immune pathways enriched? Yes
immune <- fgsea_res[grep("immune|interferon|cytokine|inflammatory", fgsea_res$pathway, ignore.case = TRUE), ]
immune_sig <- immune[immune$padj < 0.05, ]


#  Are proliferation pathways activated? No
prolif <- fgsea_res[grep("cell cycle|mitosis|E2F|G2M|proliferation", fgsea_res$pathway, ignore.case = TRUE), ]
prolif_sig <- prolif[prolif$padj < 0.05, ]
prolif_sig
## Empty data.table (0 rows and 8 cols): pathway,pval,padj,ES,NES,nMoreExtreme...
#  Which biological processes dominate the dataset?
top <- fgsea_res[fgsea_res$padj < 0.05, ]
top <- top[order(-abs(top$NES)), ]
head(top, 20)
##                                       pathway        pval       padj         ES
##                                        <char>       <num>      <num>      <num>
## 1: HALLMARK_EPITHELIAL_MESENCHYMAL_TRANSITION 0.001251564 0.03101266 -0.8540225
## 2:                        HALLMARK_MYOGENESIS 0.001265823 0.03101266 -0.7570904
##          NES nMoreExtreme  size                            leadingEdge
##        <num>        <num> <int>                                 <list>
## 1: -1.845140            0   120 960,5054,11167,3673,56937,5768,...[44]
## 2: -1.608063            0    97   351,3693,3691,1266,6319,1191,...[21]

Mini Challenge

Try modifying one parameter in the analysis:

Examples:

  • change DEG threshold

Questions:

  1. How many enriched pathways do you obtain?
  2. Which pathways disappear?
  3. Which pathways become more significant?