Skip to content

Commit 29abbda

Browse files
attack68CGe0516
authored andcommitted
ENH: add environment, e.g. "longtable", to Styler.to_latex (pandas-dev#41866)
1 parent aaf360b commit 29abbda

File tree

6 files changed

+280
-54
lines changed

6 files changed

+280
-54
lines changed

doc/source/whatsnew/v1.4.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Other enhancements
3535
- Additional options added to :meth:`.Styler.bar` to control alignment and display, with keyword only arguments (:issue:`26070`, :issue:`36419`)
3636
- :meth:`Styler.bar` now validates the input argument ``width`` and ``height`` (:issue:`42511`)
3737
- :meth:`Series.ewm`, :meth:`DataFrame.ewm`, now support a ``method`` argument with a ``'table'`` option that performs the windowing operation over an entire :class:`DataFrame`. See :ref:`Window Overview <window.overview>` for performance and functional benefits (:issue:`42273`)
38+
- Added keyword argument ``environment`` to :meth:`.Styler.to_latex` also allowing a specific "longtable" entry with a separate jinja2 template (:issue:`41866`)
3839
-
3940

4041
.. ---------------------------------------------------------------------------

pandas/io/formats/style.py

+16
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@ def to_latex(
427427
multirow_align: str = "c",
428428
multicol_align: str = "r",
429429
siunitx: bool = False,
430+
environment: str | None = None,
430431
encoding: str | None = None,
431432
convert_css: bool = False,
432433
):
@@ -457,6 +458,8 @@ def to_latex(
457458
\\begin{table}[<position>]
458459
459460
\\<position_float>
461+
462+
Cannot be used if ``environment`` is "longtable".
460463
hrules : bool, default False
461464
Set to `True` to add \\toprule, \\midrule and \\bottomrule from the
462465
{booktabs} LaTeX package.
@@ -483,6 +486,12 @@ def to_latex(
483486
the left, centrally, or at the right.
484487
siunitx : bool, default False
485488
Set to ``True`` to structure LaTeX compatible with the {siunitx} package.
489+
environment : str, optional
490+
If given, the environment that will replace 'table' in ``\\begin{table}``.
491+
If 'longtable' is specified then a more suitable template is
492+
rendered.
493+
494+
.. versionadded:: 1.4.0
486495
encoding : str, default "utf-8"
487496
Character encoding setting.
488497
convert_css : bool, default False
@@ -519,6 +528,8 @@ def to_latex(
519528
italic (with siunitx) | \\usepackage{etoolbox}
520529
| \\robustify\\itshape
521530
| \\sisetup{detect-all = true} *(within {document})*
531+
environment \\usepackage{longtable} if arg is "longtable"
532+
| or any other relevant environment package
522533
===================== ==========================================================
523534
524535
**Cell Styles**
@@ -748,6 +759,10 @@ def to_latex(
748759
)
749760

750761
if position_float:
762+
if environment == "longtable":
763+
raise ValueError(
764+
"`position_float` cannot be used in 'longtable' `environment`"
765+
)
751766
if position_float not in ["raggedright", "raggedleft", "centering"]:
752767
raise ValueError(
753768
f"`position_float` should be one of "
@@ -788,6 +803,7 @@ def to_latex(
788803
sparse_columns=sparse_columns,
789804
multirow_align=multirow_align,
790805
multicol_align=multicol_align,
806+
environment=environment,
791807
convert_css=convert_css,
792808
)
793809

pandas/io/formats/templates/latex.tpl

+4-51
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,5 @@
1-
{% if parse_wrap(table_styles, caption) %}
2-
\begin{table}
3-
{%- set position = parse_table(table_styles, 'position') %}
4-
{%- if position is not none %}
5-
[{{position}}]
6-
{%- endif %}
7-
8-
{% set position_float = parse_table(table_styles, 'position_float') %}
9-
{% if position_float is not none%}
10-
\{{position_float}}
11-
{% endif %}
12-
{% if caption and caption is string %}
13-
\caption{% raw %}{{% endraw %}{{caption}}{% raw %}}{% endraw %}
14-
15-
{% elif caption and caption is sequence %}
16-
\caption[{{caption[1]}}]{% raw %}{{% endraw %}{{caption[0]}}{% raw %}}{% endraw %}
17-
18-
{% endif %}
19-
{% for style in table_styles %}
20-
{% if style['selector'] not in ['position', 'position_float', 'caption', 'toprule', 'midrule', 'bottomrule', 'column_format'] %}
21-
\{{style['selector']}}{{parse_table(table_styles, style['selector'])}}
22-
{% endif %}
23-
{% endfor %}
24-
{% endif %}
25-
\begin{tabular}
26-
{%- set column_format = parse_table(table_styles, 'column_format') %}
27-
{% raw %}{{% endraw %}{{column_format}}{% raw %}}{% endraw %}
28-
29-
{% set toprule = parse_table(table_styles, 'toprule') %}
30-
{% if toprule is not none %}
31-
\{{toprule}}
32-
{% endif %}
33-
{% for row in head %}
34-
{% for c in row %}{%- if not loop.first %} & {% endif %}{{parse_header(c, multirow_align, multicol_align, True)}}{% endfor %} \\
35-
{% endfor %}
36-
{% set midrule = parse_table(table_styles, 'midrule') %}
37-
{% if midrule is not none %}
38-
\{{midrule}}
39-
{% endif %}
40-
{% for row in body %}
41-
{% for c in row %}{% if not loop.first %} & {% endif %}
42-
{%- if c.type == 'th' %}{{parse_header(c, multirow_align, multicol_align)}}{% else %}{{parse_cell(c.cellstyle, c.display_value, convert_css)}}{% endif %}
43-
{%- endfor %} \\
44-
{% endfor %}
45-
{% set bottomrule = parse_table(table_styles, 'bottomrule') %}
46-
{% if bottomrule is not none %}
47-
\{{bottomrule}}
48-
{% endif %}
49-
\end{tabular}
50-
{% if parse_wrap(table_styles, caption) %}
51-
\end{table}
1+
{% if environment == "longtable" %}
2+
{% include "latex_longtable.tpl" %}
3+
{% else %}
4+
{% include "latex_table.tpl" %}
525
{% endif %}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
\begin{longtable}
2+
{%- set position = parse_table(table_styles, 'position') %}
3+
{%- if position is not none %}
4+
[{{position}}]
5+
{%- endif %}
6+
{%- set column_format = parse_table(table_styles, 'column_format') %}
7+
{% raw %}{{% endraw %}{{column_format}}{% raw %}}{% endraw %}
8+
9+
{% for style in table_styles %}
10+
{% if style['selector'] not in ['position', 'position_float', 'caption', 'toprule', 'midrule', 'bottomrule', 'column_format', 'label'] %}
11+
\{{style['selector']}}{{parse_table(table_styles, style['selector'])}}
12+
{% endif %}
13+
{% endfor %}
14+
{% if caption and caption is string %}
15+
\caption{% raw %}{{% endraw %}{{caption}}{% raw %}}{% endraw %}
16+
{%- set label = parse_table(table_styles, 'label') %}
17+
{%- if label is not none %}
18+
\label{{label}}
19+
{%- endif %} \\
20+
{% elif caption and caption is sequence %}
21+
\caption[{{caption[1]}}]{% raw %}{{% endraw %}{{caption[0]}}{% raw %}}{% endraw %}
22+
{%- set label = parse_table(table_styles, 'label') %}
23+
{%- if label is not none %}
24+
\label{{label}}
25+
{%- endif %} \\
26+
{% endif %}
27+
{% set toprule = parse_table(table_styles, 'toprule') %}
28+
{% if toprule is not none %}
29+
\{{toprule}}
30+
{% endif %}
31+
{% for row in head %}
32+
{% for c in row %}{%- if not loop.first %} & {% endif %}{{parse_header(c, multirow_align, multicol_align, True)}}{% endfor %} \\
33+
{% endfor %}
34+
{% set midrule = parse_table(table_styles, 'midrule') %}
35+
{% if midrule is not none %}
36+
\{{midrule}}
37+
{% endif %}
38+
\endfirsthead
39+
{% if caption and caption is string %}
40+
\caption[]{% raw %}{{% endraw %}{{caption}}{% raw %}}{% endraw %} \\
41+
{% elif caption and caption is sequence %}
42+
\caption[]{% raw %}{{% endraw %}{{caption[0]}}{% raw %}}{% endraw %} \\
43+
{% endif %}
44+
{% if toprule is not none %}
45+
\{{toprule}}
46+
{% endif %}
47+
{% for row in head %}
48+
{% for c in row %}{%- if not loop.first %} & {% endif %}{{parse_header(c, multirow_align, multicol_align, True)}}{% endfor %} \\
49+
{% endfor %}
50+
{% if midrule is not none %}
51+
\{{midrule}}
52+
{% endif %}
53+
\endhead
54+
{% if midrule is not none %}
55+
\{{midrule}}
56+
{% endif %}
57+
\multicolumn{% raw %}{{% endraw %}{{column_format|length}}{% raw %}}{% endraw %}{r}{Continued on next page} \\
58+
{% if midrule is not none %}
59+
\{{midrule}}
60+
{% endif %}
61+
\endfoot
62+
{% set bottomrule = parse_table(table_styles, 'bottomrule') %}
63+
{% if bottomrule is not none %}
64+
\{{bottomrule}}
65+
{% endif %}
66+
\endlastfoot
67+
{% for row in body %}
68+
{% for c in row %}{% if not loop.first %} & {% endif %}
69+
{%- if c.type == 'th' %}{{parse_header(c, multirow_align, multicol_align)}}{% else %}{{parse_cell(c.cellstyle, c.display_value, convert_css)}}{% endif %}
70+
{%- endfor %} \\
71+
{% endfor %}
72+
\end{longtable}
73+
{% raw %}{% endraw %}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{% if environment or parse_wrap(table_styles, caption) %}
2+
\begin{% raw %}{{% endraw %}{{environment if environment else "table"}}{% raw %}}{% endraw %}
3+
{%- set position = parse_table(table_styles, 'position') %}
4+
{%- if position is not none %}
5+
[{{position}}]
6+
{%- endif %}
7+
8+
{% set position_float = parse_table(table_styles, 'position_float') %}
9+
{% if position_float is not none%}
10+
\{{position_float}}
11+
{% endif %}
12+
{% if caption and caption is string %}
13+
\caption{% raw %}{{% endraw %}{{caption}}{% raw %}}{% endraw %}
14+
15+
{% elif caption and caption is sequence %}
16+
\caption[{{caption[1]}}]{% raw %}{{% endraw %}{{caption[0]}}{% raw %}}{% endraw %}
17+
18+
{% endif %}
19+
{% for style in table_styles %}
20+
{% if style['selector'] not in ['position', 'position_float', 'caption', 'toprule', 'midrule', 'bottomrule', 'column_format'] %}
21+
\{{style['selector']}}{{parse_table(table_styles, style['selector'])}}
22+
{% endif %}
23+
{% endfor %}
24+
{% endif %}
25+
\begin{tabular}
26+
{%- set column_format = parse_table(table_styles, 'column_format') %}
27+
{% raw %}{{% endraw %}{{column_format}}{% raw %}}{% endraw %}
28+
29+
{% set toprule = parse_table(table_styles, 'toprule') %}
30+
{% if toprule is not none %}
31+
\{{toprule}}
32+
{% endif %}
33+
{% for row in head %}
34+
{% for c in row %}{%- if not loop.first %} & {% endif %}{{parse_header(c, multirow_align, multicol_align, True)}}{% endfor %} \\
35+
{% endfor %}
36+
{% set midrule = parse_table(table_styles, 'midrule') %}
37+
{% if midrule is not none %}
38+
\{{midrule}}
39+
{% endif %}
40+
{% for row in body %}
41+
{% for c in row %}{% if not loop.first %} & {% endif %}
42+
{%- if c.type == 'th' %}{{parse_header(c, multirow_align, multicol_align)}}{% else %}{{parse_cell(c.cellstyle, c.display_value, convert_css)}}{% endif %}
43+
{%- endfor %} \\
44+
{% endfor %}
45+
{% set bottomrule = parse_table(table_styles, 'bottomrule') %}
46+
{% if bottomrule is not none %}
47+
\{{bottomrule}}
48+
{% endif %}
49+
\end{tabular}
50+
{% if environment or parse_wrap(table_styles, caption) %}
51+
\end{% raw %}{{% endraw %}{{environment if environment else "table"}}{% raw %}}{% endraw %}
52+
53+
{% endif %}

0 commit comments

Comments
 (0)