Skip to content

Commit de50590

Browse files
authored
REF: organize Timedelta, Index tests (#55675)
* Collect Timedelta tests * organize tests * typo fixup * mypy fixup
1 parent f34cf54 commit de50590

File tree

11 files changed

+541
-500
lines changed

11 files changed

+541
-500
lines changed

pandas/tests/indexes/period/test_formats.py

+9
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,15 @@ def test_get_values_for_csv():
5656

5757

5858
class TestPeriodIndexRendering:
59+
def test_format_empty(self):
60+
# GH#35712
61+
empty_idx = PeriodIndex([], freq="Y")
62+
msg = r"PeriodIndex\.format is deprecated"
63+
with tm.assert_produces_warning(FutureWarning, match=msg):
64+
assert empty_idx.format() == []
65+
with tm.assert_produces_warning(FutureWarning, match=msg):
66+
assert empty_idx.format(name=True) == [""]
67+
5968
def test_frame_repr(self):
6069
df = pd.DataFrame({"A": [1, 2, 3]}, index=pd.date_range("2000", periods=3))
6170
result = repr(df)

pandas/tests/indexes/period/test_period.py

-9
Original file line numberDiff line numberDiff line change
@@ -272,15 +272,6 @@ def test_map(self):
272272
exp = Index([x.ordinal for x in index])
273273
tm.assert_index_equal(result, exp)
274274

275-
def test_format_empty(self):
276-
# GH35712
277-
empty_idx = PeriodIndex([], freq="Y")
278-
msg = r"PeriodIndex\.format is deprecated"
279-
with tm.assert_produces_warning(FutureWarning, match=msg):
280-
assert empty_idx.format() == []
281-
with tm.assert_produces_warning(FutureWarning, match=msg):
282-
assert empty_idx.format(name=True) == [""]
283-
284275
def test_period_index_frequency_ME_error_message(self):
285276
msg = "Invalid frequency: 2ME"
286277

pandas/tests/indexes/test_index_new.py

+8
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,14 @@ def test_constructor_dtypes_to_timedelta(self, cast_index, vals):
350350
index = Index(vals)
351351
assert isinstance(index, TimedeltaIndex)
352352

353+
def test_pass_timedeltaindex_to_index(self):
354+
rng = timedelta_range("1 days", "10 days")
355+
idx = Index(rng, dtype=object)
356+
357+
expected = Index(rng.to_pytimedelta(), dtype=object)
358+
359+
tm.assert_numpy_array_equal(idx.values, expected.values)
360+
353361

354362
class TestIndexConstructorUnwrapping:
355363
# Test passing different arraylike values to pd.Index

pandas/tests/indexes/timedeltas/methods/test_astype.py

+51
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
timedelta_range,
1313
)
1414
import pandas._testing as tm
15+
from pandas.core.arrays import TimedeltaArray
1516

1617

1718
class TestTimedeltaIndex:
@@ -95,6 +96,56 @@ def test_astype_timedelta64(self):
9596
tm.assert_index_equal(result, idx)
9697
assert result is idx
9798

