diff --git a/pandas/tests/io/formats/style/test_align.py b/pandas/tests/io/formats/style/test_align.py index be7d2fc3be518..7451d9d2369ab 100644 --- a/pandas/tests/io/formats/style/test_align.py +++ b/pandas/tests/io/formats/style/test_align.py @@ -23,258 +23,20 @@ def no_bar(): return bar_grad() -def bar_to(x): - return bar_grad(f" #d65f5f {x:.1f}%", f" transparent {x:.1f}%") +def bar_to(x, color="#d65f5f"): + return bar_grad(f" {color} {x:.1f}%", f" transparent {x:.1f}%") -def bar_from_to(x, y): +def bar_from_to(x, y, color="#d65f5f"): return bar_grad( f" transparent {x:.1f}%", - f" #d65f5f {x:.1f}%", - f" #d65f5f {y:.1f}%", + f" {color} {x:.1f}%", + f" {color} {y:.1f}%", f" transparent {y:.1f}%", ) class TestStylerBarAlign: - def test_bar_align_left(self): - df = DataFrame({"A": [0, 1, 2]}) - result = df.style.bar(align="left")._compute().ctx - expected = { - (0, 0): bar_grad(), - (1, 0): bar_grad(" #d65f5f 50.0%", " transparent 50.0%"), - (2, 0): bar_grad(" #d65f5f 100.0%", " transparent 100.0%"), - } - assert result == expected - - result = df.style.bar(color="red", width=50, align="left")._compute().ctx - expected = { - (0, 0): bar_grad(), - (1, 0): bar_grad(" red 25.0%", " transparent 25.0%"), - (2, 0): bar_grad(" red 50.0%", " transparent 50.0%"), - } - assert result == expected - - df["C"] = ["a"] * len(df) - result = df.style.bar(color="red", width=50, align="left")._compute().ctx - assert result == expected - df["C"] = df["C"].astype("category") - result = df.style.bar(color="red", width=50, align="left")._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(align="left")._compute().ctx - expected = { - (0, 0): bar_grad(), - (0, 1): bar_grad(), - (0, 2): bar_grad(), - (1, 0): bar_grad(" #d65f5f 50.0%", " transparent 50.0%"), - (1, 1): bar_grad(" #d65f5f 50.0%", " transparent 50.0%"), - (1, 2): bar_grad(" #d65f5f 50.0%", " transparent 50.0%"), - (2, 0): bar_grad(" #d65f5f 100.0%", " transparent 100.0%"), - (2, 1): bar_grad(" #d65f5f 100.0%", " transparent 100.0%"), - (2, 2): bar_grad(" #d65f5f 100.0%", " transparent 100.0%"), - } - assert result == expected - - result = df.style.bar(axis=1, align="left")._compute().ctx - expected = { - (0, 0): bar_grad(), - (0, 1): bar_grad(" #d65f5f 50.0%", " transparent 50.0%"), - (0, 2): bar_grad(" #d65f5f 100.0%", " transparent 100.0%"), - (1, 0): bar_grad(), - (1, 1): bar_grad(" #d65f5f 50.0%", " transparent 50.0%"), - (1, 2): bar_grad(" #d65f5f 100.0%", " transparent 100.0%"), - (2, 0): bar_grad(), - (2, 1): bar_grad(" #d65f5f 50.0%", " transparent 50.0%"), - (2, 2): bar_grad(" #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): bar_grad( - " #d65f5f 10.0%", - " transparent 10.0%", - ), - (1, 0): bar_grad(), - (2, 0): bar_grad( - " transparent 10.0%", - " #5fba7d 10.0%", - " #5fba7d 30.0%", - " transparent 30.0%", - ), - (3, 0): bar_grad( - " 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): bar_grad( - " #5fba7d 10.0%", - " transparent 10.0%", - ), - (1, 0): bar_grad( - " #5fba7d 20.0%", - " transparent 20.0%", - ), - (2, 0): bar_grad( - " #5fba7d 50.0%", - " transparent 50.0%", - ), - (3, 0): bar_grad( - " #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): bar_grad( - " #d65f5f 100.0%", - " transparent 100.0%", - ), - (1, 0): bar_grad( - " transparent 40.0%", - " #d65f5f 40.0%", - " #d65f5f 100.0%", - " transparent 100.0%", - ), - (2, 0): bar_grad( - " transparent 70.0%", - " #d65f5f 70.0%", - " #d65f5f 100.0%", - " transparent 100.0%", - ), - (3, 0): bar_grad( - " 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): bar_grad( - " transparent 40.0%", - " #d65f5f 40.0%", - " #d65f5f 45.0%", - " transparent 45.0%", - ), - (1, 0): bar_grad(), - (2, 0): bar_grad( - " transparent 45.0%", - " #5fba7d 45.0%", - " #5fba7d 55.0%", - " transparent 55.0%", - ), - (3, 0): bar_grad( - " 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, align="left")._compute().ctx - expected = { - (0, 0): bar_grad(), - (1, 0): bar_grad( - " #d65f5f 25.0%", - " transparent 25.0%", - ), - (0, 1): bar_grad( - " #d65f5f 50.0%", - " transparent 50.0%", - ), - (1, 1): bar_grad( - " #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): bar_grad(), - (1, 0): bar_grad( - " transparent 50.0%", - " #d65f5f 50.0%", - " #d65f5f 62.5%", - " transparent 62.5%", - ), - (0, 1): bar_grad( - " transparent 25.0%", - " #d65f5f 25.0%", - " #d65f5f 50.0%", - " transparent 50.0%", - ), - (1, 1): bar_grad( - " 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): bar_grad(), - (1, 0): bar_grad( - " transparent 33.3%", - " #d65f5f 33.3%", - " #d65f5f 50.0%", - " transparent 50.0%", - ), - (0, 1): bar_grad( - " #d65f5f 33.3%", - " transparent 33.3%", - ), - (1, 1): bar_grad( - " 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 @@ -438,7 +200,7 @@ def test_bar_bad_align_raises(self): (np.median, [bar_to(50), no_bar(), bar_from_to(50, 100)]), ], ) -def test_bar_align_positive_cases(align, exp): +def test_align_positive_cases(align, exp): # test different align cases for all positive values data = DataFrame([[1], [2], [3]]) result = data.style.bar(align=align)._compute().ctx @@ -458,7 +220,7 @@ def test_bar_align_positive_cases(align, exp): (np.median, [bar_from_to(50, 100), no_bar(), bar_to(50)]), ], ) -def test_bar_align_negative_cases(align, exp): +def test_align_negative_cases(align, exp): # test different align cases for all negative values data = DataFrame([[-1], [-2], [-3]]) result = data.style.bar(align=align)._compute().ctx @@ -478,9 +240,105 @@ def test_bar_align_negative_cases(align, exp): (np.median, [bar_to(50), no_bar(), bar_from_to(50, 62.5)]), ], ) -def test_bar_align_mixed_cases(align, exp): +def test_align_mixed_cases(align, exp): # test different align cases for mixed positive and negative values data = DataFrame([[-3], [1], [2]]) result = data.style.bar(align=align)._compute().ctx expected = {(0, 0): exp[0], (1, 0): exp[1], (2, 0): exp[2]} assert result == expected + + +@pytest.mark.parametrize( + "align, exp", + [ + ( + "left", + { + "index": [[no_bar(), no_bar()], [bar_to(100), bar_to(100)]], + "columns": [[no_bar(), bar_to(100)], [no_bar(), bar_to(100)]], + "none": [[no_bar(), bar_to(33.33)], [bar_to(66.66), bar_to(100)]], + }, + ), + ( + "mid", + { + "index": [[bar_to(33.33), bar_to(50)], [bar_to(100), bar_to(100)]], + "columns": [[bar_to(50), bar_to(100)], [bar_to(75), bar_to(100)]], + "none": [[bar_to(25), bar_to(50)], [bar_to(75), bar_to(100)]], + }, + ), + ( + "zero", + { + "index": [ + [bar_from_to(50, 66.66), bar_from_to(50, 75)], + [bar_from_to(50, 100), bar_from_to(50, 100)], + ], + "columns": [ + [bar_from_to(50, 75), bar_from_to(50, 100)], + [bar_from_to(50, 87.5), bar_from_to(50, 100)], + ], + "none": [ + [bar_from_to(50, 62.5), bar_from_to(50, 75)], + [bar_from_to(50, 87.5), bar_from_to(50, 100)], + ], + }, + ), + ( + 2, + { + "index": [ + [bar_to(50), no_bar()], + [bar_from_to(50, 100), bar_from_to(50, 100)], + ], + "columns": [ + [bar_to(50), no_bar()], + [bar_from_to(50, 75), bar_from_to(50, 100)], + ], + "none": [ + [bar_from_to(25, 50), no_bar()], + [bar_from_to(50, 75), bar_from_to(50, 100)], + ], + }, + ), + ], +) +@pytest.mark.parametrize("axis", ["index", "columns", "none"]) +def test_align_axis(align, exp, axis): + # test all axis combinations with positive values and different aligns + data = DataFrame([[1, 2], [3, 4]]) + result = ( + data.style.bar(align=align, axis=None if axis == "none" else axis) + ._compute() + .ctx + ) + expected = { + (0, 0): exp[axis][0][0], + (0, 1): exp[axis][0][1], + (1, 0): exp[axis][1][0], + (1, 1): exp[axis][1][1], + } + assert result == expected + + +def test_numerics(): + # test data is pre-selected for numeric values + data = DataFrame([[1, "a"], [2, "b"]]) + result = data.style.bar()._compute().ctx + assert (0, 1) not in result + assert (1, 1) not in result + + +@pytest.mark.parametrize( + "align, exp", + [ + ("left", [no_bar(), bar_to(100, "green")]), + ("right", [bar_to(100, "red"), no_bar()]), + ("mid", [bar_to(25, "red"), bar_from_to(25, 100, "green")]), + ("zero", [bar_from_to(33.33, 50, "red"), bar_from_to(50, 100, "green")]), + ], +) +def test_colors_mixed(align, exp): + data = DataFrame([[-1], [3]]) + result = data.style.bar(align=align, color=["red", "green"])._compute().ctx + assert result == {(0, 0): exp[0], (1, 0): exp[1]}