-
-
Notifications
You must be signed in to change notification settings - Fork 18k
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: support Styler in ExcelFormatter #15530
Changes from all commits
ada5101
f1cde08
96680f9
8780076
0ce72f9
cb5cf02
c589c35
176e51c
d103f61
7db59c0
dc953d4
f62f02d
3b26087
eb02cc1
1984cab
9a5b791
1a8818f
f17a0f4
efce9b6
350eab5
306eebe
a1127f6
a43d6b7
c1fc232
8e9a567
7c54a69
9a62699
433be03
096f26c
b1d774b
2c3d015
c4f59c6
79eae41
6d3ffc6
6ff8a46
61fdc69
d144fdf
4e72993
60d6a3b
0256fc6
d5db0ac
e2cfa77
ceb9171
3071bac
14035c5
9669d7d
6168765
6465913
934df06
a5d51f9
de53808
836f39e
c7a51ca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,11 +4,11 @@ | |
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"# HTML Styling\n", | ||
"# Styling\n", | ||
"\n", | ||
"*New in version 0.17.1*\n", | ||
"\n", | ||
"<p style=\"color: red\">*Provisional: This is a new feature and still under development. We'll be adding features and possibly making breaking changes in future releases. We'd love to hear your feedback.*<p style=\"color: red\">\n", | ||
"<span style=\"color: red\">*Provisional: This is a new feature and still under development. We'll be adding features and possibly making breaking changes in future releases. We'd love to hear your feedback.*</span>\n", | ||
"\n", | ||
"This document is written as a Jupyter Notebook, and can be viewed or downloaded [here](http://nbviewer.ipython.org/github/pandas-dev/pandas/blob/master/doc/source/html-styling.ipynb).\n", | ||
"\n", | ||
|
@@ -49,7 +49,6 @@ | |
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": { | ||
"collapsed": true, | ||
"nbsphinx": "hidden" | ||
}, | ||
"outputs": [], | ||
|
@@ -62,9 +61,7 @@ | |
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": { | ||
"collapsed": true | ||
}, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"import pandas as pd\n", | ||
|
@@ -130,9 +127,7 @@ | |
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": { | ||
"collapsed": true | ||
}, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"def color_negative_red(val):\n", | ||
|
@@ -186,9 +181,7 @@ | |
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": { | ||
"collapsed": true | ||
}, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"def highlight_max(s):\n", | ||
|
@@ -240,7 +233,7 @@ | |
"source": [ | ||
"Above we used `Styler.apply` to pass in each column one at a time.\n", | ||
"\n", | ||
"<p style=\"background-color: #DEDEBE\">*Debugging Tip*: If you're having trouble writing your style function, try just passing it into <code style=\"background-color: #DEDEBE\">DataFrame.apply</code>. Internally, <code style=\"background-color: #DEDEBE\">Styler.apply</code> uses <code style=\"background-color: #DEDEBE\">DataFrame.apply</code> so the result should be the same.</p>\n", | ||
"<span style=\"background-color: #DEDEBE\">*Debugging Tip*: If you're having trouble writing your style function, try just passing it into <code style=\"background-color: #DEDEBE\">DataFrame.apply</code>. Internally, <code style=\"background-color: #DEDEBE\">Styler.apply</code> uses <code style=\"background-color: #DEDEBE\">DataFrame.apply</code> so the result should be the same.</span>\n", | ||
"\n", | ||
"What if you wanted to highlight just the maximum value in the entire table?\n", | ||
"Use `.apply(function, axis=None)` to indicate that your function wants the entire table, not one column or row at a time. Let's try that next.\n", | ||
|
@@ -251,9 +244,7 @@ | |
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": { | ||
"collapsed": true | ||
}, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"def highlight_max(data, color='yellow'):\n", | ||
|
@@ -819,9 +810,7 @@ | |
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": { | ||
"collapsed": true | ||
}, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"def magnify():\n", | ||
|
@@ -854,6 +843,53 @@ | |
" .set_table_styles(magnify())" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## Export to Excel\n", | ||
"\n", | ||
"*New in version 0.20.0*\n", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you might want to add an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's one right below, isn't there? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's one right below, isn't there? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh i c, ok then. |
||
"\n", | ||
"<span style=\"color: red\">*Experimental: This is a new feature and still under development. We'll be adding features and possibly making breaking changes in future releases. We'd love to hear your feedback.*</span>\n", | ||
"\n", | ||
"Some support is available for exporting styled `DataFrames` to Excel worksheets using the `OpenPyXL` engine. CSS2.2 properties handled include:\n", | ||
"\n", | ||
"- `background-color`\n", | ||
"- `border-style`, `border-width`, `border-color` and their {`top`, `right`, `bottom`, `left` variants}\n", | ||
"- `color`\n", | ||
"- `font-family`\n", | ||
"- `font-style`\n", | ||
"- `font-weight`\n", | ||
"- `text-align`\n", | ||
"- `text-decoration`\n", | ||
"- `vertical-align`\n", | ||
"- `white-space: nowrap`\n", | ||
"\n", | ||
"Only CSS2 named colors and hex colors of the form `#rgb` or `#rrggbb` are currently supported." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"df.style.\\\n", | ||
" applymap(color_negative_red).\\\n", | ||
" apply(highlight_max).\\\n", | ||
" to_excel('styled.xlsx', engine='openpyxl')" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"A screenshot of the output:\n", | ||
"\n", | ||
"![Excel spreadsheet with styled DataFrame](_static/style-excel.png)\n" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
|
@@ -1039,8 +1075,7 @@ | |
"mimetype": "text/x-python", | ||
"name": "python", | ||
"nbconvert_exporter": "python", | ||
"pygments_lexer": "ipython3", | ||
"version": "3.6.1" | ||
"pygments_lexer": "ipython3" | ||
} | ||
}, | ||
"nbformat": 4, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ Highlights include: | |
- Improved user API when accessing levels in ``.groupby()``, see :ref:`here <whatsnew_0200.enhancements.groupby_access>` | ||
- Improved support for ``UInt64`` dtypes, see :ref:`here <whatsnew_0200.enhancements.uint64_support>` | ||
- A new orient for JSON serialization, ``orient='table'``, that uses the :ref:`Table Schema spec <whatsnew_0200.enhancements.table_schema>` | ||
- Experimental support for exporting ``DataFrame.style`` formats to Excel , see :ref:`here <whatsnew_0200.enhancements.style_excel>` | ||
- Window Binary Corr/Cov operations now return a MultiIndexed ``DataFrame`` rather than a ``Panel``, as ``Panel`` is now deprecated, see :ref:`here <whatsnew_0200.api_breaking.rolling_pairwise>` | ||
- Support for S3 handling now uses ``s3fs``, see :ref:`here <whatsnew_0200.api_breaking.s3>` | ||
- Google BigQuery support now uses the ``pandas-gbq`` library, see :ref:`here <whatsnew_0200.api_breaking.gbq>` | ||
|
@@ -398,6 +399,39 @@ To convert a ``SparseDataFrame`` back to sparse SciPy matrix in COO format, you | |
|
||
sdf.to_coo() | ||
|
||
.. _whatsnew_0200.enhancements.style_excel: | ||
|
||
Excel output for styled DataFrames | ||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
Experimental support has been added to export ``DataFrame.style`` formats to Excel using the ``openpyxl`` engine. (:issue:`15530`) | ||
|
||
For example, after running the following, ``styled.xlsx`` renders as below: | ||
|
||
.. ipython:: python | ||
|
||
np.random.seed(24) | ||
df = pd.DataFrame({'A': np.linspace(1, 10, 10)}) | ||
df = pd.concat([df, pd.DataFrame(np.random.RandomState(24).randn(10, 4), | ||
columns=list('BCDE'))], | ||
axis=1) | ||
df.iloc[0, 2] = np.nan | ||
df | ||
styled = df.style.\ | ||
applymap(lambda val: 'color: %s' % 'red' if val < 0 else 'black').\ | ||
apply(lambda s: ['background-color: yellow' if v else '' | ||
for v in s == s.max()]) | ||
styled.to_excel('styled.xlsx', engine='openpyxl') | ||
|
||
.. image:: _static/style-excel.png | ||
|
||
.. ipython:: python | ||
:suppress: | ||
import os | ||
os.remove('styled.xlsx') | ||
|
||
See the :ref:`Style documentation <style>` for more detail. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This ref isn't working. @TomAugspurger, I assume this is related to the nbsphinx changes. There is another ref to |
||
|
||
.. _whatsnew_0200.enhancements.intervalindex: | ||
|
||
IntervalIndex | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1419,28 +1419,17 @@ def to_excel(self, excel_writer, sheet_name='Sheet1', na_rep='', | |
index_label=None, startrow=0, startcol=0, engine=None, | ||
merge_cells=True, encoding=None, inf_rep='inf', verbose=True, | ||
freeze_panes=None): | ||
from pandas.io.excel import ExcelWriter | ||
need_save = False | ||
if encoding is None: | ||
encoding = 'ascii' | ||
|
||
if isinstance(excel_writer, compat.string_types): | ||
excel_writer = ExcelWriter(excel_writer, engine=engine) | ||
need_save = True | ||
|
||
formatter = fmt.ExcelFormatter(self, na_rep=na_rep, cols=columns, | ||
header=header, | ||
float_format=float_format, index=index, | ||
index_label=index_label, | ||
merge_cells=merge_cells, | ||
inf_rep=inf_rep) | ||
|
||
formatted_cells = formatter.get_formatted_cells() | ||
excel_writer.write_cells(formatted_cells, sheet_name, | ||
startrow=startrow, startcol=startcol, | ||
freeze_panes=freeze_panes) | ||
if need_save: | ||
excel_writer.save() | ||
|
||
from pandas.io.formats.excel import ExcelFormatter | ||
formatter = ExcelFormatter(self, na_rep=na_rep, cols=columns, | ||
header=header, | ||
float_format=float_format, index=index, | ||
index_label=index_label, | ||
merge_cells=merge_cells, | ||
inf_rep=inf_rep) | ||
formatter.write(excel_writer, sheet_name=sheet_name, startrow=startrow, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes this is nice |
||
startcol=startcol, freeze_panes=freeze_panes, | ||
engine=engine) | ||
|
||
def to_stata(self, fname, convert_dates=None, write_index=True, | ||
encoding="latin-1", byteorder=None, time_stamp=None, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# -*- coding: utf-8 -*- | ||
""" | ||
Common helper methods used in different submodules of pandas.io.formats | ||
""" | ||
|
||
|
||
def get_level_lengths(levels, sentinel=''): | ||
"""For each index in each level the function returns lengths of indexes. | ||
|
||
Parameters | ||
---------- | ||
levels : list of lists | ||
List of values on for level. | ||
sentinel : string, optional | ||
Value which states that no new index starts on there. | ||
|
||
Returns | ||
---------- | ||
Returns list of maps. For each level returns map of indexes (key is index | ||
in row and value is length of index). | ||
""" | ||
if len(levels) == 0: | ||
return [] | ||
|
||
control = [True for x in levels[0]] | ||
|
||
result = [] | ||
for level in levels: | ||
last_index = 0 | ||
|
||
lengths = {} | ||
for i, key in enumerate(level): | ||
if control[i] and key == sentinel: | ||
pass | ||
else: | ||
control[i] = False | ||
lengths[last_index] = i - last_index | ||
last_index = i | ||
|
||
lengths[last_index] = len(level) - last_index | ||
|
||
result.append(lengths) | ||
|
||
return result |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry about these. I can try to fix up later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a big deal, I think I have another PR in the works that does the same thing.