Closed jtlandis closed 3 years ago
Is simply including both
treatment
and the batch effect factors in the model enough to capture the batch effects when I eventually call thefdr
for thetreatment
factors?
There's a lot to unpack here. In a generalized linear model, which MAST is1 "batch effects" and "adjustment" for them depend on the mean model, which depends on the link function.
Your simulation assumes one particular mean model (looks to be linear, with a Poisson distribution), while MAST is a two-part model. Assuming log-transformed normalized values, for the values > 0, it's linear. For the 0s, it's logit. So your simulation mis-specifies the MAST model, and MAST will not estimate its parameters correctly. Now, that's totally fine, if MAST's behavior under mispecification is what you are interested in exploring, though if I suspected my data was Poisson with an identity link, I wouldn't use MAST, I'd just use glm(, family = quasipoisson(link = 'identity'))
. Since there's no issues with boundary separation, or variance parameters to estimate you really won't need anything fancy. I'd use the quasipoisson family to guard against overdispersion.
You are getting warnings that the data you simulate isn't well-suited for MAST when you had to set FromMatrix (..., checkSanity = FALSE)
.
Otherwise, your use of doLRT looks correct.
See below for an example of a well-specified model that includes a confounding batch effect, and how MAST adjusts for it. The first 50 genes include (at most) a batch effect, the second 50 have both (sometimes) treatment + batch. In the second plot the spread between the green (batch effected) and black (no batch effect) for the genes with a treatment effect is due to increased power under the batch effect, not a bias in the effect sizes, which you can confirm if you plot the coefficients.
library(MAST)
#> Loading required package: SingleCellExperiment
#> 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: GenomicRanges
#> Loading required package: stats4
#> Loading required package: BiocGenerics
#> Loading required package: parallel
#>
#> Attaching package: 'BiocGenerics'
#> The following objects are masked from 'package:parallel':
#>
#> clusterApply, clusterApplyLB, clusterCall, clusterEvalQ,
#> clusterExport, clusterMap, parApply, parCapply, parLapply,
#> parLapplyLB, parRapply, parSapply, parSapplyLB
#> The following objects are masked from 'package:stats':
#>
#> IQR, mad, sd, var, xtabs
#> The following objects are masked from 'package:base':
#>
#> anyDuplicated, 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, sort, table, tapply,
#> union, unique, unsplit, which.max, which.min
#> Loading required package: S4Vectors
#>
#> Attaching package: 'S4Vectors'
#> The following object is masked from 'package:base':
#>
#> expand.grid
#> Loading required package: IRanges
#> Loading required package: GenomeInfoDb
#> Warning: package 'GenomeInfoDb' was built under R version 4.0.4
#> 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
n = 200
p = 100
treat = gl(2, k = n/2)
batch = gl(2, k = 1, length = n)
batch[treat == 2] = 2
cov2cor(crossprod(model.matrix(~ treat + batch)))
#> (Intercept) treat2 batch2
#> (Intercept) 1.0000000 0.7071068 0.8660254
#> treat2 0.7071068 1.0000000 0.8164966
#> batch2 0.8660254 0.8164966 1.0000000
## batch effect is confounded with treatment effect
beta0 = 3
beta_treat = c(rep(0, p/2), rep(2, p/2))
beta_batch = rep(c(0, 2), times = p/2)
eta_norm = model.matrix(~ treat + batch) %*% t(cbind(beta0, beta_treat, beta_batch))
eta_binom = eta_norm - 5 # on logit scale
u = eta_norm + rnorm(n*p)
v = runif(n*p) < exp(eta_binom)/(1+exp(eta_binom))
u[!v] = 0
sca = FromMatrix(t(u), cData = data.frame(treat, batch))
#> `cData` has no wellKey. I'll make something up.
#> No dimnames in `exprsArray`, assuming `fData` and `cData` are sorted according to `exprsArray`
#> Assuming data assay in position 1, with name et is log-transformed.
z_treat = zlm(~treat, sca)
#>
#> Done!
lr_treat = lrTest(z_treat, 'treat')
#> Refitting on reduced model...
#>
#> Done!
fdr_plot = function(lrt){
plot(-log10(p.adjust(lrt[,'hurdle',"Pr(>Chisq)"], 'fdr')), col = beta_batch+1, ylab = '-log10(FDR)')
legend('topleft', fill = c(1, 3), legend = c('No batch', 'Batch effect'))
abline(h = -log10(.05), lty = 2)
abline(v = 50)
}
fdr_plot(lr_treat)
z_treat_batch = zlm(~treat + batch, sca)
#>
#> Done!
lr_treat_batch = lrTest(z_treat_batch, 'treat')
#> Refitting on reduced model...
#>
#> Done!
fdr_plot(lr_treat_batch)
Created on 2021-03-28 by the reprex package (v1.0.0)
Hello,
I'm trying to understand how to make a model for a scRNA-seq data set that has batch effects. I've read through the vignettes on how to do Differential Expression Analysis and I don't know if I fully understand what
doLRT
does.Here is a repex I made to articulate the issue. In this example I make a set of fake genes that either will be affected by the Cell treatments or not.
Plate1
andPlate2
have a general large difference between them, but for the purpose of this example, we are interested in finding the genes that are differentially expressed via thetreatment
factor.Is simply including both
treatment
and the batch effect factors in the model enough to capture the batch effects when I eventually call thefdr
for thetreatment
factors?Created on 2021-03-27 by the reprex package (v1.0.0)