Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH 14194: add style option for hiding index and columns #16141

Merged
merged 7 commits into from
Nov 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 69 additions & 12 deletions doc/source/style.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -674,13 +674,14 @@
"- precision\n",
"- captions\n",
"- table-wide styles\n",
"- hiding the index or columns\n",
"\n",
"Each of these can be specified in two ways:\n",
"\n",
"- A keyword argument to `Styler.__init__`\n",
"- A call to one of the `.set_` methods, e.g. `.set_caption`\n",
"- A call to one of the `.set_` or `.hide_` methods, e.g. `.set_caption` or `.hide_columns`\n",
"\n",
"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."
"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."
]
},
{
Expand Down Expand Up @@ -814,6 +815,38 @@
"We hope to collect some useful ones either in pandas, or preferable in a new package that [builds on top](#Extensibility) the tools here."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Hiding the Index or Columns"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"df.style.hide_index()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"df.style.hide_columns(['C','D'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand Down Expand Up @@ -875,7 +908,9 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from IPython.html import widgets\n",
Expand Down Expand Up @@ -911,7 +946,9 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"np.random.seed(25)\n",
Expand Down Expand Up @@ -1010,7 +1047,9 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"%mkdir templates"
Expand All @@ -1027,7 +1066,9 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"%%file templates/myhtml.tpl\n",
Expand Down Expand Up @@ -1078,7 +1119,9 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"MyStyler(df)"
Expand All @@ -1094,7 +1137,9 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"HTML(MyStyler(df).render(table_title=\"Extending Example\"))"
Expand All @@ -1110,7 +1155,9 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"EasyStyler = Styler.from_custom_template(\"templates\", \"myhtml.tpl\")\n",
Expand All @@ -1127,7 +1174,9 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"with open(\"template_structure.html\") as f:\n",
Expand All @@ -1147,6 +1196,7 @@
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true,
"nbsphinx": "hidden"
},
"outputs": [],
Expand All @@ -1163,7 +1213,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"display_name": "Python [default]",
"language": "python",
"name": "python3"
},
Expand All @@ -1177,7 +1227,14 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.1"
"version": "3.5.3"
},
"widgets": {
"application/vnd.jupyter.widget-state+json": {
"state": {},
"version_major": 1,
"version_minor": 0
}
}
},
"nbformat": 4,
Expand Down
3 changes: 2 additions & 1 deletion doc/source/whatsnew/v0.22.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ Other Enhancements

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

.. _whatsnew_0220.api_breaking:

Expand Down
87 changes: 73 additions & 14 deletions pandas/io/formats/style.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ def __init__(self, data, precision=None, table_styles=None, uuid=None,
precision = get_option('display.precision')
self.precision = precision
self.table_attributes = table_attributes
self.hidden_index = False
self.hidden_columns = []

# display_funcs maps (row, col) -> formatting function

def default_display_func(x):
Expand Down Expand Up @@ -180,6 +183,8 @@ def _translate(self):
caption = self.caption
ctx = self.ctx
precision = self.precision
hidden_index = self.hidden_index
hidden_columns = self.hidden_columns
uuid = self.uuid or str(uuid1()).replace("-", "_")
ROW_HEADING_CLASS = "row_heading"
COL_HEADING_CLASS = "col_heading"
Expand All @@ -194,7 +199,7 @@ def format_attr(pair):

# for sparsifying a MultiIndex
idx_lengths = _get_level_lengths(self.index)
col_lengths = _get_level_lengths(self.columns)
col_lengths = _get_level_lengths(self.columns, hidden_columns)

cell_context = dict()

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

# ... except maybe the last for columns.names
Expand All @@ -229,7 +234,7 @@ def format_attr(pair):
"value": name,
"display_value": name,
"class": " ".join(cs),
"is_visible": True})
"is_visible": not hidden_index})

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

if self.data.index.names and _any_not_none(*self.data.index.names):
if (self.data.index.names and _any_not_none(*self.data.index.names) and
not hidden_index):
index_header_row = []

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

head.append(index_header_row)

