o_html()
@pytest.mark.parametrize("index", [False, True])
@pytest.mark.parametrize("columns", [False, True])
@pytest.mark.parametrize("index_name", [True, False])
def test_sticky_basic(styler, index, columns, index_name):
if index_name:
styler.index.name = "some text"
if index:
styler.set_sticky(axis=0)
if columns:
styler.set_sticky(axis=1)
left_css = (
"#T_ {0} {{\n position: sticky;\n background-color: inherit;\n"
" left: 0px;\n z-index: {1};\n}}"
)
top_css = (
"#T_ {0} {{\n position: sticky;\n background-color: inherit;\n"
" top: {1}px;\n z-index: {2};\n{3}}}"
)
res = styler.set_uuid("").to_html()
# test index stickys over thead and tbody
assert (left_css.format("thead tr th:nth-child(1)", "3 !important") in res) is index
assert (left_css.format("tbody tr th:nth-child(1)", "1") in res) is index
# test column stickys including if name row
assert (
top_css.format("thead tr:nth-child(1) th", "0", "2", " height: 25px;\n") in res
) is (columns and index_name)
assert (
top_css.format("thead tr:nth-child(2) th", "25", "2", " height: 25px;\n")
in res
) is (columns and index_name)
assert (top_css.format("thead tr:nth-child(1) th", "0", "2", "") in res) is (
columns and not index_name
)
@pytest.mark.parametrize("index", [False, True])
@pytest.mark.parametrize("columns", [False, True])
def test_sticky_mi(styler_mi, index, columns):
if index:
styler_mi.set_sticky(axis=0)
if columns:
styler_mi.set_sticky(axis=1)
left_css = (
"#T_ {0} {{\n position: sticky;\n background-color: inherit;\n"
" left: {1}px;\n min-width: 75px;\n max-width: 75px;\n z-index: {2};\n}}"
)
top_css = (
"#T_ {0} {{\n position: sticky;\n background-color: inherit;\n"
" top: {1}px;\n height: 25px;\n z-index: {2};\n}}"
)
res = styler_mi.set_uuid("").to_html()
# test the index stickys for thead and tbody over both levels
assert (
left_css.format("thead tr th:nth-child(1)", "0", "3 !important") in res
) is index
assert (left_css.format("tbody tr th.level0", "0", "1") in res) is index
assert (
left_css.format("thead tr th:nth-child(2)", "75", "3 !important") in res
) is index
assert (left_css.format("tbody tr th.level1", "75", "1") in res) is index
# test the column stickys for each level row
assert (top_css.format("thead tr:nth-child(1) th", "0", "2") in res) is columns
assert (top_css.format("thead tr:nth-child(2) th", "25", "2") in res) is columns
@pytest.mark.parametrize("index", [False, True])
@pytest.mark.parametrize("columns", [False, True])
@pytest.mark.parametrize("levels", [[1], ["one"], "one"])
def test_sticky_levels(styler_mi, index, columns, levels):
styler_mi.index.names, styler_mi.columns.names = ["zero", "one"], ["zero", "one"]
if index:
styler_mi.set_sticky(axis=0, levels=levels)
if columns:
styler_mi.set_sticky(axis=1, levels=levels)
left_css = (
"#T_ {0} {{\n position: sticky;\n background-color: inherit;\n"
" left: {1}px;\n min-width: 75px;\n max-width: 75px;\n z-index: {2};\n}}"
)
top_css = (
"#T_ {0} {{\n position: sticky;\n background-color: inherit;\n"
" top: {1}px;\n height: 25px;\n z-index: {2};\n}}"
)
res = styler_mi.set_uuid("").to_html()
# test no sticking of level0
assert "#T_ thead tr th:nth-child(1)" not in res
assert "#T_ tbody tr th.level0" not in res
assert "#T_ thead tr:nth-child(1) th" not in res
# test sticking level1
assert (
left_css.format("thead tr th:nth-child(2)", "0", "3 !important") in res
) is index
assert (left_css.format("tbody tr th.level1", "0", "1") in res) is index
assert (top_css.format("thead tr:nth-child(2) th", "0", "2") in res) is columns
def test_sticky_raises(styler):
with pytest.raises(ValueError, match="No axis named bad for object type DataFrame"):
styler.set_sticky(axis="bad")
@pytest.mark.parametrize(
"sparse_index, sparse_columns",
[(True, True), (True, False), (False, True), (False, False)],
)
def test_sparse_options(sparse_index, sparse_columns):
cidx = MultiIndex.from_tuples([("Z", "a"), ("Z", "b"), ("Y", "c")])
ridx = MultiIndex.from_tuples([("A", "a"), ("A", "b"), ("B", "c")])
df = DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]], index=ridx, columns=cidx)
styler = df.style
default_html = styler.to_html() # defaults under pd.options to (True , True)
with option_context(
"styler.sparse.index", sparse_index, "styler.sparse.columns", sparse_columns
):
html1 = styler.to_html()
assert (html1 == default_html) is (sparse_index and sparse_columns)
html2 = styler.to_html(sparse_index=sparse_index, sparse_columns=sparse_columns)
assert html1 == html2
@pytest.mark.parametrize("index", [True, False])
@pytest.mark.parametrize("columns", [True, False])
def test_applymap_header_cell_ids(styler, index, columns):
# GH 41893
func = lambda v: "attr: val;"
styler.uuid, styler.cell_ids = "", False
if index:
styler.applymap_index(func, axis="index")
if columns:
styler.applymap_index(func, axis="columns")
result = styler.to_html()
# test no data cell ids
assert '
2.610000 | ' in result
assert '2.690000 | ' in result
# test index header ids where needed and css styles
assert (
'a | ' in result
) is index
assert (
'b | ' in result
) is index
assert ("#T__level0_row0, #T__level0_row1 {\n attr: val;\n}" in result) is index
# test column header ids where needed and css styles
assert (
'A | ' in result
) is columns
assert ("#T__level0_col0 {\n attr: val;\n}" in result) is columns
@pytest.mark.parametrize("rows", [True, False])
@pytest.mark.parametrize("cols", [True, False])
def test_maximums(styler_mi, rows, cols):
result = styler_mi.to_html(
max_rows=2 if rows else None,
max_columns=2 if cols else None,
)
assert ">5" in result # [[0,1], [4,5]] always visible
assert (">8" in result) is not rows # first trimmed vertical element
assert (">2" in result) is not cols # first trimmed horizontal element
def test_replaced_css_class_names():
css = {
"row_heading": "ROWHEAD",
# "col_heading": "COLHEAD",
"index_name": "IDXNAME",
# "col": "COL",
"row": "ROW",
# "col_trim": "COLTRIM",
"row_trim": "ROWTRIM",
"level": "LEVEL",
"data": "DATA",
"blank": "BLANK",
}
midx = MultiIndex.from_product([["a", "b"], ["c", "d"]])
styler_mi = Styler(
DataFrame(np.arange(16).reshape(4, 4), index=midx, columns=midx),
uuid_len=0,
).set_table_styles(css_class_names=css)
styler_mi.index.names = ["n1", "n2"]
styler_mi.hide(styler_mi.index[1:], axis=0)
styler_mi.hide(styler_mi.columns[1:], axis=1)
styler_mi.applymap_index(lambda v: "color: red;", axis=0)
styler_mi.applymap_index(lambda v: "color: green;", axis=1)
styler_mi.applymap(lambda v: "color: blue;")
expected = dedent(
"""\
"""
)
result = styler_mi.to_html()
assert result == expected
def test_include_css_style_rules_only_for_visible_cells(styler_mi):
# GH 43619
result = (
styler_mi.set_uuid("")
.applymap(lambda v: "color: blue;")
.hide(styler_mi.data.columns[1:], axis="columns")
.hide(styler_mi.data.index[1:], axis="index")
.to_html()
)
expected_styles = dedent(
"""\
"""
)
assert expected_styles in result
def test_include_css_style_rules_only_for_visible_index_labels(styler_mi):
# GH 43619
result = (
styler_mi.set_uuid("")
.applymap_index(lambda v: "color: blue;", axis="index")
.hide(styler_mi.data.columns, axis="columns")
.hide(styler_mi.data.index[1:], axis="index")
.to_html()
)
expected_styles = dedent(
"""\
"""
)
assert expected_styles in result
def test_include_css_style_rules_only_for_visible_column_labels(styler_mi):
# GH 43619
result = (
styler_mi.set_uuid("")
.applymap_index(lambda v: "color: blue;", axis="columns")
.hide(styler_mi.data.columns[1:], axis="columns")
.hide(styler_mi.data.index, axis="index")
.to_html()
)
expected_styles = dedent(
"""\
"""
)
assert expected_styles in result
def test_hiding_index_columns_multiindex_alignment():
# gh 43644
midx = MultiIndex.from_product(
[["i0", "j0"], ["i1"], ["i2", "j2"]], names=["i-0", "i-1", "i-2"]
)
cidx = MultiIndex.from_product(
[["c0"], ["c1", "d1"], ["c2", "d2"]], names=["c-0", "c-1", "c-2"]
)
df = DataFrame(np.arange(16).reshape(4, 4), index=midx, columns=cidx)
styler = Styler(df, uuid_len=0)
styler.hide(level=1, axis=0).hide(level=0, axis=1)
styler.hide([("j0", "i1", "j2")], axis=0)
styler.hide([("c0", "d1", "d2")], axis=1)
result = styler.to_html()
expected = dedent(
"""\
| |
c-1 |
c1 |
d1 |
| |
c-2 |
c2 |
d2 |
c2 |
| i-0 |
i-2 |
|
|
|
| i0 |
i2 |
0 |
1 |
2 |
| j2 |
4 |
5 |
6 |
| j0 |
i2 |
8 |
9 |
10 |
"""
)
assert result == expected
def test_hiding_index_columns_multiindex_trimming():
# gh 44272
df = DataFrame(np.arange(64).reshape(8, 8))
df.columns = MultiIndex.from_product([[0, 1, 2, 3], [0, 1]])
df.index = MultiIndex.from_product([[0, 1, 2, 3], [0, 1]])
df.index.names, df.columns.names = ["a", "b"], ["c", "d"]
styler = Styler(df, cell_ids=False, uuid_len=0)
styler.hide([(0, 0), (0, 1), (1, 0)], axis=1).hide([(0, 0), (0, 1), (1, 0)], axis=0)
with option_context("styler.render.max_rows", 4, "styler.render.max_columns", 4):
result = styler.to_html()
expected = dedent(
"""\
| |
c |
1 |
2 |
3 |
| |
d |
1 |
0 |
1 |
0 |
... |
| a |
b |
|
|
|
|
|
| 1 |
1 |
27 |
28 |
29 |
30 |
... |
| 2 |
0 |
35 |
36 |
37 |
38 |
... |
| 1 |
43 |
44 |
45 |
46 |
... |
| 3 |
0 |
51 |
52 |
53 |
54 |
... |
| ... |
... |
... |
... |
... |
... |
... |
"""
)
assert result == expected
@pytest.mark.parametrize("type", ["data", "index"])
@pytest.mark.parametrize(
"text, exp, found",
[
("no link, just text", False, ""),
("subdomain not www: sub.web.com", False, ""),
("www subdomain: www.web.com other", True, "www.web.com"),
("scheme full structure: http://www.web.com", True, "http://www.web.com"),
("scheme no top-level: http://www.web", True, "http://www.web"),
("no scheme, no top-level: www.web", False, "www.web"),
("https scheme: https://www.web.com", True, "https://www.web.com"),
("ftp scheme: ftp://www.web", True, "ftp://www.web"),
("ftps scheme: ftps://www.web", True, "ftps://www.web"),
("subdirectories: www.web.com/directory", True, "www.web.com/directory"),
("Multiple domains: www.1.2.3.4", True, "www.1.2.3.4"),
("with port: http://web.com:80", True, "http://web.com:80"),
(
"full net_loc scheme: http://user:pass@web.com",
True,
"http://user:pass@web.com",
),
(
"with valid special chars: http://web.com/,.':;~!@#$*()[]",
True,
"http://web.com/,.':;~!@#$*()[]",
),
],
)
def test_rendered_links(type, text, exp, found):
if type == "data":
df = DataFrame([text])
styler = df.style.format(hyperlinks="html")
else:
df = DataFrame([0], index=[text])
styler = df.style.format_index(hyperlinks="html")
rendered = f'{found}'
result = styler.to_html()
assert (rendered in result) is exp
assert (text in result) is not exp # test conversion done when expected and not
def test_multiple_rendered_links():
links = ("www.a.b", "http://a.c", "https://a.d", "ftp://a.e")
# pylint: disable-next=consider-using-f-string
df = DataFrame(["text {} {} text {} {}".format(*links)])
result = df.style.format(hyperlinks="html").to_html()
href = '{0}'
for link in links:
assert href.format(link) in result
assert href.format("text") not in result
def test_concat(styler):
other = styler.data.agg(["mean"]).style
styler.concat(other).set_uuid("X")
result = styler.to_html()
fp = "foot0_"
expected = dedent(
f"""\
| b |
2.690000 |
| mean |
2.650000 |
"""
)
assert expected in result
def test_concat_recursion(styler):
df = styler.data
styler1 = styler
styler2 = Styler(df.agg(["mean"]), precision=3)
styler3 = Styler(df.agg(["mean"]), precision=4)
styler1.concat(styler2.concat(styler3)).set_uuid("X")
result = styler.to_html()
# notice that the second concat (last of the output html),
# there are two `foot_` in the id and class
fp1 = "foot0_"
fp2 = "foot0_foot0_"
expected = dedent(
f"""\
| b |
2.690000 |
| mean |
2.650 |
| mean |
2.6500 |
"""
)
assert expected in result
def test_concat_chain(styler):
df = styler.data
styler1 = styler
styler2 = Styler(df.agg(["mean"]), precision=3)
styler3 = Styler(df.agg(["mean"]), precision=4)
styler1.concat(styler2).concat(styler3).set_uuid("X")
result = styler.to_html()
fp1 = "foot0_"
fp2 = "foot1_"
expected = dedent(
f"""\
| b |
2.690000 |
| mean |
2.650 |
| mean |
2.6500 |
"""
)
assert expected in result
def test_concat_combined():
def html_lines(foot_prefix: str):
assert foot_prefix.endswith("_") or foot_prefix == ""
fp = foot_prefix
return indent(
dedent(
f"""\
| a |
2.610000 |
| b |
2.690000 |
"""
),
prefix=" " * 4,
)
df = DataFrame([[2.61], [2.69]], index=["a", "b"], columns=["A"])
s1 = df.style.highlight_max(color="red")
s2 = df.style.highlight_max(color="green")
s3 = df.style.highlight_max(color="blue")
s4 = df.style.highlight_max(color="yellow")
result = s1.concat(s2).concat(s3.concat(s4)).set_uuid("X").to_html()
expected_css = dedent(
"""\
"""
)
expected_table = (
dedent(
"""\
| |
A |
"""
)
+ html_lines("")
+ html_lines("foot0_")
+ html_lines("foot1_")
+ html_lines("foot1_foot0_")
+ dedent(
"""\
"""
)
)
assert expected_css + expected_table == result
def test_to_html_na_rep_non_scalar_data(datapath):
# GH47103
df = DataFrame([dict(a=1, b=[1, 2, 3], c=np.nan)])
result = df.style.format(na_rep="-").to_html(table_uuid="test")
expected = """\
"""
assert result == expected