@@ -140,9 +140,15 @@ def __init__(
140
140
self ._display_funcs_index : DefaultDict [ # maps (row, level) -> format func
141
141
tuple [int , int ], Callable [[Any ], str ]
142
142
] = defaultdict (lambda : partial (_default_formatter , precision = precision ))
143
+ self ._display_funcs_index_names : DefaultDict [ # maps index level -> format func
144
+ int , Callable [[Any ], str ]
145
+ ] = defaultdict (lambda : partial (_default_formatter , precision = precision ))
143
146
self ._display_funcs_columns : DefaultDict [ # maps (level, col) -> format func
144
147
tuple [int , int ], Callable [[Any ], str ]
145
148
] = defaultdict (lambda : partial (_default_formatter , precision = precision ))
149
+ self ._display_funcs_column_names : DefaultDict [ # maps col level -> format func
150
+ int , Callable [[Any ], str ]
151
+ ] = defaultdict (lambda : partial (_default_formatter , precision = precision ))
146
152
147
153
def _render (
148
154
self ,
@@ -460,6 +466,12 @@ def _generate_col_header_row(
460
466
] * (self .index .nlevels - sum (self .hide_index_ ) - 1 )
461
467
462
468
name = self .data .columns .names [r ]
469
+
470
+ is_display = name is not None and not self .hide_column_names
471
+ value = name if is_display else self .css ["blank_value" ]
472
+ display_value = (
473
+ self ._display_funcs_column_names [r ](value ) if is_display else None
474
+ )
463
475
column_name = [
464
476
_element (
465
477
"th" ,
@@ -468,10 +480,9 @@ def _generate_col_header_row(
468
480
if name is None
469
481
else f"{ self .css ['index_name' ]} { self .css ['level' ]} { r } "
470
482
),
471
- name
472
- if (name is not None and not self .hide_column_names )
473
- else self .css ["blank_value" ],
483
+ value ,
474
484
not all (self .hide_index_ ),
485
+ display_value = display_value ,
475
486
)
476
487
]
477
488
@@ -553,6 +564,9 @@ def _generate_index_names_row(
553
564
f"{ self .css ['index_name' ]} { self .css ['level' ]} { c } " ,
554
565
self .css ["blank_value" ] if name is None else name ,
555
566
not self .hide_index_ [c ],
567
+ display_value = (
568
+ None if name is None else self ._display_funcs_index_names [c ](name )
569
+ ),
556
570
)
557
571
for c , name in enumerate (self .data .index .names )
558
572
]
@@ -1005,6 +1019,7 @@ def format(
1005
1019
Returns
1006
1020
-------
1007
1021
Styler
1022
+ Returns itself for chaining.
1008
1023
1009
1024
See Also
1010
1025
--------
@@ -1261,6 +1276,7 @@ def format_index(
1261
1276
Returns
1262
1277
-------
1263
1278
Styler
1279
+ Returns itself for chaining.
1264
1280
1265
1281
See Also
1266
1282
--------
@@ -1425,6 +1441,7 @@ def relabel_index(
1425
1441
Returns
1426
1442
-------
1427
1443
Styler
1444
+ Returns itself for chaining.
1428
1445
1429
1446
See Also
1430
1447
--------
@@ -1560,6 +1577,140 @@ def alias_(x, value):
1560
1577
1561
1578
return self
1562
1579
1580
+ def format_index_names (
1581
+ self ,
1582
+ formatter : ExtFormatter | None = None ,
1583
+ axis : Axis = 0 ,
1584
+ level : Level | list [Level ] | None = None ,
1585
+ na_rep : str | None = None ,
1586
+ precision : int | None = None ,
1587
+ decimal : str = "." ,
1588
+ thousands : str | None = None ,
1589
+ escape : str | None = None ,
1590
+ hyperlinks : str | None = None ,
1591
+ ) -> StylerRenderer :
1592
+ r"""
1593
+ Format the text display value of index names or column names.
1594
+
1595
+ .. versionadded:: 3.0
1596
+
1597
+ Parameters
1598
+ ----------
1599
+ formatter : str, callable, dict or None
1600
+ Object to define how values are displayed. See notes.
1601
+ axis : {0, "index", 1, "columns"}
1602
+ Whether to apply the formatter to the index or column headers.
1603
+ level : int, str, list
1604
+ The level(s) over which to apply the generic formatter.
1605
+ na_rep : str, optional
1606
+ Representation for missing values.
1607
+ If ``na_rep`` is None, no special formatting is applied.
1608
+ precision : int, optional
1609
+ Floating point precision to use for display purposes, if not determined by
1610
+ the specified ``formatter``.
1611
+ decimal : str, default "."
1612
+ Character used as decimal separator for floats, complex and integers.
1613
+ thousands : str, optional, default None
1614
+ Character used as thousands separator for floats, complex and integers.
1615
+ escape : str, optional
1616
+ Use 'html' to replace the characters ``&``, ``<``, ``>``, ``'``, and ``"``
1617
+ in cell display string with HTML-safe sequences.
1618
+ Use 'latex' to replace the characters ``&``, ``%``, ``$``, ``#``, ``_``,
1619
+ ``{``, ``}``, ``~``, ``^``, and ``\`` in the cell display string with
1620
+ LaTeX-safe sequences.
1621
+ Escaping is done before ``formatter``.
1622
+ hyperlinks : {"html", "latex"}, optional
1623
+ Convert string patterns containing https://, http://, ftp:// or www. to
1624
+ HTML <a> tags as clickable URL hyperlinks if "html", or LaTeX \href
1625
+ commands if "latex".
1626
+
1627
+ Returns
1628
+ -------
1629
+ Styler
1630
+ Returns itself for chaining.
1631
+
1632
+ Raises
1633
+ ------
1634
+ ValueError
1635
+ If the `formatter` is a string and the dtypes are incompatible.
1636
+
1637
+ See Also
1638
+ --------
1639
+ Styler.format_index: Format the text display value of index labels
1640
+ or column headers.
1641
+
1642
+ Notes
1643
+ -----
1644
+ This method has a similar signature to :meth:`Styler.format_index`. Since
1645
+ `names` are generally label based, and often not numeric, the typical features
1646
+ expected to be more frequently used here are ``escape`` and ``hyperlinks``.
1647
+
1648
+ .. warning::
1649
+ `Styler.format_index_names` is ignored when using the output format
1650
+ `Styler.to_excel`, since Excel and Python have inherrently different
1651
+ formatting structures.
1652
+
1653
+ Examples
1654
+ --------
1655
+ >>> df = pd.DataFrame(
1656
+ ... [[1, 2], [3, 4]],
1657
+ ... index=pd.Index(["a", "b"], name="idx"),
1658
+ ... )
1659
+ >>> df # doctest: +SKIP
1660
+ 0 1
1661
+ idx
1662
+ a 1 2
1663
+ b 3 4
1664
+ >>> df.style.format_index_names(lambda x: x.upper(), axis=0) # doctest: +SKIP
1665
+ 0 1
1666
+ IDX
1667
+ a 1 2
1668
+ b 3 4
1669
+ """
1670
+ axis = self .data ._get_axis_number (axis )
1671
+ if axis == 0 :
1672
+ display_funcs_ , obj = self ._display_funcs_index_names , self .index
1673
+ else :
1674
+ display_funcs_ , obj = self ._display_funcs_column_names , self .columns
1675
+ levels_ = refactor_levels (level , obj )
1676
+
1677
+ if all (
1678
+ (
1679
+ formatter is None ,
1680
+ level is None ,
1681
+ precision is None ,
1682
+ decimal == "." ,
1683
+ thousands is None ,
1684
+ na_rep is None ,
1685
+ escape is None ,
1686
+ hyperlinks is None ,
1687
+ )
1688
+ ):
1689
+ display_funcs_ .clear ()
1690
+ return self # clear the formatter / revert to default and avoid looping
1691
+
1692
+ if not isinstance (formatter , dict ):
1693
+ formatter = {level : formatter for level in levels_ }
1694
+ else :
1695
+ formatter = {
1696
+ obj ._get_level_number (level ): formatter_
1697
+ for level , formatter_ in formatter .items ()
1698
+ }
1699
+
1700
+ for lvl in levels_ :
1701
+ format_func = _maybe_wrap_formatter (
1702
+ formatter .get (lvl ),
1703
+ na_rep = na_rep ,
1704
+ precision = precision ,
1705
+ decimal = decimal ,
1706
+ thousands = thousands ,
1707
+ escape = escape ,
1708
+ hyperlinks = hyperlinks ,
1709
+ )
1710
+ display_funcs_ [lvl ] = format_func
1711
+
1712
+ return self
1713
+
1563
1714
1564
1715
def _element (
1565
1716
html_element : str ,
@@ -1571,7 +1722,7 @@ def _element(
1571
1722
"""
1572
1723
Template to return container with information for a <td></td> or <th></th> element.
1573
1724
"""
1574
- if "display_value" not in kwargs :
1725
+ if "display_value" not in kwargs or kwargs [ "display_value" ] is None :
1575
1726
kwargs ["display_value" ] = value
1576
1727
return {
1577
1728
"type" : html_element ,
0 commit comments