Skip to content

Commit e7a45c2

Browse files
ENH: Add centering option to DataFrame.to_latex
1 parent 9da8705 commit e7a45c2

File tree

7 files changed

+54
-5
lines changed

7 files changed

+54
-5
lines changed

doc/source/whatsnew/v3.0.0.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ Other enhancements
189189
- :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`)
190190
- :meth:`DataFrame.pivot_table` and :func:`pivot_table` now allow the passing of keyword arguments to ``aggfunc`` through ``**kwargs`` (:issue:`57884`)
191191
- :meth:`DataFrame.to_json` now encodes ``Decimal`` as strings instead of floats (:issue:`60698`)
192+
- :meth:`DataFrame.to_latex` and :meth:`.Styler.to_latex` now support a ``centering`` parameter to center the table in the LaTeX output (:issue:`62733`)
192193
- :meth:`Series.cummin` and :meth:`Series.cummax` now supports :class:`CategoricalDtype` (:issue:`52335`)
193194
- :meth:`Series.plot` now correctly handle the ``ylabel`` parameter for pie charts, allowing for explicit control over the y-axis label (:issue:`58239`)
194195
- :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`)

pandas/core/generic.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3309,6 +3309,7 @@ def to_latex(
33093309
caption: str | tuple[str, str] | None = ...,
33103310
label: str | None = ...,
33113311
position: str | None = ...,
3312+
centering: bool = ...,
33123313
) -> str: ...
33133314

33143315
@overload
@@ -3336,6 +3337,7 @@ def to_latex(
33363337
caption: str | tuple[str, str] | None = ...,
33373338
label: str | None = ...,
33383339
position: str | None = ...,
3340+
centering: bool = ...,
33393341
) -> None: ...
33403342

33413343
@final
@@ -3363,6 +3365,7 @@ def to_latex(
33633365
caption: str | tuple[str, str] | None = None,
33643366
label: str | None = None,
33653367
position: str | None = None,
3368+
centering: bool = False,
33663369
) -> str | None:
33673370
r"""
33683371
Render object to a LaTeX tabular, longtable, or nested table.
@@ -3469,6 +3472,10 @@ def to_latex(
34693472
The LaTeX positional argument for tables, to be placed after
34703473
``\begin{}`` in the output.
34713474
3475+
centering : bool, default False
3476+
Whether to add the ``\centering`` command to center the table
3477+
inside the table environment.
3478+
34723479
Returns
34733480
-------
34743481
str or None
@@ -3614,6 +3621,7 @@ def _wrap(x, alt_format_):
36143621
if (multirow and isinstance(self.index, MultiIndex))
36153622
else None,
36163623
"bold_rows": bold_rows,
3624+
"centering": centering,
36173625
}
36183626

36193627
return self._to_latex_via_styler(

pandas/io/formats/style.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,7 @@ def to_latex(
637637
multicol_align: str | None = ...,
638638
siunitx: bool = ...,
639639
environment: str | None = ...,
640+
centering: bool = ...,
640641
encoding: str | None = ...,
641642
convert_css: bool = ...,
642643
) -> None: ...
@@ -659,6 +660,7 @@ def to_latex(
659660
multicol_align: str | None = ...,
660661
siunitx: bool = ...,
661662
environment: str | None = ...,
663+
centering: bool = ...,
662664
encoding: str | None = ...,
663665
convert_css: bool = ...,
664666
) -> str: ...
@@ -680,6 +682,7 @@ def to_latex(
680682
multicol_align: str | None = None,
681683
siunitx: bool = False,
682684
environment: str | None = None,
685+
centering: bool = False,
683686
encoding: str | None = None,
684687
convert_css: bool = False,
685688
) -> str | None:
@@ -1227,6 +1230,7 @@ def to_latex(
12271230
convert_css=convert_css,
12281231
siunitx=siunitx,
12291232
clines=clines,
1233+
centering=centering,
12301234
)
12311235

12321236
encoding = (

pandas/io/formats/style_render.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -226,12 +226,16 @@ def _render_latex(
226226
"""
227227
Render a Styler in latex format
228228
"""
229+
centering = kwargs.pop("centering", False)
229230
d = self._render(sparse_index, sparse_columns, None, None)
230231
self._translate_latex(d, clines=clines)
231-
self.template_latex.globals["parse_wrap"] = _parse_latex_table_wrapping
232+
self.template_latex.globals["parse_wrap"] = partial(
233+
_parse_latex_table_wrapping, centering=centering
234+
)
232235
self.template_latex.globals["parse_table"] = _parse_latex_table_styles
233236
self.template_latex.globals["parse_cell"] = _parse_latex_cell_styles
234237
self.template_latex.globals["parse_header"] = _parse_latex_header_span
238+
d["centering"] = centering
235239
d.update(kwargs)
236240
return self.template_latex.render(**d)
237241

@@ -2329,7 +2333,9 @@ def _translate(self, styler: StylerRenderer, d: dict):
23292333
return d
23302334

23312335

2332-
def _parse_latex_table_wrapping(table_styles: CSSStyles, caption: str | None) -> bool:
2336+
def _parse_latex_table_wrapping(
2337+
table_styles: CSSStyles, caption: str | None, centering: bool = False
2338+
) -> bool:
23332339
"""
23342340
Indicate whether LaTeX {tabular} should be wrapped with a {table} environment.
23352341
@@ -2340,9 +2346,13 @@ def _parse_latex_table_wrapping(table_styles: CSSStyles, caption: str | None) ->
23402346
IGNORED_WRAPPERS = ["toprule", "midrule", "bottomrule", "column_format"]
23412347
# ignored selectors are included with {tabular} so do not need wrapping
23422348
return (
2343-
table_styles is not None
2344-
and any(d["selector"] not in IGNORED_WRAPPERS for d in table_styles)
2345-
) or caption is not None
2349+
(
2350+
table_styles is not None
2351+
and any(d["selector"] not in IGNORED_WRAPPERS for d in table_styles)
2352+
)
2353+
or caption is not None
2354+
or centering
2355+
)
23462356

