groups= filtering behavior changes silently when na_color="lightgray" is passed explicitly
Passing the documented default value of na_color changes the output.
Environment
spatialdata-plot 0.3.4.dev (main, commit 5cfedc7)
spatialdata 0.5.0
- Python 3.13
Problem
When groups=["A"] is specified, non-matching elements are hidden by default. But if na_color="lightgray" (which is the documented default) is passed explicitly, non-matching elements are shown in lightgray instead of hidden.
The filter condition checks an internal default_color_set flag rather than comparing the actual color value:
# render.py ~395
should_filter = _na.default_color_set or _na.is_fully_transparent()
default_color_set is True only when na_color was never set. Once any value is passed — including the same "lightgray" string that is the default — the flag is False, and the filter is skipped.
Consequence: a user who reads the docs, sees na_color defaults to "lightgray", and explicitly writes na_color="lightgray" to be explicit gets a completely different plot from the user who omits the parameter. This is particularly confusing in notebook environments where output diffs are hard to spot.
Minimal reproducible example
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import geopandas as gpd
import anndata as ad
import dask
dask.config.set({"dataframe.query-planning": False})
import spatialdata as sd
from spatialdata.models import ShapesModel, TableModel
from shapely.geometry import box
import spatialdata_plot
shapes = ShapesModel.parse(gpd.GeoDataFrame(
{"geometry": [box(i,0,i+1,1) for i in range(3)], "radius": [0.5]*3}, geometry="geometry"
))
obs = pd.DataFrame({
"region": pd.Categorical(["s"]*3),
"instance_id": [0, 1, 2],
"cat": pd.Categorical(["A", "B", "C"]),
})
table = TableModel.parse(
ad.AnnData(X=np.zeros((3,1)), obs=obs),
region="s", region_key="region", instance_key="instance_id",
)
sdata = sd.SpatialData(shapes={"s": shapes}, tables={"t": table})
fig, (ax0, ax1) = plt.subplots(1, 2)
# groups=["A"] with default na_color
sdata.pl.render_shapes("s", color="cat", groups=["A"]).pl.show(ax=ax0)
# groups=["A"] with na_color="lightgray" (the documented default)
sdata.pl.render_shapes("s", color="cat", groups=["A"], na_color="lightgray").pl.show(ax=ax1)
print(f"default na_color: {len(ax0.collections)} shape collections (B, C hidden)")
print(f"explicit na_color=lightgray: {len(ax1.collections)} shape collections (B, C visible)")
Expected behaviour
render_shapes("s", color="cat", groups=["A"]) and render_shapes("s", color="cat", groups=["A"], na_color="lightgray") should produce identical output, since "lightgray" is the stated default.
Actual behaviour
default na_color: 2 collections (B, C hidden)
explicit na_color=lightgray: 3 collections (B, C shown in lightgray)
Fix sketch
Two possible approaches:
Option 1 — compare the color value, not the flag:
from matplotlib.colors import to_hex
_DEFAULT_NA_HEX = to_hex("lightgray", keep_alpha=False)
should_filter = (
_na.default_color_set
or _na.is_fully_transparent()
or _na.get_hex() == _DEFAULT_NA_HEX
)
Option 2 — document clearly that na_color is an opt-in to show non-matching elements. Any explicit na_color value (including the default) shows non-matching elements. This is a valid design choice but must be documented explicitly in the docstring, since the current docs just describe the color, not the behavioral toggle.
Related
finding_groups_nacolor_lightgray_inconsistency.md
Triage tier: Tier 1
groups=filtering behavior changes silently whenna_color="lightgray"is passed explicitlyPassing the documented default value of
na_colorchanges the output.Environment
spatialdata-plot0.3.4.dev(main, commit5cfedc7)spatialdata0.5.0Problem
When
groups=["A"]is specified, non-matching elements are hidden by default. But ifna_color="lightgray"(which is the documented default) is passed explicitly, non-matching elements are shown in lightgray instead of hidden.The filter condition checks an internal
default_color_setflag rather than comparing the actual color value:default_color_setisTrueonly whenna_colorwas never set. Once any value is passed — including the same"lightgray"string that is the default — the flag isFalse, and the filter is skipped.Consequence: a user who reads the docs, sees
na_colordefaults to"lightgray", and explicitly writesna_color="lightgray"to be explicit gets a completely different plot from the user who omits the parameter. This is particularly confusing in notebook environments where output diffs are hard to spot.Minimal reproducible example
Expected behaviour
render_shapes("s", color="cat", groups=["A"])andrender_shapes("s", color="cat", groups=["A"], na_color="lightgray")should produce identical output, since"lightgray"is the stated default.Actual behaviour
Fix sketch
Two possible approaches:
Option 1 — compare the color value, not the flag:
Option 2 — document clearly that
na_coloris an opt-in to show non-matching elements. Any explicitna_colorvalue (including the default) shows non-matching elements. This is a valid design choice but must be documented explicitly in the docstring, since the current docs just describe the color, not the behavioral toggle.Related
finding_groups_nacolor_lightgray_inconsistency.mdTriage tier: Tier 1