Skip to content

ENH: Add Styler.to_typst() #60733

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
2 changes: 2 additions & 0 deletions doc/source/reference/style.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Styler properties
Styler.template_html_style
Styler.template_html_table
Styler.template_latex
Styler.template_typst
Styler.template_string
Styler.loader

Expand Down Expand Up @@ -77,6 +78,7 @@ Style export and import

Styler.to_html
Styler.to_latex
Styler.to_typst
Styler.to_excel
Styler.to_string
Styler.export
Expand Down
5 changes: 3 additions & 2 deletions doc/source/whatsnew/v2.3.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ Upcoming changes in pandas 3.0
Enhancements
~~~~~~~~~~~~

.. _whatsnew_230.enhancements.enhancement1:
.. _whatsnew_230.enhancements.styler:

enhancement1
Styler
^^^^^^^^^^^^

- The :meth:`.Styler.to_typst` function has been added to write Styler to a file, buffer or string in Typst format (:issue:`57617`)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this to 3.0.0?


.. _whatsnew_230.enhancements.other:

Expand Down
105 changes: 105 additions & 0 deletions pandas/io/formats/style.py
Original file line number Diff line number Diff line change
Expand Up @@ -1228,6 +1228,111 @@ def to_latex(
)
return save_to_buffer(latex, buf=buf, encoding=encoding)

@overload
def to_typst(
self,
buf: FilePath | WriteBuffer[str],
*,
encoding: str | None = ...,
sparse_index: bool | None = ...,
sparse_columns: bool | None = ...,
max_rows: int | None = ...,
max_columns: int | None = ...,
) -> None: ...

@overload
def to_typst(
self,
buf: None = ...,
*,
encoding: str | None = ...,
sparse_index: bool | None = ...,
sparse_columns: bool | None = ...,
max_rows: int | None = ...,
max_columns: int | None = ...,
) -> str: ...

@Substitution(buf=buffering_args, encoding=encoding_args)
def to_typst(
self,
buf: FilePath | WriteBuffer[str] | None = None,
*,
encoding: str | None = None,
sparse_index: bool | None = None,
sparse_columns: bool | None = None,
max_rows: int | None = None,
max_columns: int | None = None,
) -> str | None:
"""
Write Styler to a file, buffer or string in Typst format.

.. versionadded:: 2.3.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.. versionadded:: 2.3.0
.. versionadded:: 3.0.0


Parameters
----------
%(buf)s
%(encoding)s
sparse_index : bool, optional
Whether to sparsify the display of a hierarchical index. Setting to False
will display each explicit level element in a hierarchical key for each row.
Defaults to ``pandas.options.styler.sparse.index`` value.
sparse_columns : bool, optional
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.
max_rows : int, optional
The maximum number of rows that will be rendered. Defaults to
``pandas.options.styler.render.max_rows``, which is None.
max_columns : int, optional
The maximum number of columns that will be rendered. Defaults to
``pandas.options.styler.render.max_columns``, which is None.

Rows and columns may be reduced if the number of total elements is
large. This value is set to ``pandas.options.styler.render.max_elements``,
which is 262144 (18 bit browser rendering).

Returns
-------
str or None
If `buf` is None, returns the result as a string. Otherwise returns `None`.

See Also
--------
DataFrame.to_typst : Write a DataFrame to a file,
buffer or string in Typst format.

Examples
--------
>>> df = pd.DataFrame({"A": [1, 2], "B": [3, 4]})
>>> df.style.to_typst() # doctest: +SKIP

.. code-block:: typst

#table(
columns: 3,
[], [A], [B],

[0], [1], [3],
[1], [2], [4],
)
"""
obj = self._copy(deepcopy=True)

if sparse_index is None:
sparse_index = get_option("styler.sparse.index")
if sparse_columns is None:
sparse_columns = get_option("styler.sparse.columns")

text = obj._render_typst(
sparse_columns=sparse_columns,
sparse_index=sparse_index,
max_rows=max_rows,
max_cols=max_columns,
)
return save_to_buffer(
text, buf=buf, encoding=(encoding if buf is not None else None)
)

