We start by loading our results from the previous week

library(DESeq2)
Loading required package: S4Vectors
Loading required package: stats4
Loading required package: BiocGenerics

Attaching package: ‘BiocGenerics’

The following objects are masked from ‘package:stats’:

    IQR, mad, sd, var, xtabs

The following objects are masked from ‘package:base’:

    anyDuplicated, aperm, append, as.data.frame, basename, cbind,
    colnames, dirname, do.call, duplicated, eval, evalq, Filter, Find,
    get, grep, grepl, intersect, is.unsorted, lapply, Map, mapply,
    match, mget, order, paste, pmax, pmax.int, pmin, pmin.int,
    Position, rank, rbind, Reduce, rownames, sapply, setdiff, table,
    tapply, union, unique, unsplit, which.max, which.min


Attaching package: ‘S4Vectors’

The following object is masked from ‘package:utils’:

    findMatches

The following objects are masked from ‘package:base’:

    expand.grid, I, unname

Loading required package: IRanges
Loading required package: GenomicRanges
Loading required package: GenomeInfoDb
Loading required package: SummarizedExperiment
Loading required package: MatrixGenerics
Loading required package: matrixStats

Attaching package: ‘MatrixGenerics’

The following objects are masked from ‘package:matrixStats’:

    colAlls, colAnyNAs, colAnys, colAvgsPerRowSet, colCollapse,
    colCounts, colCummaxs, colCummins, colCumprods, colCumsums,
    colDiffs, colIQRDiffs, colIQRs, colLogSumExps, colMadDiffs,
    colMads, colMaxs, colMeans2, colMedians, colMins, colOrderStats,
    colProds, colQuantiles, colRanges, colRanks, colSdDiffs, colSds,
    colSums2, colTabulates, colVarDiffs, colVars, colWeightedMads,
    colWeightedMeans, colWeightedMedians, colWeightedSds,
    colWeightedVars, rowAlls, rowAnyNAs, rowAnys, rowAvgsPerColSet,
    rowCollapse, rowCounts, rowCummaxs, rowCummins, rowCumprods,
    rowCumsums, rowDiffs, rowIQRDiffs, rowIQRs, rowLogSumExps,
    rowMadDiffs, rowMads, rowMaxs, rowMeans2, rowMedians, rowMins,
    rowOrderStats, rowProds, rowQuantiles, rowRanges, rowRanks,
    rowSdDiffs, rowSds, rowSums2, rowTabulates, rowVarDiffs, rowVars,
    rowWeightedMads, rowWeightedMeans, rowWeightedMedians,
    rowWeightedSds, rowWeightedVars

Loading required package: Biobase
Welcome to Bioconductor

    Vignettes contain introductory material; view with
    'browseVignettes()'. To cite Bioconductor, see
    'citation("Biobase")', and for packages 'citation("pkgname")'.


Attaching package: ‘Biobase’

The following object is masked from ‘package:MatrixGenerics’:

    rowMedians

The following objects are masked from ‘package:matrixStats’:

    anyMissing, rowMedians
library(dplyr)

Attaching package: ‘dplyr’

The following object is masked from ‘package:Biobase’:

    combine

The following object is masked from ‘package:matrixStats’:

    count

The following objects are masked from ‘package:GenomicRanges’:

    intersect, setdiff, union

The following object is masked from ‘package:GenomeInfoDb’:

    intersect

The following objects are masked from ‘package:IRanges’:

    collapse, desc, intersect, setdiff, slice, union

The following objects are masked from ‘package:S4Vectors’:

    first, intersect, rename, setdiff, setequal, union

The following objects are masked from ‘package:BiocGenerics’:

    combine, intersect, setdiff, union

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
## Read the annotated results
results_tgf <- readRDS("Robjects/results_TGF_vs_CTR_annotated_BACKUP.rds")
## Read the counts that we produced previously
dds <- readRDS("Robjects/dds_BACKUP.rds")

Exercise: Volcano plot

The standard volcano plot can be produced with the ggplot2 package


library(ggplot2)
results_tgf %>% 
  ggplot(aes(x = log2FoldChange, y = -log10(padj))) + geom_point()

