Fix scatterplot with a subset hue/size/style order (#3601)#3964
Open
Leonard013 wants to merge 1 commit into
Open
Fix scatterplot with a subset hue/size/style order (#3601)#3964Leonard013 wants to merge 1 commit into
Leonard013 wants to merge 1 commit into
Conversation
_ScatterPlotter.plot mapped every row through the hue/size/style lookup tables, so an order that is a strict subset of the data left unmapped rows: a subset style_order/size_order raised KeyError and a subset hue_order drew transparent points. Reduce the data to rows whose non-numeric (categorical or datetime) mapped value is present in the order, matching lineplot and the objects interface. Numeric mappings interpolate out-of-table values via the norm and are left untouched. Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #3601
Problem
scatterplot(andrelplot(kind="scatter")) mishandle ahue_order/size_order/style_orderthat is a strict subset of the values present in the data:style_ordersubset → hard crash (KeyError).hue_ordersubset → silent wrong output: rows whose hue value is omitted from the order are still drawn, but as fully transparent points.lineplot,relplot(kind="line"), and the objects interface all handle a subset order correctly by simply not drawing the omitted rows. Only the scatter-type path is broken.Minimal reproduction:
Root cause
_ScatterPlotter.plot(seaborn/relational.py) draws every row ofself.comp_data.dropna()and then maps each row's semantic value to a visual attribute:When the order is a subset, the mapping lookup tables only contain the ordered levels, so values outside the order are unmapped:
StyleMapping._lookup_single(seaborn/_base.py:587) doesself.lookup_table[key][attr]and raisesKeyErrorfor an unmapped style value.HueMapping._lookup_single(seaborn/_base.py:172-183) explicitly returns(0, 0, 0, 0)(transparent) for an unmapped categorical hue value — its own comment notes this only happens "in scatterplot with hue_order, because scatterplot does not consider hue a grouping variable".By contrast,
_LinePlotter.plotiteratesself.iter_data(("hue", "size", "style")), which only yields the mapped levels, so line plots naturally drop the unmapped rows.Fix
Reduce the scatter data to the rows whose table-mapped hue/size/style value is actually mapped, right after
dropna()— matching the maintainer's endorsed direction (reduce the dataframe to the rows with relevant hue/size/style values, in the scatter plotting method) and making scatter behave likelineplot/ the objects interface:Notes on the design:
map_type != "numeric", i.e. it covers the table-based mappings — bothcategoricalanddatetime. A datetime hue/size is built through the samecategorical_mappinglookup table (seaborn/_base.py), so a subset order exhibits the identical bug; keying on "not numeric" rather than "is categorical" fixes datetime too. Numeric hue/size mappings are intentionally left untouched: they interpolate out-of-table values through the norm/colormap (e.g. a numeric dict palette that does not cover every value), whichlineplotalso preserves. For a numeric mapping the levels are the full set of unique data values anyway, so filtering would at best be a no-op and at worst break intended interpolation.self._hue_mapetc.) is accessed only insideif "<var>" in self.variables, exactly as the existing code below it already does, so instances built without the correspondingmap_*()call are unaffected.Tests (
tests/test_relational.py,TestScatterPlotter)test_hue_order(updated): previously asserted that omitted-hue rows were drawn as transparent points (i.e. it encoded the bug). It now asserts those rows are dropped, that no drawn point is transparent, and that the legend shows only the ordered levels.test_style_order(new): astyle_ordersubset no longer raises; the omitted rows are dropped and the legend shows only the ordered levels.test_hue_order_datetime(new): a datetime hue with a subsethue_orderdrops the omitted rows instead of drawing them transparent — covering thedatetimemap type the!= "numeric"guard now handles. It fails on the narrower== "categorical"guard and passes with!= "numeric".All fail on
mainand pass with the fix.Verification
Environment: seaborn 0.14.0.dev0 (this branch), matplotlib 3.11.0, pandas 3.0.3, numpy 2.5.1, Python 3.12.
Regression tests — before the source fix (tests present, fix absent):
Regression tests — after the source fix:
Behavioral before/after on a 30-row frame with levels a/b/c and a subset order
['a','b'](10 'c' rows):style_order=['a','b']KeyError: 'c'hue_order=['a','b']['a','b']Common (non-subset) case is unchanged — 30/30 points drawn:
Full modules:
Compatibility
The only observable change is for the previously-broken subset case, which either crashed (
style_order) or produced wrong output (hue_order/transparent points). Passing no order, or an order equal to the full set of levels, is unchanged, as are numeric hue/size mappings. The single updated existing test (test_hue_order) had asserted the buggy transparent-point behavior. This brings scatter into line withlineplotand the objects interface.