60
60
)
61
61
62
62
try :
63
- from matplotlib import colors
63
+ import matplotlib as mpl
64
64
import matplotlib .pyplot as plt
65
65
66
66
has_mpl = True
72
72
@contextmanager
73
73
def _mpl (func : Callable ):
74
74
if has_mpl :
75
- yield plt , colors
75
+ yield plt , mpl
76
76
else :
77
77
raise ImportError (no_mpl_message .format (func .__name__ ))
78
78
@@ -2608,7 +2608,8 @@ def bar(
2608
2608
subset : Subset | None = None ,
2609
2609
axis : Axis | None = 0 ,
2610
2610
* ,
2611
- color = "#d65f5f" ,
2611
+ color : str | list | tuple | None = None ,
2612
+ cmap : Any | None = None ,
2612
2613
width : float = 100 ,
2613
2614
height : float = 100 ,
2614
2615
align : str | float | int | Callable = "mid" ,
@@ -2631,12 +2632,16 @@ def bar(
2631
2632
Apply to each column (``axis=0`` or ``'index'``), to each row
2632
2633
(``axis=1`` or ``'columns'``), or to the entire DataFrame at once
2633
2634
with ``axis=None``.
2634
- color : str, 2-tuple/list, matplotlib Colormap
2635
+ color : str or 2-tuple/list
2635
2636
If a str is passed, the color is the same for both
2636
2637
negative and positive numbers. If 2-tuple/list is used, the
2637
2638
first element is the color_negative and the second is the
2638
- color_positive (eg: ['#d65f5f', '#5fba7d']). Alternatively, assigns
2639
- colors from a Colormap based on the datavalues.
2639
+ color_positive (eg: ['#d65f5f', '#5fba7d']).
2640
+ cmap : str, matplotlib.cm.ColorMap
2641
+ A string name of a matplotlib Colormap, or a Colormap object. Cannot be
2642
+ used together with ``color``.
2643
+
2644
+ .. versionadded:: 1.4.0
2640
2645
width : float, default 100
2641
2646
The percentage of the cell, measured from the left, in which to draw the
2642
2647
bars, in [0, 100].
@@ -2686,6 +2691,19 @@ def bar(
2686
2691
`Table Visualization <../../user_guide/style.ipynb>`_ gives
2687
2692
a number of examples for different settings and color coordination.
2688
2693
"""
2694
+ if color is None and cmap is None :
2695
+ color = "#d65f5f"
2696
+ elif color is not None and cmap is not None :
2697
+ raise ValueError ("`color` and `cmap` cannot both be given" )
2698
+ elif color is not None :
2699
+ if (isinstance (color , (list , tuple )) and len (color ) > 2 ) or not isinstance (
2700
+ color , (str , list , tuple )
2701
+ ):
2702
+ raise ValueError (
2703
+ "`color` must be string or list or tuple of 2 strings,"
2704
+ "(eg: color=['#d65f5f', '#5fba7d'])"
2705
+ )
2706
+
2689
2707
if not (0 <= width <= 100 ):
2690
2708
raise ValueError (f"`width` must be a value in [0, 100], got { width } " )
2691
2709
elif not (0 <= height <= 100 ):
@@ -2700,6 +2718,7 @@ def bar(
2700
2718
axis = axis ,
2701
2719
align = align ,
2702
2720
colors = color ,
2721
+ cmap = cmap ,
2703
2722
width = width / 100 ,
2704
2723
height = height / 100 ,
2705
2724
vmin = vmin ,
@@ -3256,12 +3275,12 @@ def _background_gradient(
3256
3275
else : # else validate gmap against the underlying data
3257
3276
gmap = _validate_apply_axis_arg (gmap , "gmap" , float , data )
3258
3277
3259
- with _mpl (Styler .background_gradient ) as (plt , colors ):
3278
+ with _mpl (Styler .background_gradient ) as (plt , mpl ):
3260
3279
smin = np .nanmin (gmap ) if vmin is None else vmin
3261
3280
smax = np .nanmax (gmap ) if vmax is None else vmax
3262
3281
rng = smax - smin
3263
3282
# extend lower / upper bounds, compresses color range
3264
- norm = colors .Normalize (smin - (rng * low ), smax + (rng * high ))
3283
+ norm = mpl . colors .Normalize (smin - (rng * low ), smax + (rng * high ))
3265
3284
rgbas = plt .cm .get_cmap (cmap )(norm (gmap ))
3266
3285
3267
3286
def relative_luminance (rgba ) -> float :
@@ -3290,9 +3309,11 @@ def css(rgba, text_only) -> str:
3290
3309
if not text_only :
3291
3310
dark = relative_luminance (rgba ) < text_color_threshold
3292
3311
text_color = "#f1f1f1" if dark else "#000000"
3293
- return f"background-color: { colors .rgb2hex (rgba )} ;color: { text_color } ;"
3312
+ return (
3313
+ f"background-color: { mpl .colors .rgb2hex (rgba )} ;color: { text_color } ;"
3314
+ )
3294
3315
else :
3295
- return f"color: { colors .rgb2hex (rgba )} ;"
3316
+ return f"color: { mpl . colors .rgb2hex (rgba )} ;"
3296
3317
3297
3318
if data .ndim == 1 :
3298
3319
return [css (rgba , text_only ) for rgba in rgbas ]
@@ -3365,7 +3386,8 @@ def _highlight_value(data: FrameOrSeries, op: str, props: str) -> np.ndarray:
3365
3386
def _bar (
3366
3387
data : FrameOrSeries ,
3367
3388
align : str | float | int | Callable ,
3368
- colors : Any ,
3389
+ colors : str | list | tuple ,
3390
+ cmap : Any ,
3369
3391
width : float ,
3370
3392
height : float ,
3371
3393
vmin : float | None ,
@@ -3427,7 +3449,7 @@ def css_bar(start: float, end: float, color: str) -> str:
3427
3449
cell_css += f" { color } { end * 100 :.1f} %, transparent { end * 100 :.1f} %)"
3428
3450
return cell_css
3429
3451
3430
- def css_calc (x , left : float , right : float , align : str , color : list | str ):
3452
+ def css_calc (x , left : float , right : float , align : str , color : str | list | tuple ):
3431
3453
"""
3432
3454
Return the correct CSS for bar placement based on calculated values.
3433
3455
@@ -3458,7 +3480,7 @@ def css_calc(x, left: float, right: float, align: str, color: list | str):
3458
3480
if pd .isna (x ):
3459
3481
return base_css
3460
3482
3461
- if isinstance (color , list ):
3483
+ if isinstance (color , ( list , tuple ) ):
3462
3484
color = color [0 ] if x < 0 else color [1 ]
3463
3485
assert isinstance (color , str ) # mypy redefinition
3464
3486
@@ -3525,25 +3547,20 @@ def css_calc(x, left: float, right: float, align: str, color: list | str):
3525
3547
)
3526
3548
3527
3549
rgbas = None
3528
- if not isinstance ( colors , ( list , str )) :
3550
+ if cmap is not None :
3529
3551
# use the matplotlib colormap input
3530
- with _mpl (Styler .bar ) as (plt , mpl_colors ):
3531
- norm = mpl_colors . Normalize ( left , right )
3532
- if not isinstance ( colors , mpl_colors . Colormap ):
3533
- raise ValueError (
3534
- "`colors` must be a matplotlib Colormap if not string "
3535
- "or list of strings."
3536
- )
3537
- rgbas = colors (norm (values ))
3552
+ with _mpl (Styler .bar ) as (plt , mpl ):
3553
+ cmap = (
3554
+ mpl . cm . get_cmap ( cmap )
3555
+ if isinstance ( cmap , str )
3556
+ else cmap # assumed to be a Colormap instance as documented
3557
+ )
3558
+ norm = mpl . colors . Normalize ( left , right )
3559
+ rgbas = cmap (norm (values ))
3538
3560
if data .ndim == 1 :
3539
- rgbas = [mpl_colors .rgb2hex (rgba ) for rgba in rgbas ]
3561
+ rgbas = [mpl . colors .rgb2hex (rgba ) for rgba in rgbas ]
3540
3562
else :
3541
- rgbas = [[mpl_colors .rgb2hex (rgba ) for rgba in row ] for row in rgbas ]
3542
- elif isinstance (colors , list ) and len (colors ) > 2 :
3543
- raise ValueError (
3544
- "`color` must be string, list-like of 2 strings, or matplotlib Colormap "
3545
- "(eg: color=['#d65f5f', '#5fba7d'])"
3546
- )
3563
+ rgbas = [[mpl .colors .rgb2hex (rgba ) for rgba in row ] for row in rgbas ]
3547
3564
3548
3565
assert isinstance (align , str ) # mypy: should now be in [left, right, mid, zero]
3549
3566
if data .ndim == 1 :
0 commit comments