Skip to content

Commit 7257c7d

Browse files
committed
ENH: add named based column css styling to pandas Styler
1 parent d90b73b commit 7257c7d

File tree

3 files changed

+141
-5
lines changed

3 files changed

+141
-5
lines changed

doc/source/user_guide/style.ipynb

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -793,7 +793,8 @@
793793
"source": [
794794
"The next option you have are \"table styles\".\n",
795795
"These are styles that apply to the table as a whole, but don't look at the data.\n",
796-
"Certain sytlings, including pseudo-selectors like `:hover` can only be used this way."
796+
"Certain stylings, including pseudo-selectors like `:hover` can only be used this way.\n",
797+
"These can also be used to set specific row or column based class selectors, as will be shown."
797798
]
798799
},
799800
{
@@ -831,9 +832,31 @@
831832
"The value for `props` should be a list of tuples of `('attribute', 'value')`.\n",
832833
"\n",
833834
"`table_styles` are extremely flexible, but not as fun to type out by hand.\n",
834-
"We hope to collect some useful ones either in pandas, or preferable in a new package that [builds on top](#Extensibility) the tools here."
835+
"We hope to collect some useful ones either in pandas, or preferable in a new package that [builds on top](#Extensibility) the tools here.\n",
836+
"\n",
837+
"`table_styles` can be used to add column and row based class descriptors. For large tables this can increase performance by avoiding repetitive individual css for each cell, and it can also simplify style construction in some cases.\n",
838+
"`Styler.extend_column_styles` is a specific function to set styles to individual columns.\n",
839+
"\n",
840+
"Note that `Styler.set_table_styles` will overwrite existing styles (including those column based) whilst the preferred `Styler.extend_table_styles` will add to the current items."
835841
]
836842
},
843+
{
844+
"cell_type": "code",
845+
"execution_count": null,
846+
"outputs": [],
847+
"source": [
848+
"html = html.extend_column_styles({\n",
849+
" 'B': [dict(selector='', props=[('color', 'blue')])]\n",
850+
"})\n",
851+
"html"
852+
],
853+
"metadata": {
854+
"collapsed": false,
855+
"pycharm": {
856+
"name": "#%%\n"
857+
}
858+
}
859+
},
837860
{
838861
"cell_type": "markdown",
839862
"metadata": {},
@@ -922,10 +945,12 @@
922945
"- DataFrame only `(use Series.to_frame().style)`\n",
923946
"- The index and columns must be unique\n",
924947
"- No large repr, and performance isn't great; this is intended for summary DataFrames\n",
925-
"- You can only style the *values*, not the index or columns\n",
948+
"- You can only style the *values*, not the index or columns (except with `table_styles` above)\n",
926949
"- You can only apply styles, you can't insert new HTML entities\n",
927950
"\n",
928-
"Some of these will be addressed in the future.\n"
951+
"Some of these will be addressed in the future.\n",
952+
"Performance can suffer when adding styles to each cell in a large DataFrame.\n",
953+
"It is recommended to apply table or column based styles where possible to limit overall HTML length, as well as setting a shorter UUID to avoid unnecessary repeated data transmission. \n"
929954
]
930955
},
931956
{
@@ -1237,8 +1262,17 @@
12371262
"nbconvert_exporter": "python",
12381263
"pygments_lexer": "ipython3",
12391264
"version": "3.7.0"
1265+
},
1266+
"pycharm": {
1267+
"stem_cell": {
1268+
"cell_type": "raw",
1269+
"source": [],
1270+
"metadata": {
1271+
"collapsed": false
1272+
}
1273+
}
12401274
}
12411275
},
12421276
"nbformat": 4,
12431277
"nbformat_minor": 1
1244-
}
1278+
}

pandas/io/formats/style.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,86 @@ def set_table_styles(self, table_styles) -> "Styler":
939939
self.table_styles = table_styles
940940
return self
941941

