From 36dd331ec6bfbf4c90fb71b74ccdd7b0f3496ff7 Mon Sep 17 00:00:00 2001 From: joelostblom Date: Wed, 30 May 2018 15:52:43 -0400 Subject: [PATCH 01/18] Color text based on background gradient Close #21258 --- pandas/io/formats/style.py | 47 ++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index f876ceb8a26bf..6708a3fe6cfc8 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -863,7 +863,7 @@ def highlight_null(self, null_color='red'): return self def background_gradient(self, cmap='PuBu', low=0, high=0, axis=0, - subset=None): + subset=None, text_color=0.2): """ Color the background in a gradient according to the data in each column (optionally row). @@ -879,6 +879,10 @@ def background_gradient(self, cmap='PuBu', low=0, high=0, axis=0, 1 or 'columns' for columnwise, 0 or 'index' for rowwise subset: IndexSlice a valid slice for ``data`` to limit the style application to + text_color: float or int + luminance threshold for determining text color. Facilitates text + visibility across varying background colors. From 0 to 1. + 0 = all text is dark colored, 1 = all text is light colored. Returns ------- @@ -886,19 +890,19 @@ def background_gradient(self, cmap='PuBu', low=0, high=0, axis=0, Notes ----- - Tune ``low`` and ``high`` to keep the text legible by - not using the entire range of the color map. These extend - the range of the data by ``low * (x.max() - x.min())`` - and ``high * (x.max() - x.min())`` before normalizing. + Set ``text_color`` or tune ``low`` and ``high`` to keep the text + legible by not using the entire range of the color map. The range of + the data is extended by ``low * (x.max() - x.min())`` and ``high * + (x.max() - x.min())`` before normalizing. """ subset = _maybe_numeric_slice(self.data, subset) subset = _non_reducing_slice(subset) self.apply(self._background_gradient, cmap=cmap, subset=subset, - axis=axis, low=low, high=high) + axis=axis, low=low, high=high, text_color=text_color) return self @staticmethod - def _background_gradient(s, cmap='PuBu', low=0, high=0): + def _background_gradient(s, cmap='PuBu', low=0, high=0, text_color=0.2): """Color background in a range according to the data.""" with _mpl(Styler.background_gradient) as (plt, colors): rng = s.max() - s.min() @@ -909,8 +913,33 @@ def _background_gradient(s, cmap='PuBu', low=0, high=0): # https://github.com/matplotlib/matplotlib/issues/5427 normed = norm(s.values) c = [colors.rgb2hex(x) for x in plt.cm.get_cmap(cmap)(normed)] - return ['background-color: {color}'.format(color=color) - for color in c] + if (not isinstance(text_color, (float, int)) or + not 0 <= text_color <= 1): + msg = "`text_color` must be a value from 0 to 1." + raise ValueError(msg) + + def relative_luminance(color): + """Calculate the relative luminance of a color according to W3C + standards, https://www.w3.org/WAI/GL/wiki/Relative_luminance + Parameters + ---------- + color : matplotlib color. Hex code, rgb-tuple, or + HTML color name. + Returns + ------- + luminance : float between 0 and 1 + """ + rgb = colors.colorConverter.to_rgba_array(color)[:, :3] + rgb = np.where(rgb <= .03928, rgb / 12.92, + ((rgb + .055) / 1.055) ** 2.4) + lum = rgb.dot([.2126, .7152, .0722]) + return lum.item() + + text_colors = ['#f1f1f1' if relative_luminance(x) < text_color + else '#000000' for x in c] + + return ['background-color: {color};color: {tc}'.format( + color=color, tc=tc) for color, tc in zip(c, text_colors)] def set_properties(self, subset=None, **kwargs): """ From 04d30f8342767047be71b9b0a0e7492a59aa99a8 Mon Sep 17 00:00:00 2001 From: joelostblom Date: Wed, 30 May 2018 17:44:56 -0400 Subject: [PATCH 02/18] Add additional return value to the test --- pandas/tests/io/formats/test_style.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pandas/tests/io/formats/test_style.py b/pandas/tests/io/formats/test_style.py index c1ab9cd184340..875ffd8dd7ae2 100644 --- a/pandas/tests/io/formats/test_style.py +++ b/pandas/tests/io/formats/test_style.py @@ -1031,7 +1031,9 @@ def test_background_gradient(self): result = df.style.background_gradient( subset=pd.IndexSlice[1, 'A'])._compute().ctx - assert result[(1, 0)] == ['background-color: #fff7fb'] + + assert result[(1, 0)] == ['background-color: #fff7fb', + 'color: #000000'] def test_block_names(): From 6ce03f3bb6807e150bfcb0c9849516f8cb78429f Mon Sep 17 00:00:00 2001 From: joelostblom Date: Thu, 31 May 2018 20:20:35 -0400 Subject: [PATCH 03/18] Format docstring to pandas standards --- pandas/io/formats/style.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index 6708a3fe6cfc8..f423099aaa46b 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -919,15 +919,21 @@ def _background_gradient(s, cmap='PuBu', low=0, high=0, text_color=0.2): raise ValueError(msg) def relative_luminance(color): - """Calculate the relative luminance of a color according to W3C - standards, https://www.w3.org/WAI/GL/wiki/Relative_luminance + """ + Calculate relative luminance of a color. + + The calculation adheres to the W3C standards + (https://www.w3.org/WAI/GL/wiki/Relative_luminance) + Parameters ---------- - color : matplotlib color. Hex code, rgb-tuple, or - HTML color name. + color : matplotlib color + Hex code, rgb-tuple, or HTML color name. + Returns ------- - luminance : float between 0 and 1 + float + The relative luminance as a value from 0 to 1 """ rgb = colors.colorConverter.to_rgba_array(color)[:, :3] rgb = np.where(rgb <= .03928, rgb / 12.92, From a8783596c11b7edb159a504dd8d2c26443a1e660 Mon Sep 17 00:00:00 2001 From: joelostblom Date: Thu, 31 May 2018 20:23:23 -0400 Subject: [PATCH 04/18] Set relative luminance default to same as in seaborn --- pandas/io/formats/style.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index f423099aaa46b..2ba5c56e32f68 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -863,7 +863,7 @@ def highlight_null(self, null_color='red'): return self def background_gradient(self, cmap='PuBu', low=0, high=0, axis=0, - subset=None, text_color=0.2): + subset=None, text_color=0.408): """ Color the background in a gradient according to the data in each column (optionally row). @@ -902,7 +902,7 @@ def background_gradient(self, cmap='PuBu', low=0, high=0, axis=0, return self @staticmethod - def _background_gradient(s, cmap='PuBu', low=0, high=0, text_color=0.2): + def _background_gradient(s, cmap='PuBu', low=0, high=0, text_color=0.408): """Color background in a range according to the data.""" with _mpl(Styler.background_gradient) as (plt, colors): rng = s.max() - s.min() From e7e8444712d13a8c934251a735a76f924b7680fb Mon Sep 17 00:00:00 2001 From: joelostblom Date: Thu, 31 May 2018 20:24:03 -0400 Subject: [PATCH 05/18] Add version added note --- pandas/io/formats/style.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index 2ba5c56e32f68..e5879dfbf2da5 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -884,6 +884,8 @@ def background_gradient(self, cmap='PuBu', low=0, high=0, axis=0, visibility across varying background colors. From 0 to 1. 0 = all text is dark colored, 1 = all text is light colored. + .. versionadded:: 0.24.0 + Returns ------- self : Styler From ee79b1018529f5230fa5603860f9e8f13da2f59c Mon Sep 17 00:00:00 2001 From: joelostblom Date: Thu, 31 May 2018 20:25:29 -0400 Subject: [PATCH 06/18] Test a larger range of expected values --- pandas/tests/io/formats/test_style.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pandas/tests/io/formats/test_style.py b/pandas/tests/io/formats/test_style.py index 875ffd8dd7ae2..143e9af163b04 100644 --- a/pandas/tests/io/formats/test_style.py +++ b/pandas/tests/io/formats/test_style.py @@ -1028,6 +1028,11 @@ def test_background_gradient(self): assert all("#" in x[0] for x in result.values()) assert result[(0, 0)] == result[(0, 1)] assert result[(1, 0)] == result[(1, 1)] + for res in result: + if result[res][0].split(' ')[1] in ['#fde725', '#ffffcc']: + assert result[(res)][1].split(' ')[1] == '#000000' + elif result[res][0].split(' ')[1] in ['#800026', '#440154']: + assert result[(res)][1].split(' ')[1] == '#f1f1f1' result = df.style.background_gradient( subset=pd.IndexSlice[1, 'A'])._compute().ctx From 075cd54a7b47f7e417a15b9d83104b7d38b2c574 Mon Sep 17 00:00:00 2001 From: joelostblom Date: Thu, 31 May 2018 20:28:12 -0400 Subject: [PATCH 07/18] Change parameter name to text_color_threshold --- pandas/io/formats/style.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index e5879dfbf2da5..80030cea4ad01 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -863,7 +863,7 @@ def highlight_null(self, null_color='red'): return self def background_gradient(self, cmap='PuBu', low=0, high=0, axis=0, - subset=None, text_color=0.408): + subset=None, text_color_threshold=0.408): """ Color the background in a gradient according to the data in each column (optionally row). @@ -879,7 +879,7 @@ def background_gradient(self, cmap='PuBu', low=0, high=0, axis=0, 1 or 'columns' for columnwise, 0 or 'index' for rowwise subset: IndexSlice a valid slice for ``data`` to limit the style application to - text_color: float or int + text_color_threshold: float or int luminance threshold for determining text color. Facilitates text visibility across varying background colors. From 0 to 1. 0 = all text is dark colored, 1 = all text is light colored. @@ -892,19 +892,21 @@ def background_gradient(self, cmap='PuBu', low=0, high=0, axis=0, Notes ----- - Set ``text_color`` or tune ``low`` and ``high`` to keep the text - legible by not using the entire range of the color map. The range of - the data is extended by ``low * (x.max() - x.min())`` and ``high * + Set ``text_color_threshold`` or tune ``low`` and ``high`` to keep the + text legible by not using the entire range of the color map. The range + of the data is extended by ``low * (x.max() - x.min())`` and ``high * (x.max() - x.min())`` before normalizing. """ subset = _maybe_numeric_slice(self.data, subset) subset = _non_reducing_slice(subset) self.apply(self._background_gradient, cmap=cmap, subset=subset, - axis=axis, low=low, high=high, text_color=text_color) + axis=axis, low=low, high=high, + text_color_threshold=text_color_threshold) return self @staticmethod - def _background_gradient(s, cmap='PuBu', low=0, high=0, text_color=0.408): + def _background_gradient(s, cmap='PuBu', low=0, high=0, + text_color_threshold=0.408): """Color background in a range according to the data.""" with _mpl(Styler.background_gradient) as (plt, colors): rng = s.max() - s.min() @@ -915,9 +917,9 @@ def _background_gradient(s, cmap='PuBu', low=0, high=0, text_color=0.408): # https://github.com/matplotlib/matplotlib/issues/5427 normed = norm(s.values) c = [colors.rgb2hex(x) for x in plt.cm.get_cmap(cmap)(normed)] - if (not isinstance(text_color, (float, int)) or - not 0 <= text_color <= 1): - msg = "`text_color` must be a value from 0 to 1." + if (not isinstance(text_color_threshold, (float, int)) or + not 0 <= text_color_threshold <= 1): + msg = "`text_color_threshold` must be a value from 0 to 1." raise ValueError(msg) def relative_luminance(color): @@ -943,8 +945,8 @@ def relative_luminance(color): lum = rgb.dot([.2126, .7152, .0722]) return lum.item() - text_colors = ['#f1f1f1' if relative_luminance(x) < text_color - else '#000000' for x in c] + text_colors = ['#f1f1f1' if relative_luminance(x) < + text_color_threshold else '#000000' for x in c] return ['background-color: {color};color: {tc}'.format( color=color, tc=tc) for color, tc in zip(c, text_colors)] From 5023dd60fe4915a5a360419f84aa227724a91268 Mon Sep 17 00:00:00 2001 From: joelostblom Date: Thu, 31 May 2018 20:29:27 -0400 Subject: [PATCH 08/18] Add test for bad text_color_threshold values --- pandas/tests/io/formats/test_style.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pandas/tests/io/formats/test_style.py b/pandas/tests/io/formats/test_style.py index 143e9af163b04..a5b42863bba12 100644 --- a/pandas/tests/io/formats/test_style.py +++ b/pandas/tests/io/formats/test_style.py @@ -1040,6 +1040,14 @@ def test_background_gradient(self): assert result[(1, 0)] == ['background-color: #fff7fb', 'color: #000000'] + @td.skip_if_no_mpl + def test_text_color_threshold(self): + df = pd.DataFrame([[1, 2], [2, 4]], columns=['A', 'B']) + for text_color_threshold in [1.1, '1', -1, [2, 2]]: + with pytest.raises(ValueError): + df.style.background_gradient( + text_color_threshold=text_color_threshold) + def test_block_names(): # catch accidental removal of a block From 163c3649ce3dce5b307c06219e8b8604752d5953 Mon Sep 17 00:00:00 2001 From: joelostblom Date: Fri, 1 Jun 2018 09:51:07 -0400 Subject: [PATCH 09/18] Add Raises section to docstring --- pandas/io/formats/style.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index 80030cea4ad01..668cafec4b522 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -896,6 +896,11 @@ def background_gradient(self, cmap='PuBu', low=0, high=0, axis=0, text legible by not using the entire range of the color map. The range of the data is extended by ``low * (x.max() - x.min())`` and ``high * (x.max() - x.min())`` before normalizing. + + Raises + ------ + ValueError + If ``text_color_threshold`` is not a value from 0 to 1. """ subset = _maybe_numeric_slice(self.data, subset) subset = _non_reducing_slice(subset) From ba6f98159b48027d98b6ea362c476a5ec4d453f6 Mon Sep 17 00:00:00 2001 From: joelostblom Date: Fri, 1 Jun 2018 09:54:58 -0400 Subject: [PATCH 10/18] Add separate test for text_color_threshold --- pandas/tests/io/formats/test_style.py | 28 ++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/pandas/tests/io/formats/test_style.py b/pandas/tests/io/formats/test_style.py index a5b42863bba12..a957d97407dbe 100644 --- a/pandas/tests/io/formats/test_style.py +++ b/pandas/tests/io/formats/test_style.py @@ -1028,11 +1028,6 @@ def test_background_gradient(self): assert all("#" in x[0] for x in result.values()) assert result[(0, 0)] == result[(0, 1)] assert result[(1, 0)] == result[(1, 1)] - for res in result: - if result[res][0].split(' ')[1] in ['#fde725', '#ffffcc']: - assert result[(res)][1].split(' ')[1] == '#000000' - elif result[res][0].split(' ')[1] in ['#800026', '#440154']: - assert result[(res)][1].split(' ')[1] == '#f1f1f1' result = df.style.background_gradient( subset=pd.IndexSlice[1, 'A'])._compute().ctx @@ -1043,10 +1038,25 @@ def test_background_gradient(self): @td.skip_if_no_mpl def test_text_color_threshold(self): df = pd.DataFrame([[1, 2], [2, 4]], columns=['A', 'B']) - for text_color_threshold in [1.1, '1', -1, [2, 2]]: - with pytest.raises(ValueError): - df.style.background_gradient( - text_color_threshold=text_color_threshold) + for c_map in [None, 'YlOrRd']: + result = df.style.background_gradient(cmap=c_map)._compute().ctx + for res in result: + bg_color = result[res][0].split(' ')[1] + assert bg_color in ['#fde725', '#ffffcc', + '#800026', '#440154'], ( + "Unexpected background color returned from " + "`style.background_gradient()`") + text_color = result[(res)][1].split(' ')[1] + if bg_color in ['#fde725', '#ffffcc']: + assert text_color == '#000000' + elif bg_color in ['#800026', '#440154']: + assert text_color == '#f1f1f1' + for res in result: + if result[res][0].split(' ')[1] in ['#fde725', '#ffffcc']: + assert result[(res)][1].split(' ')[1] == '#000000' + elif result[res][0].split(' ')[1] in ['#800026', '#440154']: + assert result[(res)][1].split(' ')[1] == '#f1f1f1' + def test_block_names(): From a6dc14d24299e865b75505c484bfb8378936acf2 Mon Sep 17 00:00:00 2001 From: joelostblom Date: Fri, 1 Jun 2018 10:05:38 -0400 Subject: [PATCH 11/18] Add parameterized test for text_color_threshold ValueError --- pandas/tests/io/formats/test_style.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pandas/tests/io/formats/test_style.py b/pandas/tests/io/formats/test_style.py index a957d97407dbe..cf6bc2846856e 100644 --- a/pandas/tests/io/formats/test_style.py +++ b/pandas/tests/io/formats/test_style.py @@ -1057,6 +1057,14 @@ def test_text_color_threshold(self): elif result[res][0].split(' ')[1] in ['#800026', '#440154']: assert result[(res)][1].split(' ')[1] == '#f1f1f1' + @td.skip_if_no_mpl + @pytest.mark.parametrize("text_color_threshold", [1.1, '1', -1, [2, 2]]) + def test_text_color_threshold_raises(self, text_color_threshold): + df = pd.DataFrame([[1, 2], [2, 4]], columns=['A', 'B']) + msg = "`text_color_threshold` must be a value from 0 to 1." + with tm.assert_raises_regex(ValueError, msg): + df.style.background_gradient( + text_color_threshold=text_color_threshold) def test_block_names(): From 8993dffd7769de95c0fded6e117425618b36a45b Mon Sep 17 00:00:00 2001 From: joelostblom Date: Sun, 3 Jun 2018 12:53:04 -0400 Subject: [PATCH 12/18] Fix test by calling `._compute()` --- pandas/tests/io/formats/test_style.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/io/formats/test_style.py b/pandas/tests/io/formats/test_style.py index cf6bc2846856e..34a96fa41b6e4 100644 --- a/pandas/tests/io/formats/test_style.py +++ b/pandas/tests/io/formats/test_style.py @@ -1064,7 +1064,7 @@ def test_text_color_threshold_raises(self, text_color_threshold): msg = "`text_color_threshold` must be a value from 0 to 1." with tm.assert_raises_regex(ValueError, msg): df.style.background_gradient( - text_color_threshold=text_color_threshold) + text_color_threshold=text_color_threshold)._compute() def test_block_names(): From 6eb0930d20ca7ed4400a1f7be69aacbad9e7b8a1 Mon Sep 17 00:00:00 2001 From: joelostblom Date: Sun, 3 Jun 2018 12:54:43 -0400 Subject: [PATCH 13/18] Add whatsnew entry --- doc/source/whatsnew/v0.24.0.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.24.0.txt b/doc/source/whatsnew/v0.24.0.txt index e931450cb5c01..772833d28a465 100644 --- a/doc/source/whatsnew/v0.24.0.txt +++ b/doc/source/whatsnew/v0.24.0.txt @@ -181,7 +181,7 @@ Reshaping Other ^^^^^ -- +- :meth: `~pandas.io.formats.style.Styler.background_gradient` now takes a ``text_color_threshold`` parameter to automatically lighten the text color based on the luminance of the background color. This improves readability with dark background colors without the need to limit the background colormap range. (:issue:`21258`, :issue:`21269`) - - From c30255b0882c71e6c77f60801f6929468c721061 Mon Sep 17 00:00:00 2001 From: joelostblom Date: Sun, 3 Jun 2018 14:52:51 -0400 Subject: [PATCH 14/18] Make threshold test explicit and parameterized --- pandas/tests/io/formats/test_style.py | 36 +++++++++++++-------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/pandas/tests/io/formats/test_style.py b/pandas/tests/io/formats/test_style.py index 34a96fa41b6e4..456e88f74726f 100644 --- a/pandas/tests/io/formats/test_style.py +++ b/pandas/tests/io/formats/test_style.py @@ -1036,26 +1036,24 @@ def test_background_gradient(self): 'color: #000000'] @td.skip_if_no_mpl - def test_text_color_threshold(self): + @pytest.mark.parametrize("c_map", [None, 'YlOrRd']) + def test_text_color_threshold(self, c_map): df = pd.DataFrame([[1, 2], [2, 4]], columns=['A', 'B']) - for c_map in [None, 'YlOrRd']: - result = df.style.background_gradient(cmap=c_map)._compute().ctx - for res in result: - bg_color = result[res][0].split(' ')[1] - assert bg_color in ['#fde725', '#ffffcc', - '#800026', '#440154'], ( - "Unexpected background color returned from " - "`style.background_gradient()`") - text_color = result[(res)][1].split(' ')[1] - if bg_color in ['#fde725', '#ffffcc']: - assert text_color == '#000000' - elif bg_color in ['#800026', '#440154']: - assert text_color == '#f1f1f1' - for res in result: - if result[res][0].split(' ')[1] in ['#fde725', '#ffffcc']: - assert result[(res)][1].split(' ')[1] == '#000000' - elif result[res][0].split(' ')[1] in ['#800026', '#440154']: - assert result[(res)][1].split(' ')[1] == '#f1f1f1' + result = df.style.background_gradient(cmap=c_map)._compute().ctx + test_colors = {None: {(0, 0): ('#440154', '#f1f1f1'), + (1, 0): ('#fde725', '#000000')}, + 'YlOrRd': {(0, 0): ('#ffffcc', '#000000'), + (1, 0): ('#800026', '#f1f1f1')}} + # Light text on dark background + assert result[0, 0][0].split(' ')[1] == test_colors[c_map][0, 0][0], ( + 'Unexpected background color returned from ' + '`style.background_gradient()`') + assert result[0, 0][1].split(' ')[1] == test_colors[c_map][0, 0][1] + # Dark text on light background + assert result[1, 0][0].split(' ')[1] == test_colors[c_map][1, 0][0], ( + 'Unexpected background color returned from ' + '`style.background_gradient()`') + assert result[1, 0][1].split(' ')[1] == test_colors[c_map][1, 0][1] @td.skip_if_no_mpl @pytest.mark.parametrize("text_color_threshold", [1.1, '1', -1, [2, 2]]) From 2c34fb97c15ff6437b59bec15e58971a57cb7a0b Mon Sep 17 00:00:00 2001 From: joelostblom Date: Sun, 3 Jun 2018 16:15:41 -0400 Subject: [PATCH 15/18] Parametrize test further to simplify assertions --- pandas/tests/io/formats/test_style.py | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/pandas/tests/io/formats/test_style.py b/pandas/tests/io/formats/test_style.py index 456e88f74726f..31978c88ee63d 100644 --- a/pandas/tests/io/formats/test_style.py +++ b/pandas/tests/io/formats/test_style.py @@ -1036,24 +1036,19 @@ def test_background_gradient(self): 'color: #000000'] @td.skip_if_no_mpl - @pytest.mark.parametrize("c_map", [None, 'YlOrRd']) - def test_text_color_threshold(self, c_map): + @pytest.mark.parametrize( + 'c_map,expected', [ + (None, [ + ['background-color: #440154', 'color: #f1f1f1'], + ['background-color: #fde725', 'color: #000000']]), + ('YlOrRd', [ + ['background-color: #ffffcc', 'color: #000000'], + ['background-color: #800026', 'color: #f1f1f1']])]) + def test_text_color_threshold(self, c_map, expected): df = pd.DataFrame([[1, 2], [2, 4]], columns=['A', 'B']) result = df.style.background_gradient(cmap=c_map)._compute().ctx - test_colors = {None: {(0, 0): ('#440154', '#f1f1f1'), - (1, 0): ('#fde725', '#000000')}, - 'YlOrRd': {(0, 0): ('#ffffcc', '#000000'), - (1, 0): ('#800026', '#f1f1f1')}} - # Light text on dark background - assert result[0, 0][0].split(' ')[1] == test_colors[c_map][0, 0][0], ( - 'Unexpected background color returned from ' - '`style.background_gradient()`') - assert result[0, 0][1].split(' ')[1] == test_colors[c_map][0, 0][1] - # Dark text on light background - assert result[1, 0][0].split(' ')[1] == test_colors[c_map][1, 0][0], ( - 'Unexpected background color returned from ' - '`style.background_gradient()`') - assert result[1, 0][1].split(' ')[1] == test_colors[c_map][1, 0][1] + assert result[0, 0] == expected[0] + assert result[1, 0] == expected[1] @td.skip_if_no_mpl @pytest.mark.parametrize("text_color_threshold", [1.1, '1', -1, [2, 2]]) From ae2e849499c5180c7a829b7aedd9e3507d68656b Mon Sep 17 00:00:00 2001 From: joelostblom Date: Sun, 3 Jun 2018 16:32:32 -0400 Subject: [PATCH 16/18] Simplify test further --- pandas/tests/io/formats/test_style.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/pandas/tests/io/formats/test_style.py b/pandas/tests/io/formats/test_style.py index 31978c88ee63d..dfda9f2695aca 100644 --- a/pandas/tests/io/formats/test_style.py +++ b/pandas/tests/io/formats/test_style.py @@ -1038,17 +1038,16 @@ def test_background_gradient(self): @td.skip_if_no_mpl @pytest.mark.parametrize( 'c_map,expected', [ - (None, [ - ['background-color: #440154', 'color: #f1f1f1'], - ['background-color: #fde725', 'color: #000000']]), - ('YlOrRd', [ - ['background-color: #ffffcc', 'color: #000000'], - ['background-color: #800026', 'color: #f1f1f1']])]) + (None, { + (0, 0): ['background-color: #440154', 'color: #f1f1f1'], + (1, 0): ['background-color: #fde725', 'color: #000000']}), + ('YlOrRd', { + (0, 0): ['background-color: #ffffcc', 'color: #000000'], + (1, 0): ['background-color: #800026', 'color: #f1f1f1']})]) def test_text_color_threshold(self, c_map, expected): - df = pd.DataFrame([[1, 2], [2, 4]], columns=['A', 'B']) + df = pd.DataFrame([1, 2], columns=['A']) result = df.style.background_gradient(cmap=c_map)._compute().ctx - assert result[0, 0] == expected[0] - assert result[1, 0] == expected[1] + assert result == expected @td.skip_if_no_mpl @pytest.mark.parametrize("text_color_threshold", [1.1, '1', -1, [2, 2]]) From 7be45594c7a8657d9671008ff496a3134dca772c Mon Sep 17 00:00:00 2001 From: joelostblom Date: Sun, 3 Jun 2018 16:36:20 -0400 Subject: [PATCH 17/18] Move matplotlib decorator to the class --- pandas/tests/io/formats/test_style.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pandas/tests/io/formats/test_style.py b/pandas/tests/io/formats/test_style.py index dfda9f2695aca..b355cda8df1bd 100644 --- a/pandas/tests/io/formats/test_style.py +++ b/pandas/tests/io/formats/test_style.py @@ -1017,9 +1017,9 @@ def test_hide_columns_mult_levels(self): assert ctx['body'][1][2]['display_value'] == 3 +@td.skip_if_no_mpl class TestStylerMatplotlibDep(object): - @td.skip_if_no_mpl def test_background_gradient(self): df = pd.DataFrame([[1, 2], [2, 4]], columns=['A', 'B']) @@ -1035,7 +1035,6 @@ def test_background_gradient(self): assert result[(1, 0)] == ['background-color: #fff7fb', 'color: #000000'] - @td.skip_if_no_mpl @pytest.mark.parametrize( 'c_map,expected', [ (None, { @@ -1049,7 +1048,6 @@ def test_text_color_threshold(self, c_map, expected): result = df.style.background_gradient(cmap=c_map)._compute().ctx assert result == expected - @td.skip_if_no_mpl @pytest.mark.parametrize("text_color_threshold", [1.1, '1', -1, [2, 2]]) def test_text_color_threshold_raises(self, text_color_threshold): df = pd.DataFrame([[1, 2], [2, 4]], columns=['A', 'B']) From 41911af6b41ecc191d0a8de29b1560b9f61b694e Mon Sep 17 00:00:00 2001 From: joelostblom Date: Sun, 3 Jun 2018 16:48:17 -0400 Subject: [PATCH 18/18] Remove ref to duplicate issue --- doc/source/whatsnew/v0.24.0.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.24.0.txt b/doc/source/whatsnew/v0.24.0.txt index 772833d28a465..0c604f9aad993 100644 --- a/doc/source/whatsnew/v0.24.0.txt +++ b/doc/source/whatsnew/v0.24.0.txt @@ -181,7 +181,7 @@ Reshaping Other ^^^^^ -- :meth: `~pandas.io.formats.style.Styler.background_gradient` now takes a ``text_color_threshold`` parameter to automatically lighten the text color based on the luminance of the background color. This improves readability with dark background colors without the need to limit the background colormap range. (:issue:`21258`, :issue:`21269`) +- :meth: `~pandas.io.formats.style.Styler.background_gradient` now takes a ``text_color_threshold`` parameter to automatically lighten the text color based on the luminance of the background color. This improves readability with dark background colors without the need to limit the background colormap range. (:issue:`21258`) - -