ggnomics 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. ggnomics-0.1.1/.gitignore +74 -0
  2. ggnomics-0.1.1/API_STABILIZATION_PLAN.md +363 -0
  3. ggnomics-0.1.1/AUTHORS.md +27 -0
  4. ggnomics-0.1.1/CHANGELOG.md +73 -0
  5. ggnomics-0.1.1/CODE_OF_CONDUCT.md +84 -0
  6. ggnomics-0.1.1/CONTRIBUTING.md +332 -0
  7. ggnomics-0.1.1/LICENSE +21 -0
  8. ggnomics-0.1.1/MANIFEST.in +39 -0
  9. ggnomics-0.1.1/PKG-INFO +302 -0
  10. ggnomics-0.1.1/README.md +203 -0
  11. ggnomics-0.1.1/examples/00_mock_data.py +449 -0
  12. ggnomics-0.1.1/ggnomics/__init__.py +175 -0
  13. ggnomics-0.1.1/ggnomics/_backends/__init__.py +35 -0
  14. ggnomics-0.1.1/ggnomics/_backends/_bioc.py +54 -0
  15. ggnomics-0.1.1/ggnomics/_backends/anndata.py +632 -0
  16. ggnomics-0.1.1/ggnomics/_backends/mudata.py +206 -0
  17. ggnomics-0.1.1/ggnomics/_backends/singlecellexperiment.py +689 -0
  18. ggnomics-0.1.1/ggnomics/_backends/summarizedexperiment.py +454 -0
  19. ggnomics-0.1.1/ggnomics/_compose.py +153 -0
  20. ggnomics-0.1.1/ggnomics/_utils.py +231 -0
  21. ggnomics-0.1.1/ggnomics/_version.py +24 -0
  22. ggnomics-0.1.1/ggnomics/abundance.py +104 -0
  23. ggnomics-0.1.1/ggnomics/bulk/__init__.py +1 -0
  24. ggnomics-0.1.1/ggnomics/bulk/pca.py +131 -0
  25. ggnomics-0.1.1/ggnomics/coldata.py +223 -0
  26. ggnomics-0.1.1/ggnomics/composition.py +45 -0
  27. ggnomics-0.1.1/ggnomics/de_plots.py +363 -0
  28. ggnomics-0.1.1/ggnomics/dotplot.py +114 -0
  29. ggnomics-0.1.1/ggnomics/expression.py +469 -0
  30. ggnomics-0.1.1/ggnomics/heatmap.py +77 -0
  31. ggnomics-0.1.1/ggnomics/highest_exprs.py +247 -0
  32. ggnomics-0.1.1/ggnomics/multimodal.py +323 -0
  33. ggnomics-0.1.1/ggnomics/pairs.py +168 -0
  34. ggnomics-0.1.1/ggnomics/palettes.py +392 -0
  35. ggnomics-0.1.1/ggnomics/pca/__init__.py +2 -0
  36. ggnomics-0.1.1/ggnomics/pca/methods.py +50 -0
  37. ggnomics-0.1.1/ggnomics/pca/result.py +34 -0
  38. ggnomics-0.1.1/ggnomics/pseudobulk.py +477 -0
  39. ggnomics-0.1.1/ggnomics/qc.py +59 -0
  40. ggnomics-0.1.1/ggnomics/reduced_dim.py +19 -0
  41. ggnomics-0.1.1/ggnomics/repertoire.py +489 -0
  42. ggnomics-0.1.1/ggnomics/ridge.py +81 -0
  43. ggnomics-0.1.1/ggnomics/scatter.py +437 -0
  44. ggnomics-0.1.1/ggnomics/signif/__init__.py +13 -0
  45. ggnomics-0.1.1/ggnomics/signif/_brackets.py +136 -0
  46. ggnomics-0.1.1/ggnomics/signif/_geom.py +327 -0
  47. ggnomics-0.1.1/ggnomics/signif/_stats.py +130 -0
  48. ggnomics-0.1.1/ggnomics/singlecell/__init__.py +2 -0
  49. ggnomics-0.1.1/ggnomics/singlecell/pca.py +113 -0
  50. ggnomics-0.1.1/ggnomics/singlecell/umap.py +64 -0
  51. ggnomics-0.1.1/ggnomics/stats_plots.py +692 -0
  52. ggnomics-0.1.1/ggnomics/upset/README.md +59 -0
  53. ggnomics-0.1.1/ggnomics/upset/THIRD-PARTY-NOTICES-upset.md +30 -0
  54. ggnomics-0.1.1/ggnomics/upset/__init__.py +127 -0
  55. ggnomics-0.1.1/ggnomics/upset/_types.py +185 -0
  56. ggnomics-0.1.1/ggnomics/upset/_utils_query.py +67 -0
  57. ggnomics-0.1.1/ggnomics/upset/_utils_theme.py +22 -0
  58. ggnomics-0.1.1/ggnomics/upset/alignment.py +229 -0
  59. ggnomics-0.1.1/ggnomics/upset/annotations.py +387 -0
  60. ggnomics-0.1.1/ggnomics/upset/examples.py +22 -0
  61. ggnomics-0.1.1/ggnomics/upset/intersections.py +569 -0
  62. ggnomics-0.1.1/ggnomics/upset/matrix.py +189 -0
  63. ggnomics-0.1.1/ggnomics/upset/modes.py +129 -0
  64. ggnomics-0.1.1/ggnomics/upset/plot.py +267 -0
  65. ggnomics-0.1.1/ggnomics/upset/queries.py +40 -0
  66. ggnomics-0.1.1/ggnomics/upset/set_size.py +88 -0
  67. ggnomics-0.1.1/ggnomics/upset/stripes.py +40 -0
  68. ggnomics-0.1.1/ggnomics/upset/testing.py +213 -0
  69. ggnomics-0.1.1/ggnomics/upset/themes.py +80 -0
  70. ggnomics-0.1.1/ggnomics/upset/venn.py +372 -0
  71. ggnomics-0.1.1/ggnomics/violin.py +159 -0
  72. ggnomics-0.1.1/ggnomics/volcano.py +71 -0
  73. ggnomics-0.1.1/ggnomics.egg-info/PKG-INFO +302 -0
  74. ggnomics-0.1.1/ggnomics.egg-info/SOURCES.txt +108 -0
  75. ggnomics-0.1.1/ggnomics.egg-info/dependency_links.txt +1 -0
  76. ggnomics-0.1.1/ggnomics.egg-info/requires.txt +85 -0
  77. ggnomics-0.1.1/ggnomics.egg-info/scm_file_list.json +265 -0
  78. ggnomics-0.1.1/ggnomics.egg-info/scm_version.json +8 -0
  79. ggnomics-0.1.1/ggnomics.egg-info/top_level.txt +1 -0
  80. ggnomics-0.1.1/great-docs.yml +847 -0
  81. ggnomics-0.1.1/pyproject.toml +270 -0
  82. ggnomics-0.1.1/setup.cfg +4 -0
  83. ggnomics-0.1.1/tests/conftest.py +128 -0
  84. ggnomics-0.1.1/tests/test_abundance.py +52 -0
  85. ggnomics-0.1.1/tests/test_backends.py +224 -0
  86. ggnomics-0.1.1/tests/test_coldata.py +82 -0
  87. ggnomics-0.1.1/tests/test_compose.py +196 -0
  88. ggnomics-0.1.1/tests/test_de_plots.py +145 -0
  89. ggnomics-0.1.1/tests/test_default_palette.py +230 -0
  90. ggnomics-0.1.1/tests/test_edge_cases.py +110 -0
  91. ggnomics-0.1.1/tests/test_expression.py +236 -0
  92. ggnomics-0.1.1/tests/test_heatmap.py +48 -0
  93. ggnomics-0.1.1/tests/test_highest_exprs.py +106 -0
  94. ggnomics-0.1.1/tests/test_multimodal.py +319 -0
  95. ggnomics-0.1.1/tests/test_pairs.py +56 -0
  96. ggnomics-0.1.1/tests/test_palettes.py +161 -0
  97. ggnomics-0.1.1/tests/test_plotting.py +74 -0
  98. ggnomics-0.1.1/tests/test_pseudobulk.py +264 -0
  99. ggnomics-0.1.1/tests/test_public_api_contract.py +205 -0
  100. ggnomics-0.1.1/tests/test_reduced_dim.py +143 -0
  101. ggnomics-0.1.1/tests/test_repertoire.py +195 -0
  102. ggnomics-0.1.1/tests/test_scatter.py +282 -0
  103. ggnomics-0.1.1/tests/test_signif.py +533 -0
  104. ggnomics-0.1.1/tests/test_stats_plots.py +295 -0
  105. ggnomics-0.1.1/tests/test_upset_api.py +142 -0
  106. ggnomics-0.1.1/tests/test_upset_components.py +419 -0
  107. ggnomics-0.1.1/tests/test_upset_intersections.py +435 -0
  108. ggnomics-0.1.1/tests/test_upset_plot.py +270 -0
  109. ggnomics-0.1.1/tests/test_upset_statistics.py +275 -0
  110. ggnomics-0.1.1/tests/test_upset_venn.py +262 -0