library(org.Hs.eg.db)
Loading required package: AnnotationDbi

Attaching package: ‘AnnotationDbi’

The following object is masked from ‘package:dplyr’:

    select
ecm_anno <- AnnotationDbi::select(org.Hs.eg.db,
                                 keys = "GO:0030198",
                                 keytype = "GO",
                                 columns=c("ENSEMBL","SYMBOL"))
'select()' returned 1:many mapping between keys and columns
ecm_anno
ecm_ids <- pull(ecm_anno, "ENSEMBL")
results_tgf %>% 
  mutate(ECM_Gene = ENSEMBL %in% ecm_ids) %>% 
  ggplot(aes(x = log2FoldChange, y = -log10(padj),col=ECM_Gene)) + geom_point()

The default colouring does not distinguish the ECM genes as much as we might like. We could manually adjust it so that genes belonging to the pathway are coloured in red, and the rest in black. The transparency (alpha) can also be adjusted. Below is a suggestion.

results_tgf %>% 
  mutate(ECM_Gene = ENSEMBL %in% ecm_ids) %>% 
  ggplot(aes(x = log2FoldChange, y = -log10(padj),col=ECM_Gene,alpha=ECM_Gene)) + geom_point() + scale_color_manual(values=c("black","red")) + scale_alpha_manual(values=c(0.2,1))

Exercise: Heatmap

The tricky part of making a heatmap is deciding which genes to include. The gene names have to be in ENSEMBL format, as the count matrix we will be plotting has ENSEMBL names in the rows.

In the following example, we chose the genes with most extreme log2 fold-change

# The pheatmap library is needed to make the heatmap

library(pheatmap)

#first, get variance-stablised counts

vsd <- vst(dds)
using 'avgTxLength' from assays(dds), correcting for library size
sampleInfo <- as.data.frame(colData(dds)[,c("condition","Treated")])

## Order results by log2FoldChange and get the most extreme

top_genes_logFC <- results_tgf %>% 
  arrange(desc(abs(log2FoldChange))) %>% 
  slice(1:30) %>% 
  pull(ENSEMBL)

top_genes_logFC_labels <- results_tgf %>% 
  arrange(desc(abs(log2FoldChange))) %>% 
  slice(1:30) %>% 
  pull(SYMBOL)

## get the corresponding geen symbols

pheatmap(assay(vsd)[top_genes_logFC,],
         scale = "row",
         labels_row = top_genes_logFC_labels)

Exercise: Pathways

In order to use the enrichKEGG function, we will have to define a set of enriched genes in terms of their ENTREZID (instead of ENSEMBL)

sigGenesEntrez <- results_tgf %>% 
  filter(padj < 0.05, !is.na(ENTREZID)) %>% pull(ENTREZID)

Make sure you check the help for enrichKEGG, as it uses different argument names

library(clusterProfiler)

Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
clusterProfiler v4.12.6 Learn more at https://yulab-smu.top/contribution-knowledge-mining/

Please cite:

S Xu, E Hu, Y Cai, Z Xie, X Luo, L Zhan, W Tang, Q Wang, B Liu, R Wang,
W Xie, T Wu, L Xie, G Yu. Using clusterProfiler to characterize
multiomics data. Nature Protocols. 2024, doi:10.1038/s41596-024-01020-z

Attaching package: ‘clusterProfiler’

The following object is masked from ‘package:AnnotationDbi’:

    select

The following object is masked from ‘package:IRanges’:

    slice

The following object is masked from ‘package:S4Vectors’:

    rename

The following object is masked from ‘package:stats’:

    filter
enrich_kegg <- enrichKEGG(gene = sigGenesEntrez,
                       organism = "hsa",)
Reading KEGG annotation online: "https://rest.kegg.jp/link/hsa/pathway"...
Reading KEGG annotation online: "https://rest.kegg.jp/list/pathway/hsa"...
as.data.frame(enrich_kegg)

The same plots can be created from the results. Here is the dot plot.

dotplot(enrich_kegg)

and the upset plot.

