Repository navigation
Conversation
Tests: add spec-guided TDD support conftest Add tests/conftest.py from the spec-guided-tdd skill, formatted to ProteoPy's black/flake8 standards. It only affects test modules marked `pytestmark = pytest.mark.spec_guided` (e.g. tests/pl/test_upset.py); all other tests run unchanged. - `pytest` (default, CI) runs the holdout and randomized tests and deselects the `_IMP` mirrors - `pytest --implementer` runs the `_IMP` and randomized tests with obscured failure output, for implementation sessions - `--seed N` reproduces randomized inputs (seed printed in the header) - provides the `rng` and `report_input` fixtures - checks test naming and holdout/_IMP pairing
Tests: pl.var_detected_by_cat_upset() Add a spec-guided test suite for var_detected_by_cat_upset() (module marked `spec_guided`, run by tests/conftest.py): - TestVarDetectedByCatUpset: 75 holdout tests plus 9 randomized property and metamorphic tests - TestVarDetectedByCatUpsetIMP: 75 _IMP mirrors on different inputs, run only with `pytest --implementer` Coverage: intersection counts and thresholds (min_count / min_fraction, inclusivity, per-category denominators, empty categories, float edge cases), zero/NaN detection, category order and str coercion, sparse input, non-mutation (incl. views), UpSet construction and rendered axes, print_stats tables (incl. column-name collisions), verbose output, save/show, and argument validation. UpSet calls are observed through spies on the upsetplot class; randomized tests use the seeded rng fixture (reproduce with `--seed N`).
Feature: pl.var_detected_by_cat_upset() Add an UpSet plot of which features (.var) are detected in which categories of an .obs column, e.g. which proteins are found in which tissues. - A feature is a member of a category when it is detected (non-NaN in .X; with zero_to_na=True also non-zero) in enough of that category's observations: min_count (default 1) or min_fraction, inclusive, per category. Setting both raises ValueError. - Features that are members of no category form a "No category" set, always included, also with a count of 0. - Categories follow the default pl order: category order for a Categorical column (unused categories kept), else lexicographic order of the str-coerced values. Values colliding after str coercion raise ValueError. - print_stats prints global, per-intersection and per-category tables; verbose reports the input matrix, threshold and sizes. - Sparse .X is densified with a UserWarning; adata is never modified. - Returns the axes dict of upsetplot's UpSet.plot(). Add upsetplot>=0.9.0,<0.10 as a core dependency. upsetplot 0.9.0 is broken on pandas 3 / numpy >= 2.4, so proteopy.pl.upset patches it on import (dot style defaults, single-category aggregation, count-label positions, all-empty totals warning). The upper bound keeps these patches from hitting an untested upsetplot release. Export via pr.pl, list it under Quality Control in the plotting API docs, and add a HISTORY.md entry.
bee29ba to
2c85530
Compare
Implemented sort_by with "degree", "cardinality", "-degree" and "-cardinality". New tests cover all modes, equal counts and degrees, repeatability, bar/dot alignment, and invalid inputs.
2c85530 to
a4164b5
Compare
| verbose: bool = False, | ||
| show: bool = True, | ||
| save: str | Path | None = None, | ||
| *, |
There was a problem hiding this comment.
Put * after cat_key, only adata and cat_key should be positional arguments.
| ProteoPy AnnData (peptide- or protein-level). Intensities are | ||
| read from ``adata.X`` only. |
There was a problem hiding this comment.
Reword: AnnData in ProteoData format.
| show : bool | ||
| Call ``plt.show()`` at the end. | ||
| save : str | Path | None | ||
| Path to save the figure to; None skips saving. |
There was a problem hiding this comment.
"; None skips saving" is redundant.
| Call ``plt.show()`` at the end. | ||
| save : str | Path | None | ||
| Path to save the figure to; None skips saving. | ||
| sort_by : str |
|
|
||
| Returns | ||
| ------- | ||
| dict[str, Axes] |
There was a problem hiding this comment.
If pyupsetplot coopts the matplotlib figure, return None
| order after conversion to strings. Use an ordered | ||
| :class:`pandas.Categorical` to customize the order. | ||
| min_count : int | None | ||
| Minimum detections per category, as a nonnegative integer. |
There was a problem hiding this comment.
Make it a tiny bit clearer by wording like:
Minimum number of non-missing var values (detections) within a category for the membership to be counted.
Make same improvement to min_fraction
| ) | ||
|
|
||
| # -- Plot | ||
| axes = _plot_upset(counts, sort_by) |
There was a problem hiding this comment.
Remove axes return. Return None.
| def _print_verbose_report( | ||
| cat_key: str, | ||
| threshold_name: str, | ||
| threshold_value: int | float, | ||
| n_vars: int, | ||
| n_categories: int, | ||
| ) -> None: | ||
| """Print a short report about the input of the plot.""" | ||
| print( | ||
| f"Using the .X matrix.\n" | ||
| f"Categories from .obs['{cat_key}'].\n" | ||
| f"Threshold: {threshold_name} = {str(threshold_value)}\n" | ||
| f"Features: {n_vars}\n" | ||
| f"Categories: {n_categories}" | ||
| ) |
There was a problem hiding this comment.
Too obvious verbose statements. They don't bring any new information. i dont think this plot needs any verbose output.
Guideline for plotting functions:
- verbose: non-obvious useful printouts e.g. if several heavy steps are involved, announce them. If expensive looping algorithm, print at which loop we are etc.
- print_stats: print what we see in the plot so that the user can have tangible numbers to report in a paper. This includes sometimes making stats of what you see: mean of data points...
| counts = _intersection_counts(membership, names) | ||
|
|
||
| # -- Report | ||
| if verbose: |
There was a problem hiding this comment.
Remove verbose parameter, see other comment why,
| axes = _plot_upset(counts, sort_by) | ||
|
|
||
| if save is not None: | ||
| axes["matrix"].figure.savefig(save) |
There was a problem hiding this comment.
Does this actually work? From the code I would think that if there are several axes and you only save one of them, we are not saving the entire upset figure. Please check in practice.




upset_test_spec.md
upset_function_spec.md