Skip to content

Commit 9e89e6c

Browse files
committed
ENH: Styler.background_gradient to accept vmin vmax and dtype Int64
Resolve pandas-dev#12145 and pandas-dev#28869 For `vmin` and `vmax` use the same implementation in `Styler.bar` For dtype `Int64` issue, deprecated `.values` and use `.to_numpy` instead Here explicitly assign the dtype to float since we are doing normalize
1 parent 953757a commit 9e89e6c

File tree

3 files changed

+46
-5
lines changed

3 files changed

+46
-5
lines changed

doc/source/whatsnew/v1.0.0.rst

+2
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ Other enhancements
112112
- :meth:`read_stata` can read Stata 119 dta files. (:issue:`28250`)
113113
- Added ``encoding`` argument to :meth:`DataFrame.to_string` for non-ascii text (:issue:`28766`)
114114
- Added ``encoding`` argument to :func:`DataFrame.to_html` for non-ascii text (:issue:`28663`)
115+
- :meth:`Styler.background_gradient` now accepts ``vmin`` and ``vmax`` arguments (:issue:`12145`)
115116

116117
Build Changes
117118
^^^^^^^^^^^^^
@@ -385,6 +386,7 @@ I/O
385386
- Bug in :meth:`DataFrame.read_excel` with ``engine='ods'`` when ``sheet_name`` argument references a non-existent sheet (:issue:`27676`)
386387
- Bug in :meth:`pandas.io.formats.style.Styler` formatting for floating values not displaying decimals correctly (:issue:`13257`)
387388
- Bug in :meth:`DataFrame.to_html` when using ``formatters=<list>`` and ``max_cols`` together. (:issue:`25955`)
389+
- Bug in :meth:`Styler.background_gradient` not able to work with dtype ``Int64`` (:issue:`28869`)
388390

389391
Plotting
390392
^^^^^^^^

pandas/io/formats/style.py

+27-5
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,8 @@ def background_gradient(
958958
axis=0,
959959
subset=None,
960960
text_color_threshold=0.408,
961+
vmin=None,
962+
vmax=None,
961963
):
962964
"""
963965
Color the background in a gradient according to
@@ -986,6 +988,18 @@ def background_gradient(
986988
987989
.. versionadded:: 0.24.0
988990
991+
vmin : float, optional
992+
Minimum data value that corresponds to colormap minimum value.
993+
When None (default): the minimum value of the data will be used.
994+
995+
.. versionadded:: 1.0.0
996+
997+
vmax : float, optional
998+
Maximum data value that corresponds to colormap maximum value.
999+
When None (default): the maximum value of the data will be used.
1000+
1001+
.. versionadded:: 1.0.0
1002+
9891003
Returns
9901004
-------
9911005
self : Styler
@@ -1012,11 +1026,15 @@ def background_gradient(
10121026
low=low,
10131027
high=high,
10141028
text_color_threshold=text_color_threshold,
1029+
vmin=vmin,
1030+
vmax=vmax,
10151031
)
10161032
return self
10171033

10181034
@staticmethod
1019-
def _background_gradient(s, cmap="PuBu", low=0, high=0, text_color_threshold=0.408):
1035+
def _background_gradient(
1036+
s, cmap="PuBu", low=0, high=0, text_color_threshold=0.408, vmin=None, vmax=None
1037+
):
10201038
"""
10211039
Color background in a range according to the data.
10221040
"""
@@ -1028,14 +1046,18 @@ def _background_gradient(s, cmap="PuBu", low=0, high=0, text_color_threshold=0.4
10281046
raise ValueError(msg)
10291047

10301048
with _mpl(Styler.background_gradient) as (plt, colors):
1031-
smin = s.values.min()
1032-
smax = s.values.max()
1049+
smin = s.min() if vmin is None else vmin
1050+
if isinstance(smin, ABCSeries):
1051+
smin = smin.min()
1052+
smax = s.max() if vmax is None else vmax
1053+
if isinstance(smax, ABCSeries):
1054+
smax = smax.max()
10331055
rng = smax - smin
10341056
# extend lower / upper bounds, compresses color range
10351057
norm = colors.Normalize(smin - (rng * low), smax + (rng * high))
10361058
# matplotlib colors.Normalize modifies inplace?
10371059
# https://github.com/matplotlib/matplotlib/issues/5427
1038-
rgbas = plt.cm.get_cmap(cmap)(norm(s.values))
1060+
rgbas = plt.cm.get_cmap(cmap)(norm(s.to_numpy(dtype=float)))
10391061

10401062
def relative_luminance(rgba):
10411063
"""
@@ -1121,7 +1143,7 @@ def _bar(s, align, colors, width=100, vmin=None, vmax=None):
11211143
smax = max(abs(smin), abs(smax))
11221144
smin = -smax
11231145
# Transform to percent-range of linear-gradient
1124-
normed = width * (s.values - smin) / (smax - smin + 1e-12)
1146+
normed = width * (s.to_numpy(dtype=float) - smin) / (smax - smin + 1e-12)
11251147
zero = -width * smin / (smax - smin + 1e-12)
11261148

11271149
def css_bar(start, end, color):

pandas/tests/io/formats/test_style.py

+17
Original file line numberDiff line numberDiff line change
@@ -1648,6 +1648,23 @@ def test_background_gradient_axis(self):
16481648
assert result[(1, 0)] == mid
16491649
assert result[(1, 1)] == high
16501650

1651+
def test_background_gradient_vmin_vmax(self):
1652+
# GH 12145
1653+
df = pd.DataFrame(range(5))
1654+
ctx = df.style.background_gradient(vmin=1, vmax=3)._compute().ctx
1655+
assert ctx[(0, 0)] == ctx[(1, 0)]
1656+
assert ctx[(4, 0)] == ctx[(3, 0)]
1657+
1658+
def test_background_gradient_int64(self):
1659+
# GH 28869
1660+
df1 = pd.Series(range(3)).to_frame()
1661+
df2 = pd.Series(range(3), dtype='Int64').to_frame()
1662+
ctx1 = df1.style.background_gradient()._compute().ctx
1663+
ctx2 = df2.style.background_gradient()._compute().ctx
1664+
assert ctx2[(0, 0)] == ctx1[(0, 0)]
1665+
assert ctx2[(1, 0)] == ctx1[(1, 0)]
1666+
assert ctx2[(2, 0)] == ctx1[(2, 0)]
1667+
16511668

16521669
def test_block_names():
16531670
# catch accidental removal of a block

0 commit comments

Comments
 (0)