test_highlight.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. import numpy as np
  2. import pytest
  3. from pandas import (
  4. NA,
  5. DataFrame,
  6. IndexSlice,
  7. )
  8. pytest.importorskip("jinja2")
  9. from pandas.io.formats.style import Styler
  10. @pytest.fixture(params=[(None, "float64"), (NA, "Int64")])
  11. def df(request):
  12. # GH 45804
  13. return DataFrame(
  14. {"A": [0, np.nan, 10], "B": [1, request.param[0], 2]}, dtype=request.param[1]
  15. )
  16. @pytest.fixture
  17. def styler(df):
  18. return Styler(df, uuid_len=0)
  19. def test_highlight_null(styler):
  20. result = styler.highlight_null()._compute().ctx
  21. expected = {
  22. (1, 0): [("background-color", "red")],
  23. (1, 1): [("background-color", "red")],
  24. }
  25. assert result == expected
  26. def test_highlight_null_subset(styler):
  27. # GH 31345
  28. result = (
  29. styler.highlight_null(color="red", subset=["A"])
  30. .highlight_null(color="green", subset=["B"])
  31. ._compute()
  32. .ctx
  33. )
  34. expected = {
  35. (1, 0): [("background-color", "red")],
  36. (1, 1): [("background-color", "green")],
  37. }
  38. assert result == expected
  39. @pytest.mark.parametrize("f", ["highlight_min", "highlight_max"])
  40. def test_highlight_minmax_basic(df, f):
  41. expected = {
  42. (0, 1): [("background-color", "red")],
  43. # ignores NaN row,
  44. (2, 0): [("background-color", "red")],
  45. }
  46. if f == "highlight_min":
  47. df = -df
  48. result = getattr(df.style, f)(axis=1, color="red")._compute().ctx
  49. assert result == expected
  50. @pytest.mark.parametrize("f", ["highlight_min", "highlight_max"])
  51. @pytest.mark.parametrize(
  52. "kwargs",
  53. [
  54. {"axis": None, "color": "red"}, # test axis
  55. {"axis": 0, "subset": ["A"], "color": "red"}, # test subset and ignores NaN
  56. {"axis": None, "props": "background-color: red"}, # test props
  57. ],
  58. )
  59. def test_highlight_minmax_ext(df, f, kwargs):
  60. expected = {(2, 0): [("background-color", "red")]}
  61. if f == "highlight_min":
  62. df = -df
  63. result = getattr(df.style, f)(**kwargs)._compute().ctx
  64. assert result == expected
  65. @pytest.mark.parametrize("f", ["highlight_min", "highlight_max"])
  66. @pytest.mark.parametrize("axis", [None, 0, 1])
  67. def test_highlight_minmax_nulls(f, axis):
  68. # GH 42750
  69. expected = {
  70. (1, 0): [("background-color", "yellow")],
  71. (1, 1): [("background-color", "yellow")],
  72. }
  73. if axis == 1:
  74. expected.update({(2, 1): [("background-color", "yellow")]})
  75. if f == "highlight_max":
  76. df = DataFrame({"a": [NA, 1, None], "b": [np.nan, 1, -1]})
  77. else:
  78. df = DataFrame({"a": [NA, -1, None], "b": [np.nan, -1, 1]})
  79. result = getattr(df.style, f)(axis=axis)._compute().ctx
  80. assert result == expected
  81. @pytest.mark.parametrize(
  82. "kwargs",
  83. [
  84. {"left": 0, "right": 1}, # test basic range
  85. {"left": 0, "right": 1, "props": "background-color: yellow"}, # test props
  86. {"left": -100, "right": 100, "subset": IndexSlice[[0, 1], :]}, # test subset
  87. {"left": 0, "subset": IndexSlice[[0, 1], :]}, # test no right
  88. {"right": 1}, # test no left
  89. {"left": [0, 0, 11], "axis": 0}, # test left as sequence
  90. {"left": DataFrame({"A": [0, 0, 11], "B": [1, 1, 11]}), "axis": None}, # axis
  91. {"left": 0, "right": [0, 1], "axis": 1}, # test sequence right
  92. ],
  93. )
  94. def test_highlight_between(styler, kwargs):
  95. expected = {
  96. (0, 0): [("background-color", "yellow")],
  97. (0, 1): [("background-color", "yellow")],
  98. }
  99. result = styler.highlight_between(**kwargs)._compute().ctx
  100. assert result == expected
  101. @pytest.mark.parametrize(
  102. "arg, map, axis",
  103. [
  104. ("left", [1, 2], 0), # 0 axis has 3 elements not 2
  105. ("left", [1, 2, 3], 1), # 1 axis has 2 elements not 3
  106. ("left", np.array([[1, 2], [1, 2]]), None), # df is (2,3) not (2,2)
  107. ("right", [1, 2], 0), # same tests as above for 'right' not 'left'
  108. ("right", [1, 2, 3], 1), # ..
  109. ("right", np.array([[1, 2], [1, 2]]), None), # ..
  110. ],
  111. )
  112. def test_highlight_between_raises(arg, styler, map, axis):
  113. msg = f"supplied '{arg}' is not correct shape"
  114. with pytest.raises(ValueError, match=msg):
  115. styler.highlight_between(**{arg: map, "axis": axis})._compute()
  116. def test_highlight_between_raises2(styler):
  117. msg = "values can be 'both', 'left', 'right', or 'neither'"
  118. with pytest.raises(ValueError, match=msg):
  119. styler.highlight_between(inclusive="badstring")._compute()
  120. with pytest.raises(ValueError, match=msg):
  121. styler.highlight_between(inclusive=1)._compute()
  122. @pytest.mark.parametrize(
  123. "inclusive, expected",
  124. [
  125. (
  126. "both",
  127. {
  128. (0, 0): [("background-color", "yellow")],
  129. (0, 1): [("background-color", "yellow")],
  130. },
  131. ),
  132. ("neither", {}),
  133. ("left", {(0, 0): [("background-color", "yellow")]}),
  134. ("right", {(0, 1): [("background-color", "yellow")]}),
  135. ],
  136. )
  137. def test_highlight_between_inclusive(styler, inclusive, expected):
  138. kwargs = {"left": 0, "right": 1, "subset": IndexSlice[[0, 1], :]}
  139. result = styler.highlight_between(**kwargs, inclusive=inclusive)._compute()
  140. assert result.ctx == expected
  141. @pytest.mark.parametrize(
  142. "kwargs",
  143. [
  144. {"q_left": 0.5, "q_right": 1, "axis": 0}, # base case
  145. {"q_left": 0.5, "q_right": 1, "axis": None}, # test axis
  146. {"q_left": 0, "q_right": 1, "subset": IndexSlice[2, :]}, # test subset
  147. {"q_left": 0.5, "axis": 0}, # test no high
  148. {"q_right": 1, "subset": IndexSlice[2, :], "axis": 1}, # test no low
  149. {"q_left": 0.5, "axis": 0, "props": "background-color: yellow"}, # tst prop
  150. ],
  151. )
  152. def test_highlight_quantile(styler, kwargs):
  153. expected = {
  154. (2, 0): [("background-color", "yellow")],
  155. (2, 1): [("background-color", "yellow")],
  156. }
  157. result = styler.highlight_quantile(**kwargs)._compute().ctx
  158. assert result == expected
  159. @pytest.mark.parametrize(
  160. "f,kwargs",
  161. [
  162. ("highlight_min", {"axis": 1, "subset": IndexSlice[1, :]}),
  163. ("highlight_max", {"axis": 0, "subset": [0]}),
  164. ("highlight_quantile", {"axis": None, "q_left": 0.6, "q_right": 0.8}),
  165. ("highlight_between", {"subset": [0]}),
  166. ],
  167. )
  168. @pytest.mark.parametrize(
  169. "df",
  170. [
  171. DataFrame([[0, 10], [20, 30]], dtype=int),
  172. DataFrame([[0, 10], [20, 30]], dtype=float),
  173. DataFrame([[0, 10], [20, 30]], dtype="datetime64[ns]"),
  174. DataFrame([[0, 10], [20, 30]], dtype=str),
  175. DataFrame([[0, 10], [20, 30]], dtype="timedelta64[ns]"),
  176. ],
  177. )
  178. def test_all_highlight_dtypes(f, kwargs, df):
  179. if f == "highlight_quantile" and isinstance(df.iloc[0, 0], (str)):
  180. return None # quantile incompatible with str
  181. if f == "highlight_between":
  182. kwargs["left"] = df.iloc[1, 0] # set the range low for testing
  183. expected = {(1, 0): [("background-color", "yellow")]}
  184. result = getattr(df.style, f)(**kwargs)._compute().ctx
  185. assert result == expected