@@ -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 = 0.3 ):
867
867
"""
868
868
Color the background in a gradient according to
869
869
the data in each column (optionally row).
@@ -879,26 +879,30 @@ 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: 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 colored black, 1 = all text is colored white.
882
886
883
887
Returns
884
888
-------
885
889
self : Styler
886
890
887
891
Notes
888
892
-----
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.
893
+ Set ``text_color`` or tune `` low`` and ``high`` to keep the text
894
+ legible by not using the entire range of the color map. The range of
895
+ the data is extended by ``low * (x.max() - x.min())`` and ``high *
896
+ (x.max() - x.min())`` before normalizing.
893
897
"""
894
898
subset = _maybe_numeric_slice (self .data , subset )
895
899
subset = _non_reducing_slice (subset )
896
900
self .apply (self ._background_gradient , cmap = cmap , subset = subset ,
897
- axis = axis , low = low , high = high )
901
+ axis = axis , low = low , high = high , text_color = text_color )
898
902
return self
899
903
900
904
@staticmethod
901
- def _background_gradient (s , cmap = 'PuBu' , low = 0 , high = 0 ):
905
+ def _background_gradient (s , cmap = 'PuBu' , low = 0 , high = 0 , text_color = 0.3 ):
902
906
"""Color background in a range according to the data."""
903
907
with _mpl (Styler .background_gradient ) as (plt , colors ):
904
908
rng = s .max () - s .min ()
@@ -909,8 +913,33 @@ def _background_gradient(s, cmap='PuBu', low=0, high=0):
909
913
# https://github.com/matplotlib/matplotlib/issues/5427
910
914
normed = norm (s .values )
911
915
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 ]
916
+ if (not isinstance (text_color , (float , int )) or
917
+ not 0 <= text_color <= 1 ):
918
+ msg = "`text_color` must be a value from 0 to 1."
919
+ raise ValueError (msg )
920
+
921
+ def relative_luminance (color ):
922
+ """Calculate the relative luminance of a color according to W3C
923
+ standards, https://www.w3.org/WAI/GL/wiki/Relative_luminance
924
+ Parameters
925
+ ----------
926
+ color : matplotlib color. Hex code, rgb-tuple, or
927
+ HTML color name.
928
+ Returns
929
+ -------
930
+ luminance : float between 0 and 1
931
+ """
932
+ rgb = colors .colorConverter .to_rgba_array (color )[:, :3 ]
933
+ rgb = np .where (rgb <= .03928 , rgb / 12.92 ,
934
+ ((rgb + .055 ) / 1.055 ) ** 2.4 )
935
+ lum = rgb .dot ([.2126 , .7152 , .0722 ])
936
+ return lum .item ()
937
+
938
+ text_colors = ['#ffffff' if relative_luminance (x ) < text_color
939
+ else '#000000' for x in c ]
940
+
941
+ return ['background-color: {color};color: {tc}' .format (
942
+ color = color , tc = tc ) for color , tc in zip (c , text_colors )]
914
943
915
944
def set_properties (self , subset = None , ** kwargs ):
916
945
"""
0 commit comments