99+
def test_astype_to_td64d_raises(self, index_or_series):
100+
# We don't support "D" reso
101+
scalar = Timedelta(days=31)
102+
td = index_or_series(
103+
[scalar, scalar, scalar + timedelta(minutes=5, seconds=3), NaT],
104+
dtype="m8[ns]",
105+
)
106+
msg = (
107+
r"Cannot convert from timedelta64\[ns\] to timedelta64\[D\]. "
108+
"Supported resolutions are 's', 'ms', 'us', 'ns'"
109+
)
110+
with pytest.raises(ValueError, match=msg):
111+
td.astype("timedelta64[D]")
112+
113+
def test_astype_ms_to_s(self, index_or_series):
114+
scalar = Timedelta(days=31)
115+
td = index_or_series(
116+
[scalar, scalar, scalar + timedelta(minutes=5, seconds=3), NaT],
117+
dtype="m8[ns]",
118+
)
119+
120+
exp_values = np.asarray(td).astype("m8[s]")
121+
exp_tda = TimedeltaArray._simple_new(exp_values, dtype=exp_values.dtype)
122+
expected = index_or_series(exp_tda)
123+
assert expected.dtype == "m8[s]"
124+
result = td.astype("timedelta64[s]")
125+
tm.assert_equal(result, expected)
126+
127+
def test_astype_freq_conversion(self):
128+
# pre-2.0 td64 astype converted to float64. now for supported units
129+
# (s, ms, us, ns) this converts to the requested dtype.
130+
# This matches TDA and Series
131+
tdi = timedelta_range("1 Day", periods=30)
132+
133+
res = tdi.astype("m8[s]")
134+
exp_values = np.asarray(tdi).astype("m8[s]")
135+
exp_tda = TimedeltaArray._simple_new(
136+
exp_values, dtype=exp_values.dtype, freq=tdi.freq
137+
)
138+
expected = Index(exp_tda)
139+
assert expected.dtype == "m8[s]"
140+
tm.assert_index_equal(res, expected)
141+
142+
# check this matches Series and TimedeltaArray
143+
res = tdi._data.astype("m8[s]")
144+
tm.assert_equal(res, expected._values)
145+
146+
res = tdi.to_series().astype("m8[s]")
147+
tm.assert_equal(res._values, expected._values._with_freq(None))
148+
98149
@pytest.mark.parametrize("dtype", [float, "datetime64", "datetime64[ns]"])
99150
def test_astype_raises(self, dtype):
100151
# GH 13149, GH 13209
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Arithmetic tests for TimedeltaIndex are generally about the result's `freq` attribute.
2+
# Other cases can be shared in tests.arithmetic.test_timedelta64
3+
import numpy as np
4+
5+
from pandas import (
6+
NaT,
7+
Timedelta,
8+
timedelta_range,
9+
)
10+
import pandas._testing as tm
11+
12+
13+
class TestTimedeltaIndexArithmetic:
14+
def test_arithmetic_zero_freq(self):
15+
# GH#51575 don't get a .freq with freq.n = 0
16+
tdi = timedelta_range(0, periods=100, freq="ns")
17+
result = tdi / 2
18+
assert result.freq is None
19+
expected = tdi[:50].repeat(2)
20+
tm.assert_index_equal(result, expected)
21+
22+
result2 = tdi // 2
23+
assert result2.freq is None
24+
expected2 = expected
25+
tm.assert_index_equal(result2, expected2)
26+
27+
result3 = tdi * 0
28+
assert result3.freq is None
29+
expected3 = tdi[:1].repeat(100)
30+
tm.assert_index_equal(result3, expected3)
31+
32+
def test_tdi_division(self, index_or_series):
33+
# doc example
34+
35+
scalar = Timedelta(days=31)
36+
td = index_or_series(
37+
[scalar, scalar, scalar + Timedelta(minutes=5, seconds=3), NaT],
38+
dtype="m8[ns]",
39+
)
40+
41+
result = td / np.timedelta64(1, "D")
42+
expected = index_or_series(
43+
[31, 31, (31 * 86400 + 5 * 60 + 3) / 86400.0, np.nan]
44+
)
45+
tm.assert_equal(result, expected)
46+
47+
result = td / np.timedelta64(1, "s")
48+
expected = index_or_series(
49+
[31 * 86400, 31 * 86400, 31 * 86400 + 5 * 60 + 3, np.nan]
50+
)
51+
tm.assert_equal(result, expected)
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,16 @@
1-
from datetime import timedelta
2-
31
import numpy as np
42
import pytest
53

64
from pandas import (
75
Index,
8-
NaT,
96
Series,
107
Timedelta,
118
timedelta_range,
129
)
1310
import pandas._testing as tm
14-
from pandas.core.arrays import TimedeltaArray
1511

1612

1713
class TestTimedeltaIndex:
18-
@pytest.fixture
19-
def index(self):
20-
return tm.makeTimedeltaIndex(10)
21-
2214
def test_misc_coverage(self):
2315
rng = timedelta_range("1 day", periods=5)
2416
result = rng.groupby(rng.days)
@@ -34,14 +26,6 @@ def test_map(self):
3426
exp = Index([f(x) for x in rng], dtype=np.int64)
3527
tm.assert_index_equal(result, exp)
3628

