Skip to content

Difficulties and errors with Categorial data in Plot(..., color=) #3948

Description

@drewejohnson

I would like to group data in the colors of a so.Plot object by grouping their values. For scatter plots, this is mostly okay. But for line plots, it can get unruly fast if you don't do the grouping, as each individual value gets its own unique color.

Example

import seaborn as sns
df = sns.load_dataset("penguins")
sns.objects.Plot(
    df,
    x="bill_length_mm",
    y="body_mass_g", 
    color="flipper_length_mm",
).add(mark=sns.objects.Line())
Image

If I make a "group" of the flipper data, seaborn errors

df["flipper_group"] = pd.cut(df["flipper_length_mm"], bins=7)
sns.objects.Plot(
    df,
    x="bill_length_mm",
    y="body_mass_g", 
    color="flipper_group",
).add(mark=sns.objects.Line())
[...]
TypeError: 'pandas._libs.interval.Interval' object is not iterable

The above exception was the direct cause of the following exception:
[...]
PlotSpecError: Scaling operation failed for the `color` variable. See the traceback above for more information.
Full traceback
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
File [...]/lib/python3.14/site-packages/seaborn/_marks/base.py:180, in Mark._resolve(self, data, name, scales)
    179 try:
--> 180    feature = scale(value)
    181 except Exception as err:

File [...]/lib/python3.14/site-packages/seaborn/_core/scales.py:129, in Scale.__call__(self, data)
    128     if func is not None:
--> 129         trans_data = func(trans_data)
    131 if scalar_data:

File [...]/lib/python3.14/site-packages/seaborn/_core/scales.py:311, in Nominal._setup.<locals>.convert_units(x)
    305 def convert_units(x):
    306     # TODO only do this with explicit order?
    307     # (But also category dtype?)
    308     # TODO isin fails when units_seed mixes numbers and strings (numpy error?)
    309     # but np.isin also does not seem any faster? (Maybe not broadcasting in C)
    310     # keep = x.isin(units_seed)
--> 311     keep = np.array([x_ in units_seed for x_ in x], bool)
    312     out = np.full(len(x), np.nan)

TypeError: 'pandas._libs.interval.Interval' object is not iterable

The above exception was the direct cause of the following exception:

PlotSpecError                             Traceback (most recent call last)
File [...]/lib/python3.14/site-packages/seaborn/_core/plot.py:387, in Plot._repr_png_(self)
    385 if Plot.config.display["format"] != "png":
    386     return None
--> 387return self.plot()._repr_png_()

File [...]/lib/python3.14/site-packages/seaborn/_core/plot.py:932, in Plot.plot(self, pyplot)
    928 """
    929 Compile the plot spec and return the Plotter object.
    930 """
    931 with theme_context(self._theme_with_defaults()):
--> 932    return self._plot(pyplot)

File [...]/lib/python3.14/site-packages/seaborn/_core/plot.py:962, in Plot._plot(self, pyplot)
    960 # Process the data for each layer and add matplotlib artists
    961 for layer in layers:
--> 962    plotter._plot_layer(self, layer)
    964 # Add various figure decorations
    965 plotter._make_legend(self)

File [...]/lib/python3.14/site-packages/seaborn/_core/plot.py:1488, in Plotter._plot_layer(self, p, layer)
   1485     grouping_vars = mark._grouping_props + default_grouping_vars
   1486     split_generator = self._setup_split_generator(grouping_vars, df, subplots)
-> 1488    mark._plot(split_generator, scales, orient)
   1490 # TODO is this the right place for this?
   1491 for view in self._subplots:

File [...]/lib/python3.14/site-packages/seaborn/_marks/line.py:52, in Path._plot(self, split_gen, scales, orient)
     48 def _plot(self, split_gen, scales, orient):
     50     for keys, data, ax in split_gen(keep_na=not self._sort):
---> 52        vals = resolve_properties(self, keys, scales)
     53         vals["color"] = resolve_color(self, keys, scales=scales)
     54         vals["fillcolor"] = resolve_color(self, keys, prefix="fill", scales=scales)

File [...]/lib/python3.14/site-packages/seaborn/_marks/base.py:237, in resolve_properties(mark, data, scales)
    232 def resolve_properties(
    233     mark: Mark, data: DataFrame, scales: dict[str, Scale]
    234 ) -> dict[str, Any]:
    236     props = {
--> 237        name: mark._resolve(data, name, scales) for name in mark._mappable_props
    238     }
    239     return props

File [...]/lib/python3.14/site-packages/seaborn/_marks/base.py:182, in Mark._resolve(self, data, name, scales)
    180         feature = scale(value)
    181     except Exception as err:
--> 182        raise PlotSpecError._during("Scaling operation", name) from err
    184 if return_array:
    185     feature = np.asarray(feature)
PlotSpecError: Scaling operation failed for the `color` variable. See the traceback above for more information.

I can kind of get around this by casting the data to strings, but the sorting is not consistent

df["flipper_group_str"] = df["flipper_group"].astype(str)
sns.objects.Plot(
    df,
    x="bill_length_mm",
    y="body_mass_g", 
    color="flipper_group_str",
).add(mark=sns.objects.Line())
Image

Note the last three entries of the grouping are not numerically sorted. Which makes sense, because seaborn is not seeing numbers. But they aren't even sorted lexicographically, going 205 -> 222 -> 214.

Context

This is in reference to https://codeberg.org/djsn/sweet-pareto/issues/15

There, I use the so.Plot and faceting with a custom so.Mark to make graphs that would otherwise be challenging to make. The faceting and coloring provided by so.Plot is exceptional. Thank you for this

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions