Skip to content

Commit 4274b11

Browse files
authored
PERF: to speed up rendering of styler (#34863)
1 parent c8d85e2 commit 4274b11

File tree

5 files changed

+52
-15
lines changed

5 files changed

+52
-15
lines changed

asv_bench/asv.conf.json

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"xlwt": [],
5454
"odfpy": [],
5555
"pytest": [],
56+
"jinja2": [],
5657
// If using Windows with python 2.7 and want to build using the
5758
// mingw toolchain (rather than MSVC), uncomment the following line.
5859
// "libpython": [],

asv_bench/benchmarks/io/style.py

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import numpy as np
2+
3+
from pandas import DataFrame
4+
5+
6+
class RenderApply:
7+
8+
params = [[12, 24, 36], [12, 120]]
9+
param_names = ["cols", "rows"]
10+
11+
def setup(self, cols, rows):
12+
self.df = DataFrame(
13+
np.random.randn(rows, cols),
14+
columns=[f"float_{i+1}" for i in range(cols)],
15+
index=[f"row_{i+1}" for i in range(rows)],
16+
)
17+
self._style_apply()
18+
19+
def time_render(self, cols, rows):
20+
self.st.render()
21+
22+
def peakmem_apply(self, cols, rows):
23+
self._style_apply()
24+
25+
def peakmem_render(self, cols, rows):
26+
self.st.render()
27+
28+
def _style_apply(self):
29+
def _apply_func(s):
30+
return [
31+
"background-color: lightcyan" if s.name == "row_1" else "" for v in s
32+
]
33+
34+
self.st = self.df.style.apply(_apply_func, axis=1)

doc/source/whatsnew/v1.1.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,7 @@ Performance improvements
854854
- Performance improvement in :class:`pandas.core.groupby.RollingGroupby` (:issue:`34052`)
855855
- Performance improvement in arithmetic operations (sub, add, mul, div) for MultiIndex (:issue:`34297`)
856856
- Performance improvement in `DataFrame[bool_indexer]` when `bool_indexer` is a list (:issue:`33924`)
857+
- Significant performance improvement of :meth:`io.formats.style.Styler.render` with styles added with various ways such as :meth:`io.formats.style.Styler.apply`, :meth:`io.formats.style.Styler.applymap` or :meth:`io.formats.style.Styler.bar` (:issue:`19917`)
857858

858859
.. ---------------------------------------------------------------------------
859860

pandas/io/formats/style.py

+13-5
Original file line numberDiff line numberDiff line change
@@ -561,11 +561,19 @@ def _update_ctx(self, attrs: DataFrame) -> None:
561561
Whitespace shouldn't matter and the final trailing ';' shouldn't
562562
matter.
563563
"""
564-
for row_label, v in attrs.iterrows():
565-
for col_label, col in v.items():
566-
i = self.index.get_indexer([row_label])[0]
567-
j = self.columns.get_indexer([col_label])[0]
568-
for pair in col.rstrip(";").split(";"):
564+
coli = {k: i for i, k in enumerate(self.columns)}
565+
rowi = {k: i for i, k in enumerate(self.index)}
566+
for jj in range(len(attrs.columns)):
567+
cn = attrs.columns[jj]
568+
j = coli[cn]
569+
for rn, c in attrs[[cn]].itertuples():
570+
if not c:
571+
continue
572+
c = c.rstrip(";")
573+
if not c:
574+
continue
575+
i = rowi[rn]
576+
for pair in c.split(";"):
569577
self.ctx[(i, j)].append(pair)
570578

571579
def _copy(self, deepcopy: bool = False) -> "Styler":

pandas/tests/io/formats/test_style.py

+3-10
Original file line numberDiff line numberDiff line change
@@ -405,9 +405,10 @@ def f(x):
405405

406406
result = self.df.style.where(f, style1)._compute().ctx
407407
expected = {
408-
(r, c): [style1 if f(self.df.loc[row, col]) else ""]
408+
(r, c): [style1]
409409
for r, row in enumerate(self.df.index)
410410
for c, col in enumerate(self.df.columns)
411+
if f(self.df.loc[row, col])
411412
}
412413
assert result == expected
413414

@@ -966,7 +967,6 @@ def test_bar_align_mid_nans(self):
966967
"transparent 25.0%, #d65f5f 25.0%, "
967968
"#d65f5f 50.0%, transparent 50.0%)",
968969
],
969-
(1, 0): [""],
970970
(0, 1): [
971971
"width: 10em",
972972
" height: 80%",
@@ -994,7 +994,6 @@ def test_bar_align_zero_nans(self):
994994
"transparent 50.0%, #d65f5f 50.0%, "
995995
"#d65f5f 75.0%, transparent 75.0%)",
996996
],
997-
(1, 0): [""],
998997
(0, 1): [
999998
"width: 10em",
1000999
" height: 80%",
@@ -1091,7 +1090,7 @@ def test_format_with_bad_na_rep(self):
10911090
def test_highlight_null(self, null_color="red"):
10921091
df = pd.DataFrame({"A": [0, np.nan]})
10931092
result = df.style.highlight_null()._compute().ctx
1094-
expected = {(0, 0): [""], (1, 0): ["background-color: red"]}
1093+
expected = {(1, 0): ["background-color: red"]}
10951094
assert result == expected
10961095

10971096
def test_highlight_null_subset(self):
@@ -1104,9 +1103,7 @@ def test_highlight_null_subset(self):
11041103
.ctx
11051104
)
11061105
expected = {
1107-
(0, 0): [""],
11081106
(1, 0): ["background-color: red"],
1109-
(0, 1): [""],
11101107
(1, 1): ["background-color: green"],
11111108
}
11121109
assert result == expected
@@ -1219,17 +1216,13 @@ def test_highlight_max(self):
12191216
expected = {
12201217
(1, 0): ["background-color: yellow"],
12211218
(1, 1): ["background-color: yellow"],
1222-
(0, 1): [""],
1223-
(0, 0): [""],
12241219
}
12251220
assert result == expected
12261221

12271222
result = getattr(df.style, attr)(axis=1)._compute().ctx
12281223
expected = {
12291224
(0, 1): ["background-color: yellow"],
12301225
(1, 1): ["background-color: yellow"],
1231-
(0, 0): [""],
1232-
(1, 0): [""],
12331226
}
12341227
assert result == expected
12351228

0 commit comments

Comments
 (0)