Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 2dfb2dd

Browse files
authoredNov 20, 2023
TST: collect Interval tests (#56072)
* TST: collect scalar Interval tests * TST: collect IntervalIndex tests * TST: collect Interval tests
1 parent 0994ecd commit 2dfb2dd

File tree

12 files changed

+545
-536
lines changed

12 files changed

+545
-536
lines changed
 

‎pandas/tests/arrays/interval/test_interval.py

Lines changed: 0 additions & 175 deletions
Original file line numberDiff line numberDiff line change
@@ -229,178 +229,3 @@ def test_min_max(self, left_right_dtypes, index_or_series_or_array):
229229
res = arr_na.max(skipna=True)
230230
assert res == MAX
231231
assert type(res) == type(MAX)
232-
233-
234-
# ----------------------------------------------------------------------------
235-
# Arrow interaction
236-
237-
238-
def test_arrow_extension_type():
239-
pa = pytest.importorskip("pyarrow")
240-
241-
from pandas.core.arrays.arrow.extension_types import ArrowIntervalType
242-
243-
p1 = ArrowIntervalType(pa.int64(), "left")
244-
p2 = ArrowIntervalType(pa.int64(), "left")
245-
p3 = ArrowIntervalType(pa.int64(), "right")
246-
247-
assert p1.closed == "left"
248-
assert p1 == p2
249-
assert p1 != p3
250-
assert hash(p1) == hash(p2)
251-
assert hash(p1) != hash(p3)
252-
253-
254-
def test_arrow_array():
255-
pa = pytest.importorskip("pyarrow")
256-
257-
from pandas.core.arrays.arrow.extension_types import ArrowIntervalType
258-
259-
intervals = pd.interval_range(1, 5, freq=1).array
260-
261-
result = pa.array(intervals)
262-
assert isinstance(result.type, ArrowIntervalType)
263-
assert result.type.closed == intervals.closed
264-
assert result.type.subtype == pa.int64()
265-
assert result.storage.field("left").equals(pa.array([1, 2, 3, 4], type="int64"))
266-
assert result.storage.field("right").equals(pa.array([2, 3, 4, 5], type="int64"))
267-
268-
expected = pa.array([{"left": i, "right": i + 1} for i in range(1, 5)])
269-
assert result.storage.equals(expected)
270-
271-
# convert to its storage type
272-
result = pa.array(intervals, type=expected.type)
273-
assert result.equals(expected)
274-
275-
# unsupported conversions
276-
with pytest.raises(TypeError, match="Not supported to convert IntervalArray"):
277-
pa.array(intervals, type="float64")
278-
279-
with pytest.raises(TypeError, match="Not supported to convert IntervalArray"):
280-
pa.array(intervals, type=ArrowIntervalType(pa.float64(), "left"))
281-
282-
283-
def test_arrow_array_missing():
284-
pa = pytest.importorskip("pyarrow")
285-
286-
from pandas.core.arrays.arrow.extension_types import ArrowIntervalType
287-
288-
arr = IntervalArray.from_breaks([0.0, 1.0, 2.0, 3.0])
289-
arr[1] = None
290-
291-
result = pa.array(arr)
292-
assert isinstance(result.type, ArrowIntervalType)
293-
assert result.type.closed == arr.closed
294-
assert result.type.subtype == pa.float64()
295-
296-
# fields have missing values (not NaN)
297-
left = pa.array([0.0, None, 2.0], type="float64")
298-
right = pa.array([1.0, None, 3.0], type="float64")
299-
assert result.storage.field("left").equals(left)
300-
assert result.storage.field("right").equals(right)
301-
302-
# structarray itself also has missing values on the array level
303-
vals = [
304-
{"left": 0.0, "right": 1.0},
305-
{"left": None, "right": None},
306-
{"left": 2.0, "right": 3.0},
307-
]
308-
expected = pa.StructArray.from_pandas(vals, mask=np.array([False, True, False]))
309-
assert result.storage.equals(expected)
310-
311-
312-
@pytest.mark.filterwarnings(
313-
"ignore:Passing a BlockManager to DataFrame:DeprecationWarning"
314-
)
315-
@pytest.mark.parametrize(
316-
"breaks",
317-
[[0.0, 1.0, 2.0, 3.0], date_range("2017", periods=4, freq="D")],
318-
ids=["float", "datetime64[ns]"],
319-
)
320-
def test_arrow_table_roundtrip(breaks):
321-
pa = pytest.importorskip("pyarrow")
322-
323-
from pandas.core.arrays.arrow.extension_types import ArrowIntervalType
324-
325-
arr = IntervalArray.from_breaks(breaks)
326-
arr[1] = None
327-
df = pd.DataFrame({"a": arr})
328-
329-
table = pa.table(df)
330-
assert isinstance(table.field("a").type, ArrowIntervalType)
331-
result = table.to_pandas()
332-
assert isinstance(result["a"].dtype, pd.IntervalDtype)
333-
tm.assert_frame_equal(result, df)
334-
335-
table2 = pa.concat_tables([table, table])
336-
result = table2.to_pandas()
337-
expected = pd.concat([df, df], ignore_index=True)
338-
tm.assert_frame_equal(result, expected)
339-
340-
# GH-41040
341-
table = pa.table(
342-
[pa.chunked_array([], type=table.column(0).type)], schema=table.schema
343-
)
344-
result = table.to_pandas()
345-
tm.assert_frame_equal(result, expected[0:0])
346-
347-
348-
@pytest.mark.filterwarnings(
349-
"ignore:Passing a BlockManager to DataFrame:DeprecationWarning"
350-
)
351-
@pytest.mark.parametrize(
352-
"breaks",
353-
[[0.0, 1.0, 2.0, 3.0], date_range("2017", periods=4, freq="D")],
354-
ids=["float", "datetime64[ns]"],
355-
)
356-
def test_arrow_table_roundtrip_without_metadata(breaks):
357-
pa = pytest.importorskip("pyarrow")
358-
359-
arr = IntervalArray.from_breaks(breaks)
360-
arr[1] = None
361-
df = pd.DataFrame({"a": arr})
362-
363-
table = pa.table(df)
364-
# remove the metadata
365-
table = table.replace_schema_metadata()
366-
assert table.schema.metadata is None
367-
368-
result = table.to_pandas()
369-
assert isinstance(result["a"].dtype, pd.IntervalDtype)
370-
tm.assert_frame_equal(result, df)
371-
372-
373-
def test_from_arrow_from_raw_struct_array():
374-
# in case pyarrow lost the Interval extension type (eg on parquet roundtrip
375-
# with datetime64[ns] subtype, see GH-45881), still allow conversion
376-
# from arrow to IntervalArray
377-
pa = pytest.importorskip("pyarrow")
378-
379-
arr = pa.array([{"left": 0, "right": 1}, {"left": 1, "right": 2}])
380-
dtype = pd.IntervalDtype(np.dtype("int64"), closed="neither")
381-
382-
result = dtype.__from_arrow__(arr)
383-
expected = IntervalArray.from_breaks(
384-
np.array([0, 1, 2], dtype="int64"), closed="neither"
385-
)
386-
tm.assert_extension_array_equal(result, expected)
387-
388-
result = dtype.__from_arrow__(pa.chunked_array([arr]))
389-
tm.assert_extension_array_equal(result, expected)
390-
391-
392-
@pytest.mark.parametrize("timezone", ["UTC", "US/Pacific", "GMT"])
393-
def test_interval_index_subtype(timezone, inclusive_endpoints_fixture):
394-
# GH 46999
395-
dates = date_range("2022", periods=3, tz=timezone)
396-
dtype = f"interval[datetime64[ns, {timezone}], {inclusive_endpoints_fixture}]"
397-
result = IntervalIndex.from_arrays(
398-
["2022-01-01", "2022-01-02"],
399-
["2022-01-02", "2022-01-03"],
400-
closed=inclusive_endpoints_fixture,
401-
dtype=dtype,
402-
)
403-
expected = IntervalIndex.from_arrays(
404-
dates[:-1], dates[1:], closed=inclusive_endpoints_fixture
405-
)
406-
tm.assert_index_equal(result, expected)
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import numpy as np
2+
import pytest
3+
4+
import pandas as pd
5+
import pandas._testing as tm
6+
from pandas.core.arrays import IntervalArray
7+
8+
9+
def test_arrow_extension_type():
10+
pa = pytest.importorskip("pyarrow")
11+
12+
from pandas.core.arrays.arrow.extension_types import ArrowIntervalType
13+
14+
p1 = ArrowIntervalType(pa.int64(), "left")
15+
p2 = ArrowIntervalType(pa.int64(), "left")
16+
p3 = ArrowIntervalType(pa.int64(), "right")
17+
18+
assert p1.closed == "left"
19+
assert p1 == p2
20+
assert p1 != p3
21+
assert hash(p1) == hash(p2)
22+
assert hash(p1) != hash(p3)
23+
24+
25+
def test_arrow_array():
26+
pa = pytest.importorskip("pyarrow")
27+
28+
from pandas.core.arrays.arrow.extension_types import ArrowIntervalType
29+
30+
intervals = pd.interval_range(1, 5, freq=1).array
31+
32+
result = pa.array(intervals)
33+
assert isinstance(result.type, ArrowIntervalType)
34+
assert result.type.closed == intervals.closed
35+
assert result.type.subtype == pa.int64()
36+
assert result.storage.field("left").equals(pa.array([1, 2, 3, 4], type="int64"))
37+
assert result.storage.field("right").equals(pa.array([2, 3, 4, 5], type="int64"))
38+
39+
expected = pa.array([{"left": i, "right": i + 1} for i in range(1, 5)])
40+
assert result.storage.equals(expected)
41+
42+
# convert to its storage type
43+
result = pa.array(intervals, type=expected.type)
44+
assert result.equals(expected)
45+
46+
# unsupported conversions
47+
with pytest.raises(TypeError, match="Not supported to convert IntervalArray"):
48+
pa.array(intervals, type="float64")
49+
50+
with pytest.raises(TypeError, match="Not supported to convert IntervalArray"):
51+
pa.array(intervals, type=ArrowIntervalType(pa.float64(), "left"))
52+
53+
54+
def test_arrow_array_missing():
55+
pa = pytest.importorskip("pyarrow")
56+
57+
from pandas.core.arrays.arrow.extension_types import ArrowIntervalType
58+
59+
arr = IntervalArray.from_breaks([0.0, 1.0, 2.0, 3.0])
60+
arr[1] = None
61+
62+
result = pa.array(arr)
63+
assert isinstance(result.type, ArrowIntervalType)
64+
assert result.type.closed == arr.closed
65+
assert result.type.subtype == pa.float64()
66+
67+
# fields have missing values (not NaN)
68+
left = pa.array([0.0, None, 2.0], type="float64")
69+
right = pa.array([1.0, None, 3.0], type="float64")
70+
assert result.storage.field("left").equals(left)
71+
assert result.storage.field("right").equals(right)
72+
73+
# structarray itself also has missing values on the array level
74+
vals = [
75+
{"left": 0.0, "right": 1.0},
76+
{"left": None, "right": None},
77+
{"left": 2.0, "right": 3.0},
78+
]
79+
expected = pa.StructArray.from_pandas(vals, mask=np.array([False, True, False]))
80+
assert result.storage.equals(expected)
81+
82+
83+
@pytest.mark.filterwarnings(
84+
"ignore:Passing a BlockManager to DataFrame:DeprecationWarning"
85+
)
86+
@pytest.mark.parametrize(
87+
"breaks",
88+
[[0.0, 1.0, 2.0, 3.0], pd.date_range("2017", periods=4, freq="D")],
89+
ids=["float", "datetime64[ns]"],
90+
)
91+
def test_arrow_table_roundtrip(breaks):
92+
pa = pytest.importorskip("pyarrow")
93+
94+
from pandas.core.arrays.arrow.extension_types import ArrowIntervalType
95+
96+
arr = IntervalArray.from_breaks(breaks)
97+
arr[1] = None
98+
df = pd.DataFrame({"a": arr})
99+
100+
table = pa.table(df)
101+
assert isinstance(table.field("a").type, ArrowIntervalType)
102+
result = table.to_pandas()
103+
assert isinstance(result["a"].dtype, pd.IntervalDtype)
104+
tm.assert_frame_equal(result, df)
105+
106+
table2 = pa.concat_tables([table, table])
107+
result = table2.to_pandas()
108+
expected = pd.concat([df, df], ignore_index=True)
109+
tm.assert_frame_equal(result, expected)
110+
111+
# GH#41040
112+
table = pa.table(
113+
[pa.chunked_array([], type=table.column(0).type)], schema=table.schema
114+
)
115+
result = table.to_pandas()
116+
tm.assert_frame_equal(result, expected[0:0])
117+
118+
119+
@pytest.mark.filterwarnings(
120+
"ignore:Passing a BlockManager to DataFrame:DeprecationWarning"
121+
)
122+
@pytest.mark.parametrize(
123+
"breaks",
124+
[[0.0, 1.0, 2.0, 3.0], pd.date_range("2017", periods=4, freq="D")],
125+
ids=["float", "datetime64[ns]"],
126+
)
127+
def test_arrow_table_roundtrip_without_metadata(breaks):
128+
pa = pytest.importorskip("pyarrow")
129+
130+
arr = IntervalArray.from_breaks(breaks)
131+
arr[1] = None
132+
df = pd.DataFrame({"a": arr})
133+
134+
table = pa.table(df)
135+
# remove the metadata
136+
table = table.replace_schema_metadata()
137+
assert table.schema.metadata is None
138+
139+
result = table.to_pandas()
140+
assert isinstance(result["a"].dtype, pd.IntervalDtype)
141+
tm.assert_frame_equal(result, df)
142+
143+
144+
def test_from_arrow_from_raw_struct_array():
145+
# in case pyarrow lost the Interval extension type (eg on parquet roundtrip
146+
# with datetime64[ns] subtype, see GH-45881), still allow conversion
147+
# from arrow to IntervalArray
148+
pa = pytest.importorskip("pyarrow")
149+
150+
arr = pa.array([{"left": 0, "right": 1}, {"left": 1, "right": 2}])
151+
dtype = pd.IntervalDtype(np.dtype("int64"), closed="neither")
152+
153+
result = dtype.__from_arrow__(arr)
154+
expected = IntervalArray.from_breaks(
155+
np.array([0, 1, 2], dtype="int64"), closed="neither"
156+
)
157+
tm.assert_extension_array_equal(result, expected)
158+
159+
result = dtype.__from_arrow__(pa.chunked_array([arr]))
160+
tm.assert_extension_array_equal(result, expected)

‎pandas/tests/indexes/interval/test_base.py

Lines changed: 0 additions & 56 deletions
This file was deleted.

‎pandas/tests/indexes/interval/test_constructors.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,23 @@ def test_index_mixed_closed(self):
488488
tm.assert_index_equal(result, expected)
489489

490490

491+
@pytest.mark.parametrize("timezone", ["UTC", "US/Pacific", "GMT"])
492+
def test_interval_index_subtype(timezone, inclusive_endpoints_fixture):
493+
# GH#46999
494+
dates = date_range("2022", periods=3, tz=timezone)
495+
dtype = f"interval[datetime64[ns, {timezone}], {inclusive_endpoints_fixture}]"
496+
result = IntervalIndex.from_arrays(
497+
["2022-01-01", "2022-01-02"],
498+
["2022-01-02", "2022-01-03"],
499+
closed=inclusive_endpoints_fixture,
500+
dtype=dtype,
501+
)
502+
expected = IntervalIndex.from_arrays(
503+
dates[:-1], dates[1:], closed=inclusive_endpoints_fixture
504+
)
505+
tm.assert_index_equal(result, expected)
506+
507+
491508
def test_dtype_closed_mismatch():
492509
# GH#38394 closed specified in both dtype and IntervalIndex constructor
493510

‎pandas/tests/indexes/interval/test_indexing.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,75 @@
1919
array,
2020
date_range,
2121
interval_range,
22+
isna,
2223
period_range,
2324
timedelta_range,
2425
)
2526
import pandas._testing as tm
2627

