From e6fd6990244043dca01e53928e73b32d1df83054 Mon Sep 17 00:00:00 2001 From: Liudeng Zhang Date: Sun, 8 Mar 2026 16:20:19 -0500 Subject: [PATCH 1/5] Filter unused categories from clonotype network legend Only show legend entries for categories that are actually present in the plotted clonotype network, rather than listing all categories from the full dataset. This makes the legend easier to read when plotting subsets of data with many possible category values. Closes #321 --- src/scirpy/pl/_clonotypes.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/scirpy/pl/_clonotypes.py b/src/scirpy/pl/_clonotypes.py index 2976fa55a..0a2bcf5db 100644 --- a/src/scirpy/pl/_clonotypes.py +++ b/src/scirpy/pl/_clonotypes.py @@ -617,9 +617,14 @@ def _aggregate_per_dot_continuous(values): **text_kwds, ) - # add legend for categorical colors + # add legend for categorical colors, showing only categories present in the plot if cat_colors is not None and show_legend: - for cat, color in cat_colors.items(): + used_colors = set() + if pie_colors is not None: + for pc in pie_colors: + used_colors.update(pc.keys()) + visible_cat_colors = {cat: c for cat, c in cat_colors.items() if c in used_colors} if used_colors else cat_colors + for cat, color in visible_cat_colors.items(): # use empty scatter to set labels legend_ax.scatter([], [], c=color, label=cat) legend_ax.legend( @@ -627,7 +632,7 @@ def _aggregate_per_dot_continuous(values): loc="center left", # bbox_to_anchor=(1, 0.5), fontsize=legend_fontsize, - ncol=(1 if len(cat_colors) <= 14 else 2 if len(cat_colors) <= 30 else 3), + ncol=(1 if len(visible_cat_colors) <= 14 else 2 if len(visible_cat_colors) <= 30 else 3), ) legend_ax.axis("off") From 867a897f3abd285ad4476e018dfda1d09f909f5f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 8 Mar 2026 21:26:40 +0000 Subject: [PATCH 2/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/scirpy/pl/_clonotypes.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/scirpy/pl/_clonotypes.py b/src/scirpy/pl/_clonotypes.py index 0a2bcf5db..e388d396f 100644 --- a/src/scirpy/pl/_clonotypes.py +++ b/src/scirpy/pl/_clonotypes.py @@ -623,7 +623,9 @@ def _aggregate_per_dot_continuous(values): if pie_colors is not None: for pc in pie_colors: used_colors.update(pc.keys()) - visible_cat_colors = {cat: c for cat, c in cat_colors.items() if c in used_colors} if used_colors else cat_colors + visible_cat_colors = ( + {cat: c for cat, c in cat_colors.items() if c in used_colors} if used_colors else cat_colors + ) for cat, color in visible_cat_colors.items(): # use empty scatter to set labels legend_ax.scatter([], [], c=color, label=cat) From 703f2882806ce5aeb457a4425027a56d98b88b0b Mon Sep 17 00:00:00 2001 From: Liudeng Zhang Date: Wed, 11 Mar 2026 16:23:59 -0500 Subject: [PATCH 3/5] Add test for legend filtering of unused categories Verify that clonotype_network legend only shows categories present in the plotted network, not all categories from the full categorical palette. --- src/scirpy/tests/test_plotting.py | 32 +++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/scirpy/tests/test_plotting.py b/src/scirpy/tests/test_plotting.py index d6113076d..88a053bb2 100644 --- a/src/scirpy/tests/test_plotting.py +++ b/src/scirpy/tests/test_plotting.py @@ -163,6 +163,38 @@ def test_clonotype_network_pie( assert isinstance(p, plt.Axes) +@pytest.mark.extra +def test_clonotype_network_pie_legend_filters_unused(adata_clonotype_network): + """Legend should only contain categories present in the plotted network. + + Regression test for https://github.com/scverse/scirpy/issues/679 + """ + import numpy as np + + adata = adata_clonotype_network + # Add an extra category that no cell in the network has, to ensure + # the legend filters it out. + obs_col = "receptor_type" + tmp_ad = adata.mod["airr"] if isinstance(adata, MuData) else adata + tmp_ad.obs[obs_col] = tmp_ad.obs[obs_col].cat.add_categories("extra_unused") + + fig = plt.gcf() + fig.clear() + p = pl.clonotype_network(adata, color=obs_col, show_legend=True) + assert isinstance(p, plt.Axes) + + # Collect legend labels from all axes in the figure + legend_labels = [] + for ax in fig.get_axes(): + legend = ax.get_legend() + if legend is not None: + legend_labels.extend([t.get_text() for t in legend.get_texts()]) + + assert "extra_unused" not in legend_labels, ( + f"Legend should not contain unused category 'extra_unused', got: {legend_labels}" + ) + + @pytest.mark.extra def test_logoplot(adata_cdr3): p = pl.logoplot_cdr3_motif(adata_cdr3, chains="VJ_1") From 58020ff24ff7bb098c96da621bf1f6007ead803a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 11 Mar 2026 21:24:13 +0000 Subject: [PATCH 4/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/scirpy/tests/test_plotting.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/scirpy/tests/test_plotting.py b/src/scirpy/tests/test_plotting.py index 88a053bb2..93330a05c 100644 --- a/src/scirpy/tests/test_plotting.py +++ b/src/scirpy/tests/test_plotting.py @@ -169,8 +169,6 @@ def test_clonotype_network_pie_legend_filters_unused(adata_clonotype_network): Regression test for https://github.com/scverse/scirpy/issues/679 """ - import numpy as np - adata = adata_clonotype_network # Add an extra category that no cell in the network has, to ensure # the legend filters it out. From aba34fc9d9dbfd20ca8a276da1f22e39364cfae7 Mon Sep 17 00:00:00 2001 From: Gregor Sturm Date: Tue, 24 Mar 2026 20:59:12 +0100 Subject: [PATCH 5/5] Update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e1cea5df..fd6f3aadf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ and this project adheres to [Semantic Versioning][]. [keep a changelog]: https://keepachangelog.com/en/1.0.0/ [semantic versioning]: https://semver.org/spec/v2.0.0.html +## [Unreleased] + +### Fixes + + - Filter unused categories from clonotype network legend ([#680](https://github.com/scverse/scirpy/pull/680/)). + ## v0.23.0 ### Changes