Skip to content

Commit d7ac4c8

Browse files
author
Sarah Teichman
authored
Merge pull request #190 from luhann/tse
Add full SummarizedExperiment support
2 parents 5ece0d5 + 6572a52 commit d7ac4c8

22 files changed

Lines changed: 747 additions & 13 deletions

.github/workflows/R-CMD-check.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ jobs:
6464
run: |
6565
remotes::install_deps(dependencies = TRUE)
6666
remotes::install_cran("rcmdcheck")
67-
if (!requireNamespace("BiocManager", quietly = TRUE)){install.packages("BiocManager")}; BiocManager::install(c("phyloseq", "limma"), ask = FALSE)
67+
if (!requireNamespace("BiocManager", quietly = TRUE)){install.packages("BiocManager")}; BiocManager::install(c("phyloseq", "limma", "SummarizedExperiment"), ask = FALSE)
6868
shell: Rscript {0}
6969

7070
- name: Check

.github/workflows/test-coverage.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
- name: Install dependencies
3838
run: |
3939
install.packages(c("remotes"))
40-
if (!requireNamespace("BiocManager", quietly = TRUE)){install.packages("BiocManager")}; BiocManager::install(c("phyloseq", "limma"), ask = FALSE)
40+
if (!requireNamespace("BiocManager", quietly = TRUE)){install.packages("BiocManager")}; BiocManager::install(c("phyloseq", "limma", "SummarizedExperiment"), ask = FALSE)
4141
remotes::install_deps(dependencies = TRUE)
4242
remotes::install_cran("covr")
4343
shell: Rscript {0}

DESCRIPTION

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ License: GPL (>= 2)
1717
Imports: stats, utils, VGAM, numDeriv, ggplot2, trust, dplyr, magrittr, detectseparation, scales, rlang
1818
Encoding: UTF-8
1919
LazyData: true
20-
RoxygenNote: 7.3.2
20+
RoxygenNote: 7.3.3
2121
Suggests: knitr,
2222
rmarkdown,
2323
testthat,
@@ -26,5 +26,6 @@ Suggests: knitr,
2626
slam,
2727
R.rsp,
2828
optimx,
29-
phyloseq
29+
phyloseq,
30+
SummarizedExperiment
3031
VignetteBuilder: knitr

NAMESPACE

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ export(HDIbetabinom)
1212
export(bbdml)
1313
export(checkNested)
1414
export(clean_taxa_names)
15+
export(clean_taxa_names_se)
1516
export(contrastsTest)
1617
export(convert_phylo)
18+
export(convert_sumexp)
1719
export(coth)
1820
export(differentialTest)
1921
export(fishZ)
@@ -24,6 +26,7 @@ export(invlogit)
2426
export(logit)
2527
export(lrtest)
2628
export(otu_to_taxonomy)
29+
export(otu_to_taxonomy_se)
2730
export(pbLRT)
2831
export(pbRao)
2932
export(pbWald)
@@ -35,6 +38,7 @@ export(score)
3538
export(waldchisq)
3639
export(waldt)
3740
export(warn_phyloseq)
41+
export(warn_sumexp)
3842
importFrom(magrittr,"%>%")
3943
importFrom(rlang,.data)
4044
importFrom(stats,simulate)

R/bbdml.R

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#'
33
#' @param formula an object of class \code{formula}: a symbolic description of the model to be fitted to the abundance
44
#' @param phi.formula an object of class \code{formula} without the response: a symbolic description of the model to be fitted to the dispersion
5-
#' @param data a data frame or \code{phyloseq} object containing the variables in the models
5+
#' @param data a data frame, \code{phyloseq}, or \code{SummarizedExperiment} object containing the variables in the models
66
#' @param link link function for abundance covariates, defaults to \code{"logit"}
77
#' @param phi.link link function for dispersion covariates, defaults to \code{"logit"}
88
#' @param method optimization method, defaults to \code{"trust"}, or see \code{\link[optimx]{optimr}} for other options
@@ -60,6 +60,14 @@ bbdml <- function(formula, phi.formula, data,
6060
formula <- stats::update(formula, cbind(W, M - W) ~ .)
6161
}
6262

