Skip to content
Closed
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
1 change: 1 addition & 0 deletions doc/source/whatsnew/v3.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ Other enhancements
- :meth:`DataFrame.fillna` and :meth:`Series.fillna` can now accept ``value=None``; for non-object dtype the corresponding NA value will be used (:issue:`57723`)
- :meth:`DataFrame.pivot_table` and :func:`pivot_table` now allow the passing of keyword arguments to ``aggfunc`` through ``**kwargs`` (:issue:`57884`)
- :meth:`DataFrame.to_json` now encodes ``Decimal`` as strings instead of floats (:issue:`60698`)
- :meth:`DataFrame.to_latex` and :meth:`.Styler.to_latex` now support a ``centering`` parameter to center the table in the LaTeX output (:issue:`62733`)
- :meth:`Series.cummin` and :meth:`Series.cummax` now supports :class:`CategoricalDtype` (:issue:`52335`)
- :meth:`Series.plot` now correctly handle the ``ylabel`` parameter for pie charts, allowing for explicit control over the y-axis label (:issue:`58239`)
- :meth:`DataFrame.plot.scatter` argument ``c`` now accepts a column of strings, where rows with the same string are colored identically (:issue:`16827` and :issue:`16485`)
Expand Down
8 changes: 8 additions & 0 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -3309,6 +3309,7 @@ def to_latex(
caption: str | tuple[str, str] | None = ...,
label: str | None = ...,
position: str | None = ...,
centering: bool = ...,
) -> str: ...

@overload
Expand Down Expand Up @@ -3336,6 +3337,7 @@ def to_latex(
caption: str | tuple[str, str] | None = ...,
label: str | None = ...,
position: str | None = ...,
centering: bool = ...,
) -> None: ...

@final
Expand Down Expand Up @@ -3363,6 +3365,7 @@ def to_latex(
caption: str | tuple[str, str] | None = None,
label: str | None = None,
position: str | None = None,
centering: bool = False,
) -> str | None:
r"""
Render object to a LaTeX tabular, longtable, or nested table.
Expand Down Expand Up @@ -3469,6 +3472,10 @@ def to_latex(
The LaTeX positional argument for tables, to be placed after
``\begin{}`` in the output.

centering : bool, default False
Whether to add the ``\centering`` command to center the table
inside the table environment.

Returns
-------
str or None
Expand Down Expand Up @@ -3614,6 +3621,7 @@ def _wrap(x, alt_format_):
if (multirow and isinstance(self.index, MultiIndex))
else None,
"bold_rows": bold_rows,
"centering": centering,
}

return self._to_latex_via_styler(
Expand Down
7 changes: 7 additions & 0 deletions pandas/io/formats/style.py
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,7 @@ def to_latex(
multicol_align: str | None = ...,
siunitx: bool = ...,
environment: str | None = ...,
centering: bool = ...,
encoding: str | None = ...,
convert_css: bool = ...,
) -> None: ...
Expand All @@ -659,6 +660,7 @@ def to_latex(
multicol_align: str | None = ...,
siunitx: bool = ...,
environment: str | None = ...,
centering: bool = ...,
encoding: str | None = ...,
convert_css: bool = ...,
) -> str: ...
Expand All @@ -680,6 +682,7 @@ def to_latex(
multicol_align: str | None = None,
siunitx: bool = False,
environment: str | None = None,
centering: bool = False,
encoding: str | None = None,
convert_css: bool = False,
) -> str | None:
Expand Down Expand Up @@ -778,6 +781,9 @@ def to_latex(
``pandas.options.styler.latex.environment``, which is `None`.

.. versionadded:: 1.4.0
centering : bool, default False
Whether to add the ``\centering`` command to center the table
inside the table environment.
encoding : str, optional
Character encoding setting. Defaults
to ``pandas.options.styler.render.encoding``, which is "utf-8".
Expand Down Expand Up @@ -1227,6 +1233,7 @@ def to_latex(
convert_css=convert_css,
siunitx=siunitx,
clines=clines,
centering=centering,
)

encoding = (
Expand Down
20 changes: 15 additions & 5 deletions pandas/io/formats/style_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,12 +226,16 @@ def _render_latex(
"""
Render a Styler in latex format
"""
centering = kwargs.pop("centering", False)
d = self._render(sparse_index, sparse_columns, None, None)
self._translate_latex(d, clines=clines)
self.template_latex.globals["parse_wrap"] = _parse_latex_table_wrapping
self.template_latex.globals["parse_wrap"] = partial(
_parse_latex_table_wrapping, centering=centering
)
self.template_latex.globals["parse_table"] = _parse_latex_table_styles
self.template_latex.globals["parse_cell"] = _parse_latex_cell_styles
self.template_latex.globals["parse_header"] = _parse_latex_header_span
d["centering"] = centering
d.update(kwargs)
return self.template_latex.render(**d)

Expand Down Expand Up @@ -2329,7 +2333,9 @@ def _translate(self, styler: StylerRenderer, d: dict):
return d


def _parse_latex_table_wrapping(table_styles: CSSStyles, caption: str | None) -> bool:
def _parse_latex_table_wrapping(
table_styles: CSSStyles, caption: str | None, centering: bool = False
) -> bool:
"""
Indicate whether LaTeX {tabular} should be wrapped with a {table} environment.

