Skip to content

Commit cc85e2c

Browse files
authored
CLN: Matplotlib imports in style (#58921)
Use import_optional_dependency in style.py
1 parent 4526ea7 commit cc85e2c

File tree

1 file changed

+67
-83
lines changed

1 file changed

+67
-83
lines changed

pandas/io/formats/style.py

+67-83
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
from __future__ import annotations
66

7-
from contextlib import contextmanager
87
import copy
98
from functools import partial
109
import operator
@@ -56,7 +55,6 @@
5655

5756
if TYPE_CHECKING:
5857
from collections.abc import (
59-
Generator,
6058
Hashable,
6159
Sequence,
6260
)
@@ -84,22 +82,6 @@
8482

8583
from pandas import ExcelWriter
8684

87-
try:
88-
import matplotlib as mpl
89-
import matplotlib.pyplot as plt
90-
91-
has_mpl = True
92-
except ImportError:
93-
has_mpl = False
94-
95-
96-
@contextmanager
97-
def _mpl(func: Callable) -> Generator[tuple[Any, Any], None, None]:
98-
if has_mpl:
99-
yield plt, mpl
100-
else:
101-
raise ImportError(f"{func.__name__} requires matplotlib.")
102-
10385

10486
####
10587
# Shared Doc Strings
@@ -3832,61 +3814,61 @@ def _background_gradient(
38323814
else: # else validate gmap against the underlying data
38333815
gmap = _validate_apply_axis_arg(gmap, "gmap", float, data)
38343816

3835-
with _mpl(Styler.background_gradient) as (_, _matplotlib):
3836-
smin = np.nanmin(gmap) if vmin is None else vmin
3837-
smax = np.nanmax(gmap) if vmax is None else vmax
3838-
rng = smax - smin
3839-
# extend lower / upper bounds, compresses color range
3840-
norm = _matplotlib.colors.Normalize(smin - (rng * low), smax + (rng * high))
3817+
smin = np.nanmin(gmap) if vmin is None else vmin
3818+
smax = np.nanmax(gmap) if vmax is None else vmax
3819+
rng = smax - smin
3820+
_matplotlib = import_optional_dependency(
3821+
"matplotlib", extra="Styler.background_gradient requires matplotlib."
3822+
)
3823+
# extend lower / upper bounds, compresses color range
3824+
norm = _matplotlib.colors.Normalize(smin - (rng * low), smax + (rng * high))
3825+
3826+
if cmap is None:
3827+
rgbas = _matplotlib.colormaps[_matplotlib.rcParams["image.cmap"]](norm(gmap))
3828+
else:
3829+
rgbas = _matplotlib.colormaps.get_cmap(cmap)(norm(gmap))
3830+
3831+
def relative_luminance(rgba) -> float:
3832+
"""
3833+
Calculate relative luminance of a color.
3834+
3835+
The calculation adheres to the W3C standards
3836+
(https://www.w3.org/WAI/GL/wiki/Relative_luminance)
3837+
3838+
Parameters
3839+
----------
3840+
color : rgb or rgba tuple
3841+
3842+
Returns
3843+
-------
3844+
float
3845+
The relative luminance as a value from 0 to 1
3846+
"""
3847+
r, g, b = (
3848+
x / 12.92 if x <= 0.04045 else ((x + 0.055) / 1.055) ** 2.4
3849+
for x in rgba[:3]
3850+
)
3851+
return 0.2126 * r + 0.7152 * g + 0.0722 * b
38413852

3842-
if cmap is None:
3843-
rgbas = _matplotlib.colormaps[_matplotlib.rcParams["image.cmap"]](
3844-
norm(gmap)
3853+
def css(rgba, text_only) -> str:
3854+
if not text_only:
3855+
dark = relative_luminance(rgba) < text_color_threshold
3856+
text_color = "#f1f1f1" if dark else "#000000"
3857+
return (
3858+
f"background-color: {_matplotlib.colors.rgb2hex(rgba)};"
3859+
f"color: {text_color};"
38453860
)
38463861
else:
3847-
rgbas = _matplotlib.colormaps.get_cmap(cmap)(norm(gmap))
3848-
3849-
def relative_luminance(rgba) -> float:
3850-
"""
3851-
Calculate relative luminance of a color.
3852-
3853-
The calculation adheres to the W3C standards
3854-
(https://www.w3.org/WAI/GL/wiki/Relative_luminance)
3855-
3856-
Parameters
3857-
----------
3858-
color : rgb or rgba tuple
3859-
3860-
Returns
3861-
-------
3862-
float
3863-
The relative luminance as a value from 0 to 1
3864-
"""
3865-
r, g, b = (
3866-
x / 12.92 if x <= 0.04045 else ((x + 0.055) / 1.055) ** 2.4
3867-
for x in rgba[:3]
3868-
)
3869-
return 0.2126 * r + 0.7152 * g + 0.0722 * b
3870-
3871-
def css(rgba, text_only) -> str:
3872-
if not text_only:
3873-
dark = relative_luminance(rgba) < text_color_threshold
3874-
text_color = "#f1f1f1" if dark else "#000000"
3875-
return (
3876-
f"background-color: {_matplotlib.colors.rgb2hex(rgba)};"
3877-
f"color: {text_color};"
3878-
)
3879-
else:
3880-
return f"color: {_matplotlib.colors.rgb2hex(rgba)};"
3862+
return f"color: {_matplotlib.colors.rgb2hex(rgba)};"
38813863

3882-
if data.ndim == 1:
3883-
return [css(rgba, text_only) for rgba in rgbas]
3884-
else:
3885-
return DataFrame(
3886-
[[css(rgba, text_only) for rgba in row] for row in rgbas],
3887-
index=data.index,
3888-
columns=data.columns,
3889-
)
3864+
if data.ndim == 1:
3865+
return [css(rgba, text_only) for rgba in rgbas]
3866+
else:
3867+
return DataFrame(
3868+
[[css(rgba, text_only) for rgba in row] for row in rgbas],
3869+
index=data.index,
3870+
columns=data.columns,
3871+
)
38903872

38913873

38923874
def _highlight_between(
@@ -4124,20 +4106,22 @@ def css_calc(x, left: float, right: float, align: str, color: str | list | tuple
41244106
rgbas = None
41254107
if cmap is not None:
41264108
# use the matplotlib colormap input
4127-
with _mpl(Styler.bar) as (_, _matplotlib):
4128-
cmap = (
4129-
_matplotlib.colormaps[cmap]
4130-
if isinstance(cmap, str)
4131-
else cmap # assumed to be a Colormap instance as documented
4132-
)
4133-
norm = _matplotlib.colors.Normalize(left, right)
4134-
rgbas = cmap(norm(values))
4135-
if data.ndim == 1:
4136-
rgbas = [_matplotlib.colors.rgb2hex(rgba) for rgba in rgbas]
4137-
else:
4138-
rgbas = [
4139-
[_matplotlib.colors.rgb2hex(rgba) for rgba in row] for row in rgbas
4140-
]
4109+
_matplotlib = import_optional_dependency(
4110+
"matplotlib", extra="Styler.bar requires matplotlib."
4111+
)
4112+
cmap = (
4113+
_matplotlib.colormaps[cmap]
4114+
if isinstance(cmap, str)
4115+
else cmap # assumed to be a Colormap instance as documented
4116+
)
4117+
norm = _matplotlib.colors.Normalize(left, right)
4118+
rgbas = cmap(norm(values))
4119+
if data.ndim == 1:
4120+
rgbas = [_matplotlib.colors.rgb2hex(rgba) for rgba in rgbas]
4121+
else:
4122+
rgbas = [
4123+
[_matplotlib.colors.rgb2hex(rgba) for rgba in row] for row in rgbas
4124+
]
41414125

41424126
assert isinstance(align, str) # mypy: should now be in [left, right, mid, zero]
41434127
if data.ndim == 1:

0 commit comments

Comments
 (0)