Skip to content

Restructure the documentation as a short book, with one home for each fact - #70

Merged
parashardhapola merged 1 commit into
masterfrom
39c-docs-structure-and-language
Oct 28, 2026
Merged

parashardhapola merged 1 commit into
masterfrom
39c-docs-structure-and-language

Conversation

@parashardhapola

Copy link
Copy Markdown
Member

The explanatory pages said the same things several times, from different angles: once in
the chapters, again in the module docstrings that the API reference renders, again in the
README's status table and known limits, and in four lists of the same capabilities. This
pull request gives every fact one home and links to it from everywhere else. "How the
statistics work" becomes "Understand the methods", a short book of 17 pages that reads from
the first chapter to the last, each chapter placing itself after the one before and leading
to the next. The guides stay practical: each opens with the default for its question, with
the alternatives under Choices and links to the chapters for the statistics. The copy rules
reach every page, the README, the docstrings, the messages and the figure labels, and every
guide was executed again.

The new structure

Overview
Start here                 Installation; Quick start; Why pseudobulk
Build and share cubes      Pool cells into pseudobulk profiles; Save, share and trace cubes;
                           Export a cube from another tool; Work with AnnData and MuData
Compare conditions         Normalization, filtering and sample quality; Genes that change in
                           each cell type; Repeated measures and donors; Cell-type
                           composition; Gene sets and pathways; Pathway and regulator
                           activity; Bulk RNA-seq
Combine studies            Align and combine studies; Replication across studies
Understand the methods     Choosing a method
  Foundations              Pseudobulk profiles and cubes; Designs, linear models and
                           contrasts; Empirical Bayes moderation; Multiple testing
  Differential expression  Normalization and filtering; limma-voom and limma-trend; edgeR
                           and DESeq2; Mixed models and dream
  Composition tests
  Gene sets and pathways   Gene-set tests; Over-representation and GSEA; Pathway scores per
                           sample; Activities from networks
  Several studies          Aligning studies; Meta-analysis; Replication
Reference                  Validation; Glossary; Files and datasets (Cube files and provenance
                           records; Datasets); Citing and related software (How to cite;
                           Bibliography; Related software); Report a bug
API reference              its own section, its pages in eight groups that follow the book
  • One home for each kind of fact. The chapters explain the methods: the model, the
    default and why, the choices, and the limits and deliberate differences from the
    reference tools in plain words. The Validation page holds the agreement with each tool,
    with the numbers the scripts print, and the timings. Docstrings define behaviour. The
    guides show tasks on data. "Choosing a method" lists every question with its default,
    the alternatives and when to choose them, and the one list of what is not implemented
    yet. Every other place gives a clause and a link to the section that states the fact,
    by its label.
  • Every chapter has the same shape: an opening that places it and names its default
    and its guide, the sections of the model, then "Defaults and choices", "Limits and
    differences" and "Checks and further reading", which links the Validation sections it
    rests on and the works it draws on. A test checks the parts a machine can check.
  • The guides open with their defaults. Genes that change in each cell type opens with
    limma_voom, with limma-trend and the adjustment across cell types under Choices;
    Repeated measures and donors with dream_voom, with the fixed donor effect and the
    profiles pooled by donor under Choices; Gene sets and pathways with camera on a
    collection and GSEA, with one set chosen in advance and over-representation under
    Choices; Bulk RNA-seq with limma_voom on a cube of one group, with edgeR's
    quasi-likelihood F-tests and DESeq2 under Choices; Align and combine studies with
    meta_analysis, with the joint model of stacked studies under Choices; Replication
    across studies with sumrank, with the replication tables and combined p-values under
    Choices. Each guide's first paragraph reads well on its own, since it becomes the page's
    description in search results and link previews.
  • Module docstrings keep a summary, the address of their chapter, the rules their
    functions share, the conventions that match the reference tool, and their references.
    The models, steps and comparisons moved to the chapters. Where a module listed again
    the differences from its reference tool that its chapter states, the list became the
    address of the chapter's "Limits and differences", and each difference that changes
    what a function returns or refuses is stated in that function's docstring. Without
    their references, module docstrings went from about 43,500 words to 25,800, and
    function and class docstrings from about 100,800 to 107,700, as they took over the
    rules that help() needs. A function that sent readers to its module's documentation
    now gives the rules itself or its chapter's address, and every site address in a
    docstring is checked to resolve.
  • Chapters and docstrings no longer repeat each other. Measured as the words of the
    chapters that appear word for word in a docstring, in a run of eight words or more,
    the repetition fell from 3,000 of the chapters' 15,400 words on master (19.5%) to 143
    of 36,200 (0.4%), all of them in formulas that a chapter shares with the function
    that computes them.
  • Reference is for looking things up. The capability matrix is folded into the
    Validation page's summary, one row per method with its functions, the reference and
    version, the tier with the bounds its checks use, and its section. Validation's
    sections are grouped as the API reference is, each opening with its script's checks,
    run time and memory and a link to the chapter section it checks, and the scaling rules
    of its comparisons are stated once. "Cube files and provenance records" is now the
    complete specification of the file, and a test checks that it names every key that
    CytePool writes. The glossary gives one sentence per term with a link to the section
    that defines it, and gains 29 terms. The bibliography is grouped by the chapters that
    cite each work, and How to cite gains rows for every function that had none.

