Skip to content

Commit 314f3da

Browse files
authored
ENH: kw names to Styler.hide_index (#43346)
1 parent 2ec05a5 commit 314f3da

File tree

3 files changed

+104
-2
lines changed

3 files changed

+104
-2
lines changed

pandas/io/formats/style.py

+57-1
Original file line numberDiff line numberDiff line change
@@ -1132,6 +1132,8 @@ def _copy(self, deepcopy: bool = False) -> Styler:
11321132
shallow = [ # simple string or boolean immutables
11331133
"hide_index_",
11341134
"hide_columns_",
1135+
"hide_column_names",
1136+
"hide_index_names",
11351137
"table_attributes",
11361138
"cell_ids",
11371139
"caption",
@@ -2042,6 +2044,7 @@ def hide_index(
20422044
self,
20432045
subset: Subset | None = None,
20442046
level: Level | list[Level] | None = None,
2047+
names: bool = False,
20452048
) -> Styler:
20462049
"""
20472050
Hide the entire index, or specific keys in the index from rendering.
@@ -2065,6 +2068,11 @@ def hide_index(
20652068
The level(s) to hide in a MultiIndex if hiding the entire index. Cannot be
20662069
used simultaneously with ``subset``.
20672070
2071+
.. versionadded:: 1.4.0
2072+
names : bool
2073+
Whether to hide the index name(s), in the case the index or part of it
2074+
remains visible.
2075+
20682076
.. versionadded:: 1.4.0
20692077
20702078
Returns
@@ -2118,7 +2126,7 @@ def hide_index(
21182126
21192127
Hide a specific level:
21202128
2121-
>>> df.style.format("{:,.1f").hide_index(level=1) # doctest: +SKIP
2129+
>>> df.style.format("{:,.1f}").hide_index(level=1) # doctest: +SKIP
21222130
x y
21232131
a b c a b c
21242132
x 0.1 0.0 0.4 1.3 0.6 -1.4
@@ -2127,11 +2135,29 @@ def hide_index(
21272135
y 0.4 1.0 -0.2 -0.8 -1.2 1.1
21282136
-0.6 1.2 1.8 1.9 0.3 0.3
21292137
0.8 0.5 -0.3 1.2 2.2 -0.8
2138+
2139+
Hiding just the index level names:
2140+
2141+
>>> df.index.names = ["lev0", "lev1"]
2142+
>>> df.style.format("{:,.1f}").hide_index(names=True) # doctest: +SKIP
2143+
x y
2144+
a b c a b c
2145+
x a 0.1 0.0 0.4 1.3 0.6 -1.4
2146+
b 0.7 1.0 1.3 1.5 -0.0 -0.2
2147+
c 1.4 -0.8 1.6 -0.2 -0.4 -0.3
2148+
y a 0.4 1.0 -0.2 -0.8 -1.2 1.1
2149+
b -0.6 1.2 1.8 1.9 0.3 0.3
2150+
c 0.8 0.5 -0.3 1.2 2.2 -0.8
21302151
"""
21312152
if level is not None and subset is not None:
21322153
raise ValueError("`subset` and `level` cannot be passed simultaneously")
21332154

21342155
if subset is None:
2156+
if level is None and names:
2157+
# this combination implies user shows the index and hides just names
2158+
self.hide_index_names = True
2159+
return self
2160+
21352161
levels_ = _refactor_levels(level, self.index)
21362162
self.hide_index_ = [
21372163
True if lev in levels_ else False for lev in range(self.index.nlevels)
@@ -2144,12 +2170,16 @@ def hide_index(
21442170
# error: Incompatible types in assignment (expression has type
21452171
# "ndarray", variable has type "Sequence[int]")
21462172
self.hidden_rows = hrows # type: ignore[assignment]
2173+
2174+
if names:
2175+
self.hide_index_names = True
21472176
return self
21482177

21492178
def hide_columns(
21502179
self,
21512180
subset: Subset | None = None,
21522181
level: Level | list[Level] | None = None,
2182+
names: bool = False,
21532183
) -> Styler:
21542184
"""
21552185
Hide the column headers or specific keys in the columns from rendering.
@@ -2173,6 +2203,11 @@ def hide_columns(
21732203
The level(s) to hide in a MultiIndex if hiding the entire column headers
21742204
row. Cannot be used simultaneously with ``subset``.
21752205
2206+
.. versionadded:: 1.4.0
2207+
names : bool
2208+
Whether to hide the column index name(s), in the case all column headers,
2209+
or some levels, are visible.
2210+
21762211
.. versionadded:: 1.4.0
21772212
21782213
Returns
@@ -2239,11 +2274,29 @@ def hide_columns(
22392274
y a 0.4 1.0 -0.2 -0.8 -1.2 1.1
22402275
b -0.6 1.2 1.8 1.9 0.3 0.3
22412276
c 0.8 0.5 -0.3 1.2 2.2 -0.8
2277+
2278+
Hiding just the column level names:
2279+
2280+
>>> df.columns.names = ["lev0", "lev1"]
2281+
>>> df.style.format("{:.1f").hide_columns(names=True) # doctest: +SKIP
2282+
x y
2283+
a b c a b c
2284+
x a 0.1 0.0 0.4 1.3 0.6 -1.4
2285+
b 0.7 1.0 1.3 1.5 -0.0 -0.2
2286+
c 1.4 -0.8 1.6 -0.2 -0.4 -0.3
2287+
y a 0.4 1.0 -0.2 -0.8 -1.2 1.1
2288+
b -0.6 1.2 1.8 1.9 0.3 0.3
2289+
c 0.8 0.5 -0.3 1.2 2.2 -0.8
22422290
"""
22432291
if level is not None and subset is not None:
22442292
raise ValueError("`subset` and `level` cannot be passed simultaneously")
22452293

22462294
if subset is None:
2295+
if level is None and names:
2296+
# this combination implies user shows the column headers but hides names
2297+
self.hide_column_names = True
2298+
return self
2299+
22472300
levels_ = _refactor_levels(level, self.columns)
22482301
self.hide_columns_ = [
22492302
True if lev in levels_ else False for lev in range(self.columns.nlevels)
@@ -2256,6 +2309,9 @@ def hide_columns(
22562309
# error: Incompatible types in assignment (expression has type
22572310
# "ndarray", variable has type "Sequence[int]")
22582311
self.hidden_columns = hcols # type: ignore[assignment]
2312+
2313+
if names:
2314+
self.hide_column_names = True
22592315
return self
22602316

22612317
# -----------------------------------------------------------------------

pandas/io/formats/style_render.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ def __init__(
9898
self.cell_ids = cell_ids
9999

100100
# add rendering variables
101+
self.hide_index_names: bool = False
102+
self.hide_column_names: bool = False
101103
self.hide_index_: list = [False] * self.index.nlevels
102104
self.hide_columns_: list = [False] * self.columns.nlevels
103105
self.hidden_rows: Sequence[int] = [] # sequence for specific hidden rows/cols
@@ -335,7 +337,9 @@ def _translate_header(
335337
_element(
336338
"th",
337339
f"{blank_class if name is None else index_name_class} level{r}",
338-
name if name is not None else blank_value,
340+
name
341+
if (name is not None and not self.hide_column_names)
342+
else blank_value,
339343
not all(self.hide_index_),
340344
)
341345
]
@@ -384,6 +388,7 @@ def _translate_header(
384388
and com.any_not_none(*self.data.index.names)
385389
and not all(self.hide_index_)
386390
and not all(self.hide_columns_)
391+
and not self.hide_index_names
387392
):
388393
index_names = [
389394
_element(

pandas/tests/io/formats/style/test_style.py

+41
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,10 @@ def mi_styler_comp(mi_styler):
4848
mi_styler.set_table_styles([{"selector": "a", "props": "a:v;"}])
4949
mi_styler.hide_columns()
5050
mi_styler.hide_columns([("c0", "c1_a")])
51+
mi_styler.hide_columns(names=True)
5152
mi_styler.hide_index()
5253
mi_styler.hide_index([("i0", "i1_a")])
54+
mi_styler.hide_index(names=True)
5355
mi_styler.set_table_attributes('class="box"')
5456
mi_styler.format(na_rep="MISSING", precision=3)
5557
mi_styler.highlight_max(axis=None)
@@ -239,6 +241,8 @@ def test_copy(comprehensive, render, deepcopy, mi_styler, mi_styler_comp):
239241
"cell_ids",
240242
"hide_index_",
241243
"hide_columns_",
244+
"hide_index_names",
245+
"hide_column_names",
242246
"table_attributes",
243247
]
244248
for attr in shallow:
@@ -1398,3 +1402,40 @@ def test_non_reducing_multi_slice_on_multiindex(self, slice_):
13981402
with tm.assert_produces_warning(warn, match=msg):
13991403
result = df.loc[non_reducing_slice(slice_)]
14001404
tm.assert_frame_equal(result, expected)
1405+
1406+
1407+
def test_hidden_index_names(mi_df):
1408+
mi_df.index.names = ["Lev0", "Lev1"]
1409+
mi_styler = mi_df.style
1410+
ctx = mi_styler._translate(True, True)
1411+
assert len(ctx["head"]) == 3 # 2 column index levels + 1 index names row
1412+
1413+
mi_styler.hide_index(names=True)
1414+
ctx = mi_styler._translate(True, True)
1415+
assert len(ctx["head"]) == 2 # index names row is unparsed
1416+
for i in range(4):
1417+
assert ctx["body"][0][i]["is_visible"] # 2 index levels + 2 data values visible
1418+
1419+
mi_styler.hide_index(level=1)
1420+
ctx = mi_styler._translate(True, True)
1421+
assert len(ctx["head"]) == 2 # index names row is still hidden
1422+
assert ctx["body"][0][0]["is_visible"] is True
1423+
assert ctx["body"][0][1]["is_visible"] is False
1424+
1425+
1426+
def test_hidden_column_names(mi_df):
1427+
mi_df.columns.names = ["Lev0", "Lev1"]
1428+
mi_styler = mi_df.style
1429+
ctx = mi_styler._translate(True, True)
1430+
assert ctx["head"][0][1]["display_value"] == "Lev0"
1431+
assert ctx["head"][1][1]["display_value"] == "Lev1"
1432+
1433+
mi_styler.hide_columns(names=True)
1434+
ctx = mi_styler._translate(True, True)
1435+
assert ctx["head"][0][1]["display_value"] == " "
1436+
assert ctx["head"][1][1]["display_value"] == " "
1437+
1438+
mi_styler.hide_columns(level=0)
1439+
ctx = mi_styler._translate(True, True)
1440+
assert len(ctx["head"]) == 1 # no index names and only one visible column headers
1441+
assert ctx["head"][0][1]["display_value"] == " "

0 commit comments

Comments
 (0)