2728

29+
class TestGetItem:
30+
def test_getitem(self, closed):
31+
idx = IntervalIndex.from_arrays((0, 1, np.nan), (1, 2, np.nan), closed=closed)
32+
assert idx[0] == Interval(0.0, 1.0, closed=closed)
33+
assert idx[1] == Interval(1.0, 2.0, closed=closed)
34+
assert isna(idx[2])
35+
36+
result = idx[0:1]
37+
expected = IntervalIndex.from_arrays((0.0,), (1.0,), closed=closed)
38+
tm.assert_index_equal(result, expected)
39+
40+
result = idx[0:2]
41+
expected = IntervalIndex.from_arrays((0.0, 1), (1.0, 2.0), closed=closed)
42+
tm.assert_index_equal(result, expected)
43+
44+
result = idx[1:3]
45+
expected = IntervalIndex.from_arrays(
46+
(1.0, np.nan), (2.0, np.nan), closed=closed
47+
)
48+
tm.assert_index_equal(result, expected)
49+
50+
def test_getitem_2d_deprecated(self):
51+
# GH#30588 multi-dim indexing is deprecated, but raising is also acceptable
52+
idx = IntervalIndex.from_breaks(range(11), closed="right")
53+
with pytest.raises(ValueError, match="multi-dimensional indexing not allowed"):
54+
idx[:, None]
55+
with pytest.raises(ValueError, match="multi-dimensional indexing not allowed"):
56+
# GH#44051
57+
idx[True]
58+
with pytest.raises(ValueError, match="multi-dimensional indexing not allowed"):
59+
# GH#44051
60+
idx[False]
61+
62+
63+
class TestWhere:
64+
def test_where(self, listlike_box):
65+
klass = listlike_box
66+
67+
idx = IntervalIndex.from_breaks(range(11), closed="right")
68+
cond = [True] * len(idx)
69+
expected = idx
70+
result = expected.where(klass(cond))
71+
tm.assert_index_equal(result, expected)
72+
73+
cond = [False] + [True] * len(idx[1:])
74+
expected = IntervalIndex([np.nan] + idx[1:].tolist())
75+
result = idx.where(klass(cond))
76+
tm.assert_index_equal(result, expected)
77+
78+
79+
class TestTake:
80+
def test_take(self, closed):
81+
index = IntervalIndex.from_breaks(range(11), closed=closed)
82+
83+
result = index.take(range(10))
84+
tm.assert_index_equal(result, index)
85+
86+
result = index.take([0, 0, 1])
87+
expected = IntervalIndex.from_arrays([0, 0, 1], [1, 1, 2], closed=closed)
88+
tm.assert_index_equal(result, expected)
89+
90+
2891
class TestGetLoc:
2992
@pytest.mark.parametrize("side", ["right", "left", "both", "neither"])
3093
def test_get_loc_interval(self, closed, side):