@@ -0,0 +1,74 @@
1
+ # --- Python ---------------------------------------------------------------
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+
7
+ # --- Packaging / build ----------------------------------------------------
8
+ # Written by setuptools_scm at build time from the git tag. Generated, never
9
+ # edited or committed.
10
+ ggnomics/_version.py
11
+ build/
12
+ dist/
13
+ sdist/
14
+ wheels/
15
+ *.egg-info/
16
+ *.egg
17
+ *.whl
18
+ *.tar.gz
19
+ MANIFEST
20
+
21
+ # --- Testing / coverage ---------------------------------------------------
22
+ .pytest_cache/
23
+ .cache/
24
+ .coverage
25
+ .coverage.*
26
+ coverage.xml
27
+ htmlcov/
28
+ nosetests.xml
29
+
30
+ # --- Linting / type checking ----------------------------------------------
31
+ .ruff_cache/
32
+ .mypy_cache/
33
+
34
+ # --- tox / virtual environments -------------------------------------------
35
+ .tox/
36
+ .nox/
37
+ .venv/
38
+ venv/
39
+ env/
40
+ ENV/
41
+
42
+ # --- Documentation --------------------------------------------------------
43
+ # Legacy MkDocs output (MkDocs is no longer the site renderer).
44
+ /site/
45
+
46
+ # Retired documentation systems (Sphinx + quartodoc/Quarto), superseded by
47
+ # great-docs. Kept on disk, prefixed with __old_, and excluded from version
48
+ # control rather than deleted outright.
49
+ _old_docs/
50
+ __old_docs/
51
+ __old_vignettes/
52
+ __old_readthedocs.yml
53
+ .github/workflows/__old_docs.yml
54
+ .github/workflows/__old_publish-docs.yml
55
+ .github/workflows/__old_readme_plots.yml
56
+ tools/__old_build_tutorial_notebooks.py
57
+
58
+ # --- Editors and OS -------------------------------------------------------
59
+ .vscode/
60
+ .idea/
61
+ *.swp
62
+ *.swo
63
+ *~
64
+ .DS_Store
65
+ Thumbs.db
66
+
67
+ # Great Docs build directory (ephemeral, do not commit)
68
+ great-docs/
69
+
70
+ # Great Docs versioned-build artifacts
71
+ _great_docs_build/
72
+ .great-docs-build/
73
+ .great-docs-cache/
74
+ .great-docs/
@@ -0,0 +1,363 @@
1
+ # ggnomics API stabilization plan
2
+
3
+ *Status: discussion draft. Written from a full read-through of `ggnomics/` (~11.6k lines)
4
+ on 2026-08-21, ahead of committing to a stable public API.*
5
+
6
+ ## 1. Why this document
7
+
8
+ `ggnomics` already has a working, well-tested architecture: a `singledispatch` generic
9
+ per plot family, a canonical `pandas.DataFrame` implementation, and lazily-registered
10
+ adapters for `AnnData`, `SingleCellExperiment`, `SummarizedExperiment`, and `MuData`
11
+ (`tests/test_public_api_contract.py` pins this contract — return types, non-mutation,
12
+ error messages). That pattern is the right foundation and does not need to change.
13
+
14
+ What's *not* settled is the **naming and grouping of the ~50 public functions built on
15
+ top of it** — which verb/noun a user reaches for, which names are aliases of which,
16
+ and where the boundary sits between "plot", "compute", and "legacy". This document is
17
+ an inventory of the current state, the specific inconsistencies that will bite users
18
+ once the API is declared stable, and concrete proposals for resolving them.
19
+
20
+ Nothing here is a decision — several sections end with an explicit choice for you to
21
+ make.
22
+
23
+ ## 2. The architecture that already works (keep this)
24
+
25
+ Every modern plot function follows the same three-to-four layer shape:
26
+
27
+ 1. **Private plotnine builder** — pure geometry from an already-tidy `DataFrame`, no
28
+ data extraction (e.g. `_scatter_ggplot`, `_build_adt_qc_plot`,
29
+ `_build_highest_exprs_plot`). Independently testable, reused across containers.
30
+ 2. **Public `@singledispatch` generic**, registered for `pd.DataFrame` — this is the
31
+ canonical implementation and "the interface". It validates columns, builds the tidy
32
+ frame, and calls layer 1. Its docstring is the one source of truth for the
33
+ function's contract.
34
+ 3. **Container adapters** in `ggnomics/_backends/{anndata,singlecellexperiment,summarizedexperiment,mudata}.py`
35
+ — extract a `DataFrame` slice from the container (sparse-aware, only densifying the
36
+ requested columns) and re-dispatch to layer 2. Registered only if the optional
37
+ package is importable (`_backends/__init__.py`).
38
+ 4. **Convenience wrappers** on top of a layer-2/3 generic, for a fixed common case
39
+ (e.g. `plot_umap(data, ...)` = `plot_embedding(data, dimred="X_umap", ...)`).
40
+
41
+ This gives every function in the "modern" set: one docstring, one error-message
42
+ convention (`TypeError` naming the type + remedy, `KeyError` listing available
43
+ columns), one non-mutation guarantee, and free multi-container support. **This is the
44
+ part of the API that should be declared stable as-is** — the problem is entirely in
45
+ the layer above it (which names exist, what they're called, what they alias).
46
+
47
+ ## 3. Two generations of API currently coexist
48
+
49
+ `ggnomics/__init__.py` explicitly separates them, but both ship in the same
50
+ top-level namespace today:
51
+
52
+ | | Modern (singledispatch) | Legacy (flat, DataFrame-only) |
53
+ |---|---|---|
54
+ | Embedding | `plot_embedding` / `plot_reduced_dim` / `dim_plot` / `plot_umap` / `plot_pca` / `plot_tsne` | — |
55
+ | Expression violin | `plot_expression` | `expression_violin`, `expression_violin_sce`, `expression_violin_se` |
56
+ | Dot plot | `plot_dot` | `marker_dotplot`, `marker_dotplot_from_matrix`, `marker_dotplot_sce` |
57
+ | Heatmap | `plot_heatmap` | `heatmap_long`, `heatmap_from_matrix` |
58
+ | Volcano | `plot_volcano` | `volcano_plot` |
59
+ | Composition | `plot_abundance` | `cluster_composition_barplot` |
60
+ | QC scatter/hist | `plot_coldata` (covers scatter) | `qc_scatter`, `qc_histogram` |
61
+ | Ridge density | *(no modern replacement)* | `ridge_density` |
62
+
63
+ Every legacy function has a modern 1:1 replacement **except `ridge_density`** (gap —
64
+ no modern replacement exists, and it should be exempted from any deprecation until one
65
+ does) and the matrix-input convenience wrappers (`*_from_matrix`, which the modern
66
+ generics don't offer — see §4.7).
67
+
68
+ The legacy functions aren't dispatch-based, don't share the modern error-message or
69
+ non-mutation conventions, and use a different naming grammar entirely (`volcano_plot`,
70
+ `expression_violin`, `ridge_density` put the verb *after* the noun; `heatmap_long`,
71
+ `marker_dotplot` put a qualifier after the noun). Keeping ~15 of them in the same flat
72
+ `__all__` as the modern set is the single biggest source of "which function do I
73
+ use?" confusion right now — worse than any individual naming choice below.
74
+
75
+ ## 4. Specific naming problems to resolve before declaring the API stable
76
+
77
+ ### 4.1 `plot_embedding` has three names for one function — no distinct audiences
78
+
79
+ ```python
80
+ plot_reduced_dim = plot_embedding
81
+ dim_plot = plot_embedding
82
+ ```
83
+
84
+ These are literal object aliases (`ggnomics/scatter.py:392-394`), not wrappers — same
85
+ function, same dispatch registry, three import names. `dim_plot` targets Seurat users
86
+ (`DimPlot`), `plot_reduced_dim` targets... the same audience as `plot_embedding`,
87
+ just with a different word for "embedding". It adds a name to remember without adding
88
+ a distinct mental model. Recommendation in §7.1.
89
+
90
+ ### 4.2 `plot_pca` and `plot_umap` mean two different things depending on namespace
91
+
92
+ This is the sharpest landmine in the current API:
93
+
94
+ | Call | Behavior |
95
+ |---|---|
96
+ | `gg.plot_pca(data, color=...)` | Displays an **already-computed** `X_pca` embedding. Container-dispatched (DataFrame/AnnData/SCE). Never runs PCA. |
97
+ | `gg.sc_pca.plot_pca(sce, run_if_missing=True, ...)` | **Computes** PCA via scikit-learn if missing, stores it in `reduced_dims`, then plots. SCE-only, not dispatched. |
98
+ | `gg.bulk_pca.plot_pca(se, pca_res=None, n_top_genes=None, ...)` | **Computes** PCA (optionally on top-variance genes) if `pca_res` not supplied, then plots. SE-only. |
99
+
100
+ Same name, three incompatible signatures and three different "does this run an
101
+ analysis?" answers, distinguished only by which submodule you happened to import
102
+ (`gg.plot_pca` vs `gg.sc_pca.plot_pca` vs `gg.bulk_pca.plot_pca`). The equivalent
103
+ collision exists for `plot_umap` vs `gg.sc_umap.plot_umap`. `bulk.pca` and
104
+ `singlecell.pca` are also ~80% duplicate code (`build_plot_df_from_sce` /
105
+ `build_plot_df_from_result` do the same "embedding + coldata → tidy DataFrame" job
106
+ that `_embedding_frame_from_dataframe` / `_embedding_frame` already do elsewhere).
107
+
108
+ This is also the one place where the "ggnomics is a visualization layer; BiocPy /
109
+ scranpy / PyDESeq2 / scverse do analysis" boundary stated in the README is violated —
110
+ `sc_pca.run_pca` / `bulk_pca.run_pca` are genuine analysis functions, not adapters.
111
+ Recommendation in §7.2.
112
+
113
+ ### 4.3 `plot_coldata` re-implements what `plot_scatter` already does
114
+
115
+ For two numeric columns, `plot_coldata` builds its own `geom_point` + continuous
116
+ color-scale plot (`ggnomics/coldata.py:110-115`) rather than delegating to
117
+ `plot_scatter`, which does the identical thing with a more complete feature set
118
+ (facets, adaptive size/stroke, palettes, aspect ratio). `plot_bimodal_scatter`, by
119
+ contrast, does delegate to `plot_scatter` for its non-marginal case — showing the
120
+ delegation pattern is known and used elsewhere, just not applied consistently. Two
121
+ consequences: (a) any future improvement to `plot_scatter`'s numeric-numeric path
122
+ silently doesn't reach `plot_coldata`, and (b) it's not obvious to a new user *when*
123
+ to reach for `plot_scatter` vs `plot_coldata` — the honest answer today is "`plot_scatter`
124
+ takes any two columns; `plot_coldata` takes any two columns from obs/colData and adds
125
+ violin/box/bar for categorical x" i.e. `plot_coldata` is a strict superset scoped to
126
+ metadata. That scoping rule is real and worth keeping, but should be enforced by
127
+ delegation, not duplication.
128
+
129
+ ### 4.4 Container-mediated modality selection has two competing conventions
130
+
131
+ `plot_bimodal_scatter`'s `AnnData` adapter accepts *both* `x_mod`/`y_mod` (older,
132
+ implicit: names an obsm/layer key) and `layer_x`/`layer_y` (newer, explicit layer
133
+ name), with the docstring itself noting `layer_x`/`layer_y` were added for backward
134
+ compatibility and take precedence. That's a sign the parameter design is still
135
+ settling — worth locking down before "stable" rather than after.
136
+
137
+ ### 4.5 Inconsistent return type for the same visual family
138
+
139
+ `plot_heatmap` returns a bare `ggplot`. `plot_clonotype_overlap` and
140
+ `plot_coef_expression(plot_type="heatmap")` return `HeatmapResult` (a `ggplot` +
141
+ the underlying matrix) for what is visually the same geom (`geom_tile`). A user who
142
+ row-clusters with `plot_heatmap(cluster_rows=True)` gets back the *reordered* matrix
143
+ nowhere — they'd have to recompute clustering themselves to get row order. That's the
144
+ exact case `HeatmapResult` exists to solve for the other two callers.
145
+
146
+ ### 4.6 Backend coverage is asymmetric in ways that look unintentional
147
+
148
+ Diffing the `.register()` calls across all four backends:
149
+
150
+ - **`SummarizedExperiment` never registers `plot_scatter`** (only
151
+ `plot_scatter_marginal`, which requires it internally). A bulk user cannot make a
152
+ plain x/y scatter of two `colData` columns through the dispatch API — looks like an
153
+ oversight, not a deliberate scope limit.
154
+ - **`SummarizedExperiment` *does* register `plot_adt_qc` and
155
+ `plot_clonotype_abundance`/`overlap`** — single-cell-protein and immune-repertoire
156
+ concepts, on a container whose defining trait is "no per-cell reduced dimensions."
157
+ Harmless if the columns exist, but it's not obvious this was a deliberate design
158
+ choice vs. mechanical copy-paste across backend files.
159
+ - **`MuData` registers only `plot_adt_qc` and `plot_bimodal_scatter`** — none of
160
+ `plot_embedding`, `plot_scatter`, `plot_expression`, `plot_coldata`, etc. have a
161
+ `MuData` adapter, so a `MuData` object is unusable for most of the library. This may
162
+ be intentional (MuData's whole point is cross-modality; maybe every other generic
163
+ is meant to be called on `mdata.mod["rna"]` directly), but it isn't documented as a
164
+ deliberate scope decision anywhere, so it currently reads as "unfinished."
165
+ - **Legacy `expression_violin` is registered for `SCE`/`SE` but not `AnnData`** — the
166
+ legacy API's own coverage is inconsistent, on top of being superseded.
167
+
168
+ ### 4.7 No modern replacement for matrix-input convenience wrappers
169
+
170
+ `marker_dotplot_from_matrix` and `heatmap_from_matrix` (legacy) take a bare
171
+ `np.ndarray`/`DataFrame` matrix plus a group vector and do the aggregation themselves,
172
+ which is genuinely convenient when a user has a raw expression matrix and no tidy
173
+ `DataFrame` yet. The modern `plot_dot`/`plot_heatmap` generics only dispatch on
174
+ `DataFrame` and registered containers — there's no `ndarray` path. This isn't a naming
175
+ inconsistency like the others in this section, it's a real feature gap: deprecating
176
+ the legacy matrix wrappers per §7.3 without replacing them would remove capability,
177
+ not just rename it. Resolve by either registering `plot_dot`/`plot_heatmap` for
178
+ `np.ndarray` directly (consistent with the dispatch pattern used everywhere else) or
179
+ explicitly deciding matrix input is out of scope for the modern API and keeping the
180
+ two `*_from_matrix` legacy functions permanently exempt from deprecation.
181
+
182
+ ### 4.8 `ggnomics/pca/` sits in an ambiguous public/private zone
183
+
184
+ `run_pca_sklearn`, `run_pca_svd`, `PcaResult` live in a package-visible path
185
+ (`ggnomics/pca/`, no leading underscore) but are deliberately **not** re-exported from
186
+ `ggnomics.__init__` — the plot-gallery article says so explicitly ("It is not
187
+ re-exported from the top-level namespace"). A no-underscore top-level subpackage is
188
+ normally read as "this is public, just deep"; right now it's public-by-path but
189
+ private-by-convention, which is exactly the kind of ambiguity that becomes a support
190
+ burden once users start `from ggnomics.pca import PcaResult`. Pick one (see §7.6).
191
+
192
+ ## 5. The "levels" of plot you described, made explicit
193
+
194
+ Your framing (container extensions vs. results-table plots vs. everything else) is
195
+ accurate and is a good organizing axis for docs even if the file layout doesn't
196
+ change. Five kinds of public surface exist today:
197
+
198
+ 1. **Container-dispatched plots** — the majority. Take a `DataFrame` *or* a registered
199
+ container; the container adapter reduces to the `DataFrame` case. Examples:
200
+ `plot_embedding` family, `plot_scatter`, `plot_coldata`/`plot_rowdata`,
201
+ `plot_expression`/`plot_dot`/`plot_heatmap`, `plot_abundance`,
202
+ `plot_highest_exprs`, `plot_pairs`, `plot_violin_stats`/`plot_box_stats`,
203
+ `plot_scatter_marginal`, `plot_embedding_panel`, `plot_bimodal_scatter`,
204
+ `plot_adt_qc`, `plot_clonotype_*`, `plot_pseudobulk_qc`.
205
+ 2. **Results-table plots** — operate on a plain DE/coefficient/count table that has no
206
+ natural container form, so dispatch buys nothing. Examples: `plot_volcano`,
207
+ `plot_ma`, `plot_coef_lollipop`, `plot_pseudobulk_de`. `plot_coef_expression` is a
208
+ hybrid: it takes *both* a container/DataFrame (`data`, for expression) and a
209
+ results table (`coefs`, for feature selection) — worth calling out as its own
210
+ pattern rather than forcing it into one bucket.
211
+ 3. **Compute + plot combos** — run an analysis, then plot the result. Currently only
212
+ `singlecell.pca`, `singlecell.umap`, `bulk.pca` (not exported at top level). This
213
+ is the one place analysis and visualization are fused, and it's the source of the
214
+ `plot_pca` collision in §4.2.
215
+ 4. **Grammar extensions** — not "a plot", but new geoms/stats/scales for plotnine
216
+ itself, usable standalone: `ggnomics.signif` (`geom_signif`, `run_comparisons`,
217
+ `map_pvalue_to_stars`) and `ggnomics.upset` (a full ComplexUpset-style
218
+ compute→select→apply pipeline with its own ~35 exports). These are already
219
+ correctly scoped as their own subpackages/namespaces and don't need to change.
220
+ 5. **Composition & palette utilities** — `hstack`/`vstack`/`grid`/
221
+ `annotate_composition`/`save_composition`; `get_palette`/`resolve_palette` +
222
+ palette constants. Cross-cutting, used by everything above.
223
+
224
+ Formalizing these five categories (in docs navigation, and in a short "how to name a
225
+ new function" note in `CONTRIBUTING.md`) will do most of the organizational work you're
226
+ after, independent of any renaming below.
227
+
228
+ ## 6. Recommended naming convention going forward
229
+
230
+ A new public plot function should satisfy:
231
+
232
+ - **Prefix**: `plot_` for anything returning `ggplot` or `Compose`. No exceptions —
233
+ this is already true for 100% of the modern set and should stay a hard rule.
234
+ - **Subject, not container**: the name describes *what* is plotted
235
+ (`embedding`, `coldata`, `expression`, `volcano`), never the input type. Already
236
+ followed everywhere in the modern set — keep enforcing it.
237
+ - **Qualifiers as suffixes, not alternate verbs**: `_stats` for automatic
238
+ significance annotation (`plot_violin_stats`, `plot_box_stats`), `_panel`/`_marginal`
239
+ for multi-panel composition (`plot_embedding_panel`, `plot_scatter_marginal`). This
240
+ convention already exists and reads well — codify it explicitly so new functions
241
+ follow it instead of inventing a new pattern (e.g. a hypothetical future
242
+ "expression + stats" function should be `plot_expression_stats`, not
243
+ `plot_stat_expression` or `plot_expression_with_stats`).
244
+ - **At most one familiarity alias per function**, and it must be declared as an alias,
245
+ not shipped as an equal-weight second name. See §7.1.
246
+ - **Compute-then-plot functions get their own verb**, never `plot_*`. See §7.2.
247
+
248
+ ## 7. Decisions to make
249
+
250
+ ### 7.1 Collapse the embedding aliases
251
+
252
+ Recommendation: **`plot_embedding` is canonical.** It's the most general (any
253
+ `obsm`/`reducedDims` key, not just PCA/UMAP/t-SNE) and already mirrors
254
+ `scanpy.pl.embedding`, which is the convention most of your target audience already
255
+ knows.
256
+
257
+ - Drop `plot_reduced_dim` — it's a pure synonym of `plot_embedding` with no distinct
258
+ audience; keeping it only doubles the surface a user has to learn is "the same
259
+ thing."
260
+ - Keep `dim_plot` **only** as a documented Seurat-familiarity alias (one line in the
261
+ docstring: "alias of `plot_embedding`, for users coming from Seurat's `DimPlot`"),
262
+ not as an equal `__all__` entry with its own doc page.
263
+ - Keep `plot_umap`/`plot_pca`/`plot_tsne` — these are legitimate convenience wrappers
264
+ (fixed `dimred=`), not synonyms, and match `scanpy.pl.umap`/`.pca`/`.tsne` closely
265
+ enough to lower the adoption cost. (`plot_pca` needs the fix in §7.2 first.)
266
+
267
+ ### 7.2 Split "compute" from "plot" for PCA/UMAP
268
+
269
+ The `plot_pca`/`plot_umap` name collision (§4.2) should be resolved by removing the
270
+ second definition, not by renaming around it. Two ways to get there — pick one:
271
+
272
+ **Option A — compute-only helpers, single plot entry point (recommended).**
273
+ Rename `sc_pca.plot_pca` → drop it entirely; `sc_pca.run_pca` keeps computing and
274
+ storing the embedding, and the user calls the one canonical `gg.plot_pca` /
275
+ `gg.plot_embedding` on the result afterward, exactly as they already do for any
276
+ precomputed embedding. Same for `sc_umap`/`bulk_pca`. This is a small deletion (the
277
+ `plot_pca`/`plot_umap` wrappers in `singlecell/pca.py`, `singlecell/umap.py`,
278
+ `bulk/pca.py` are ~15 lines each of pure pass-through to `plot_embedding` already) and
279
+ it makes the analysis/visualization boundary from the README literally true in code,
280
+ not just in prose. It also removes the `build_plot_df_from_sce`/
281
+ `build_plot_df_from_result` duplication for free, since there's no longer a
282
+ compute-module-local plot path to feed.
283
+
284
+ **Option B — keep the combo, rename it out of the `plot_` namespace.** e.g.
285
+ `sc_pca.compute_embedding(sce, ..., plot=True)` or a distinctly-named
286
+ `sc.pca(...)` / `bulk.pca(...)` callable that isn't spelled `plot_pca`. Lower-value
287
+ than A (keeps two ways to do the same thing) but preserves the one-call convenience
288
+ some users may like for exploratory work.
289
+
290
+ Either way, this is the highest-priority fix in this document: it's the one place two
291
+ functions with the *identical name* do observably different things depending on
292
+ import path, which is the kind of surprise that erodes trust in a "stable" API fast.
293
+
294
+ ### 7.3 Retire the legacy namespace as a namespace, not just individually
295
+
296
+ Rather than leaving ~15 legacy functions permanently mixed into the root `__all__`
297
+ next to their modern replacements, move them behind `ggnomics.legacy`:
298
+
299
+ 1. Now: add a `DeprecationWarning` inside each legacy function pointing at its modern
300
+ replacement (table in §3), keep the root-level import working.
301
+ 2. Next minor version: move the definitions under `ggnomics/legacy/` (or keep the
302
+ files where they are and just re-export from `ggnomics.legacy`), stop importing
303
+ them into `ggnomics.__all__` directly — `import ggnomics.legacy as gglegacy` still
304
+ works, `ggnomics.volcano_plot` starts raising `AttributeError` with a message
305
+ pointing at `ggnomics.legacy.volcano_plot`.
306
+ 3. Next major version: delete.
307
+
308
+ `ridge_density` (no modern replacement, §3) and the `*_from_matrix` convenience
309
+ wrappers (§4.7) are the two exceptions — decide whether those get a modern
310
+ replacement before deprecating, or get promoted (rather than deprecated) since they
311
+ fill a real gap.
312
+
313
+ ### 7.4 Fill or document the backend-coverage gaps (§4.6)
314
+
315
+ - Register `plot_scatter` for `SummarizedExperiment` (looks like a straightforward
316
+ bug fix, not a design question).
317
+ - For `MuData`'s narrow coverage: either (a) write adapters for the rest of the
318
+ container-dispatched set (treating a `MuData` mostly like "AnnData with a modality
319
+ selector"), or (b) keep it scoped to cross-modality functions and add one sentence
320
+ to the docs/README stating that scope explicitly, so it reads as a decision rather
321
+ than an omission. Given multimodal is already flagged as a smaller, newer surface
322
+ area, (b) is the lower-effort and probably-correct near-term choice — just make it
323
+ explicit.
324
+ - Decide whether `plot_adt_qc`/`plot_clonotype_*` on `SummarizedExperiment` is
325
+ intentional generality (works if the columns exist, no harm) or should be narrowed;
326
+ either is defensible, but it should be a recorded decision.
327
+
328
+ ### 7.5 Decide the `HeatmapResult` policy
329
+
330
+ Recommendation: return `HeatmapResult` from `plot_heatmap` whenever
331
+ `cluster_rows`/`cluster_cols` reorders the matrix (the reordered matrix is exactly the
332
+ derived artifact the caller can't otherwise recover), and a bare `ggplot` when no
333
+ clustering happened — matching the existing rule that `HeatmapResult` marks "this
334
+ call produced a matrix you couldn't already reconstruct."
335
+
336
+ ### 7.6 Decide `ggnomics/pca/`'s visibility (§4.8)
337
+
338
+ Either rename to `ggnomics/_pca/` to make "internal plumbing" true by path (cheapest,
339
+ recommended if the sklearn/SVD PCA core isn't meant to be a public API in its own
340
+ right), or keep the path and add it to `ggnomics.__all__` with the same documentation
341
+ weight as everything else (only worth it if there's a real audience for calling
342
+ `run_pca_sklearn` directly rather than through `sc_pca`/`bulk_pca`).
343
+
344
+ ## 8. Suggested sequencing
345
+
346
+ 1. Fix §7.2 (the `plot_pca`/`plot_umap` collision) — highest confusion-per-line-of-fix
347
+ ratio, and a pure subtraction (Option A deletes code, doesn't add any).
348
+ 2. Fix §7.1 (drop `plot_reduced_dim`, demote `dim_plot` to a documented alias) —
349
+ another pure subtraction from `__all__`.
350
+ 3. Land §4.3 (`plot_coldata` delegates to `plot_scatter` for the numeric/numeric case)
351
+ and §4.5 (`HeatmapResult` policy) — internal correctness fixes, no public API
352
+ change, but they remove behavior that would otherwise need to be preserved forever
353
+ once "stable" is declared.
354
+ 4. Start the §7.3 legacy-deprecation clock (add warnings now, move the namespace in
355
+ the next minor release) — this is the change most visible to existing users, so it
356
+ should go out with the most advance notice.
357
+ 5. §7.4/§7.5/§7.6 can land opportunistically — they're gaps and small inconsistencies,
358
+ not landmines, so they don't block declaring the rest of the API stable.
359
+
360
+ Once 1–4 are done, the "modern" `plot_*` set (§5, category 1) plus `plot_volcano`/
361
+ `plot_ma`/`plot_coef_*`/`plot_pseudobulk_de` (category 2), `ggnomics.signif`,
362
+ `ggnomics.upset`, and the composition/palette utilities are in good shape to freeze as
363
+ `ggnomics` 1.0's public surface.
@@ -0,0 +1,27 @@
1
+ # Authors
2
+
3
+ ggnomics is developed and maintained by:
4
+
5
+ * **MaximilianNuber** — [maximilian.d.nuber@gmail.com](mailto:maximilian.d.nuber@gmail.com)
6
+
7
+ ## Contributors
8
+
9
+ Contributions from the community are listed here as they arrive. See
10
+ [CONTRIBUTING.md](https://github.com/MaximilianNuber/ggnomics/blob/main/CONTRIBUTING.md) to get started.
11
+
12
+ Automated commits by `github-actions[bot]` (README plot regeneration) are not
13
+ listed as authorship.
14
+
15
+ ## Acknowledgements
16
+
17
+ ggnomics builds on, and is designed to work alongside, the following projects:
18
+
19
+ * [plotnine](https://plotnine.org) — the grammar-of-graphics implementation
20
+ every ggnomics function returns objects from.
21
+ * [BiocPy](https://github.com/BiocPy) — `SingleCellExperiment`,
22
+ `SummarizedExperiment`, `BiocFrame`, and the data-access packages used in the
23
+ workflow guides.
24
+ * [scverse](https://scverse.org) — AnnData and MuData.
25
+ * [ComplexUpset](https://github.com/krassowski/complex-upset) — the design that
26
+ `ggnomics.upset` follows.
27
+ * [Marsilea](https://marsilea.readthedocs.io) — composable heatmaps.
@@ -0,0 +1,73 @@
1
+ # Changelog
2
+
3
+ All notable changes to ggnomics are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+
12
+ - Development tooling: `tox.ini` (`py`, `core`, `lint`, `format`, `build`,
13
+ `docs`, `linkcheck`), `.pre-commit-config.yaml`, and Ruff lint/format
14
+ configuration in `pyproject.toml`.
15
+ - `tools/check_internal_links.py`, an offline internal-link checker for the
16
+ rendered Quarto site.
17
+ - `tests/test_public_api_contract.py`: return-type contracts, non-mutation
18
+ guarantees for DataFrame / AnnData / `SingleCellExperiment` /
19
+ `SummarizedExperiment` inputs, and error-message checks for unsupported
20
+ containers.
21
+ - Project documents: `CONTRIBUTING.md`, `CHANGELOG.md`, `AUTHORS.md`,
22
+ `CODE_OF_CONDUCT.md`, plus pull-request and issue templates.
23
+ - GitHub Actions workflows for documentation validation and for PyPI trusted
24
+ publishing (the trusted publisher itself is not yet configured).
25
+ - Automatic versioning with `setuptools_scm`: the version is derived from the
26
+ git tag, written to the generated `ggnomics/_version.py` at build time, and
27
+ exposed as `ggnomics.__version__`. `MANIFEST.in` keeps the sdist to the
28
+ package plus its test suite, since setuptools_scm's file finder would
29
+ otherwise ship the whole documentation tree.
30
+
31
+ ### Changed
32
+
33
+ - The "UpSet plots" vignette now appears in the site navigation bar and is
34
+ linked from the documentation home page.
35
+ - `pyproject.toml` metadata completed: real author and maintainer information,
36
+ classifiers, keywords, and the full set of project URLs. The `docs` extra now
37
+ installs quartodoc instead of MkDocs.
38
+ - Coverage configuration added; the full suite covers about 83% of `ggnomics/`
39
+ and CI enforces a floor of 80%.
40
+ - The test workflow matrix now covers Python 3.10, 3.12, and 3.13.
41
+ - `pyproject.toml` no longer carries a static `version`; it is `dynamic`.
42
+
43
+ ### Removed
44
+
45
+ - The legacy MkDocs site (`mkdocs.yml`, `docs/index.md`, `docs/vignettes.md`,
46
+ `docs/api/*.md`). Quarto with quartodoc is the single documentation renderer.
47
+ `docs/img/` and `docs/generate_readme_plots.py` are unaffected.
48
+
49
+ ### Fixed
50
+
51
+ - Two bare `except:` clauses in `ggnomics/singlecell/` narrowed to
52
+ `except Exception:` so `KeyboardInterrupt` and `SystemExit` propagate.
53
+ - `run_umap` now chains the original `ImportError` when `umap-learn` is missing.
54
+ - The "many groups" warning in `ggnomics.signif` now reports the caller's
55
+ location via `stacklevel=2`.
56
+ - Removed dead assignments in `ggnomics/dotplot.py` and in several tests; one
57
+ test asserted on a variable that was never bound.
58
+
59
+ ## [0.1.0]
60
+
61
+ Initial release.
62
+
63
+ - `plot_*` plotting functions built on `functools.singledispatch`, returning
64
+ ordinary plotnine objects.
65
+ - Backends for AnnData, BiocPy `SingleCellExperiment` and
66
+ `SummarizedExperiment`, and MuData, registered lazily and only when the
67
+ corresponding package is installed.
68
+ - Native plotnine UpSet plots and Venn diagrams (`ggnomics.upset`).
69
+ - Significance brackets and statistical annotations (`ggnomics.signif`).
70
+ - Marsilea-backed composable heatmaps.
71
+ - Quarto workflow guides and a quartodoc-generated API reference.
72
+
73
+ [Unreleased]: https://github.com/MaximilianNuber/ggnomics/compare/main...HEAD
@@ -0,0 +1,84 @@
1
+
2
+ # Contributor Covenant Code of Conduct
3
+
4
+ ## Our Pledge
5
+
6
+ We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
7
+
8
+ We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
9
+
10
+ ## Our Standards
11
+
12
+ Examples of behavior that contributes to a positive environment for our community include:
13
+
14
+ * Demonstrating empathy and kindness toward other people
15
+ * Being respectful of differing opinions, viewpoints, and experiences
16
+ * Giving and gracefully accepting constructive feedback
17
+ * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
18
+ * Focusing on what is best not just for us as individuals, but for the overall community
19
+
20
+ Examples of unacceptable behavior include:
21
+
22
+ * The use of sexualized language or imagery, and sexual attention or advances of any kind
23
+ * Trolling, insulting or derogatory comments, and personal or political attacks
24
+ * Public or private harassment
25
+ * Publishing others' private information, such as a physical or email address, without their explicit permission
26
+ * Other conduct which could reasonably be considered inappropriate in a professional setting
27
+
28
+ ## Enforcement Responsibilities
29
+
30
+ Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
31
+
32
+ Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
33
+
34
+ ## Scope
35
+
36
+ This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
37
+
38
+ ## Enforcement
39
+
40
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at maximilian.d.nuber@gmail.com. All complaints will be reviewed and investigated promptly and fairly.
41
+
42
+ All community leaders are obligated to respect the privacy and security of the reporter of any incident.
43
+
44
+ ## Enforcement Guidelines
45
+
46
+ Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
47
+
48
+ ### 1. Correction
49
+
50
+ **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
51
+
52
+ **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
53
+
54
+ ### 2. Warning
55
+
56
+ **Community Impact**: A violation through a single incident or series of actions.
57
+
58
+ **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
59
+
60
+ ### 3. Temporary Ban
61
+
62
+ **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
63
+
64
+ **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
65
+
66
+ ### 4. Permanent Ban
67
+
68
+ **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
69
+
70
+ **Consequence**: A permanent ban from any sort of public interaction within the community.
71
+
72
+ ## Attribution
73
+
74
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
75
+
76
+ Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
77
+
78
+ For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations].
79
+
80
+ [homepage]: https://www.contributor-covenant.org
81
+ [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
82
+ [Mozilla CoC]: https://github.com/mozilla/diversity
83
+ [FAQ]: https://www.contributor-covenant.org/faq
84
+ [translations]: https://www.contributor-covenant.org/translations