Skip to content

Commit 3e48393

Browse files
marknsikoraTomAugspurger
authored andcommitted
ENH: Number formatting support for excel styles (#22015)
* ENH: Number formatting support for excel styles
1 parent a439472 commit 3e48393

File tree

5 files changed

+21
-7
lines changed

5 files changed

+21
-7
lines changed

doc/source/style.ipynb

+4-1
Original file line numberDiff line numberDiff line change
@@ -985,7 +985,10 @@
985985
"- `vertical-align`\n",
986986
"- `white-space: nowrap`\n",
987987
"\n",
988-
"Only CSS2 named colors and hex colors of the form `#rgb` or `#rrggbb` are currently supported."
988+
"Only CSS2 named colors and hex colors of the form `#rgb` or `#rrggbb` are currently supported.\n",
989+
"\n",
990+
"The following pseudo CSS properties are also available to set excel specific style properties:\n",
991+
"- `number-format`\n"
989992
]
990993
},
991994
{

doc/source/whatsnew/v0.24.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ Other API Changes
372372
- Trying to reindex a ``DataFrame`` with a non unique ``MultiIndex`` now raises a ``ValueError`` instead of an ``Exception`` (:issue:`21770`)
373373
- :meth:`PeriodIndex.tz_convert` and :meth:`PeriodIndex.tz_localize` have been removed (:issue:`21781`)
374374
- :class:`Index` subtraction will attempt to operate element-wise instead of raising ``TypeError`` (:issue:`19369`)
375+
- :class:`pandas.io.formats.style.Styler` supports a ``number-format`` property when using :meth:`~pandas.io.formats.style.Styler.to_excel` (:issue:`22015`)
375376

376377
.. _whatsnew_0240.deprecations:
377378

pandas/io/formats/excel.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ def build_xlstyle(self, props):
9898
'border': self.build_border(props),
9999
'fill': self.build_fill(props),
100100
'font': self.build_font(props),
101+
'number_format': self.build_number_format(props),
101102
}
102-
# TODO: support number format
103103
# TODO: handle cell width and height: needs support in pandas.io.excel
104104

105105
def remove_none(d):
@@ -314,6 +314,9 @@ def color_to_excel(self, val):
314314
warnings.warn('Unhandled color format: {val!r}'.format(val=val),
315315
CSSWarning)
316316

317+
def build_number_format(self, props):
318+
return {'format_code': props.get('number-format')}
319+
317320

318321
class ExcelFormatter(object):
319322
"""

pandas/tests/io/formats/test_to_excel.py

+3
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@
172172
{'alignment': {'wrap_text': False}}),
173173
('white-space: normal',
174174
{'alignment': {'wrap_text': True}}),
175+
# NUMBER FORMAT
176+
('number-format: 0%',
177+
{'number_format': {'format_code': '0%'}}),
175178
])
176179
def test_css_to_excel(css, expected):
177180
convert = CSSToExcelConverter()

pandas/tests/io/test_excel.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -2241,6 +2241,7 @@ def style(df):
22412241
['', 'font-style: italic', ''],
22422242
['', '', 'text-align: right'],
22432243
['background-color: red', '', ''],
2244+
['number-format: 0%', '', ''],
22442245
['', '', ''],
22452246
['', '', ''],
22462247
['', '', '']],
@@ -2266,7 +2267,7 @@ def custom_converter(css):
22662267

22672268
# Prepare spreadsheets
22682269

2269-
df = DataFrame(np.random.randn(10, 3))
2270+
df = DataFrame(np.random.randn(11, 3))
22702271
with ensure_clean('.xlsx' if engine != 'xlwt' else '.xls') as path:
22712272
writer = ExcelWriter(path, engine=engine)
22722273
df.to_excel(writer, sheet_name='frame')
@@ -2294,7 +2295,7 @@ def custom_converter(css):
22942295
n_cells += 1
22952296

22962297
# ensure iteration actually happened:
2297-
assert n_cells == (10 + 1) * (3 + 1)
2298+
assert n_cells == (11 + 1) * (3 + 1)
22982299

22992300
# (2) check styling with default converter
23002301

@@ -2344,13 +2345,16 @@ def custom_converter(css):
23442345
assert cell1.fill.patternType != cell2.fill.patternType
23452346
assert cell2.fill.fgColor.rgb == alpha + 'FF0000'
23462347
assert cell2.fill.patternType == 'solid'
2348+
elif ref == 'B9':
2349+
assert cell1.number_format == 'General'
2350+
assert cell2.number_format == '0%'
23472351
else:
23482352
assert_equal_style(cell1, cell2)
23492353

23502354
assert cell1.value == cell2.value
23512355
n_cells += 1
23522356

2353-
assert n_cells == (10 + 1) * (3 + 1)
2357+
assert n_cells == (11 + 1) * (3 + 1)
23542358

23552359
# (3) check styling with custom converter
23562360
n_cells = 0
@@ -2359,7 +2363,7 @@ def custom_converter(css):
23592363
assert len(col1) == len(col2)
23602364
for cell1, cell2 in zip(col1, col2):
23612365
ref = '%s%d' % (cell2.column, cell2.row)
2362-
if ref in ('B2', 'C3', 'D4', 'B5', 'C6', 'D7', 'B8'):
2366+
if ref in ('B2', 'C3', 'D4', 'B5', 'C6', 'D7', 'B8', 'B9'):
23632367
assert not cell1.font.bold
23642368
assert cell2.font.bold
23652369
else:
@@ -2368,7 +2372,7 @@ def custom_converter(css):
23682372
assert cell1.value == cell2.value
23692373
n_cells += 1
23702374

2371-
assert n_cells == (10 + 1) * (3 + 1)
2375+
assert n_cells == (11 + 1) * (3 + 1)
23722376

23732377

23742378
@td.skip_if_no('openpyxl')

0 commit comments

Comments
 (0)