--- title: "QSEA Tutorial" author: "Matthias Lienhard, Lukas Chavez and Ralf Herwig" date: "`r doc_date()`" package: "`r pkg_ver('qsea')`" abstract: > QSEA (quantitative sequencing enrichment analysis) was developed as the successor of the MEDIPS package for analyzing data derived from methylated DNA immunoprecipitation (MeDIP) experiments followed by sequencing (MeDIP-seq). However, qsea provides functionality for the analysis of other kinds of quantitative sequencing data (e.g. ChIP-seq, MBD-seq, CMS-seq and others) including calculation of differential enrichment between groups of samples. vignette: > %\VignetteIndexEntry{qsea} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} output: BiocStyle::html_document: fig_width: 7 fig_height: 6 fig_caption: true bibliography: qseaTutorial.bib --- # Introduction QSEA stands for "Quantitative Sequencing Enrichment Analysis" and implements a statistical framework for modeling and transformation of MeDIP-seq enrichment data to absolute methylation levels similar to BS-sequencing read-outs. Furthermore, QSEA comprises functionality for data normalization that accounts for the effect of CNVs on the read-counts as well as for the detection and annotation of differently methylated regions (DMRs). The transformation is based on a Bayesian model, that explicitly takes background reads and CpG density dependent enrichment profiles of the experiments into account. # Installation To install the QSEA package in your R environment, start R and enter: ```{r , eval=FALSE} BiocManager::install("qsea") ``` Next, it is necessary to have the genome of interest available in your R environment. As soon as you have the `r Biocpkg("BSgenome")` package installed and the library loaded using ```{r , eval=FALSE} BiocManager::install("BSgenome") library("BSgenome") ``` you can list all available genomes by typing ```{r, eval=FALSE} available.genomes() ``` In case a genome of interest is not available as a BSgenome package but the sequence of the genome is available, a custom BSgenome package can be generated, please see the "How to forge a BSgenome data package" manual of the `r Biocpkg("BSgenome")` package. In the given example, we mapped the short reads against the human genome build hg19. Therefore, we download and install this genome build: ```{r, eval=FALSE} BiocManager::install("BSgenome.Hsapiens.UCSC.hg19") ``` This takes some time, but has to be done only once for each reference genome. In order to specify genomic regions of interest, QSEA utilitzes the GenomicRanges package ```{r, eval=FALSE} BiocManager::install("GenomicRanges") ``` Finally, the QSEA work flow described below requires access to example data available in the `r Biocpkg("MEDIPSData")` package which can be installed by typing: ```{r, eval=FALSE} BiocManager::install("MEDIPSData") ``` # QSEA workflow Here, we show the most important steps of the QSEA work-flow for the analysis of MeDIP seq data. We assume that the reads have been aligned to reference genome hg19 and are on hand in .bam format. ## Preparation and import of short reads In order to describe the samples, we must provide a data.frame object with at least 3 columns: * sample_name: unique names of the samples, * file_name: path to the alignment files of MeDIP seq, typically bam files * group: assignment of samples to groups, such as "treatment" and "control". Further columns describing the samples are optional, and can be considered in downstream analysis: * sex: if provided, the number of sex chromosomes is taken into account. * input files: If sequencing of the input libraries (before enrichment) are available, this data can be used in the addCNV function, by specifying the respective column. * alternative group arrangements or clinical data can be used in the statistical test, or when creating result tables. To demonstrate the QSEA analysis steps, we use data available in the `r Biocpkg("MEDIPSData")` package. This package contains aligned MeDIP seq data for chromosomes 20, 21 and 22 of 3 NSCLC tumor samples and adjacent normal tissue. ```{r, results='asis'} data(samplesNSCLC, package="MEDIPSData") knitr::kable(samples_NSCLC) path=system.file("extdata", package="MEDIPSData") samples_NSCLC$file_name=paste0(path,"/",samples_NSCLC$file_name ) ``` Now, the QSEA package has to be loaded. ```{r lib_qsea, message=FALSE} library(qsea) ``` To access information of the reference genome, we load the pre-installed (see chapter [Installation](#hg19install)) hg19 library: ```{r lib_BSgenome, message=FALSE} library(BSgenome.Hsapiens.UCSC.hg19) ``` All relevant information of the enrichment experiment, including sample information, the genomic read coverage, CpG density and other parameters are stored in a "qseaSet" object. To create such an object, call the function "createQseaSet", that takes the following parameters: * BSgenome: The reference genome name as defined by BSgenome. (required) * window_size: defines the genomic resolution by which short read coverage is calculated. (default:250 bases) * chr.select: Only data at the specified chromosomes will be processed. (default: NULL=all chromosomes) * Regions: If specified, only data in the specified regions will be processed. ```{r createSet, collapse=TRUE} qseaSet=createQseaSet(sampleTable=samples_NSCLC, BSgenome="BSgenome.Hsapiens.UCSC.hg19", chr.select=paste0("chr", 20:22), window_size=500) qseaSet ``` We now read the alignment files and compute the MeDIP coverage for each window. For this step, we have to specify the following parameters: * qs: the QSEA set, as prepared in the step above * uniquePos: if set, fragments at the exact same start and end position are considered to be PCR duplicates and replaced by one representative * minMapQual: minimum mapping quality for a read to be considered. Commonly, a mapping quality of 0 reflects ambigious alignments in most alignment tools. * paired: if set, reads are considered to be paired end. In this case, exact fragment sizes are imported. * fragment_size: for single end sequencing data (paired=FALSE), this parameter provides the fragment size. ```{r addCoverage, eval=FALSE} qseaSet=addCoverage(qseaSet, uniquePos=TRUE, paired=TRUE) ``` ##Normalization ###Copy Number Variation The QSEA model can consider Copy Number Variation (CNV), and account for their influence on MeDIP read density. CNV can either be computed externally and imported into QSEA, or estimated directly within QSEA. QSEA internally uses the bioconductor package `r Biocpkg("HMMcopy")` to estimate CNV from sequencing data of the input library, or from the MeDIP library (parameter "MeDIP"=TRUE). In the latter case, only fragments that do not contain CpG dinucleotides are considered. Externally computed CNVs can be imported with the addCNV function by specifying the "cnv" parameter: * cnv: externally computed CNVs as a GRange object, containing a metadata column with log2 CNV values (relative to normal Copy Number). for each sample in the analysis. The names of the columns must match the sample names, as specified in the sample table. If no "cnv" object is provided, the following parameters control the internal CNV analysis: * file_name: specify the column in the sample table, containing the sequencing alignment files, to be used for the CNV analysis. * window_size: window size used for the CNV analysis. * paired, "fragment_size": see addCoverage step above * mu: Suggested median for copy numbers in states, see HMMcopy::HMMsegment * normal_idx: samples, that presumably do not have copy number variations. By default, samples grouped as "normal" or "control" in the sample table are selected, * MeDIP: Estimate CNV from CpG methylation enriched sequencing data (e.g. MeDIP seq) * plot_dir: optionally, chromosome wise CNV plots are generated in the specified directory ```{r addCNV, eval=FALSE} qseaSet=addCNV(qseaSet, file_name="file_name",window_size=2e6, paired=TRUE, parallel=FALSE, MeDIP=TRUE) ``` Note, that CNV estimation will benefit from analysing the whole genome. For this step, limiting the estimation to single chromosomes is not recommendable in general. ###Scaling Library Factor QSEA accounts for differences in sequencing depth and library composition by estimating the effective library size for each sample using the trimmed mean of m-values approach [TMM, @robinson2010]. Alternatively, QSEA can import user provided normalization factors, using the "factors" parameter in addLibraryFactors. ```{r addLibraryFactors, eval=FALSE} qseaSet=addLibraryFactors(qseaSet) ``` ###Estimating model parameters for transformation to absolute methylation values As MeDIP seq read coverage is dependent on the CpG density of the fragment, we estimate the average CpG density per fragment for each genomic window. ```{r addPatternDensity, eval=FALSE} qseaSet=addPatternDensity(qseaSet, "CG", name="CpG") ``` From the regions without CpGs we can estimate the coverage offset from background reads. ```{r loadDataSet, include=FALSE} #load prepared dataset data(NSCLC_dataset, package="MEDIPSData") ``` ```{r addOffset, tidy=TRUE, collapse=TRUE} qseaSet=addOffset(qseaSet, enrichmentPattern="CpG") ``` In order to estimate the relative enrichment, we need to model the enrichment efficiency, which is dependent on the CpG density. The parameters for this model can be estimated using the addEnrichmentParameters() function. For this task we need to provide known values for a subset of windows, for example gained by validation experiments or from other studies. QSEA then fits a sigmoidal function to these values, in order to smooth and extrapolate the provided values. The enrichment parameters, together with the estimated offset of background reads are applied in the transformation to absolute methylation values. For the example dataset, we can make use of methylation data from the TCGA lung adenocarcinoma [LUAD,@TCGA_LUAD ] and squamous cell carcinoma [LUSC,@TCGA_LUSC] studies. For this purpose, DNA methylation datasets have been downloaded from , and averaged in 500 base windows, and filtered for highly methylated windows across all samples. The relevant chromosomes of this data is contained in the "tcga_lung" object of the `r Biocpkg("MEDIPSData")` package. ```{r addEnrichmentParametersTCGA, collapse=TRUE} data(tcga_luad_lusc_450kmeth, package="MEDIPSData") wd=findOverlaps(tcga_luad_lusc_450kmeth, getRegions(qseaSet), select="first") signal=as.matrix(mcols(tcga_luad_lusc_450kmeth)[,rep(1:2,3)]) qseaSet=addEnrichmentParameters(qseaSet, enrichmentPattern="CpG", windowIdx=wd, signal=signal) ``` In case such information is not available for the analyzed data set, the enrichment model can be calibrated using rough estimates ("blind calibration"). For instance, in adult tissue samples, we can assume high methylation at regions with low CpG density, and linearly decreasing average methylation levels for higher CpG density. However, for different types of samples, such as cell lines or embryonic cells, these assumptions would be a poor fit and lead to false results. ```{r addEnrichmentParameters_blind, collapse=TRUE} wd=which(getRegions(qseaSet)$CpG_density>1 & getRegions(qseaSet)$CpG_density<15) signal=(15-getRegions(qseaSet)$CpG_density[wd])*.55/15+.25 qseaSet_blind=addEnrichmentParameters(qseaSet, enrichmentPattern="CpG", windowIdx=wd, signal=signal) ``` ##Quality control In order to review the quality of the MeDIP enrichment and the model assumptions, made in the previous steps, we can check the model parameters estimated by QSEA, which describe the signal to noise ratio, and the enrichment efficiency. At first, we check the estimated fraction of background reads: ```{r getOffset, collapse=TRUE} getOffset(qseaSet, scale="fraction") ``` This reveals, that between 9% and 13% of the fragments are due to the background rather than enrichment. High values (e.g. > 0.9) would indicate a lack of enrichment efficiency. To examine the the sample-wise CpG density dependent enrichment profiles, as estimated by addEnrichmentParameters(), QSEA provides the plotEPmatrix() function: ```{r plotEPmatrix, results='hide',collapse=TRUE, fig.width=10,fig.height=7,fig.cap='CpG density dependent Enrichment Profiles'} plotEPmatrix(qseaSet) ``` The average enrichment, observed in the provided "signal", is depicted in black, and the fitted sigmoidal function in green. Samples with flat profiles might indicate low enrichment efficiency, or poor agreement with the calibration data. ##Exploratory Analysis In order to analyze the main characteristics of the data, and to visualize the relationships between the samples, QSEA provides a set of tools for exploratory data analysis. The plotCNV() function provides a genome wide overview on CNV. Deletations are depicted in green shades, while amplifications are green, and normal copy numbers are blue. Each sample is represented as a row, which is ordered based on the similarity of CNV profiles. ```{r plotCNV, collapse=TRUE,fig.width=10,fig.height=7,fig.cap="Overview representation of Copy Number Variation"} plotCNV(qseaSet) ``` This plot shows, that one half of chr 20 is amplified in sample 2T, while a large part of chr21 is deleted in sample 1T. The other samples, including 3T, have normal copy numbers. Regions without information (e.g. that are masked in the reference) are represented in gray. To explore the relationship between samples, a principal component plot can be intuitive. By default, principal component analysis (PCA) is computed based on log transformed normalized count values. In order to use the transformation to absolute methylation values, the "norm_method" parameter is set to "beta". To get an impression of the effects on different levels of annotation, the PCA can be restricted on regions of interest, provided as a GRanges object. To demonstrate this feature, we import an annotation object, containing different UCSC annotation tracks. To obtain CpG Island promoter regions, we intersect CpG Islands with transcription start sites(TSS). ```{r, collapse=TRUE} data(annotation, package="MEDIPSData") CGIprom=intersect(ROIs[["CpG Island"]], ROIs[["TSS"]],ignore.strand=TRUE) pca_cgi=getPCA(qseaSet, norm_method="beta", ROIs=CGIprom) col=rep(c("red", "green"), 3) plotPCA(pca_cgi, bg=col, main="PCA plot based on CpG Island Promoters") ``` In this plot, we can that DNA methylation is different between Tumor and Normal samples, and that the Tumor samples are a heterogeneous group, while the Normal samples cluster tightly together. ##Differential Methylation Analysis Differential Methylation Analysis in QSEA is based on generalized linear models (GLMs), and a likelihood ratio test, similar to tests performed to detect deferentially expressed genes, implemented in `r Biocpkg("DESeq2")` or `r Biocpkg("edgeR")`. Briefly, we model read counts with a negative binomial distribution with mean and dispersion parameter. For each window, we fit a GLM with logarithmic link function. For significance testing, we fit a reduced model, and compare the likelihood ratio (LR) of the models to a Chi-squared distribution. These steps are implemented in the following functions: ```{r, eval=FALSE} design=model.matrix(~group, getSampleTable(qseaSet) ) qseaGLM=fitNBglm(qseaSet, design, norm_method="beta") qseaGLM=addContrast(qseaSet,qseaGLM, coef=2, name="TvN" ) ``` At fist, the model matrix ("design") for the full model is defined, using the group column of the sample table. The model contains two coeficients: an intercept, and a dummy variable, distinguishing between "Normal" and "Tumor". "fitNBglm" simultaneously fits the dispersion parameters and the model coefficients for the full model. In the next step, "addContrast" fits a model, reduced by the 2nd component (thus, only intercept remains). In addition, the likelihood ratio test is performed. All results are stored in a QSEA GLM object. Note, that one GLM object can contain several contrasts and test results (for more complex experimental designs). ##Annotating, Exploring and Exporting Results We can now create a results table with raw counts (_reads) estimated % methylation values (_beta), as well as an 95% credibility interval [_betaLB, _betaUB] for the estimates for all deferentially methylated windows. "isSignificant" selects windows below the defined significance threshold. ```{r, collapse=TRUE} library(GenomicRanges) sig=isSignificant(qseaGLM, fdr_th=.01) result=makeTable(qseaSet, glm=qseaGLM, groupMeans=getSampleGroups(qseaSet), keep=sig, annotation=ROIs, norm_method="beta") knitr::kable(head(result)) ``` The makeTable function is implemented quite generically, and provides the following options, to specify the regions and the content of the table: * norm_methods: a vector of normalization method names. Valid names include ** counts: raw (un-normalized) read counts. ** nrpkm: CNV normalized reads per kilobase (genomic window) per million mapped reads. ** beta: transformation to absolute methylation. * samples: sample names to be included in the table as individual columns. * groupMeans: named list of sample names, that are summarized as group averages. * glm: optional argument, to include test statistics in the result table. * keep: vector of window ids, as obtained by isSignificant() * ROIs: GRange object defining regions of interest, to which the result table will be restricted. * annotation: A named list of GRange objects, containing genomic annotations. To assess the enrichment of differentially methylated regions within genomic annotations, the regionsStats() functions ```{r, results='asis', collapse=TRUE} sigList=list(gain=isSignificant(qseaGLM, fdr_th=.1,direction="gain"), loss=isSignificant(qseaGLM, fdr_th=.1,direction="loss")) roi_stats=regionStats(qseaSet, subsets=sigList, ROIs=ROIs) knitr::kable(roi_stats) roi_stats_rel=roi_stats[,-1]/roi_stats[,1] x=barplot(t(roi_stats_rel)*100,ylab="fraction of ROIs[%]", names.arg=rep("", length(ROIs)+1), beside=TRUE, legend=TRUE, las=2, args.legend=list(x="topleft"), main="Feature enrichment Tumor vs Normal DNA methylation") text(x=x[2,],y=-.15,labels=rownames(roi_stats_rel), xpd=TRUE, srt=30, cex=1, adj=c(1,0)) ``` As expected for tumor samples, hypomethylated regions are more frequent genome wide, while CpG islands are enriched for hypermethylation. If we are interested in a particular genomic region, it can be depicted in a genome browser like manner as follows: (find a more interesting example, add annotations ...) ```{r, collapse=TRUE} plotCoverage(qseaSet, samples=getSampleNames(qseaSet), chr="chr20", start=38076001, end=38090000, norm_method="beta", col=rep(c("red", "green"), 3), yoffset=1,space=.1, reorder="clust", regions=ROIs["TFBS"],regions_offset=.5, cex=.7 ) ``` #Parallelization A large part of the run time is required for processing the alignment files. These steps can be parallelized using the `r Biocpkg("BiocParallel")` package: ```{r, eval=FALSE} library("BiocParallel") register(MulticoreParam(workers=3)) qseaSet=addCoverage(qseaSet, uniquePos=TRUE, paired=TRUE, parallel=TRUE) ``` The "parallel" parameter is also available for the addCNV function. Note, that for parallel scanning of files, reading speed might be a limiting factor. # References