|
4 | 4 |
|
5 | 5 | from __future__ import annotations
|
6 | 6 |
|
7 |
| -from contextlib import contextmanager |
8 | 7 | import copy
|
9 | 8 | from functools import partial
|
10 | 9 | import operator
|
|
56 | 55 |
|
57 | 56 | if TYPE_CHECKING:
|
58 | 57 | from collections.abc import (
|
59 |
| - Generator, |
60 | 58 | Hashable,
|
61 | 59 | Sequence,
|
62 | 60 | )
|
|
84 | 82 |
|
85 | 83 | from pandas import ExcelWriter
|
86 | 84 |
|
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 |
| - |
103 | 85 |
|
104 | 86 | ####
|
105 | 87 | # Shared Doc Strings
|
@@ -3832,61 +3814,61 @@ def _background_gradient(
|
3832 | 3814 | else: # else validate gmap against the underlying data
|
3833 | 3815 | gmap = _validate_apply_axis_arg(gmap, "gmap", float, data)
|
3834 | 3816 |
|
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 |
3841 | 3852 |
|
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};" |
3845 | 3860 | )
|
3846 | 3861 | 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)};" |
3881 | 3863 |
|
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 | + ) |
3890 | 3872 |
|
3891 | 3873 |
|
3892 | 3874 | def _highlight_between(
|
@@ -4124,20 +4106,22 @@ def css_calc(x, left: float, right: float, align: str, color: str | list | tuple
|
4124 | 4106 | rgbas = None
|
4125 | 4107 | if cmap is not None:
|
4126 | 4108 | # 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 | + ] |
4141 | 4125 |
|
4142 | 4126 | assert isinstance(align, str) # mypy: should now be in [left, right, mid, zero]
|
4143 | 4127 | if data.ndim == 1:
|
|
0 commit comments