23472357

23482358
def _parse_latex_table_styles(table_styles: CSSStyles, selector: str) -> str | None:

pandas/io/formats/templates/latex_longtable.tpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
\label{{label}} \\
3030
{% endif %}
3131
{% endif %}
32+
{%- if centering %}\centering{% endif -%}
3233
{% set toprule = parse_table(table_styles, 'toprule') %}
3334
{% if toprule is not none %}
3435
\{{toprule}}

pandas/io/formats/templates/latex_table.tpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
{% if position_float is not none%}
1010
\{{position_float}}
1111
{% endif %}
12+
{%- if centering %}\centering{% endif -%}
1213
{% if caption and caption is string %}
1314
\caption{% raw %}{{% endraw %}{{caption}}{% raw %}}{% endraw %}
1415

pandas/tests/io/formats/test_to_latex.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,30 @@ def test_to_latex_midrule_location(self):
182182
)
183183
assert result == expected
184184

185+
def test_to_latex_centering(self):
186+
# test for \centering command #GH ENH #62733
187+
df = DataFrame({"a": [1], "b": [2]})
188+
189+
# test with DataFrame.to_latex (centering=True)
190+
latex_centered = df.to_latex(centering=True)
191+
assert r"\centering" in latex_centered
192+
# test with DataFrame.to_latex (centering=False)
193+
latex_default = df.to_latex(centering=False)
194+
assert r"\centering" not in latex_default
195+
# test with DataFrame.to_latex with longtable (centering=True)
196+
latex_long_centered = df.to_latex(centering=True, longtable=True)
197+
assert r"\centering" in latex_long_centered
198+
# test with DataFrame.to_latex with longtable (centering=False)
199+
latex_long_default = df.to_latex(centering=False, longtable=True)
200+
assert r"\centering" not in latex_long_default
201+
# test with Styler.to_latex (centering=True)
202+
styler = df.style
203+
latex_styler_centered = styler.to_latex(centering=True)
204+
assert r"\centering" in latex_styler_centered
205+
# test with Styler.to_latex (centering=False)
206+
latex_styler_default = styler.to_latex(centering=False)
207+
assert r"\centering" not in latex_styler_default
208+
185209

186210
class TestToLatexLongtable:
187211
def test_to_latex_empty_longtable(self):

0 commit comments

Comments
 (0)