In this vignette, we describe usage of a suite of tools, SEESAW, Statistical Estimation of allelic Expression using Salmon and Swish, which allow for testing allelic imbalance across samples.
The methods are described in Wu et al. (2022) doi: 10.1101/2022.08.12.503785.
SEESAW makes use of Swish (Zhu et al. 2019) for paired inference, which is an extension of the SAMseq (Li and Tibshirani 2011) methods for permutation-based FDR control.
Type of tests
SEESAW allows for testing global allelic imbalance across all samples (pairwise testing within each individual), as well as differential, or dynamic allelic imbalance (pairwise allelic fold changes estimated within individual, followed by testing across two groups, or along an additional covariate). Each of these allelic imbalance (AI) analyses takes into account the potentially heterogeneous amount of inferential uncertainty per sample, per feature (transcript, transcript-group, or gene), and per allele.
Steps in SEESAW
Running SEESAW involves generation of a diploid transcriptome (e.g. using g2gtools, construction of a diploid Salmon index (specifying
--keepDuplicates), followed by Salmon quantification with a number of bootstrap inferential replicates (we recommend 30 bootstrap replicates). These three steps (diploid reference preparation, indexing, quantification with bootstraps) provide the input data for the following statistical analyses in R/Bioconductor. The steps shown in this vignette leverage Bioconductor infrastructure including SummarizedExperiment for storage of input data and results, tximport for data import, and GRanges and Gviz for plotting.
In short the SEESAW steps are as listed, and diagrammed below:
makeTx2Tss()aggregates data to TSS-level (optional)
importAllelicCounts()creates a SummarizedExperiment
Below we demonstrate an analysis where transcripts are grouped by their transcription start site (TSS), although gene-level or transcript-level analysis is also possible. Additionally, any custom grouping could be used, by manually generating a
t2g table as shown below. Special plotting functions in fishpond facilitate visualization of allelic and isoform changes at different resolutions, alongside gene models. In three examples, we perform global AI testing, differential AI testing, and dynamic AI testing, in all cases on simulated data associated with human genes.
Here we will use simulated data, but we can import allelic counts with the
importAllelicCounts() function. It is best to read over the manual page for this function. For TSS-level analysis, the
t2g GRanges generated above should be passed to the
tx2gene argument. This will summarize transcript-level counts to the TSS level, and will attach
rowRanges that provide the genomic locations of the grouped transcripts. Note that
importAllelicCounts does not yet have the ability to automatically generate ranges based on sequence hashing (as occurs in
Because we use
--keepDuplicates in the step when we build the Salmon index, there will be a number of features in which there is no information about the allelic expression in the reads. We can find these features in bootstrap data by examining when the inferential replicates are nearly identical for the two alleles, as this is how the EM will split the reads. Removing these features avoids downstream problems during differential testing. Code for this filtering follows:
ncol(y)/2 n <- assay(y, "infRep1")[,y$allele == "a1"] rep1a1 <- assay(y, "infRep1")[,y$allele == "a2"] rep1a2 <-mcols(y)$someInfo <- rowSums(abs(rep1a1 - rep1a2) < 1) < n y[ mcols(y)$someInfo, ]y <-
We begin by generating a simulated data object that resembles what one would obtain with
importAllelicCounts(). The import function arranges the
a2 (non-effect) allelic counts first, followed by the
a1 (effect) allelic counts. Allelic ratios are calculated as
a1/a2, which follows the notational standard in PLINK and other tools.
set.seed(1) makeSimSwishData(allelic=TRUE) y <-colData(y)
## DataFrame with 20 rows and 2 columns ## allele sample ## <factor> <factor> ## s1-a2 a2 sample1 ## s2-a2 a2 sample2 ## s3-a2 a2 sample3 ## s4-a2 a2 sample4 ## s5-a2 a2 sample5 ## ... ... ... ## s6-a1 a1 sample6 ## s7-a1 a1 sample7 ## s8-a1 a1 sample8 ## s9-a1 a1 sample9 ## s10-a1 a1 sample10
levels(y$allele) # a1/a2 allelic fold changes
##  "a2" "a1"
A hidden code chunk is used to add ranges from the EnsDb to the simulated dataset. For a real dataset, the ranges would be added either by
importAllelicCounts (if using
tx2gene) or could be added manually for transcript- or gene-level analysis, using the
rowRanges<- setter function. The ranges are only needed for the
plotAllelicGene plotting function below.
<hidden code chunk>
We can already plot a heatmap of allelic ratios, before performing statistical testing. We can see in the first gene, ADSS, there appear to be two groups of transcripts with opposing allelic fold change. SEESAW makes use of pheatmap for plotting a heatmap of allelic ratios.
computeInfRV(y) # for posterior mean, variance y <- rowRanges(y)$gene_id gene <- mcols(y)$gene_id == gene idx <-plotAllelicHeatmap(y, idx=idx)
The following two functions perform a Swish analysis, comparing the allelic counts within sample, while accounting for uncertainty in the assignment of the reads. The underlying test statistic is a Wilcoxon signed-rank statistic, which compares the two allele counts from each sample, so a paired analysis.
Scaling: Note that we do not use
scaleInfReps in the allelic pipeline. Because we compare the two alleles within samples, there is no need to perform scaling of the counts to adjust for sequencing depth. We simply import counts, filter low counts with
lableKeep and then run the statistical testing with
Fast mode: for basic allelic analysis, we use a paired test, comparing one allele to the other. The default in
swish for a simple paired test is to use a Wilcoxon signed rank test statistic with bootstrap aggregation and permutation significance. The ranks must be recomputed per permutation, which is a slow operation that is not necessary with other designs in
swish. A faster test statistic is the one-sample z-score, which gives similar results. Here we demonstrate using the fast version of the paired test. Note that
fast=1 is only relevant for simple paired tests, not for other designs, which are already fast.
labelKeep(y) y <- swish(y, x="allele", pair="sample", fast=1)y <-
We can return to the heatmap, and now add q-values, etc. For details on adding metadata to a pheatmap plot object, see
data.frame(minusLogQ=-log10(mcols(y)$qvalue[idx]), dat <-row.names=rownames(y)[idx]) plotAllelicHeatmap(y, idx=idx, annotation_row=dat)