enrichplot::upsetplot(enrich_kegg)

For the GSEA analysis using KEGG we can use the same set of ranked statistics, but make sure to name them according to ENTREZID

ranked_genes <- results_tgf %>% 
  arrange(desc(stat)) %>% 
  filter(!is.na(stat))
  
geneList <- pull(ranked_genes, stat)
names(geneList) <- pull(ranked_genes, ENTREZID)

Again, make sure to check the arguments for gseKEGG.

gse_kegg <- gseKEGG(geneList = geneList,
                    organism = "hsa")
using 'fgsea' for GSEA analysis, please cite Korotkevich et al (2019).

preparing geneSet collections...
GSEA analysis...
Warning: There are ties in the preranked stats (11.21% of the list).
The order of those tied genes will be arbitrary, which may produce unexpected results.Warning: There are duplicate gene names, fgsea may produce unexpected results.Warning: For some pathways, in reality P-values are less than 1e-10. You can set the `eps` argument to zero for better estimation.leading edge analysis...
done...
as.data.frame(gse_kegg)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKV2Ugc3RhcnQgYnkgbG9hZGluZyBvdXIgcmVzdWx0cyBmcm9tIHRoZSBwcmV2aW91cyB3ZWVrCgpgYGB7cn0KbGlicmFyeShERVNlcTIpCmxpYnJhcnkoZHBseXIpCiMjIFJlYWQgdGhlIGFubm90YXRlZCByZXN1bHRzCnJlc3VsdHNfdGdmIDwtIHJlYWRSRFMoIlJvYmplY3RzL3Jlc3VsdHNfVEdGX3ZzX0NUUl9hbm5vdGF0ZWRfQkFDS1VQLnJkcyIpCiMjIFJlYWQgdGhlIGNvdW50cyB0aGF0IHdlIHByb2R1Y2VkIHByZXZpb3VzbHkKZGRzIDwtIHJlYWRSRFMoIlJvYmplY3RzL2Rkc19CQUNLVVAucmRzIikKYGBgCgojIEV4ZXJjaXNlOiBWb2xjYW5vIHBsb3QKClRoZSBzdGFuZGFyZCB2b2xjYW5vIHBsb3QgY2FuIGJlIHByb2R1Y2VkIHdpdGggdGhlIGBnZ3Bsb3QyYCBwYWNrYWdlCgpgYGB7cn0KCmxpYnJhcnkoZ2dwbG90MikKcmVzdWx0c190Z2YgJT4lIAogIGdncGxvdChhZXMoeCA9IGxvZzJGb2xkQ2hhbmdlLCB5ID0gLWxvZzEwKHBhZGopKSkgKyBnZW9tX3BvaW50KCkKYGBgCgpgYGB7cn0KbGlicmFyeShvcmcuSHMuZWcuZGIpCmVjbV9hbm5vIDwtIEFubm90YXRpb25EYmk6OnNlbGVjdChvcmcuSHMuZWcuZGIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtleXMgPSAiR086MDAzMDE5OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtleXR5cGUgPSAiR08iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5zPWMoIkVOU0VNQkwiLCJTWU1CT0wiKSkKZWNtX2Fubm8KYGBgCgpgYGB7cn0KZWNtX2lkcyA8LSBwdWxsKGVjbV9hbm5vLCAiRU5TRU1CTCIpCmBgYAoKYGBge3J9CnJlc3VsdHNfdGdmICU+JSAKICBtdXRhdGUoRUNNX0dlbmUgPSBFTlNFTUJMICVpbiUgZWNtX2lkcykgJT4lIAogIGdncGxvdChhZXMoeCA9IGxvZzJGb2xkQ2hhbmdlLCB5ID0gLWxvZzEwKHBhZGopLGNvbD1FQ01fR2VuZSkpICsgZ2VvbV9wb2ludCgpCmBgYApUaGUgZGVmYXVsdCBjb2xvdXJpbmcgZG9lcyBub3QgZGlzdGluZ3Vpc2ggdGhlIEVDTSBnZW5lcyBhcyBtdWNoIGFzIHdlIG1pZ2h0IGxpa2UuIFdlIGNvdWxkIG1hbnVhbGx5IGFkanVzdCBpdCBzbyB0aGF0IGdlbmVzIGJlbG9uZ2luZyB0byB0aGUgcGF0aHdheSBhcmUgY29sb3VyZWQgaW4gcmVkLCBhbmQgdGhlIHJlc3QgaW4gYmxhY2suIFRoZSB0cmFuc3BhcmVuY3kgKGBhbHBoYWApIGNhbiBhbHNvIGJlIGFkanVzdGVkLiBCZWxvdyBpcyBhIHN1Z2dlc3Rpb24uCgpgYGB7cn0KcmVzdWx0c190Z2YgJT4lIAogIG11dGF0ZShFQ01fR2VuZSA9IEVOU0VNQkwgJWluJSBlY21faWRzKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gbG9nMkZvbGRDaGFuZ2UsIHkgPSAtbG9nMTAocGFkaiksY29sPUVDTV9HZW5lLGFscGhhPUVDTV9HZW5lKSkgKyBnZW9tX3BvaW50KCkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImJsYWNrIiwicmVkIikpICsgc2NhbGVfYWxwaGFfbWFudWFsKHZhbHVlcz1jKDAuMiwxKSkKYGBgCiMgRXhlcmNpc2U6IEhlYXRtYXAKClRoZSB0cmlja3kgcGFydCBvZiBtYWtpbmcgYSBoZWF0bWFwIGlzIGRlY2lkaW5nIHdoaWNoIGdlbmVzIHRvIGluY2x1ZGUuIFRoZSBnZW5lIG5hbWVzIGhhdmUgdG8gYmUgaW4gRU5TRU1CTCBmb3JtYXQsIGFzIHRoZSBjb3VudCBtYXRyaXggd2Ugd2lsbCBiZSBwbG90dGluZyBoYXMgRU5TRU1CTCBuYW1lcyBpbiB0aGUgcm93cy4KCkluIHRoZSBmb2xsb3dpbmcgZXhhbXBsZSwgd2UgY2hvc2UgdGhlIGdlbmVzIHdpdGggbW9zdCBleHRyZW1lIGxvZzIgZm9sZC1jaGFuZ2UKCmBgYHtyfQojIFRoZSBwaGVhdG1hcCBsaWJyYXJ5IGlzIG5lZWRlZCB0byBtYWtlIHRoZSBoZWF0bWFwCgpsaWJyYXJ5KHBoZWF0bWFwKQoKI2ZpcnN0LCBnZXQgdmFyaWFuY2Utc3RhYmxpc2VkIGNvdW50cwoKdnNkIDwtIHZzdChkZHMpCnNhbXBsZUluZm8gPC0gYXMuZGF0YS5mcmFtZShjb2xEYXRhKGRkcylbLGMoImNvbmRpdGlvbiIsIlRyZWF0ZWQiKV0pCgojIyBPcmRlciByZXN1bHRzIGJ5IGxvZzJGb2xkQ2hhbmdlIGFuZCBnZXQgdGhlIG1vc3QgZXh0cmVtZQoKdG9wX2dlbmVzX2xvZ0ZDIDwtIHJlc3VsdHNfdGdmICU+JSAKICBhcnJhbmdlKGRlc2MoYWJzKGxvZzJGb2xkQ2hhbmdlKSkpICU+JSAKICBzbGljZSgxOjMwKSAlPiUgCiAgcHVsbChFTlNFTUJMKQoKdG9wX2dlbmVzX2xvZ0ZDX2xhYmVscyA8LSByZXN1bHRzX3RnZiAlPiUgCiAgYXJyYW5nZShkZXNjKGFicyhsb2cyRm9sZENoYW5nZSkpKSAlPiUgCiAgc2xpY2UoMTozMCkgJT4lIAogIHB1bGwoU1lNQk9MKQoKIyMgZ2V0IHRoZSBjb3JyZXNwb25kaW5nIGdlZW4gc3ltYm9scwoKcGhlYXRtYXAoYXNzYXkodnNkKVt0b3BfZ2VuZXNfbG9nRkMsXSwKICAgICAgICAgc2NhbGUgPSAicm93IiwKICAgICAgICAgbGFiZWxzX3JvdyA9IHRvcF9nZW5lc19sb2dGQ19sYWJlbHMpCgpgYGAKCgoKCgojIEV4ZXJjaXNlOiBQYXRod2F5cwoKCkluIG9yZGVyIHRvIHVzZSB0aGUgYGVucmljaEtFR0dgIGZ1bmN0aW9uLCB3ZSB3aWxsIGhhdmUgdG8gZGVmaW5lIGEgc2V0IG9mIGVucmljaGVkIGdlbmVzIGluIHRlcm1zIG9mIHRoZWlyIGBFTlRSRVpJRGAgKGluc3RlYWQgb2YgYEVOU0VNQkxgKQoKYGBge3J9CnNpZ0dlbmVzRW50cmV6IDwtIHJlc3VsdHNfdGdmICU+JSAKICBmaWx0ZXIocGFkaiA8IDAuMDUsICFpcy5uYShFTlRSRVpJRCkpICU+JSBwdWxsKEVOVFJFWklEKQoKYGBgCgpNYWtlIHN1cmUgeW91IGNoZWNrIHRoZSBoZWxwIGZvciBgZW5yaWNoS0VHR2AsIGFzIGl0IHVzZXMgZGlmZmVyZW50IGFyZ3VtZW50IG5hbWVzCgpgYGB7cn0KbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpCmVucmljaF9rZWdnIDwtIGVucmljaEtFR0coZ2VuZSA9IHNpZ0dlbmVzRW50cmV6LAogICAgICAgICAgICAgICAgICAgICAgIG9yZ2FuaXNtID0gImhzYSIsKQphcy5kYXRhLmZyYW1lKGVucmljaF9rZWdnKQpgYGAKVGhlIHNhbWUgcGxvdHMgY2FuIGJlIGNyZWF0ZWQgZnJvbSB0aGUgcmVzdWx0cy4gSGVyZSBpcyB0aGUgZG90IHBsb3QuCgpgYGB7ciBmaWcud2lkdGg9MTJ9CmRvdHBsb3QoZW5yaWNoX2tlZ2cpCmBgYAphbmQgdGhlIHVwc2V0IHBsb3QuCgpgYGB7ciBmaWcud2lkdGg9MTJ9CmVucmljaHBsb3Q6OnVwc2V0cGxvdChlbnJpY2hfa2VnZykKYGBgCgpGb3IgdGhlIEdTRUEgYW5hbHlzaXMgdXNpbmcgS0VHRyB3ZSBjYW4gdXNlIHRoZSBzYW1lIHNldCBvZiByYW5rZWQgc3RhdGlzdGljcywgYnV0IG1ha2Ugc3VyZSB0byBuYW1lIHRoZW0gYWNjb3JkaW5nIHRvIEVOVFJFWklECgpgYGB7cn0KcmFua2VkX2dlbmVzIDwtIHJlc3VsdHNfdGdmICU+JSAKICBhcnJhbmdlKGRlc2Moc3RhdCkpICU+JSAKICBmaWx0ZXIoIWlzLm5hKHN0YXQpKQogIApnZW5lTGlzdCA8LSBwdWxsKHJhbmtlZF9nZW5lcywgc3RhdCkKbmFtZXMoZ2VuZUxpc3QpIDwtIHB1bGwocmFua2VkX2dlbmVzLCBFTlRSRVpJRCkKYGBgCgpBZ2FpbiwgbWFrZSBzdXJlIHRvIGNoZWNrIHRoZSBhcmd1bWVudHMgZm9yIGBnc2VLRUdHYC4gCgpgYGB7cn0KZ3NlX2tlZ2cgPC0gZ3NlS0VHRyhnZW5lTGlzdCA9IGdlbmVMaXN0LAogICAgICAgICAgICAgICAgICAgIG9yZ2FuaXNtID0gImhzYSIpCmFzLmRhdGEuZnJhbWUoZ3NlX2tlZ2cpCmBgYAoKCg==