@@ -863,7 +863,7 @@ def highlight_null(self, null_color='red'):
863
863
return self
864
864
865
865
def background_gradient (self , cmap = 'PuBu' , low = 0 , high = 0 , axis = 0 ,
866
- subset = None ):
866
+ subset = None , text_color_threshold = 0.408 ):
867
867
"""
868
868
Color the background in a gradient according to
869
869
the data in each column (optionally row).
@@ -879,26 +879,39 @@ def background_gradient(self, cmap='PuBu', low=0, high=0, axis=0,
879
879
1 or 'columns' for columnwise, 0 or 'index' for rowwise
880
880
subset: IndexSlice
881
881
a valid slice for ``data`` to limit the style application to
882
+ text_color_threshold: float or int
883
+ luminance threshold for determining text color. Facilitates text
884
+ visibility across varying background colors. From 0 to 1.
885
+ 0 = all text is dark colored, 1 = all text is light colored.
886
+
887
+ .. versionadded:: 0.24.0
882
888
883
889
Returns
884
890
-------
885
891
self : Styler
886
892
887
893
Notes
888
894
-----
889
- Tune ``low`` and ``high`` to keep the text legible by
890
- not using the entire range of the color map. These extend
891
- the range of the data by ``low * (x.max() - x.min())``
892
- and ``high * (x.max() - x.min())`` before normalizing.
895
+ Set ``text_color_threshold`` or tune ``low`` and ``high`` to keep the
896
+ text legible by not using the entire range of the color map. The range
897
+ of the data is extended by ``low * (x.max() - x.min())`` and ``high *
898
+ (x.max() - x.min())`` before normalizing.
899
+
900
+ Raises
901
+ ------
902
+ ValueError
903
+ If ``text_color_threshold`` is not a value from 0 to 1.
893
904
"""
894
905
subset = _maybe_numeric_slice (self .data , subset )
895
906
subset = _non_reducing_slice (subset )
896
907
self .apply (self ._background_gradient , cmap = cmap , subset = subset ,
897
- axis = axis , low = low , high = high )
908
+ axis = axis , low = low , high = high ,
909
+ text_color_threshold = text_color_threshold )
898
910
return self
899
911
900
912
@staticmethod
901
- def _background_gradient (s , cmap = 'PuBu' , low = 0 , high = 0 ):
913
+ def _background_gradient (s , cmap = 'PuBu' , low = 0 , high = 0 ,
914
+ text_color_threshold = 0.408 ):
902
915
"""Color background in a range according to the data."""
903
916
with _mpl (Styler .background_gradient ) as (plt , colors ):
904
917
rng = s .max () - s .min ()
@@ -909,8 +922,39 @@ def _background_gradient(s, cmap='PuBu', low=0, high=0):
909
922
# https://github.com/matplotlib/matplotlib/issues/5427
910
923
normed = norm (s .values )
911
924
c = [colors .rgb2hex (x ) for x in plt .cm .get_cmap (cmap )(normed )]
912
- return ['background-color: {color}' .format (color = color )
913
- for color in c ]
925
+ if (not isinstance (text_color_threshold , (float , int )) or
926
+ not 0 <= text_color_threshold <= 1 ):
927
+ msg = "`text_color_threshold` must be a value from 0 to 1."
928
+ raise ValueError (msg )
929
+
930
+ def relative_luminance (color ):
931
+ """
932
+ Calculate relative luminance of a color.
933
+
934
+ The calculation adheres to the W3C standards
935
+ (https://www.w3.org/WAI/GL/wiki/Relative_luminance)
936
+
937
+ Parameters
938
+ ----------
939
+ color : matplotlib color
940
+ Hex code, rgb-tuple, or HTML color name.
941
+
942
+ Returns
943
+ -------
944
+ float
945
+ The relative luminance as a value from 0 to 1
946
+ """
947
+ rgb = colors .colorConverter .to_rgba_array (color )[:, :3 ]
948
+ rgb = np .where (rgb <= .03928 , rgb / 12.92 ,
949
+ ((rgb + .055 ) / 1.055 ) ** 2.4 )
950
+ lum = rgb .dot ([.2126 , .7152 , .0722 ])
951
+ return lum .item ()
952
+
953
+ text_colors = ['#f1f1f1' if relative_luminance (x ) <
954
+ text_color_threshold else '#000000' for x in c ]
955
+
956
+ return ['background-color: {color};color: {tc}' .format (
957
+ color = color , tc = tc ) for color , tc in zip (c , text_colors )]
914
958
915
959
def set_properties (self , subset = None , ** kwargs ):
916
960
"""
0 commit comments