@@ -36,104 +36,22 @@ def bar_from_to(x, y, color="#d65f5f"):
36
36
)
37
37
38
38
39
- class TestStylerBarAlign :
40
- def test_bar_align_mid_vmin (self ):
41
- df = DataFrame ({"A" : [0 , 1 ], "B" : [- 2 , 4 ]})
42
- result = df .style .bar (align = "mid" , axis = None , vmin = - 6 )._compute ().ctx
43
- expected = {
44
- (0 , 0 ): bar_grad (),
45
- (1 , 0 ): bar_grad (
46
- " transparent 60.0%" ,
47
- " #d65f5f 60.0%" ,
48
- " #d65f5f 70.0%" ,
49
- " transparent 70.0%" ,
50
- ),
51
- (0 , 1 ): bar_grad (
52
- " transparent 40.0%" ,
53
- " #d65f5f 40.0%" ,
54
- " #d65f5f 60.0%" ,
55
- " transparent 60.0%" ,
56
- ),
57
- (1 , 1 ): bar_grad (
58
- " transparent 60.0%" ,
59
- " #d65f5f 60.0%" ,
60
- " #d65f5f 100.0%" ,
61
- " transparent 100.0%" ,
62
- ),
63
- }
64
- assert result == expected
39
+ @pytest .fixture
40
+ def df_pos ():
41
+ return DataFrame ([[1 ], [2 ], [3 ]])
65
42
66
- def test_bar_align_mid_vmax (self ):
67
- df = DataFrame ({"A" : [0 , 1 ], "B" : [- 2 , 4 ]})
68
- result = df .style .bar (align = "mid" , axis = None , vmax = 8 )._compute ().ctx
69
- expected = {
70
- (0 , 0 ): bar_grad (),
71
- (1 , 0 ): bar_grad (
72
- " transparent 20.0%" ,
73
- " #d65f5f 20.0%" ,
74
- " #d65f5f 30.0%" ,
75
- " transparent 30.0%" ,
76
- ),
77
- (0 , 1 ): bar_grad (
78
- " #d65f5f 20.0%" ,
79
- " transparent 20.0%" ,
80
- ),
81
- (1 , 1 ): bar_grad (
82
- " transparent 20.0%" ,
83
- " #d65f5f 20.0%" ,
84
- " #d65f5f 60.0%" ,
85
- " transparent 60.0%" ,
86
- ),
87
- }
88
- assert result == expected
89
43
90
- def test_bar_align_mid_vmin_vmax_wide (self ):
91
- df = DataFrame ({"A" : [0 , 1 ], "B" : [- 2 , 4 ]})
92
- result = df .style .bar (align = "mid" , axis = None , vmin = - 3 , vmax = 7 )._compute ().ctx
93
- expected = {
94
- (0 , 0 ): bar_grad (),
95
- (1 , 0 ): bar_grad (
96
- " transparent 30.0%" ,
97
- " #d65f5f 30.0%" ,
98
- " #d65f5f 40.0%" ,
99
- " transparent 40.0%" ,
100
- ),
101
- (0 , 1 ): bar_grad (
102
- " transparent 10.0%" ,
103
- " #d65f5f 10.0%" ,
104
- " #d65f5f 30.0%" ,
105
- " transparent 30.0%" ,
106
- ),
107
- (1 , 1 ): bar_grad (
108
- " transparent 30.0%" ,
109
- " #d65f5f 30.0%" ,
110
- " #d65f5f 70.0%" ,
111
- " transparent 70.0%" ,
112
- ),
113
- }
114
- assert result == expected
44
+ @pytest .fixture
45
+ def df_neg ():
46
+ return DataFrame ([[- 1 ], [- 2 ], [- 3 ]])
115
47
116
- def test_bar_align_mid_vmin_vmax_clipping (self ):
117
- df = DataFrame ({"A" : [0 , 1 ], "B" : [- 2 , 4 ]})
118
- result = df .style .bar (align = "mid" , axis = None , vmin = - 1 , vmax = 3 )._compute ().ctx
119
- expected = {
120
- (0 , 0 ): bar_grad (),
121
- (1 , 0 ): bar_grad (
122
- " transparent 25.0%" ,
123
- " #d65f5f 25.0%" ,
124
- " #d65f5f 50.0%" ,
125
- " transparent 50.0%" ,
126
- ),
127
- (0 , 1 ): bar_grad (" #d65f5f 25.0%" , " transparent 25.0%" ),
128
- (1 , 1 ): bar_grad (
129
- " transparent 25.0%" ,
130
- " #d65f5f 25.0%" ,
131
- " #d65f5f 100.0%" ,
132
- " transparent 100.0%" ,
133
- ),
134
- }
135
- assert result == expected
136
48
49
+ @pytest .fixture
50
+ def df_mix ():
51
+ return DataFrame ([[- 3 ], [1 ], [2 ]])
52
+
53
+
54
+ class TestStylerBarAlign :
137
55
def test_bar_align_mid_nans (self ):
138
56
df = DataFrame ({"A" : [1 , None ], "B" : [- 1 , 3 ]})
139
57
result = df .style .bar (align = "mid" , axis = None )._compute ().ctx
@@ -181,12 +99,6 @@ def test_bar_align_zero_nans(self):
181
99
}
182
100
assert result == expected
183
101
184
- def test_bar_bad_align_raises (self ):
185
- df = DataFrame ({"A" : [- 100 , - 60 , - 30 , - 20 ]})
186
- msg = "`align` should be in {'left', 'right', 'mid', 'mean', 'zero'} or"
187
- with pytest .raises (ValueError , match = msg ):
188
- df .style .bar (align = "poorly" , color = ["#d65f5f" , "#5fba7d" ]).render ()
189
-
190
102
191
103
@pytest .mark .parametrize (
192
104
"align, exp" ,
@@ -200,10 +112,9 @@ def test_bar_bad_align_raises(self):
200
112
(np .median , [bar_to (50 ), no_bar (), bar_from_to (50 , 100 )]),
201
113
],
202
114
)
203
- def test_align_positive_cases (align , exp ):
115
+ def test_align_positive_cases (df_pos , align , exp ):
204
116
# test different align cases for all positive values
205
- data = DataFrame ([[1 ], [2 ], [3 ]])
206
- result = data .style .bar (align = align )._compute ().ctx
117
+ result = df_pos .style .bar (align = align )._compute ().ctx
207
118
expected = {(0 , 0 ): exp [0 ], (1 , 0 ): exp [1 ], (2 , 0 ): exp [2 ]}
208
119
assert result == expected
209
120
@@ -220,10 +131,9 @@ def test_align_positive_cases(align, exp):
220
131
(np .median , [bar_from_to (50 , 100 ), no_bar (), bar_to (50 )]),
221
132
],
222
133
)
223
- def test_align_negative_cases (align , exp ):
134
+ def test_align_negative_cases (df_neg , align , exp ):
224
135
# test different align cases for all negative values
225
- data = DataFrame ([[- 1 ], [- 2 ], [- 3 ]])
226
- result = data .style .bar (align = align )._compute ().ctx
136
+ result = df_neg .style .bar (align = align )._compute ().ctx
227
137
expected = {(0 , 0 ): exp [0 ], (1 , 0 ): exp [1 ], (2 , 0 ): exp [2 ]}
228
138
assert result == expected
229
139
@@ -240,10 +150,9 @@ def test_align_negative_cases(align, exp):
240
150
(np .median , [bar_to (50 ), no_bar (), bar_from_to (50 , 62.5 )]),
241
151
],
242
152
)
243
- def test_align_mixed_cases (align , exp ):
153
+ def test_align_mixed_cases (df_mix , align , exp ):
244
154
# test different align cases for mixed positive and negative values
245
- data = DataFrame ([[- 3 ], [1 ], [2 ]])
246
- result = data .style .bar (align = align )._compute ().ctx
155
+ result = df_mix .style .bar (align = align )._compute ().ctx
247
156
expected = {(0 , 0 ): exp [0 ], (1 , 0 ): exp [1 ], (2 , 0 ): exp [2 ]}
248
157
assert result == expected
249
158
@@ -321,6 +230,72 @@ def test_align_axis(align, exp, axis):
321
230
assert result == expected
322
231
323
232
233
+ @pytest .mark .parametrize (
234
+ "values, vmin, vmax" ,
235
+ [
236
+ ("positive" , 1.5 , 2.5 ),
237
+ ("negative" , - 2.5 , - 1.5 ),
238
+ ("mixed" , - 2.5 , 1.5 ),
239
+ ],
240
+ )
241
+ @pytest .mark .parametrize ("nullify" , [None , "vmin" , "vmax" ]) # test min/max separately
242
+ @pytest .mark .parametrize ("align" , ["left" , "right" , "zero" , "mid" ])
243
+ def test_vmin_vmax_clipping (df_pos , df_neg , df_mix , values , vmin , vmax , nullify , align ):
244
+ # test that clipping occurs if any vmin > data_values or vmax < data_values
245
+ if align == "mid" : # mid acts as left or right in each case
246
+ if values == "positive" :
247
+ align = "left"
248
+ elif values == "negative" :
249
+ align = "right"
250
+ df = {"positive" : df_pos , "negative" : df_neg , "mixed" : df_mix }[values ]
251
+ vmin = None if nullify == "vmin" else vmin
252
+ vmax = None if nullify == "vmax" else vmax
253
+
254
+ clip_df = df .where (df <= (vmax if vmax else 999 ), other = vmax )
255
+ clip_df = clip_df .where (clip_df >= (vmin if vmin else - 999 ), other = vmin )
256
+
257
+ result = (
258
+ df .style .bar (align = align , vmin = vmin , vmax = vmax , color = ["red" , "green" ])
259
+ ._compute ()
260
+ .ctx
261
+ )
262
+ expected = clip_df .style .bar (align = align , color = ["red" , "green" ])._compute ().ctx
263
+ assert result == expected
264
+
265
+
266
+ @pytest .mark .parametrize (
267
+ "values, vmin, vmax" ,
268
+ [
269
+ ("positive" , 0.5 , 4.5 ),
270
+ ("negative" , - 4.5 , - 0.5 ),
271
+ ("mixed" , - 4.5 , 4.5 ),
272
+ ],
273
+ )
274
+ @pytest .mark .parametrize ("nullify" , [None , "vmin" , "vmax" ]) # test min/max separately
275
+ @pytest .mark .parametrize ("align" , ["left" , "right" , "zero" , "mid" ])
276
+ def test_vmin_vmax_widening (df_pos , df_neg , df_mix , values , vmin , vmax , nullify , align ):
277
+ # test that widening occurs if any vmax > data_values or vmin < data_values
278
+ if align == "mid" : # mid acts as left or right in each case
279
+ if values == "positive" :
280
+ align = "left"
281
+ elif values == "negative" :
282
+ align = "right"
283
+ df = {"positive" : df_pos , "negative" : df_neg , "mixed" : df_mix }[values ]
284
+ vmin = None if nullify == "vmin" else vmin
285
+ vmax = None if nullify == "vmax" else vmax
286
+
287
+ expand_df = df .copy ()
288
+ expand_df .loc [3 , :], expand_df .loc [4 , :] = vmin , vmax
289
+
290
+ result = (
291
+ df .style .bar (align = align , vmin = vmin , vmax = vmax , color = ["red" , "green" ])
292
+ ._compute ()
293
+ .ctx
294
+ )
295
+ expected = expand_df .style .bar (align = align , color = ["red" , "green" ])._compute ().ctx
296
+ assert result .items () <= expected .items ()
297
+
298
+
324
299
def test_numerics ():
325
300
# test data is pre-selected for numeric values
326
301
data = DataFrame ([[1 , "a" ], [2 , "b" ]])
@@ -342,3 +317,10 @@ def test_colors_mixed(align, exp):
342
317
data = DataFrame ([[- 1 ], [3 ]])
343
318
result = data .style .bar (align = align , color = ["red" , "green" ])._compute ().ctx
344
319
assert result == {(0 , 0 ): exp [0 ], (1 , 0 ): exp [1 ]}
320
+
321
+
322
+ def test_bar_bad_align_raises ():
323
+ df = DataFrame ({"A" : [- 100 , - 60 , - 30 , - 20 ]})
324
+ msg = "`align` should be in {'left', 'right', 'mid', 'mean', 'zero'} or"
325
+ with pytest .raises (ValueError , match = msg ):
326
+ df .style .bar (align = "poorly" , color = ["#d65f5f" , "#5fba7d" ]).render ()
0 commit comments