Pages added, renamed and removed

Addresses are below https://docs.nygen.io/CytePool/.

Page Before After
limma-voom and limma-trend statistics/precision-weights ("Precision weights") statistics/limma-voom
Normalization and filtering statistics/normalization-and-filtering (new)
Over-representation and GSEA statistics/over-representation-and-gsea (new)
Pathway scores per sample statistics/pathway-scores (new)
Activities from networks statistics/activities (new)
Aligning studies statistics/aligning-studies (new)
Replication statistics/replication (new)
Capability matrix reference/capabilities removed: its rows are the Validation summary, its list the map's "Not implemented yet"
Literature reference/literature removed: each line moved to the chapter whose question it informs, to Why pseudobulk or to the map; the Bibliography keeps every work

Kept addresses with new titles or narrower content: statistics/cubes ("Pseudobulk
profiles and cubes", was "The cube and its additive layers"), statistics/empirical-bayes
("Empirical Bayes moderation", was "... and TREAT"), statistics/count-models ("edgeR and
DESeq2", was "Count models"), statistics/mixed-models ("Mixed models and dream", was
"Mixed models, REML and dream"), statistics/gene-set-tests (camera, fry, roast, mroast and
romer; the other methods have chapters of their own), statistics/meta-analysis (SumRank,
held-out prediction, combined p-values and pi1 moved to Replication, and pi0 to Multiple
testing) and guides/combine-studies ("Align and combine studies"). The section "Other
designs and data" is gone: Bulk RNA-seq joined Compare conditions, and the guides on
several studies have their own section.

Statements that were wrong, and what is right

Moving each fact to its home brought out places that disagreed. Each was settled against
the code, the output of the validation scripts or the reference tool's documentation, and
by running the tool where its documentation leaves the answer open.

Cubes and files:

  • The provenance record of an operation names its input's fingerprint only where that is
    known without reading lazy layers; for a lazy cube whose groups were not read whole the
    input is null (the code of the provenance record).
  • A chunk of a cube file holds every sample only where it stays below 256 MiB; the writer
    splits samples otherwise. The Cube files page also missed the operation with_columns
    and the imports from AnnData and MuData, and now the test that every key CytePool
    writes is on the page keeps it complete.
  • The principal components and the MDS plot make the coordinate of largest absolute
    value of each component positive, not the largest coordinate (the code of
    principal_components and plot_mds; the docstrings and the Validation page said
    otherwise).
  • The cost of hashing a cube was given three ways; one measured note remains, in
    Cube.fingerprint (about 0.4 s per 100 million values with SHA instructions, 1.3 s
    without), and the other docstrings cite it.
  • In the glossary, a group is the cube's first axis (layers are groups x samples x
    features), a fingerprint covers the normalization too, and a library size is the total
    count of a cell or of a profile.

Designs, linear models and moderation:

  • Where the residual degrees of freedom differ, robust moderation uses the likelihood
    estimator, which down-weights outlying genes and Winsorizes nothing; Winsorizing is the
    method of moments' (the code of fit_f_distribution_likelihood).
  • The likelihood estimator is the default when the positive residual degrees of freedom
    differ; genes without residual degrees of freedom do not count (the code, and the
    Validation page, which said "when the residual df differ").
  • limma's approximate contrast standard errors were "off by a factor of two or more" in
    one place and "0.56 to 2.55 times" in another; the measured range remains (the
    validation output).
  • Gene by gene is the default of limma's voomWithQualityWeights only; arrayWeights
    chooses REML for data without prior weights or missing values (limma's help pages).
    limma_voom follows limma's workflow with two additions, the minimum of 10 cells per
    sample and exact contrast standard errors.
  • The composition chapter said that the moderated F-tests' degrees of freedom are not
    capped, and linked a section that gave only the t-tests' cap; the empirical Bayes
    chapter now says that the F-tests' are not capped, as in limma (the code, which follows
    a run of limma).
  • quasi_likelihood_f_test, like DESeq2's likelihood ratio test, tests several
    coefficients at once; the map offered only the latter.