37-
def test_pass_TimedeltaIndex_to_index(self):
38-
rng = timedelta_range("1 days", "10 days")
39-
idx = Index(rng, dtype=object)
40-
41-
expected = Index(rng.to_pytimedelta(), dtype=object)
42-
43-
tm.assert_numpy_array_equal(idx.values, expected.values)
44-
4529
def test_fields(self):
4630
rng = timedelta_range("1 days, 10:11:12.100123456", periods=2, freq="s")
4731
tm.assert_index_equal(rng.days, Index([1, 1], dtype=np.int64))
@@ -75,80 +59,3 @@ def test_fields(self):
7559
# preserve name (GH15589)
7660
rng.name = "name"
7761
assert rng.days.name == "name"
78-
79-
def test_freq_conversion_always_floating(self):
80-
# pre-2.0 td64 astype converted to float64. now for supported units
81-
# (s, ms, us, ns) this converts to the requested dtype.
82-
# This matches TDA and Series
83-
tdi = timedelta_range("1 Day", periods=30)
84-
85-
res = tdi.astype("m8[s]")
86-
exp_values = np.asarray(tdi).astype("m8[s]")
87-
exp_tda = TimedeltaArray._simple_new(
88-
exp_values, dtype=exp_values.dtype, freq=tdi.freq
89-
)
90-
expected = Index(exp_tda)
91-
assert expected.dtype == "m8[s]"
92-
tm.assert_index_equal(res, expected)
93-
94-
# check this matches Series and TimedeltaArray
95-
res = tdi._data.astype("m8[s]")
96-
tm.assert_equal(res, expected._values)
97-
98-
res = tdi.to_series().astype("m8[s]")
99-
tm.assert_equal(res._values, expected._values._with_freq(None))
100-
101-
def test_freq_conversion(self, index_or_series):
102-
# doc example
103-
104-
scalar = Timedelta(days=31)
105-
td = index_or_series(
106-
[scalar, scalar, scalar + timedelta(minutes=5, seconds=3), NaT],
107-
dtype="m8[ns]",
108-
)
109-
110-
result = td / np.timedelta64(1, "D")
111-
expected = index_or_series(
112-
[31, 31, (31 * 86400 + 5 * 60 + 3) / 86400.0, np.nan]
113-
)
114-
tm.assert_equal(result, expected)
115-
116-
# We don't support "D" reso, so we use the pre-2.0 behavior
117-
# casting to float64
118-
msg = (
119-
r"Cannot convert from timedelta64\[ns\] to timedelta64\[D\]. "
120-
"Supported resolutions are 's', 'ms', 'us', 'ns'"
121-
)
122-
with pytest.raises(ValueError, match=msg):
123-
td.astype("timedelta64[D]")
124-
125-
result = td / np.timedelta64(1, "s")
126-
expected = index_or_series(
127-
[31 * 86400, 31 * 86400, 31 * 86400 + 5 * 60 + 3, np.nan]
128-
)
129-
tm.assert_equal(result, expected)
130-
131-
exp_values = np.asarray(td).astype("m8[s]")
132-
exp_tda = TimedeltaArray._simple_new(exp_values, dtype=exp_values.dtype)
133-
expected = index_or_series(exp_tda)
134-
assert expected.dtype == "m8[s]"
135-
result = td.astype("timedelta64[s]")
136-
tm.assert_equal(result, expected)
137-
138-
def test_arithmetic_zero_freq(self):
139-
# GH#51575 don't get a .freq with freq.n = 0
140-
tdi = timedelta_range(0, periods=100, freq="ns")
141-
result = tdi / 2
142-
assert result.freq is None
143-
expected = tdi[:50].repeat(2)
144-
tm.assert_index_equal(result, expected)
145-
146-
result2 = tdi // 2
147-
assert result2.freq is None
148-
expected2 = expected
149-
tm.assert_index_equal(result2, expected2)
150-
151-
result3 = tdi * 0
152-
assert result3.freq is None
153-
expected3 = tdi[:1].repeat(100)
154-
tm.assert_index_equal(result3, expected3)

pandas/tests/scalar/timedelta/methods/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import pytest
2+
3+
from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
4+
from pandas.errors import OutOfBoundsTimedelta
5+
6+
from pandas import Timedelta
7+
8+
9+
class TestAsUnit:
10+
def test_as_unit(self):
11+
td = Timedelta(days=1)
12+
13+
assert td.as_unit("ns") is td
14+
15+
res = td.as_unit("us")
16+
assert res._value == td._value // 1000
17+
assert res._creso == NpyDatetimeUnit.NPY_FR_us.value
18+
19+
rt = res.as_unit("ns")
20+
assert rt._value == td._value
21+
assert rt._creso == td._creso
22+
23+
res = td.as_unit("ms")
24+
assert res._value == td._value // 1_000_000
25+
assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
26+
27+
rt = res.as_unit("ns")
28+
assert rt._value == td._value
29+
assert rt._creso == td._creso
30+
31+
res = td.as_unit("s")
32+
assert res._value == td._value // 1_000_000_000
33+
assert res._creso == NpyDatetimeUnit.NPY_FR_s.value
34+
35+
rt = res.as_unit("ns")
36+
assert rt._value == td._value
37+
assert rt._creso == td._creso
38+
39+
def test_as_unit_overflows(self):
40+
# microsecond that would be just out of bounds for nano
41+
us = 9223372800000000
42+
td = Timedelta._from_value_and_reso(us, NpyDatetimeUnit.NPY_FR_us.value)
43+
44+
msg = "Cannot cast 106752 days 00:00:00 to unit='ns' without overflow"
45+
with pytest.raises(OutOfBoundsTimedelta, match=msg):
46+
td.as_unit("ns")
47+
48+
res = td.as_unit("ms")
49+
assert res._value == us // 1000
50+
assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
51+
52+
def test_as_unit_rounding(self):
53+
td = Timedelta(microseconds=1500)
54+
res = td.as_unit("ms")
55+
56+
expected = Timedelta(milliseconds=1)
57+
assert res == expected
58+
59+
assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
60+
assert res._value == 1
61+
62+
with pytest.raises(ValueError, match="Cannot losslessly convert units"):
63+
td.as_unit("ms", round_ok=False)
64+
65+
def test_as_unit_non_nano(self):
66+
# case where we are going neither to nor from nano
67+
td = Timedelta(days=1).as_unit("ms")
68+
assert td.days == 1
69+
assert td._value == 86_400_000
70+
assert td.components.days == 1
71+
assert td._d == 1
72+
assert td.total_seconds() == 86400
73+
74+
res = td.as_unit("us")
75+
assert res._value == 86_400_000_000
76+
assert res.components.days == 1
77+
assert res.components.hours == 0
78+
assert res._d == 1
79+
assert res._h == 0
80+
assert res.total_seconds() == 86400

0 commit comments

Comments
 (0)