scDonorAudit reports how a fixed two-condition pseudobulk analysis changes when one biological donor is omitted. It uses edgeR for the specified contrast and links the planned omissions to design viability, execution events, and observed gene-effect changes.
For the current source version, follow the installation instructions in the repository README.
After acceptance and a successful Bioconductor build, use the command below in the matching R/Bioconductor environment. The package will first be available in devel and later in the applicable release; see the Bioconductor installation guide.
This small example shows how cell counts become pseudobulk counts and
how the sample registry records missing combinations. Two cells from
s1 are summed into its T-cell column;
registered sample s3 has no observed cells.
sce_counts <- matrix(c(1L, 4L, 2L, 5L, 3L, 6L, 4L, 7L), nrow = 2L,
dimnames = list(c("g1", "g2"), paste0("cell", 1:4)))
sce_cells <- S4Vectors::DataFrame(
sample_id = c("s1", "s1", "s2", "s2"),
cell_type = c("T", "T", "T", "B"))
sce_registry <- data.frame(sample_id = c("s1", "s2", "s3"),
donor_id = c("d1", "d2", "d3"),
condition = c("ctrl", "stim", "ctrl"))
sce_input <- SingleCellExperiment::SingleCellExperiment(
assays = list(counts = sce_counts), colData = sce_cells)
cell_pb <- scDonorAudit::preparePseudobulk(sce_input, "sample_id", "donor_id",
"condition", "cell_type", sample_table = sce_registry)
SummarizedExperiment::assay(cell_pb, "counts")
#> 2 x 3 Matrix of class "dgeMatrix"
#> pb_00001 pb_00002 pb_00003
#> g1 3 4 3
#> g2 9 7 6
knitr::kable(as.data.frame(SummarizedExperiment::colData(cell_pb))[
, c("sample_id", "cell_type", "n_cells")])| sample_id | cell_type | n_cells | |
|---|---|---|---|
| pb_00001 | s1 | T | 2 |
| pb_00002 | s2 | B | 1 |
| pb_00003 | s2 | T | 1 |
| sample_id | donor_id | condition | cell_type | observed | n_cells |
|---|---|---|---|---|---|
| s1 | d1 | ctrl | B | FALSE | NA |
| s1 | d1 | ctrl | T | TRUE | 2 |
| s2 | d2 | stim | B | TRUE | 1 |
| s2 | d2 | stim | T | TRUE | 1 |
| s3 | d3 | ctrl | B | FALSE | NA |
| s3 | d3 | ctrl | T | FALSE | NA |
The three pseudobulk columns correspond to the observed
sample/cell-type combinations. The coverage table has six rows:
registered samples crossed with B and T.
Missing combinations have observed = FALSE and
n_cells = NA, with no extra count columns.
The simulated counts below demonstrate registered coverage and
paired-design checks. Each column of the
SummarizedExperiment is one observed sample and cell type.
Donor d4 lacks a B-cell pseudobulk under
stim. The full sample registry makes that absence
visible.
set.seed(1103)
sample_table <- data.frame(sample_id = paste0("s", seq_len(8)), donor_id = rep(paste0("d",
seq_len(4)), each = 2), condition = rep(c("ctrl", "stim"), 4))
source_rows <- rbind(transform(sample_table, cell_type = "T"), transform(sample_table[seq_len(7),
], cell_type = "B"))
counts <- matrix(rnbinom(250 * nrow(source_rows), mu = 25, size = 5), nrow = 250,
dimnames = list(paste0("g", seq_len(250)), paste0("column", seq_len(nrow(source_rows)))))
source_rows$n_cells <- 30L
se <- SummarizedExperiment::SummarizedExperiment(assays = list(counts = counts),
colData = S4Vectors::DataFrame(source_rows))
pb <- scDonorAudit::preparePseudobulk(se, "sample_id", "donor_id", "condition",
"cell_type", sample_table = sample_table, n_cells = "n_cells")
contrast <- c(numerator = "stim", denominator = "ctrl")The named contrast fixes the sign: positive log2 fold-change means
higher expression in stim than ctrl. The count
assay must contain raw, non-negative integer counts. The package checks
numeric validity; users must confirm that the assay contains
unnormalized counts. Factor coding is fixed inside the design, so a
user’s global options("contrasts") does not change the
named effect’s direction or scale.
Supported assays are in-memory numeric base matrices or numeric
Matrix objects. Logical/pattern matrices and
DelayedArray or disk-backed assays are unsupported. Sparse
cell counts remain sparse during aggregation; only the smaller cell-type
pseudobulk matrices are converted for edgeR fitting. For inputs that
cannot fit in memory, aggregate raw counts upstream and use the
preaggregated SummarizedExperiment entry point, retaining
the full sample registry and n_cells. Output memory also
grows with genes and planned fits.
For either pairing policy, the registry must contain one sample per
condition for every donor. complete_pairs handles
incomplete cell-type eligibility; the registry itself must remain
paired.
The default paired policy is strict. An incomplete eligible pair blocks that cell type without affecting other cell types.
audit <- scDonorAudit::auditDesign(pb, "paired", contrast)
as.data.frame(audit[["spec"]])
#> cell_type plan_status n_planned n_samples n_donors baseline_status
#> 1 B not_created NA 0 0 INCOMPLETE_PAIR
#> 2 T created 4 8 4 OK
subset(as.data.frame(audit[["sample_membership"]]), cell_type == "B" &
reason_code != "OK")
#> cell_type sample_id donor_id condition observed n_cells in_baseline
#> 1 B s1 d1 ctrl TRUE 30 FALSE
#> 2 B s2 d1 stim TRUE 30 FALSE
#> 3 B s3 d2 ctrl TRUE 30 FALSE
#> 4 B s4 d2 stim TRUE 30 FALSE
#> 5 B s5 d3 ctrl TRUE 30 FALSE
#> 6 B s6 d3 stim TRUE 30 FALSE
#> 7 B s7 d4 ctrl TRUE 30 FALSE
#> 8 B s8 d4 stim FALSE NA FALSE
#> reason_code
#> 1 STRICT_PAIR_BLOCK
#> 2 STRICT_PAIR_BLOCK
#> 3 STRICT_PAIR_BLOCK
#> 4 STRICT_PAIR_BLOCK
#> 5 STRICT_PAIR_BLOCK
#> 6 STRICT_PAIR_BLOCK
#> 7 STRICT_PAIR_BLOCK
#> 8 MISSING_CELL_TYPEplan_status = "not_created" means a deletion plan was
never established for the blocked cell type. It does not mean that all
donor deletions succeeded or that the result was stable.
result <- scDonorAudit::assessDonorInfluence(pb, "paired", contrast)
subset(as.data.frame(result[["fits"]]), select = c(cell_type, run_id, omitted_donor,
execution_status, reason_code))
#> cell_type run_id omitted_donor execution_status reason_code
#> 1 B baseline <NA> skipped INCOMPLETE_PAIR
#> 2 T baseline <NA> completed OK
#> 3 T omit_001 d1 completed OK
#> 4 T omit_002 d2 completed OK
#> 5 T omit_003 d3 completed OK
#> 6 T omit_004 d4 completed OK
head(as.data.frame(scDonorAudit::summarizeInfluence(result, effect_threshold = 0.5))[,
c("cell_type", "gene_id", "n_planned", "n_effect_valid", "max_abs_delta_observed")])
#> cell_type gene_id n_planned n_effect_valid max_abs_delta_observed
#> 1 T g1 4 4 0.2878988
#> 2 T g10 4 4 0.6444123
#> 3 T g100 4 4 0.4444523
#> 4 T g101 4 4 0.4274512
#> 5 T g102 4 4 0.5059251
#> 6 T g103 4 4 0.9924754The summary’s n_planned includes all eligible donor
deletions, even failed or skipped refits. Read the maximum effect change
alongside valid-effect coverage: it is the maximum among refits with
comparable effects. Missing effects remain missing, rather than becoming
zero changes or non-significant tests. Adjusted p values use a fixed
baseline gene family within each cell type; they do not control error
across cell types or deletion runs.
The analysis first fixes the eligible samples and complete-pair policy, then plans all whole-donor deletions. Genes retained by the full-data filter remain the comparison family in each refit. The named contrast and covariate meanings also stay fixed. Each successful deletion refit re-estimates library-size normalization and the edgeR model on its remaining samples.
Read each gene in this order: is its baseline effect valid; how many
deletions were planned; how many effects are comparable; then how large
were the observed changes? n_sign_reversal counts even tiny
crossings of zero. With a positive effect_threshold,
n_material_reversal requires both effects to cross that
threshold in opposite directions, and baseline_near_zero
flags a baseline for which a raw sign change is especially easy to
overread. The minimum and maximum deletion effects are an
observed range, not a confidence interval. An
unavailable effect may reflect design failure, backend failure, or a
gene-level status; it is not evidence of stability.
Effect changes describe sensitivity to donor omission. A large change can reflect donor heterogeneity, limited precision, or other data properties; it does not establish that a donor is invalid or should be excluded. The p values come from the specified edgeR model, not a test that a donor is aberrant.
| Component | Meaning |
|---|---|
spec |
Per-cell-type plan status and number of planned donor omissions. |
sample_membership /
coverage |
Registered sample-by-cell-type combinations and the actual baseline inclusion decisions. |
fits / fit_samples |
Baseline and deletion ledger.
design_reason_code preserves the original design diagnosis;
execution_status, reason_code, and
stage describe the later execution outcome. The separate
fit_samples table aligns sample IDs with library sizes and
normalization factors for each completed run. |
gene_filter / results |
Fixed baseline gene family and gene-by-run effects, tests, validity flags, and gene status codes. |
issues |
Design problems and execution warnings or errors; a run can have both a backend warning and an error. |
config / provenance |
Requested design, contrast and thresholds; source preparation, package/R/edgeR versions, and the actual named contrast. |
The complete field/type/status dictionary is installed with the package:
system.file("OUTPUT_SCHEMA.md", package = "scDonorAudit")
#> [1] "/tmp/RtmpFjCgU6/Rinstd3777b167b0/scDonorAudit/OUTPUT_SCHEMA.md"It also documents the 16 typed summary columns, which remain available in zero-row summaries when all genes are filtered or all cell types are blocked.
Design rank deficiency can coexist with an estimable target contrast.
A rank problem is still a reason to skip fitting and is not relabeled as
target non-estimability. When a baseline cannot run, the planned
deletion rows remain in fits;
design_reason_code keeps their independent audit results
even if their execution outcome becomes
BASELINE_UNAVAILABLE.
To analyze the B-cell data from complete pairs, request
the alternative policy explicitly. This changes the analysis cohort, so
report the excluded pair when presenting the result.
The coverage plot shows included and excluded sample/cell-type combinations. Gene plots use the explicit, stable gene IDs supplied by the user.
Before interpreting a gene, inspect result[["fits"]],
result[["sample_membership"]], and the number of valid
deletion runs. A failed fit or unestimable contrast limits the observed
sensitivity analysis. Grey influence tiles mean the effect is
unavailable; consult the fit ledger and gene status for the specific
cause.
This independent-donor example has eight donors, a missing
B pseudobulk for d8, and a batch category
observed only in d1. The baseline models are estimable.
Omitting d1 leaves the frozen batch column all zero,
causing rank deficiency; the target contrast can still be estimable. The
failed design is recorded as a skipped run. Other deletions provide
valid effects.
set.seed(995201)
mixed_registry <- data.frame(sample_id = paste0("s", seq_len(8)), donor_id = paste0("d",
seq_len(8)), condition = rep(c("ctrl", "stim"), each = 4), batch = c("special",
rep("common", 7)))
mixed_rows <- rbind(transform(mixed_registry, cell_type = "T"), transform(mixed_registry[seq_len(7),
], cell_type = "B"))
mixed_rows$n_cells <- 30L
mixed_counts <- matrix(rnbinom(250 * nrow(mixed_rows), mu = 25, size = 5),
nrow = 250, dimnames = list(paste0("g", seq_len(250)), NULL))
mixed_source <- SummarizedExperiment::SummarizedExperiment(assays = list(counts = mixed_counts),
colData = S4Vectors::DataFrame(mixed_rows))
mixed_pb <- scDonorAudit::preparePseudobulk(mixed_source, "sample_id",
"donor_id", "condition", "cell_type", sample_vars = "batch", sample_table = mixed_registry,
n_cells = "n_cells")
mixed_result <- scDonorAudit::assessDonorInfluence(mixed_pb, "independent",
contrast, covariates = "batch")
mixed_fits <- as.data.frame(mixed_result[["fits"]])
stopifnot(all(mixed_fits$execution_status[mixed_fits$omitted_donor %in%
"d1"] == "skipped"), all(mixed_fits$reason_code[mixed_fits$omitted_donor %in%
"d1"] == "DESIGN_RANK_DEFICIENT"))
knitr::kable(subset(as.data.frame(mixed_result[["sample_membership"]]),
!in_baseline))| cell_type | sample_id | donor_id | condition | observed | n_cells | in_baseline | reason_code | |
|---|---|---|---|---|---|---|---|---|
| 8 | B | s8 | d8 | stim | FALSE | NA | FALSE | MISSING_CELL_TYPE |
knitr::kable(mixed_fits[, c("cell_type", "run_id", "omitted_donor", "execution_status",
"design_reason_code", "target_estimable")])| cell_type | run_id | omitted_donor | execution_status | design_reason_code | target_estimable |
|---|---|---|---|---|---|
| B | baseline | NA | completed | OK | TRUE |
| B | omit_001 | d1 | skipped | DESIGN_RANK_DEFICIENT | TRUE |
| B | omit_002 | d2 | completed | OK | TRUE |
| B | omit_003 | d3 | completed | OK | TRUE |
| B | omit_004 | d4 | completed | OK | TRUE |
| B | omit_005 | d5 | completed | OK | TRUE |
| B | omit_006 | d6 | completed | OK | TRUE |
| B | omit_007 | d7 | completed | OK | TRUE |
| T | baseline | NA | completed | OK | TRUE |
| T | omit_001 | d1 | skipped | DESIGN_RANK_DEFICIENT | TRUE |
| T | omit_002 | d2 | completed | OK | TRUE |
| T | omit_003 | d3 | completed | OK | TRUE |
| T | omit_004 | d4 | completed | OK | TRUE |
| T | omit_005 | d5 | completed | OK | TRUE |
| T | omit_006 | d6 | completed | OK | TRUE |
| T | omit_007 | d7 | completed | OK | TRUE |
| T | omit_008 | d8 | completed | OK | TRUE |
mixed_summary <- as.data.frame(scDonorAudit::summarizeInfluence(mixed_result))
knitr::kable(subset(mixed_summary, gene_id == "g1", select = c(cell_type,
gene_id, n_planned, n_effect_valid, complete_effect_coverage, max_abs_delta_observed)),
digits = 3)| cell_type | gene_id | n_planned | n_effect_valid | complete_effect_coverage | max_abs_delta_observed | |
|---|---|---|---|---|---|---|
| 1 | B | g1 | 7 | 6 | FALSE | 0.224 |
| 251 | T | g1 | 8 | 7 | FALSE | 0.276 |
scDonorAudit::plotInfluence(mixed_result, "influence", cell_type = "T",
gene_id = c("g1", "g2", "g3"))For T, eight omissions are planned but only seven can
supply effects; for B, seven are planned and six can supply
effects. The missing d8/B combination was never eligible
for that cell type’s deletion plan. Omitting d1 remains in
each plan and appears as unavailable in the plot. The observed maximum
covers the valid deletions; the unavailable deletion has no measured
effect change. Backend failures use
execution_status="failed",
reason_code="BACKEND_ERROR" and an error event; the same
planned-denominator and missing-effect rules apply.
The package includes a compact pseudobulk derived from the public Crowell19 mouse
cortex dataset (ExperimentHub EH3297). Raw counts from
1,740 Astrocyte nuclei were summed within each of eight mice, retaining
all 11,076 genes. Four mice received LPS and four received Vehicle. The
original single-nucleus object is available through ExperimentHub and is
not bundled here. The included extdata/README-crowell19.md
records the transformation, attribution, and source’s CC BY 4.0
license.
count_file <- system.file("extdata", "crowell19_astrocyte_counts.csv",
package = "scDonorAudit")
sample_file <- system.file("extdata", "crowell19_astrocyte_samples.csv",
package = "scDonorAudit")
real_counts <- as.matrix(utils::read.csv(count_file, row.names = 1, check.names = FALSE))
real_samples <- utils::read.csv(sample_file)
real_source <- SummarizedExperiment::SummarizedExperiment(assays = list(counts = real_counts),
colData = S4Vectors::DataFrame(real_samples))
real_registry <- real_samples[, c("sample_id", "donor_id", "condition")]
real_pb <- scDonorAudit::preparePseudobulk(real_source, "sample_id", "donor_id",
"condition", "cell_type", sample_table = real_registry, n_cells = "n_cells")
real_contrast <- c(numerator = "LPS", denominator = "Vehicle")
real_audit <- scDonorAudit::auditDesign(real_pb, "independent", real_contrast,
min_cells = 20L)
as.data.frame(real_audit[["spec"]])
#> cell_type plan_status n_planned n_samples n_donors baseline_status
#> 1 Astrocytes created 8 8 8 OK
knitr::kable(as.data.frame(real_audit[["sample_membership"]])[, c("sample_id",
"donor_id", "condition", "n_cells", "in_baseline")])| sample_id | donor_id | condition | n_cells | in_baseline |
|---|---|---|---|---|
| LC016 | LC016 | Vehicle | 151 | TRUE |
| LC017 | LC017 | LPS | 105 | TRUE |
| LC019 | LC019 | Vehicle | 231 | TRUE |
| LC020 | LC020 | LPS | 299 | TRUE |
| LC022 | LC022 | Vehicle | 270 | TRUE |
| LC023 | LC023 | LPS | 209 | TRUE |
| LC025 | LC025 | Vehicle | 114 | TRUE |
| LC026 | LC026 | LPS | 361 | TRUE |
real_result <- scDonorAudit::assessDonorInfluence(real_pb, "independent",
real_contrast, min_cells = 20L)
table(as.data.frame(real_result[["fits"]])$execution_status)
#>
#> completed
#> 9
knitr::kable(as.data.frame(real_result[["fits"]])[, c("run_id", "omitted_donor",
"execution_status", "design_reason_code", "reason_code")])| run_id | omitted_donor | execution_status | design_reason_code | reason_code |
|---|---|---|---|---|
| baseline | NA | completed | OK | OK |
| omit_001 | LC016 | completed | OK | OK |
| omit_002 | LC017 | completed | OK | OK |
| omit_003 | LC019 | completed | OK | OK |
| omit_004 | LC020 | completed | OK | OK |
| omit_005 | LC022 | completed | OK | OK |
| omit_006 | LC023 | completed | OK | OK |
| omit_007 | LC025 | completed | OK | OK |
| omit_008 | LC026 | completed | OK | OK |
head(as.data.frame(scDonorAudit::summarizeInfluence(real_result))[, c("gene_id",
"n_planned", "n_effect_valid", "max_abs_delta_observed")])
#> gene_id n_planned n_effect_valid max_abs_delta_observed
#> 1 ENSMUSG00000000001 8 8 0.2306918
#> 2 ENSMUSG00000000056 8 8 0.1135039
#> 3 ENSMUSG00000000078 8 8 0.1688491
#> 4 ENSMUSG00000000085 8 8 0.1130049
#> 5 ENSMUSG00000000088 8 8 0.2293102
#> 6 ENSMUSG00000000127 8 8 0.2121334Eight mice offer limited replication, especially after one is omitted. Read the observed changes with this sample size and the specified model in mind.
The fit ledger above has one full-data fit and eight donor-deletion fits. To illustrate what the summary adds, consider genes with valid effects in all eight deletions and an absolute full-data log2 fold-change of at least 1. Order these genes by their largest observed absolute change, breaking ties by gene ID, and show the genes at the 10th and 90th percentile ranks. This rule selects examples with different sensitivity for interpreting the audit output.
real_summary <- as.data.frame(scDonorAudit::summarizeInfluence(real_result))
candidates <- real_summary[real_summary$complete_effect_coverage & is.finite(real_summary$baseline_logFC) &
abs(real_summary$baseline_logFC) >= 1 & is.finite(real_summary$max_abs_delta_observed),
]
candidates <- candidates[order(candidates$max_abs_delta_observed, candidates$gene_id),
]
if (nrow(candidates) < 2L) {
stop("The Crowell19 example requires at least two genes to illustrate ",
"different levels of donor-deletion sensitivity; the stated ",
"selection rule returned ", nrow(candidates), ".")
}
chosen <- candidates[c(ceiling(0.1 * nrow(candidates)), ceiling(0.9 * nrow(candidates))),
]
chosen$illustration <- c("lower change", "higher change")
knitr::kable(chosen[, c("illustration", "gene_id", "baseline_logFC", "n_planned",
"n_effect_valid", "max_abs_delta_observed")], digits = 3)| illustration | gene_id | baseline_logFC | n_planned | n_effect_valid | max_abs_delta_observed | |
|---|---|---|---|---|---|---|
| 3832 | lower change | ENSMUSG00000035086 | 1.094 | 8 | 8 | 0.163 |
| 6228 | higher change | ENSMUSG00000085245 | -2.102 | 8 | 8 | 0.713 |
For the higher-change example, inspect every deletion alongside the
maximum. A missing or failed deletion would appear in the fit ledger and
reduce n_effect_valid.
real_se <- real_result[["results"]][[chosen$cell_type[2L]]]
gene_row <- match(chosen$gene_id[2L], rownames(real_se))
refits <- as.data.frame(SummarizedExperiment::colData(real_se))
refits$logFC <- as.numeric(SummarizedExperiment::assay(real_se, "logFC")[gene_row,
])
refits$change_from_full <- refits$logFC - refits$logFC[1L]
largest <- which.max(abs(refits$change_from_full[-1L])) + 1L
knitr::kable(refits[, c("run_id", "omitted_donor", "logFC", "change_from_full")],
digits = 3)| run_id | omitted_donor | logFC | change_from_full | |
|---|---|---|---|---|
| baseline | baseline | NA | -2.102 | 0.000 |
| omit_001 | omit_001 | LC016 | -2.335 | -0.233 |
| omit_002 | omit_002 | LC017 | -2.272 | -0.169 |
| omit_003 | omit_003 | LC019 | -1.390 | 0.713 |
| omit_004 | omit_004 | LC020 | -1.918 | 0.185 |
| omit_005 | omit_005 | LC022 | -2.189 | -0.086 |
| omit_006 | omit_006 | LC023 | -1.922 | 0.180 |
| omit_007 | omit_007 | LC025 | -2.321 | -0.219 |
| omit_008 | omit_008 | LC026 | -2.334 | -0.231 |
In this dataset, 504 genes meet the stated selection rule. For ENSMUSG00000085245, the full-data LPS-versus-Vehicle log2 fold-change is -2.102; omitting LC019 changes it by 0.713 to -1.390. Of the 8 planned deletion effects, 8 are valid here. By comparison, the lower-change example ENSMUSG00000035086 has a maximum absolute change of 0.163. These observations describe sensitivity to donor omission for the specified model, these eight mice, and the selected genes. The two genes illustrate the stated percentile ranks; conclusions about other genes or datasets require their own analysis.
extdata/README-crowell19.md.Use citation("edgeR") for citations matching the
installed backend version and citation("scDonorAudit") for
the package citation. The help-example data are synthetic; their
generation is documented in extdata/README-example.md.
sessionInfo()
#> R version 4.6.1 (2026-06-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 26.04.1 LTS
#>
#> Matrix products: default
#> BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.32.so; LAPACK version 3.12.0
#>
#> 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
#>
#> time zone: Etc/UTC
#> tzcode source: system (glibc)
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] BiocStyle_2.41.0
#>
#> loaded via a namespace (and not attached):
#> [1] sass_0.4.10 generics_0.1.4
#> [3] SparseArray_1.13.4 lattice_0.23-1
#> [5] digest_0.6.39 RColorBrewer_1.1-3
#> [7] evaluate_1.0.5 grid_4.6.1
#> [9] fastmap_1.2.0 jsonlite_2.0.0
#> [11] Matrix_1.7-6 limma_3.99.0
#> [13] BiocManager_1.30.27 SingleCellExperiment_1.35.2
#> [15] scales_1.4.0 jquerylib_0.1.4
#> [17] abind_1.4-8 cli_3.6.6
#> [19] rlang_1.3.0 XVector_0.53.0
#> [21] Biobase_2.73.2 withr_3.0.3
#> [23] cachem_1.1.0 DelayedArray_0.39.8
#> [25] yaml_2.3.12 otel_0.2.0
#> [27] S4Arrays_1.13.2 tools_4.6.1
#> [29] ggplot2_4.0.3 locfit_1.5-9.12
#> [31] SummarizedExperiment_1.43.0 BiocGenerics_0.59.12
#> [33] vctrs_0.7.3 buildtools_1.0.0
#> [35] R6_2.6.1 matrixStats_1.5.0
#> [37] stats4_4.6.1 lifecycle_1.0.5
#> [39] Seqinfo_1.3.2 S4Vectors_0.51.10
#> [41] edgeR_4.99.6 IRanges_2.47.5
#> [43] bslib_0.12.0 gtable_0.3.6
#> [45] glue_1.8.1 statmod_1.5.2
#> [47] xfun_0.61 GenomicRanges_1.65.4
#> [49] sys_3.4.3 MatrixGenerics_1.25.0
#> [51] knitr_1.52 farver_2.1.2
#> [53] htmltools_0.5.9 labeling_0.4.3
#> [55] rmarkdown_2.32 maketools_1.3.2
#> [57] scDonorAudit_0.99.9 compiler_4.6.1
#> [59] S7_0.2.2