Skip to content

Commit ba72f26

Browse files
committed
ENH: add math mode with parentheses
1 parent 7ffb960 commit ba72f26

File tree

2 files changed

+69
-21
lines changed

2 files changed

+69
-21
lines changed

pandas/io/formats/style_render.py

+55-16
Original file line numberDiff line numberDiff line change
@@ -1117,7 +1117,8 @@ def format(
11171117
2 & \textbf{\$\%\#} \\
11181118
\end{tabular}
11191119
1120-
Using ``escape`` in 'latex-math' mode.
1120+
Applying ``escape`` in 'latex-math' mode. In the example below
1121+
we enter math mode using the charackter ``$``.
11211122
11221123
>>> df = pd.DataFrame([[r"$\sum_{i=1}^{10} a_i$ a~b $\alpha \
11231124
... = \frac{\beta}{\zeta^2}$"], ["%#^ $ \$x^2 $"]])
@@ -1129,6 +1130,20 @@ def format(
11291130
1 & \%\#\textasciicircum \space $ \$x^2 $ \\
11301131
\end{tabular}
11311132
1133+
We can use the charackter ``\(`` to enter math mode and the charackter ``\)``
1134+
to close math mode.
1135+
1136+
>>> df = pd.DataFrame([[r"\(\sum_{i=1}^{10} a_i\) a~b \(\alpha \
1137+
... = \frac{\beta}{\zeta^2}\)"], ["%#^ \( \$x^2 \)"]])
1138+
>>> df.style.format(escape="latex-math").to_latex()
1139+
... # doctest: +SKIP
1140+
\begin{tabular}{ll}
1141+
& 0 \\
1142+
0 & \(\sum_{i=1}^{10} a_i\) a\textasciitilde b \(\alpha
1143+
= \frac{\beta}{\zeta^2}\) \\
1144+
1 & \%\#\textasciicircum \space \( \$x^2 \) \\
1145+
\end{tabular}
1146+
11321147
Pandas defines a `number-format` pseudo CSS attribute instead of the `.format`
11331148
method to create `to_excel` permissible formatting. Note that semi-colons are
11341149
CSS protected characters but used as separators in Excel's format string.
@@ -2357,17 +2372,20 @@ def _escape_latex(s):
23572372
.replace("~", "\\textasciitilde ")
23582373
.replace("^ ", "^\\space ") # since \textasciicircum gobbles spaces
23592374
.replace("^", "\\textasciicircum ")
2375+
.replace("ab2§=§8yz(", "\\( ")
2376+
.replace("ab2§=§8yz)", "\\) ")
23602377
.replace("ab2§=§8yz", "\\textbackslash ")
23612378
)
23622379

23632380

23642381
def _escape_latex_math(s):
23652382
r"""
2366-
All characters between two characters ``$`` are preserved.
2383+
All characters in LaTeX math mode are preserved.
23672384
2368-
The substrings in LaTeX math mode, which start with the character ``$``
2369-
and end with ``$``, are preserved without escaping. Otherwise
2370-
regular LaTeX escaping applies. See ``_escape_latex()``.
2385+
The substrings in LaTeX math mode, which either are surrounded
2386+
by two characters ``$`` or start with the character ``\(`` and end with ``\)``,
2387+
are preserved without escaping. Otherwise regular LaTeX escaping applies.
2388+
See ``_escape_latex()``.
23712389
23722390
Parameters
23732391
----------
@@ -2379,16 +2397,37 @@ def _escape_latex_math(s):
23792397
str :
23802398
Escaped string
23812399
"""
2382-
s = s.replace(r"\$", r"rt8§=§7wz")
2383-
pattern = re.compile(r"\$.*?\$")
2384-
pos = 0
2385-
ps = pattern.search(s, pos)
2386-
res = []
2387-
while ps:
2388-
res.append(_escape_latex(s[pos : ps.span()[0]]))
2389-
res.append(ps.group())
2390-
pos = ps.span()[1]
2400+
2401+
def _math_mode_with_dollar(s):
2402+
s = s.replace(r"\$", r"rt8§=§7wz")
2403+
pattern = re.compile(r"\$.*?\$")
2404+
pos = 0
23912405
ps = pattern.search(s, pos)
2406+
res = []
2407+
while ps:
2408+
res.append(_escape_latex(s[pos : ps.span()[0]]))
2409+
res.append(ps.group())
2410+
pos = ps.span()[1]
2411+
ps = pattern.search(s, pos)
2412+
2413+
res.append(_escape_latex(s[pos : len(s)]))
2414+
return "".join(res).replace(r"rt8§=§7wz", r"\$")
2415+
2416+
def _math_mode_with_parentheses(s):
2417+
s = s.replace(r"\(", r"LEFT§=§6yzLEFT").replace(r"\)", r"RIGHTab5§=§RIGHT")
2418+
res = []
2419+
for item in re.split(r"LEFT§=§6yz|ab5§=§RIGHT", s):
2420+
if item.startswith("LEFT") and item.endswith("RIGHT"):
2421+
res.append(item.replace("LEFT", r"\(").replace("RIGHT", r"\)"))
2422+
else:
2423+
res.append(
2424+
_escape_latex(item).replace("LEFT", r"\(").replace("RIGHT", r"\)")
2425+
)
2426+
return "".join(res)
23922427

2393-
res.append(_escape_latex(s[pos : len(s)]))
2394-
return "".join(res).replace(r"rt8§=§7wz", r"\$")
2428+
if s.replace(r"\$", "ab").find(r"$") > -1:
2429+
return _math_mode_with_dollar(s)
2430+
elif s.find(r"\(") > -1:
2431+
return _math_mode_with_parentheses(s)
2432+
else:
2433+
return _escape_latex(s)

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

+14-5
Original file line numberDiff line numberDiff line change
@@ -192,13 +192,22 @@ def test_format_escape_html(escape, exp):
192192
assert styler._translate(True, True)["head"][0][1]["display_value"] == f"&{exp}&"
193193

194194

195-
def test_format_escape_latex_math():
196-
chars = r"$\frac{1}{2} \$ x^2$ ~%#^"
197-
df = DataFrame([[chars]])
195+
@pytest.mark.parametrize(
196+
"chars, expected",
197+
[
198+
(r"$\frac{1}{2} \$ x^2$ ", r"$\frac{1}{2} \$ x^2$ "),
199+
(r"\(\frac{1}{2} \$ x^2\) ", r"\(\frac{1}{2} \$ x^2\) "),
200+
(r"\)", r"\) "),
201+
],
202+
)
203+
def test_format_escape_latex_math(chars, expected):
204+
df = DataFrame([["".join([chars, "~%#^"])]])
198205

199-
expected = r"$\frac{1}{2} \$ x^2$ \textasciitilde \%\#\textasciicircum "
200206
s = df.style.format("{0}", escape="latex-math")
201-
assert expected == s._translate(True, True)["body"][0][1]["display_value"]
207+
assert (
208+
"".join([expected, r"\textasciitilde \%\#\textasciicircum "])
209+
== s._translate(True, True)["body"][0][1]["display_value"]
210+
)
202211

203212

204213
def test_format_escape_na_rep():

0 commit comments

Comments
 (0)