Expand All @@ -278,7 +284,8 @@ def format_attr(pair):
"row{row}".format(row=r)]
es = {
"type": "th",
"is_visible": _is_visible(r, c, idx_lengths),
"is_visible": (_is_visible(r, c, idx_lengths) and
not hidden_index),
"value": value,
"display_value": value,
"id": "_".join(rid[1:]),
Expand All @@ -302,7 +309,8 @@ def format_attr(pair):
"value": value,
"class": " ".join(cs),
"id": "_".join(cs[1:]),
"display_value": formatter(value)
"display_value": formatter(value),
"is_visible": (c not in hidden_columns)
})
props = []
for x in ctx[r, c]:
Expand Down Expand Up @@ -742,7 +750,7 @@ def set_uuid(self, uuid):

def set_caption(self, caption):
"""
Se the caption on a Styler
Set the caption on a Styler

Parameters
----------
Expand Down Expand Up @@ -784,6 +792,40 @@ def set_table_styles(self, table_styles):
self.table_styles = table_styles
return self

def hide_index(self):
"""
Hide any indices from rendering.

.. versionadded:: 0.22.0

Returns
-------
self : Styler
"""
self.hidden_index = True
return self

def hide_columns(self, subset):
"""
Hide columns from rendering.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how does subset make sense here? does it make sense to hide some columns?

Copy link
Contributor Author

@nchmura4 nchmura4 May 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is that you may have columns that are needed for formatting -- determining the color of a row, for example -- but you may not want to display them. For example, lets say you had a dataframe with columns City and AvgTemp. Avg Temp was used by Styler to color each row. But in the final rendering, you only want to display city (colored appropriately). AFAIK there is no way to do that right now, but with this change you could do df.style.hide_columns(['AvgTemp']). You only want to hide AvgTemp.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a bit unusual for pandas, but I think it's fine here. It's kind of like drop, except it's hiding instead of dropping.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we should support df.style[list_of_selected_columns] for showing some columns. (separate discussion)

.. versionadded:: 0.22.0

Parameters
----------
subset: IndexSlice
An argument to ``DataFrame.loc`` that identifies which columns
are hidden.

Returns
-------
self : Styler
"""
subset = _non_reducing_slice(subset)
hidden_df = self.data.loc[subset]
self.hidden_columns = self.columns.get_indexer_for(hidden_df.columns)
return self

# -----------------------------------------------------------------------
# A collection of "builtin" styles
# -----------------------------------------------------------------------
Expand Down Expand Up @@ -1158,31 +1200,48 @@ def _is_visible(idx_row, idx_col, lengths):
return (idx_col, idx_row) in lengths


def _get_level_lengths(index):
def _get_level_lengths(index, hidden_elements=None):
"""
Given an index, find the level lenght for each element.
Optional argument is a list of index positions which
should not be visible.

Result is a dictionary of (level, inital_position): span
"""
sentinel = sentinel_factory()
levels = index.format(sparsify=sentinel, adjoin=False, names=False)

if index.nlevels == 1:
return {(0, i): 1 for i, value in enumerate(levels)}
if hidden_elements is None:
hidden_elements = []

lengths = {}
if index.nlevels == 1:
for i, value in enumerate(levels):
if(i not in hidden_elements):
lengths[(0, i)] = 1
return lengths

for i, lvl in enumerate(levels):
for j, row in enumerate(lvl):
if not get_option('display.multi_sparse'):
lengths[(i, j)] = 1
elif row != sentinel:
elif (row != sentinel) and (j not in hidden_elements):
last_label = j
lengths[(i, last_label)] = 1
else:
elif (row != sentinel):
# even if its hidden, keep track of it in case
# length >1 and later elemens are visible
last_label = j
lengths[(i, last_label)] = 0
elif(j not in hidden_elements):
lengths[(i, last_label)] += 1

return lengths
non_zero_lengths = {}
for element, length in lengths.items():
if(length >= 1):
non_zero_lengths[element] = length

return non_zero_lengths


def _maybe_wrap_formatter(formatter):
Expand Down
Loading