Repository navigation
Add the quick start, Why pseudobulk and the guides to cubes - #66
Merged
Merged
Conversation
parashardhapola
force-pushed
the
55-threads-and-garbage-collection
branch
from
October 26, 2026 23:18
45b1bb4 to
9bb9150
Compare
parashardhapola
changed the base branch from
55-threads-and-garbage-collection
to
master
October 26, 2026 23:23
The quick start goes from cells to a cube to differential expression per cell type and a result traced back to its cube; it absorbs the longer tutorial of the same path. "Why pseudobulk" shows, on a split of control samples, how testing cells instead of samples makes false calls. New guides pool cells into profiles (with the dot plot and the PCA), save, share and trace cubes, and work with AnnData and MuData; the quality guide gains the PCA plot.
parashardhapola
marked this pull request as ready for review
October 26, 2026 23:23
parashardhapola
force-pushed
the
38b-start-here-and-cube-guides
branch
from
October 26, 2026 23:23
ac08aa1 to
35ebb8a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The first pages a new user meets, and the ones on cubes, as executed notebooks with
figures from
cytepool.plotting. The quick start goes from cells to a cube, tolimma-voom in every cell type, to a result traced back to its cube; "Why pseudobulk"
tests a comparison in which nothing differs both per cell and per sample, and gives the
record of benchmarks and replication as the papers report it; three guides show how to
pool cells, save, share and trace cubes, and exchange them with AnnData and MuData. The
normalization and quality guide gains DESeq2's PCA plot beside the MDS plot.
Changes
Pages (all read the
simulated_pbmcdataset, whichdocs/datasets.pysimulates, unlessnoted; "checked in CI" means the page is in the
check-outputslist of.github/workflows/docs.yml):quickstart.ipynb, "Quick start" (rewritten):from_cellson the cells,limma_voomwith a paired design, one cell type's table, the cube saved with
write_cube, openedwith
read_cubeand matched to the result's record by its fingerprint, and theprofiles a cell type's fit left out, with the reason. Figures: the cells pooled into
each sample and cell type (
plot_cell_numbers), the genes up and down in every celltype (
plot_significant_genes), the volcano plot of the monocytes (plot_volcano).Checked in CI.
why_pseudobulk.ipynb, "Why pseudobulk" (new): the 12 control samples split at randominto two groups of six, every gene tested by Welch's t-test on the cells (computed
from the cube's sums with
Cube.collapse_samples,Cube.mean,Cube.varianceandt_sf) and bylimma_voomon the samples; the false calls of each in every celltype; why the test of cells fails, on the gene it ranks first; the benchmarks
(Squair et al. 2021, Zimmerman et al. 2021, Murphy and Skene 2022, Junttila et al.
2022, Crowell et al. 2020) and replication studies (Murphy et al. 2023, Nakatsuka et
al. 2025, Garg et al. 2021); Choices. Figures: the p-value histograms of the CD4 T
cells tested both ways (
plot_p_values); the mean of each donor's cells against eachgroup's mean and its interval from the test of cells. Checked in CI.
guides/pool_cells.ipynb, "Pool cells into pseudobulk profiles" (new): sample andgroup tables (a lineage column), the five layers, the dot plot of three markers of
every cell type, the stimulation's effect on one response gene from the samples
pooled by condition (
Cube.collapse_samples,Cube.fraction_detected,Cube.mean,Cube.variance), the PCA of the monocyte samples, the rare dendritic cells mergedinto myeloid cells (
Table.filter,Cube.regroup); Choices. Figures:plot_dotplotscaled per gene,plot_pca(PC1 and PC2 by condition, PC2 and PC3 bydonor),
plot_cell_numbersof the merged cube. Checked in CI.guides/save_and_trace_cubes.ipynb, "Save, share and trace cubes" (new): the cubefile opened lazily, its layers' sizes against the cells', and its fingerprint from
the stored digests; a derived cube saved and its
history(); a copy changed withzarr-python, caught by
verify_fingerprint; alimma_voomresult traced to the fileby fingerprint, its T-cell profiles read back by id, the library sizes and the
average log2 CPM of the table recomputed from them; what CytePool can and cannot
resolve; Choices. Figure: the traced gene's log2 CPM in every sample, the two samples
of each donor joined. Checked in CI.
guides/anndata_and_mudata.ipynb, "Work with AnnData and MuData" (new): an AnnData ofcells pooled by
from_cells;to_anndataand itsobs, back through an.h5adfilewith the same fingerprint;
to_mudataand back; the calls for the layouts ofscanpy, decoupler and LIANA, as validated, shown as code that is not run (the
documentation environment has none of the three); Choices. No figure: its outputs are
the AnnData and MuData summaries and the layout of
obs. Checked in CI.guides/export_cubes.ipynb, "Export a cube from another tool" (prose only): anopening for its reader, a link to the new guide for what the file and its records
allow, and a Choices section (arrays for lazy layers, layers, the source record, the
check against
from_cellson part of the data). Its code and outputs are unchanged.Checked in CI, as before.
guides/normalization_and_quality.ipynb, "Normalization, filtering and samplequality": a cell after the MDS plot with
principal_componentsandplot_pcaof theCD14+ monocytes, the genes with the largest loadings on PC1 (interferon-stimulated
genes) and PC2 (HBB), and how MDS and PCA differ. Executed again (the Kang data, a
download, so not in the CI list); every output but the new one is unchanged.
guides/bulk_rna_seq.ipynb: the sentence that sent readers to the bulk quick startnow points to a new last section, "The same steps in R", the table of CytePool and R
functions that the bulk quick start ended with, with edgeR's and DESeq2's tests
added. Prose only.
guides/genes_per_cell_type.ipynb: the link to "From cells to results" for a celltype that cannot be fitted now goes to "Designs that cannot answer the question".
Prose only.
differential_expression.ipynb, "From cells to results": removed (below).index.md: the line of links separated by centred dots is a sentence, with the quickstart and Why pseudobulk in place of From cells to results.
statistics/cubes.md,statistics/choosing_a_method.md,reference/cube_files.md:one sentence each linking to the new pages.
Navigation (
docs/source/myst.yml): Start here is Installation, Quick start, Whypseudobulk; Build and share cubes is Pool cells into pseudobulk profiles; Save, share
and trace cubes; Export a cube from another tool; Work with AnnData and MuData.
Coverage (
docs/coverage_exemptions.toml):plot_dotplot,plot_pca,Table.select,Table.filterandTable.with_columnsleave[planned].normalization.library_sizes,which only the bulk quick start used, is used by the trace guide. The report: 255
public names, 198 used or explained in a page, 53 exempt, 4 planned (the dispersion
plots of the bulk guide;
plot_forestandCubeCollection.selectof the guide ofseveral studies).
Workflow:
check-outputsrunsquickstart why_pseudobulk guides/pool_cells guides/save_and_trace_cubes guides/export_cubes guides/anndata_and_mudata guides/bulk_rna_seq guides/composition;differential_expressionleaves it.Design notes
cells, cube,
limma_voomin every cell type, provenance, on two simulations, and theagreed structure has one tutorial for it; two pages would say the same thing. The
quick start now runs on the shared simulated PBMC study, as the cube guides do, in
four cells. What the longer tutorial added has a home where a reader looks for it:
tracing a result, and what CytePool can and cannot resolve, in "Save, share and trace
cubes"; the options for pseudobulk data (
min_counts, quality weights, weights fromcell numbers, adjustment across cell types) in "Genes that change in each cell type"
and "Normalization, filtering and sample quality"; cell types that cannot be fitted
and contrasts that cannot be estimated in "Designs, linear models and contrasts". The
bulk quick start's table of R functions moved to the bulk guide, which already ran
its analysis. The page's URL,
/differential-expression, goes; nothing is released.reader develops a tool that holds cells and its task is to build a cube from its own
sums, record its source and check the export; the new guide's reader analyses a
cube and checks and traces it. One page for both would be twice as long. The overlap
is removed: the export page links to the new guide for what files, fingerprints and
records allow, and gained a Choices section.
the documentation's datasets has patients with an outcome (the 8 donors of Kang et
al. are all patients, with none recorded), and a simulation would show only what it
plants. CytePool also lacks two of its steps: a table of features per patient across
cell types (cell type proportions, scores of pathways and regulators per cell type,
pooled expression), and an unsupervised grouping or a test of such features against
a clinical outcome. A page for 0.2: a cohort with an outcome (Perez et al. 2022, 162
lupus cases and 99 controls, as the Literature page lists it), the features built per
patient from a cube (
propeller's proportions,gsvaorulmper cell type,principal_componentsof the pooled profiles), mapped withplot_pcaand testedagainst the outcome.
monocytes are a case for caution: the stimulation raises the 40 genes of the
response program, and so lowers the share of every other gene. Library sizes alone
leave the monocytes' unchanged genes a log2 fold change of -0.084 on average, and
TMM -0.015, which, with 24 samples of up to 444 cells, still makes 18 of the 157
monocyte calls of the quick start's design false (11%; 686 of 825 without TMM). The
quick start therefore shows no table against the truth; a page that adds one should
know it.
pooled sums. On the CD4 T cells of the page's split it gives the t-statistics of
scanpy 1.12.4's
rank_genes_groupswith its default method (t-test) to 6e-8relative, and its p-values to 6e-12, run on the cells as a check outside the page.
and full texts in Europe PMC: eighteen datasets with matched bulk RNA-seq and
hundreds of genes without biological differences (Squair et al. 2021); highly
inflated type 1 error rates, pseudobulk conservative relative to mixed models
(Zimmerman et al. 2021); the Matthews correlation coefficient, pseudobulk best
(Murphy and Skene 2022); 18 methods, pseudobulk generally best (Junttila et al.
2022); 26 genes against 14,274, 549 times fewer (Murphy et al. 2023); over 85% of the
Alzheimer's disease genes of one dataset not reproduced in any of the 16 others, with
pseudobulk DESeq2 (Nakatsuka et al. 2025); 8 of 20 results confirmed (Garg et al.
2021). Every citation is a key of
references.bibalready.no stock metaphors; figure titles and axis labels set in the pages follow them too.
tests/test_documentation.pynameddifferential_expression.ipynbinthree examples; they now name
quickstart.ipynbandwhy_pseudobulk.ipynb. The checkof the API pages' backlinks reads those of
limma_voom, whose first use is the quickstart, instead of those of
voom, which the quick start no longer imports.Bugs found
plot_dotplot's colour bar label ran into the key of the dot sizes whenever thelabel was longer than the colour bar. The PCA and dot plots pull request below this
one now writes the label above the bar, as scanpy does, with a test that the label,
the bar and the key do not overlap; the pooling guide draws the dot plot as it is.
Awkward APIs, for the API and language pull request
Cube.collapse_samplestakes only thename of one: Why pseudobulk pools an arbitrary set of samples with
cube.select(samples=mask).collapse_samples("condition"), which works only becausethose samples share a condition. Labels per sample, or a
Cubemethod that addssample columns, would do it directly.
plot_dotplotdraws, the fractions and means pooled oversamples; users pool with
collapse_samplesand divide, or sum layers by hand.plot_dotplotreturns only the main Axes: the colour bar isax.figure.axes[-1], andthere is no
vminorvmax(scanpy has both) to give dot plots of two conditions onecolour scale. Its
standard_scale="gene"is scanpy's"var".next(fit for fit in result.provenance["groups"] if fit["group_id"] == group), andits fitted samples with
result.samples.filter((result.samples["group_id"] == group) & result.samples["used"]);accessors by cell type would read better.
log2((counts + 0.5) / (library_size * norm_factor + 1) * 1e6), to draw a gene.cube.n_cells[:, cube.groups["group_id"].tolist().index(group)].plot_p_valuesand the other scatter plots put their legend outside the axes, so twopanels side by side are narrow; an option to place it inside would help.
to_anndataleavesXempty, and every user of scanpy's tools then writesadata.X = adata.layers["counts"].copy().PrincipalComponents.genesholds positions; gene names needcube.features["symbol"][pca.genes].Cube.history()prints a "References" line of about 250 characters, which overflowsthe output boxes of the pages.
Validation
No library code changes, so no validation script changed; the checks of the pages:
make -C docs html(Node.js 24): the API reference with warnings as errors, and thesite, built.
make -C docs check-outputsfor every page of the CI list and for the other pagesthis change executed or edited,
guides/normalization_and_qualityandguides/genes_per_cell_type(with the Kang download): outputs current, 10 pages. Thesame with numba compiling for a generic processor, OpenBLAS's Sandybridge kernels and
4 threads: outputs current, so what the pages print does not depend on the
processor's rounding. The pages print at most three significant digits of fitted
values.
uv run python docs/coverage_report.py: no name uncovered or listed wrongly.uv run ruff checkandruff format --checkoncytepool tests validation docs,notebooks included: clean.
page says.
Tests
tests/test_documentation.py: 85 passed, with the three examples that named theremoved page now naming existing ones. The full suite on top of the pull requests
before it: 4,931 passed, 18 skipped, also with bounds checks and with the rounding
changed; Python 3.14: 4,952 passed; Python 3.15: 4,846 passed, 107 skipped. The ten
pages that CI checks are current when their datasets are made from an empty cache, as
on CI, and the five guides that read downloaded data are current too.
The check of saved outputs now leaves out a version written after "CytePool " in
both texts (
docs/execute_notebooks.py): the save guide prints a cube's history,which names the version that built the dataset, and on CI every dataset is built
again by the version being checked, so the saved text could never match. A test
covers it, beside the replacement of the version that ran the page.