projektAI/venv/Lib/site-packages/pandas/tests/io/formats/test_style.py
2021-06-06 22:13:05 +02:00

1909 lines
66 KiB
Python

import copy
import re
import textwrap
import numpy as np
import pytest
import pandas.util._test_decorators as td
import pandas as pd
from pandas import DataFrame
import pandas._testing as tm
jinja2 = pytest.importorskip("jinja2")
from pandas.io.formats.style import Styler, _get_level_lengths # isort:skip
class TestStyler:
def setup_method(self, method):
np.random.seed(24)
self.s = DataFrame({"A": np.random.permutation(range(6))})
self.df = DataFrame({"A": [0, 1], "B": np.random.randn(2)})
self.f = lambda x: x
self.g = lambda x: x
def h(x, foo="bar"):
return pd.Series(f"color: {foo}", index=x.index, name=x.name)
self.h = h
self.styler = Styler(self.df)
self.attrs = DataFrame({"A": ["color: red", "color: blue"]})
self.dataframes = [
self.df,
DataFrame(
{"f": [1.0, 2.0], "o": ["a", "b"], "c": pd.Categorical(["a", "b"])}
),
]
def test_init_non_pandas(self):
msg = "``data`` must be a Series or DataFrame"
with pytest.raises(TypeError, match=msg):
Styler([1, 2, 3])
def test_init_series(self):
result = Styler(pd.Series([1, 2]))
assert result.data.ndim == 2
def test_repr_html_ok(self):
self.styler._repr_html_()
def test_repr_html_mathjax(self):
# gh-19824
assert "tex2jax_ignore" not in self.styler._repr_html_()
with pd.option_context("display.html.use_mathjax", False):
assert "tex2jax_ignore" in self.styler._repr_html_()
def test_update_ctx(self):
self.styler._update_ctx(self.attrs)
expected = {(0, 0): ["color: red"], (1, 0): ["color: blue"]}
assert self.styler.ctx == expected
def test_update_ctx_flatten_multi(self):
attrs = DataFrame({"A": ["color: red; foo: bar", "color: blue; foo: baz"]})
self.styler._update_ctx(attrs)
expected = {
(0, 0): ["color: red", " foo: bar"],
(1, 0): ["color: blue", " foo: baz"],
}
assert self.styler.ctx == expected
def test_update_ctx_flatten_multi_traliing_semi(self):
attrs = DataFrame({"A": ["color: red; foo: bar;", "color: blue; foo: baz;"]})
self.styler._update_ctx(attrs)
expected = {
(0, 0): ["color: red", " foo: bar"],
(1, 0): ["color: blue", " foo: baz"],
}
assert self.styler.ctx == expected
def test_copy(self):
s2 = copy.copy(self.styler)
assert self.styler is not s2
assert self.styler.ctx is s2.ctx # shallow
assert self.styler._todo is s2._todo
self.styler._update_ctx(self.attrs)
self.styler.highlight_max()
assert self.styler.ctx == s2.ctx
assert self.styler._todo == s2._todo
def test_deepcopy(self):
s2 = copy.deepcopy(self.styler)
assert self.styler is not s2
assert self.styler.ctx is not s2.ctx
assert self.styler._todo is not s2._todo
self.styler._update_ctx(self.attrs)
self.styler.highlight_max()
assert self.styler.ctx != s2.ctx
assert s2._todo == []
assert self.styler._todo != s2._todo
def test_clear(self):
s = self.df.style.highlight_max()._compute()
assert len(s.ctx) > 0
assert len(s._todo) > 0
s.clear()
assert len(s.ctx) == 0
assert len(s._todo) == 0
def test_render(self):
df = DataFrame({"A": [0, 1]})
style = lambda x: pd.Series(["color: red", "color: blue"], name=x.name)
s = Styler(df, uuid="AB").apply(style)
s.render()
# it worked?
def test_render_empty_dfs(self):
empty_df = DataFrame()
es = Styler(empty_df)
es.render()
# An index but no columns
DataFrame(columns=["a"]).style.render()
# A column but no index
DataFrame(index=["a"]).style.render()
# No IndexError raised?
def test_render_double(self):
df = DataFrame({"A": [0, 1]})
style = lambda x: pd.Series(
["color: red; border: 1px", "color: blue; border: 2px"], name=x.name
)
s = Styler(df, uuid="AB").apply(style)
s.render()
# it worked?
def test_set_properties(self):
df = DataFrame({"A": [0, 1]})
result = df.style.set_properties(color="white", size="10px")._compute().ctx
# order is deterministic
v = ["color: white", "size: 10px"]
expected = {(0, 0): v, (1, 0): v}
assert result.keys() == expected.keys()
for v1, v2 in zip(result.values(), expected.values()):
assert sorted(v1) == sorted(v2)
def test_set_properties_subset(self):
df = DataFrame({"A": [0, 1]})
result = (
df.style.set_properties(subset=pd.IndexSlice[0, "A"], color="white")
._compute()
.ctx
)
expected = {(0, 0): ["color: white"]}
assert result == expected
def test_empty_index_name_doesnt_display(self):
# https://github.com/pandas-dev/pandas/pull/12090#issuecomment-180695902
df = DataFrame({"A": [1, 2], "B": [3, 4], "C": [5, 6]})
result = df.style._translate()
expected = [
[
{
"class": "blank level0",
"type": "th",
"value": "",
"is_visible": True,
"display_value": "",
},
{
"class": "col_heading level0 col0",
"display_value": "A",
"type": "th",
"value": "A",
"is_visible": True,
},
{
"class": "col_heading level0 col1",
"display_value": "B",
"type": "th",
"value": "B",
"is_visible": True,
},
{
"class": "col_heading level0 col2",
"display_value": "C",
"type": "th",
"value": "C",
"is_visible": True,
},
]
]
assert result["head"] == expected
def test_index_name(self):
# https://github.com/pandas-dev/pandas/issues/11655
df = DataFrame({"A": [1, 2], "B": [3, 4], "C": [5, 6]})
result = df.set_index("A").style._translate()
expected = [
[
{
"class": "blank level0",
"type": "th",
"value": "",
"display_value": "",
"is_visible": True,
},
{
"class": "col_heading level0 col0",
"type": "th",
"value": "B",
"display_value": "B",
"is_visible": True,
},
{
"class": "col_heading level0 col1",
"type": "th",
"value": "C",
"display_value": "C",
"is_visible": True,
},
],
[
{"class": "index_name level0", "type": "th", "value": "A"},
{"class": "blank", "type": "th", "value": ""},
{"class": "blank", "type": "th", "value": ""},
],
]
assert result["head"] == expected
def test_multiindex_name(self):
# https://github.com/pandas-dev/pandas/issues/11655
df = DataFrame({"A": [1, 2], "B": [3, 4], "C": [5, 6]})
result = df.set_index(["A", "B"]).style._translate()
expected = [
[
{
"class": "blank",
"type": "th",
"value": "",
"display_value": "",
"is_visible": True,
},
{
"class": "blank level0",
"type": "th",
"value": "",
"display_value": "",
"is_visible": True,
},
{
"class": "col_heading level0 col0",
"type": "th",
"value": "C",
"display_value": "C",
"is_visible": True,
},
],
[
{"class": "index_name level0", "type": "th", "value": "A"},
{"class": "index_name level1", "type": "th", "value": "B"},
{"class": "blank", "type": "th", "value": ""},
],
]
assert result["head"] == expected
def test_numeric_columns(self):
# https://github.com/pandas-dev/pandas/issues/12125
# smoke test for _translate
df = DataFrame({0: [1, 2, 3]})
df.style._translate()
def test_apply_axis(self):
df = DataFrame({"A": [0, 0], "B": [1, 1]})
f = lambda x: [f"val: {x.max()}" for v in x]
result = df.style.apply(f, axis=1)
assert len(result._todo) == 1
assert len(result.ctx) == 0
result._compute()
expected = {
(0, 0): ["val: 1"],
(0, 1): ["val: 1"],
(1, 0): ["val: 1"],
(1, 1): ["val: 1"],
}
assert result.ctx == expected
result = df.style.apply(f, axis=0)
expected = {
(0, 0): ["val: 0"],
(0, 1): ["val: 1"],
(1, 0): ["val: 0"],
(1, 1): ["val: 1"],
}
result._compute()
assert result.ctx == expected
result = df.style.apply(f) # default
result._compute()
assert result.ctx == expected
def test_apply_subset(self):
axes = [0, 1]
slices = [
pd.IndexSlice[:],
pd.IndexSlice[:, ["A"]],
pd.IndexSlice[[1], :],
pd.IndexSlice[[1], ["A"]],
pd.IndexSlice[:2, ["A", "B"]],
]
for ax in axes:
for slice_ in slices:
result = (
self.df.style.apply(self.h, axis=ax, subset=slice_, foo="baz")
._compute()
.ctx
)
expected = {
(r, c): ["color: baz"]
for r, row in enumerate(self.df.index)
for c, col in enumerate(self.df.columns)
if row in self.df.loc[slice_].index
and col in self.df.loc[slice_].columns
}
assert result == expected
def test_applymap_subset(self):
def f(x):
return "foo: bar"
slices = [
pd.IndexSlice[:],
pd.IndexSlice[:, ["A"]],
pd.IndexSlice[[1], :],
pd.IndexSlice[[1], ["A"]],
pd.IndexSlice[:2, ["A", "B"]],
]
for slice_ in slices:
result = self.df.style.applymap(f, subset=slice_)._compute().ctx
expected = {
(r, c): ["foo: bar"]
for r, row in enumerate(self.df.index)
for c, col in enumerate(self.df.columns)
if row in self.df.loc[slice_].index
and col in self.df.loc[slice_].columns
}
assert result == expected
def test_applymap_subset_multiindex(self):
# GH 19861
# Smoke test for applymap
def color_negative_red(val):
"""
Takes a scalar and returns a string with
the css property `'color: red'` for negative
strings, black otherwise.
"""
color = "red" if val < 0 else "black"
return f"color: {color}"
dic = {
("a", "d"): [-1.12, 2.11],
("a", "c"): [2.78, -2.88],
("b", "c"): [-3.99, 3.77],
("b", "d"): [4.21, -1.22],
}
idx = pd.IndexSlice
df = DataFrame(dic, index=[0, 1])
(df.style.applymap(color_negative_red, subset=idx[:, idx["b", "d"]]).render())
def test_applymap_subset_multiindex_code(self):
# https://github.com/pandas-dev/pandas/issues/25858
# Checks styler.applymap works with multindex when codes are provided
codes = np.array([[0, 0, 1, 1], [0, 1, 0, 1]])
columns = pd.MultiIndex(
levels=[["a", "b"], ["%", "#"]], codes=codes, names=["", ""]
)
df = DataFrame(
[[1, -1, 1, 1], [-1, 1, 1, 1]], index=["hello", "world"], columns=columns
)
pct_subset = pd.IndexSlice[:, pd.IndexSlice[:, "%":"%"]]
def color_negative_red(val):
color = "red" if val < 0 else "black"
return f"color: {color}"
df.loc[pct_subset]
df.style.applymap(color_negative_red, subset=pct_subset)
def test_where_with_one_style(self):
# GH 17474
def f(x):
return x > 0.5
style1 = "foo: bar"
result = self.df.style.where(f, style1)._compute().ctx
expected = {
(r, c): [style1]
for r, row in enumerate(self.df.index)
for c, col in enumerate(self.df.columns)
if f(self.df.loc[row, col])
}
assert result == expected
def test_where_subset(self):
# GH 17474
def f(x):
return x > 0.5
style1 = "foo: bar"
style2 = "baz: foo"
slices = [
pd.IndexSlice[:],
pd.IndexSlice[:, ["A"]],
pd.IndexSlice[[1], :],
pd.IndexSlice[[1], ["A"]],
pd.IndexSlice[:2, ["A", "B"]],
]
for slice_ in slices:
result = (
self.df.style.where(f, style1, style2, subset=slice_)._compute().ctx
)
expected = {
(r, c): [style1 if f(self.df.loc[row, col]) else style2]
for r, row in enumerate(self.df.index)
for c, col in enumerate(self.df.columns)
if row in self.df.loc[slice_].index
and col in self.df.loc[slice_].columns
}
assert result == expected
def test_where_subset_compare_with_applymap(self):
# GH 17474
def f(x):
return x > 0.5
style1 = "foo: bar"
style2 = "baz: foo"
def g(x):
return style1 if f(x) else style2
slices = [
pd.IndexSlice[:],
pd.IndexSlice[:, ["A"]],
pd.IndexSlice[[1], :],
pd.IndexSlice[[1], ["A"]],
pd.IndexSlice[:2, ["A", "B"]],
]
for slice_ in slices:
result = (
self.df.style.where(f, style1, style2, subset=slice_)._compute().ctx
)
expected = self.df.style.applymap(g, subset=slice_)._compute().ctx
assert result == expected
def test_empty(self):
df = DataFrame({"A": [1, 0]})
s = df.style
s.ctx = {(0, 0): ["color: red"], (1, 0): [""]}
result = s._translate()["cellstyle"]
expected = [
{"props": [("color", " red")], "selectors": ["row0_col0"]},
{"props": [("", "")], "selectors": ["row1_col0"]},
]
assert result == expected
def test_duplicate(self):
df = DataFrame({"A": [1, 0]})
s = df.style
s.ctx = {(0, 0): ["color: red"], (1, 0): ["color: red"]}
result = s._translate()["cellstyle"]
expected = [
{"props": [("color", " red")], "selectors": ["row0_col0", "row1_col0"]}
]
assert result == expected
def test_bar_align_left(self):
df = DataFrame({"A": [0, 1, 2]})
result = df.style.bar()._compute().ctx
expected = {
(0, 0): ["width: 10em", " height: 80%"],
(1, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient("
"90deg,#d65f5f 50.0%, transparent 50.0%)",
],
(2, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient("
"90deg,#d65f5f 100.0%, transparent 100.0%)",
],
}
assert result == expected
result = df.style.bar(color="red", width=50)._compute().ctx
expected = {
(0, 0): ["width: 10em", " height: 80%"],
(1, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,red 25.0%, transparent 25.0%)",
],
(2, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,red 50.0%, transparent 50.0%)",
],
}
assert result == expected
df["C"] = ["a"] * len(df)
result = df.style.bar(color="red", width=50)._compute().ctx
assert result == expected
df["C"] = df["C"].astype("category")
result = df.style.bar(color="red", width=50)._compute().ctx
assert result == expected
def test_bar_align_left_0points(self):
df = DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
result = df.style.bar()._compute().ctx
expected = {
(0, 0): ["width: 10em", " height: 80%"],
(0, 1): ["width: 10em", " height: 80%"],
(0, 2): ["width: 10em", " height: 80%"],
(1, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,#d65f5f 50.0%, transparent 50.0%)",
],
(1, 1): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,#d65f5f 50.0%, transparent 50.0%)",
],
(1, 2): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,#d65f5f 50.0%, transparent 50.0%)",
],
(2, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,#d65f5f 100.0%"
", transparent 100.0%)",
],
(2, 1): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,#d65f5f 100.0%"
", transparent 100.0%)",
],
(2, 2): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,#d65f5f 100.0%"
", transparent 100.0%)",
],
}
assert result == expected
result = df.style.bar(axis=1)._compute().ctx
expected = {
(0, 0): ["width: 10em", " height: 80%"],
(0, 1): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,#d65f5f 50.0%, transparent 50.0%)",
],
(0, 2): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,#d65f5f 100.0%"
", transparent 100.0%)",
],
(1, 0): ["width: 10em", " height: 80%"],
(1, 1): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,#d65f5f 50.0%"
", transparent 50.0%)",
],
(1, 2): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,#d65f5f 100.0%"
", transparent 100.0%)",
],
(2, 0): ["width: 10em", " height: 80%"],
(2, 1): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,#d65f5f 50.0%"
", transparent 50.0%)",
],
(2, 2): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,#d65f5f 100.0%"
", transparent 100.0%)",
],
}
assert result == expected
def test_bar_align_mid_pos_and_neg(self):
df = DataFrame({"A": [-10, 0, 20, 90]})
result = df.style.bar(align="mid", color=["#d65f5f", "#5fba7d"])._compute().ctx
expected = {
(0, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,"
"#d65f5f 10.0%, transparent 10.0%)",
],
(1, 0): ["width: 10em", " height: 80%"],
(2, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 10.0%, #5fba7d 10.0%"
", #5fba7d 30.0%, transparent 30.0%)",
],
(3, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 10.0%, "
"#5fba7d 10.0%, #5fba7d 100.0%, "
"transparent 100.0%)",
],
}
assert result == expected
def test_bar_align_mid_all_pos(self):
df = DataFrame({"A": [10, 20, 50, 100]})
result = df.style.bar(align="mid", color=["#d65f5f", "#5fba7d"])._compute().ctx
expected = {
(0, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,"
"#5fba7d 10.0%, transparent 10.0%)",
],
(1, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,"
"#5fba7d 20.0%, transparent 20.0%)",
],
(2, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,"
"#5fba7d 50.0%, transparent 50.0%)",
],
(3, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,"
"#5fba7d 100.0%, transparent 100.0%)",
],
}
assert result == expected
def test_bar_align_mid_all_neg(self):
df = DataFrame({"A": [-100, -60, -30, -20]})
result = df.style.bar(align="mid", color=["#d65f5f", "#5fba7d"])._compute().ctx
expected = {
(0, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,"
"#d65f5f 100.0%, transparent 100.0%)",
],
(1, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 40.0%, "
"#d65f5f 40.0%, #d65f5f 100.0%, "
"transparent 100.0%)",
],
(2, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 70.0%, "
"#d65f5f 70.0%, #d65f5f 100.0%, "
"transparent 100.0%)",
],
(3, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 80.0%, "
"#d65f5f 80.0%, #d65f5f 100.0%, "
"transparent 100.0%)",
],
}
assert result == expected
def test_bar_align_zero_pos_and_neg(self):
# See https://github.com/pandas-dev/pandas/pull/14757
df = DataFrame({"A": [-10, 0, 20, 90]})
result = (
df.style.bar(align="zero", color=["#d65f5f", "#5fba7d"], width=90)
._compute()
.ctx
)
expected = {
(0, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 40.0%, #d65f5f 40.0%, "
"#d65f5f 45.0%, transparent 45.0%)",
],
(1, 0): ["width: 10em", " height: 80%"],
(2, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 45.0%, #5fba7d 45.0%, "
"#5fba7d 55.0%, transparent 55.0%)",
],
(3, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 45.0%, #5fba7d 45.0%, "
"#5fba7d 90.0%, transparent 90.0%)",
],
}
assert result == expected
def test_bar_align_left_axis_none(self):
df = DataFrame({"A": [0, 1], "B": [2, 4]})
result = df.style.bar(axis=None)._compute().ctx
expected = {
(0, 0): ["width: 10em", " height: 80%"],
(1, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,"
"#d65f5f 25.0%, transparent 25.0%)",
],
(0, 1): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,"
"#d65f5f 50.0%, transparent 50.0%)",
],
(1, 1): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,"
"#d65f5f 100.0%, transparent 100.0%)",
],
}
assert result == expected
def test_bar_align_zero_axis_none(self):
df = DataFrame({"A": [0, 1], "B": [-2, 4]})
result = df.style.bar(align="zero", axis=None)._compute().ctx
expected = {
(0, 0): ["width: 10em", " height: 80%"],
(1, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 50.0%, #d65f5f 50.0%, "
"#d65f5f 62.5%, transparent 62.5%)",
],
(0, 1): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 25.0%, #d65f5f 25.0%, "
"#d65f5f 50.0%, transparent 50.0%)",
],
(1, 1): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 50.0%, #d65f5f 50.0%, "
"#d65f5f 100.0%, transparent 100.0%)",
],
}
assert result == expected
def test_bar_align_mid_axis_none(self):
df = DataFrame({"A": [0, 1], "B": [-2, 4]})
result = df.style.bar(align="mid", axis=None)._compute().ctx
expected = {
(0, 0): ["width: 10em", " height: 80%"],
(1, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 33.3%, #d65f5f 33.3%, "
"#d65f5f 50.0%, transparent 50.0%)",
],
(0, 1): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,"
"#d65f5f 33.3%, transparent 33.3%)",
],
(1, 1): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 33.3%, #d65f5f 33.3%, "
"#d65f5f 100.0%, transparent 100.0%)",
],
}
assert result == expected
def test_bar_align_mid_vmin(self):
df = DataFrame({"A": [0, 1], "B": [-2, 4]})
result = df.style.bar(align="mid", axis=None, vmin=-6)._compute().ctx
expected = {
(0, 0): ["width: 10em", " height: 80%"],
(1, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 60.0%, #d65f5f 60.0%, "
"#d65f5f 70.0%, transparent 70.0%)",
],
(0, 1): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 40.0%, #d65f5f 40.0%, "
"#d65f5f 60.0%, transparent 60.0%)",
],
(1, 1): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 60.0%, #d65f5f 60.0%, "
"#d65f5f 100.0%, transparent 100.0%)",
],
}
assert result == expected
def test_bar_align_mid_vmax(self):
df = DataFrame({"A": [0, 1], "B": [-2, 4]})
result = df.style.bar(align="mid", axis=None, vmax=8)._compute().ctx
expected = {
(0, 0): ["width: 10em", " height: 80%"],
(1, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 20.0%, #d65f5f 20.0%, "
"#d65f5f 30.0%, transparent 30.0%)",
],
(0, 1): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,"
"#d65f5f 20.0%, transparent 20.0%)",
],
(1, 1): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 20.0%, #d65f5f 20.0%, "
"#d65f5f 60.0%, transparent 60.0%)",
],
}
assert result == expected
def test_bar_align_mid_vmin_vmax_wide(self):
df = DataFrame({"A": [0, 1], "B": [-2, 4]})
result = df.style.bar(align="mid", axis=None, vmin=-3, vmax=7)._compute().ctx
expected = {
(0, 0): ["width: 10em", " height: 80%"],
(1, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 30.0%, #d65f5f 30.0%, "
"#d65f5f 40.0%, transparent 40.0%)",
],
(0, 1): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 10.0%, #d65f5f 10.0%, "
"#d65f5f 30.0%, transparent 30.0%)",
],
(1, 1): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 30.0%, #d65f5f 30.0%, "
"#d65f5f 70.0%, transparent 70.0%)",
],
}
assert result == expected
def test_bar_align_mid_vmin_vmax_clipping(self):
df = DataFrame({"A": [0, 1], "B": [-2, 4]})
result = df.style.bar(align="mid", axis=None, vmin=-1, vmax=3)._compute().ctx
expected = {
(0, 0): ["width: 10em", " height: 80%"],
(1, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 25.0%, #d65f5f 25.0%, "
"#d65f5f 50.0%, transparent 50.0%)",
],
(0, 1): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,"
"#d65f5f 25.0%, transparent 25.0%)",
],
(1, 1): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 25.0%, #d65f5f 25.0%, "
"#d65f5f 100.0%, transparent 100.0%)",
],
}
assert result == expected
def test_bar_align_mid_nans(self):
df = DataFrame({"A": [1, None], "B": [-1, 3]})
result = df.style.bar(align="mid", axis=None)._compute().ctx
expected = {
(0, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 25.0%, #d65f5f 25.0%, "
"#d65f5f 50.0%, transparent 50.0%)",
],
(0, 1): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg,"
"#d65f5f 25.0%, transparent 25.0%)",
],
(1, 1): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 25.0%, #d65f5f 25.0%, "
"#d65f5f 100.0%, transparent 100.0%)",
],
}
assert result == expected
def test_bar_align_zero_nans(self):
df = DataFrame({"A": [1, None], "B": [-1, 2]})
result = df.style.bar(align="zero", axis=None)._compute().ctx
expected = {
(0, 0): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 50.0%, #d65f5f 50.0%, "
"#d65f5f 75.0%, transparent 75.0%)",
],
(0, 1): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 25.0%, #d65f5f 25.0%, "
"#d65f5f 50.0%, transparent 50.0%)",
],
(1, 1): [
"width: 10em",
" height: 80%",
"background: linear-gradient(90deg, "
"transparent 50.0%, #d65f5f 50.0%, "
"#d65f5f 100.0%, transparent 100.0%)",
],
}
assert result == expected
def test_bar_bad_align_raises(self):
df = DataFrame({"A": [-100, -60, -30, -20]})
msg = "`align` must be one of {'left', 'zero',' mid'}"
with pytest.raises(ValueError, match=msg):
df.style.bar(align="poorly", color=["#d65f5f", "#5fba7d"])
def test_format_with_na_rep(self):
# GH 21527 28358
df = DataFrame([[None, None], [1.1, 1.2]], columns=["A", "B"])
ctx = df.style.format(None, na_rep="-")._translate()
assert ctx["body"][0][1]["display_value"] == "-"
assert ctx["body"][0][2]["display_value"] == "-"
ctx = df.style.format("{:.2%}", na_rep="-")._translate()
assert ctx["body"][0][1]["display_value"] == "-"
assert ctx["body"][0][2]["display_value"] == "-"
assert ctx["body"][1][1]["display_value"] == "110.00%"
assert ctx["body"][1][2]["display_value"] == "120.00%"
ctx = df.style.format("{:.2%}", na_rep="-", subset=["B"])._translate()
assert ctx["body"][0][2]["display_value"] == "-"
assert ctx["body"][1][2]["display_value"] == "120.00%"
def test_init_with_na_rep(self):
# GH 21527 28358
df = DataFrame([[None, None], [1.1, 1.2]], columns=["A", "B"])
ctx = Styler(df, na_rep="NA")._translate()
assert ctx["body"][0][1]["display_value"] == "NA"
assert ctx["body"][0][2]["display_value"] == "NA"
def test_set_na_rep(self):
# GH 21527 28358
df = DataFrame([[None, None], [1.1, 1.2]], columns=["A", "B"])
ctx = df.style.set_na_rep("NA")._translate()
assert ctx["body"][0][1]["display_value"] == "NA"
assert ctx["body"][0][2]["display_value"] == "NA"
ctx = (
df.style.set_na_rep("NA")
.format(None, na_rep="-", subset=["B"])
._translate()
)
assert ctx["body"][0][1]["display_value"] == "NA"
assert ctx["body"][0][2]["display_value"] == "-"
def test_format_non_numeric_na(self):
# GH 21527 28358
df = DataFrame(
{
"object": [None, np.nan, "foo"],
"datetime": [None, pd.NaT, pd.Timestamp("20120101")],
}
)
ctx = df.style.set_na_rep("NA")._translate()
assert ctx["body"][0][1]["display_value"] == "NA"
assert ctx["body"][0][2]["display_value"] == "NA"
assert ctx["body"][1][1]["display_value"] == "NA"
assert ctx["body"][1][2]["display_value"] == "NA"
ctx = df.style.format(None, na_rep="-")._translate()
assert ctx["body"][0][1]["display_value"] == "-"
assert ctx["body"][0][2]["display_value"] == "-"
assert ctx["body"][1][1]["display_value"] == "-"
assert ctx["body"][1][2]["display_value"] == "-"
def test_format_with_bad_na_rep(self):
# GH 21527 28358
df = DataFrame([[None, None], [1.1, 1.2]], columns=["A", "B"])
msg = "Expected a string, got -1 instead"
with pytest.raises(TypeError, match=msg):
df.style.format(None, na_rep=-1)
def test_highlight_null(self, null_color="red"):
df = DataFrame({"A": [0, np.nan]})
result = df.style.highlight_null()._compute().ctx
expected = {(1, 0): ["background-color: red"]}
assert result == expected
def test_highlight_null_subset(self):
# GH 31345
df = DataFrame({"A": [0, np.nan], "B": [0, np.nan]})
result = (
df.style.highlight_null(null_color="red", subset=["A"])
.highlight_null(null_color="green", subset=["B"])
._compute()
.ctx
)
expected = {
(1, 0): ["background-color: red"],
(1, 1): ["background-color: green"],
}
assert result == expected
def test_nonunique_raises(self):
df = DataFrame([[1, 2]], columns=["A", "A"])
msg = "style is not supported for non-unique indices."
with pytest.raises(ValueError, match=msg):
df.style
with pytest.raises(ValueError, match=msg):
Styler(df)
def test_caption(self):
styler = Styler(self.df, caption="foo")
result = styler.render()
assert all(["caption" in result, "foo" in result])
styler = self.df.style
result = styler.set_caption("baz")
assert styler is result
assert styler.caption == "baz"
def test_uuid(self):
styler = Styler(self.df, uuid="abc123")
result = styler.render()
assert "abc123" in result
styler = self.df.style
result = styler.set_uuid("aaa")
assert result is styler
assert result.uuid == "aaa"
def test_unique_id(self):
# See https://github.com/pandas-dev/pandas/issues/16780
df = DataFrame({"a": [1, 3, 5, 6], "b": [2, 4, 12, 21]})
result = df.style.render(uuid="test")
assert "test" in result
ids = re.findall('id="(.*?)"', result)
assert np.unique(ids).size == len(ids)
def test_table_styles(self):
style = [{"selector": "th", "props": [("foo", "bar")]}]
styler = Styler(self.df, table_styles=style)
result = " ".join(styler.render().split())
assert "th { foo: bar; }" in result
styler = self.df.style
result = styler.set_table_styles(style)
assert styler is result
assert styler.table_styles == style
def test_table_attributes(self):
attributes = 'class="foo" data-bar'
styler = Styler(self.df, table_attributes=attributes)
result = styler.render()
assert 'class="foo" data-bar' in result
result = self.df.style.set_table_attributes(attributes).render()
assert 'class="foo" data-bar' in result
def test_precision(self):
with pd.option_context("display.precision", 10):
s = Styler(self.df)
assert s.precision == 10
s = Styler(self.df, precision=2)
assert s.precision == 2
s2 = s.set_precision(4)
assert s is s2
assert s.precision == 4
def test_apply_none(self):
def f(x):
return DataFrame(
np.where(x == x.max(), "color: red", ""),
index=x.index,
columns=x.columns,
)
result = DataFrame([[1, 2], [3, 4]]).style.apply(f, axis=None)._compute().ctx
assert result[(1, 1)] == ["color: red"]
def test_trim(self):
result = self.df.style.render() # trim=True
assert result.count("#") == 0
result = self.df.style.highlight_max().render()
assert result.count("#") == len(self.df.columns)
def test_highlight_max(self):
df = DataFrame([[1, 2], [3, 4]], columns=["A", "B"])
# max(df) = min(-df)
for max_ in [True, False]:
if max_:
attr = "highlight_max"
else:
df = -df
attr = "highlight_min"
result = getattr(df.style, attr)()._compute().ctx
assert result[(1, 1)] == ["background-color: yellow"]
result = getattr(df.style, attr)(color="green")._compute().ctx
assert result[(1, 1)] == ["background-color: green"]
result = getattr(df.style, attr)(subset="A")._compute().ctx
assert result[(1, 0)] == ["background-color: yellow"]
result = getattr(df.style, attr)(axis=0)._compute().ctx
expected = {
(1, 0): ["background-color: yellow"],
(1, 1): ["background-color: yellow"],
}
assert result == expected
result = getattr(df.style, attr)(axis=1)._compute().ctx
expected = {
(0, 1): ["background-color: yellow"],
(1, 1): ["background-color: yellow"],
}
assert result == expected
# separate since we can't negate the strs
df["C"] = ["a", "b"]
result = df.style.highlight_max()._compute().ctx
expected = {(1, 1): ["background-color: yellow"]}
result = df.style.highlight_min()._compute().ctx
expected = {(0, 0): ["background-color: yellow"]}
def test_export(self):
f = lambda x: "color: red" if x > 0 else "color: blue"
g = lambda x, z: f"color: {z}" if x > 0 else f"color: {z}"
style1 = self.styler
style1.applymap(f).applymap(g, z="b").highlight_max()
result = style1.export()
style2 = self.df.style
style2.use(result)
assert style1._todo == style2._todo
style2.render()
def test_display_format(self):
df = DataFrame(np.random.random(size=(2, 2)))
ctx = df.style.format("{:0.1f}")._translate()
assert all(["display_value" in c for c in row] for row in ctx["body"])
assert all(
[len(c["display_value"]) <= 3 for c in row[1:]] for row in ctx["body"]
)
assert len(ctx["body"][0][1]["display_value"].lstrip("-")) <= 3
def test_display_format_raises(self):
df = DataFrame(np.random.randn(2, 2))
msg = "Expected a template string or callable, got 5 instead"
with pytest.raises(TypeError, match=msg):
df.style.format(5)
msg = "Expected a template string or callable, got True instead"
with pytest.raises(TypeError, match=msg):
df.style.format(True)
def test_display_set_precision(self):
# Issue #13257
df = DataFrame(data=[[1.0, 2.0090], [3.2121, 4.566]], columns=["a", "b"])
s = Styler(df)
ctx = s.set_precision(1)._translate()
assert s.precision == 1
assert ctx["body"][0][1]["display_value"] == "1.0"
assert ctx["body"][0][2]["display_value"] == "2.0"
assert ctx["body"][1][1]["display_value"] == "3.2"
assert ctx["body"][1][2]["display_value"] == "4.6"
ctx = s.set_precision(2)._translate()
assert s.precision == 2
assert ctx["body"][0][1]["display_value"] == "1.00"
assert ctx["body"][0][2]["display_value"] == "2.01"
assert ctx["body"][1][1]["display_value"] == "3.21"
assert ctx["body"][1][2]["display_value"] == "4.57"
ctx = s.set_precision(3)._translate()
assert s.precision == 3
assert ctx["body"][0][1]["display_value"] == "1.000"
assert ctx["body"][0][2]["display_value"] == "2.009"
assert ctx["body"][1][1]["display_value"] == "3.212"
assert ctx["body"][1][2]["display_value"] == "4.566"
def test_display_subset(self):
df = DataFrame([[0.1234, 0.1234], [1.1234, 1.1234]], columns=["a", "b"])
ctx = df.style.format(
{"a": "{:0.1f}", "b": "{0:.2%}"}, subset=pd.IndexSlice[0, :]
)._translate()
expected = "0.1"
raw_11 = "1.123400"
assert ctx["body"][0][1]["display_value"] == expected
assert ctx["body"][1][1]["display_value"] == raw_11
assert ctx["body"][0][2]["display_value"] == "12.34%"
ctx = df.style.format("{:0.1f}", subset=pd.IndexSlice[0, :])._translate()
assert ctx["body"][0][1]["display_value"] == expected
assert ctx["body"][1][1]["display_value"] == raw_11
ctx = df.style.format("{:0.1f}", subset=pd.IndexSlice["a"])._translate()
assert ctx["body"][0][1]["display_value"] == expected
assert ctx["body"][0][2]["display_value"] == "0.123400"
ctx = df.style.format("{:0.1f}", subset=pd.IndexSlice[0, "a"])._translate()
assert ctx["body"][0][1]["display_value"] == expected
assert ctx["body"][1][1]["display_value"] == raw_11
ctx = df.style.format(
"{:0.1f}", subset=pd.IndexSlice[[0, 1], ["a"]]
)._translate()
assert ctx["body"][0][1]["display_value"] == expected
assert ctx["body"][1][1]["display_value"] == "1.1"
assert ctx["body"][0][2]["display_value"] == "0.123400"
assert ctx["body"][1][2]["display_value"] == raw_11
def test_display_dict(self):
df = DataFrame([[0.1234, 0.1234], [1.1234, 1.1234]], columns=["a", "b"])
ctx = df.style.format({"a": "{:0.1f}", "b": "{0:.2%}"})._translate()
assert ctx["body"][0][1]["display_value"] == "0.1"
assert ctx["body"][0][2]["display_value"] == "12.34%"
df["c"] = ["aaa", "bbb"]
ctx = df.style.format({"a": "{:0.1f}", "c": str.upper})._translate()
assert ctx["body"][0][1]["display_value"] == "0.1"
assert ctx["body"][0][3]["display_value"] == "AAA"
def test_bad_apply_shape(self):
df = DataFrame([[1, 2], [3, 4]])
msg = "returned the wrong shape"
with pytest.raises(ValueError, match=msg):
df.style._apply(lambda x: "x", subset=pd.IndexSlice[[0, 1], :])
with pytest.raises(ValueError, match=msg):
df.style._apply(lambda x: [""], subset=pd.IndexSlice[[0, 1], :])
with pytest.raises(ValueError, match=msg):
df.style._apply(lambda x: ["", "", "", ""])
with pytest.raises(ValueError, match=msg):
df.style._apply(lambda x: ["", "", ""], subset=1)
msg = "Length mismatch: Expected axis has 3 elements"
with pytest.raises(ValueError, match=msg):
df.style._apply(lambda x: ["", "", ""], axis=1)
def test_apply_bad_return(self):
def f(x):
return ""
df = DataFrame([[1, 2], [3, 4]])
msg = "must return a DataFrame when passed to `Styler.apply` with axis=None"
with pytest.raises(TypeError, match=msg):
df.style._apply(f, axis=None)
def test_apply_bad_labels(self):
def f(x):
return DataFrame(index=[1, 2], columns=["a", "b"])
df = DataFrame([[1, 2], [3, 4]])
msg = "must have identical index and columns as the input"
with pytest.raises(ValueError, match=msg):
df.style._apply(f, axis=None)
def test_get_level_lengths(self):
index = pd.MultiIndex.from_product([["a", "b"], [0, 1, 2]])
expected = {
(0, 0): 3,
(0, 3): 3,
(1, 0): 1,
(1, 1): 1,
(1, 2): 1,
(1, 3): 1,
(1, 4): 1,
(1, 5): 1,
}
result = _get_level_lengths(index)
tm.assert_dict_equal(result, expected)
def test_get_level_lengths_un_sorted(self):
index = pd.MultiIndex.from_arrays([[1, 1, 2, 1], ["a", "b", "b", "d"]])
expected = {
(0, 0): 2,
(0, 2): 1,
(0, 3): 1,
(1, 0): 1,
(1, 1): 1,
(1, 2): 1,
(1, 3): 1,
}
result = _get_level_lengths(index)
tm.assert_dict_equal(result, expected)
def test_mi_sparse(self):
df = DataFrame(
{"A": [1, 2]}, index=pd.MultiIndex.from_arrays([["a", "a"], [0, 1]])
)
result = df.style._translate()
body_0 = result["body"][0][0]
expected_0 = {
"value": "a",
"display_value": "a",
"is_visible": True,
"type": "th",
"attributes": ['rowspan="2"'],
"class": "row_heading level0 row0",
"id": "level0_row0",
}
tm.assert_dict_equal(body_0, expected_0)
body_1 = result["body"][0][1]
expected_1 = {
"value": 0,
"display_value": 0,
"is_visible": True,
"type": "th",
"class": "row_heading level1 row0",
"id": "level1_row0",
}
tm.assert_dict_equal(body_1, expected_1)
body_10 = result["body"][1][0]
expected_10 = {
"value": "a",
"display_value": "a",
"is_visible": False,
"type": "th",
"class": "row_heading level0 row1",
"id": "level0_row1",
}
tm.assert_dict_equal(body_10, expected_10)
head = result["head"][0]
expected = [
{
"type": "th",
"class": "blank",
"value": "",
"is_visible": True,
"display_value": "",
},
{
"type": "th",
"class": "blank level0",
"value": "",
"is_visible": True,
"display_value": "",
},
{
"type": "th",
"class": "col_heading level0 col0",
"value": "A",
"is_visible": True,
"display_value": "A",
},
]
assert head == expected
def test_mi_sparse_disabled(self):
with pd.option_context("display.multi_sparse", False):
df = DataFrame(
{"A": [1, 2]}, index=pd.MultiIndex.from_arrays([["a", "a"], [0, 1]])
)
result = df.style._translate()
body = result["body"]
for row in body:
assert "attributes" not in row[0]
def test_mi_sparse_index_names(self):
df = DataFrame(
{"A": [1, 2]},
index=pd.MultiIndex.from_arrays(
[["a", "a"], [0, 1]], names=["idx_level_0", "idx_level_1"]
),
)
result = df.style._translate()
head = result["head"][1]
expected = [
{"class": "index_name level0", "value": "idx_level_0", "type": "th"},
{"class": "index_name level1", "value": "idx_level_1", "type": "th"},
{"class": "blank", "value": "", "type": "th"},
]
assert head == expected
def test_mi_sparse_column_names(self):
df = DataFrame(
np.arange(16).reshape(4, 4),
index=pd.MultiIndex.from_arrays(
[["a", "a", "b", "a"], [0, 1, 1, 2]],
names=["idx_level_0", "idx_level_1"],
),
columns=pd.MultiIndex.from_arrays(
[["C1", "C1", "C2", "C2"], [1, 0, 1, 0]], names=["col_0", "col_1"]
),
)
result = df.style._translate()
head = result["head"][1]
expected = [
{
"class": "blank",
"value": "",
"display_value": "",
"type": "th",
"is_visible": True,
},
{
"class": "index_name level1",
"value": "col_1",
"display_value": "col_1",
"is_visible": True,
"type": "th",
},
{
"class": "col_heading level1 col0",
"display_value": 1,
"is_visible": True,
"type": "th",
"value": 1,
},
{
"class": "col_heading level1 col1",
"display_value": 0,
"is_visible": True,
"type": "th",
"value": 0,
},
{
"class": "col_heading level1 col2",
"display_value": 1,
"is_visible": True,
"type": "th",
"value": 1,
},
{
"class": "col_heading level1 col3",
"display_value": 0,
"is_visible": True,
"type": "th",
"value": 0,
},
]
assert head == expected
def test_hide_single_index(self):
# GH 14194
# single unnamed index
ctx = self.df.style._translate()
assert ctx["body"][0][0]["is_visible"]
assert ctx["head"][0][0]["is_visible"]
ctx2 = self.df.style.hide_index()._translate()
assert not ctx2["body"][0][0]["is_visible"]
assert not ctx2["head"][0][0]["is_visible"]
# single named index
ctx3 = self.df.set_index("A").style._translate()
assert ctx3["body"][0][0]["is_visible"]
assert len(ctx3["head"]) == 2 # 2 header levels
assert ctx3["head"][0][0]["is_visible"]
ctx4 = self.df.set_index("A").style.hide_index()._translate()
assert not ctx4["body"][0][0]["is_visible"]
assert len(ctx4["head"]) == 1 # only 1 header levels
assert not ctx4["head"][0][0]["is_visible"]
def test_hide_multiindex(self):
# GH 14194
df = DataFrame(
{"A": [1, 2]},
index=pd.MultiIndex.from_arrays(
[["a", "a"], [0, 1]], names=["idx_level_0", "idx_level_1"]
),
)
ctx1 = df.style._translate()
# tests for 'a' and '0'
assert ctx1["body"][0][0]["is_visible"]
assert ctx1["body"][0][1]["is_visible"]
# check for blank header rows
assert ctx1["head"][0][0]["is_visible"]
assert ctx1["head"][0][1]["is_visible"]
ctx2 = df.style.hide_index()._translate()
# tests for 'a' and '0'
assert not ctx2["body"][0][0]["is_visible"]
assert not ctx2["body"][0][1]["is_visible"]
# check for blank header rows
assert not ctx2["head"][0][0]["is_visible"]
assert not ctx2["head"][0][1]["is_visible"]
def test_hide_columns_single_level(self):
# GH 14194
# test hiding single column
ctx = self.df.style._translate()
assert ctx["head"][0][1]["is_visible"]
assert ctx["head"][0][1]["display_value"] == "A"
assert ctx["head"][0][2]["is_visible"]
assert ctx["head"][0][2]["display_value"] == "B"
assert ctx["body"][0][1]["is_visible"] # col A, row 1
assert ctx["body"][1][2]["is_visible"] # col B, row 1
ctx = self.df.style.hide_columns("A")._translate()
assert not ctx["head"][0][1]["is_visible"]
assert not ctx["body"][0][1]["is_visible"] # col A, row 1
assert ctx["body"][1][2]["is_visible"] # col B, row 1
# test hiding mulitiple columns
ctx = self.df.style.hide_columns(["A", "B"])._translate()
assert not ctx["head"][0][1]["is_visible"]
assert not ctx["head"][0][2]["is_visible"]
assert not ctx["body"][0][1]["is_visible"] # col A, row 1
assert not ctx["body"][1][2]["is_visible"] # col B, row 1
def test_hide_columns_mult_levels(self):
# GH 14194
# setup dataframe with multiple column levels and indices
i1 = pd.MultiIndex.from_arrays(
[["a", "a"], [0, 1]], names=["idx_level_0", "idx_level_1"]
)
i2 = pd.MultiIndex.from_arrays(
[["b", "b"], [0, 1]], names=["col_level_0", "col_level_1"]
)
df = DataFrame([[1, 2], [3, 4]], index=i1, columns=i2)
ctx = df.style._translate()
# column headers
assert ctx["head"][0][2]["is_visible"]
assert ctx["head"][1][2]["is_visible"]
assert ctx["head"][1][3]["display_value"] == 1
# indices
assert ctx["body"][0][0]["is_visible"]
# data
assert ctx["body"][1][2]["is_visible"]
assert ctx["body"][1][2]["display_value"] == 3
assert ctx["body"][1][3]["is_visible"]
assert ctx["body"][1][3]["display_value"] == 4
# hide top column level, which hides both columns
ctx = df.style.hide_columns("b")._translate()
assert not ctx["head"][0][2]["is_visible"] # b
assert not ctx["head"][1][2]["is_visible"] # 0
assert not ctx["body"][1][2]["is_visible"] # 3
assert ctx["body"][0][0]["is_visible"] # index
# hide first column only
ctx = df.style.hide_columns([("b", 0)])._translate()
assert ctx["head"][0][2]["is_visible"] # b
assert not ctx["head"][1][2]["is_visible"] # 0
assert not ctx["body"][1][2]["is_visible"] # 3
assert ctx["body"][1][3]["is_visible"]
assert ctx["body"][1][3]["display_value"] == 4
# hide second column and index
ctx = df.style.hide_columns([("b", 1)]).hide_index()._translate()
assert not ctx["body"][0][0]["is_visible"] # index
assert ctx["head"][0][2]["is_visible"] # b
assert ctx["head"][1][2]["is_visible"] # 0
assert not ctx["head"][1][3]["is_visible"] # 1
assert not ctx["body"][1][3]["is_visible"] # 4
assert ctx["body"][1][2]["is_visible"]
assert ctx["body"][1][2]["display_value"] == 3
def test_pipe(self):
def set_caption_from_template(styler, a, b):
return styler.set_caption(f"Dataframe with a = {a} and b = {b}")
styler = self.df.style.pipe(set_caption_from_template, "A", b="B")
assert "Dataframe with a = A and b = B" in styler.render()
# Test with an argument that is a (callable, keyword_name) pair.
def f(a, b, styler):
return (a, b, styler)
styler = self.df.style
result = styler.pipe((f, "styler"), a=1, b=2)
assert result == (1, 2, styler)
def test_no_cell_ids(self):
# GH 35588
# GH 35663
df = DataFrame(data=[[0]])
styler = Styler(df, uuid="_", cell_ids=False)
styler.render()
s = styler.render() # render twice to ensure ctx is not updated
assert s.find('<td class="data row0 col0" >') != -1
@pytest.mark.parametrize(
"classes",
[
DataFrame(
data=[["", "test-class"], [np.nan, None]],
columns=["A", "B"],
index=["a", "b"],
),
DataFrame(data=[["test-class"]], columns=["B"], index=["a"]),
DataFrame(data=[["test-class", "unused"]], columns=["B", "C"], index=["a"]),
],
)
def test_set_data_classes(self, classes):
# GH 36159
df = DataFrame(data=[[0, 1], [2, 3]], columns=["A", "B"], index=["a", "b"])
s = Styler(df, uuid="_", cell_ids=False).set_td_classes(classes).render()
assert '<td class="data row0 col0" >0</td>' in s
assert '<td class="data row0 col1 test-class" >1</td>' in s
assert '<td class="data row1 col0" >2</td>' in s
assert '<td class="data row1 col1" >3</td>' in s
def test_chaining_table_styles(self):
# GH 35607
df = DataFrame(data=[[0, 1], [1, 2]], columns=["A", "B"])
styler = df.style.set_table_styles(
[{"selector": "", "props": [("background-color", "yellow")]}]
).set_table_styles(
[{"selector": ".col0", "props": [("background-color", "blue")]}],
overwrite=False,
)
assert len(styler.table_styles) == 2
def test_column_and_row_styling(self):
# GH 35607
df = DataFrame(data=[[0, 1], [1, 2]], columns=["A", "B"])
s = Styler(df, uuid_len=0)
s = s.set_table_styles({"A": [{"selector": "", "props": [("color", "blue")]}]})
assert "#T__ .col0 {\n color: blue;\n }" in s.render()
s = s.set_table_styles(
{0: [{"selector": "", "props": [("color", "blue")]}]}, axis=1
)
assert "#T__ .row0 {\n color: blue;\n }" in s.render()
def test_colspan_w3(self):
# GH 36223
df = DataFrame(data=[[1, 2]], columns=[["l0", "l0"], ["l1a", "l1b"]])
s = Styler(df, uuid="_", cell_ids=False)
assert '<th class="col_heading level0 col0" colspan="2">l0</th>' in s.render()
def test_rowspan_w3(self):
# GH 38533
df = DataFrame(data=[[1, 2]], index=[["l0", "l0"], ["l1a", "l1b"]])
s = Styler(df, uuid="_", cell_ids=False)
assert (
'<th id="T___level0_row0" class="row_heading '
'level0 row0" rowspan="2">l0</th>' in s.render()
)
@pytest.mark.parametrize("len_", [1, 5, 32, 33, 100])
def test_uuid_len(self, len_):
# GH 36345
df = DataFrame(data=[["A"]])
s = Styler(df, uuid_len=len_, cell_ids=False).render()
strt = s.find('id="T_')
end = s[strt + 6 :].find('"')
if len_ > 32:
assert end == 32 + 1
else:
assert end == len_ + 1
@pytest.mark.parametrize("len_", [-2, "bad", None])
def test_uuid_len_raises(self, len_):
# GH 36345
df = DataFrame(data=[["A"]])
msg = "``uuid_len`` must be an integer in range \\[0, 32\\]."
with pytest.raises(TypeError, match=msg):
Styler(df, uuid_len=len_, cell_ids=False).render()
@td.skip_if_no_mpl
class TestStylerMatplotlibDep:
def test_background_gradient(self):
df = DataFrame([[1, 2], [2, 4]], columns=["A", "B"])
for c_map in [None, "YlOrRd"]:
result = df.style.background_gradient(cmap=c_map)._compute().ctx
assert all("#" in x[0] for x in result.values())
assert result[(0, 0)] == result[(0, 1)]
assert result[(1, 0)] == result[(1, 1)]
result = (
df.style.background_gradient(subset=pd.IndexSlice[1, "A"])._compute().ctx
)
assert result[(1, 0)] == ["background-color: #fff7fb", "color: #000000"]
@pytest.mark.parametrize(
"c_map,expected",
[
(
None,
{
(0, 0): ["background-color: #440154", "color: #f1f1f1"],
(1, 0): ["background-color: #fde725", "color: #000000"],
},
),
(
"YlOrRd",
{
(0, 0): ["background-color: #ffffcc", "color: #000000"],
(1, 0): ["background-color: #800026", "color: #f1f1f1"],
},
),
],
)
def test_text_color_threshold(self, c_map, expected):
df = DataFrame([1, 2], columns=["A"])
result = df.style.background_gradient(cmap=c_map)._compute().ctx
assert result == expected
@pytest.mark.parametrize("text_color_threshold", [1.1, "1", -1, [2, 2]])
def test_text_color_threshold_raises(self, text_color_threshold):
df = DataFrame([[1, 2], [2, 4]], columns=["A", "B"])
msg = "`text_color_threshold` must be a value from 0 to 1."
with pytest.raises(ValueError, match=msg):
df.style.background_gradient(
text_color_threshold=text_color_threshold
)._compute()
@td.skip_if_no_mpl
def test_background_gradient_axis(self):
df = DataFrame([[1, 2], [2, 4]], columns=["A", "B"])
low = ["background-color: #f7fbff", "color: #000000"]
high = ["background-color: #08306b", "color: #f1f1f1"]
mid = ["background-color: #abd0e6", "color: #000000"]
result = df.style.background_gradient(cmap="Blues", axis=0)._compute().ctx
assert result[(0, 0)] == low
assert result[(0, 1)] == low
assert result[(1, 0)] == high
assert result[(1, 1)] == high
result = df.style.background_gradient(cmap="Blues", axis=1)._compute().ctx
assert result[(0, 0)] == low
assert result[(0, 1)] == high
assert result[(1, 0)] == low
assert result[(1, 1)] == high
result = df.style.background_gradient(cmap="Blues", axis=None)._compute().ctx
assert result[(0, 0)] == low
assert result[(0, 1)] == mid
assert result[(1, 0)] == mid
assert result[(1, 1)] == high
def test_background_gradient_vmin_vmax(self):
# GH 12145
df = DataFrame(range(5))
ctx = df.style.background_gradient(vmin=1, vmax=3)._compute().ctx
assert ctx[(0, 0)] == ctx[(1, 0)]
assert ctx[(4, 0)] == ctx[(3, 0)]
def test_background_gradient_int64(self):
# GH 28869
df1 = pd.Series(range(3)).to_frame()
df2 = pd.Series(range(3), dtype="Int64").to_frame()
ctx1 = df1.style.background_gradient()._compute().ctx
ctx2 = df2.style.background_gradient()._compute().ctx
assert ctx2[(0, 0)] == ctx1[(0, 0)]
assert ctx2[(1, 0)] == ctx1[(1, 0)]
assert ctx2[(2, 0)] == ctx1[(2, 0)]
def test_block_names():
# catch accidental removal of a block
expected = {
"before_style",
"style",
"table_styles",
"before_cellstyle",
"cellstyle",
"before_table",
"table",
"caption",
"thead",
"tbody",
"after_table",
"before_head_rows",
"head_tr",
"after_head_rows",
"before_rows",
"tr",
"after_rows",
}
result = set(Styler.template.blocks)
assert result == expected
def test_from_custom_template(tmpdir):
p = tmpdir.mkdir("templates").join("myhtml.tpl")
p.write(
textwrap.dedent(
"""\
{% extends "html.tpl" %}
{% block table %}
<h1>{{ table_title|default("My Table") }}</h1>
{{ super() }}
{% endblock table %}"""
)
)
result = Styler.from_custom_template(str(tmpdir.join("templates")), "myhtml.tpl")
assert issubclass(result, Styler)
assert result.env is not Styler.env
assert result.template is not Styler.template
styler = result(DataFrame({"A": [1, 2]}))
assert styler.render()