@overload
def to_html(
self,
Expand Down
16 changes: 16 additions & 0 deletions pandas/io/formats/style_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class StylerRenderer:
template_html_table = env.get_template("html_table.tpl")
template_html_style = env.get_template("html_style.tpl")
template_latex = env.get_template("latex.tpl")
template_typst = env.get_template("typst.tpl")
template_string = env.get_template("string.tpl")

def __init__(
Expand Down Expand Up @@ -232,6 +233,21 @@ def _render_latex(
d.update(kwargs)
return self.template_latex.render(**d)

def _render_typst(
self,
sparse_index: bool,
sparse_columns: bool,
max_rows: int | None = None,
max_cols: int | None = None,
**kwargs,
) -> str:
"""
Render a Styler in typst format
"""
d = self._render(sparse_index, sparse_columns, max_rows, max_cols)
d.update(kwargs)
return self.template_typst.render(**d)

def _render_string(
self,
sparse_index: bool,
Expand Down
12 changes: 12 additions & 0 deletions pandas/io/formats/templates/typst.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#table(
columns: {{ head[0] | length }},
{% for r in head %}
{% for c in r %}[{% if c["is_visible"] %}{{ c["display_value"] }}{% endif %}],{% if not loop.last %} {% endif%}{% endfor %}

{% endfor %}

{% for r in body %}
{% for c in r %}[{% if c["is_visible"] %}{{ c["display_value"] }}{% endif %}],{% if not loop.last %} {% endif%}{% endfor %}

{% endfor %}
)
96 changes: 96 additions & 0 deletions pandas/tests/io/formats/style/test_to_typst.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from textwrap import dedent

import pytest

from pandas import (
DataFrame,
Series,
)

pytest.importorskip("jinja2")
from pandas.io.formats.style import Styler


@pytest.fixture
def df():
return DataFrame(
{"A": [0, 1], "B": [-0.61, -1.22], "C": Series(["ab", "cd"], dtype=object)}
)


@pytest.fixture
def styler(df):
return Styler(df, uuid_len=0, precision=2)


def test_basic_table(styler):
result = styler.to_typst()
expected = dedent(
"""\
#table(
columns: 4,
[], [A], [B], [C],

[0], [0], [-0.61], [ab],
[1], [1], [-1.22], [cd],
)"""
)
assert result == expected


def test_concat(styler):
result = styler.concat(styler.data.agg(["sum"]).style).to_typst()
expected = dedent(
"""\
#table(
columns: 4,
[], [A], [B], [C],

[0], [0], [-0.61], [ab],
[1], [1], [-1.22], [cd],
[sum], [1], [-1.830000], [abcd],
)"""
)
assert result == expected


def test_concat_recursion(styler):
df = styler.data
styler1 = styler
styler2 = Styler(df.agg(["sum"]), uuid_len=0, precision=3)
styler3 = Styler(df.agg(["sum"]), uuid_len=0, precision=4)
result = styler1.concat(styler2.concat(styler3)).to_typst()
expected = dedent(
"""\
#table(
columns: 4,
[], [A], [B], [C],

[0], [0], [-0.61], [ab],
[1], [1], [-1.22], [cd],
[sum], [1], [-1.830], [abcd],
[sum], [1], [-1.8300], [abcd],
)"""
)
assert result == expected


def test_concat_chain(styler):
df = styler.data
styler1 = styler
styler2 = Styler(df.agg(["sum"]), uuid_len=0, precision=3)
styler3 = Styler(df.agg(["sum"]), uuid_len=0, precision=4)
result = styler1.concat(styler2).concat(styler3).to_typst()
expected = dedent(
"""\
#table(
columns: 4,
[], [A], [B], [C],

[0], [0], [-0.61], [ab],
[1], [1], [-1.22], [cd],
[sum], [1], [-1.830], [abcd],
[sum], [1], [-1.8300], [abcd],
)"""
)
assert result == expected
1 change: 1 addition & 0 deletions scripts/validate_docstrings.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"Styler.template_html_style",
"Styler.template_html_table",
"Styler.template_latex",
"Styler.template_typst",
"Styler.template_string",
"Styler.loader",
"errors.InvalidComparison",
Expand Down
Loading