Skip to content

Commit 4f9e8e1

Browse files
committed
ENH: add escape math mode with escape=latex-math
1 parent 852518e commit 4f9e8e1

File tree

2 files changed

+56
-6
lines changed

2 files changed

+56
-6
lines changed

pandas/io/formats/style_render.py

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,8 @@ def format(
989989
Use 'latex' to replace the characters ``&``, ``%``, ``$``, ``#``, ``_``,
990990
``{``, ``}``, ``~``, ``^``, and ``\`` in the cell display string with
991991
LaTeX-safe sequences.
992+
Use 'latex-math' to replace the characters the same way as in 'latex' mode,
993+
except for math substrings, which start and end with ``$``.
992994
Escaping is done before ``formatter``.
993995
994996
.. versionadded:: 1.3.0
@@ -1105,16 +1107,28 @@ def format(
11051107
<td .. >NA</td>
11061108
...
11071109
1108-
Using a ``formatter`` with LaTeX ``escape``.
1110+
Using a ``formatter`` with ``escape`` in 'latex' mode.
11091111
1110-
>>> df = pd.DataFrame([["123"], ["~ ^"], ["$%#"]])
1112+
>>> df = pd.DataFrame([["123"], ["~ ^"], ["%#"]])
11111113
>>> df.style.format("\\textbf{{{}}}", escape="latex").to_latex()
11121114
... # doctest: +SKIP
11131115
\begin{tabular}{ll}
1114-
{} & {0} \\
1116+
& 0 \\
11151117
0 & \textbf{123} \\
11161118
1 & \textbf{\textasciitilde \space \textasciicircum } \\
1117-
2 & \textbf{\$\%\#} \\
1119+
2 & \textbf{\%\#} \\
1120+
\end{tabular}
1121+
1122+
Using ``escape`` in 'latex-math' mode.
1123+
1124+
>>> df = pd.DataFrame([[r"$\sum_{i=1}^{10} a_i$ a~b $\alpha \
1125+
... = \frac{\beta}{\zeta^2}$"], ["%#^ $ \$x^2 $"]])
1126+
>>> df.style.format(escape="latex-math").to_latex()
1127+
... # doctest: +SKIP
1128+
\begin{tabular}{ll}
1129+
& 0 \\
1130+
0 & $\sum_{i=1}^{10} a_i$ a\textasciitilde b $\alpha = \frac{\beta}{\zeta^2}$ \\
1131+
1 & \%\#\textasciicircum \space $ \$x^2 $ \\
11181132
\end{tabular}
11191133
11201134
Pandas defines a `number-format` pseudo CSS attribute instead of the `.format`
@@ -1743,9 +1757,12 @@ def _str_escape(x, escape):
17431757
return escape_html(x)
17441758
elif escape == "latex":
17451759
return _escape_latex(x)
1760+
elif escape == "latex-math":
1761+
return _escape_latex_math(x)
17461762
else:
17471763
raise ValueError(
1748-
f"`escape` only permitted in {{'html', 'latex'}}, got {escape}"
1764+
f"`escape` only permitted in {{'html', 'latex', 'latex-math'}}, \
1765+
got {escape}"
17491766
)
17501767
return x
17511768

@@ -2344,3 +2361,36 @@ def _escape_latex(s):
23442361
.replace("^", "\\textasciicircum ")
23452362
.replace("ab2§=§8yz", "\\textbackslash ")
23462363
)
2364+
2365+
2366+
def _escape_latex_math(s):
2367+
r"""
2368+
All characters between two characters ``$`` are preserved.
2369+
2370+
The substrings in LaTeX math mode, which start with the character ``$``
2371+
and end with ``$``, are preserved without escaping. Otherwise
2372+
regular LaTeX escaping applies. See ``_escape_latex()``.
2373+
2374+
Parameters
2375+
----------
2376+
s : str
2377+
Input to be escaped
2378+
2379+
Return
2380+
------
2381+
str :
2382+
Escaped string
2383+
"""
2384+
s = s.replace(r"\$", r"ab2§=§8yz")
2385+
pattern = re.compile(r"\$.*?\$")
2386+
pos = 0
2387+
ps = pattern.search(s, pos)
2388+
res = []
2389+
while ps:
2390+
res.append(_escape_latex(s[pos : ps.span()[0]]))
2391+
res.append(ps.group())
2392+
pos = ps.span()[1]
2393+
ps = pattern.search(s, pos)
2394+
2395+
res.append(_escape_latex(s[pos : len(s)]))
2396+
return "".join(res).replace(r"ab2§=§8yz", r"\$")

pandas/tests/io/formats/style/test_format.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ def test_format_decimal(formatter, thousands, precision, func, col):
359359

360360

361361
def test_str_escape_error():
362-
msg = "`escape` only permitted in {'html', 'latex'}, got "
362+
msg = "`escape` only permitted in {'html', 'latex', 'latex-math'}, got "
363363
with pytest.raises(ValueError, match=msg):
364364
_str_escape("text", "bad_escape")
365365

0 commit comments

Comments
 (0)