Skip to content

Commit 76d3759

Browse files
author
Justine Kosinski
committed
fix: fix with wrong formatting
1 parent 72ba35b commit 76d3759

File tree

1 file changed

+61
-29
lines changed

1 file changed

+61
-29
lines changed

pandas/io/formats/excel.py

Lines changed: 61 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Utilities for conversion to writer-agnostic Excel representation.
33
"""
44

5-
from __future__ import annotations
5+
from _future_ import annotations
66

77
from collections.abc import (
88
Callable,
@@ -11,6 +11,7 @@
1111
Mapping,
1212
Sequence,
1313
)
14+
import pandas as pd
1415
import functools
1516
import itertools
1617
import re
@@ -63,10 +64,10 @@
6364

6465

6566
class ExcelCell:
66-
__fields__ = ("row", "col", "val", "style", "mergestart", "mergeend")
67-
__slots__ = __fields__
67+
_fields_ = ("row", "col", "val", "style", "mergestart", "mergeend")
68+
_slots_ = _fields_
6869

69-
def __init__(
70+
def _init_(
7071
self,
7172
row: int,
7273
col: int,
@@ -84,7 +85,7 @@ def __init__(
8485

8586

8687
class CssExcelCell(ExcelCell):
87-
def __init__(
88+
def _init_(
8889
self,
8990
row: int,
9091
col: int,
@@ -105,7 +106,7 @@ def __init__(
105106
unique_declarations = frozenset(declaration_dict.items())
106107
style = css_converter(unique_declarations)
107108

108-
super().__init__(row=row, col=col, val=val, style=style, **kwargs)
109+
super()._init_(row=row, col=col, val=val, style=style, **kwargs)
109110

110111

111112
class CSSToExcelConverter:
@@ -116,14 +117,14 @@ class CSSToExcelConverter:
116117
focusing on font styling, backgrounds, borders and alignment.
117118
118119
Operates by first computing CSS styles in a fairly generic
119-
way (see :meth:`compute_css`) then determining Excel style
120-
properties from CSS properties (see :meth:`build_xlstyle`).
120+
way (see :meth:⁠ compute_css ⁠) then determining Excel style
121+
properties from CSS properties (see :meth:⁠ build_xlstyle ⁠).
121122
122123
Parameters
123124
----------
124125
inherited : str, optional
125126
CSS declarations understood to be the containing scope for the
126-
CSS processed by :meth:`__call__`.
127+
CSS processed by :meth:⁠ __call__ ⁠.
127128
"""
128129

129130
NAMED_COLORS = CSS4_COLORS
@@ -183,25 +184,25 @@ class CSSToExcelConverter:
183184
]
184185
}
185186

186-
# NB: Most of the methods here could be classmethods, as only __init__
187-
# and __call__ make use of instance attributes. We leave them as
187+
# NB: Most of the methods here could be classmethods, as only _init_
188+
# and _call_ make use of instance attributes. We leave them as
188189
# instancemethods so that users can easily experiment with extensions
189190
# without monkey-patching.
190191
inherited: dict[str, str] | None
191192

192-
def __init__(self, inherited: str | None = None) -> None:
193+
def _init_(self, inherited: str | None = None) -> None:
193194
if inherited is not None:
194195
self.inherited = self.compute_css(inherited)
195196
else:
196197
self.inherited = None
197-
# We should avoid cache on the __call__ method.
198-
# Otherwise once the method __call__ has been called
198+
# We should avoid cache on the _call_ method.
199+
# Otherwise once the method _call_ has been called
199200
# garbage collection no longer deletes the instance.
200201
self._call_cached = functools.cache(self._call_uncached)
201202

202203
compute_css = CSSResolver()
203204

204-
def __call__(
205+
def _call_(
205206
self, declarations: str | frozenset[tuple[str, str]]
206207
) -> dict[str, dict[str, str]]:
207208
"""
@@ -517,27 +518,27 @@ class ExcelFormatter:
517518
output row names (index)
518519
index_label : str or sequence, default None
519520
Column label for index column(s) if desired. If None is given, and
520-
`header` and `index` are True, then the index names are used. A
521+
⁠ header ⁠ and ⁠ index ⁠ are True, then the index names are used. A
521522
sequence should be given if the DataFrame uses MultiIndex.
522523
merge_cells : bool or 'columns', default False
523524
Format MultiIndex column headers and Hierarchical Rows as merged cells
524525
if True. Merge MultiIndex column headers only if 'columns'.
525526
.. versionchanged:: 3.0.0
526527
Added the 'columns' option.
527-
inf_rep : str, default `'inf'`
528+
inf_rep : str, default ⁠ 'inf' ⁠
528529
representation for np.inf values (which aren't representable in Excel)
529-
A `'-'` sign will be added in front of -inf.
530+
A ⁠ '-' ⁠ sign will be added in front of -inf.
530531
style_converter : callable, optional
531532
This translates Styler styles (CSS) into ExcelWriter styles.
532-
Defaults to ``CSSToExcelConverter()``.
533+
Defaults to `⁠ CSSToExcelConverter() ⁠`.
533534
It should have signature css_declarations string -> excel style.
534535
This is only called for body cells.
535536
"""
536537

537538
max_rows = 2**20
538539
max_cols = 2**14
539540

540-
def __init__(
541+
def _init_(
541542
self,
542543
df,
543544
na_rep: str = "",
@@ -594,7 +595,11 @@ def _format_value(self, val):
594595
elif missing.isneginf_scalar(val):
595596
val = f"-{self.inf_rep}"
596597
elif self.float_format is not None:
597-
val = float(self.float_format % val)
598+
val = self.float_format % val
599+
else:
600+
# respecter l'affichage par défaut de pandas (console)
601+
val = repr(val)
602+
598603
if getattr(val, "tzinfo", None) is not None:
599604
raise ValueError(
600605
"Excel does not support datetimes with "
@@ -616,7 +621,20 @@ def _format_header_mi(self) -> Iterable[ExcelCell]:
616621

617622
columns = self.columns
618623
merge_columns = self.merge_cells in {True, "columns"}
619-
level_strs = columns._format_multi(sparsify=merge_columns, include_names=False)
624+
625+
# Replace NaN column header values with a non-breaking space so
626+
# Excel output matches console display (see user's _fix_headers).
627+
NBSP = "\u00A0"
628+
if isinstance(columns, MultiIndex):
629+
fixed_levels = []
630+
for lvl in range(columns.nlevels):
631+
vals = columns.get_level_values(lvl)
632+
fixed_levels.append([NBSP if pd.isna(v) else str(v) for v in vals])
633+
fixed_columns = MultiIndex.from_arrays(fixed_levels, names=columns.names)
634+
else:
635+
fixed_columns = Index([NBSP if pd.isna(v) else str(v) for v in columns], name=columns.name)
636+
637+
level_strs = fixed_columns._format_multi(sparsify=merge_columns, include_names=False)
620638
level_lengths = get_level_lengths(level_strs)
621639
coloffset = 0
622640
lnum = 0
@@ -625,17 +643,24 @@ def _format_header_mi(self) -> Iterable[ExcelCell]:
625643
coloffset = self.df.index.nlevels - 1
626644

627645
for lnum, name in enumerate(columns.names):
646+
val = NBSP if pd.isna(name) else str(name)
628647
yield ExcelCell(
629648
row=lnum,
630649
col=coloffset,
631-
val=name,
650+
val=val,
632651
style=None,
633652
)
634653

635-
for lnum, (spans, levels, level_codes) in enumerate(
636-
zip(level_lengths, columns.levels, columns.codes)
654+
655+
656+
# Iterate the fixed_columns levels/codes so values already have
657+
# NaNs replaced by NBSP (and are strings).
658+
for lnum, (spans, level, codes) in enumerate(
659+
zip(level_lengths, fixed_columns.levels, fixed_columns.codes)
637660
):
638-
values = levels.take(level_codes)
661+
# level.take(codes) on fixed_columns.levels yields string values
662+
values = level.take(codes).to_numpy()
663+
639664
for i, span_val in spans.items():
640665
mergestart, mergeend = None, None
641666
if merge_columns and span_val > 1:
@@ -652,6 +677,7 @@ def _format_header_mi(self) -> Iterable[ExcelCell]:
652677
mergestart=mergestart,
653678
mergeend=mergeend,
654679
)
680+
655681
self.rowcounter = lnum
656682

657683
def _format_header_regular(self) -> Iterable[ExcelCell]:
@@ -673,6 +699,12 @@ def _format_header_regular(self) -> Iterable[ExcelCell]:
673699
)
674700
colnames = self.header
675701

702+
# Normalize NaN column labels to a non-breaking space so Excel
703+
# header output matches console display (same behavior as
704+
# applied to MultiIndex headers in _format_header_mi).
705+
NBSP = "\u00A0"
706+
colnames = [NBSP if pd.isna(v) else str(v) for v in colnames]
707+
676708
for colindex, colname in enumerate(colnames):
677709
yield CssExcelCell(
678710
row=self.rowcounter,
@@ -896,8 +928,8 @@ def write(
896928
is to be frozen
897929
engine : string, default None
898930
write engine to use if writer is a path - you can also set this
899-
via the options ``io.excel.xlsx.writer``,
900-
or ``io.excel.xlsm.writer``.
931+
via the options `⁠ io.excel.xlsx.writer ⁠`,
932+
or `⁠ io.excel.xlsm.writer ⁠`.
901933
902934
{storage_options}
903935
@@ -939,4 +971,4 @@ def write(
939971
finally:
940972
# make sure to close opened file handles
941973
if need_save:
942-
writer.close()
974+
writer.close()

0 commit comments

Comments
 (0)