test_interval.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. import numpy as np
  2. import pytest
  3. from pandas import (
  4. Interval,
  5. Period,
  6. Timedelta,
  7. Timestamp,
  8. )
  9. import pandas._testing as tm
  10. import pandas.core.common as com
  11. @pytest.fixture
  12. def interval():
  13. return Interval(0, 1)
  14. class TestInterval:
  15. def test_properties(self, interval):
  16. assert interval.closed == "right"
  17. assert interval.left == 0
  18. assert interval.right == 1
  19. assert interval.mid == 0.5
  20. def test_repr(self, interval):
  21. assert repr(interval) == "Interval(0, 1, closed='right')"
  22. assert str(interval) == "(0, 1]"
  23. interval_left = Interval(0, 1, closed="left")
  24. assert repr(interval_left) == "Interval(0, 1, closed='left')"
  25. assert str(interval_left) == "[0, 1)"
  26. def test_contains(self, interval):
  27. assert 0.5 in interval
  28. assert 1 in interval
  29. assert 0 not in interval
  30. interval_both = Interval(0, 1, "both")
  31. assert 0 in interval_both
  32. assert 1 in interval_both
  33. interval_neither = Interval(0, 1, closed="neither")
  34. assert 0 not in interval_neither
  35. assert 0.5 in interval_neither
  36. assert 1 not in interval_neither
  37. def test_equal(self):
  38. assert Interval(0, 1) == Interval(0, 1, closed="right")
  39. assert Interval(0, 1) != Interval(0, 1, closed="left")
  40. assert Interval(0, 1) != 0
  41. def test_comparison(self):
  42. msg = (
  43. "'<' not supported between instances of "
  44. "'pandas._libs.interval.Interval' and 'int'"
  45. )
  46. with pytest.raises(TypeError, match=msg):
  47. Interval(0, 1) < 2
  48. assert Interval(0, 1) < Interval(1, 2)
  49. assert Interval(0, 1) < Interval(0, 2)
  50. assert Interval(0, 1) < Interval(0.5, 1.5)
  51. assert Interval(0, 1) <= Interval(0, 1)
  52. assert Interval(0, 1) > Interval(-1, 2)
  53. assert Interval(0, 1) >= Interval(0, 1)
  54. def test_hash(self, interval):
  55. # should not raise
  56. hash(interval)
  57. @pytest.mark.parametrize(
  58. "left, right, expected",
  59. [
  60. (0, 5, 5),
  61. (-2, 5.5, 7.5),
  62. (10, 10, 0),
  63. (10, np.inf, np.inf),
  64. (-np.inf, -5, np.inf),
  65. (-np.inf, np.inf, np.inf),
  66. (Timedelta("0 days"), Timedelta("5 days"), Timedelta("5 days")),
  67. (Timedelta("10 days"), Timedelta("10 days"), Timedelta("0 days")),
  68. (Timedelta("1H10min"), Timedelta("5H5min"), Timedelta("3H55min")),
  69. (Timedelta("5S"), Timedelta("1H"), Timedelta("59min55S")),
  70. ],
  71. )
  72. def test_length(self, left, right, expected):
  73. # GH 18789
  74. iv = Interval(left, right)
  75. result = iv.length
  76. assert result == expected
  77. @pytest.mark.parametrize(
  78. "left, right, expected",
  79. [
  80. ("2017-01-01", "2017-01-06", "5 days"),
  81. ("2017-01-01", "2017-01-01 12:00:00", "12 hours"),
  82. ("2017-01-01 12:00", "2017-01-01 12:00:00", "0 days"),
  83. ("2017-01-01 12:01", "2017-01-05 17:31:00", "4 days 5 hours 30 min"),
  84. ],
  85. )
  86. @pytest.mark.parametrize("tz", (None, "UTC", "CET", "US/Eastern"))
  87. def test_length_timestamp(self, tz, left, right, expected):
  88. # GH 18789
  89. iv = Interval(Timestamp(left, tz=tz), Timestamp(right, tz=tz))
  90. result = iv.length
  91. expected = Timedelta(expected)
  92. assert result == expected
  93. @pytest.mark.parametrize(
  94. "left, right",
  95. [
  96. (0, 1),
  97. (Timedelta("0 days"), Timedelta("1 day")),
  98. (Timestamp("2018-01-01"), Timestamp("2018-01-02")),
  99. (
  100. Timestamp("2018-01-01", tz="US/Eastern"),
  101. Timestamp("2018-01-02", tz="US/Eastern"),
  102. ),
  103. ],
  104. )
  105. def test_is_empty(self, left, right, closed):
  106. # GH27219
  107. # non-empty always return False
  108. iv = Interval(left, right, closed)
  109. assert iv.is_empty is False
  110. # same endpoint is empty except when closed='both' (contains one point)
  111. iv = Interval(left, left, closed)
  112. result = iv.is_empty
  113. expected = closed != "both"
  114. assert result is expected
  115. @pytest.mark.parametrize(
  116. "left, right",
  117. [
  118. ("a", "z"),
  119. (("a", "b"), ("c", "d")),
  120. (list("AB"), list("ab")),
  121. (Interval(0, 1), Interval(1, 2)),
  122. (Period("2018Q1", freq="Q"), Period("2018Q1", freq="Q")),
  123. ],
  124. )
  125. def test_construct_errors(self, left, right):
  126. # GH 23013
  127. msg = "Only numeric, Timestamp and Timedelta endpoints are allowed"
  128. with pytest.raises(ValueError, match=msg):
  129. Interval(left, right)
  130. def test_math_add(self, closed):
  131. interval = Interval(0, 1, closed=closed)
  132. expected = Interval(1, 2, closed=closed)
  133. result = interval + 1
  134. assert result == expected
  135. result = 1 + interval
  136. assert result == expected
  137. result = interval
  138. result += 1
  139. assert result == expected
  140. msg = r"unsupported operand type\(s\) for \+"
  141. with pytest.raises(TypeError, match=msg):
  142. interval + interval
  143. with pytest.raises(TypeError, match=msg):
  144. interval + "foo"
  145. def test_math_sub(self, closed):
  146. interval = Interval(0, 1, closed=closed)
  147. expected = Interval(-1, 0, closed=closed)
  148. result = interval - 1
  149. assert result == expected
  150. result = interval
  151. result -= 1
  152. assert result == expected
  153. msg = r"unsupported operand type\(s\) for -"
  154. with pytest.raises(TypeError, match=msg):
  155. interval - interval
  156. with pytest.raises(TypeError, match=msg):
  157. interval - "foo"
  158. def test_math_mult(self, closed):
  159. interval = Interval(0, 1, closed=closed)
  160. expected = Interval(0, 2, closed=closed)
  161. result = interval * 2
  162. assert result == expected
  163. result = 2 * interval
  164. assert result == expected
  165. result = interval
  166. result *= 2
  167. assert result == expected
  168. msg = r"unsupported operand type\(s\) for \*"
  169. with pytest.raises(TypeError, match=msg):
  170. interval * interval
  171. msg = r"can\'t multiply sequence by non-int"
  172. with pytest.raises(TypeError, match=msg):
  173. interval * "foo"
  174. def test_math_div(self, closed):
  175. interval = Interval(0, 1, closed=closed)
  176. expected = Interval(0, 0.5, closed=closed)
  177. result = interval / 2.0
  178. assert result == expected
  179. result = interval
  180. result /= 2.0
  181. assert result == expected
  182. msg = r"unsupported operand type\(s\) for /"
  183. with pytest.raises(TypeError, match=msg):
  184. interval / interval
  185. with pytest.raises(TypeError, match=msg):
  186. interval / "foo"
  187. def test_math_floordiv(self, closed):
  188. interval = Interval(1, 2, closed=closed)
  189. expected = Interval(0, 1, closed=closed)
  190. result = interval // 2
  191. assert result == expected
  192. result = interval
  193. result //= 2
  194. assert result == expected
  195. msg = r"unsupported operand type\(s\) for //"
  196. with pytest.raises(TypeError, match=msg):
  197. interval // interval
  198. with pytest.raises(TypeError, match=msg):
  199. interval // "foo"
  200. def test_constructor_errors(self):
  201. msg = "invalid option for 'closed': foo"
  202. with pytest.raises(ValueError, match=msg):
  203. Interval(0, 1, closed="foo")
  204. msg = "left side of interval must be <= right side"
  205. with pytest.raises(ValueError, match=msg):
  206. Interval(1, 0)
  207. @pytest.mark.parametrize(
  208. "tz_left, tz_right", [(None, "UTC"), ("UTC", None), ("UTC", "US/Eastern")]
  209. )
  210. def test_constructor_errors_tz(self, tz_left, tz_right):
  211. # GH 18538
  212. left = Timestamp("2017-01-01", tz=tz_left)
  213. right = Timestamp("2017-01-02", tz=tz_right)
  214. if com.any_none(tz_left, tz_right):
  215. error = TypeError
  216. msg = "Cannot compare tz-naive and tz-aware timestamps"
  217. else:
  218. error = ValueError
  219. msg = "left and right must have the same time zone"
  220. with pytest.raises(error, match=msg):
  221. Interval(left, right)
  222. def test_equality_comparison_broadcasts_over_array(self):
  223. # https://github.com/pandas-dev/pandas/issues/35931
  224. interval = Interval(0, 1)
  225. arr = np.array([interval, interval])
  226. result = interval == arr
  227. expected = np.array([True, True])
  228. tm.assert_numpy_array_equal(result, expected)