@@ -993,132 +993,67 @@ def set_properties(self, subset=None, **kwargs):
993993 return self .applymap (f , subset = subset )
994994
995995 @staticmethod
996- def _bar_left (s , color , width , base ):
997- """
998- The minimum value is aligned at the left of the cell
999- Parameters
1000- ----------
1001- color: 2-tuple/list, of [``color_negative``, ``color_positive``]
1002- width: float
1003- A number between 0 or 100. The largest value will cover ``width``
1004- percent of the cell's width
1005- base: str
1006- The base css format of the cell, e.g.:
1007- ``base = 'width: 10em; height: 80%;'``
1008- Returns
1009- -------
1010- self : Styler
1011- """
1012- normed = width * (s - s .min ()) / (s .max () - s .min ())
1013- zero_normed = width * (0 - s .min ()) / (s .max () - s .min ())
1014- attrs = (base + 'background: linear-gradient(90deg,{c} {w:.1f}%, '
1015- 'transparent 0%)' )
1016-
1017- return [base if x == 0 else attrs .format (c = color [0 ], w = x )
1018- if x < zero_normed
1019- else attrs .format (c = color [1 ], w = x ) if x >= zero_normed
1020- else base for x in normed ]
1021-
1022- @staticmethod
1023- def _bar_center_zero (s , color , width , base ):
1024- """
1025- Creates a bar chart where the zero is centered in the cell
1026- Parameters
1027- ----------
1028- color: 2-tuple/list, of [``color_negative``, ``color_positive``]
1029- width: float
1030- A number between 0 or 100. The largest value will cover ``width``
1031- percent of the cell's width
1032- base: str
1033- The base css format of the cell, e.g.:
1034- ``base = 'width: 10em; height: 80%;'``
1035- Returns
1036- -------
1037- self : Styler
1038- """
1039-
1040- # Either the min or the max should reach the edge
1041- # (50%, centered on zero)
1042- m = max (abs (s .min ()), abs (s .max ()))
1043-
1044- normed = s * 50 * width / (100.0 * m )
1045-
1046- attrs_neg = (base + 'background: linear-gradient(90deg, transparent 0%'
1047- ', transparent {w:.1f}%, {c} {w:.1f}%, '
1048- '{c} 50%, transparent 50%)' )
1049-
1050- attrs_pos = (base + 'background: linear-gradient(90deg, transparent 0%'
1051- ', transparent 50%, {c} 50%, {c} {w:.1f}%, '
1052- 'transparent {w:.1f}%)' )
1053-
1054- return [attrs_pos .format (c = color [1 ], w = (50 + x )) if x >= 0
1055- else attrs_neg .format (c = color [0 ], w = (50 + x ))
1056- for x in normed ]
996+ def _bar (s , align , colors , width = 100 ):
997+ """Draw bar chart in dataframe cells"""
998+
999+ # Get input value range.
1000+ smin = s .values .min ()
1001+ smax = s .values .max ()
1002+ if align == 'mid' :
1003+ smin = min (0 , smin )
1004+ smax = max (0 , smax )
1005+ elif align == 'zero' :
1006+ # For "zero" mode, we want the range to be symmetrical around zero.
1007+ smax = max (abs (smin ), abs (smax ))
1008+ smin = - smax
1009+ # Transform to percent-range of linear-gradient
1010+ normed = width * (s .values - smin ) / (smax - smin + 1e-12 )
1011+ zero = - width * smin / (smax - smin + 1e-12 )
1012+
1013+ def css_bar (start , end , color ):
1014+ """Generate CSS code to draw a bar from start to end."""
1015+ css = 'width: 10em; height: 80%;'
1016+ if end > start :
1017+ css += 'background: linear-gradient(90deg,'
1018+ if start > 0 :
1019+ css += ' transparent {s:.1f}%, {c} {s:.1f}%, ' .format (
1020+ s = start , c = color
1021+ )
1022+ css += '{c} {e:.1f}%, transparent {e:.1f}%)' .format (
1023+ e = end , c = color ,
1024+ )
1025+ return css
10571026
1058- @staticmethod
1059- def _bar_center_mid (s , color , width , base ):
1060- """
1061- Creates a bar chart where the midpoint is centered in the cell
1062- Parameters
1063- ----------
1064- color: 2-tuple/list, of [``color_negative``, ``color_positive``]
1065- width: float
1066- A number between 0 or 100. The largest value will cover ``width``
1067- percent of the cell's width
1068- base: str
1069- The base css format of the cell, e.g.:
1070- ``base = 'width: 10em; height: 80%;'``
1071- Returns
1072- -------
1073- self : Styler
1074- """
1027+ def css (x ):
1028+ if align == 'left' :
1029+ return css_bar (0 , x , colors [x > zero ])
1030+ else :
1031+ return css_bar (min (x , zero ), max (x , zero ), colors [x > zero ])
10751032
1076- if s .min () >= 0 :
1077- # In this case, we place the zero at the left, and the max() should
1078- # be at width
1079- zero = 0.0
1080- slope = width / s .max ()
1081- elif s .max () <= 0 :
1082- # In this case, we place the zero at the right, and the min()
1083- # should be at 100-width
1084- zero = 100.0
1085- slope = width / - s .min ()
1033+ if s .ndim == 1 :
1034+ return [css (x ) for x in normed ]
10861035 else :
1087- slope = width / (s .max () - s .min ())
1088- zero = (100.0 + width ) / 2.0 - slope * s .max ()
1089-
1090- normed = zero + slope * s
1091-
1092- attrs_neg = (base + 'background: linear-gradient(90deg, transparent 0%'
1093- ', transparent {w:.1f}%, {c} {w:.1f}%, '
1094- '{c} {zero:.1f}%, transparent {zero:.1f}%)' )
1095-
1096- attrs_pos = (base + 'background: linear-gradient(90deg, transparent 0%'
1097- ', transparent {zero:.1f}%, {c} {zero:.1f}%, '
1098- '{c} {w:.1f}%, transparent {w:.1f}%)' )
1099-
1100- return [attrs_pos .format (c = color [1 ], zero = zero , w = x ) if x > zero
1101- else attrs_neg .format (c = color [0 ], zero = zero , w = x )
1102- for x in normed ]
1036+ return pd .DataFrame (
1037+ [[css (x ) for x in row ] for row in normed ],
1038+ index = s .index , columns = s .columns
1039+ )
11031040
11041041 def bar (self , subset = None , axis = 0 , color = '#d65f5f' , width = 100 ,
11051042 align = 'left' ):
11061043 """
1107- Color the background ``color`` proportional to the values in each
1108- column.
1109- Excludes non-numeric data by default.
1044+ Draw bar chart in the cell backgrounds.
11101045
11111046 Parameters
11121047 ----------
1113- subset: IndexSlice, default None
1048+ subset: IndexSlice, optional
11141049 a valid slice for ``data`` to limit the style application to
1115- axis: int
1050+ axis: int, default 0, meaning column-wise
11161051 color: str or 2-tuple/list
11171052 If a str is passed, the color is the same for both
11181053 negative and positive numbers. If 2-tuple/list is used, the
11191054 first element is the color_negative and the second is the
11201055 color_positive (eg: ['#d65f5f', '#5fba7d'])
1121- width: float
1056+ width: float, default 100
11221057 A number between 0 or 100. The largest value will cover ``width``
11231058 percent of the cell's width
11241059 align : {'left', 'zero',' mid'}, default 'left'
@@ -1134,33 +1069,22 @@ def bar(self, subset=None, axis=0, color='#d65f5f', width=100,
11341069 -------
11351070 self : Styler
11361071 """
1137- subset = _maybe_numeric_slice (self .data , subset )
1138- subset = _non_reducing_slice (subset )
1139-
1140- base = 'width: 10em; height: 80%;'
1072+ if align not in ('left' , 'zero' , 'mid' ):
1073+ raise ValueError ("`align` must be one of {'left', 'zero',' mid'}" )
11411074
1142- if not (is_list_like (color )):
1075+ if not (is_list_like (color )):
11431076 color = [color , color ]
11441077 elif len (color ) == 1 :
11451078 color = [color [0 ], color [0 ]]
11461079 elif len (color ) > 2 :
1147- msg = ("Must pass `color` as string or a list-like"
1148- " of length 2: [`color_negative`, `color_positive`]\n "
1149- "(eg: color=['#d65f5f', '#5fba7d'])" )
1150- raise ValueError (msg )
1080+ raise ValueError ("`color` must be string or a list-like"
1081+ " of length 2: [`color_neg`, `color_pos`]"
1082+ " (eg: color=['#d65f5f', '#5fba7d'])" )
11511083
1152- if align == 'left' :
1153- self .apply (self ._bar_left , subset = subset , axis = axis , color = color ,
1154- width = width , base = base )
1155- elif align == 'zero' :
1156- self .apply (self ._bar_center_zero , subset = subset , axis = axis ,
1157- color = color , width = width , base = base )
1158- elif align == 'mid' :
1159- self .apply (self ._bar_center_mid , subset = subset , axis = axis ,
1160- color = color , width = width , base = base )
1161- else :
1162- msg = ("`align` must be one of {'left', 'zero',' mid'}" )
1163- raise ValueError (msg )
1084+ subset = _maybe_numeric_slice (self .data , subset )
1085+ subset = _non_reducing_slice (subset )
1086+ self .apply (self ._bar , subset = subset , axis = axis ,
1087+ align = align , colors = color , width = width )
11641088
11651089 return self
11661090
0 commit comments