Expand All @@ -2340,9 +2346,13 @@ def _parse_latex_table_wrapping(table_styles: CSSStyles, caption: str | None) ->
IGNORED_WRAPPERS = ["toprule", "midrule", "bottomrule", "column_format"]
# ignored selectors are included with {tabular} so do not need wrapping
return (
table_styles is not None
and any(d["selector"] not in IGNORED_WRAPPERS for d in table_styles)
) or caption is not None
(
table_styles is not None
and any(d["selector"] not in IGNORED_WRAPPERS for d in table_styles)
)
or caption is not None
or centering
)


def _parse_latex_table_styles(table_styles: CSSStyles, selector: str) -> str | None:
Expand Down
1 change: 1 addition & 0 deletions pandas/io/formats/templates/latex_longtable.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
\label{{label}} \\
{% endif %}
{% endif %}
{%- if centering %}\centering{% endif -%}
{% set toprule = parse_table(table_styles, 'toprule') %}
{% if toprule is not none %}
\{{toprule}}
Expand Down
1 change: 1 addition & 0 deletions pandas/io/formats/templates/latex_table.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
{% if position_float is not none%}
\{{position_float}}
{% endif %}
{%- if centering %}\centering{% endif -%}
{% if caption and caption is string %}
\caption{% raw %}{{% endraw %}{{caption}}{% raw %}}{% endraw %}

Expand Down
24 changes: 24 additions & 0 deletions pandas/tests/io/formats/test_to_latex.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,30 @@ def test_to_latex_midrule_location(self):
)
assert result == expected

def test_to_latex_centering(self):
# test for \centering command #GH ENH #62733
df = DataFrame({"a": [1], "b": [2]})

# test with DataFrame.to_latex (centering=True)
latex_centered = df.to_latex(centering=True)
assert r"\centering" in latex_centered
# test with DataFrame.to_latex (centering=False)
latex_default = df.to_latex(centering=False)
assert r"\centering" not in latex_default
# test with DataFrame.to_latex with longtable (centering=True)
latex_long_centered = df.to_latex(centering=True, longtable=True)
assert r"\centering" in latex_long_centered
# test with DataFrame.to_latex with longtable (centering=False)
latex_long_default = df.to_latex(centering=False, longtable=True)
assert r"\centering" not in latex_long_default
# test with Styler.to_latex (centering=True)
styler = df.style
latex_styler_centered = styler.to_latex(centering=True)
assert r"\centering" in latex_styler_centered
# test with Styler.to_latex (centering=False)
latex_styler_default = styler.to_latex(centering=False)
assert r"\centering" not in latex_styler_default


class TestToLatexLongtable:
def test_to_latex_empty_longtable(self):
Expand Down
Loading