942+
def extend_table_styles(self, table_styles) -> "Styler":
943+
"""
944+
Extend the existing table styles on a Styler.
945+
946+
These are placed in a ``<style>`` tag before the generated HTML table.
947+
948+
Parameters
949+
----------
950+
table_styles : list
951+
Each individual table_style should be a dictionary with
952+
``selector`` and ``props`` keys. ``selector`` should be a CSS
953+
selector that the style will be applied to (automatically
954+
prefixed by the table's UUID) and ``props`` should be a list of
955+
tuples with ``(attribute, value)``.
956+
957+
Returns
958+
-------
959+
self : Styler
960+
961+
Examples
962+
--------
963+
>>> df = pd.DataFrame(np.random.randn(10, 4))
964+
>>> df.style.set_table_styles(
965+
... [{'selector': 'tr:hover',
966+
... 'props': [('background-color', 'yellow')]}]
967+
... ).extend_table_styles(
968+
... [{'selector': '.col2',
969+
... 'props': [('background-color', 'blue')]}]
970+
...)
971+
"""
972+
if self.table_styles is None:
973+
return self.set_table_styles(table_styles)
974+
self.table_styles.extend(table_styles)
975+
return self
976+
977+
def extend_column_styles(self, column_styles) -> "Styler":
978+
"""
979+
Sets class styles for each column on a Styler.
980+
981+
These are placed in a ``<style>`` tag before the generated HTML table.
982+
983+
Parameters
984+
----------
985+
column_styles : dict
986+
Each key of the dict should be a column header with the value
987+
being a list of individual styles. Each style is a dictionary with
988+
``selector`` and ``props`` keys. ``selector`` should be a CSS
989+
selector that the style will be applied to (automatically
990+
prefixed by the table's UUID) and ``props`` should be a list of
991+
tuples with ``(attribute, value)``.
992+
993+
Returns
994+
-------
995+
self : Styler
996+
997+
Notes
998+
-----
999+
Where the ``selector`` is an empty string or `None` the ``props`` will
1000+
be applied to the column class generically.
1001+
1002+
Examples
1003+
--------
1004+
>>> df = pd.DataFrame(np.random.randn(3, 2), columns=['a', 'b'])
1005+
>>> df.style.extend_column_styles({
1006+
... 'a': [{'selector': '',
1007+
... 'props': [('font-size', '20px')]},
1008+
... {'selector': 'td:hover',
1009+
... 'props': [('color', 'pink')]}],
1010+
... 'b': [{'selector': '',
1011+
... 'props': [('font-size', '10px')]}]
1012+
... })
1013+
"""
1014+
_styles = []
1015+
for col, styles in column_styles.items():
1016+
for s in styles:
1017+
c = str(self.data.columns.get_loc(col))
1018+
_styles.append({'selector': s['selector'] + '.col' + c,
1019+
'props': s['props']})
1020+
return self.extend_table_styles(_styles)
1021+
9421022
def set_na_rep(self, na_rep: str) -> "Styler":
9431023
"""
9441024
Set the missing data representation on a Styler.

pandas/tests/io/formats/test_style.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1691,6 +1691,28 @@ def test_no_cell_ids(self):
16911691
s = styler.render() # render twice to ensure ctx is not updated
16921692
assert s.find('<td class="data row0 col0" >') != -1
16931693

1694+
def test_extending_table_styles(self):
1695+
df = pd.DataFrame(data=[[0, 1], [1, 2]], columns=['A', 'B'])
1696+
styler = df.style.set_table_styles([{
1697+
'selector': '',
1698+
'props': [('background-color', 'yellow')]}]
1699+
).extend_table_styles([{
1700+
'selector': '.col0',
1701+
'props': [('background-color', 'blue')]}]
1702+
)
1703+
assert len(styler.table_styles) == 2
1704+
1705+
def test_column_styling(self):
1706+
df = pd.DataFrame(data=[[0, 1], [1, 2]], columns=['A', 'B'])
1707+
s = Styler(df, uuid='_')
1708+
s = s.extend_column_styles({
1709+
'A': [{
1710+
'selector': '',
1711+
'props': [('color', 'blue')]
1712+
}]
1713+
})
1714+
assert '#T__ .col0 {\n color: blue;\n }' in s.render()
1715+
16941716

16951717
@td.skip_if_no_mpl
16961718
class TestStylerMatplotlibDep:

0 commit comments

Comments
 (0)