2D Expression PCAs colored by different features
Feature correlation plots
Quantification and quality-control pipeline
The quality of each sample was independently assessed using FastQC, Preseq, Picard tools, RSeQC, SAMtools, and QualiMap. FastQ Screen and Kraken + Krona were used to screen for various sources of contamination.
Adapter sequences were removed using Cutadapt prior to mapping to hg38 reference genome. STAR was run in two-pass mode where splice-junctions are collected, aggregated, and provided to the second-pass of STAR. Gene expression levels were quantified using RSEM. The expected counts from RSEM are merged across samples to create a counts matrix for downstream analysis. RSeQC tin.py was used to calculate transcript integrity numbers for all canonical protein-coding transcripts.
Downstream Analysis
The expected counts from RSEM were filtered to remove lowly expressed genes using edgeR's filterByExpr() function. The following criteria were selected for filtering: genes must have 10 reads in >= 70% samples. After filtering, we are left with 309 genes. Trimmed mean of M-values (TMM) was performed using the calcNormFactors() function in edgeR. The normalisation factors calculated here are used as a scaling factor for the library sizes. Using the voom() function in limma, the counts are converted log2-counts-per-million (logCPM) and quantile normalized.
voom is an acronym for mean-variance modelling at the observational level. The key concern is to estimate the mean-variance relationship in the data, then use this to compute appropriate weights for each observation. Count data almost show non-trivial mean-variance relationships. Raw counts show increasing variance with increasing count size, while log-counts typically show a decreasing mean-variance trend. This function estimates the mean-variance trend for log-counts, then assigns a weight to each observation based on its predicted variance. The weights are then used in the linear modeling process to adjust for heteroscedasticity.
General Recommendations
Here is a set of generalized guidelines for different QC metrics. Some of these metrics will vary genome-to-genome depending on the quality of the assembly and annotation but that has been taken into consideration for our set of supported reference genomes (human and mouse).
| Tool | Guideline | About |
|---|---|---|
| FastQC2 | NA | Quality-control step to assess sequencing quality, run before and after adapter trimming |
| Cutadapt3 | Trimmed Reads > 10000000 | Data processing step to remove adapter sequences and perform quality trimming |
| Kraken14 | NA | Quality-control step to assess microbial taxonomic composition |
| KronaTools15 | NA | Quality-control step to visualize kraken output |
| FastQ Screen17 | % rRNA < 10% | Quality-control step to assess contamination; additional dependencies: bowtie2, perl |
| STAR4 | % Aligned to Reference > 65% | Data processing step to align reads against reference genome |
| RSEM5 | NA | Data processing step to quantify gene and isoform counts |
| Preseq1 | NA | Quality-control step to estimate library complexity |
| SAMtools13 | NA | Quality-control step to run flagstat to calculate alignment statistics |
| QualiMap16 | NA | Quality-control step to assess various alignment metrics, also calculates insert_size |
| Picard10 | % Duplicates < 65 % | Quality-control step to run MarkDuplicates, CollectRnaSeqMetrics and AddOrReplaceReadGroups |
| RSeQC9 | % Coding > 35% | Quality-control step to infer stranded-ness and read distributions over different regions |
| RSeQC tin.py19 | medTIN > 65 | Quality-control step to calculate transcript integrity numbers for canonical protein coding transcripts |
| MultiQC11 | NA | Reporting step to aggregate sample statistics and quality-control information across all sample |
References
1. Daley, T. and A.D. Smith, Predicting the molecular complexity of sequencing libraries. Nat Methods, 2013. 10(4): p. 325-7. 2. Andrews, S. (2010). FastQC: a quality control tool for high throughput sequence data. 3. Martin, M. (2011). "Cutadapt removes adapter sequences from high-throughput sequencing reads." EMBnet 17(1): 10-12. 4. Dobin, A., et al., STAR: ultrafast universal RNA-seq aligner. Bioinformatics, 2013. 29(1): p. 15-21. 5. Li, B. and C.N. Dewey, RSEM: accurate transcript quantification from RNA-Seq data with or without a reference genome. BMC Bioinformatics, 2011. 12: p. 323. 6. Harrow, J., et al., GENCODE: the reference human genome annotation for The ENCODE Project. Genome Res, 2012. 22(9): p. 1760-74. 7. Law, C.W., et al., voom: Precision weights unlock linear model analysis tools for RNA-seq read counts. Genome Biol, 2014. 15(2): p. R29. 8. Smyth, G.K., Linear models and empirical bayes methods for assessing differential expression in microarray experiments. Stat Appl Genet Mol Biol, 2004. 3: p. Article3. 9. Wang, L., et al. (2012). "RSeQC: quality control of RNA-seq experiments." Bioinformatics 28(16): 2184-2185. 10. The Picard toolkit. https://broadinstitute.github.io/picard/. 11. Ewels, P., et al. (2016). "MultiQC: summarize analysis results for multiple tools and samples in a single report." Bioinformatics 32(19): 3047-3048. 12. R Core Team (2018). R: A Language and Environment for Statistical Computing. Vienna, Austria, R Foundation for Statistical Computing. 13. Li, H., et al. (2009). "The Sequence Alignment/Map format and SAMtools." Bioinformatics 25(16): 2078-2079. 14. Wood, D. E. and S. L. Salzberg (2014). "Kraken: ultrafast metagenomic sequence classification using exact alignments." Genome Biol 15(3): R46. 15. Ondov, B. D., et al. (2011). "Interactive metagenomic visualization in a Web browser." BMC Bioinformatics 12(1): 385. 16. Okonechnikov, K., et al. (2015). "Qualimap 2: advanced multi-sample quality control for high-throughput sequencing data." Bioinformatics 32(2): 292-294. 17. Wingett, S. and S. Andrews (2018). "FastQ Screen: A tool for multi-genome mapping and quality control." F1000Research 7(2): 1338. 18. Robinson, M. D., et al. (2009). "edgeR: a Bioconductor package for differential expression analysis of digital gene expression data." Bioinformatics 26(1): 139-140. 9. Wang, L., et al. (2012). "RSeQC: quality control of RNA-seq experiments." Bioinformatics 28(16): 2184-2185.
R version 3.6.3 (2020-02-29)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 18.04.5 LTS
Matrix products: default
BLAS: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.7.1
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.7.1
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
attached base packages:
[1] grid stats graphics grDevices utils datasets methods
[8] base
other attached packages:
[1] circlize_0.4.12 ComplexHeatmap_2.2.0 reshape2_1.4.4
[4] DT_0.17 crosstalk_1.1.1 gridExtra_2.3
[7] RColorBrewer_1.1-2 edgeR_3.28.1 limma_3.42.2
[10] plotly_4.9.3 ggplot2_3.3.3 plyr_1.8.6
[13] argparse_2.0.3
loaded via a namespace (and not attached):
[1] Rcpp_1.0.6 locfit_1.5-9.4 lattice_0.20-41
[4] tidyr_1.1.2 png_0.1-7 digest_0.6.27
[7] mime_0.9 R6_2.5.0 evaluate_0.14
[10] httr_1.4.2 pillar_1.4.7 GlobalOptions_0.1.2
[13] rlang_0.4.10 lazyeval_0.2.2 data.table_1.13.6
[16] GetoptLong_1.0.5 rmarkdown_2.6 labeling_0.4.2
[19] flexdashboard_0.5.2 stringr_1.4.0 htmlwidgets_1.5.3
[22] munsell_0.5.0 shiny_1.5.0 compiler_3.6.3
[25] httpuv_1.5.5 xfun_0.20 pkgconfig_2.0.3
[28] shape_1.4.5 findpython_1.0.5 htmltools_0.5.1
[31] tidyselect_1.1.0 tibble_3.0.5 viridisLite_0.3.0
[34] crayon_1.3.4 dplyr_1.0.3 withr_2.4.0
[37] later_1.1.0.1 jsonlite_1.7.2 xtable_1.8-4
[40] gtable_0.3.0 lifecycle_0.2.0 magrittr_2.0.1
[43] scales_1.1.1 stringi_1.5.3 farver_2.0.3
[46] viridis_0.5.1 promises_1.1.1 ellipsis_0.3.1
[49] generics_0.1.0 vctrs_0.3.6 rjson_0.2.20
[52] tools_3.6.3 glue_1.4.2 purrr_0.3.4
[55] parallel_3.6.3 fastmap_1.0.1 yaml_2.2.1
[58] clue_0.3-58 colorspace_2.0-0 cluster_2.1.0
[61] knitr_1.30
---
title: "RNA Report"
author: "Assess the quality of RNA-seq samples"
params:
raw: "/data/sevillas2/tmp/RSEM.genes.expected_counts.all_samples.reformatted.tsv"
tin: "/data/sevillas2/tmp/combined_TIN.tsv"
qc: "/data/sevillas2/tmp/multiqc_matrix.tsv"
annot: "FALSE"
output:
flexdashboard::flex_dashboard:
orientation: rows
navbar:
- { title: "Pipeline Documentation", href: "https://ccbr.github.io/pipeliner-docs/RNA-seq/Theory-and-practical-guide-for-RNA-seq/", align: right }
source_code: "embed"
editor_options:
chunk_output_type: console
---
```{r setup, include=FALSE}
# Clear R environment
# rm(list = ls())
set.seed(42)
```
```{r global, include=FALSE}
# Library imports
suppressMessages(library(plyr))
suppressMessages(library(plotly))
suppressMessages(library(ggplot2))
suppressMessages(library(limma))
suppressMessages(library(edgeR))
suppressMessages(library(RColorBrewer))
suppressMessages(library(gridExtra))
suppressMessages(library(crosstalk))
suppressMessages(library(DT))
suppressMessages(library(reshape2))
suppressMessages(library(ComplexHeatmap))
suppressMessages(library(circlize))
# Reading in raw counts matrix, TIN matrix, and QC metadata
rawcounts <- read.table(file = params$raw, sep = "\t", header = TRUE, row.names = 1, quote = "")
# rawcounts = read.table(file = 'data/Test_Raw_RSEM_Genes_Dataset.txt', sep = '\t', header = TRUE, row.names = 1)
tincounts <- read.table(file = params$tin, sep = "\t", header = TRUE, row.names = 1)
# tincounts = read.table(file = 'data/Test_TIN_Dataset.txt', sep = '\t', header = TRUE, row.names = 1)
# Remove zero variance rows prior to PC
tincounts <- tincounts[apply(tincounts, 1, var) != 0, ]
multiQC <- read.table(file = params$qc, sep = "\t", header = TRUE, stringsAsFactors = TRUE)
rownames(multiQC) <- make.names(multiQC$Sample)
# Create DGEList
deg <- edgeR::DGEList(counts = rawcounts)
# Filter lowly expressed genes
keep_genes <- edgeR::filterByExpr(deg) # Using default: Gene must have 10 reads in >= 70% samples
deg <- deg[keep_genes, , keep.lib.sizes = FALSE] # Recaluate new lib.sizes after filtering
# edgeR TMM normalization
deg <- calcNormFactors(deg, method = "TMM") # calculate scaling norm.factors
# limma voom normalization
deg_voom <- voom(deg, normalize = "quantile", plot = TRUE, save.plot = TRUE)
# Order genes by MAD
deg_voom$E <- deg_voom$E[order(apply(deg_voom$E, 1, mad), decreasing = T), ]
# Remove zero variance rows prior to PC
deg_voom$E <- deg_voom$E[apply(deg_voom$E, 1, var) != 0, ]
# Principal Components Analysis
PC_labels <- c("PC1_tin", "PC2_tin", "PC3_tin")
pca_max <- min(length(prcomp(t(as.matrix(deg_voom$E)), scale. = T)$sdev), 3)
if (pca_max < 2) {
stop("Fewer than 2 principle components were found")
}
pca_exp <- prcomp(t(as.matrix(deg_voom$E)), scale. = T)$x[, 1:pca_max] # Expression PC Analysis
pca_tin <- prcomp(t(as.matrix(tincounts)), scale. = T)$x[, 1:pca_max] # Transcript Integrity Number PC Analysis
colnames(pca_tin) <- PC_labels[1:pca_max] # Renaming PC cols to avoid collision with gene expression PCs
# Merge both dataframes on rowname
multiQC <- transform(merge(multiQC, as.data.frame(pca_exp), by = "row.names", all = TRUE), row.names = Row.names, Row.names = NULL)
multiQC <- transform(merge(multiQC, as.data.frame(pca_tin), by = "row.names", all = TRUE), row.names = Row.names, Row.names = NULL)
# Crosstalk object (inter-widget connectivity)
shared_metadata <- SharedData$new(multiQC)
```
Interactives {data-icon="ion-android-options"}
=====================================
Inputs {.sidebar}
-------------------------------------
### Filters
```{r filters}
# Flowcell Lanes
filter_select(
id = "flowcell_lanes",
label = "Flowcell Lanes",
sharedData = shared_metadata,
group = ~flowcell_lanes
)
# Sequence Ranges
filter_select(
id = "sequence_length",
label = "Sequence Ranges",
sharedData = shared_metadata,
group = ~sequence_length
)
# Median TIN
filter_slider(
id = "median_tin",
label = "medTIN",
sharedData = shared_metadata,
column = ~median_tin,
step = 5,
round = TRUE,
sep = "",
ticks = TRUE,
min = 0,
max = 100
)
# Trimmed Reads
filter_slider(
id = "trimmed_read_pairs",
label = "Trimmed Reads",
sharedData = shared_metadata,
column = ~trimmed_read_pairs,
step = 5000000,
round = TRUE,
sep = "",
ticks = TRUE,
min = 0
)
# % Duplicates
filter_slider(
id = "percent_duplication",
label = "% Duplicates",
sharedData = shared_metadata,
column = ~percent_duplication,
step = 5,
round = TRUE,
sep = "",
ticks = TRUE,
min = 0,
max = 100
)
# % Aligned
filter_slider(
id = "percent_aligned",
label = "% Aligned",
sharedData = shared_metadata,
column = ~percent_aligned,
step = 5,
round = TRUE,
sep = "",
ticks = TRUE,
min = 0,
max = 100
)
# % Coding
filter_slider(
id = "pct_coding_bases",
label = "% Coding",
sharedData = shared_metadata,
column = ~pct_coding_bases,
step = 5,
round = TRUE,
sep = "",
ticks = TRUE,
min = 0,
max = 100
)
# % Intronic
filter_slider(
id = "pct_intronic_bases",
label = "% Intronic",
sharedData = shared_metadata,
column = ~pct_intronic_bases,
step = 5,
sep = "",
ticks = TRUE,
min = 0,
max = 100
)
# % UTR
filter_slider(
id = "pct_utr_bases",
label = "% UTR",
sharedData = shared_metadata,
column = ~pct_utr_bases,
step = 5,
sep = "",
ticks = TRUE,
min = 0,
max = 100
)
# % rRNA
filter_slider(
id = "rRNA_percent_aligned",
label = "% rRNA",
sharedData = shared_metadata,
column = ~rRNA_percent_aligned,
step = 5,
sep = "",
round = TRUE,
ticks = TRUE,
min = 0,
max = 100
)
# GC content
filter_slider(
id = "gc_content",
label = "GC content",
sharedData = shared_metadata,
column = ~gc_content,
sep = "",
ticks = TRUE
)
# CV Coverage
filter_slider(
id = "median_cv_coverage",
label = "CV Coverage",
sharedData = shared_metadata,
column = ~median_cv_coverage,
sep = "",
ticks = TRUE
)
# Inner Distance Maxima
filter_slider(
id = "inner_distance_maxima",
label = "Inner Distance Maxima",
sharedData = shared_metadata,
column = ~inner_distance_maxima,
step = 10,
sep = "",
ticks = TRUE,
round = TRUE
)
# Insert Size
filter_slider(
id = "median_insert_size",
label = "Insert Size",
sharedData = shared_metadata,
column = ~median_insert_size,
step = 5,
sep = "",
ticks = TRUE
)
```
Row {data-height=400}
-------------------------------------
### Metadata
```{r datatable}
shared_metadata %>%
DT::datatable(
# selection = 'none', # disable datatable row selection
# filter = "top", # allows filtering on each column
extensions = c(
"Buttons", # add download buttons
"Scroller" # for scrolling instead of pagination
),
rownames = FALSE, # remove rownames
style = "bootstrap",
class = "compact",
width = "100%",
options = list(
dom = "Blrtip", # specify content (search box, etc)
deferRender = TRUE,
scrollY = 300,
scroller = TRUE,
columnDefs = list(
list(
visible = FALSE,
targets = c(1, 11, 14, 15, 18, 19, 21:23, 27, 30, 31, 32, 33, 34, 35) # hide columes
)
),
buttons = list(
I("colvis"), # turn columns on and off
"csv", # download as .csv
"excel" # download as .xlsx
)
),
colnames = c(
"Sample ID" = "Sample",
"Total Reads" = "total_read_pairs",
"Trimmed Reads" = "trimmed_read_pairs",
"Avg Seq Length" = "avg_sequence_length",
"Seq Range" = "sequence_length",
"GC" = "gc_content",
"% Dup" = "percent_duplication",
"% Aligned " = "percent_aligned",
"Inner Distance Maxima" = "inner_distance_maxima",
"Insert Size " = "median_insert_size",
"Avg MapQ" = "mean_mapping_quality",
"Coverage" = "mean_coverage",
"% Coding" = "pct_coding_bases",
"% UTR" = "pct_utr_bases",
"% Intronic" = "pct_intronic_bases",
"CV Coverage" = "median_cv_coverage",
"% rRNA" = "rRNA_percent_aligned",
"% UniVec" = "uni_vec_percent_aligned",
"% Anti-sense" = "percent_antisense_strand",
"medTIN" = "median_tin",
"Flowcell Lanes" = "flowcell_lanes"
)
)
```
Row {data-height=600}
-------------------------------------
```{r prep-plotly, echo=FALSE, include=FALSE}
# Variance explained for PCs: 1, 2, 3
define_pcs <- function(pca_in){
pc1 <<- round(pca_in$sdev[1]^2 / sum(pca_in$sdev^2) * 100, 2)
pc2 <<- round(pca_in$sdev[2]^2 / sum(pca_in$sdev^2) * 100, 2)
pc3 <<- round(pca_in$sdev[3]^2 / sum(pca_in$sdev^2) * 100, 2)
}
# set axis list
define_axis<-function(){
axis_list<<-list(
xaxis = list(title = paste0("PC1 (", pc1, "%)")),
yaxis = list(title = paste0("PC2 (", pc2, "%)")),
zaxis = list(title = paste0("PC3 (", pc3, "%)")))[1:pca_max]
}
# set group info
cgroups <- as.factor(multiQC$flowcell_lanes)
cgroups <- addNA(cgroups)
cpalette <- brewer.pal(nlevels(cgroups), "Paired")
```
```{r plotly-function, include=FALSE, echo=FALSE}
# plot expression
plot_pca_data <- function(dat, pca_max) {
if (pca_max > 3 | pca_max < 2) {
stop(glue::glue("Number of principle components must be 2 or 3, but is {pca_max}"))
}
pcs <- c(x = ~PC1, y = ~PC2, z = ~PC3)[1:pca_max]
args <-c(pcs, list(
data = dat,
hoverinfo = "text",
marker = list(size = 8),
color = cgroups,
colors = cpalette,
text = ~ paste(
" Sample: ", Sample,
" Flowcell Lanes: ", flowcell_lanes,
" medTIN: ", median_tin,
" % Aligned: ", percent_aligned,
" % Dup: ", percent_duplication,
" % Coding: ", pct_coding_bases,
" % UTR: ", pct_utr_bases,
" % Intronic: ", pct_intronic_bases,
" Sequence Range: ", sequence_length,
" GC Content: ", gc_content,
" Inner Distance Maxima: ", inner_distance_maxima,
" Insert Size: ", median_insert_size
)
))
do.call(plot_ly, args) %>%
add_markers()%>%
layout(scene = axis_list)
}
```
### Expression PCA
```{r 3d-expression-pca}
# set pca variables
pca=prcomp(t(as.matrix(deg_voom$E)), scale. = T)
define_pcs(pca)
define_axis()
# plot expression
plot_pca_data(shared_metadata, pca_max)
```
### TIN PCA
```{r 3d-tin-pca}
# set pca variables
pca=prcomp(t(as.matrix(tincounts)), scale. = T)
define_pcs(pca)
define_axis()
# plot expression
plot_pca_data(shared_metadata, pca_max)
```
Subplots {data-icon="ion-grid"}
=====================================
**2D Expression PCAs colored by different features**
Row
-------------------------------------
```{r pca-initialize}
# Principal Components Analysis
pca <- prcomp(t(as.matrix(deg_voom$E)), scale. = T)
# Variance explained for PCs: 1, 2, 3
pc1 <- round(pca$sdev[1]^2 / sum(pca$sdev^2) * 100, 2)
pc2 <- round(pca$sdev[2]^2 / sum(pca$sdev^2) * 100, 2)
```
### Flowcell Lanes
```{r colored-by-flowcell-lanes}
# Gene Expression PCA colored by Flowcell Lanes
g <- ggplot(multiQC, aes(PC1, PC2, color = flowcell_lanes), xlab) +
geom_point(size = multiQC$flowcell_lanes) +
theme_minimal() +
labs(color = "Flowcell + Lanes", x = paste0("PC1 (", pc1, "%)"), y = paste0("PC2 (", pc2, "%)"))
g
```
### % Dups
```{r colored-by-dups}
# Gene Expression PCA colored by % Duplicates
g <- ggplot(multiQC, aes(PC1, PC2, color = percent_duplication), xlab) +
geom_point(size = multiQC$flowcell_lanes) +
theme_minimal() +
labs(color = "% Dups", x = paste0("PC1 (", pc1, "%)"), y = paste0("PC2 (", pc2, "%)")) +
scale_colour_gradientn(colours = viridis::viridis(100))
g
```
### % Aligned
```{r colored-by-alignment}
# Gene Expression PCA colored by % Aligned
g <- ggplot(multiQC, aes(PC1, PC2, color = percent_aligned), xlab) +
geom_point(size = multiQC$flowcell_lanes) +
theme_minimal() +
labs(color = "% Aligned", x = paste0("PC1 (", pc1, "%)"), y = paste0("PC2 (", pc2, "%)")) +
scale_colour_gradientn(colours = viridis::viridis(100))
g
```
### % UTR
```{r colored-by-utr-bases}
# Gene Expression PCA colored by % UTR
g <- ggplot(multiQC, aes(PC1, PC2, color = pct_utr_bases), xlab) +
geom_point(size = multiQC$flowcell_lanes) +
theme_minimal() +
labs(color = "% UTR", x = paste0("PC1 (", pc1, "%)"), y = paste0("PC2 (", pc2, "%)")) +
scale_colour_gradientn(colours = viridis::viridis(100))
g
```
### % Intronic
```{r colored-by-intronic-bases}
# Gene Expression PCA colored by % Intronic
g <- ggplot(multiQC, aes(PC1, PC2, color = pct_intronic_bases), xlab) +
geom_point(size = multiQC$flowcell_lanes) +
theme_minimal() +
labs(color = "% Intronic", x = paste0("PC1 (", pc1, "%)"), y = paste0("PC2 (", pc2, "%)")) +
scale_colour_gradientn(colours = viridis::viridis(100))
g
```
Row
-------------------------------------
### Inner Distance Maxima
```{r colored-by-inner-distance-maxima}
# Gene Expression PCA colored by Inner Distance
g <- ggplot(multiQC, aes(PC1, PC2, color = inner_distance_maxima), xlab) +
geom_point(size = multiQC$flowcell_lanes) +
theme_minimal() +
labs(color = "Inner Distance Maxima", x = paste0("PC1 (", pc1, "%)"), y = paste0("PC2 (", pc2, "%)")) +
scale_colour_gradientn(colours = viridis::viridis(100))
g
```
### CV Coverage
```{r colored-by-cv-coverage}
# Gene Expression PCA colored by CV Coverage
g <- ggplot(multiQC, aes(PC1, PC2, color = median_cv_coverage), xlab) +
geom_point(size = multiQC$flowcell_lanes) +
theme_minimal() +
labs(color = "CV Coverage", x = paste0("PC1 (", pc1, "%)"), y = paste0("PC2 (", pc2, "%)")) +
scale_colour_gradientn(colours = viridis::viridis(100))
g
```
### 3' Prime Bias
```{r colored-by-3-prime-coverage}
# Gene Expression PCA colored by 3' Prime Coverage
g <- ggplot(multiQC, aes(PC1, PC2, color = median_3prime_bias), xlab) +
geom_point(size = multiQC$flowcell_lanes) +
theme_minimal() +
labs(color = "3' Prime Coverage", x = paste0("PC1 (", pc1, "%)"), y = paste0("PC2 (", pc2, "%)")) +
scale_colour_gradientn(colours = viridis::viridis(100))
g
```
### Insert Size
```{r colored-by-insert-size}
# Gene Expression PCA colored by Insert Size
g <- ggplot(multiQC, aes(PC1, PC2, color = median_insert_size), xlab) +
geom_point(size = multiQC$flowcell_lanes) +
theme_minimal() +
labs(color = "Insert Size", x = paste0("PC1 (", pc1, "%)"), y = paste0("PC2 (", pc2, "%)")) +
scale_colour_gradientn(colours = viridis::viridis(100))
g
```
### GC Content
```{r colored-by-inner-distance}
# Gene Expression PCA colored by GC Content
g <- ggplot(multiQC, aes(PC1, PC2, color = gc_content), xlab) +
geom_point(size = multiQC$flowcell_lanes) +
theme_minimal() +
labs(color = "GC Content", x = paste0("PC1 (", pc1, "%)"), y = paste0("PC2 (", pc2, "%)")) +
scale_colour_gradientn(colours = viridis::viridis(100))
g
```
Row
-------------------------------------
### medTIN
```{r colored-by-medtin}
# Gene Expression PCA colored by medTIN
g <- ggplot(multiQC, aes(PC1, PC2, color = median_tin), xlab) +
geom_point(size = multiQC$flowcell_lanes) +
theme_minimal() +
labs(color = "medTIN", x = paste0("PC1 (", pc1, "%)"), y = paste0("PC2 (", pc2, "%)")) +
scale_colour_gradientn(colours = viridis::viridis(100))
g
```
### % Coding
```{r colored-by-coding}
# Gene Expression PCA colored by % Coding
g <- ggplot(multiQC, aes(PC1, PC2, color = pct_coding_bases), xlab) +
geom_point(size = multiQC$flowcell_lanes) +
theme_minimal() +
labs(color = "% Coding", x = paste0("PC1 (", pc1, "%)"), y = paste0("PC2 (", pc2, "%)")) +
scale_colour_gradientn(colours = viridis::viridis(100))
g
```
### % rRNA
```{r colored-by-rrna}
# Gene Expression PCA colored by % rRNA
g <- ggplot(multiQC, aes(PC1, PC2, color = rRNA_percent_aligned), xlab) +
geom_point(size = multiQC$flowcell_lanes) +
theme_minimal() +
labs(color = "% rRNA", x = paste0("PC1 (", pc1, "%)"), y = paste0("PC2 (", pc2, "%)")) +
scale_colour_gradientn(colours = viridis::viridis(100))
g
```
### % Anti-sense
```{r colored-by-anti-sense}
# Gene Expression PCA colored by % Anti-sense
g <- ggplot(multiQC, aes(PC1, PC2, color = percent_antisense_strand), xlab) +
geom_point(size = multiQC$flowcell_lanes) +
theme_minimal() +
labs(color = "% Anti-sense", x = paste0("PC1 (", pc1, "%)"), y = paste0("PC2 (", pc2, "%)")) +
scale_colour_gradientn(colours = viridis::viridis(100))
g
```
Corr plots {data-orientation=columns data-icon="ion-stats-bars"}
=====================================
**Feature correlation plots**
Column {data-width=500}
-------------------------------------
### Hierarchical clustering of pairwise spearman correlation coefficients
```{r hierarchical-correlation-matrix, dpi=300}
# Helper Function
reorder_cormat <- function(cormat) {
# Use correlation between variables as distance for hierarchincal clustering
dd <- as.dist((1 - cormat) / 2)
hc <- hclust(dd)
cormat <- cormat[hc$order, hc$order]
}
# Remove all columns that are categorical
numericQC <- multiQC[, -which(sapply(multiQC, class) == "factor")]
# Additional columns to remove
additional_remove <- names(numericQC) %in% c(
"total_read_pairs", "mean_insert_size", "avg_aligned_read_length", "pct_mrna_bases",
"pct_intergenic_bases", "median_5prime_to_3prime_bias", "median_5prime_bias",
"median_3prime_bias", "percent_sense_strand", "mean_mapping_quality",
"PC1_tin", "PC2_tin", "PC3_tin"
)
# Cleaned numerical QC dataframe
numericQC <- numericQC[!additional_remove]
# Remove zero-variance columns to prevent any hlclust() errors
# numericQC = numericQC[,-which(apply(numericQC, 2, var) == 0)]
numericQC <- numericQC[, apply(numericQC, 2, var) != 0]
# Pair-wise spearman correlation matrix
cormatrix <- round(cor(numericQC, method = "spearman"), 2)
# Reorder the correlation matrix based on hierarchical clustering of the correlation coeff
cormat <- reorder_cormat(cormatrix)
# Get upper triangle of the correlation matrix
cormat[lower.tri(cormat)] <- NA
# Remove lower triangle and reshape from wide to long format
cormat <- melt(cormat, na.rm = TRUE)
# Correlation ggheatmap
ggheatmap <- ggplot(cormat, aes(Var2, Var1, fill = value)) +
geom_tile(color = "white") +
scale_fill_gradient2(low = "blue", high = "red", mid = "white", midpoint = 0, limit = c(-1, 1), space = "Lab", name = "Spearman\nCorrelation") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, vjust = 1, size = 9, hjust = 1)) +
coord_fixed() +
theme(
axis.title.x = element_blank(),
axis.title.y = element_blank(),
panel.grid.major = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
axis.ticks = element_blank(),
legend.justification = c(1, 0),
legend.position = c(0.6, 0.7),
legend.direction = "horizontal"
) +
guides(fill = guide_colorbar(barwidth = 8, barheight = 1, title.position = "top", title.hjust = 0.5))
ggheatmap
```
Column {data-width=500}
-------------------------------------
### Complete linkage clustering of PC loadings with QC annotations
```{r loadings-heatmap, dpi=300, fig.height=6}
# Principal Components Analysis: 5 PCs as heatmap input
# Try to use first five Expression PCs
pca_exp <- prcomp(t(as.matrix(deg_voom$E)), scale. = T)
pca_exp <- tryCatch(pca_exp$x[, 1:5], error = function(e) {
pca_exp$x[, 1:pca_max]
})
# Input for heatmap
hm_data <- as.matrix(t(pca_exp))
# Additional columns to remove
additional_remove <- names(numericQC) %in% c("PC1", "PC2", "PC3")
# Cleaned numerical QC dataframe with matched rownames
numericQC <- numericQC[match(colnames(hm_data), rownames(numericQC), nomatch = 0), !additional_remove]
column_annotations <- HeatmapAnnotation(df = numericQC)
if (params$annot) {
cheatmap <- ComplexHeatmap::Heatmap(hm_data,
col = colorRamp2(
seq(-max(abs(pca_exp), na.rm = T), max(abs(pca_exp), na.rm = T), length.out = 20),
rev(colorRampPalette(brewer.pal(9, "PuOr"))(20))
),
bottom_annotation = column_annotations,
show_column_names = T,
column_names_rot = 45,
cluster_rows = FALSE,
show_heatmap_legend = F
) # Turning off to control the placement
} else {
cheatmap <- ComplexHeatmap::Heatmap(hm_data,
col = colorRamp2(
seq(-max(abs(pca_exp), na.rm = T), max(abs(pca_exp), na.rm = T), length.out = 20),
rev(colorRampPalette(brewer.pal(9, "PuOr"))(20))
),
bottom_annotation = column_annotations,
show_column_names = F,
cluster_rows = FALSE,
show_heatmap_legend = F
) # Turning off to control the placement
}
draw(cheatmap, show_annotation_legend = FALSE)
```
Information {data-orientation=columns data-icon="fa-info-circle"}
=====================================
Column {data-width=600}
-------------------------------------
### Overview
**Quantification and quality-control pipeline**
The quality of each sample was independently assessed using FastQC, Preseq, Picard tools, RSeQC, SAMtools, and QualiMap. FastQ Screen and Kraken + Krona were used to screen for various sources of contamination.
Adapter sequences were removed using Cutadapt prior to mapping to hg38 reference genome. STAR was run in _two-pass_ mode where splice-junctions are collected, aggregated, and provided to the second-pass of STAR. Gene expression levels were quantified using RSEM. The expected counts from RSEM are merged across samples to create a counts matrix for downstream analysis. RSeQC `tin.py` was used to calculate transcript integrity numbers for all canonical protein-coding transcripts.
**Downstream Analysis**
The expected counts from RSEM were filtered to remove lowly expressed genes using edgeR's `filterByExpr()` function. The following criteria were selected for filtering: genes must have 10 reads in >= 70% samples. After filtering, we are left with `r dim(deg)[1]` genes. Trimmed mean of M-values (TMM) was performed using the `calcNormFactors()` function in edgeR. The normalisation factors calculated here are used as a scaling factor for the library sizes. Using the `voom()` function in limma, the counts are converted log2-counts-per-million (logCPM) and quantile normalized.
_voom_ is an acronym for mean-variance modelling at the observational level. The key concern is to estimate the mean-variance relationship in the data, then use this to compute appropriate weights for each observation. Count data almost show non-trivial mean-variance relationships. Raw counts show increasing variance with increasing count size, while log-counts typically show a decreasing mean-variance trend. This function estimates the mean-variance trend for log-counts, then assigns a weight to each observation based on its predicted variance. The weights are then used in the linear modeling process to adjust for heteroscedasticity.
**General Recommendations**
Here is a set of generalized guidelines for different QC metrics. Some of these metrics will vary genome-to-genome depending on the quality of the assembly and annotation but that has been taken into consideration for our set of supported reference genomes (human and mouse).
| Tool | Guideline | About |
|---------------------------|-----------|----------------------------------------------------------------------------------------------------|
| FastQC2 | NA | **Quality-control step** to assess sequencing quality, run before and after adapter trimming |
| Cutadapt3 | *Trimmed Reads* > 10000000 | **Data processing step** to remove adapter sequences and perform quality trimming |
| Kraken14 | NA | **Quality-control step** to assess microbial taxonomic composition |
| KronaTools15 | NA | **Quality-control step** to visualize kraken output |
| FastQ Screen17 | *% rRNA* < 10% | **Quality-control step** to assess contamination; additional dependencies: `bowtie2`, `perl` |
| STAR4 | *% Aligned to Reference* > 65% | **Data processing step** to align reads against reference genome |
| RSEM5 | NA | **Data processing step** to quantify gene and isoform counts |
| Preseq1 | NA | **Quality-control step** to estimate library complexity |
| SAMtools13 | NA | **Quality-control step** to run `flagstat` to calculate alignment statistics |
| QualiMap16 | NA | **Quality-control step** to assess various alignment metrics, also calculates insert_size |
| Picard10 | *% Duplicates* < 65 % | **Quality-control step** to run `MarkDuplicates`, `CollectRnaSeqMetrics` and `AddOrReplaceReadGroups` |
| RSeQC9 | *% Coding* > 35% | **Quality-control step** to infer stranded-ness and read distributions over different regions |
| RSeQC tin.py19 | *medTIN* > 65 | **Quality-control step** to calculate transcript integrity numbers for canonical protein coding transcripts |
| MultiQC11 | NA | **Reporting step** to aggregate sample statistics and quality-control information across all sample |
**References**
**1.** Daley, T. and A.D. Smith, Predicting the molecular complexity of sequencing libraries. Nat Methods, 2013. 10(4): p. 325-7.
**2.** Andrews, S. (2010). FastQC: a quality control tool for high throughput sequence data.
**3.** Martin, M. (2011). "Cutadapt removes adapter sequences from high-throughput sequencing reads." EMBnet 17(1): 10-12.
**4.** Dobin, A., et al., STAR: ultrafast universal RNA-seq aligner. Bioinformatics, 2013. 29(1): p. 15-21.
**5.** Li, B. and C.N. Dewey, RSEM: accurate transcript quantification from RNA-Seq data with or without a reference genome. BMC Bioinformatics, 2011. 12: p. 323.
**6.** Harrow, J., et al., GENCODE: the reference human genome annotation for The ENCODE Project. Genome Res, 2012. 22(9): p. 1760-74.
**7.** Law, C.W., et al., voom: Precision weights unlock linear model analysis tools for RNA-seq read counts. Genome Biol, 2014. 15(2): p. R29.
**8.** Smyth, G.K., Linear models and empirical bayes methods for assessing differential expression in microarray experiments. Stat Appl Genet Mol Biol, 2004. 3: p. Article3.
**9.** Wang, L., et al. (2012). "RSeQC: quality control of RNA-seq experiments." Bioinformatics 28(16): 2184-2185.
**10.** The Picard toolkit. https://broadinstitute.github.io/picard/.
**11.** Ewels, P., et al. (2016). "MultiQC: summarize analysis results for multiple tools and samples in a single report." Bioinformatics 32(19): 3047-3048.
**12.** R Core Team (2018). R: A Language and Environment for Statistical Computing. Vienna, Austria, R Foundation for Statistical Computing.
**13.** Li, H., et al. (2009). "The Sequence Alignment/Map format and SAMtools." Bioinformatics 25(16): 2078-2079.
**14.** Wood, D. E. and S. L. Salzberg (2014). "Kraken: ultrafast metagenomic sequence classification using exact alignments." Genome Biol 15(3): R46.
**15.** Ondov, B. D., et al. (2011). "Interactive metagenomic visualization in a Web browser." BMC Bioinformatics 12(1): 385.
**16.** Okonechnikov, K., et al. (2015). "Qualimap 2: advanced multi-sample quality control for high-throughput sequencing data." Bioinformatics 32(2): 292-294.
**17.** Wingett, S. and S. Andrews (2018). "FastQ Screen: A tool for multi-genome mapping and quality control." F1000Research 7(2): 1338.
**18.** Robinson, M. D., et al. (2009). "edgeR: a Bioconductor package for differential expression analysis of digital gene expression data." Bioinformatics 26(1): 139-140.
**9.** Wang, L., et al. (2012). "RSeQC: quality control of RNA-seq experiments." Bioinformatics 28(16): 2184-2185.
Column {data-width=400}
-------------------------------------
### Session Information
```{r session-info}
sessionInfo()
```