Description
Describe the bug
Hello. Recently, after upgrading to scikit-learn v1.7.0, I encountered an issue when using DecisionBoundaryDisplay.from_estimator
with the colors
keyword argument. Specifically, the following error is raised:
File "D:\Project\Python Project\venv\Lib\site-packages\sklearn\inspection\_plot\decision_boundary.py", line 276, in plot
plot_func(self.xx0, self.xx1, response, cmap=cmap, **safe_kwargs)
~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\Project\Python Project\venv\Lib\site-packages\matplotlib\contour.py", line 689, in __init__
raise ValueError('Either colors or cmap must be None')
ValueError: Either colors or cmap must be None
However, in v1.6.0, everything works fine.
After further investigation, it seems this issue was introduced by PR #29797, where both cmap
and colors
are passed to plot_func
unconditionally, without explicit conflict handling:
Additionally, when setting
plot_method='contour'
in multiclass classification scenarios, the decision boundary is no longer shown as it was in v1.6.0. It appears that this regression is due to the switch in v1.7.0 to always using a cmap to plot the entire decision surface in multiclass scenarios.
Here are the visual differences:
Suggestion
To preserve backward compatibility and expected behavior:
- Check for mutual exclusivity of
colors
andcmap
and raise a clear warning/error; - Retain the old behavior when
plot_method='contour'
.
I'd be happy to open a PR to help address this regression if the core team is supportive.
Steps/Code to Reproduce
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.inspection import DecisionBoundaryDisplay
from sklearn.linear_model import LogisticRegression
iris = load_iris()
X = iris.data[:, :2]
classifier = LogisticRegression().fit(X, iris.target)
disp = DecisionBoundaryDisplay.from_estimator(
classifier,
X,
# plot_method='contour',
xlabel=iris.feature_names[0],
ylabel=iris.feature_names[1],
colors='black',
alpha=0.5,
)
disp.ax_.scatter(X[:, 0], X[:, 1], c=iris.target, edgecolor='k')
plt.show()
Expected Results
No error is raised when using plot_method='contour'
together with colors
, and the decision boundary is displayed correctly as expected.
Actual Results
Traceback (most recent call last):
File "d:\Project\Python Project\cc.py", line 9, in <module>
disp = DecisionBoundaryDisplay.from_estimator(
classifier,
...<4 lines>...
alpha=0.5,
)
File "D:\Project\Python Project\venv\Lib\site-packages\sklearn\inspection\_plot\decision_boundary.py", line 558, in from_estimator
return display.plot(ax=ax, plot_method=plot_method, **kwargs)
~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\Project\Python Project\venv\Lib\site-packages\sklearn\inspection\_plot\decision_boundary.py", line 276, in plot
plot_func(self.xx0, self.xx1, response, cmap=cmap, **safe_kwargs)
~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\Project\Python Project\venv\Lib\site-packages\matplotlib\__init__.py", line 1521, in inner
return func(
ax,
*map(cbook.sanitize_sequence, args),
**{k: cbook.sanitize_sequence(v) for k, v in kwargs.items()})
File "D:\Project\Python Project\venv\Lib\site-packages\matplotlib\axes\_axes.py", line 6794, in contourf
contours = mcontour.QuadContourSet(self, *args, **kwargs)
File "D:\Project\Python Project\venv\Lib\site-packages\matplotlib\contour.py", line 689, in __init__
raise ValueError('Either colors or cmap must be None')
ValueError: Either colors or cmap must be None
Versions
System:
python: 3.13.4 (tags/v3.13.4:8a526ec, Jun 3 2025, 17:46:04) [MSC v.1943 64 bit (AMD64)]
executable: D:\Project\Python Project\venv\Scripts\python.exe
machine: Windows-11-10.0.26100-SP0
Python dependencies:
sklearn: 1.7.0
pip: None
setuptools: 80.9.0
numpy: 2.3.0
scipy: 1.15.3
Cython: 3.1.2
pandas: 2.3.0
matplotlib: 3.10.3
joblib: 1.5.1
threadpoolctl: 3.6.0
Built with OpenMP: True
threadpoolctl info:
user_api: openmp
internal_api: openmp
num_threads: 16
prefix: vcomp
filepath: D:\Project\Python Project\venv\Lib\site-packages\sklearn\.libs\vcomp140.dll
version: None
user_api: blas
internal_api: openblas
num_threads: 16
prefix: libscipy_openblas
filepath: D:\Project\Python Project\venv\Lib\site-packages\numpy.libs\libscipy_openblas64_-13e2df515630b4a41f92893938845698.dll
version: 0.3.29
threading_layer: pthreads
architecture: Haswell
user_api: blas
internal_api: openblas
num_threads: 16
prefix: libscipy_openblas
filepath: D:\Project\Python Project\venv\Lib\site-packages\scipy.libs\libscipy_openblas-f07f5a5d207a3a47104dca54d6d0c86a.dll
version: 0.3.28
threading_layer: pthreads
architecture: Haswell