Skip to content

Commit b00e62c

Browse files
nchmura4jreback
authored andcommitted
ENH 14194: add style option for hiding index and columns (#16141)
1 parent b315e7d commit b00e62c

File tree

4 files changed

+258
-27
lines changed

4 files changed

+258
-27
lines changed

doc/source/style.ipynb

+69-12
Original file line numberDiff line numberDiff line change
@@ -674,13 +674,14 @@
674674
"- precision\n",
675675
"- captions\n",
676676
"- table-wide styles\n",
677+
"- hiding the index or columns\n",
677678
"\n",
678679
"Each of these can be specified in two ways:\n",
679680
"\n",
680681
"- A keyword argument to `Styler.__init__`\n",
681-
"- A call to one of the `.set_` methods, e.g. `.set_caption`\n",
682+
"- A call to one of the `.set_` or `.hide_` methods, e.g. `.set_caption` or `.hide_columns`\n",
682683
"\n",
683-
"The best method to use depends on the context. Use the `Styler` constructor when building many styled DataFrames that should all share the same properties. For interactive use, the`.set_` methods are more convenient."
684+
"The best method to use depends on the context. Use the `Styler` constructor when building many styled DataFrames that should all share the same properties. For interactive use, the`.set_` and `.hide_` methods are more convenient."
684685
]
685686
},
686687
{
@@ -814,6 +815,38 @@
814815
"We hope to collect some useful ones either in pandas, or preferable in a new package that [builds on top](#Extensibility) the tools here."
815816
]
816817
},
818+
{
819+
"cell_type": "markdown",
820+
"metadata": {},
821+
"source": [
822+
"### Hiding the Index or Columns"
823+
]
824+
},
825+
{
826+
"cell_type": "markdown",
827+
"metadata": {},
828+
"source": [
829+
"The index can be hidden from rendering by calling `Styler.hide_index`. Columns can be hidden from rendering by calling `Styler.hide_columns` and passing in the name of a column, or a slice of columns."
830+
]
831+
},
832+
{
833+
"cell_type": "code",
834+
"execution_count": null,
835+
"metadata": {},
836+
"outputs": [],
837+
"source": [
838+
"df.style.hide_index()"
839+
]
840+
},
841+
{
842+
"cell_type": "code",
843+
"execution_count": null,
844+
"metadata": {},
845+
"outputs": [],
846+
"source": [
847+
"df.style.hide_columns(['C','D'])"
848+
]
849+
},
817850
{
818851
"cell_type": "markdown",
819852
"metadata": {},
@@ -875,7 +908,9 @@
875908
{
876909
"cell_type": "code",
877910
"execution_count": null,
878-
"metadata": {},
911+
"metadata": {
912+
"collapsed": true
913+
},
879914
"outputs": [],
880915
"source": [
881916
"from IPython.html import widgets\n",
@@ -911,7 +946,9 @@
911946
{
912947
"cell_type": "code",
913948
"execution_count": null,
914-
"metadata": {},
949+
"metadata": {
950+
"collapsed": true
951+
},
915952
"outputs": [],
916953
"source": [
917954
"np.random.seed(25)\n",
@@ -1010,7 +1047,9 @@
10101047
{
10111048
"cell_type": "code",
10121049
"execution_count": null,
1013-
"metadata": {},
1050+
"metadata": {
1051+
"collapsed": true
1052+
},
10141053
"outputs": [],
10151054
"source": [
10161055
"%mkdir templates"
@@ -1027,7 +1066,9 @@
10271066
{
10281067
"cell_type": "code",
10291068
"execution_count": null,
1030-
"metadata": {},
1069+
"metadata": {
1070+
"collapsed": true
1071+
},
10311072
"outputs": [],
10321073
"source": [
10331074
"%%file templates/myhtml.tpl\n",
@@ -1078,7 +1119,9 @@
10781119
{
10791120
"cell_type": "code",
10801121
"execution_count": null,
1081-
"metadata": {},
1122+
"metadata": {
1123+
"collapsed": true
1124+
},
10821125
"outputs": [],
10831126
"source": [
10841127
"MyStyler(df)"
@@ -1094,7 +1137,9 @@
10941137
{
10951138
"cell_type": "code",
10961139
"execution_count": null,
1097-
"metadata": {},
1140+
"metadata": {
1141+
"collapsed": true
1142+
},
10981143
"outputs": [],
10991144
"source": [
11001145
"HTML(MyStyler(df).render(table_title=\"Extending Example\"))"
@@ -1110,7 +1155,9 @@
11101155
{
11111156
"cell_type": "code",
11121157
"execution_count": null,
1113-
"metadata": {},
1158+
"metadata": {
1159+
"collapsed": true
1160+
},
11141161
"outputs": [],
11151162
"source": [
11161163
"EasyStyler = Styler.from_custom_template(\"templates\", \"myhtml.tpl\")\n",
@@ -1127,7 +1174,9 @@
11271174
{
11281175
"cell_type": "code",
11291176
"execution_count": null,
1130-
"metadata": {},
1177+
"metadata": {
1178+
"collapsed": true
1179+
},
11311180
"outputs": [],
11321181
"source": [
11331182
"with open(\"template_structure.html\") as f:\n",
@@ -1147,6 +1196,7 @@
11471196
"cell_type": "code",
11481197
"execution_count": null,
11491198
"metadata": {
1199+
"collapsed": true,
11501200
"nbsphinx": "hidden"
11511201
},
11521202
"outputs": [],
@@ -1163,7 +1213,7 @@
11631213
],
11641214
"metadata": {
11651215
"kernelspec": {
1166-
"display_name": "Python 3",
1216+
"display_name": "Python [default]",
11671217
"language": "python",
11681218
"name": "python3"
11691219
},
@@ -1177,7 +1227,14 @@
11771227
"name": "python",
11781228
"nbconvert_exporter": "python",
11791229
"pygments_lexer": "ipython3",
1180-
"version": "3.6.1"
1230+
"version": "3.5.3"
1231+
},
1232+
"widgets": {
1233+
"application/vnd.jupyter.widget-state+json": {
1234+
"state": {},
1235+
"version_major": 1,
1236+
"version_minor": 0
1237+
}
11811238
}
11821239
},
11831240
"nbformat": 4,

doc/source/whatsnew/v0.22.0.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ Other Enhancements
2424

2525
- Better support for :func:`Dataframe.style.to_excel` output with the ``xlsxwriter`` engine. (:issue:`16149`)
2626
- :func:`pandas.tseries.frequencies.to_offset` now accepts leading '+' signs e.g. '+1h'. (:issue:`18171`)
27-
-
27+
- :class:`pandas.io.formats.style.Styler` now has method ``hide_index()`` to determine whether the index will be rendered in ouptut (:issue:`14194`)
28+
- :class:`pandas.io.formats.style.Styler` now has method ``hide_columns()`` to determine whether columns will be hidden in output (:issue:`14194`)
2829

2930
.. _whatsnew_0220.api_breaking:
3031

pandas/io/formats/style.py

+73-14
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ def __init__(self, data, precision=None, table_styles=None, uuid=None,
133133
precision = get_option('display.precision')
134134
self.precision = precision
135135
self.table_attributes = table_attributes
136+
self.hidden_index = False
137+
self.hidden_columns = []
138+
136139
# display_funcs maps (row, col) -> formatting function
137140

138141
def default_display_func(x):
@@ -180,6 +183,8 @@ def _translate(self):
180183
caption = self.caption
181184
ctx = self.ctx
182185
precision = self.precision
186+
hidden_index = self.hidden_index
187+
hidden_columns = self.hidden_columns
183188
uuid = self.uuid or str(uuid1()).replace("-", "_")
184189
ROW_HEADING_CLASS = "row_heading"
185190
COL_HEADING_CLASS = "col_heading"
@@ -194,7 +199,7 @@ def format_attr(pair):
194199

195200
# for sparsifying a MultiIndex
196201
idx_lengths = _get_level_lengths(self.index)
197-
col_lengths = _get_level_lengths(self.columns)
202+
col_lengths = _get_level_lengths(self.columns, hidden_columns)
198203

199204
cell_context = dict()
200205

@@ -217,7 +222,7 @@ def format_attr(pair):
217222
row_es = [{"type": "th",
218223
"value": BLANK_VALUE,
219224
"display_value": BLANK_VALUE,
220-
"is_visible": True,
225+
"is_visible": not hidden_index,
221226
"class": " ".join([BLANK_CLASS])}] * (n_rlvls - 1)
222227

223228
# ... except maybe the last for columns.names
@@ -229,7 +234,7 @@ def format_attr(pair):
229234
"value": name,
230235
"display_value": name,
231236
"class": " ".join(cs),
232-
"is_visible": True})
237+
"is_visible": not hidden_index})
233238

234239
if clabels:
235240
for c, value in enumerate(clabels[r]):
@@ -252,7 +257,8 @@ def format_attr(pair):
252257
row_es.append(es)
253258
head.append(row_es)
254259

255-
if self.data.index.names and _any_not_none(*self.data.index.names):
260+
if (self.data.index.names and _any_not_none(*self.data.index.names) and
261+
not hidden_index):
256262
index_header_row = []
257263

258264
for c, name in enumerate(self.data.index.names):
@@ -266,7 +272,7 @@ def format_attr(pair):
266272
[{"type": "th",
267273
"value": BLANK_VALUE,
268274
"class": " ".join([BLANK_CLASS])
269-
}] * len(clabels[0]))
275+
}] * (len(clabels[0]) - len(hidden_columns)))
270276

271277
head.append(index_header_row)
272278

@@ -278,7 +284,8 @@ def format_attr(pair):
278284
"row{row}".format(row=r)]
279285
es = {
280286
"type": "th",
281-
"is_visible": _is_visible(r, c, idx_lengths),
287+
"is_visible": (_is_visible(r, c, idx_lengths) and
288+
not hidden_index),
282289
"value": value,
283290
"display_value": value,
284291
"id": "_".join(rid[1:]),
@@ -302,7 +309,8 @@ def format_attr(pair):
302309
"value": value,
303310
"class": " ".join(cs),
304311
"id": "_".join(cs[1:]),
305-
"display_value": formatter(value)
312+
"display_value": formatter(value),
313+
"is_visible": (c not in hidden_columns)
306314
})
307315
props = []
308316
for x in ctx[r, c]:
@@ -741,7 +749,7 @@ def set_uuid(self, uuid):
741749

742750
def set_caption(self, caption):
743751
"""
744-
Se the caption on a Styler
752+
Set the caption on a Styler
745753
746754
Parameters
747755
----------
@@ -783,6 +791,40 @@ def set_table_styles(self, table_styles):
783791
self.table_styles = table_styles
784792
return self
785793

794+
def hide_index(self):
795+
"""
796+
Hide any indices from rendering.
797+
798+
.. versionadded:: 0.22.0
799+
800+
Returns
801+
-------
802+
self : Styler
803+
"""
804+
self.hidden_index = True
805+
return self
806+
807+
def hide_columns(self, subset):
808+
"""
809+
Hide columns from rendering.
810+
811+
.. versionadded:: 0.22.0
812+
813+
Parameters
814+
----------
815+
subset: IndexSlice
816+
An argument to ``DataFrame.loc`` that identifies which columns
817+
are hidden.
818+
819+
Returns
820+
-------
821+
self : Styler
822+
"""
823+
subset = _non_reducing_slice(subset)
824+
hidden_df = self.data.loc[subset]
825+
self.hidden_columns = self.columns.get_indexer_for(hidden_df.columns)
826+
return self
827+
786828
# -----------------------------------------------------------------------
787829
# A collection of "builtin" styles
788830
# -----------------------------------------------------------------------
@@ -1157,31 +1199,48 @@ def _is_visible(idx_row, idx_col, lengths):
11571199
return (idx_col, idx_row) in lengths
11581200

11591201

1160-
def _get_level_lengths(index):
1202+
def _get_level_lengths(index, hidden_elements=None):
11611203
"""
11621204
Given an index, find the level lenght for each element.
1205+
Optional argument is a list of index positions which
1206+
should not be visible.
11631207
11641208
Result is a dictionary of (level, inital_position): span
11651209
"""
11661210
sentinel = sentinel_factory()
11671211
levels = index.format(sparsify=sentinel, adjoin=False, names=False)
11681212

1169-
if index.nlevels == 1:
1170-
return {(0, i): 1 for i, value in enumerate(levels)}
1213+
if hidden_elements is None:
1214+
hidden_elements = []
11711215

11721216
lengths = {}
1217+
if index.nlevels == 1:
1218+
for i, value in enumerate(levels):
1219+
if(i not in hidden_elements):
1220+
lengths[(0, i)] = 1
1221+
return lengths
11731222

11741223
for i, lvl in enumerate(levels):
11751224
for j, row in enumerate(lvl):
11761225
if not get_option('display.multi_sparse'):
11771226
lengths[(i, j)] = 1
1178-
elif row != sentinel:
1227+
elif (row != sentinel) and (j not in hidden_elements):
11791228
last_label = j
11801229
lengths[(i, last_label)] = 1
1181-
else:
1230+
elif (row != sentinel):
1231+
# even if its hidden, keep track of it in case
1232+
# length >1 and later elemens are visible
1233+
last_label = j
1234+
lengths[(i, last_label)] = 0
1235+
elif(j not in hidden_elements):
11821236
lengths[(i, last_label)] += 1
11831237

1184-
return lengths
1238+
non_zero_lengths = {}
1239+
for element, length in lengths.items():
1240+
if(length >= 1):
1241+
non_zero_lengths[element] = length
1242+
1243+
return non_zero_lengths
11851244

11861245

11871246
def _maybe_wrap_formatter(formatter):

0 commit comments

Comments
 (0)