Repository navigation
Add guides to combine studies and measure their replication - #64
Merged
Merged
Conversation
parashardhapola
force-pushed
the
53-release-workflow
branch
from
October 26, 2026 23:10
9948d00 to
cb0d3f3
Compare
Two short guides on six simulated cohorts with the differences real ones have (repeated donor ids, genes a cohort did not measure, cell types under their own names): "Combine studies" aligns the cohorts in a CubeCollection, analyses each, and compares meta-analysis with one joint model; "Replication across studies" compares each cohort's calls, combined p-values, partial conjunctions, SumRank and held-out prediction against the truth.
parashardhapola
marked this pull request as ready for review
October 26, 2026 23:14
parashardhapola
force-pushed
the
38b-combine-studies-guides
branch
from
October 26, 2026 23:14
5f5ed6d to
b6f80ac
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
Combine several cohorts of one disease, and find out which of their changes replicate:
two short guides under "Other designs and data", executed notebooks with the figures
users know from metafor and from the paper that introduced SumRank. "Combine studies"
aligns six cohorts' cubes, analyses each on its own and combines them by
meta-analysis, or stacks them into one cube for a joint model. "Replication across
studies" measures how well the cohorts replicate each other and which genes
replicate, with combined p-values, partial conjunction tests, π₁, SumRank and
prediction in cohorts left out. The cohorts are simulated, so every page says which
genes each method finds rightly: a new dataset of
docs/datasets.py,simulated_cohorts, six case-control cohorts with changes shared by all and changesof one cohort alone.
Changes
docs/source/guides/combine_studies.ipynb, "Combine studies" (8 code cells, 4figures, 6 s): the six cohort cubes as their sources wrote them;
CubeCollectionwith repeated donor ids, genes that two cohorts did not measure (one cube lacks them,
one writes zeros and marks them in
measured) and cell types aligned by CellOntology id, with
group_mappingrolling a cohort's naive and memory CD4 T cells upto their parent term;
limma_voomon everyaligned_cube, withplot_significant_genesfor each cohort;align_study_resultstelling genes notmeasured from genes filtered;
differential_expression_meta_analysiswithplot_volcano;plot_forestof a gene changed in every cohort and of a genechanged in one cohort only, with the fixed-effect estimate beside the random-effects
one;
CubeCollection.stackandselectwith a joint~ study + conditionmodel;the four approaches against the truth; Choices.
docs/source/guides/replication_across_studies.ipynb, "Replication across studies" (7code cells, 4 figures, 19 s, about half of it the permutations): every cohort's calls
against the truth;
differential_expression_replicationwithplot_replicated_genes(genes found in k cohorts);
plot_study_agreementfor π₁, with π₀ at λ = 0.5 and why,and the sign agreement;
combine_p_values(Fisher) andpartial_conjunction(u = 2and 3) against the replication result's weighted Stouffer p-values and the
meta-analysis; SumRank of all six cohorts with
plot_sumrank; the best 4 of 6 summed,with Irwin-Hall p-values and with 30 permutations;
held_out_predictionfor severalproportions, with
plot_held_out_auc; Choices.docs/datasets.py:simulated_cohorts()and its registry entry (a cube file percohort and the true log2 fold changes, seed 20261028, fingerprints recorded).
docs/source/myst.yml: both pages under "Other designs and data", after BulkRNA-seq.
docs/coverage_exemptions.toml:plot_forest,CubeCollection.select,plot_volcanoandplot_significant_genesleave[planned]; the pages use them..github/workflows/docs.yml: both pages added tocheck-outputs; they need nodownload.
docs/source/reference/datasets.md: a row forsimulated_cohorts.docs/source/index.md: the questions on replication and on pooled studies link theguides.
docs/source/statistics/meta_analysis.mdandstatistics/cubes.md: one sentenceeach linking the guides.
tests/test_documentation.py:simulated_cohortsadded to the registered datasets.Design notes
cells. "Combine studies" answers how to get one result from several cohorts;
"Replication across studies" answers which results hold up. Both read the same
cohorts, so the cohorts are a registered dataset rather than a simulation repeated in
each page.
types, 2,000 genes, pseudobulk counts drawn directly. The disease changes 100
monocyte genes in every cohort (80 alike, 20 by amounts that vary between cohorts),
each case by a gamma-distributed activity of mean 1, so that cases differ in severity
and the AUCs of cohorts left out stay below 1; each cohort also changes 10 monocyte
genes of its own in every case, as a confounder of that cohort would. Effect sizes
and donor variation make single cohorts find 17 to 62 of the 100 shared genes, and
most genes are found by one cohort only, as the literature reports for real studies.
The cohorts differ as real ones do: donor ids repeat, cohort_3's cube lacks 200
genes, cohort_5 wrote zeros for 150 genes it did not measure and marks them in
measured, cell types carry each cohort's own names with Cell Ontology ids,cohort_4 annotated its CD4 T cells as naive and memory cells, and cohort_6 has no NK
cells. The ontology ids, and CL:0000624 as the parent of the two CD4 subsets, were
checked in the EBI Ontology Lookup Service.
finds 80 of the 100 shared monocyte genes and none of the 60 single-cohort genes;
the fixed effect 91 and 23; the stacked joint model 76 and 18, of the 82 shared genes
it can test. Fisher's combination calls 36 single-cohort genes, weighted Stouffer 19,
the partial conjunction tests none. SumRank of all six finds 55 shared genes and no
single-cohort gene; summing the best 4 of 6, Irwin-Hall p-values call 16 unchanged
genes (the function warns, and the page prints the warning), 30 permutations none.
Mean AUC in cohorts left out: SumRank 0.92, meta-analysis 0.87.
the few dozen p-values of the genes one cohort finds (π₁ of 0 for some pairs; qvalue
2.38.0, run as a black box on the same 41 p-values, gives the same), so the page
uses
null_proportion={"lambdas": 0.5}and prints both ranges.valid, and the Manhattan plot spreads as the authors' does; permutation p-values tie
at their floor, and their labels pile up (see below). Permutations appear where they
are needed, for the best 4 of 6, with 30 permutations, about 7 s of the page.
figures to the width of the text: every line of code and printed output is at most
80 characters, two-panel figures are stacked, and the reprs with longer first lines
are left out in favour of short summaries.
85% of the DEGs of one Alzheimer's dataset not found in any of 16 others; every study
summed for Parkinson's disease, Huntington's disease and schizophrenia (6, 4 and 3
studies), 65% and 55% for Alzheimer's disease (17) and COVID-19 (16), chosen by
cross-validation; for Alzheimer's disease, mean AUCs in datasets left out of 0.78 for
SumRank and for merged datasets, 0.74 for the inverse-variance meta-analysis and
0.67 for single datasets, from Table 1.
the first page prints each cohort's names of its cell types, and the cohorts' names
under CL:0000624.
Checks
make -C docs html(Node.js 24): builds; both pages render with their API links,citations and the backlinks of the API reference. Checked by eye in a headless
browser at a width of 1300 pixels.
make -C docs check-outputsfor every page the workflow lists (quickstart,differential_expression, guides/export_cubes, guides/bulk_rna_seq,
guides/combine_studies, guides/replication_across_studies): outputs current.
uv run pytest tests/test_documentation.py: 76 passed.docs/coverage_report.py: 251 public names, 181 used or explained, 53 exempt, 17planned; no problems.
ruff checkandruff format --checkoncytepool tests validation docs, andmypy cytepool: clean.docs/datasets.py verify simulated_cohorts simulated_bulk simulated_pbmc: asregistered.
4,926 tests of the stack's last pull request; the pages that CI checks are current
when their datasets are made from an empty cache.
Bugs found
None in the library's results. One stale docstring: the module description of
cytepool.replicationstill lists held-out prediction under "Not done", althoughcytepool.held_out_predictionimplements it:python -c "import cytepool.replication as r; print(r.__doc__.split('Not done:')[1][:300])".Awkward APIs, for the API and language pull request
collection.studies, takesaligned_cube, builds aDesignfrom its samples andruns
limma_voom, collecting a dict. A collection-level call (orlimma_voomaccepting a collection and one formula) would remove it.
cell_ontology_idnames the groups
CL:0001054and so on in every table and figure, with no way togive them readable names (an agreed label column, or a
group_namesargument).differential_expression_meta_analysis(results)takes results alone, whilealign_study_results,differential_expression_replicationanddifferential_expression_sumranktake(collection, results, group, contrast), andheld_out_prediction(collection, results, contrast, variable, case, control)asksfor
variable,caseandcontrol, which the designs and the contrast alreadyimply.
differential_expression_replicationestimates π₀ by qvalue's smoother by default, which gives 0 or 1 on the few dozen
genes a study finds; λ = 0.5 by default, or a warning below some number of genes,
would serve users better.
plot_sumranklabels. With permutation p-values many genes tie at the smallestp-value; the labels go to the first of them in alphabetical order and overlap, and
close neighbours overlap with Irwin-Hall p-values too. Breaking ties by the
statistic and moving labels apart, as
plot_volcanodoes, would fix it.plot_forestneeds the studies' results passed again besides themeta-analysis, and marks a study that did not measure the gene "not tested", like
one whose filter left it out.
align_study_results(...)["p_value"].reshape(n_studies, n_features); a method thatreturns the studies x genes arrays would help.
(
proportion_of_studies=4 / 6); a number of studies summed would be plainer.aligned_cuberenames groups throughCube.regroup,which returns integer layers as int64 even for a group that is only renamed, so
aligned and stacked cubes of uint32 counts take twice the memory.
DifferentialExpressionResult,MetaAnalysisResult,ReplicationResult(one lineof about 170 characters) and
SumRankResult, and SumRank's warning, run past the 80characters that the site's output cells show; the result reprs also print feature
counts without thousands separators, where the cube and collection reprs use them.
Merging with the other guide pull requests
THIRD_PARTY_NOTICES.mdsays the documentation reads "two simulated" datasets; withsimulated_cohortsit is three. The file is left as it is here, because the guidesof "Compare conditions" change the lines next to it; the merged file should say
"three simulated".
docs/coverage_exemptions.toml: the guides of "Compare conditions" also removeplot_volcanoandplot_significant_genesfrom[planned]; the merged file hasneither, nor
plot_forestandCubeCollection.select..github/workflows/docs.yml: both pull requests append pages to the samecheck-outputsline; the merged line lists all of them.