Skip to content

REF: styler.to_latex siunitx changes: render {} only when needed. #43397

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

Merged
merged 2 commits into from
Sep 4, 2021
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
1 change: 1 addition & 0 deletions pandas/io/formats/style.py
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,7 @@ def to_latex(
multicol_align=multicol_align,
environment=environment,
convert_css=convert_css,
siunitx=siunitx,
)

encoding = encoding or get_option("styler.render.encoding")
Expand Down
4 changes: 2 additions & 2 deletions pandas/io/formats/templates/latex_longtable.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
\{{toprule}}
{% endif %}
{% for row in head %}
{% for c in row %}{%- if not loop.first %} & {% endif %}{{parse_header(c, multirow_align, multicol_align, True)}}{% endfor %} \\
{% for c in row %}{%- if not loop.first %} & {% endif %}{{parse_header(c, multirow_align, multicol_align, siunitx)}}{% endfor %} \\
{% endfor %}
{% set midrule = parse_table(table_styles, 'midrule') %}
{% if midrule is not none %}
Expand All @@ -45,7 +45,7 @@
\{{toprule}}
{% endif %}
{% for row in head %}
{% for c in row %}{%- if not loop.first %} & {% endif %}{{parse_header(c, multirow_align, multicol_align, True)}}{% endfor %} \\
{% for c in row %}{%- if not loop.first %} & {% endif %}{{parse_header(c, multirow_align, multicol_align, siunitx)}}{% endfor %} \\
{% endfor %}
{% if midrule is not none %}
\{{midrule}}
Expand Down
2 changes: 1 addition & 1 deletion pandas/io/formats/templates/latex_table.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
\{{toprule}}
{% endif %}
{% for row in head %}
{% for c in row %}{%- if not loop.first %} & {% endif %}{{parse_header(c, multirow_align, multicol_align, True)}}{% endfor %} \\
{% for c in row %}{%- if not loop.first %} & {% endif %}{{parse_header(c, multirow_align, multicol_align, siunitx)}}{% endfor %} \\
{% endfor %}
{% set midrule = parse_table(table_styles, 'midrule') %}
{% if midrule is not none %}
Expand Down
2 changes: 1 addition & 1 deletion pandas/tests/io/formats/style/test_non_unique.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ def test_latex_non_unique(styler):
assert result == dedent(
"""\
\\begin{tabular}{lrrr}
{} & {c} & {d} & {d} \\\\
& c & d & d \\\\
i & 1.000000 & 2.000000 & 3.000000 \\\\
j & 4.000000 & 5.000000 & 6.000000 \\\\
j & 7.000000 & 8.000000 & 9.000000 \\\\
Expand Down
99 changes: 61 additions & 38 deletions pandas/tests/io/formats/style/test_to_latex.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def test_minimal_latex_tabular(styler):
expected = dedent(
"""\
\\begin{tabular}{lrrl}
{} & {A} & {B} & {C} \\\\
& A & B & C \\\\
0 & 0 & -0.61 & ab \\\\
1 & 1 & -1.22 & cd \\\\
\\end{tabular}
Expand All @@ -47,7 +47,7 @@ def test_tabular_hrules(styler):
"""\
\\begin{tabular}{lrrl}
\\toprule
{} & {A} & {B} & {C} \\\\
& A & B & C \\\\
\\midrule
0 & 0 & -0.61 & ab \\\\
1 & 1 & -1.22 & cd \\\\
Expand All @@ -69,7 +69,7 @@ def test_tabular_custom_hrules(styler):
"""\
\\begin{tabular}{lrrl}
\\hline
{} & {A} & {B} & {C} \\\\
& A & B & C \\\\
0 & 0 & -0.61 & ab \\\\
1 & 1 & -1.22 & cd \\\\
\\otherline
Expand Down Expand Up @@ -169,7 +169,7 @@ def test_cell_styling(styler):
expected = dedent(
"""\
\\begin{tabular}{lrrl}
{} & {A} & {B} & {C} \\\\
& A & B & C \\\\
0 & 0 & \\itshape {\\Huge -0.61} & ab \\\\
1 & \\itshape {\\Huge 1} & -1.22 & \\itshape {\\Huge cd} \\\\
\\end{tabular}
Expand All @@ -184,8 +184,8 @@ def test_multiindex_columns(df):
expected = dedent(
"""\
\\begin{tabular}{lrrl}
{} & \\multicolumn{2}{r}{A} & {B} \\\\
{} & {a} & {b} & {c} \\\\
& \\multicolumn{2}{r}{A} & B \\\\
& a & b & c \\\\
0 & 0 & -0.61 & ab \\\\
1 & 1 & -1.22 & cd \\\\
\\end{tabular}
Expand All @@ -198,8 +198,8 @@ def test_multiindex_columns(df):
expected = dedent(
"""\
\\begin{tabular}{lrrl}
{} & {A} & {A} & {B} \\\\
{} & {a} & {b} & {c} \\\\
& A & A & B \\\\
& a & b & c \\\\
0 & 0 & -0.61 & ab \\\\
1 & 1 & -1.22 & cd \\\\
\\end{tabular}
Expand All @@ -217,7 +217,7 @@ def test_multiindex_row(df):
expected = dedent(
"""\
\\begin{tabular}{llrrl}
{} & {} & {A} & {B} & {C} \\\\
& & A & B & C \\\\
\\multirow[c]{2}{*}{A} & a & 0 & -0.61 & ab \\\\
& b & 1 & -1.22 & cd \\\\
B & c & 2 & -2.22 & de \\\\
Expand All @@ -231,7 +231,7 @@ def test_multiindex_row(df):
expected = dedent(
"""\
\\begin{tabular}{llrrl}
{} & {} & {A} & {B} & {C} \\\\
& & A & B & C \\\\
A & a & 0 & -0.61 & ab \\\\
A & b & 1 & -1.22 & cd \\\\
B & c & 2 & -2.22 & de \\\\
Expand All @@ -250,8 +250,8 @@ def test_multiindex_row_and_col(df):
expected = dedent(
"""\
\\begin{tabular}{llrrl}
{} & {} & \\multicolumn{2}{l}{Z} & {Y} \\\\
{} & {} & {a} & {b} & {c} \\\\
& & \\multicolumn{2}{l}{Z} & Y \\\\
& & a & b & c \\\\
\\multirow[b]{2}{*}{A} & a & 0 & -0.61 & ab \\\\
& b & 1 & -1.22 & cd \\\\
B & c & 2 & -2.22 & de \\\\
Expand All @@ -265,8 +265,8 @@ def test_multiindex_row_and_col(df):
expected = dedent(
"""\
\\begin{tabular}{llrrl}
{} & {} & {Z} & {Z} & {Y} \\\\
{} & {} & {a} & {b} & {c} \\\\
& & Z & Z & Y \\\\
& & a & b & c \\\\
A & a & 0 & -0.61 & ab \\\\
A & b & 1 & -1.22 & cd \\\\
B & c & 2 & -2.22 & de \\\\
Expand All @@ -286,15 +286,15 @@ def test_multi_options(df):

expected = dedent(
"""\
{} & {} & \\multicolumn{2}{r}{Z} & {Y} \\\\
{} & {} & {a} & {b} & {c} \\\\
& & \\multicolumn{2}{r}{Z} & Y \\\\
& & a & b & c \\\\
\\multirow[c]{2}{*}{A} & a & 0 & -0.61 & ab \\\\
"""
)
assert expected in styler.to_latex()

with option_context("styler.latex.multicol_align", "l"):
assert "{} & {} & \\multicolumn{2}{l}{Z} & {Y} \\\\" in styler.to_latex()
assert " & & \\multicolumn{2}{l}{Z} & Y \\\\" in styler.to_latex()

with option_context("styler.latex.multirow_align", "b"):
assert "\\multirow[b]{2}{*}{A} & a & 0 & -0.61 & ab \\\\" in styler.to_latex()
Expand Down Expand Up @@ -341,7 +341,7 @@ def test_hidden_index(styler):
expected = dedent(
"""\
\\begin{tabular}{rrl}
{A} & {B} & {C} \\\\
A & B & C \\\\
0 & -0.61 & ab \\\\
1 & -1.22 & cd \\\\
\\end{tabular}
Expand Down Expand Up @@ -384,8 +384,8 @@ def test_comprehensive(df, environment):
\\rowcolors{3}{pink}{}
\\begin{tabular}{rlrlr}
\\toprule
{} & {} & \\multicolumn{2}{r}{Z} & {Y} \\\\
{} & {} & {a} & {b} & {c} \\\\
& & \\multicolumn{2}{r}{Z} & Y \\\\
& & a & b & c \\\\
\\midrule
\\multirow[c]{2}{*}{A} & a & 0 & \\textbf{\\cellcolor[rgb]{1,1,0.6}{-0.61}} & ab \\\\
& b & 1 & -1.22 & cd \\\\
Expand Down Expand Up @@ -579,12 +579,12 @@ def test_longtable_comprehensive(styler):
\\begin{longtable}{lrrl}
\\caption[short]{full} \\label{fig:A} \\\\
\\toprule
{} & {A} & {B} & {C} \\\\
& A & B & C \\\\
\\midrule
\\endfirsthead
\\caption[]{full} \\\\
\\toprule
{} & {A} & {B} & {C} \\\\
& A & B & C \\\\
\\midrule
\\endhead
\\midrule
Expand All @@ -606,9 +606,9 @@ def test_longtable_minimal(styler):
expected = dedent(
"""\
\\begin{longtable}{lrrl}
{} & {A} & {B} & {C} \\\\
& A & B & C \\\\
\\endfirsthead
{} & {A} & {B} & {C} \\\\
& A & B & C \\\\
\\endhead
\\multicolumn{4}{r}{Continued on next page} \\\\
\\endfoot
Expand All @@ -622,27 +622,34 @@ def test_longtable_minimal(styler):


@pytest.mark.parametrize(
"sparse, exp",
"sparse, exp, siunitx",
[
(True, "{} & \\multicolumn{2}{r}{A} & {B}"),
(False, "{} & {A} & {A} & {B}"),
(True, "{} & \\multicolumn{2}{r}{A} & {B}", True),
(False, "{} & {A} & {A} & {B}", True),
(True, " & \\multicolumn{2}{r}{A} & B", False),
(False, " & A & A & B", False),
],
)
def test_longtable_multiindex_columns(df, sparse, exp):
def test_longtable_multiindex_columns(df, sparse, exp, siunitx):
cidx = MultiIndex.from_tuples([("A", "a"), ("A", "b"), ("B", "c")])
df.columns = cidx
with_si = "{} & {a} & {b} & {c} \\\\"
without_si = " & a & b & c \\\\"
expected = dedent(
f"""\
\\begin{{longtable}}{{lrrl}}
\\begin{{longtable}}{{l{"SS" if siunitx else "rr"}l}}
{exp} \\\\
{{}} & {{a}} & {{b}} & {{c}} \\\\
{with_si if siunitx else without_si}
\\endfirsthead
{exp} \\\\
{{}} & {{a}} & {{b}} & {{c}} \\\\
{with_si if siunitx else without_si}
\\endhead
"""
)
assert expected in df.style.to_latex(environment="longtable", sparse_columns=sparse)
result = df.style.to_latex(
environment="longtable", sparse_columns=sparse, siunitx=siunitx
)
assert expected in result


@pytest.mark.parametrize(
Expand All @@ -660,7 +667,7 @@ def test_longtable_caption_label(styler, caption, cap_exp, label, lab_exp):
expected = dedent(
f"""\
{cap_exp1}{lab_exp} \\\\
{{}} & {{A}} & {{B}} & {{C}} \\\\
& A & B & C \\\\
\\endfirsthead
{cap_exp2} \\\\
"""
Expand All @@ -671,8 +678,15 @@ def test_longtable_caption_label(styler, caption, cap_exp, label, lab_exp):


@pytest.mark.parametrize("index", [True, False])
@pytest.mark.parametrize("columns", [True, False])
def test_apply_map_header_render_mi(df, index, columns):
@pytest.mark.parametrize(
"columns, siunitx",
[
(True, True),
(True, False),
(False, False),
],
)
def test_apply_map_header_render_mi(df, index, columns, siunitx):
cidx = MultiIndex.from_tuples([("Z", "a"), ("Z", "b"), ("Y", "c")])
ridx = MultiIndex.from_tuples([("A", "a"), ("A", "b"), ("B", "c")])
df.loc[2, :] = [2, -2.22, "de"]
Expand All @@ -687,7 +701,7 @@ def test_apply_map_header_render_mi(df, index, columns):
if columns:
styler.applymap_index(func, axis="columns")

result = styler.to_latex()
result = styler.to_latex(siunitx=siunitx)

expected_index = dedent(
"""\
Expand All @@ -698,13 +712,17 @@ def test_apply_map_header_render_mi(df, index, columns):
)
assert (expected_index in result) is index

expected_columns = dedent(
exp_cols_si = dedent(
"""\
{} & {} & \\multicolumn{2}{r}{\\bfseries{Z}} & {Y} \\\\
{} & {} & {a} & {b} & {\\bfseries{c}} \\\\
"""
)
assert (expected_columns in result) is columns
exp_cols_no_si = """\
& & \\multicolumn{2}{r}{\\bfseries{Z}} & Y \\\\
& & a & b & \\bfseries{c} \\\\
"""
assert ((exp_cols_si if siunitx else exp_cols_no_si) in result) is columns


def test_repr_option(styler):
Expand All @@ -713,3 +731,8 @@ def test_repr_option(styler):
with option_context("styler.render.repr", "latex"):
assert "\\begin{tabular}" in styler._repr_latex_()[:15]
assert styler._repr_html_() is None


def test_siunitx_basic_headers(styler):
assert "{} & {A} & {B} & {C} \\\\" in styler.to_latex(siunitx=True)
assert " & A & B & C \\\\" in styler.to_latex() # default siunitx=False