diff --git a/doc/source/user_guide/options.rst b/doc/source/user_guide/options.rst index 00da304ec9516..1ba7ea21d6c8c 100644 --- a/doc/source/user_guide/options.rst +++ b/doc/source/user_guide/options.rst @@ -502,6 +502,10 @@ styler.format.escape None Whether to escape "html" or styler.html.mathjax True If set to False will render specific CSS classes to table attributes that will prevent Mathjax from rendering in Jupyter Notebook. +styler.latex.multicol_align r Alignment of headers in a merged column due to sparsification. Can be in {"r", "c", "l"}. +styler.latex.multirow_align c Alignment of index labels in a merged row due to sparsification. Can be in {"c", "t", "b"}. +styler.latex.environment None If given will replace the default ``\\begin{table}`` environment. If "longtable" is specified + this will render with a specific "longtable" template with longtable features. ======================================= ============ ================================== diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index b60d020de6201..ef7b6ec92f175 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -75,7 +75,7 @@ Styler - :meth:`.Styler.to_latex` introduces keyword argument ``environment``, which also allows a specific "longtable" entry through a separate jinja2 template (:issue:`41866`). - :meth:`.Styler.to_html` introduces keyword arguments ``sparse_index``, ``sparse_columns``, ``bold_headers``, ``caption`` (:issue:`41946`, :issue:`43149`). - Keyword argument ``level`` is added to :meth:`.Styler.hide_index` and :meth:`.Styler.hide_columns` for optionally controlling hidden levels in a MultiIndex (:issue:`25475`) - - Global options have been extended to configure default ``Styler`` properties including formatting and encoding and mathjax options (:issue:`41395`) + - Global options have been extended to configure default ``Styler`` properties including formatting and encoding and mathjax options and LaTeX (:issue:`41395`) Formerly Styler relied on ``display.html.use_mathjax``, which has now been replaced by ``styler.html.mathjax``. diff --git a/pandas/core/config_init.py b/pandas/core/config_init.py index cbe75e914981b..b048eaee4aee1 100644 --- a/pandas/core/config_init.py +++ b/pandas/core/config_init.py @@ -793,6 +793,22 @@ def register_converter_cb(key): A formatter object to be used as default within ``Styler.format``. """ +styler_multirow_align = """ +: {"c", "t", "b"} + The specifier for vertical alignment of sparsified LaTeX multirows. +""" + +styler_multicol_align = """ +: {"r", "c", "l"} + The specifier for horizontal alignment of sparsified LaTeX multicolumns. +""" + +styler_environment = """ +: str + The environment to replace ``\\begin{table}``. If "longtable" is used results + in a specific longtable environment format. +""" + styler_encoding = """ : str The encoding used for output HTML and LaTeX files. @@ -855,3 +871,24 @@ def register_converter_cb(key): ) cf.register_option("html.mathjax", True, styler_mathjax, validator=is_bool) + + cf.register_option( + "latex.multirow_align", + "c", + styler_multirow_align, + validator=is_one_of_factory(["c", "t", "b"]), + ) + + cf.register_option( + "latex.multicol_align", + "r", + styler_multicol_align, + validator=is_one_of_factory(["r", "c", "l"]), + ) + + cf.register_option( + "latex.environment", + None, + styler_environment, + validator=is_instance_factory([type(None), str]), + ) diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index ddafd8369ddd5..2fcac77e81cf3 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -458,8 +458,8 @@ def to_latex( caption: str | tuple | None = None, sparse_index: bool | None = None, sparse_columns: bool | None = None, - multirow_align: str = "c", - multicol_align: str = "r", + multirow_align: str | None = None, + multicol_align: str | None = None, siunitx: bool = False, environment: str | None = None, encoding: str | None = None, @@ -512,18 +512,21 @@ def to_latex( 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. - multirow_align : {"c", "t", "b"} + multirow_align : {"c", "t", "b"}, optional If sparsifying hierarchical MultiIndexes whether to align text centrally, - at the top or bottom. - multicol_align : {"r", "c", "l"} + at the top or bottom. If not given defaults to + ``pandas.options.styler.latex.multirow_align`` + multicol_align : {"r", "c", "l"}, optional If sparsifying hierarchical MultiIndex columns whether to align text at - the left, centrally, or at the right. + the left, centrally, or at the right. If not given defaults to + ``pandas.options.styler.latex.multicol_align`` siunitx : bool, default False Set to ``True`` to structure LaTeX compatible with the {siunitx} package. environment : str, optional If given, the environment that will replace 'table' in ``\\begin{table}``. If 'longtable' is specified then a more suitable template is - rendered. + rendered. If not given defaults to + ``pandas.options.styler.latex.environment``. .. versionadded:: 1.4.0 encoding : str, optional @@ -840,7 +843,9 @@ def to_latex( sparse_index = get_option("styler.sparse.index") if sparse_columns is None: sparse_columns = get_option("styler.sparse.columns") - + environment = environment or get_option("styler.latex.environment") + multicol_align = multicol_align or get_option("styler.latex.multicol_align") + multirow_align = multirow_align or get_option("styler.latex.multirow_align") latex = obj._render_latex( sparse_index=sparse_index, sparse_columns=sparse_columns, diff --git a/pandas/tests/io/formats/style/test_to_latex.py b/pandas/tests/io/formats/style/test_to_latex.py index ac164f2de9fb2..914eeff97c0fe 100644 --- a/pandas/tests/io/formats/style/test_to_latex.py +++ b/pandas/tests/io/formats/style/test_to_latex.py @@ -276,6 +276,30 @@ def test_multiindex_row_and_col(df): assert s.to_latex(sparse_index=False, sparse_columns=False) == expected +def test_multi_options(df): + 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"] + df = df.astype({"A": int}) + df.index, df.columns = ridx, cidx + styler = df.style.format(precision=2) + + expected = dedent( + """\ + {} & {} & \\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() + + with option_context("styler.latex.multirow_align", "b"): + assert "\\multirow[b]{2}{*}{A} & a & 0 & -0.61 & ab \\\\" in styler.to_latex() + + def test_multiindex_columns_hidden(): df = DataFrame([[1, 2, 3, 4]]) df.columns = MultiIndex.from_tuples([("A", 1), ("A", 2), ("A", 3), ("B", 1)]) @@ -376,6 +400,12 @@ def test_comprehensive(df, environment): assert s.format(precision=2).to_latex(environment=environment) == expected +def test_environment_option(styler): + with option_context("styler.latex.environment", "bar-env"): + assert "\\begin{bar-env}" in styler.to_latex() + assert "\\begin{foo-env}" in styler.to_latex(environment="foo-env") + + def test_parse_latex_table_styles(styler): styler.set_table_styles( [