‎pandas/tests/indexes/interval/test_interval.py

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -341,26 +341,6 @@ def test_is_monotonic_with_nans(self):
341341
assert not index._is_strictly_monotonic_decreasing
342342
assert not index.is_monotonic_decreasing
343343

344-
def test_get_item(self, closed):
345-
i = IntervalIndex.from_arrays((0, 1, np.nan), (1, 2, np.nan), closed=closed)
346-
assert i[0] == Interval(0.0, 1.0, closed=closed)
347-
assert i[1] == Interval(1.0, 2.0, closed=closed)
348-
assert isna(i[2])
349-
350-
result = i[0:1]
351-
expected = IntervalIndex.from_arrays((0.0,), (1.0,), closed=closed)
352-
tm.assert_index_equal(result, expected)
353-
354-
result = i[0:2]
355-
expected = IntervalIndex.from_arrays((0.0, 1), (1.0, 2.0), closed=closed)
356-
tm.assert_index_equal(result, expected)
357-
358-
result = i[1:3]
359-
expected = IntervalIndex.from_arrays(
360-
(1.0, np.nan), (2.0, np.nan), closed=closed
361-
)
362-
tm.assert_index_equal(result, expected)
363-
364344
@pytest.mark.parametrize(
365345
"breaks",
366346
[

‎pandas/tests/scalar/interval/test_arithmetic.py

Lines changed: 170 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,56 +8,185 @@
88
Timedelta,
99
Timestamp,
1010
)
11+
import pandas._testing as tm
1112

1213

13-
@pytest.mark.parametrize("method", ["__add__", "__sub__"])
14-
@pytest.mark.parametrize(
15-
"interval",
16-
[
17-
Interval(Timestamp("2017-01-01 00:00:00"), Timestamp("2018-01-01 00:00:00")),
18-
Interval(Timedelta(days=7), Timedelta(days=14)),
19-
],
20-
)
21-
@pytest.mark.parametrize(
22-
"delta", [Timedelta(days=7), timedelta(7), np.timedelta64(7, "D")]
23-
)
24-
def test_time_interval_add_subtract_timedelta(interval, delta, method):
25-
# https://github.com/pandas-dev/pandas/issues/32023
26-
result = getattr(interval, method)(delta)
27-
left = getattr(interval.left, method)(delta)
28-
right = getattr(interval.right, method)(delta)
29-
expected = Interval(left, right)
14+
class TestIntervalArithmetic:
15+
def test_interval_add(self, closed):
16+
interval = Interval(0, 1, closed=closed)
17+
expected = Interval(1, 2, closed=closed)
3018

31-
assert result == expected
19+
result = interval + 1
20+
assert result == expected
3221

22+
result = 1 + interval
23+
assert result == expected
3324

34-
@pytest.mark.parametrize("interval", [Interval(1, 2), Interval(1.0, 2.0)])
35-
@pytest.mark.parametrize(
36-
"delta", [Timedelta(days=7), timedelta(7), np.timedelta64(7, "D")]
37-
)
38-
def test_numeric_interval_add_timedelta_raises(interval, delta):
39-
# https://github.com/pandas-dev/pandas/issues/32023
40-
msg = "|".join(
25+
result = interval
26+
result += 1
27+
assert result == expected
28+
29+
msg = r"unsupported operand type\(s\) for \+"
30+
with pytest.raises(TypeError, match=msg):
31+
interval + interval
32+
33+
with pytest.raises(TypeError, match=msg):
34+
interval + "foo"
35+
36+
def test_interval_sub(self, closed):
37+
interval = Interval(0, 1, closed=closed)
38+
expected = Interval(-1, 0, closed=closed)
39+
40+
result = interval - 1
41+
assert result == expected
42+
43+
result = interval
44+
result -= 1
45+
assert result == expected
46+
47+
msg = r"unsupported operand type\(s\) for -"
48+
with pytest.raises(TypeError, match=msg):
49+
interval - interval
50+
51+
with pytest.raises(TypeError, match=msg):
52+
interval - "foo"
53+
54+
def test_interval_mult(self, closed):
55+
interval = Interval(0, 1, closed=closed)
56+
expected = Interval(0, 2, closed=closed)
57+
58+
result = interval * 2
59+
assert result == expected
60+
61+
result = 2 * interval
62+
assert result == expected
63+
64+
result = interval
65+
result *= 2
66+
assert result == expected
67+
68+
msg = r"unsupported operand type\(s\) for \*"
69+
with pytest.raises(TypeError, match=msg):
70+
interval * interval
71+
72+
msg = r"can\'t multiply sequence by non-int"
73+
with pytest.raises(TypeError, match=msg):
74+
interval * "foo"
75+
76+
def test_interval_div(self, closed):
77+
interval = Interval(0, 1, closed=closed)
78+
expected = Interval(0, 0.5, closed=closed)
79+
80+
result = interval / 2.0
81+
assert result == expected
82+
83+
result = interval
84+
result /= 2.0
85+
assert result == expected
86+
87+
msg = r"unsupported operand type\(s\) for /"
88+
with pytest.raises(TypeError, match=msg):
89+
interval / interval
90+
91+
with pytest.raises(TypeError, match=msg):
92+
interval / "foo"
93+
94+
def test_interval_floordiv(self, closed):
95+
interval = Interval(1, 2, closed=closed)
96+
expected = Interval(0, 1, closed=closed)
97+
98+
result = interval // 2
99+
assert result == expected
100+
101+
result = interval
102+
result //= 2
103+
assert result == expected
104+
105+
msg = r"unsupported operand type\(s\) for //"
106+
with pytest.raises(TypeError, match=msg):
107+
interval // interval
108+
109+
with pytest.raises(TypeError, match=msg):
110+
interval // "foo"
111+
112+
@pytest.mark.parametrize("method", ["__add__", "__sub__"])
113+
@pytest.mark.parametrize(
114+
"interval",
41115
[
42-
"unsupported operand",
43-
"cannot use operands",
44-
"Only numeric, Timestamp and Timedelta endpoints are allowed",
45-
]
116+
Interval(
117+
Timestamp("2017-01-01 00:00:00"), Timestamp("2018-01-01 00:00:00")
118+
),
119+
Interval(Timedelta(days=7), Timedelta(days=14)),
120+
],
46121
)
47-
with pytest.raises((TypeError, ValueError), match=msg):
48-
interval + delta
122+
@pytest.mark.parametrize(
123+
"delta", [Timedelta(days=7), timedelta(7), np.timedelta64(7, "D")]
124+
)
125+
def test_time_interval_add_subtract_timedelta(self, interval, delta, method):
126+
# https://github.com/pandas-dev/pandas/issues/32023
127+
result = getattr(interval, method)(delta)
128+
left = getattr(interval.left, method)(delta)
129+
right = getattr(interval.right, method)(delta)
130+
expected = Interval(left, right)
131+
132+
assert result == expected
133+
134+
@pytest.mark.parametrize("interval", [Interval(1, 2), Interval(1.0, 2.0)])
135+
@pytest.mark.parametrize(
136+
"delta", [Timedelta(days=7), timedelta(7), np.timedelta64(7, "D")]
137+
)
138+
def test_numeric_interval_add_timedelta_raises(self, interval, delta):
139+
# https://github.com/pandas-dev/pandas/issues/32023
140+
msg = "|".join(
141+
[
142+
"unsupported operand",
143+
"cannot use operands",
144+
"Only numeric, Timestamp and Timedelta endpoints are allowed",
145+
]
146+
)
147+
with pytest.raises((TypeError, ValueError), match=msg):
148+
interval + delta
149+
150+
with pytest.raises((TypeError, ValueError), match=msg):
151+
delta + interval
152+
153+
@pytest.mark.parametrize("klass", [timedelta, np.timedelta64, Timedelta])
154+
def test_timedelta_add_timestamp_interval(self, klass):
155+
delta = klass(0)
156+
expected = Interval(Timestamp("2020-01-01"), Timestamp("2020-02-01"))
157+
158+
result = delta + expected
159+
assert result == expected
160+
161+
result = expected + delta
162+
assert result == expected
49163

50-
with pytest.raises((TypeError, ValueError), match=msg):
51-
delta + interval
52164

165+
class TestIntervalComparisons:
166+
def test_interval_equal(self):
167+
assert Interval(0, 1) == Interval(0, 1, closed="right")
168+
assert Interval(0, 1) != Interval(0, 1, closed="left")
169+
assert Interval(0, 1) != 0
53170

54-
@pytest.mark.parametrize("klass", [timedelta, np.timedelta64, Timedelta])
55-
def test_timedelta_add_timestamp_interval(klass):
56-
delta = klass(0)
57-
expected = Interval(Timestamp("2020-01-01"), Timestamp("2020-02-01"))
171+
def test_interval_comparison(self):
172+
msg = (
173+
"'<' not supported between instances of "
174+
"'pandas._libs.interval.Interval' and 'int'"
175+
)
176+
with pytest.raises(TypeError, match=msg):
177+
Interval(0, 1) < 2
58178

59-
result = delta + expected
60-
assert result == expected
179+
assert Interval(0, 1) < Interval(1, 2)
180+
assert Interval(0, 1) < Interval(0, 2)
181+
assert Interval(0, 1) < Interval(0.5, 1.5)
182+
assert Interval(0, 1) <= Interval(0, 1)
183+
assert Interval(0, 1) > Interval(-1, 2)
184+
assert Interval(0, 1) >= Interval(0, 1)
61185

62-
result = expected + delta
63-
assert result == expected
186+
def test_equality_comparison_broadcasts_over_array(self):
187+
# https://github.com/pandas-dev/pandas/issues/35931
188+
interval = Interval(0, 1)
189+
arr = np.array([interval, interval])
190+
result = interval == arr
191+
expected = np.array([True, True])
192+
tm.assert_numpy_array_equal(result, expected)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import pytest
2+
3+
from pandas import (
4+
Interval,
5+
Period,
6+
Timestamp,
7+
)
8+
9+
10+
class TestIntervalConstructors:
11+
@pytest.mark.parametrize(
12+
"left, right",
13+
[
14+
("a", "z"),
15+
(("a", "b"), ("c", "d")),
16+
(list("AB"), list("ab")),
17+
(Interval(0, 1), Interval(1, 2)),
18+
(Period("2018Q1", freq="Q"), Period("2018Q1", freq="Q")),
19+
],
20+
)
21+
def test_construct_errors(self, left, right):
22+
# GH#23013
23+
msg = "Only numeric, Timestamp and Timedelta endpoints are allowed"
24+
with pytest.raises(ValueError, match=msg):
25+
Interval(left, right)
26+
27+
def test_constructor_errors(self):
28+
msg = "invalid option for 'closed': foo"
29+
with pytest.raises(ValueError, match=msg):
30+
Interval(0, 1, closed="foo")
31+
32+
msg = "left side of interval must be <= right side"
33+
with pytest.raises(ValueError, match=msg):
34+
Interval(1, 0)
35+
36+
@pytest.mark.parametrize(
37+
"tz_left, tz_right", [(None, "UTC"), ("UTC", None), ("UTC", "US/Eastern")]
38+
)
39+
def test_constructor_errors_tz(self, tz_left, tz_right):
40+
# GH#18538
41+
left = Timestamp("2017-01-01", tz=tz_left)
42+
right = Timestamp("2017-01-02", tz=tz_right)
43+
44+
if tz_left is None or tz_right is None:
45+
error = TypeError
46+
msg = "Cannot compare tz-naive and tz-aware timestamps"
47+
else:
48+
error = ValueError
49+
msg = "left and right must have the same time zone"
50+
with pytest.raises(error, match=msg):
51+
Interval(left, right)
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import pytest
2+
3+
from pandas import (
4+
Interval,
5+
Timedelta,
6+
Timestamp,
7+
)
8+
9+
10+
class TestContains:
11+
def test_contains(self):
12+
interval = Interval(0, 1)
13+
assert 0.5 in interval
14+
assert 1 in interval
15+
assert 0 not in interval
16+
17+
interval_both = Interval(0, 1, "both")
18+
assert 0 in interval_both
19+
assert 1 in interval_both
20+
21+
interval_neither = Interval(0, 1, closed="neither")
22+
assert 0 not in interval_neither
23+
assert 0.5 in interval_neither
24+
assert 1 not in interval_neither
25+
26+
def test_contains_interval(self, inclusive_endpoints_fixture):
27+
interval1 = Interval(0, 1, "both")
28+
interval2 = Interval(0, 1, inclusive_endpoints_fixture)
29+
assert interval1 in interval1
30+
assert interval2 in interval2
31+
assert interval2 in interval1
32+
assert interval1 not in interval2 or inclusive_endpoints_fixture == "both"
33+
34+
def test_contains_infinite_length(self):
35+
interval1 = Interval(0, 1, "both")
36+
interval2 = Interval(float("-inf"), float("inf"), "neither")
37+
assert interval1 in interval2
38+
assert interval2 not in interval1
39+
40+
def test_contains_zero_length(self):
41+
interval1 = Interval(0, 1, "both")
42+
interval2 = Interval(-1, -1, "both")
43+
interval3 = Interval(0.5, 0.5, "both")
44+
assert interval2 not in interval1
45+
assert interval3 in interval1
46+
assert interval2 not in interval3 and interval3 not in interval2
47+
assert interval1 not in interval2 and interval1 not in interval3
48+
49+
@pytest.mark.parametrize(
50+
"type1",
51+
[
52+
(0, 1),
53+
(Timestamp(2000, 1, 1, 0), Timestamp(2000, 1, 1, 1)),
54+
(Timedelta("0h"), Timedelta("1h")),
55+
],
56+
)
57+
@pytest.mark.parametrize(
58+
"type2",
59+
[
60+
(0, 1),
61+
(Timestamp(2000, 1, 1, 0), Timestamp(2000, 1, 1, 1)),
62+
(Timedelta("0h"), Timedelta("1h")),
63+
],
64+
)
65+
def test_contains_mixed_types(self, type1, type2):
66+
interval1 = Interval(*type1)
67+
interval2 = Interval(*type2)
68+
if type1 == type2:
69+
assert interval1 in interval2
70+
else:
71+
msg = "^'<=' not supported between instances of"
72+
with pytest.raises(TypeError, match=msg):
73+
interval1 in interval2
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from pandas import Interval
2+
3+
4+
def test_interval_repr():
5+
interval = Interval(0, 1)
6+
assert repr(interval) == "Interval(0, 1, closed='right')"
7+
assert str(interval) == "(0, 1]"
8+
9+
interval_left = Interval(0, 1, closed="left")
10+
assert repr(interval_left) == "Interval(0, 1, closed='left')"
11+
assert str(interval_left) == "[0, 1)"

‎pandas/tests/scalar/interval/test_interval.py

Lines changed: 0 additions & 192 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@
33

44
from pandas import (
55
Interval,
6-
Period,
76
Timedelta,
87
Timestamp,
98
)
10-
import pandas._testing as tm
11-
import pandas.core.common as com
129

1310

1411
@pytest.fixture
@@ -23,48 +20,6 @@ def test_properties(self, interval):
2320
assert interval.right == 1
2421
assert interval.mid == 0.5
2522

26-
def test_repr(self, interval):
27-
assert repr(interval) == "Interval(0, 1, closed='right')"
28-
assert str(interval) == "(0, 1]"
29-
30-
interval_left = Interval(0, 1, closed="left")
31-
assert repr(interval_left) == "Interval(0, 1, closed='left')"
32-
assert str(interval_left) == "[0, 1)"
33-
34-
def test_contains(self, interval):
35-
assert 0.5 in interval
36-
assert 1 in interval
37-
assert 0 not in interval
38-
39-
interval_both = Interval(0, 1, "both")
40-
assert 0 in interval_both
41-
assert 1 in interval_both
42-
43-
interval_neither = Interval(0, 1, closed="neither")
44-
assert 0 not in interval_neither
45-
assert 0.5 in interval_neither
46-
assert 1 not in interval_neither
47-
48-
def test_equal(self):
49-
assert Interval(0, 1) == Interval(0, 1, closed="right")
50-
assert Interval(0, 1) != Interval(0, 1, closed="left")
51-
assert Interval(0, 1) != 0
52-
53-
def test_comparison(self):
54-
msg = (
55-
"'<' not supported between instances of "
56-
"'pandas._libs.interval.Interval' and 'int'"
57-
)
58-
with pytest.raises(TypeError, match=msg):
59-
Interval(0, 1) < 2
60-
61-
assert Interval(0, 1) < Interval(1, 2)
62-
assert Interval(0, 1) < Interval(0, 2)
63-
assert Interval(0, 1) < Interval(0.5, 1.5)
64-
assert Interval(0, 1) <= Interval(0, 1)
65-
assert Interval(0, 1) > Interval(-1, 2)
66-
assert Interval(0, 1) >= Interval(0, 1)
67-
6823
def test_hash(self, interval):
6924
# should not raise
7025
hash(interval)
@@ -130,150 +85,3 @@ def test_is_empty(self, left, right, closed):
13085
result = iv.is_empty
13186
expected = closed != "both"
13287
assert result is expected
133-
134-
@pytest.mark.parametrize(
135-
"left, right",
136-
[
137-
("a", "z"),
138-
(("a", "b"), ("c", "d")),
139-
(list("AB"), list("ab")),
140-
(Interval(0, 1), Interval(1, 2)),
141-
(Period("2018Q1", freq="Q"), Period("2018Q1", freq="Q")),
142-
],
143-
)
144-
def test_construct_errors(self, left, right):
145-
# GH 23013
146-
msg = "Only numeric, Timestamp and Timedelta endpoints are allowed"
147-
with pytest.raises(ValueError, match=msg):
148-
Interval(left, right)
149-
150-
def test_math_add(self, closed):
151-
interval = Interval(0, 1, closed=closed)
152-
expected = Interval(1, 2, closed=closed)
153-
154-
result = interval + 1
155-
assert result == expected
156-
157-
result = 1 + interval
158-
assert result == expected
159-
160-
result = interval
161-
result += 1
162-
assert result == expected
163-
164-
msg = r"unsupported operand type\(s\) for \+"
165-
with pytest.raises(TypeError, match=msg):
166-
interval + interval
167-
168-
with pytest.raises(TypeError, match=msg):
169-
interval + "foo"
170-
171-
def test_math_sub(self, closed):
172-
interval = Interval(0, 1, closed=closed)
173-
expected = Interval(-1, 0, closed=closed)
174-
175-
result = interval - 1
176-
assert result == expected
177-
178-
result = interval
179-
result -= 1
180-
assert result == expected
181-
182-
msg = r"unsupported operand type\(s\) for -"
183-
with pytest.raises(TypeError, match=msg):
184-
interval - interval
185-
186-
with pytest.raises(TypeError, match=msg):
187-
interval - "foo"
188-
189-
def test_math_mult(self, closed):
190-
interval = Interval(0, 1, closed=closed)
191-
expected = Interval(0, 2, closed=closed)
192-
193-
result = interval * 2
194-
assert result == expected
195-
196-
result = 2 * interval
197-
assert result == expected
198-
199-
result = interval
200-
result *= 2
201-
assert result == expected
202-
203-
msg = r"unsupported operand type\(s\) for \*"
204-
with pytest.raises(TypeError, match=msg):
205-
interval * interval
206-
207-
msg = r"can\'t multiply sequence by non-int"
208-
with pytest.raises(TypeError, match=msg):
209-
interval * "foo"
210-
211-
def test_math_div(self, closed):
212-
interval = Interval(0, 1, closed=closed)
213-
expected = Interval(0, 0.5, closed=closed)
214-
215-
result = interval / 2.0
216-
assert result == expected
217-
218-
result = interval
219-
result /= 2.0
220-
assert result == expected
221-
222-
msg = r"unsupported operand type\(s\) for /"
223-
with pytest.raises(TypeError, match=msg):
224-
interval / interval
225-
226-
with pytest.raises(TypeError, match=msg):
227-
interval / "foo"
228-
229-
def test_math_floordiv(self, closed):
230-
interval = Interval(1, 2, closed=closed)
231-
expected = Interval(0, 1, closed=closed)
232-
233-
result = interval // 2
234-
assert result == expected
235-
236-
result = interval
237-
result //= 2
238-
assert result == expected
239-
240-
msg = r"unsupported operand type\(s\) for //"
241-
with pytest.raises(TypeError, match=msg):
242-
interval // interval
243-
244-
with pytest.raises(TypeError, match=msg):
245-
interval // "foo"
246-
247-
def test_constructor_errors(self):
248-
msg = "invalid option for 'closed': foo"
249-
with pytest.raises(ValueError, match=msg):
250-
Interval(0, 1, closed="foo")
251-
252-
msg = "left side of interval must be <= right side"
253-
with pytest.raises(ValueError, match=msg):
254-
Interval(1, 0)
255-
256-
@pytest.mark.parametrize(
257-
"tz_left, tz_right", [(None, "UTC"), ("UTC", None), ("UTC", "US/Eastern")]
258-
)
259-
def test_constructor_errors_tz(self, tz_left, tz_right):
260-
# GH 18538
261-
left = Timestamp("2017-01-01", tz=tz_left)
262-
right = Timestamp("2017-01-02", tz=tz_right)
263-
264-
if com.any_none(tz_left, tz_right):
265-
error = TypeError
266-
msg = "Cannot compare tz-naive and tz-aware timestamps"
267-
else:
268-
error = ValueError
269-
msg = "left and right must have the same time zone"
270-
with pytest.raises(error, match=msg):
271-
Interval(left, right)
272-
273-
def test_equality_comparison_broadcasts_over_array(self):
274-
# https://github.com/pandas-dev/pandas/issues/35931
275-
interval = Interval(0, 1)
276-
arr = np.array([interval, interval])
277-
result = interval == arr
278-
expected = np.array([True, True])
279-
tm.assert_numpy_array_equal(result, expected)

‎pandas/tests/scalar/interval/test_ops.py renamed to ‎pandas/tests/scalar/interval/test_overlaps.py

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
"""Tests for Interval-Interval operations, such as overlaps, contains, etc."""
21
import pytest
32

43
from pandas import (
@@ -66,54 +65,3 @@ def test_overlaps_invalid_type(self, other):
6665
msg = f"`other` must be an Interval, got {type(other).__name__}"
6766
with pytest.raises(TypeError, match=msg):
6867
interval.overlaps(other)
69-
70-
71-
class TestContains:
72-
def test_contains_interval(self, inclusive_endpoints_fixture):
73-
interval1 = Interval(0, 1, "both")
74-
interval2 = Interval(0, 1, inclusive_endpoints_fixture)
75-
assert interval1 in interval1
76-
assert interval2 in interval2
77-
assert interval2 in interval1
78-
assert interval1 not in interval2 or inclusive_endpoints_fixture == "both"
79-
80-
def test_contains_infinite_length(self):
81-
interval1 = Interval(0, 1, "both")
82-
interval2 = Interval(float("-inf"), float("inf"), "neither")
83-
assert interval1 in interval2
84-
assert interval2 not in interval1
85-
86-
def test_contains_zero_length(self):
87-
interval1 = Interval(0, 1, "both")
88-
interval2 = Interval(-1, -1, "both")
89-
interval3 = Interval(0.5, 0.5, "both")
90-
assert interval2 not in interval1
91-
assert interval3 in interval1
92-
assert interval2 not in interval3 and interval3 not in interval2
93-
assert interval1 not in interval2 and interval1 not in interval3
94-
95-
@pytest.mark.parametrize(
96-
"type1",
97-
[
98-
(0, 1),
99-
(Timestamp(2000, 1, 1, 0), Timestamp(2000, 1, 1, 1)),
100-
(Timedelta("0h"), Timedelta("1h")),
101-
],
102-
)
103-
@pytest.mark.parametrize(
104-
"type2",
105-
[
106-
(0, 1),
107-
(Timestamp(2000, 1, 1, 0), Timestamp(2000, 1, 1, 1)),
108-
(Timedelta("0h"), Timedelta("1h")),
109-
],
110-
)
111-
def test_contains_mixed_types(self, type1, type2):
112-
interval1 = Interval(*type1)
113-
interval2 = Interval(*type2)
114-
if type1 == type2:
115-
assert interval1 in interval2
116-
else:
117-
msg = "^'<=' not supported between instances of"
118-
with pytest.raises(TypeError, match=msg):
119-
interval1 in interval2

0 commit comments

Comments
 (0)
Please sign in to comment.