Skip to content

[Bug]: String contour(colors) gives confusing error when extend used #28782

Closed
@lucyleeow

Description

@lucyleeow

Bug summary

contour (and friends) specify that a single string can be passed to colors:

As a shortcut, single color strings may be used in place of one-element lists

But this only works when levels is the same length as the color string.

Code for reproduction

import numpy as np
import matplotlib.pyplot as plt

x = np.arange(1, 10)
y = x.reshape(-1, 1)
h = x * y

cs = plt.contour(h, levels=[10, 30, 50],
    colors='green', extend='both')

Actual outcome

colors='green' gives error (while colors='red' works, or making levels to be length of 5 also works):

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[64], line 8
      5 y = x.reshape(-1, 1)
      6 h = x * y
----> 8 cs = plt.contour(h, levels=[10, 30, 50],
      9     colors='green', extend='both')

File ~/miniconda3/envs/sklearn-env/lib/python3.12/site-packages/matplotlib/pyplot.py:3143, in contour(data, *args, **kwargs)
   3141 @_copy_docstring_and_deprecators(Axes.contour)
   3142 def contour(*args, data=None, **kwargs) -> QuadContourSet:
-> 3143     __ret = gca().contour(
   3144         *args, **({"data": data} if data is not None else {}), **kwargs
   3145     )
   3146     if __ret._A is not None:  # type: ignore[attr-defined]
   3147         sci(__ret)

File ~/miniconda3/envs/sklearn-env/lib/python3.12/site-packages/matplotlib/__init__.py:1473, in _preprocess_data.<locals>.inner(ax, data, *args, **kwargs)
   1470 @functools.wraps(func)
   1471 def inner(ax, *args, data=None, **kwargs):
   1472     if data is None:
-> 1473         return func(
   1474             ax,
   1475             *map(sanitize_sequence, args),
   1476             **{k: sanitize_sequence(v) for k, v in kwargs.items()})
   1478     bound = new_sig.bind(ax, *args, **kwargs)
   1479     auto_label = (bound.arguments.get(label_namer)
   1480                   or bound.kwargs.get(label_namer))

File ~/miniconda3/envs/sklearn-env/lib/python3.12/site-packages/matplotlib/axes/_axes.py:6659, in Axes.contour(self, *args, **kwargs)
   6650 """
   6651 Plot contour lines.
   6652 
   (...)
   6656 %(contour_doc)s
   6657 """
   6658 kwargs['filled'] = False
-> 6659 contours = mcontour.QuadContourSet(self, *args, **kwargs)
   6660 self._request_autoscale_view()
   6661 return contours

File ~/miniconda3/envs/sklearn-env/lib/python3.12/site-packages/matplotlib/contour.py:847, in ContourSet.__init__(self, ax, levels, filled, linewidths, linestyles, hatches, alpha, origin, extent, cmap, colors, norm, vmin, vmax, extend, antialiased, nchunk, locator, transform, negative_linestyles, clip_path, *args, **kwargs)
    845             cmap.set_under(self.colors[0])
    846         if self._extend_max:
--> 847             cmap.set_over(self.colors[-1])
    849 # label lists must be initialized here
    850 self.labelTexts = []

File ~/miniconda3/envs/sklearn-env/lib/python3.12/site-packages/matplotlib/colors.py:834, in Colormap.set_over(self, color, alpha)
    832 def set_over(self, color='k', alpha=None):
    833     """Set the color for high out-of-range values."""
--> 834     self._rgba_over = to_rgba(color, alpha)
    835     if self._isinit:
    836         self._set_extremes()

File ~/miniconda3/envs/sklearn-env/lib/python3.12/site-packages/matplotlib/colors.py:314, in to_rgba(c, alpha)
    312     rgba = None
    313 if rgba is None:  # Suppress exception chaining of cache lookup failure.
--> 314     rgba = _to_rgba_no_colorcycle(c, alpha)
    315     try:
    316         _colors_full_map.cache[c, alpha] = rgba

File ~/miniconda3/envs/sklearn-env/lib/python3.12/site-packages/matplotlib/colors.py:391, in _to_rgba_no_colorcycle(c, alpha)
    387             raise ValueError(
    388                 f"Invalid string grayscale value {orig_c!r}. "
    389                 f"Value must be within 0-1 range")
    390         return c, c, c, alpha if alpha is not None else 1.
--> 391     raise ValueError(f"Invalid RGBA argument: {orig_c!r}")
    392 # turn 2-D array into 1-D array
    393 if isinstance(c, np.ndarray):

ValueError: Invalid RGBA argument: 'n'

Expected outcome

Does not depend on color string length.

Additional information

I think it is because we don't convert to list if colors is given as string, and then we do this:

if (len(self.colors) == total_levels and
(self._extend_min or self._extend_max)):

Operating system

Ubuntu

Matplotlib Version

3.9.2

Matplotlib Backend

No response

Python version

No response

Jupyter version

No response

Installation

None

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions