Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: Making header_style a property of ExcelFormatter #22758 #22759

Merged
merged 5 commits into from
Sep 20, 2018
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.24.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ Other Enhancements
- :class:`Resampler` now is iterable like :class:`GroupBy` (:issue:`15314`).
- :meth:`Series.resample` and :meth:`DataFrame.resample` have gained the :meth:`Resampler.quantile` (:issue:`15023`).
- :meth:`Index.to_frame` now supports overriding column name(s) (:issue:`22580`).
- :class:`ExcelFormatter` now has a `header_style` property. Was previously in global scope (:issue:`22758`).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might not actually need this since the ExcelFormatter isn't part of the exposed API

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be? If we want to support use-cases like #22758 then it should be. OTOH, I don't think we consider things like HTMLFormatter to be public.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea I can see it both ways but was leaning towards no for now just because I don't want this change to make assumptions about our overall strategy with the formatters.

If we want to I'd suggest a separate change that focuses on exposing and documenting the formatters rather than doing it here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds right to me. (and there's nothing stopping people from using it anyway).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened #22773 to further that discussion; @dannyhyunkim if you can revert the change to whatsnew would be great as this in its current state is more of an internal refactor. Sorry for the back and forth from me on that!


.. _whatsnew_0240.api_breaking:

Expand Down
45 changes: 24 additions & 21 deletions pandas/io/formats/excel.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,6 @@ def __init__(self, row, col, val, style=None, mergestart=None,
self.mergeend = mergeend


header_style = {"font": {"bold": True},
"borders": {"top": "thin",
"right": "thin",
"bottom": "thin",
"left": "thin"},
"alignment": {"horizontal": "center",
"vertical": "top"}}


class CSSToExcelConverter(object):
"""A callable for converting CSS declarations to ExcelWriter styles

Expand Down Expand Up @@ -389,6 +380,16 @@ def __init__(self, df, na_rep='', float_format=None, cols=None,
self.merge_cells = merge_cells
self.inf_rep = inf_rep

@property
def header_style(self):
return {"font": {"bold": True},
"borders": {"top": "thin",
"right": "thin",
"bottom": "thin",
"left": "thin"},
"alignment": {"horizontal": "center",
"vertical": "top"}}

def _format_value(self, val):
if is_scalar(val) and missing.isna(val):
val = self.na_rep
Expand Down Expand Up @@ -427,24 +428,24 @@ def _format_header_mi(self):
# Format multi-index as a merged cells.
for lnum in range(len(level_lengths)):
name = columns.names[lnum]
yield ExcelCell(lnum, coloffset, name, header_style)
yield ExcelCell(lnum, coloffset, name, self.header_style)

for lnum, (spans, levels, labels) in enumerate(zip(
level_lengths, columns.levels, columns.labels)):
values = levels.take(labels)
for i in spans:
if spans[i] > 1:
yield ExcelCell(lnum, coloffset + i + 1, values[i],
header_style, lnum,
self.header_style, lnum,
coloffset + i + spans[i])
else:
yield ExcelCell(lnum, coloffset + i + 1, values[i],
header_style)
self.header_style)
else:
# Format in legacy format with dots to indicate levels.
for i, values in enumerate(zip(*level_strs)):
v = ".".join(map(pprint_thing, values))
yield ExcelCell(lnum, coloffset + i + 1, v, header_style)
yield ExcelCell(lnum, coloffset + i + 1, v, self.header_style)

self.rowcounter = lnum

Expand All @@ -469,7 +470,7 @@ def _format_header_regular(self):

for colindex, colname in enumerate(colnames):
yield ExcelCell(self.rowcounter, colindex + coloffset, colname,
header_style)
self.header_style)

def _format_header(self):
if isinstance(self.columns, ABCMultiIndex):
Expand All @@ -482,7 +483,8 @@ def _format_header(self):
row = [x if x is not None else ''
for x in self.df.index.names] + [''] * len(self.columns)
if reduce(lambda x, y: x and y, map(lambda x: x != '', row)):
gen2 = (ExcelCell(self.rowcounter, colindex, val, header_style)
gen2 = (ExcelCell(self.rowcounter, colindex, val,
self.header_style)
for colindex, val in enumerate(row))
self.rowcounter += 1
return itertools.chain(gen, gen2)
Expand Down Expand Up @@ -518,15 +520,16 @@ def _format_regular_rows(self):

if index_label and self.header is not False:
yield ExcelCell(self.rowcounter - 1, 0, index_label,
header_style)
self.header_style)

# write index_values
index_values = self.df.index
if isinstance(self.df.index, ABCPeriodIndex):
index_values = self.df.index.to_timestamp()

for idx, idxval in enumerate(index_values):
yield ExcelCell(self.rowcounter + idx, 0, idxval, header_style)
yield ExcelCell(self.rowcounter + idx, 0, idxval,
self.header_style)

coloffset = 1
else:
Expand Down Expand Up @@ -562,7 +565,7 @@ def _format_hierarchical_rows(self):

for cidx, name in enumerate(index_labels):
yield ExcelCell(self.rowcounter - 1, cidx, name,
header_style)
self.header_style)

if self.merge_cells:
# Format hierarchical rows as merged cells.
Expand All @@ -581,20 +584,20 @@ def _format_hierarchical_rows(self):
for i in spans:
if spans[i] > 1:
yield ExcelCell(self.rowcounter + i, gcolidx,
values[i], header_style,
values[i], self.header_style,
self.rowcounter + i + spans[i] - 1,
gcolidx)
else:
yield ExcelCell(self.rowcounter + i, gcolidx,
values[i], header_style)
values[i], self.header_style)
gcolidx += 1

else:
# Format hierarchical rows with non-merged values.
for indexcolvals in zip(*self.df.index):
for idx, indexcolval in enumerate(indexcolvals):
yield ExcelCell(self.rowcounter + idx, gcolidx,
indexcolval, header_style)
indexcolval, self.header_style)
gcolidx += 1

for cell in self._generate_body(gcolidx):
Expand Down