Models of counts:

  • edgeR's fitted values that tend to zero stop between about 1e-8 and 1e-7, where the
    pages said near 1e-7 in one place and near 1e-8 in another (edgeR 4.4.0's glmFit
    and glmQLFit run on the validation's data: 2.6e-8 to 9.8e-8).
  • edgeR's one-way fits agree with CytePool's to the last bit, and only its fits of other
    designs stop short of the maximum (the validation output).
  • DESeq2's default for minReplicatesForReplace is 7, not Inf: the comparisons are with
    DESeq(..., minReplicatesForReplace = Inf), which gives DESeq2's defaults where no row
    of the design occurs seven times or more.
  • The map listed options that fit_quasi_likelihood and fit_deseq2 do not take; it is
    rewritten from their signatures. Where edgeR 4.4.0 alone was checked, the pages name
    that version rather than "edgeR 4".

Mixed models and dream:

  • ModeratedFixedEffectTests said that dream's moderated tests use Satterthwaite's
    degrees of freedom; dream_voom uses Kenward-Roger's below 20 samples by default
    (the code, and variancePartition's help page, now quoted once, with what running it on
    20 samples showed).
  • Kenward-Roger's cost was given as 0.7 s and 13 s, numbers that no script printed;
    validation/mixed_models.py --benchmark now times both methods: on 20,000 genes on one
    thread, Kenward-Roger's tests took 0.15 s for 18 samples and 8.5 to 9.7 s for 120,
    against 0.06 to 0.08 s for Satterthwaite's.
  • The two treatments of weights in Kenward-Roger's method differ by measured amounts,
    which replace "tens of percent" (the validation output).
  • Variance partitioning fits by maximum likelihood, which variancePartition's help page
    reports gives unbiased fractions where REML can show substantial bias; what REML does
    is now worded one way. A mixed model's residual degrees of freedom are approximate,
    between n - p and n minus the rank of the fixed and random effects.
  • The chapter's checks called Satterthwaite's F-tests exact; their degrees of freedom and
    p-values agree with lmerTest to 1e-5, since lmerTest differentiates numerically (the
    validation output).

Gene sets and pathways:

  • roast's smallest p-value is 1/(B + 1) for the two-sided and mixed alternatives and
    1/(2B + 1) for up or down: 0.0005 and 0.00025 with 1,999 rotations, where the guide said
    "about one over its number of rotations" (run with 99 and 1,999 rotations).
  • The differences from decoupler were listed three, four and five ways; the chapter
    lists them once, and the Validation page adds that decoupler reports MLM's p-values
    unadjusted under the name of its adjusted p-values.
  • The gene-set tests take fixed effects only, without a block correlation, and refuse
    results of dream_voom; one place said "any design and contrast".
  • GSEA's default of 15 to 500 genes per set is the GSEA program's; gsea keeps every size,
    as fgsea does. PLAGE's sign is a deliberate difference: CytePool's scores rise with the
    mean standardized expression of a set's genes, and GSVA's follow its linear algebra
    library, so they are compared up to sign.
  • With values stored as integers and 200 samples or more, kernel="auto" uses no kernel,
    as GSVA documents, where GSVA 2.0.0 stops with an error; the README had said "as GSVA
    does". PROGENy's gene symbols are those of progeny 1.28.0 from Bioconductor 3.20, and
    the version of GSVA whose licence the module names is 2.0.0.
  • Activities said to test activities with fit_linear_model and a design built from the
    profiles; limma with the cube's design does it, as for pathway scores.
  • Over-representation analysis on a limma fit tests the genes up and down, as kegga
    does; the list of genes in either direction is CytePool's own.
  • The activity module said that every method's p-values are adjusted "as decoupler
    adjusts them"; decoupler leaves MLM's unadjusted (the comparison with decoupler 2.2.0
    on the Validation page).

Several studies:

  • pi1 counts the p-values at least lambda, as qvalue's pi0est does, not those above it.
  • SumRank sums every study by default, so that a gene must rank high in every study, not
    "in most"; its p-values come from permutations only when n_permutations is set.
  • Nakatsuka et al. found that over 85% of the genes that one dataset of Alzheimer's
    disease called differentially expressed were not reproduced in any of 16 others, where
    some places said "not found in the next". The arm of their comparison that they call
    inverse-variance is a random-effects meta-analysis, while "inverse-variance" elsewhere
    names the fixed-effect model.
  • metafor's REML can stop at the smaller of two maxima, or set tau² to zero where the
    likelihood is higher at zero than at the maximum it found.
  • A two-sided p-value below 2.2e-308 loses digits when halved, which combine_p_values
    now says; SumRank takes the results of the studies, not the stacked cube.
  • One study's p-value is its own combined p-value by every method but the logit
    ("mudholkar_george"), whose t distribution approximates the sum of logistic
    variables also for one study; the chapter said that one study gives its own p-value
    by every method (run: one p-value of 0.03 combines to 0.0289 by the logit, and to 0.03
    by the six other methods).