63+
# Convert SummarizedExperiment objects
64+
if (inherits(data, "SummarizedExperiment")) {
65+
selection <- all.vars(formula)[1]
66+
data <- convert_sumexp(data, select = selection)
67+
# Update formula to match convert_phylo specification
68+
formula <- stats::update(formula, cbind(W, M - W) ~ .)
69+
}
70+
6371
# Record call
6472
call <- match.call(expand.dots = FALSE)
6573
# Record mu link

R/clean_taxa_names.R

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,30 @@ clean_taxa_names <- function(x, name = "OTU") {
2323
warn_phyloseq()
2424
}
2525
}
26+
27+
#' Rename taxa
28+
#'
29+
#' Renames taxa to have short human-readable names
30+
#'
31+
#' @param x Object of class \code{SummarizedExperiment}
32+
#' @param name Character, defaults to \code{"OTU"}. Optional. String to use in every taxa name.
33+
#'
34+
#' @details The original taxa names are saved as the \code{original_names} attribute. See the example for an example of how to access the original names.
35+
#'
36+
#' @return Object of class \code{SummarizedExperiment}, with taxa renamed (defaults to OTU1, OTU2, ...), with the original taxa names saved as an attribute.
37+
#'
38+
#' @export
39+
clean_taxa_names_se <- function(x, name = "OTU") {
40+
if (requireNamespace("SummarizedExperiment", quietly = TRUE)) {
41+
if (inherits(x, "SummarizedExperiment")) {
42+
attr(x, "original_names") <- row.names(x)
43+
row.names(x) <- paste0(name, seq_len(nrow(x)))
44+
return(x)
45+
} else {
46+
stop("clean_taxa_names_se is intended for SummarizedExperiment objects!")
47+
}
48+
} else {
49+
warn_sumexp()
50+
}
51+
52+
}

R/contrastsTest.R

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ contrastsTest <- function(formula, phi.formula,
9595
} else {
9696
warn_phyloseq()
9797
}
98+
} else if (inherits(data, "SummarizedExperiment")) {
99+
if (requireNamespace("SummarizedExperiment", quietly = TRUE)) {
100+
# Set up response
101+
taxanames <- row.names(data)
102+
sample_data <- SummarizedExperiment::colData(data)
103+
} else {
104+
warn_sumexp()
105+
}
98106
} else if (is.matrix(data) || is.data.frame(data)) {
99107

100108
# # use phyloseq
@@ -118,7 +126,7 @@ contrastsTest <- function(formula, phi.formula,
118126
M <- rowSums(data)
119127

120128
} else {
121-
stop("Input must be either data frame, matrix, or phyloseq object!")
129+
stop("Input must be either data frame, matrix, phyloseq object, or SummarizedExperiment object!")
122130
}
123131

124132
# Set up output
@@ -147,6 +155,8 @@ contrastsTest <- function(formula, phi.formula,
147155
# Subset data to only select that taxa
148156
if ("phyloseq" %in% class(data)) {
149157
data_i <- convert_phylo(data, select = taxanames[i])
158+
} else if (inherits(data, "SummarizedExperiment")) {
159+
data_i <- convert_sumexp(data, select = taxanames[i])
150160
} else {
151161
response_i <- data.frame(W = data[, taxanames[i]], M = M)
152162
data_i <- cbind(response_i, sample_data)

R/convert_sumexp.R

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#' Function to subset and convert SummarizedExperiment data
2+
#'
3+
#' @param data a \code{SummarizedExperiment} object
4+
#' @param select Name of OTU or taxa to select, must match taxa name in \code{data}
5+
#'
6+
#' @return A \code{data.frame} object, with elements \code{W} as the observed counts, \code{M} as the sequencing depth, and the sample data with their original names.
7+
#'
8+
#' @export
9+
convert_sumexp <- function(data, select) {
10+
if (requireNamespace("SummarizedExperiment", quietly = TRUE)) {
11+
subsamp <- data[select, ]
12+
W_tmp <- matrix(t(SummarizedExperiment::assay(subsamp)), ncol = 1)
13+
14+
15+
out <- data.frame(W = W_tmp,
16+
M = colSums(SummarizedExperiment::assay(data)),
17+
SummarizedExperiment::colData(subsamp))
18+
return(out)
19+
} else {
20+
warn_sumexp()
21+
}
22+
}

R/differentialTest.R

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#' @param phi.formula an object of class \code{formula} without the response: a symbolic description of the model to be fitted to the dispersion
55
#' @param formula_null Formula for mean under null, without response
66
#' @param phi.formula_null Formula for overdispersion under null, without response
7-
#' @param data a data frame containing the OTU table, or \code{phyloseq} object containing the variables in the models
7+
#' @param data a data frame containing the OTU table, \code{phyloseq}, or \code{SummarizedExperiment} object containing the variables in the models
88
#' @param link link function for abundance covariates, defaults to \code{"logit"}
99
#' @param phi.link link function for dispersion covariates, defaults to \code{"logit"}
1010
#' @param test Character. Hypothesis testing procedure to use. One of \code{"Wald"}, \code{"LRT"} (likelihood ratio test), or \code{"Rao"}.
@@ -95,6 +95,15 @@ differentialTest <- function(formula, phi.formula,
9595
} else {
9696
warn_phyloseq()
9797
}
98+
} else if (inherits(data, "SummarizedExperiment")) {
99+
if (requireNamespace("SummarizedExperiment", quietly = TRUE)) {
100+
# Set up response
101+
taxanames <- row.names(data)
102+
sample_data <- SummarizedExperiment::colData(data)
103+
} else {
104+
warn_sumexp()
105+
}
106+
98107
} else if (is.matrix(data) || is.data.frame(data)) {
99108

100109
# # use phyloseq
@@ -118,7 +127,7 @@ differentialTest <- function(formula, phi.formula,
118127
M <- rowSums(data)
119128

120129
} else {
121-
stop("Input must be either data frame, matrix, or phyloseq object!")
130+
stop("Input must be either data frame, matrix, phyloseq object or SummarizedExperiment!")
122131
}
123132

124133
# Set up output
@@ -158,6 +167,8 @@ differentialTest <- function(formula, phi.formula,
158167
# Subset data to only select that taxa
159168
if ("phyloseq" %in% class(data)) {
160169
data_i <- convert_phylo(data, select = taxanames[i])
170+
} else if (inherits(data, "SummarizedExperiment")) {
171+
data_i <- convert_sumexp(data, select = taxanames[i])
161172
} else {
162173
response_i <- data.frame(W = data[, taxanames[i]], M = M)
163174
data_i <- cbind(response_i, sample_data)
@@ -275,6 +286,8 @@ We *strongly recommend* running `bbdml` on a single taxon (especially before pos
275286
i <- (try_only[!(try_only %in% ind_disc)])[1]
276287
if ("phyloseq" %in% class(data)) {
277288
data_i <- convert_phylo(data, select = taxanames[i])
289+
} else if (inherits(data, "SummarizedExperiment")) {
290+
data_i <- convert_sumexp(data, select = taxanames[i])
278291
} else {
279292
response_i <- data.frame(W = data[, taxanames[i]], M = M)
280293
data_i <- cbind(response_i, sample_data)

R/genInits.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ genInits <- function(W, M,
3333
nstart = 1, use = TRUE) {
3434

3535

36-
init.glm <- eval(parse(text = paste("quasibinomial(link =", link,")")))
36+
init.glm <- eval(parse(text = paste("quasibinomial(link =", link, ")")))
3737
tmp <- stats::glm.fit(x = X, y = cbind(W, M - W), family = init.glm)
3838
b_init <- stats::coef(tmp)
3939
# Just use 0.5 for phi_init

0 commit comments

Comments
 (0)