From be1596790408b0317649a69717abb3858a52eb1e Mon Sep 17 00:00:00 2001 From: "JHM Darbyshire (iMac)" Date: Fri, 11 Jun 2021 10:52:39 +0200 Subject: [PATCH 01/14] add sparsify to to_html --- pandas/io/formats/style.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index 93c3843b36846..3e60ab3097ba5 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -750,6 +750,8 @@ def to_html( *, table_uuid: str | None = None, table_attributes: str | None = None, + sparse_index: bool | None = None, + sparse_columns: bool | None = None, encoding: str | None = None, doctype_html: bool = False, exclude_styles: bool = False, @@ -775,6 +777,14 @@ def to_html( `` >`` If not given defaults to Styler's preexisting value. + sparse_index : bool, optional + Whether to sparsify the display of a hierarchical index. Setting to False + will display each explicit level element in a hierarchical key for each row. + Defaults to ``pandas.options.styler.sparse.index`` value. + sparse_columns : bool, optional + Whether to sparsify the display of a hierarchical index. Setting to False + will display each explicit level element in a hierarchical key for each row. + Defaults to ``pandas.options.styler.sparse.columns`` value. encoding : str, optional Character encoding setting for file output, and HTML meta tags, defaults to "utf-8" if None. @@ -801,8 +811,15 @@ def to_html( if table_attributes: self.set_table_attributes(table_attributes) + if sparse_index is None: + sparse_index = get_option("styler.sparse.index") + if sparse_columns is None: + sparse_columns = get_option("styler.sparse.columns") + # Build HTML string.. - html = self.render( + html = self._render_html( + sparse_index=sparse_index, + sparse_columns=sparse_columns, exclude_styles=exclude_styles, encoding=encoding if encoding else "utf-8", doctype_html=doctype_html, From c187dc2367fc4e7877fbd41ae5867ed881d4dbb2 Mon Sep 17 00:00:00 2001 From: "JHM Darbyshire (iMac)" Date: Fri, 11 Jun 2021 11:48:36 +0200 Subject: [PATCH 02/14] add tests --- pandas/tests/io/formats/style/test_html.py | 31 +++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/pandas/tests/io/formats/style/test_html.py b/pandas/tests/io/formats/style/test_html.py index 74b4c7ea3977c..84a331c971f4d 100644 --- a/pandas/tests/io/formats/style/test_html.py +++ b/pandas/tests/io/formats/style/test_html.py @@ -2,7 +2,11 @@ import pytest -from pandas import DataFrame +from pandas import ( + DataFrame, + MultiIndex, + option_context, +) jinja2 = pytest.importorskip("jinja2") from pandas.io.formats.style import Styler @@ -236,3 +240,28 @@ def test_from_custom_template(tmpdir): def test_caption_as_sequence(styler): styler.set_caption(("full cap", "short cap")) assert "" in styler.render() + + +def test_sparse_options(): + cidx = MultiIndex.from_tuples([("Z", "a"), ("Z", "b"), ("Y", "c")]) + ridx = MultiIndex.from_tuples([("A", "a"), ("A", "b"), ("B", "c")]) + df = DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]], index=ridx, columns=cidx) + styler = df.style + + base_html = styler.to_html() + + # test option context and method arguments simultaneously + # output generation is tested in test_style.test_mi_sparse + for sparse_index in [True, False]: + with option_context("styler.sparse.index", sparse_index): + html1 = styler.to_html() + assert (html1 == base_html) is sparse_index + html2 = styler.to_html(sparse_index=sparse_index, sparse_columns=True) + assert html1 == html2 + + for sparse_columns in [True, False]: + with option_context("styler.sparse.columns", sparse_columns): + html1 = styler.to_html() + assert (html1 == base_html) is sparse_columns + html2 = styler.to_html(sparse_index=True, sparse_columns=sparse_columns) + assert html1 == html2 From c53040ed2db24208f516889c9125accffdb234fa Mon Sep 17 00:00:00 2001 From: "JHM Darbyshire (iMac)" Date: Fri, 11 Jun 2021 11:54:53 +0200 Subject: [PATCH 03/14] doc fix --- pandas/io/formats/style.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index 3e60ab3097ba5..d476f4ec2b901 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -470,8 +470,8 @@ def to_latex( Defaults to ``pandas.options.styler.sparse.index`` value. sparse_columns : bool, optional Whether to sparsify the display of a hierarchical index. Setting to False - will display each explicit level element in a hierarchical key for each row. - Defaults to ``pandas.options.styler.sparse.columns`` value. + will display each explicit level element in a hierarchical key for each + column. Defaults to ``pandas.options.styler.sparse.columns`` value. multirow_align : {"c", "t", "b"} If sparsifying hierarchical MultiIndexes whether to align text centrally, at the top or bottom. @@ -783,8 +783,8 @@ def to_html( Defaults to ``pandas.options.styler.sparse.index`` value. sparse_columns : bool, optional Whether to sparsify the display of a hierarchical index. Setting to False - will display each explicit level element in a hierarchical key for each row. - Defaults to ``pandas.options.styler.sparse.columns`` value. + will display each explicit level element in a hierarchical key for each + column. Defaults to ``pandas.options.styler.sparse.columns`` value. encoding : str, optional Character encoding setting for file output, and HTML meta tags, defaults to "utf-8" if None. From 93d78e64788346e211b82f1a5fab83ef73b11e85 Mon Sep 17 00:00:00 2001 From: "JHM Darbyshire (iMac)" Date: Sun, 20 Jun 2021 08:53:17 +0200 Subject: [PATCH 04/14] deprecate styler.render() and amend tests --- asv_bench/benchmarks/io/style.py | 4 +- pandas/io/formats/style.py | 15 ++ .../tests/io/formats/style/test_deprecated.py | 157 +++++++++++++++ pandas/tests/io/formats/style/test_format.py | 21 +- pandas/tests/io/formats/style/test_html.py | 10 +- .../tests/io/formats/style/test_non_unique.py | 4 +- pandas/tests/io/formats/style/test_style.py | 181 ++++-------------- pandas/tests/io/formats/style/test_tooltip.py | 8 +- 8 files changed, 224 insertions(+), 176 deletions(-) create mode 100644 pandas/tests/io/formats/style/test_deprecated.py diff --git a/asv_bench/benchmarks/io/style.py b/asv_bench/benchmarks/io/style.py index 82166a2a95c76..a0067307c3905 100644 --- a/asv_bench/benchmarks/io/style.py +++ b/asv_bench/benchmarks/io/style.py @@ -36,11 +36,11 @@ def peakmem_classes_render(self, cols, rows): def time_format_render(self, cols, rows): self._style_format() - self.st.render() + self.st._render_html(True, True) def peakmem_format_render(self, cols, rows): self._style_format() - self.st.render() + self.st._render_html(True, True) def _style_apply(self): def _apply_func(s): diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index fe5430d3f68db..a45da8fe2946a 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -224,6 +224,8 @@ def render( """ Render the ``Styler`` including all applied styles to HTML. + .. deprecated:: 1.4.0 + Parameters ---------- sparse_index : bool, optional @@ -247,6 +249,8 @@ def render( Notes ----- + This method is deprecated in favour of ``Styler.to_html``. + Styler objects have defined the ``_repr_html_`` method which automatically calls ``self.render()`` when it's the last item in a Notebook cell. When calling ``Styler.render()`` @@ -265,6 +269,11 @@ def render( * caption * table_attributes """ + warnings.warn( + "this method is deprecated in favour of `Styler.to_html()`", + FutureWarning, + stacklevel=2, + ) if sparse_index is None: sparse_index = get_option("styler.sparse.index") if sparse_columns is None: @@ -803,6 +812,7 @@ def to_html( encoding: str | None = None, doctype_html: bool = False, exclude_styles: bool = False, + **kwargs, ): """ Write Styler to a file, buffer or string in HTML-CSS format. @@ -843,6 +853,10 @@ def to_html( Whether to include the ``" in s.render() + " color: red;\n}\n" in s.to_html() ) def test_render_empty_dfs(self): empty_df = DataFrame() es = Styler(empty_df) - es.render() + es.to_html() # An index but no columns - DataFrame(columns=["a"]).style.render() + DataFrame(columns=["a"]).style.to_html() # A column but no index - DataFrame(index=["a"]).style.render() + DataFrame(index=["a"]).style.to_html() # No IndexError raised? def test_render_double(self): @@ -366,7 +366,7 @@ def test_render_double(self): ["color: red; border: 1px", "color: blue; border: 2px"], name=x.name ) s = Styler(df, uuid="AB").apply(style) - s.render() + s.to_html() # it worked? def test_set_properties(self): @@ -649,7 +649,7 @@ def test_applymap_subset_multiindex(self, slice_): idx = MultiIndex.from_product([["a", "b"], [1, 2]]) col = MultiIndex.from_product([["x", "y"], ["A", "B"]]) df = DataFrame(np.random.rand(4, 4), columns=col, index=idx) - df.style.applymap(lambda x: "color: red;", subset=slice_).render() + df.style.applymap(lambda x: "color: red;", subset=slice_).to_html() def test_applymap_subset_multiindex_code(self): # https://github.com/pandas-dev/pandas/issues/25858 @@ -670,94 +670,6 @@ def color_negative_red(val): df.loc[pct_subset] df.style.applymap(color_negative_red, subset=pct_subset) - def test_where_with_one_style(self): - # GH 17474 - def f(x): - return x > 0.5 - - style1 = "foo: bar" - - with tm.assert_produces_warning(FutureWarning): - result = self.df.style.where(f, style1)._compute().ctx - expected = { - (r, c): [("foo", "bar")] - for r, row in enumerate(self.df.index) - for c, col in enumerate(self.df.columns) - if f(self.df.loc[row, col]) - } - assert result == expected - - @pytest.mark.parametrize( - "slice_", - [ - pd.IndexSlice[:], - pd.IndexSlice[:, ["A"]], - pd.IndexSlice[[1], :], - pd.IndexSlice[[1], ["A"]], - pd.IndexSlice[:2, ["A", "B"]], - ], - ) - def test_where_subset(self, slice_): - # GH 17474 - def f(x): - return x > 0.5 - - style1 = "foo: bar" - style2 = "baz: foo" - - with tm.assert_produces_warning(FutureWarning): - res = self.df.style.where(f, style1, style2, subset=slice_)._compute().ctx - expected = { - (r, c): [("foo", "bar") if f(self.df.loc[row, col]) else ("baz", "foo")] - for r, row in enumerate(self.df.index) - for c, col in enumerate(self.df.columns) - if row in self.df.loc[slice_].index and col in self.df.loc[slice_].columns - } - assert res == expected - - def test_where_subset_compare_with_applymap(self): - # GH 17474 - def f(x): - return x > 0.5 - - style1 = "foo: bar" - style2 = "baz: foo" - - def g(x): - return style1 if f(x) else style2 - - slices = [ - pd.IndexSlice[:], - pd.IndexSlice[:, ["A"]], - pd.IndexSlice[[1], :], - pd.IndexSlice[[1], ["A"]], - pd.IndexSlice[:2, ["A", "B"]], - ] - - for slice_ in slices: - with tm.assert_produces_warning(FutureWarning): - result = ( - self.df.style.where(f, style1, style2, subset=slice_)._compute().ctx - ) - expected = self.df.style.applymap(g, subset=slice_)._compute().ctx - assert result == expected - - def test_where_kwargs(self): - df = DataFrame([[1, 2], [3, 4]]) - - def f(x, val): - return x > val - - with tm.assert_produces_warning(FutureWarning): - res = df.style.where(f, "color:green;", "color:red;", val=2)._compute().ctx - expected = { - (0, 0): [("color", "red")], - (0, 1): [("color", "red")], - (1, 0): [("color", "green")], - (1, 1): [("color", "green")], - } - assert res == expected - def test_empty(self): df = DataFrame({"A": [1, 0]}) s = df.style @@ -789,27 +701,9 @@ def test_init_with_na_rep(self): assert ctx["body"][0][1]["display_value"] == "NA" assert ctx["body"][0][2]["display_value"] == "NA" - def test_set_na_rep(self): - # GH 21527 28358 - df = DataFrame([[None, None], [1.1, 1.2]], columns=["A", "B"]) - - with tm.assert_produces_warning(FutureWarning): - ctx = df.style.set_na_rep("NA")._translate(True, True) - assert ctx["body"][0][1]["display_value"] == "NA" - assert ctx["body"][0][2]["display_value"] == "NA" - - with tm.assert_produces_warning(FutureWarning): - ctx = ( - df.style.set_na_rep("NA") - .format(None, na_rep="-", subset=["B"]) - ._translate(True, True) - ) - assert ctx["body"][0][1]["display_value"] == "NA" - assert ctx["body"][0][2]["display_value"] == "-" - def test_caption(self): styler = Styler(self.df, caption="foo") - result = styler.render() + result = styler.to_html() assert all(["caption" in result, "foo" in result]) styler = self.df.style @@ -819,7 +713,7 @@ def test_caption(self): def test_uuid(self): styler = Styler(self.df, uuid="abc123") - result = styler.render() + result = styler.to_html() assert "abc123" in result styler = self.df.style @@ -830,7 +724,7 @@ def test_uuid(self): def test_unique_id(self): # See https://github.com/pandas-dev/pandas/issues/16780 df = DataFrame({"a": [1, 3, 5, 6], "b": [2, 4, 12, 21]}) - result = df.style.render(uuid="test") + result = df.style.to_html(uuid="test") assert "test" in result ids = re.findall('id="(.*?)"', result) assert np.unique(ids).size == len(ids) @@ -838,7 +732,7 @@ def test_unique_id(self): def test_table_styles(self): style = [{"selector": "th", "props": [("foo", "bar")]}] # default format styler = Styler(self.df, table_styles=style) - result = " ".join(styler.render().split()) + result = " ".join(styler.to_html().split()) assert "th { foo: bar; }" in result styler = self.df.style @@ -849,7 +743,7 @@ def test_table_styles(self): # GH 39563 style = [{"selector": "th", "props": "foo:bar;"}] # css string format styler = self.df.style.set_table_styles(style) - result = " ".join(styler.render().split()) + result = " ".join(styler.to_html().split()) assert "th { foo: bar; }" in result def test_table_styles_multiple(self): @@ -880,21 +774,12 @@ def test_maybe_convert_css_to_tuples_err(self): def test_table_attributes(self): attributes = 'class="foo" data-bar' styler = Styler(self.df, table_attributes=attributes) - result = styler.render() + result = styler.to_html() assert 'class="foo" data-bar' in result - result = self.df.style.set_table_attributes(attributes).render() + result = self.df.style.set_table_attributes(attributes).to_html() assert 'class="foo" data-bar' in result - def test_precision(self): - s = Styler(self.df, precision=2) - assert s.precision == 2 - - with tm.assert_produces_warning(FutureWarning): - s2 = s.set_precision(4) - assert s is s2 - assert s.precision == 4 - def test_apply_none(self): def f(x): return DataFrame( @@ -907,10 +792,10 @@ def f(x): assert result[(1, 1)] == [("color", "red")] def test_trim(self): - result = self.df.style.render() # trim=True + result = self.df.style.to_html() # trim=True assert result.count("#") == 0 - result = self.df.style.highlight_max().render() + result = self.df.style.highlight_max().to_html() assert result.count("#") == len(self.df.columns) def test_export(self): @@ -922,7 +807,7 @@ def test_export(self): style2 = self.df.style style2.use(result) assert style1._todo == style2._todo - style2.render() + style2.to_html() def test_bad_apply_shape(self): df = DataFrame([[1, 2], [3, 4]]) @@ -1269,7 +1154,7 @@ def set_caption_from_template(styler, a, b): return styler.set_caption(f"Dataframe with a = {a} and b = {b}") styler = self.df.style.pipe(set_caption_from_template, "A", b="B") - assert "Dataframe with a = A and b = B" in styler.render() + assert "Dataframe with a = A and b = B" in styler.to_html() # Test with an argument that is a (callable, keyword_name) pair. def f(a, b, styler): @@ -1284,8 +1169,8 @@ def test_no_cell_ids(self): # GH 35663 df = DataFrame(data=[[0]]) styler = Styler(df, uuid="_", cell_ids=False) - styler.render() - s = styler.render() # render twice to ensure ctx is not updated + styler.to_html() + s = styler.to_html() # render twice to ensure ctx is not updated assert s.find('' in s assert '' in s assert '' in s assert '' in s # GH 39317 - s = Styler(df, uuid_len=0, cell_ids=True).set_td_classes(classes).render() + s = Styler(df, uuid_len=0, cell_ids=True).set_td_classes(classes).to_html() assert '' in s assert '' in s assert '' in s @@ -1325,7 +1210,7 @@ def test_set_data_classes_reindex(self): columns=[0, 2], index=[0, 2], ) - s = Styler(df, uuid_len=0).set_td_classes(classes).render() + s = Styler(df, uuid_len=0).set_td_classes(classes).to_html() assert '' in s assert '' in s assert '' in s @@ -1348,17 +1233,17 @@ def test_column_and_row_styling(self): df = DataFrame(data=[[0, 1], [1, 2]], columns=["A", "B"]) s = Styler(df, uuid_len=0) s = s.set_table_styles({"A": [{"selector": "", "props": [("color", "blue")]}]}) - assert "#T__ .col0 {\n color: blue;\n}" in s.render() + assert "#T__ .col0 {\n color: blue;\n}" in s.to_html() s = s.set_table_styles( {0: [{"selector": "", "props": [("color", "blue")]}]}, axis=1 ) - assert "#T__ .row0 {\n color: blue;\n}" in s.render() + assert "#T__ .row0 {\n color: blue;\n}" in s.to_html() @pytest.mark.parametrize("len_", [1, 5, 32, 33, 100]) def test_uuid_len(self, len_): # GH 36345 df = DataFrame(data=[["A"]]) - s = Styler(df, uuid_len=len_, cell_ids=False).render() + s = Styler(df, uuid_len=len_, cell_ids=False).to_html() strt = s.find('id="T_') end = s[strt + 6 :].find('"') if len_ > 32: @@ -1372,7 +1257,7 @@ def test_uuid_len_raises(self, len_): df = DataFrame(data=[["A"]]) msg = "``uuid_len`` must be an integer in range \\[0, 32\\]." with pytest.raises(TypeError, match=msg): - Styler(df, uuid_len=len_, cell_ids=False).render() + Styler(df, uuid_len=len_, cell_ids=False).to_html() @pytest.mark.parametrize( "slc", diff --git a/pandas/tests/io/formats/style/test_tooltip.py b/pandas/tests/io/formats/style/test_tooltip.py index 71ce496cca030..1bef89be78377 100644 --- a/pandas/tests/io/formats/style/test_tooltip.py +++ b/pandas/tests/io/formats/style/test_tooltip.py @@ -36,7 +36,7 @@ def styler(df): ) def test_tooltip_render(ttips, styler): # GH 21266 - result = styler.set_tooltips(ttips).render() + result = styler.set_tooltips(ttips).to_html() # test tooltip table level class assert "#T__ .pd-t {\n visibility: hidden;\n" in result @@ -61,7 +61,7 @@ def test_tooltip_render(ttips, styler): def test_tooltip_ignored(styler): # GH 21266 - result = styler.render() # no set_tooltips() creates no + result = styler.to_html() # no set_tooltips() creates no assert '' in result assert '' not in result @@ -72,7 +72,7 @@ def test_tooltip_css_class(styler): DataFrame([["tooltip"]], index=["x"], columns=["A"]), css_class="other-class", props=[("color", "green")], - ).render() + ).to_html() assert "#T__ .other-class {\n color: green;\n" in result assert '#T__ #T__row0_col0 .other-class::after {\n content: "tooltip";\n' in result @@ -81,5 +81,5 @@ def test_tooltip_css_class(styler): DataFrame([["tooltip"]], index=["x"], columns=["A"]), css_class="another-class", props="color:green;color:red;", - ).render() + ).to_html() assert "#T__ .another-class {\n color: green;\n color: red;\n}" in result From 617e8d8bb567e2e2b77cee893fe85ad46690756a Mon Sep 17 00:00:00 2001 From: "JHM Darbyshire (iMac)" Date: Sun, 20 Jun 2021 09:01:38 +0200 Subject: [PATCH 05/14] more cases to change --- pandas/io/formats/style.py | 19 ++++++++++--------- pandas/io/formats/style_render.py | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index a45da8fe2946a..9ddea52c9d03e 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -150,7 +150,7 @@ class Styler(StylerRenderer): be applied to the indicated cells. If using in the Jupyter notebook, Styler has defined a ``_repr_html_`` - to automatically render itself. Otherwise call Styler.render to get + to automatically render itself. Otherwise call Styler.to_html to get the generated HTML. CSS classes are attached to the generated HTML @@ -213,7 +213,7 @@ def _repr_html_(self) -> str: """ Hooks into Jupyter notebook rich display system. """ - return self.render() + return self.to_html() def render( self, @@ -252,10 +252,11 @@ def render( This method is deprecated in favour of ``Styler.to_html``. Styler objects have defined the ``_repr_html_`` method - which automatically calls ``self.render()`` when it's the - last item in a Notebook cell. When calling ``Styler.render()`` - directly, wrap the result in ``IPython.display.HTML`` to view - the rendered HTML in the notebook. + which automatically calls ``self.to_html()`` when it's the + last item in a Notebook cell. + + When calling ``Styler.render()`` directly, wrap the result in + ``IPython.display.HTML`` to view the rendered HTML in the notebook. Pandas uses the following keys in render. Arguments passed in ``**kwargs`` take precedence, so think carefully if you want @@ -344,7 +345,7 @@ def set_tooltips( >>> ttips = pd.DataFrame( ... data=[["Min", ""], [np.nan, "Max"]], columns=df.columns, index=df.index ... ) - >>> s = df.style.set_tooltips(ttips).render() + >>> s = df.style.set_tooltips(ttips).to_html() Optionally controlling the tooltip visual display @@ -546,7 +547,7 @@ def to_latex( >>> df = pd.DataFrame([[1,2], [3,4]]) >>> s = df.style.highlight_max(axis=None, ... props='background-color:red; font-weight:bold;') - >>> s.render() + >>> s.to_html() The equivalent using LaTeX only commands is the following: @@ -944,7 +945,7 @@ def set_td_classes(self, classes: DataFrame) -> Styler: >>> df = pd.DataFrame([[1]]) >>> css = pd.DataFrame([["other-class"]]) >>> s = Styler(df, uuid="_", cell_ids=False).set_td_classes(css) - >>> s.hide_index().render() + >>> s.hide_index().to_html() '' '
full cap
') != -1 @pytest.mark.parametrize( @@ -1303,13 +1188,13 @@ def test_no_cell_ids(self): def test_set_data_classes(self, classes): # GH 36159 df = DataFrame(data=[[0, 1], [2, 3]], columns=["A", "B"], index=["a", "b"]) - s = Styler(df, uuid_len=0, cell_ids=False).set_td_classes(classes).render() + s = Styler(df, uuid_len=0, cell_ids=False).set_td_classes(classes).to_html() assert '0123012024
' ' ' diff --git a/pandas/io/formats/style_render.py b/pandas/io/formats/style_render.py index 054dd443bd657..ee4cf7c4529e4 100644 --- a/pandas/io/formats/style_render.py +++ b/pandas/io/formats/style_render.py @@ -685,7 +685,7 @@ def format( >>> s = df.style.format( ... '{0}', escape="html", na_rep="NA" ... ) - >>> s.render() + >>> s.to_html() ... From a2c6ad3417f2a0fcb537054ac99f93fb729e3595 Mon Sep 17 00:00:00 2001 From: "JHM Darbyshire (iMac)" Date: Sun, 20 Jun 2021 15:07:07 +0200 Subject: [PATCH 06/14] pytest --- pandas/tests/io/formats/style/test_deprecated.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pandas/tests/io/formats/style/test_deprecated.py b/pandas/tests/io/formats/style/test_deprecated.py index 21e50fe73d9d8..a582761d7bff7 100644 --- a/pandas/tests/io/formats/style/test_deprecated.py +++ b/pandas/tests/io/formats/style/test_deprecated.py @@ -4,6 +4,8 @@ import numpy as np import pytest +jinja2 = pytest.importorskip("jinja2") + from pandas import ( DataFrame, IndexSlice, From 512af500ba1b6201b350f7b4cd60bd44c8f783bc Mon Sep 17 00:00:00 2001 From: "JHM Darbyshire (iMac)" Date: Mon, 5 Jul 2021 09:24:13 +0200 Subject: [PATCH 07/14] whats new 1.4.0 --- doc/source/whatsnew/v1.4.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index 764a50e13586a..c5249bfd0276a 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -30,7 +30,7 @@ enhancement2 Other enhancements ^^^^^^^^^^^^^^^^^^ - :meth:`Series.sample`, :meth:`DataFrame.sample`, and :meth:`.GroupBy.sample` now accept a ``np.random.Generator`` as input to ``random_state``. A generator will be more performant, especially with ``replace=False`` (:issue:`38100`) -- +- Added ``sparse_index`` and ``sparse_columns`` keyword arguments to :meth:`.Styler.to_html` (:issue:`41946`) .. --------------------------------------------------------------------------- From 444d9f83066afb63e00d35759d707ca987005051 Mon Sep 17 00:00:00 2001 From: "JHM Darbyshire (iMac)" Date: Mon, 5 Jul 2021 09:25:25 +0200 Subject: [PATCH 08/14] whats new 1.4.0 --- pandas/io/formats/style.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index 91741535ee306..a9e0c90948ac2 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -841,10 +841,14 @@ def to_html( Whether to sparsify the display of a hierarchical index. Setting to False will display each explicit level element in a hierarchical key for each row. Defaults to ``pandas.options.styler.sparse.index`` value. + + .. versionadded:: 1.4.0 sparse_columns : bool, optional Whether to sparsify the display of a hierarchical index. Setting to False will display each explicit level element in a hierarchical key for each column. Defaults to ``pandas.options.styler.sparse.columns`` value. + + .. versionadded:: 1.4.0 encoding : str, optional Character encoding setting for file output, and HTML meta tags, defaults to "utf-8" if None. From 4e89e641625f27b288cbbbad3e2d5e3e9480ab3b Mon Sep 17 00:00:00 2001 From: "JHM Darbyshire (iMac)" Date: Mon, 5 Jul 2021 09:34:09 +0200 Subject: [PATCH 09/14] better test --- pandas/tests/io/formats/style/test_html.py | 32 ++++++++++------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/pandas/tests/io/formats/style/test_html.py b/pandas/tests/io/formats/style/test_html.py index 4693139607d35..12d6e26ff0d97 100644 --- a/pandas/tests/io/formats/style/test_html.py +++ b/pandas/tests/io/formats/style/test_html.py @@ -414,26 +414,22 @@ def test_sticky_raises(styler): styler.set_sticky(axis="bad") -def test_sparse_options(): +@pytest.mark.parametrize( + "sparse_index, sparse_columns", + [(True, True), (True, False), (False, True), (False, False)], +) +def test_sparse_options(sparse_index, sparse_columns): cidx = MultiIndex.from_tuples([("Z", "a"), ("Z", "b"), ("Y", "c")]) ridx = MultiIndex.from_tuples([("A", "a"), ("A", "b"), ("B", "c")]) df = DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]], index=ridx, columns=cidx) styler = df.style - base_html = styler.to_html() - - # test option context and method arguments simultaneously - # output generation is tested in test_style.test_mi_sparse - for sparse_index in [True, False]: - with option_context("styler.sparse.index", sparse_index): - html1 = styler.to_html() - assert (html1 == base_html) is sparse_index - html2 = styler.to_html(sparse_index=sparse_index, sparse_columns=True) - assert html1 == html2 - - for sparse_columns in [True, False]: - with option_context("styler.sparse.columns", sparse_columns): - html1 = styler.to_html() - assert (html1 == base_html) is sparse_columns - html2 = styler.to_html(sparse_index=True, sparse_columns=sparse_columns) - assert html1 == html2 + default_html = styler.to_html() # defaults under pd.options to (True , True) + + with option_context( + "styler.sparse.index", sparse_index, "styler.sparse.columns", sparse_columns + ): + html1 = styler.to_html() + assert (html1 == default_html) is (sparse_index and sparse_columns) + html2 = styler.to_html(sparse_index=sparse_index, sparse_columns=sparse_columns) + assert html1 == html2 From 13090ab93c2c18f8a0775db8bbfaa3e5e4ea3886 Mon Sep 17 00:00:00 2001 From: "JHM Darbyshire (iMac)" Date: Mon, 5 Jul 2021 09:37:29 +0200 Subject: [PATCH 10/14] better test --- pandas/tests/io/formats/style/test_html.py | 32 ++++++++++------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/pandas/tests/io/formats/style/test_html.py b/pandas/tests/io/formats/style/test_html.py index 9e5f429a47239..451f53936d9e0 100644 --- a/pandas/tests/io/formats/style/test_html.py +++ b/pandas/tests/io/formats/style/test_html.py @@ -414,26 +414,22 @@ def test_sticky_raises(styler): styler.set_sticky(axis="bad") -def test_sparse_options(): +@pytest.mark.parametrize( + "sparse_index, sparse_columns", + [(True, True), (True, False), (False, True), (False, False)], +) +def test_sparse_options(sparse_index, sparse_columns): cidx = MultiIndex.from_tuples([("Z", "a"), ("Z", "b"), ("Y", "c")]) ridx = MultiIndex.from_tuples([("A", "a"), ("A", "b"), ("B", "c")]) df = DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]], index=ridx, columns=cidx) styler = df.style - base_html = styler.to_html() - - # test option context and method arguments simultaneously - # output generation is tested in test_style.test_mi_sparse - for sparse_index in [True, False]: - with option_context("styler.sparse.index", sparse_index): - html1 = styler.to_html() - assert (html1 == base_html) is sparse_index - html2 = styler.to_html(sparse_index=sparse_index, sparse_columns=True) - assert html1 == html2 - - for sparse_columns in [True, False]: - with option_context("styler.sparse.columns", sparse_columns): - html1 = styler.to_html() - assert (html1 == base_html) is sparse_columns - html2 = styler.to_html(sparse_index=True, sparse_columns=sparse_columns) - assert html1 == html2 + default_html = styler.to_html() # defaults under pd.options to (True , True) + + with option_context( + "styler.sparse.index", sparse_index, "styler.sparse.columns", sparse_columns + ): + html1 = styler.to_html() + assert (html1 == default_html) is (sparse_index and sparse_columns) + html2 = styler.to_html(sparse_index=sparse_index, sparse_columns=sparse_columns) + assert html1 == html2 From 38559f5962954245c00e338d936bd6ea3164e664 Mon Sep 17 00:00:00 2001 From: "JHM Darbyshire (iMac)" Date: Mon, 5 Jul 2021 09:40:02 +0200 Subject: [PATCH 11/14] versionadded --- pandas/io/formats/style.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index 86ae82c93e15f..86185b46f70e6 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -830,10 +830,14 @@ def to_html( Whether to sparsify the display of a hierarchical index. Setting to False will display each explicit level element in a hierarchical key for each row. Defaults to ``pandas.options.styler.sparse.index`` value. + + .. versionadded:: 1.4.0 sparse_columns : bool, optional Whether to sparsify the display of a hierarchical index. Setting to False will display each explicit level element in a hierarchical key for each column. Defaults to ``pandas.options.styler.sparse.columns`` value. + + .. versionadded:: 1.4.0 encoding : str, optional Character encoding setting for file output, and HTML meta tags, defaults to "utf-8" if None. From 14b2fb49f4d77f8fa70f9bed7a0c84efd0acb573 Mon Sep 17 00:00:00 2001 From: "JHM Darbyshire (MBP)" Date: Thu, 29 Jul 2021 17:09:23 +0200 Subject: [PATCH 12/14] update user guide --- doc/source/user_guide/style.ipynb | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/doc/source/user_guide/style.ipynb b/doc/source/user_guide/style.ipynb index f77d134d75988..b543a76304d32 100644 --- a/doc/source/user_guide/style.ipynb +++ b/doc/source/user_guide/style.ipynb @@ -60,9 +60,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The above output looks very similar to the standard DataFrame HTML representation. But the HTML here has already attached some CSS classes to each cell, even if we haven't yet created any styles. We can view these by calling the [.render()][render] method, which returns the raw HTML as string, which is useful for further processing or adding to a file - read on in [More about CSS and HTML](#More-About-CSS-and-HTML). Below we will show how we can use these to format the DataFrame to be more communicative. For example how we can build `s`:\n", + "The above output looks very similar to the standard DataFrame HTML representation. But the HTML here has already attached some CSS classes to each cell, even if we haven't yet created any styles. We can view these by calling the [.to_html()][to_html] method, which returns the raw HTML as string, which is useful for further processing or adding to a file - read on in [More about CSS and HTML](#More-About-CSS-and-HTML). Below we will show how we can use these to format the DataFrame to be more communicative. For example how we can build `s`:\n", "\n", - "[render]: ../reference/api/pandas.io.formats.style.Styler.render.rst" + "[tohtml]: ../reference/api/pandas.io.formats.style.Styler.to_html.rst" ] }, { @@ -379,7 +379,7 @@ "metadata": {}, "outputs": [], "source": [ - "out = s.set_table_attributes('class=\"my-table-cls\"').render()\n", + "out = s.set_table_attributes('class=\"my-table-cls\"').to_html()\n", "print(out[out.find('{}\".format(s.to_frame().style.hide_index().bar(align=align, \n", " color=['#d65f5f', '#5fba7d'], \n", - " width=100).render()) #testn['width']\n", + " width=100).to_html()) #testn['width']\n", " row += ''\n", " head += row\n", " \n", @@ -1573,9 +1573,9 @@ "\n", "The structure of the `id` is `T_uuid_level_row_col` where `level` is used only on headings, and headings will only have either `row` or `col` whichever is needed. By default we've also prepended each row/column identifier with a UUID unique to each DataFrame so that the style from one doesn't collide with the styling from another within the same notebook or page. You can read more about the use of UUIDs in [Optimization](#Optimization).\n", "\n", - "We can see example of the HTML by calling the [.render()][render] method.\n", + "We can see example of the HTML by calling the [.to_html()][tohtml] method.\n", "\n", - "[render]: ../reference/api/pandas.io.formats.style.Styler.render.rst" + "[tohtml]: ../reference/api/pandas.io.formats.style.Styler.to_html.rst" ] }, { @@ -1584,7 +1584,7 @@ "metadata": {}, "outputs": [], "source": [ - "print(pd.DataFrame([[1,2],[3,4]], index=['i1', 'i2'], columns=['c1', 'c2']).style.render())" + "print(pd.DataFrame([[1,2],[3,4]], index=['i1', 'i2'], columns=['c1', 'c2']).style.to_html())" ] }, { @@ -1811,7 +1811,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Our custom template accepts a `table_title` keyword. We can provide the value in the `.render` method." + "Our custom template accepts a `table_title` keyword. We can provide the value in the `.to_html` method." ] }, { @@ -1820,7 +1820,7 @@ "metadata": {}, "outputs": [], "source": [ - "HTML(MyStyler(df3).render(table_title=\"Extending Example\"))" + "HTML(MyStyler(df3).to_html(table_title=\"Extending Example\"))" ] }, { @@ -1837,7 +1837,7 @@ "outputs": [], "source": [ "EasyStyler = Styler.from_custom_template(\"templates\", \"myhtml.tpl\")\n", - "HTML(EasyStyler(df3).render(table_title=\"Another Title\"))" + "HTML(EasyStyler(df3).to_html(table_title=\"Another Title\"))" ] }, { @@ -1946,7 +1946,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.6" + "version": "3.8.7" } }, "nbformat": 4, From 770a5459eb928eed47964c4591228fd7d9f7abe2 Mon Sep 17 00:00:00 2001 From: "JHM Darbyshire (MBP)" Date: Thu, 29 Jul 2021 17:20:30 +0200 Subject: [PATCH 13/14] whats new 1.4.0 --- doc/source/whatsnew/v1.4.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index e42360558d284..3653ae25c5c59 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -159,6 +159,7 @@ Deprecations - Deprecated treating ``numpy.datetime64`` objects as UTC times when passed to the :class:`Timestamp` constructor along with a timezone. In a future version, these will be treated as wall-times. To retain the old behavior, use ``Timestamp(dt64).tz_localize("UTC").tz_convert(tz)`` (:issue:`24559`) - Deprecated ignoring missing labels when indexing with a sequence of labels on a level of a MultiIndex (:issue:`42351`) - Creating an empty Series without a dtype will now raise a more visible ``FutureWarning`` instead of a ``DeprecationWarning`` (:issue:`30017`) +- Deprecated :meth:`.Styler.render` in favour of :meth:`.Styler.to_html` (:issue:`42140`) .. --------------------------------------------------------------------------- From 2c1ca3403524c54f573b57954d61e02037b744de Mon Sep 17 00:00:00 2001 From: "JHM Darbyshire (MBP)" Date: Thu, 29 Jul 2021 17:26:57 +0200 Subject: [PATCH 14/14] doc update --- doc/source/reference/style.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/source/reference/style.rst b/doc/source/reference/style.rst index 7b790daea37ff..ae62c51f13094 100644 --- a/doc/source/reference/style.rst +++ b/doc/source/reference/style.rst @@ -69,9 +69,8 @@ Style export and import .. autosummary:: :toctree: api/ - Styler.render - Styler.export - Styler.use Styler.to_html - Styler.to_excel Styler.to_latex + Styler.to_excel + Styler.export + Styler.use
<div></div> "A&B"