Source: https://github.com/markziemann/10mistakes
#knitr::opts_chunk$set(dev = 'svg') # set output device to svg
suppressPackageStartupMessages({
library("getDEE2")
library("DESeq2")
library("kableExtra")
library("eulerr")
library("gplots")
library("mitch")
library("fgsea")
library("RhpcBLASctl")
library("parallel")
library("beeswarm")
library("vioplot")
})
RhpcBLASctl::blas_set_num_threads(1)
if ( ! dir.exists("img") ) {
dir.create("img")
}
For this guide I will be using bulk RNA-seq data from a previous study, which is deposited at NCBI GEO and SRA under accession numbers: GSE55123 and SRP038101 (Lund et al, 2014). The experiment is designed to investigate the effect of Azacitidine treatment on AML3 cells.
The raw data have been processed by the DEE2 project, and the summary gene expression counts are available at the dee2.io website, and programmatically with the getDEE2 bioconductor package (Ziemann et al, 2019).
The gene counts have also been deposited to the /data
folder in the example.Rdata file in case the DEE2 resource
becomes unavailable. To import it, use the command:
load("data/example.Rdata")
Alternatively, you could fetch data from another resource like NCBI GEO, Zenodo or from the host storage drive.
mdat <- getDEE2Metadata("hsapiens")
# get sample sheet
ss <- subset(mdat,SRP_accession=="SRP038101")
# fetch the whole set of RNA-seq data
x <- getDEE2("hsapiens",ss$SRR_accession , metadata=mdat, legacy=TRUE)
## For more information about DEE2 QC metrics, visit
## https://github.com/markziemann/dee2/blob/master/qc/qc_metrics.md
mx <- x$GeneCounts
rownames(mx) <- paste(rownames(mx),x$GeneInfo$GeneSymbol)
dim(mx)
## [1] 58302 6
# aza no filtering
ss$trt <- grepl("Treated",ss$Experiment_title)
ss %>%
kbl(caption="sample sheet for Aza treatment in AML3 cells") %>%
kable_paper("hover", full_width = F)
| SRR_accession | QC_summary | SRX_accession | SRS_accession | SRP_accession | Experiment_title | GEO_series | trt | |
|---|---|---|---|---|---|---|---|---|
| 168876 | SRR1171523 | PASS | SRX472607 | SRS559064 | SRP038101 | GSM1329859: Untreated.1; Homo sapiens; RNA-Seq | GSE55123 | FALSE |
| 168877 | SRR1171524 | WARN(3,4) | SRX472608 | SRS559066 | SRP038101 | GSM1329860: Untreated.2; Homo sapiens; RNA-Seq | GSE55123 | FALSE |
| 168878 | SRR1171525 | WARN(3,4) | SRX472609 | SRS559065 | SRP038101 | GSM1329861: Untreated.3; Homo sapiens; RNA-Seq | GSE55123 | FALSE |
| 168879 | SRR1171526 | WARN(3,4) | SRX472610 | SRS559068 | SRP038101 | GSM1329862: Treated.1; Homo sapiens; RNA-Seq | GSE55123 | TRUE |
| 168880 | SRR1171527 | WARN(3,4) | SRX472611 | SRS559067 | SRP038101 | GSM1329863: Treated.2; Homo sapiens; RNA-Seq | GSE55123 | TRUE |
| 168881 | SRR1171528 | WARN(3,4) | SRX472612 | SRS559069 | SRP038101 | GSM1329864: Treated.3; Homo sapiens; RNA-Seq | GSE55123 | TRUE |
QC is important, even if you are using public transcriptome data. For RNA-seq it is a good idea to show the number of reads for each sample.
par(mar=c(5,7,5,1))
barplot(rev(colSums(mx)),horiz=TRUE,las=1,main="number of reads per sample in SRP038101")
Now make a MDS plot.
mds <- cmdscale(dist(t(mx)))
# expand plot area
XMIN=min(mds[,1])*1.3
XMAX=max(mds[,1])*1.3
YMIN=min(mds[,2])*1.3
YMAX=max(mds[,2])*1.3
cols <- as.character(grepl("Treated",ss$Experiment_title))
cols <- gsub("FALSE","lightblue",cols)
cols <- gsub("TRUE","pink",cols)
plot(mds, xlab="Coordinate 1", ylab="Coordinate 2",
xlim=c(XMIN,XMAX),ylim=c(YMIN,YMAX),
pch=19,cex=2,col=cols, main="MDS plot")
text(cmdscale(dist(t(mx))), labels=colnames(mx) )
I will be using DESeq2 for DE analysis, the count matrix is prefiltered using a detection threshold of 10 reads per sample across all samples. All genes that meet the detection threshold will comprise the background list. The first 6 rows of the count matrix are shows.
mxf <- mx[which(rowMeans(mx)>=10),]
dim(mxf)
## [1] 13168 6
head(mxf,6) %>%
kbl(caption="Count matrix format") %>%
kable_paper("hover", full_width = F)
| SRR1171523 | SRR1171524 | SRR1171525 | SRR1171526 | SRR1171527 | SRR1171528 | |
|---|---|---|---|---|---|---|
| ENSG00000225630 MTND2P28 | 494 | 396 | 340 | 333 | 415 | 418 |
| ENSG00000237973 MTCO1P12 | 52 | 39 | 40 | 30 | 37 | 29 |
| ENSG00000248527 MTATP6P1 | 853 | 544 | 537 | 582 | 702 | 716 |
| ENSG00000228327 AL669831.1 | 17 | 13 | 21 | 21 | 22 | 12 |
| ENSG00000228794 LINC01128 | 42 | 27 | 30 | 32 | 40 | 23 |
| ENSG00000230699 AL645608.3 | 20 | 11 | 13 | 10 | 15 | 22 |
dds <- DESeqDataSetFromMatrix(countData = mxf , colData = ss, design = ~ trt )
res <- DESeq(dds)
## estimating size factors
## estimating dispersions
## gene-wise dispersion estimates
## mean-dispersion relationship
## final dispersion estimates
## fitting model and testing
z <- results(res)
vsd <- vst(dds, blind=FALSE)
zz <-cbind(as.data.frame(z),mxf)
def <-as.data.frame(zz[order(zz$pvalue),])
head(def,10) %>%
kbl(caption="Top DE genes for Aza treatment") %>%
kable_paper("hover", full_width = F)
| baseMean | log2FoldChange | lfcSE | stat | pvalue | padj | SRR1171523 | SRR1171524 | SRR1171525 | SRR1171526 | SRR1171527 | SRR1171528 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ENSG00000165949 IFI27 | 1960.1970 | -3.384492 | 0.0938869 | -36.04861 | 0 | 0 | 4689 | 3583 | 2758 | 309 | 384 | 334 |
| ENSG00000090382 LYZ | 7596.0299 | -1.650342 | 0.0561143 | -29.41036 | 0 | 0 | 14212 | 10237 | 10789 | 3476 | 3764 | 3738 |
| ENSG00000115461 IGFBP5 | 531.2217 | -5.071157 | 0.1795239 | -28.24781 | 0 | 0 | 1320 | 823 | 1025 | 23 | 26 | 43 |
| ENSG00000157601 MX1 | 827.1511 | -2.877795 | 0.1047823 | -27.46450 | 0 | 0 | 1732 | 1501 | 1206 | 184 | 223 | 186 |
| ENSG00000111331 OAS3 | 2127.2010 | -2.661214 | 0.0972124 | -27.37525 | 0 | 0 | 4204 | 3977 | 2972 | 562 | 614 | 560 |
| ENSG00000070915 SLC12A3 | 424.5509 | -3.374852 | 0.1298671 | -25.98697 | 0 | 0 | 1012 | 721 | 653 | 63 | 85 | 76 |
| ENSG00000234745 HLA-B | 3197.0159 | -1.431566 | 0.0604169 | -23.69479 | 0 | 0 | 6085 | 4256 | 4023 | 1590 | 1872 | 1719 |
| ENSG00000137965 IFI44 | 409.0957 | -2.978581 | 0.1319352 | -22.57608 | 0 | 0 | 829 | 740 | 635 | 76 | 111 | 89 |
| ENSG00000204525 HLA-C | 1631.6421 | -1.461550 | 0.0660214 | -22.13750 | 0 | 0 | 3112 | 2150 | 2106 | 791 | 923 | 891 |
| ENSG00000110042 DTX4 | 524.1318 | -2.470219 | 0.1173182 | -21.05572 | 0 | 0 | 1166 | 883 | 688 | 166 | 168 | 145 |
Make a smear plot.
sigf <- subset(def,padj<=0.05)
DET=nrow(mxf)
NSIG=nrow(sigf)
NUP=nrow(subset(sigf,log2FoldChange>0))
NDN=nrow(subset(sigf,log2FoldChange<0))
HEADER=paste(DET,"detected genes;",NSIG,"w/FDR<0.05;",NUP,"up;",NDN,"down")
plot(log10(def$baseMean) ,def$log2FoldChange,
cex=0.6,pch=19,col="darkgray",
main="5-azacitidine treatment in AML3 cells",
xlab="log10(basemean)",ylab="log2(fold change)")
points(log10(sigf$baseMean) ,sigf$log2FoldChange,
cex=0.6,pch=19,col="red")
mtext(HEADER)
In the next sections I will run enrichment analysis with over-representation test and compare it to functional class scoring.
Let’s see if many of the gene symbols have changed.
Comparing Ensembl release 90 to GENCODE Release 49 (GRCh38.p14) which corresponds to Ensembl 115 (Sept 2025).
So v90 had 58k genes and v115 has 86k which is a big increase. The v90 has 1609 genes that have been removed an v115 has 29676 that have been added. There are 56693 common.
From these common ones, 20456 have different gene names. I think the approach to naming these putative genes has changed a lot. Lots of CXorfXX genes have changed, not just AL358472.5 type names.
In the next chunk, I’ll show a few lines of the older source gene names.
There is also an Euler diagram of the gene symbol repertoires.
gt <- read.table("https://dee2.io/data/hsapiens/hsa_gene_info.tsv",header=TRUE)
head(gt)
## GeneID GeneSymbol mean median longest_isoform merged
## 1 ENSG00000223972 DDX11L1 1144 1144 1657 1735
## 2 ENSG00000227232 WASH7P 1351 1351 1351 1351
## 3 ENSG00000278267 MIR6859-1 68 68 68 68
## 4 ENSG00000243485 MIR1302-2HG 623 623 712 1021
## 5 ENSG00000284332 MIR1302-2 138 138 138 138
## 6 ENSG00000237613 FAM138A 888 888 1187 1219
gtnew <- read.table("ref/gencode.v49.genenames.tsv")
v1 <- list("Ens90"=gt$GeneID, "Ens115"=gtnew$V1)
plot(euler(v1),quantities = list(cex = 2), labels = list(cex = 2))
message("Number of unique genes in the original gene table:")
## Number of unique genes in the original gene table:
length(gt$GeneID)
## [1] 58302
message("Number of unique genes in the new gene annotation set:")
## Number of unique genes in the new gene annotation set:
length(gtnew$V1)
## [1] 86369
message("Number of shared gene IDs between new and old annotations:")
## Number of shared gene IDs between new and old annotations:
length(intersect(gt$GeneID,gtnew$V1))
## [1] 56693
message("Number of gene IDs specific to the old annotation:")
## Number of gene IDs specific to the old annotation:
length(setdiff(gt$GeneID,gtnew$V1))
## [1] 1609
message("Number of gene IDs specific to the new annotation:")
## Number of gene IDs specific to the new annotation:
length(setdiff(gtnew$V1,gt$GeneID))
## [1] 29676
message("Here is the mapping table for old-new gene symbols:")
## Here is the mapping table for old-new gene symbols:
gtm <- merge(gt,gtnew,by.x="GeneID",by.y="V1")
message("Number of rows:")
## Number of rows:
nrow(gtm)
## [1] 56693
message("How many of these gene symbols are the same?")
## How many of these gene symbols are the same?
table(gtm$GeneSymbol == gtm$V2)
##
## FALSE TRUE
## 20456 36237
Reading in Reactome gene sets.
# from https://reactome.org/download/current/ReactomePathways.gmt.zip
gsets <- gmtPathways("ref/ReactomePathways_2025-09-02.gmt")
First, discard old gene symbols in favour of the new ones.
In this analysis, we are doing ORA with fora, and comparing the results of raw p-values versus using FDR correction.
This corresponds to the results shown in Figure 1B.
up <- rownames(subset(def, log2FoldChange>0 & padj<0.05))
up <- sapply(strsplit(up," "),'[[',1)
up <- unique(gtnew[match(up,gtnew$V1),2])
up <- up[which(!is.na(up))]
dn <- rownames(subset(def, log2FoldChange<0 & padj<0.05))
dn <- sapply(strsplit(dn," "),'[[',1)
dn <- unique(gtnew[match(dn,gtnew$V1),2])
dn <- dn[which(!is.na(dn))]
bg <- rownames(def)
bg <- sapply(strsplit(bg," "),'[[',1)
bg <- unique(gtnew[match(bg,gtnew$V1),2])
bg <- bg[which(!is.na(bg))]
message("length of up, down and background sets")
## length of up, down and background sets
lapply(list(up,dn,bg),length)
## [[1]]
## [1] 1667
##
## [[2]]
## [1] 1923
##
## [[3]]
## [1] 13063
fres_up <- fora(pathways=gsets, genes=up, universe=bg, minSize = 5, maxSize = Inf)
message("number of gene sets in the results")
## number of gene sets in the results
nrow(fres_up)
## [1] 1840
message("number of up-regulated gene sets with FDR<0.05")
## number of up-regulated gene sets with FDR<0.05
nrow(subset(fres_up,padj<0.05))
## [1] 224
message("number of up-regulated gene sets with p<0.05")
## number of up-regulated gene sets with p<0.05
nrow(subset(fres_up,pval<0.05))
## [1] 351
fres_up_fdr <- subset(fres_up,padj<0.05)$pathway
fres_up_nom <- subset(fres_up,pval<0.05)$pathway
fres_dn <- fora(pathways=gsets, genes=dn, universe=bg, minSize = 5, maxSize = Inf)
message("number of down-regulated gene sets with FDR<0.05")
## number of down-regulated gene sets with FDR<0.05
nrow(subset(fres_dn,padj<0.05))
## [1] 128
message("number of down-regulated gene sets with p<0.05")
## number of down-regulated gene sets with p<0.05
nrow(subset(fres_dn,pval<0.05))
## [1] 352
fres_dn_fdr <- subset(fres_dn,padj<0.05)$pathway
fres_dn_nom <- subset(fres_dn,pval<0.05)$pathway
v1 <- list("up p"=fres_up_nom, "up FDR"=fres_up_fdr,
"dn p"=fres_dn_nom, "dn FDR"=fres_dn_fdr)
par(mar=c(1,1,1,1))
par(mfrow=c(1,1))
plot(euler(v1),quantities = list(cex = 2), labels = list(cex = 2))
fres_fdr <- union(fres_dn_fdr,fres_up_fdr)
fres_nom <- union(fres_dn_nom,fres_up_nom)
v1 <- list("p"=fres_nom, "FDR"=fres_fdr)
plot(euler(v1),quantities = list(cex = 2), labels = list(cex = 2))
unlink("img/1b.png")
png("img/1b.png", width = 480, height = 400)
plot(euler(v1),quantities = list(cex = 2), labels = list(cex = 2))
dev.off()
## png
## 2
par(mfrow=c(1,1))
unlink("img/1b.pdf")
pdf("img/1b.pdf", width = 5, height = 5)
plot(euler(v1),quantities = list(cex = 2), labels = list(cex = 2))
dev.off()
## png
## 2
par(mfrow=c(1,1))
par(mar=c(5.1,4.1,4.1,2.1))
That analysis shows how a big difference between FDR and raw p-values, but it doesn’t prove that the p-value hits are false positives.
Here, we can prove they are false positives by randomly selecting 1000 genes from the list of detected ones, and testing them for enrichment.
When doing enrichment analysis of a random list of 1000 genes, we got an average of 51.6 pathways with p<0.05, while after p-value correction, that number was just 0.16 with FDR<0.05. This correponds to the data shown in Figure 1A.
seeds <- seq(from=100,to=100*100,by=100)
m1sim <- mclapply(seeds, function(seed) {
set.seed(seed)
rand <- sample(x=bg,size=1000,replace=FALSE)
fres_rand <- fora(pathways=gsets, genes=rand, universe=bg, minSize = 5, maxSize = Inf)
nom <- nrow(subset(fres_rand,pval<0.05))
fdr <- nrow(subset(fres_rand,padj<0.05))
c(nom,fdr)
},mc.cores=8)
m1sim <- do.call(rbind,m1sim)
colnames(m1sim) <- c("p","FDR")
boxplot(m1sim,main="Random gene sets (100 repetitions)",ylab="no. gene sets meeting \"significance\" threshold <0.05",frame=FALSE)
beeswarm(list(mean(m1sim[,1]), mean(m1sim[,2])),add=TRUE,pch=4,cex=2)
lab1 <- paste("Mean=",signif(mean(m1sim[,1]) , 3),sep="")
lab2 <- paste("Mean=",signif(mean(m1sim[,2]) , 2),sep="")
text(c(1.2,2.2), c( mean(m1sim[,1])+5, mean(m1sim[,2])+5 ), labels=c(lab1,lab2) )
unlink("img/1a.png")
png("img/1a.png", width = 480, height = 480)
boxplot(m1sim,main="Random gene sets (100 repetitions)",ylab="no. gene sets meeting \"significance\" threshold <0.05",frame=FALSE)
beeswarm(list(mean(m1sim[,1]), mean(m1sim[,2])),add=TRUE,pch=4,cex=2)
lab1 <- paste("Mean=",signif(mean(m1sim[,1]) , 3),sep="")
lab2 <- paste("Mean=",signif(mean(m1sim[,2]) , 2),sep="")
text(c(1.2,2.2), c( mean(m1sim[,1])+5, mean(m1sim[,2])+5 ), labels=c(lab1,lab2) )
dev.off()
## png
## 2
par(mfrow=c(1,1))
unlink("img/1a.pdf")
pdf("img/1a.pdf", width = 5, height = 5)
boxplot(m1sim,main="Random gene sets (100 repetitions)",ylab="no. gene sets meeting \"significance\" threshold <0.05",frame=FALSE)
beeswarm(list(mean(m1sim[,1]), mean(m1sim[,2])),add=TRUE,pch=4,cex=2)
lab1 <- paste("Mean=",signif(mean(m1sim[,1]) , 3),sep="")
lab2 <- paste("Mean=",signif(mean(m1sim[,2]) , 2),sep="")
text(c(1.2,2.2), c( mean(m1sim[,1])+5, mean(m1sim[,2])+5 ), labels=c(lab1,lab2) )
dev.off()
## png
## 2
par(mfrow=c(1,1))
par(mar=c(5.1,4.1,4.1,2.1))
Will try FORA.
| Reporting criteria | Method/resource used |
|---|---|
| Origin of gene sets | Reactome (2023-03-06) |
| Tool used | FORA (check fgsea version at foot of report) |
| Statistical test used | hypergeometric |
| P-value correction for multiple comparisons | FDR method |
| Background list | Genes with >=10 reads per sample on average across all samples |
| Gene Selection | DESeq2 FDR<0.05 and log2FC>0 or log2FC<0 |
| Data availability | via DEE2 at accession SRP038101 (human) |
| Other parameters | Min gene set size of 5 |
message("Number of genes in the count matrix")
## Number of genes in the count matrix
nrow(x$GeneCounts)
## [1] 58302
rowmeans <- rowMeans(x$GeneCounts)
message("Number of genes in the count matrix with more than 10 reads per gene on average.")
## Number of genes in the count matrix with more than 10 reads per gene on average.
length(rowmeans[which(rowmeans>=10)])
## [1] 13168
message("Number of genes in the count matrix with less than 10 reads per gene on average.")
## Number of genes in the count matrix with less than 10 reads per gene on average.
length(rowmeans[which(rowmeans<10)])
## [1] 45134
message("Proportion of genes below the detection threshold.")
## Proportion of genes below the detection threshold.
length(rowmeans[which(rowmeans<10)]) / length(rowmeans)
## [1] 0.7741415
message("Proportion of genes above the detection threshold.")
## Proportion of genes above the detection threshold.
( 1- sum(rowmeans[which(rowmeans>=10)]) / sum(rowmeans) ) * 100
## [1] 0.1917788
unfilt <- x$GeneCounts
message("Run deseq2 for differential expression with and without filtering.")
## Run deseq2 for differential expression with and without filtering.
dds_unfilt <- DESeqDataSetFromMatrix(countData = unfilt , colData = ss, design = ~ trt )
res_unfilt <- DESeq(dds_unfilt)
## estimating size factors
## estimating dispersions
## gene-wise dispersion estimates
## mean-dispersion relationship
## final dispersion estimates
## fitting model and testing
z_unfilt <- results(res_unfilt)
vsd_unfilt <- vst(dds_unfilt, blind=FALSE)
zz_unfilt <-cbind(as.data.frame(z_unfilt),unfilt)
def_unfilt <-as.data.frame(zz_unfilt[order(zz_unfilt$pvalue),])
filt <- unfilt[which(rowMeans(unfilt)>=10),]
dds_filt <- DESeqDataSetFromMatrix(countData = filt , colData = ss, design = ~ trt )
res_filt <- DESeq(dds_filt)
## estimating size factors
## estimating dispersions
## gene-wise dispersion estimates
## mean-dispersion relationship
## final dispersion estimates
## fitting model and testing
z_filt <- results(res_filt)
vsd_filt <- vst(dds_filt, blind=FALSE)
zz_filt <-cbind(as.data.frame(z_filt),filt)
def_filt <-as.data.frame(zz_filt[order(zz_filt$pvalue),])
message("No significant genes without filtering low genes")
## No significant genes without filtering low genes
nrow(subset(def_unfilt,padj<0.05))
## [1] 3463
message("No significant genes with filtering low genes")
## No significant genes with filtering low genes
nrow(subset(def_filt,padj<0.05))
## [1] 3598
The violin chart is used to understand whether the distribution of gene expression levels of the background gene list (NS) is similar to the distributrion of DEGs. If there is a big difference, it is bad, because we already know that genes with high expression in a cell type are likely to be very different ontologies when compared to lower expressed genes. In the unfiltered pair of violin charts, we see that there is a big difference in the distributions indicating that a lot of the enrichment results could be solely due to basemean expression levels, not related to the up or down regulation at all. When filtering is applied, we see that the difference between DEG and NS distributions is smaller.
deg_unfilt <- subset(def_unfilt,padj<0.05)
ns_unfilt <- subset(def_unfilt,padj>0.05)
rowmeans_deg_unfilt <- rowMeans(deg_unfilt[,7:12])
rowmeans_ns_unfilt <- rowMeans(ns_unfilt[,7:12])
viodata <- list("DEG"=log10(rowmeans_deg_unfilt+0.01),"NS"=log10(rowmeans_ns_unfilt+0.01))
deg_filt <- subset(def_filt,padj<0.05)
ns_filt <- subset(def_filt,padj>0.05)
rowmeans_deg_filt <- rowMeans(deg_filt[,7:12])
rowmeans_ns_filt <- rowMeans(ns_filt[,7:12])
message("Distribution of gene expression levels")
## Distribution of gene expression levels
viodata2 <- list("DEG unfilt"=log10(rowmeans_deg_unfilt+0.01),
"NS unfilt"=log10(rowmeans_ns_unfilt+0.01),
"DEG filt"=log10(rowmeans_deg_filt+0.01),
"NS filt"=log10(rowmeans_ns_filt+0.01))
vioplot(viodata2,ylab="log10 mean expression",las=2,side="left")
lapply(viodata2,summary)
## $`DEG unfilt`
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.502 2.268 2.800 2.667 3.201 4.759
##
## $`NS unfilt`
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.502 1.195 2.161 2.037 2.764 4.901
##
## $`DEG filt`
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1.000 2.280 2.775 2.691 3.171 4.759
##
## $`NS filt`
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1.000 1.763 2.436 2.346 2.880 4.901
In this next analysis, we run parallel fora tests, one uses the correct background made up of detected genes (bg), while the other one uses all genes in the genome (marked with *).
The second euler diagram shown below corresponds to Figure 2C.
# up dn and bg are defined above
all <- unique(gtm$V2)
message("no. genes in each of the lists up, down, bg and all.")
## no. genes in each of the lists up, down, bg and all.
lapply(list(up,dn,bg,all),length)
## [[1]]
## [1] 1667
##
## [[2]]
## [1] 1923
##
## [[3]]
## [1] 13063
##
## [[4]]
## [1] 55503
fres_up_bg <- fora(pathways=gsets, genes=up, universe=bg, minSize = 5, maxSize = Inf)
fres_up_bg <- subset(fres_up_bg,padj<0.05)$pathway
message("no up-regulated pathways with the correct background.")
## no up-regulated pathways with the correct background.
length(fres_up_bg)
## [1] 224
fres_up_all <- fora(pathways=gsets, genes=up, universe=all, minSize = 5, maxSize = Inf)
fres_up_all <- subset(fres_up_all,padj<0.05)$pathway
message("no up-regulated pathways with wg background.")
## no up-regulated pathways with wg background.
length(fres_up_all)
## [1] 744
fres_dn_bg <- fora(pathways=gsets, genes=dn, universe=bg, minSize = 5, maxSize = Inf)
fres_dn_bg <- subset(fres_dn_bg,padj<0.05)$pathway
message("no down-regulated pathways with the correct background.")
## no down-regulated pathways with the correct background.
length(fres_dn_bg)
## [1] 128
fres_dn_all <- fora(pathways=gsets, genes=dn, universe=all, minSize = 5, maxSize = Inf)
fres_dn_all <- subset(fres_dn_all,padj<0.05)$pathway
message("no down-regulated pathways with wg background.")
## no down-regulated pathways with wg background.
length(fres_dn_all)
## [1] 812
v1 <- list("up"=fres_up_bg, "up*"=fres_up_all, "dn"=fres_dn_bg, "dn*"=fres_dn_all)
message("no. significant pathways with correct and incorrect* background with direction specified.")
## no. significant pathways with correct and incorrect* background with direction specified.
par(mar=c(1,1,1,1))
par(mfrow=c(1,1))
plot(euler(v1),quantities = list(cex = 2), labels = list(cex = 2))
v2 <- list("bg"=union(fres_up_bg,fres_dn_bg),
"bg*"=union(fres_up_all, fres_dn_all) )
message("no. significant pathways with correct and incorrect* background. Direction not specified.")
## no. significant pathways with correct and incorrect* background. Direction not specified.
plot(euler(v2),quantities = list(cex = 2), labels = list(cex = 2))
par(mar=c(1,1,1,1))
unlink("img/2c.png")
png("img/2c.png", width = 480, height = 400)
plot(euler(v2),quantities = list(cex = 2), labels = list(cex = 2))
dev.off()
## png
## 2
par(mfrow=c(1,1))
par(mar=c(1,1,1,1))
unlink("img/2c.pdf")
pdf("img/2c.pdf", width = 5, height = 5)
plot(euler(v2),quantities = list(cex = 2), labels = list(cex = 2))
dev.off()
## png
## 2
par(mfrow=c(1,1))
par(mar=c(5.1,4.1,4.1,2.1))
Here, we select genes randomly from the genes above the detection threshold (bg list) and compare to whole genome background.
There are ~444 gene sets significant on average with this approach, as the genes expressed in AML3 cells are distinctive compared to the whole genome annotation.
When using the correct background, a mean of 0.16 was obtained. This shows that false positives can be nearly eliminated by using this detection threshold.
This result corresponds to Figure 2A.
seeds <- seq(from=100,to=100*100,by=100)
m2sim <- mclapply(seeds, function(seed) {
set.seed(seed)
rand <- sample(x=bg,size=1000,replace=FALSE)
sim_all <- fora(pathways=gsets, genes=rand, universe=all, minSize = 5, maxSize = Inf)
sim_bg <- fora(pathways=gsets, genes=rand, universe=bg, minSize = 5, maxSize = Inf)
sig_all <- nrow(subset(sim_all,padj<0.05))
sig_bg <- nrow(subset(sim_bg,padj<0.05))
c(sig_all,sig_bg)
},mc.cores=8)
m2sim <- do.call(rbind,m2sim)
colnames(m2sim) <- c("Whole genome background","Correct background")
par(mar=c(5.1,4.1,4.1,2.1))
boxplot(m2sim,main="Random gene sets drawn from BG",ylab="no. gene sets FDR<0.05",frame=FALSE)
mtext("100 simulations")
beeswarm(list(mean(m2sim[,1]), mean(m2sim[,2])),add=TRUE,pch=4,cex=2)
lab1 <- paste("Mean=",signif(mean(m2sim[,1]) , 3),sep="")
lab2 <- paste("Mean=",signif(mean(m2sim[,2]) , 2),sep="")
text(c(1.2,2.2), c( mean(m2sim[,1])+15, mean(m2sim[,2])+15 ), labels=c(lab1,lab2) )
par(mar=c(5.1,4.1,4.1,2.1))
unlink("img/2a.png")
png("img/2a.png", width = 480, height = 480)
boxplot(m2sim,main="Random gene sets drawn from BG",ylab="no. gene sets FDR<0.05",frame=FALSE)
mtext("100 simulations")
beeswarm(list(mean(m2sim[,1]), mean(m2sim[,2])),add=TRUE,pch=4,cex=2)
lab1 <- paste("Mean=",signif(mean(m2sim[,1]) , 3),sep="")
lab2 <- paste("Mean=",signif(mean(m2sim[,2]) , 2),sep="")
text(c(1.2,2.2), c( mean(m2sim[,1])+15, mean(m2sim[,2])+15 ), labels=c(lab1,lab2) )
dev.off()
## png
## 2
par(mfrow=c(1,1))
par(mar=c(5.1,4.1,4.1,2.1))
unlink("img/2a.pdf")
pdf("img/2a.pdf", width = 5, height = 5)
boxplot(m2sim,main="Random gene sets drawn from BG",ylab="no. gene sets FDR<0.05",frame=FALSE)
mtext("100 simulations")
beeswarm(list(mean(m2sim[,1]), mean(m2sim[,2])),add=TRUE,pch=4,cex=2)
lab1 <- paste("Mean=",signif(mean(m2sim[,1]) , 3),sep="")
lab2 <- paste("Mean=",signif(mean(m2sim[,2]) , 2),sep="")
text(c(1.2,2.2), c( mean(m2sim[,1])+15, mean(m2sim[,2])+15 ), labels=c(lab1,lab2) )
dev.off()
## png
## 2
par(mfrow=c(1,1))
par(mar=c(5.1,4.1,4.1,2.1))
In the simulation, which gene sets falsely come up most?
There’s 37 pathways that came up 100/100 simulations. This corresponds to Figure 2B.
m2simpw <- mclapply(seeds, function(seed) {
set.seed(seed)
rand <- sample(x=bg,size=1000,replace=FALSE)
sim_all <- fora(pathways=gsets, genes=rand, universe=all, minSize = 5, maxSize = Inf)
sig_all <- subset(sim_all,padj<0.05)$pathway
return(sig_all)
},mc.cores=8)
m2tbl <- table(unlist((m2simpw)))
m2pw <- table(m2tbl)
plot(table(m2tbl),type="b",
xlab="no. appearances as false positive, from 100 simulations",
ylab="no. gene sets",
main="1000 random genes drawn from expressed genes",
bty="n"
)
set.seed(42) ; m2selected <- sample(names(which(m2tbl==100)),20)
text(100,seq(from=40,to=120,by=(120-40)/20),labels=m2selected,cex=0.8,pos=2)
par(mar=c(5.1,4.1,4.1,2.1))
unlink("img/2b.png")
png("img/2b.png", width = 480, height = 480)
plot(table(m2tbl),type="b",
xlab="no. appearances as false positive, from 100 simulations",
ylab="no. gene sets",
main="1000 random genes drawn from expressed genes",
bty="n"
)
set.seed(42) ; m2selected <- sample(names(which(m2tbl==100)),20)
text(100,seq(from=40,to=120,by=(120-40)/20),labels=m2selected,cex=0.8,pos=2)
dev.off()
## png
## 2
par(mfrow=c(1,1))
par(mar=c(5.1,4.1,4.1,2.1))
unlink("img/2b.pdf")
pdf("img/2b.pdf", width = 5, height = 5)
plot(table(m2tbl),type="b",
xlab="no. appearances as false positive, from 100 simulations",
ylab="no. gene sets",
main="1000 random genes drawn from expressed genes",
bty="n"
)
set.seed(42) ; m2selected <- sample(names(which(m2tbl==100)),20)
text(100,seq(from=40,to=120,by=(120-40)/20),labels=m2selected,cex=0.7,pos=2)
dev.off()
## png
## 2
par(mfrow=c(1,1))
| Reporting criteria | Method/resource used |
|---|---|
| Origin of gene sets | Reactome (2023-03-06) |
| Tool used | mitch (check version at foot of report) |
| Statistical test used | Rank ANOVA (Kruskal Wallis) |
| P-value correction for multiple comparisons | FDR method |
| Background list | Genes with >=10 reads per sample on average across all samples |
| Gene Scoring | DESeq2 Wald stat |
| Data availability | via DEE2 at accession SRP038101 (human) |
| Other parameters | Min gene set size of 5 |
This scatterplot shows how prioritising by FDR can lead to missing the gene sets with the biggest effect sizes. This corresponds to Figure 3.
def2 <- def
rownames(def2) <- sapply(strsplit(rownames(def)," "),'[[',1)
m <- mitch_import(x=def2,DEtype="deseq2",geneTable=gtnew)
## The input is a single dataframe; one contrast only. Converting
## it to a list for you.
## Note: Mean no. genes in input = 13168
## Note: no. genes in output = 13063
## Note: estimated proportion of input genes in output = 0.992
mres <- mitch_calc(x=m,genesets=gsets,minsetsize=5,cores=4,priority="significance")
## Note: When prioritising by significance (ie: small
## p-values), large effect sizes might be missed.
message("No. significant gene sets")
## No. significant gene sets
nrow(subset(mres$enrichment_result,p.adjustANOVA<0.05))
## [1] 515
message("No. gene sets included")
## No. gene sets included
nrow(subset(mres$enrichment_result,p.adjustANOVA<1))
## [1] 1840
mresdf <- mres$enrichment_result
sig <- subset(mresdf,p.adjustANOVA<0.05)
plot(abs(mresdf$s.dist),-log10(mresdf$pANOVA),
main="enrichment scores versus significance",
bty="n",pch=19,cex=1,col="darkgray",
xlab="absolute enrichment score",
ylab="-log10(p-value)",
xlim=c(0,0.95))
points(abs(sig$s.dist),-log10(sig$pANOVA),
pch=19,cex=1,col="red")
points(c(0.35,0.8),c(42,8),cex=17,pch=0)
text(c(0.35,0.8),c(42,8),c("generic","specific"))
par(mar=c(5.1,4.1,4.1,2.1))
unlink("img/3a.png")
png("img/3a.png", width = 480, height = 480)
plot(abs(mresdf$s.dist),-log10(mresdf$pANOVA),
main="enrichment scores versus significance",
bty="n",pch=19,cex=1,col="darkgray",
xlab="absolute enrichment score",
ylab="-log10(p-value)",
xlim=c(0,0.95))
points(abs(sig$s.dist),-log10(sig$pANOVA),
pch=19,cex=1,col="red")
points(c(0.35,0.8),c(41,8),cex=17,pch=0)
text(c(0.35,0.8),c(41,8),c("generic","specific"))
dev.off()
## png
## 2
par(mfrow=c(1,1))
par(mar=c(5.1,4.1,4.1,2.1))
par(mar=c(5.1,4.1,4.1,2.1))
unlink("img/3a.pdf")
pdf("img/3a.pdf", width = 5, height = 5)
plot(abs(mresdf$s.dist),-log10(mresdf$pANOVA),
main="enrichment scores versus significance",
bty="n",pch=19,cex=1,col="darkgray",
xlab="absolute enrichment score",
ylab="-log10(p-value)",
xlim=c(0,0.95))
points(abs(sig$s.dist),-log10(sig$pANOVA),
pch=19,cex=1,col="red")
points(c(0.35,0.8),c(41,8),cex=17,pch=0)
text(c(0.35,0.8),c(41,8),c("generic","specific"))
dev.off()
## png
## 2
par(mfrow=c(1,1))
par(mar=c(5.1,4.1,4.1,2.1))
Now, let’s take a look at the top results when prioritising with FDR only and with enrichment score.
FDR prioritisation emphasises larger and more generic functions.
ES prioritisation emphasises smaller, more specific categories with larger effect sizes.
To prove this, I go back to the DE table and fetch the log2 fold changes.
These tables correspond to Table 1 and 2.
mres_sig_up <- head(subset(mres$enrichment_result,s.dist>0),10)
mres_sig_dn <- head(subset(mres$enrichment_result,s.dist<0),10)
# get log2fc values
mres_sig_up$mean_lfc <- sapply(mres_sig_up$set[1:10], function(y) {
genes <- gsets[[which(names(gsets) == y)]]
gid <- unique(gtnew[match(genes,gtnew$V2),1])
lfc <- def2[which(rownames(def2) %in% gid),"log2FoldChange"]
return(mean(lfc))
})
mres_sig_dn$mean_lfc <- sapply(mres_sig_dn$set[1:10], function(y) {
genes <- gsets[[which(names(gsets) == y)]]
gid <- unique(gtnew[match(genes,gtnew$V2),1])
lfc <- def2[which(rownames(def2) %in% gid),"log2FoldChange"]
return(mean(lfc))
})
mres_sig_up_tbl <- apply(data.frame(mres_sig_up[,1:2],
apply(mres_sig_up[,3:6],2,signif,2)),2,as.character)
mres_sig_dn_tbl <- apply(data.frame(mres_sig_dn[,1:2],
apply(mres_sig_dn[,3:6],2,signif,2)),2,as.character)
mres_sig_up_tbl %>%
kbl(caption="Top upregulated pathways when prioritised by FDR. ",row.names=FALSE) %>%
kable_paper("hover", full_width = F)
| set | setSize | pANOVA | s.dist | p.adjustANOVA | mean_lfc |
|---|---|---|---|---|---|
| Cell Cycle | 589 | 1.4e-47 | 0.35 | 2.6e-44 | 0.069 |
| Metabolism of RNA | 725 | 4.4e-46 | 0.31 | 4.0e-43 | 0.079 |
| Cell Cycle, Mitotic | 479 | 1.5e-39 | 0.35 | 9.1e-37 | 0.066 |
| Cell Cycle Checkpoints | 246 | 2.1e-27 | 0.40 | 9.6e-25 | 0.081 |
| M Phase | 336 | 9.5e-27 | 0.34 | 2.9e-24 | 0.065 |
| Mitotic Prometaphase | 189 | 4.6e-25 | 0.44 | 1.1e-22 | 0.120 |
| Mitotic Metaphase and Anaphase | 208 | 8.9e-24 | 0.40 | 1.8e-21 | 0.110 |
| Mitotic Anaphase | 207 | 2.3e-23 | 0.40 | 4.3e-21 | 0.110 |
| Processing of Capped Intron-Containing Pre-mRNA | 273 | 6.6e-23 | 0.35 | 1.1e-20 | 0.084 |
| Resolution of Sister Chromatid Cohesion | 114 | 1.0e-20 | 0.51 | 1.6e-18 | 0.140 |
mres_sig_dn_tbl %>%
kbl(caption="Top downregulated pathways when prioritised by FDR",row.names=FALSE) %>%
kable_paper("hover", full_width = F)
| set | setSize | pANOVA | s.dist | p.adjustANOVA | mean_lfc |
|---|---|---|---|---|---|
| Innate Immune System | 747 | 4.3e-27 | -0.23 | 1.6e-24 | -0.23 |
| Immune System | 1434 | 5.8e-26 | -0.17 | 1.5e-23 | -0.23 |
| Interferon alpha/beta signaling | 60 | 1.6e-19 | -0.67 | 2.3e-17 | -1.00 |
| Neutrophil degranulation | 382 | 1.4e-18 | -0.26 | 1.7e-16 | -0.27 |
| Interferon gamma signaling | 67 | 1.8e-15 | -0.56 | 1.6e-13 | -0.64 |
| Extracellular matrix organization | 154 | 1.4e-13 | -0.35 | 8.5e-12 | -0.48 |
| Cytokine Signaling in Immune system | 567 | 2.1e-12 | -0.17 | 1.0e-10 | -0.30 |
| Platelet activation, signaling and aggregation | 174 | 9.1e-12 | -0.30 | 3.6e-10 | -0.26 |
| Signaling by Interleukins | 327 | 2.0e-10 | -0.21 | 5.7e-09 | -0.25 |
| Immunoregulatory interactions between a Lymphoid and a non-Lymphoid cell | 60 | 5.1e-10 | -0.46 | 1.3e-08 | -0.70 |
mres <- mitch_calc(x=m,genesets=gsets,minsetsize=5,cores=4,priority="effect")
## Note: Enrichments with large effect sizes may not be
## statistically significant.
mres_es_up <- head(subset(mres$enrichment_result,s.dist>0 & p.adjustANOVA < 0.05),10)
mres_es_dn <- head(subset(mres$enrichment_result,s.dist<0 & p.adjustANOVA < 0.05),10)
# get log2fc values
mres_es_up$mean_lfc <- sapply(mres_es_up$set[1:10], function(y) {
genes <- gsets[[which(names(gsets) == y)]]
gid <- unique(gtnew[match(genes,gtnew$V2),1])
lfc <- def2[which(rownames(def2) %in% gid),"log2FoldChange"]
return(mean(lfc))
})
mres_es_dn$mean_lfc <- sapply(mres_es_dn$set[1:10], function(y) {
genes <- gsets[[which(names(gsets) == y)]]
gid <- unique(gtnew[match(genes,gtnew$V2),1])
lfc <- def2[which(rownames(def2) %in% gid),"log2FoldChange"]
return(mean(lfc))
})
mres_es_up_tbl <- apply(data.frame(mres_es_up[,1:2],
apply(mres_es_up[,3:6],2,signif,2)),2,as.character)
mres_es_dn_tbl <- apply(data.frame(mres_es_dn[,1:2],
apply(mres_es_dn[,3:6],2,signif,2)),2,as.character)
mres_es_up_tbl %>%
kbl(caption="Top upregulated pathways when prioritised by ES (discarding any with FDR>0.05)",row.names=FALSE) %>%
kable_paper("hover", full_width = F)
| set | setSize | pANOVA | s.dist | p.adjustANOVA | mean_lfc |
|---|---|---|---|---|---|
| Activation of NOXA and translocation to mitochondria | 5 | 1.1e-03 | 0.84 | 6.9e-03 | 0.26 |
| Condensation of Prometaphase Chromosomes | 11 | 2.5e-06 | 0.82 | 3.5e-05 | 0.26 |
| Postmitotic nuclear pore complex (NPC) reformation | 27 | 1.8e-11 | 0.75 | 6.4e-10 | 0.21 |
| Phosphorylation of Emi1 | 6 | 1.6e-03 | 0.75 | 8.8e-03 | 0.24 |
| Interactions of Rev with host cellular proteins | 37 | 6.8e-15 | 0.74 | 5.2e-13 | 0.20 |
| Nuclear import of Rev protein | 34 | 1.7e-13 | 0.73 | 9.9e-12 | 0.20 |
| Rev-mediated nuclear export of HIV RNA | 35 | 1.0e-13 | 0.73 | 6.3e-12 | 0.20 |
| Transport of Ribonucleoproteins into the Host Nucleus | 32 | 2.1e-12 | 0.72 | 1.0e-10 | 0.20 |
| Export of Viral Ribonucleoproteins from Nucleus | 32 | 2.9e-12 | 0.71 | 1.3e-10 | 0.19 |
| NEP/NS2 Interacts with the Cellular Export Machinery | 32 | 2.9e-12 | 0.71 | 1.3e-10 | 0.19 |
mres_es_dn_tbl %>%
kbl(caption="Top upregulated pathways when prioritised by ES (discarding any with FDR>0.05)",row.names=FALSE) %>%
kable_paper("hover", full_width = F)
| set | setSize | pANOVA | s.dist | p.adjustANOVA | mean_lfc |
|---|---|---|---|---|---|
| Prostanoid ligand receptors | 5 | 9.7e-04 | -0.85 | 0.00620 | -1.90 |
| Interleukin-9 signaling | 6 | 6.0e-04 | -0.81 | 0.00430 | -1.00 |
| Interleukin-2 signaling | 7 | 3.5e-04 | -0.78 | 0.00270 | -0.76 |
| Interleukin-21 signaling | 8 | 2.6e-04 | -0.75 | 0.00220 | -0.93 |
| Specification of primordial germ cells | 6 | 1.7e-03 | -0.74 | 0.00960 | -0.69 |
| Fatty Acids bound to GPR40 (FFAR1) regulate insulin secretion | 6 | 1.8e-03 | -0.74 | 0.00990 | -0.60 |
| Scavenging by Class A Receptors | 5 | 4.5e-03 | -0.73 | 0.02100 | -1.30 |
| Biosynthesis of Lipoxins (LX) | 5 | 6.9e-03 | -0.70 | 0.02900 | -0.72 |
| Interleukin-20 family signaling | 12 | 3.3e-05 | -0.69 | 0.00038 | -0.79 |
| Epithelial-Mesenchymal Transition (EMT) during gastrulation | 5 | 7.7e-03 | -0.69 | 0.03100 | -0.96 |
m4tbls <- list("esup"=mres_es_up_tbl,"esdn"=mres_es_dn_tbl,"sigup"=mres_sig_up_tbl,"sigdn"=mres_sig_dn_tbl)
saveRDS(m4tbls,"m4tbls.Rds")
message("Are mean log2FC values for ES prioritisation bigger than FDR approach?")
## Are mean log2FC values for ES prioritisation bigger than FDR approach?
message("Are mean log2FC values for ES prioritisation (UP)")
## Are mean log2FC values for ES prioritisation (UP)
mean(abs(mres_es_up$mean_lfc ))
## [1] 0.2169444
message("Are mean log2FC values for FDR prioritisation (UP)")
## Are mean log2FC values for FDR prioritisation (UP)
mean(abs(mres_sig_up$mean_lfc ))
## [1] 0.0914005
message("ES:FDR ratio (UP)")
## ES:FDR ratio (UP)
mean(abs(mres_es_up$mean_lfc )) / mean(abs(mres_sig_up$mean_lfc ))
## [1] 2.373559
message("Are mean log2FC values for ES prioritisation (DOWN)")
## Are mean log2FC values for ES prioritisation (DOWN)
mean(abs(mres_es_dn$mean_lfc ))
## [1] 0.9607844
message("Are mean log2FC values for FDR prioritisation (DOWN)")
## Are mean log2FC values for FDR prioritisation (DOWN)
mean(abs(mres_sig_dn$mean_lfc ))
## [1] 0.439364
message("ES:FDR ratio (DOWN)")
## ES:FDR ratio (DOWN)
mean(abs(mres_es_dn$mean_lfc )) / mean(abs(mres_sig_dn$mean_lfc ))
## [1] 2.186762
Make a barplot
par(mar=c(2,20,2,0))
mres_sig_up_vals <- -log(mres_sig_up$pANOVA)
names(mres_sig_up_vals) <- mres_sig_up$set
mres_sig_dn_vals <- -log(mres_sig_dn$pANOVA)
names(mres_sig_dn_vals) <- mres_sig_dn$set
mres_es_up_vals <- mres_es_up$s.dist
names(mres_es_up_vals) <- mres_es_up$set
mres_es_dn_vals <- -1 * mres_es_dn$s.dist
names(mres_es_dn_vals) <- mres_es_dn$set
par(mfrow=c(2,1))
barplot( mres_sig_up_vals,
horiz=TRUE,las=1,cex.names=0.65,col="gray",
main="Up Sig (-log10 p-value)",
xlab="-log10 p-value",
cex.main=0.9)
barplot( mres_es_up_vals,
horiz=TRUE,las=1,cex.names=0.65,col="gray",
main="Up ES",
xlab="ES",
cex.main=0.9)
barplot( mres_sig_dn_vals,
horiz=TRUE,las=1,cex.names=0.65,col="gray",
main="Down Sig (-log10 p-value)",
xlab="-log10 p-value",
cex.main=0.9)
barplot( mres_es_dn_vals,
horiz=TRUE,las=1,cex.names=0.65,col="gray",
main="Down ES",
xlab="-ES",
cex.main=0.9)
par(mfrow=c(1,1))
par(mar=c(5.1,4.1,4.1,2.1))
ORA is known to have variable results when using gene lists of different length. I will vary the foreground gene length from 50 to 7000 and see how many significant hits I get.
up <- rownames(subset(def, log2FoldChange>0 & padj<0.05))
up <- sapply(strsplit(up," "),'[[',1)
up <- unique(gtnew[match(up,gtnew$V1),2])
up <- up[which(!is.na(up))]
dn <- rownames(subset(def, log2FoldChange<0 & padj<0.05))
dn <- sapply(strsplit(dn," "),'[[',1)
dn <- unique(gtnew[match(dn,gtnew$V1),2])
dn <- dn[which(!is.na(dn))]
bg <- rownames(def)
bg <- sapply(strsplit(bg," "),'[[',1)
bg <- unique(gtnew[match(bg,gtnew$V1),2])
bg <- bg[which(!is.na(bg))]
message("Length of up, down and background gene lists. Only for context.")
## Length of up, down and background gene lists. Only for context.
lapply(list(up,dn,bg),length)
## [[1]]
## [1] 1667
##
## [[2]]
## [1] 1923
##
## [[3]]
## [1] 13063
sizes <- c(50,100,200,300,400,500,600,700,800,900,1000,1500,2000,2500,3000,4000,5000,6000,7000)
fup <- nrow(subset(fora(pathways=gsets, genes=up, universe=bg, minSize = 5, maxSize = Inf),padj<0.05))
fdn <- nrow(subset(fora(pathways=gsets, genes=dn, universe=bg, minSize = 5, maxSize = Inf),padj<0.05))
nups <- unlist(lapply(sizes, function(i) {
ids <- rownames(subset(def2, log2FoldChange>0))
ids <- head(ids,i)
genes <- unique(gtnew[match(ids,gtnew$V1),2])
fres <- fora(pathways=gsets, genes=genes, universe=bg, minSize = 5, maxSize = Inf)
nrow(subset(fres,padj<0.05))
}))
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
ndns <- unlist(lapply(sizes, function(i) {
ids <- rownames(subset(def2, log2FoldChange<0))
ids <- head(ids,i)
genes <- unique(gtnew[match(ids,gtnew$V1),2])
fres <- fora(pathways=gsets, genes=genes, universe=bg, minSize = 5, maxSize = Inf)
nrow(subset(fres,padj<0.05))
}))
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
df <- data.frame("up"=nups,"dn"=ndns,row.names=sizes)
df$tot <- df$up + df$dn
df %>%
kbl(caption="Table XX. Number of differentially regulated gene sets when varying the foreground gene list length.") %>%
kable_paper("hover", full_width = F)
| up | dn | tot | |
|---|---|---|---|
| 50 | 0 | 31 | 31 |
| 100 | 0 | 40 | 40 |
| 200 | 8 | 46 | 54 |
| 300 | 46 | 51 | 97 |
| 400 | 88 | 54 | 142 |
| 500 | 116 | 72 | 188 |
| 600 | 158 | 71 | 229 |
| 700 | 189 | 86 | 275 |
| 800 | 180 | 117 | 297 |
| 900 | 194 | 125 | 319 |
| 1000 | 202 | 120 | 322 |
| 1500 | 227 | 118 | 345 |
| 2000 | 256 | 143 | 399 |
| 2500 | 283 | 172 | 455 |
| 3000 | 282 | 159 | 441 |
| 4000 | 263 | 143 | 406 |
| 5000 | 228 | 102 | 330 |
| 6000 | 192 | 96 | 288 |
| 7000 | 165 | 57 | 222 |
dn_fes <- lapply(sizes, function(i) {
ids <- rownames(subset(def2, log2FoldChange<0))
ids <- head(ids,i)
genes <- unique(gtnew[match(ids,gtnew$V1),2])
fres <- fora(pathways=gsets, genes=genes, universe=bg, minSize = 5, maxSize = Inf)
subset(fres,padj<0.05)$foldEnrichment
})
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
up_fes <- lapply(sizes, function(i) {
ids <- rownames(subset(def2, log2FoldChange>0))
ids <- head(ids,i)
genes <- unique(gtnew[match(ids,gtnew$V1),2])
fres <- fora(pathways=gsets, genes=genes, universe=bg, minSize = 5, maxSize = Inf)
subset(fres,padj<0.05)$foldEnrichment
})
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
## Warning in fora(pathways = gsets, genes = genes, universe = bg, minSize = 5, :
## Not all of the input genes belong to the universe, such genes were removed
fes_df <- data.frame(sapply(up_fes,mean),sapply(dn_fes,mean))
rownames(fes_df) <- sizes
fes_df %>%
kbl(caption="Table XX. Mean enrichment scores of differentially regulated gene sets when varying the foreground gene list length.") %>%
kable_paper("hover", full_width = F)
| sapply.up_fes..mean. | sapply.dn_fes..mean. | |
|---|---|---|
| 50 | NaN | 26.130373 |
| 100 | NaN | 16.850203 |
| 200 | 6.707725 | 10.828490 |
| 300 | 6.209672 | 8.201187 |
| 400 | 6.009917 | 6.484802 |
| 500 | 5.015326 | 5.355630 |
| 600 | 4.345136 | 5.050545 |
| 700 | 4.032200 | 4.390475 |
| 800 | 3.746988 | 4.245228 |
| 900 | 3.485536 | 4.076048 |
| 1000 | 3.536388 | 3.834779 |
| 1500 | 2.968448 | 3.453910 |
| 2000 | 2.822895 | 2.801642 |
| 2500 | 2.506884 | 2.475556 |
| 3000 | 2.283138 | 2.285824 |
| 4000 | 2.015312 | 1.929465 |
| 5000 | 1.759356 | 1.698034 |
| 6000 | 1.621670 | 1.588060 |
| 7000 | 1.548557 | 1.463401 |
nups3 <- unlist(lapply(up_fes,function(x) { length(which(x>=3)) }))
ndns3 <- unlist(lapply(dn_fes,function(x) { length(which(x>=3)) }))
df3 <- data.frame("up3"=nups3,"dn3"=ndns3,row.names=sizes)
df3$tot3 <- df3$up3 + df3$dn3
df3 %>%
kbl(caption="Table XX. No differentially regulated gene sets meeting the FES threshold of 3 when varying the foreground gene list length.") %>%
kable_paper("hover", full_width = F)
| up3 | dn3 | tot3 | |
|---|---|---|---|
| 50 | 0 | 30 | 30 |
| 100 | 0 | 39 | 39 |
| 200 | 8 | 43 | 51 |
| 300 | 44 | 43 | 87 |
| 400 | 79 | 44 | 123 |
| 500 | 99 | 55 | 154 |
| 600 | 131 | 48 | 179 |
| 700 | 140 | 55 | 195 |
| 800 | 128 | 74 | 202 |
| 900 | 110 | 74 | 184 |
| 1000 | 117 | 70 | 187 |
| 1500 | 92 | 67 | 159 |
| 2000 | 99 | 47 | 146 |
| 2500 | 68 | 35 | 103 |
| 3000 | 37 | 27 | 64 |
| 4000 | 3 | 4 | 7 |
| 5000 | 0 | 0 | 0 |
| 6000 | 0 | 0 | 0 |
| 7000 | 0 | 0 | 0 |
Now to make a line chart of this data. The first chart is the whole range of data, and the second is a zoom in to 50 to 500 genes. The line chart shown below corresponds to Figure 4.
plot(1:nrow(df),df$up,type="b",col="red", pch=19, las=1,
ylab="no. gene sets with FDR<0.05",xlab="gene list length",
bty="n",xaxt = "n")
axis(1, at = 1:nrow(df), labels=sizes, cex.axis=1,las=2)
lines(1:nrow(df),df$dn,type="b",col="blue",pch=19)
lines(1:nrow(df3),df3$dn3,type="b",col="lightblue",pch=19)
lines(1:nrow(df3),df3$up3,type="b",col="pink",pch=19)
legend("topleft", legend=c("Up", "Down","Up,FES>3","Down,FES>3"),
col=c("red", "blue","pink","lightblue"), lty=1, cex=1,pch=19)
grid()
unlink("img/5m.png")
png("img/5m.png", width = 540, height = 480)
plot(1:nrow(df),df$up,type="b",col="red", pch=19, las=1,
ylab="no. gene sets with FDR<0.05",xlab="gene list length",
bty="n",xaxt = "n")
axis(1, at = 1:nrow(df), labels=sizes, cex.axis=1,las=2)
lines(1:nrow(df),df$dn,type="b",col="blue",pch=19)
lines(1:nrow(df3),df3$dn3,type="b",col="lightblue",pch=19)
lines(1:nrow(df3),df3$up3,type="b",col="pink",pch=19)
legend("topleft", legend=c("Up", "Down","Up,FES>3","Down,FES>3"),
col=c("red", "blue","pink","lightblue"), lty=1, cex=1,pch=19)
grid()
dev.off()
## png
## 2
unlink("img/5m.pdf")
pdf("img/5m.pdf", width = 7, height = 5)
plot(1:nrow(df),df$up,type="b",col="red", pch=19, las=1,
ylab="no. gene sets with FDR<0.05",xlab="gene list length",
bty="n",xaxt = "n")
axis(1, at = 1:nrow(df), labels=sizes, cex.axis=1,las=2)
lines(1:nrow(df),df$dn,type="b",col="blue",pch=19)
lines(1:nrow(df3),df3$dn3,type="b",col="lightblue",pch=19)
lines(1:nrow(df3),df3$up3,type="b",col="pink",pch=19)
legend("topleft", legend=c("Up", "Down","Up,FES>3","Down,FES>3"),
col=c("red", "blue","pink","lightblue"), lty=1, cex=1,pch=19)
grid()
dev.off()
## png
## 2
Combining up and down into a single test can reduce the number of results significantly. This is because gene sets show typically have a predominantly pattern of up or down regulation but not both. The combined approach discovers only a few unique sets (10 in this case). The euler plot shown below corresponds to Figure 5.
message("No. up genes.")
## No. up genes.
length(up)
## [1] 1667
message("No. down genes.")
## No. down genes.
length(dn)
## [1] 1923
message("No. unique DE genes.")
## No. unique DE genes.
updn <- unique(c(up,dn))
length(updn)
## [1] 3590
fres_updn_bg <- fora(pathways=gsets, genes=updn, universe=bg, minSize = 5, maxSize = Inf)
fres_updn_bg <- subset(fres_updn_bg,padj<0.05)$pathway
message("no. significant sets using the combined test:")
## no. significant sets using the combined test:
length(fres_updn_bg)
## [1] 166
message("no. significant upregulated sets using the separate test:")
## no. significant upregulated sets using the separate test:
length(fres_up_bg)
## [1] 224
message("no. significant downregulated sets using the separate test:")
## no. significant downregulated sets using the separate test:
length(fres_dn_bg)
## [1] 128
message("no. significant sets using the separate tests:")
## no. significant sets using the separate tests:
length(unique(c(fres_up_bg,fres_dn_bg)))
## [1] 351
v1 <- list("up"=fres_up_bg,"dn"=fres_dn_bg,"combined"=fres_updn_bg)
plot(euler(v1),quantities = list(cex = 2), labels = list(cex = 2))
unlink("img/6m. png")
png("img/6m.png", width = 480, height = 300)
plot(euler(v1),quantities = list(cex = 2), labels = list(cex = 2))
dev.off()
## png
## 2
unlink("img/6m. pdf")
pdf("img/6m.pdf", width = 5, height = 4)
plot(euler(v1),quantities = list(cex = 2), labels = list(cex = 2))
dev.off()
## png
## 2
Here, the idea is to show that the gene set database being used makes a huge difference to the quality of the results. In particular, the more comprehensive the gene sets, the more comprehensive the pathway enrichment will be. This corresponds to Table 3.
msdb <- gmtPathways("ref/msigdb/msigdb.v2025.1.Hs.symbols.gmt")
kegg <- msdb[grep("KEGG",names(msdb))]
kegg_med <- msdb[grep("KEGG_MED",names(msdb))]
kegg <- kegg[grep("KEGG_MED",names(kegg),invert=TRUE)] #separate KEGG LEG from KEGG MED
gobp <- msdb[grep("GOBP",names(msdb))]
reactome <- msdb[grep("REACTOME",names(msdb))]
gslist <- list("KEGG"=kegg,"KEGGM"=kegg_med,"Reactome"=reactome,"GOBP"=gobp,"MSigDB"=msdb)
# no. gene sets
d1 <- unlist( lapply(gslist,length) )
# total number of annotations
d2 <- unlist( lapply(gslist,function(i) { length(unlist(i)) } ) )
# median gene set size
d3 <- unlist(lapply(gslist,function(i) { median(unlist(lapply(i,length))) } ))
# number of genes with one or more functions
d4 <- unlist( lapply(gslist,function(i) { length(unique(unlist(i))) } ))
kegg_up <- fora(pathways=kegg, genes=up, universe=bg, minSize = 5, maxSize = Inf)
kegg_dn <- fora(pathways=kegg, genes=dn, universe=bg, minSize = 5, maxSize = Inf)
message("no. significantly upregulated KEGG pathways")
## no. significantly upregulated KEGG pathways
nrow(subset(kegg_up,padj<0.05))
## [1] 11
message("no. significantly downregulated KEGG pathways")
## no. significantly downregulated KEGG pathways
nrow(subset(kegg_dn,padj<0.05))
## [1] 51
message("no. significantly dysregulated KEGG pathways")
## no. significantly dysregulated KEGG pathways
sum(nrow(subset(kegg_up,padj<0.05)), nrow(subset(kegg_dn,padj<0.05) ))
## [1] 62
kegg_med_up <- fora(pathways=kegg_med, genes=up, universe=bg, minSize = 5, maxSize = Inf)
kegg_med_dn <- fora(pathways=kegg_med, genes=dn, universe=bg, minSize = 5, maxSize = Inf)
message("no. significantly upregulated KEGG medicus pathways")
## no. significantly upregulated KEGG medicus pathways
nrow(subset(kegg_med_up,padj<0.05))
## [1] 20
message("no. significantly downregulated KEGG medicus pathways")
## no. significantly downregulated KEGG medicus pathways
nrow(subset(kegg_med_dn,padj<0.05))
## [1] 18
message("no. significantly dysregulated KEGG medicus pathways")
## no. significantly dysregulated KEGG medicus pathways
sum(nrow(subset(kegg_med_up,padj<0.05)), nrow(subset(kegg_med_dn,padj<0.05) ))
## [1] 38
reactome_up <- fora(pathways=reactome, genes=up, universe=bg, minSize = 5, maxSize = Inf)
reactome_dn <- fora(pathways=reactome, genes=dn, universe=bg, minSize = 5, maxSize = Inf)
message("no. significantly upregulated Reactome pathways")
## no. significantly upregulated Reactome pathways
nrow(subset(reactome_up,padj<0.05))
## [1] 165
message("no. significantly downregulated Reactome pathways")
## no. significantly downregulated Reactome pathways
nrow(subset(reactome_dn,padj<0.05))
## [1] 117
message("no. significantly dysregulated Reactome pathways")
## no. significantly dysregulated Reactome pathways
sum(nrow(subset(reactome_up,padj<0.05)), nrow(subset(reactome_dn,padj<0.05) ))
## [1] 282
gobp_up <- fora(pathways=gobp, genes=up, universe=bg, minSize = 5, maxSize = Inf)
gobp_dn <- fora(pathways=gobp, genes=dn, universe=bg, minSize = 5, maxSize = Inf)
message("no. significantly upregulated GO BP terms")
## no. significantly upregulated GO BP terms
nrow(subset(gobp_up,padj<0.05))
## [1] 340
message("no. significantly upregulated GO BP terms")
## no. significantly upregulated GO BP terms
nrow(subset(gobp_dn,padj<0.05))
## [1] 1416
message("no. significantly dysregulated GO BP terms")
## no. significantly dysregulated GO BP terms
sum(nrow(subset(gobp_up,padj<0.05)), nrow(subset(gobp_dn,padj<0.05) ))
## [1] 1756
msdb_up <- fora(pathways=msdb, genes=up, universe=bg, minSize = 5, maxSize = Inf)
msdb_dn <- fora(pathways=msdb, genes=dn, universe=bg, minSize = 5, maxSize = Inf)
message("no. significantly upregulated MSigDB gene sets")
## no. significantly upregulated MSigDB gene sets
nrow(subset(msdb_up,padj<0.05))
## [1] 3214
message("no. significantly downregulated MSigDB gene sets")
## no. significantly downregulated MSigDB gene sets
nrow(subset(msdb_dn,padj<0.05))
## [1] 7217
message("no. significantly dysregulated MSigDB gene sets")
## no. significantly dysregulated MSigDB gene sets
sum(nrow(subset(msdb_up,padj<0.05)), nrow(subset(msdb_dn,padj<0.05) ))
## [1] 10431
up_pw <- c(nrow(subset(kegg_up,padj<0.05)),nrow(subset(kegg_med_up,padj<0.05)),
nrow(subset(reactome_up,padj<0.05)), nrow(subset(gobp_up,padj<0.05)),
nrow(subset(msdb_up,padj<0.05)) )
dn_pw <- c(nrow(subset(kegg_dn,padj<0.05)), nrow(subset(kegg_med_dn,padj<0.05)),
nrow(subset(reactome_dn,padj<0.05)), nrow(subset(gobp_dn,padj<0.05)),
nrow(subset(msdb_dn,padj<0.05)) )
m8tbl <- data.frame(d1,d2,d3,d4,up_pw, dn_pw)
colnames(m8tbl) <- c("No. gene sets","Total no. annotations","Median gene set size",
"No. genes with ≥1 annotation","Up-regulated","Down-regulated")
m8tbl %>%
kbl(caption="Table XX. Gene set metrics, and number of significantly differentially regulated pathways (FDR<0.05).") %>%
kable_paper("hover", full_width = F)
| No. gene sets | Total no. annotations | Median gene set size | No. genes with ≥1 annotation | Up-regulated | Down-regulated | |
|---|---|---|---|---|---|---|
| KEGG | 186 | 12800 | 52.5 | 5245 | 11 | 51 |
| KEGGM | 658 | 9662 | 11.5 | 2788 | 20 | 18 |
| Reactome | 1787 | 97544 | 23.0 | 11369 | 165 | 117 |
| GOBP | 7583 | 616242 | 20.0 | 18000 | 340 | 1416 |
| MSigDB | 35134 | 4089406 | 47.0 | 43351 | 3214 | 7217 |
saveRDS(m8tbl,"m8tbl.Rds")
Here, we want to show how the depth of the gene set library increases over time. To achieve this, I downloaded all the Reactome gene sets that were available in MSigDB going back to 2010. I also included the most recent gene set library from Reactome directly - the one we used above. There are a few ways to show the gene set library size including number of gene sets, mean set size, total number of annotations, and number of unique genes included. The charts below show that the number of gene sets increases over time, and so does the total number of annotations, and the gene coverage. For simplicity, in the article we just show the number of gene sets. This corresponds to Figure 6.
par(mar=c(5.1,5.1,4.1,2.1))
reactome_files <- list.files("ref/msigdb/reactome/",full.names=TRUE)
reactome_files <- reactome_files[c(6:20,1:5)]
reactome_files <- c(reactome_files,"ref/ReactomePathways_2025-09-02.gmt")
reactomes <- lapply(reactome_files,gmtPathways)
names(reactomes) <- c("2010_09","2012_09","2013_05","2015_04","2016_01","2016_09","2017_04",
"2017_10","2018_07","2019_08","2020_03","2020_09","2021_03","2021_04","2022_01","2022_09",
"2023_03","2023_10","2024_08","2025_06","2025_09")
nogs <- unlist(lapply(reactomes,length))
nogs
## 2010_09 2012_09 2013_05 2015_04 2016_01 2016_09 2017_04 2017_10 2018_07 2019_08
## 430 674 674 674 674 674 674 674 674 1499
## 2020_03 2020_09 2021_03 2021_04 2022_01 2022_09 2023_03 2023_10 2024_08 2025_06
## 1532 1554 1569 1604 1615 1635 1654 1692 1736 1787
## 2025_09
## 2785
# remove sets with 4 or fewer genes
reactomes_filt <- lapply(reactomes,function(gs) {
l <- lapply(gs,length)
gs[which(l>=5)]
})
nogs <- unlist(lapply(reactomes_filt,length))
nogs
## 2010_09 2012_09 2013_05 2015_04 2016_01 2016_09 2017_04 2017_10 2018_07 2019_08
## 430 674 674 674 674 674 674 674 674 1499
## 2020_03 2020_09 2021_03 2021_04 2022_01 2022_09 2023_03 2023_10 2024_08 2025_06
## 1532 1554 1569 1604 1615 1635 1654 1692 1736 1787
## 2025_09
## 2159
barplot(nogs,las=2,ylim=c(0,2500),ylab="no. gene sets",main="Reactome gene sets over time",
border=NA)
lapply(seq(0,2500,500),function(h) { abline(h=h,lty=2,col="gray") })
## [[1]]
## NULL
##
## [[2]]
## NULL
##
## [[3]]
## NULL
##
## [[4]]
## NULL
##
## [[5]]
## NULL
##
## [[6]]
## NULL
unlink("img/9m.png")
png("img/9m.png", width = 480, height = 300)
barplot(nogs,las=2,ylim=c(0,2500),ylab="no. gene sets",main="Reactome gene sets over time",
border=NA,col="darkgray")
lapply(seq(0,2500,500),function(h) { abline(h=h,lty=2,col="lightgray") })
## [[1]]
## NULL
##
## [[2]]
## NULL
##
## [[3]]
## NULL
##
## [[4]]
## NULL
##
## [[5]]
## NULL
##
## [[6]]
## NULL
dev.off()
## png
## 2
unlink("img/9m.pdf")
pdf("img/9m.pdf", width = 5, height = 4)
barplot(nogs,las=2,ylim=c(0,2500),ylab="no. gene sets",main="Reactome gene sets over time",
border=NA,col="darkgray")
lapply(seq(0,2500,500),function(h) { abline(h=h,lty=2,col="lightgray") })
## [[1]]
## NULL
##
## [[2]]
## NULL
##
## [[3]]
## NULL
##
## [[4]]
## NULL
##
## [[5]]
## NULL
##
## [[6]]
## NULL
dev.off()
## png
## 2
reactomes <- reactomes_filt
tot_anno <- unlist(lapply(reactomes,function(gs) {
length(unlist(gs))
}))
tot_anno
## 2010_09 2012_09 2013_05 2015_04 2016_01 2016_09 2017_04 2017_10 2018_07 2019_08
## 21362 37601 37601 37601 37601 37601 37601 37601 37601 85647
## 2020_03 2020_09 2021_03 2021_04 2022_01 2022_09 2023_03 2023_10 2024_08 2025_06
## 87352 88301 86752 89457 89476 92250 92780 95226 97590 97544
## 2025_09
## 134094
barplot(tot_anno,las=2,ylim=c(0,140000),main="Total no. annotations")
gene_coverage <- unlist(lapply(reactomes,function(gs) {
length(unique(unlist(gs)))
}))
gene_coverage
## 2010_09 2012_09 2013_05 2015_04 2016_01 2016_09 2017_04 2017_10 2018_07 2019_08
## 4159 6025 6025 6025 6025 6025 6025 6025 6025 10718
## 2020_03 2020_09 2021_03 2021_04 2022_01 2022_09 2023_03 2023_10 2024_08 2025_06
## 10807 10821 10826 10968 10646 11013 11065 11155 11290 11369
## 2025_09
## 11886
barplot(gene_coverage,las=2,ylim=c(0,12000),ylab="gene coverage")
For reproducibility
save.image("analysis.Rdata")
sessionInfo()
## R version 4.5.1 (2025-06-13)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 24.04.2 LTS
##
## Matrix products: default
## BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
## LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.26.so; LAPACK version 3.12.0
##
## locale:
## [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
## [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
## [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
## [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
## [9] LC_ADDRESS=C LC_TELEPHONE=C
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
##
## time zone: Etc/UTC
## tzcode source: system (glibc)
##
## attached base packages:
## [1] parallel stats4 stats graphics grDevices utils datasets
## [8] methods base
##
## other attached packages:
## [1] vioplot_0.5.1 zoo_1.8-14
## [3] sm_2.2-6.0 beeswarm_0.4.0
## [5] RhpcBLASctl_0.23-42 fgsea_1.34.2
## [7] mitch_1.20.0 gplots_3.2.0
## [9] eulerr_7.0.4 kableExtra_1.4.0
## [11] DESeq2_1.48.2 SummarizedExperiment_1.38.1
## [13] Biobase_2.68.0 MatrixGenerics_1.20.0
## [15] matrixStats_1.5.0 GenomicRanges_1.60.0
## [17] GenomeInfoDb_1.44.3 IRanges_2.42.0
## [19] S4Vectors_0.46.0 BiocGenerics_0.54.1
## [21] generics_0.1.4 getDEE2_1.18.0
##
## loaded via a namespace (and not attached):
## [1] bitops_1.0-9 gridExtra_2.3 echarts4r_0.4.6
## [4] rlang_1.1.6 magrittr_2.0.3 compiler_4.5.1
## [7] polylabelr_0.3.0 systemfonts_1.3.1 vctrs_0.6.5
## [10] reshape2_1.4.4 htm2txt_2.2.2 stringr_1.5.1
## [13] pkgconfig_2.0.3 crayon_1.5.3 fastmap_1.2.0
## [16] XVector_0.48.0 caTools_1.18.3 promises_1.3.3
## [19] rmarkdown_2.29 UCSC.utils_1.4.0 network_1.19.0
## [22] purrr_1.1.0 xfun_0.53 cachem_1.1.0
## [25] jsonlite_2.0.0 later_1.4.4 DelayedArray_0.34.1
## [28] BiocParallel_1.42.2 R6_2.6.1 bslib_0.9.0
## [31] stringi_1.8.7 RColorBrewer_1.1-3 GGally_2.4.0
## [34] jquerylib_0.1.4 Rcpp_1.1.0 knitr_1.50
## [37] httpuv_1.6.16 Matrix_1.7-4 tidyselect_1.2.1
## [40] rstudioapi_0.17.1 abind_1.4-8 yaml_2.3.10
## [43] codetools_0.2-20 lattice_0.22-7 tibble_3.3.0
## [46] plyr_1.8.9 shiny_1.11.1 S7_0.2.0
## [49] coda_0.19-4.1 evaluate_1.0.5 polyclip_1.10-7
## [52] ggstats_0.11.0 xml2_1.4.0 pillar_1.11.0
## [55] KernSmooth_2.23-26 ggplot2_4.0.0 scales_1.4.0
## [58] gtools_3.9.5 xtable_1.8-4 glue_1.8.0
## [61] tools_4.5.1 data.table_1.17.8 locfit_1.5-9.12
## [64] fastmatch_1.1-6 cowplot_1.2.0 grid_4.5.1
## [67] tidyr_1.3.1 GenomeInfoDbData_1.2.14 cli_3.6.5
## [70] textshaping_1.0.1 S4Arrays_1.8.1 viridisLite_0.4.2
## [73] svglite_2.2.2 dplyr_1.1.4 gtable_0.3.6
## [76] sass_0.4.10 digest_0.6.37 SparseArray_1.8.1
## [79] htmlwidgets_1.6.4 farver_2.1.2 htmltools_0.5.8.1
## [82] lifecycle_1.0.4 httr_1.4.7 statnet.common_4.12.0
## [85] mime_0.13 MASS_7.3-65