Across the site:

  • The lists of what is not implemented disagreed between the README, the capability
    matrix, the Overview and the chapters; the map now holds the one list.
  • The capability matrix called checks of 1e-5 and 1e-2 "close (1e-6)"; each row of the
    Validation summary now gives its own bound. GSEA's runs with their own random sets and
    SumRank's permutations are calibrated too, not only the rotation tests, and the tests of
    scores and activities with a design (limma) are checked by
    tests/test_score_tests.py against the functions this page validates.
  • Links that missed now land where they say: Why pseudobulk's link to the guides, the
    guide's pointer to the cell types that are not fitted, the glossary's entry for CPM, and
    Design.check and model_matrix, whose rules the designs page of the API reference now
    renders.

The README

The README is GitHub's and PyPI's front page, and it now says what CytePool does and
links to the site (about 570 words, from about 6,100): a first paragraph that leads with
the task; the status; how to install; an example that pools cells, tests a stimulation
in every cell type and tests gene sets, checked by running it; the questions CytePool
answers, each with its default function, linked to "Choosing a method" for the
alternatives and to Validation for the agreement with each tool; where the documentation
is; the development checks; and the licence. Its links are absolute, since PyPI does not
resolve relative ones. The status table, the list of planned work and the known limits
moved to their homes: the Validation summary, the map, the chapters and the docstrings.

Messages unified

  • Flags say " must be True or False, got ", where some modules said "must be a
    bool" (and "trend must be True, False or one covariate value per feature").
  • Lists in messages and reprs go through one function: up to 8 items, as "a, b, c and 3
    more", or with "; " between items that hold commas, as "a; b; and 3 more"; the design
    checks, the model matrix and the reprs of gene sets and networks built their own.
  • A bound of (0, 1) is "must be in (0, 1)" everywhere; a TypeError says what type was
    expected, such as "an integer", and ends "got ", and the range is the
    ValueError's, such as "at least 1".

The tests that match these messages changed with them.

Other changes

  • The copy rules are in AGENTS.md ("Writing"), with the homes of each kind of fact
    ("Documentation"). A test scans the pages, the README, AGENTS.md and every string and
    comment of the package for em dashes, en dashes used as punctuation, centred dots
    between words, stock metaphors and all-caps headings; others were read for, such as "the
    X behind it", and reworded.
  • New tests: every chapter has the shape of the book and no chapter is left a draft; the
    map links every chapter and leads to the first; labels are unique and unlike the
    headings of other pages; every site address in a docstring, the README and the API
    pages resolves; the Validation summary agrees with its sections; the Cube files page
    names every key CytePool writes; and every repository path that THIRD_PARTY_NOTICES.md
    names exists, which would have caught the three files the renames left behind there.
  • validation/mixed_models.py --benchmark times Kenward-Roger's tests as well as
    Satterthwaite's, at 120 samples and in a study of 18, and the Validation page's Timings
    give its output.
  • cytepool.plotting's docstrings say which tool each figure follows and how the signs
    of components are chosen, and list each reference once.

Checks

  • uv run pytest: 5,183 passed, 18 skipped (the zstd tests, which need Python 3.14).
  • uv run ruff check cytepool tests validation docs and uv run ruff format --check cytepool tests validation docs: clean.
  • uv run mypy cytepool: no issues.
  • make -C docs api and make -C docs html: built without warnings.
  • Every guide, the Quick start and Why pseudobulk were executed again, and
    make -C docs check-outputs on every page reports its outputs current. The printed
    results are those of the previous run, but for one message of Design.check that had
    changed; the figures are unchanged, and their descriptions were checked against them,
    six of them corrected. The README's example was run on the simulated study of the
    guides.
  • validation/mixed_models.py --benchmark and validation/quasi_likelihood.py were run
    again for the changes to them; the second printed the same 223 checks as before.

@parashardhapola
parashardhapola force-pushed the 39c-docs-structure-and-language branch from 7709c69 to ce134e6 Compare October 28, 2026 05:15
@parashardhapola
parashardhapola merged commit 7220ca3 into master Oct 28, 2026
6 checks passed
@parashardhapola
parashardhapola deleted the 39c-docs-structure-and-language branch October 28, 2026 05:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant