Skip to content

Commit 0dd7fb9

Browse files
Backport PR #46239: DOC+BUG: Styler.to_excel pseudo css number-format (#46245)
Co-authored-by: JHM Darbyshire <[email protected]>
1 parent 1bcb2f9 commit 0dd7fb9

File tree

5 files changed

+42
-1
lines changed

5 files changed

+42
-1
lines changed
32.4 KB
Loading

doc/source/whatsnew/v1.4.2.rst

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Fixed regressions
1616
~~~~~~~~~~~~~~~~~
1717
- Fixed regression in :meth:`DataFrame.drop` and :meth:`Series.drop` when :class:`Index` had extension dtype and duplicates (:issue:`45860`)
1818
- Fixed memory performance regression in :meth:`Series.fillna` when called on a :class:`DataFrame` column with ``inplace=True`` (:issue:`46149`)
19+
- Provided an alternative solution for passing custom Excel formats in :meth:`.Styler.to_excel`, which was a regression based on stricter CSS validation. Examples available in the documentation for :meth:`.Styler.format` (:issue:`46152`)
1920
-
2021

2122
.. ---------------------------------------------------------------------------

pandas/io/formats/excel.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,9 @@ def build_fill(self, props: Mapping[str, str]):
309309
return {"fgColor": self.color_to_excel(fill_color), "patternType": "solid"}
310310

311311
def build_number_format(self, props: Mapping[str, str]) -> dict[str, str | None]:
312-
return {"format_code": props.get("number-format")}
312+
fc = props.get("number-format")
313+
fc = fc.replace("§", ";") if isinstance(fc, str) else fc
314+
return {"format_code": fc}
313315

314316
def build_font(
315317
self, props: Mapping[str, str]

pandas/io/formats/style_render.py

+34
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,10 @@ def format(
891891
-------
892892
self : Styler
893893
894+
See Also
895+
--------
896+
Styler.format_index: Format the text display value of index labels.
897+
894898
Notes
895899
-----
896900
This method assigns a formatting function, ``formatter``, to each cell in the
@@ -926,6 +930,12 @@ def format(
926930
- ``styler.format.thousands``: default None.
927931
- ``styler.format.escape``: default None.
928932
933+
.. warning::
934+
`Styler.format` is ignored when using the output format `Styler.to_excel`,
935+
since Excel and Python have inherrently different formatting structures.
936+
However, it is possible to use the `number-format` pseudo CSS attribute
937+
to force Excel permissible formatting. See examples.
938+
929939
Examples
930940
--------
931941
Using ``na_rep`` and ``precision`` with the default ``formatter``
@@ -993,6 +1003,19 @@ def format(
9931003
1 & \textbf{\textasciitilde \space \textasciicircum } \\
9941004
2 & \textbf{\$\%\#} \\
9951005
\end{tabular}
1006+
1007+
Pandas defines a `number-format` pseudo CSS attribute instead of the `.format`
1008+
method to create `to_excel` permissible formatting. Note that semi-colons are
1009+
CSS protected characters but used as separators in Excel's format string.
1010+
Replace semi-colons with the section separator character (ASCII-245) when
1011+
defining the formatting here.
1012+
1013+
>>> df = pd.DataFrame({"A": [1, 0, -1]})
1014+
>>> pseudo_css = "number-format: 0§[Red](0)§-§@;"
1015+
>>> df.style.applymap(lambda v: css).to_excel("formatted_file.xlsx")
1016+
... # doctest: +SKIP
1017+
1018+
.. figure:: ../../_static/style/format_excel_css.png
9961019
"""
9971020
if all(
9981021
(
@@ -1084,6 +1107,10 @@ def format_index(
10841107
-------
10851108
self : Styler
10861109
1110+
See Also
1111+
--------
1112+
Styler.format: Format the text display value of data cells.
1113+
10871114
Notes
10881115
-----
10891116
This method assigns a formatting function, ``formatter``, to each level label
@@ -1110,6 +1137,13 @@ def format_index(
11101137
When using a ``formatter`` string the dtypes must be compatible, otherwise a
11111138
`ValueError` will be raised.
11121139
1140+
.. warning::
1141+
`Styler.format_index` is ignored when using the output format
1142+
`Styler.to_excel`, since Excel and Python have inherrently different
1143+
formatting structures.
1144+
However, it is possible to use the `number-format` pseudo CSS attribute
1145+
to force Excel permissible formatting. See documentation for `Styler.format`.
1146+
11131147
Examples
11141148
--------
11151149
Using ``na_rep`` and ``precision`` with the default ``formatter``

pandas/tests/io/formats/test_to_excel.py

+4
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@
206206
("white-space: normal", {"alignment": {"wrap_text": True}}),
207207
# NUMBER FORMAT
208208
("number-format: 0%", {"number_format": {"format_code": "0%"}}),
209+
(
210+
"number-format: 0§[Red](0)§-§@;",
211+
{"number_format": {"format_code": "0;[red](0);-;@"}}, # GH 46152
212+
),
209213
],
210214
)
211215
def test_css_to_excel(css, expected):

0 commit comments

Comments
 (0)