This commit is contained in:
2024-12-04 13:35:57 +05:00
parent d346bf4b2a
commit 73ce681a55
7059 changed files with 1196501 additions and 0 deletions

View File

@ -0,0 +1,176 @@
from datetime import timedelta
import numpy as np
import pytest
import pandas as pd
from pandas import (
Index,
NaT,
Timedelta,
TimedeltaIndex,
timedelta_range,
)
import pandas._testing as tm
from pandas.core.arrays import TimedeltaArray
class TestTimedeltaIndex:
def test_astype_object(self):
idx = timedelta_range(start="1 days", periods=4, freq="D", name="idx")
expected_list = [
Timedelta("1 days"),
Timedelta("2 days"),
Timedelta("3 days"),
Timedelta("4 days"),
]
result = idx.astype(object)
expected = Index(expected_list, dtype=object, name="idx")
tm.assert_index_equal(result, expected)
assert idx.tolist() == expected_list
def test_astype_object_with_nat(self):
idx = TimedeltaIndex(
[timedelta(days=1), timedelta(days=2), NaT, timedelta(days=4)], name="idx"
)
expected_list = [
Timedelta("1 days"),
Timedelta("2 days"),
NaT,
Timedelta("4 days"),
]
result = idx.astype(object)
expected = Index(expected_list, dtype=object, name="idx")
tm.assert_index_equal(result, expected)
assert idx.tolist() == expected_list
def test_astype(self):
# GH 13149, GH 13209
idx = TimedeltaIndex([1e14, "NaT", NaT, np.nan], name="idx")
result = idx.astype(object)
expected = Index(
[Timedelta("1 days 03:46:40")] + [NaT] * 3, dtype=object, name="idx"
)
tm.assert_index_equal(result, expected)
result = idx.astype(np.int64)
expected = Index(
[100000000000000] + [-9223372036854775808] * 3, dtype=np.int64, name="idx"
)
tm.assert_index_equal(result, expected)
result = idx.astype(str)
expected = Index([str(x) for x in idx], name="idx", dtype=object)
tm.assert_index_equal(result, expected)
rng = timedelta_range("1 days", periods=10)
result = rng.astype("i8")
tm.assert_index_equal(result, Index(rng.asi8))
tm.assert_numpy_array_equal(rng.asi8, result.values)
def test_astype_uint(self):
arr = timedelta_range("1h", periods=2)
with pytest.raises(TypeError, match=r"Do obj.astype\('int64'\)"):
arr.astype("uint64")
with pytest.raises(TypeError, match=r"Do obj.astype\('int64'\)"):
arr.astype("uint32")
def test_astype_timedelta64(self):
# GH 13149, GH 13209
idx = TimedeltaIndex([1e14, "NaT", NaT, np.nan])
msg = (
r"Cannot convert from timedelta64\[ns\] to timedelta64. "
"Supported resolutions are 's', 'ms', 'us', 'ns'"
)
with pytest.raises(ValueError, match=msg):
idx.astype("timedelta64")
result = idx.astype("timedelta64[ns]")
tm.assert_index_equal(result, idx)
assert result is not idx
result = idx.astype("timedelta64[ns]", copy=False)
tm.assert_index_equal(result, idx)
assert result is idx
def test_astype_to_td64d_raises(self, index_or_series):
# We don't support "D" reso
scalar = Timedelta(days=31)
td = index_or_series(
[scalar, scalar, scalar + timedelta(minutes=5, seconds=3), NaT],
dtype="m8[ns]",
)
msg = (
r"Cannot convert from timedelta64\[ns\] to timedelta64\[D\]. "
"Supported resolutions are 's', 'ms', 'us', 'ns'"
)
with pytest.raises(ValueError, match=msg):
td.astype("timedelta64[D]")
def test_astype_ms_to_s(self, index_or_series):
scalar = Timedelta(days=31)
td = index_or_series(
[scalar, scalar, scalar + timedelta(minutes=5, seconds=3), NaT],
dtype="m8[ns]",
)
exp_values = np.asarray(td).astype("m8[s]")
exp_tda = TimedeltaArray._simple_new(exp_values, dtype=exp_values.dtype)
expected = index_or_series(exp_tda)
assert expected.dtype == "m8[s]"
result = td.astype("timedelta64[s]")
tm.assert_equal(result, expected)
def test_astype_freq_conversion(self):
# pre-2.0 td64 astype converted to float64. now for supported units
# (s, ms, us, ns) this converts to the requested dtype.
# This matches TDA and Series
tdi = timedelta_range("1 Day", periods=30)
res = tdi.astype("m8[s]")
exp_values = np.asarray(tdi).astype("m8[s]")
exp_tda = TimedeltaArray._simple_new(
exp_values, dtype=exp_values.dtype, freq=tdi.freq
)
expected = Index(exp_tda)
assert expected.dtype == "m8[s]"
tm.assert_index_equal(res, expected)
# check this matches Series and TimedeltaArray
res = tdi._data.astype("m8[s]")
tm.assert_equal(res, expected._values)
res = tdi.to_series().astype("m8[s]")
tm.assert_equal(res._values, expected._values._with_freq(None))
@pytest.mark.parametrize("dtype", [float, "datetime64", "datetime64[ns]"])
def test_astype_raises(self, dtype):
# GH 13149, GH 13209
idx = TimedeltaIndex([1e14, "NaT", NaT, np.nan])
msg = "Cannot cast TimedeltaIndex to dtype"
with pytest.raises(TypeError, match=msg):
idx.astype(dtype)
def test_astype_category(self):
obj = timedelta_range("1h", periods=2, freq="h")
result = obj.astype("category")
expected = pd.CategoricalIndex([Timedelta("1h"), Timedelta("2h")])
tm.assert_index_equal(result, expected)
result = obj._data.astype("category")
expected = expected.values
tm.assert_categorical_equal(result, expected)
def test_astype_array_fallback(self):
obj = timedelta_range("1h", periods=2)
result = obj.astype(bool)
expected = Index(np.array([True, True]))
tm.assert_index_equal(result, expected)
result = obj._data.astype(bool)
expected = np.array([True, True])
tm.assert_numpy_array_equal(result, expected)

View File

@ -0,0 +1,40 @@
import numpy as np
from pandas import (
TimedeltaIndex,
factorize,
timedelta_range,
)
import pandas._testing as tm
class TestTimedeltaIndexFactorize:
def test_factorize(self):
idx1 = TimedeltaIndex(["1 day", "1 day", "2 day", "2 day", "3 day", "3 day"])
exp_arr = np.array([0, 0, 1, 1, 2, 2], dtype=np.intp)
exp_idx = TimedeltaIndex(["1 day", "2 day", "3 day"])
arr, idx = idx1.factorize()
tm.assert_numpy_array_equal(arr, exp_arr)
tm.assert_index_equal(idx, exp_idx)
assert idx.freq == exp_idx.freq
arr, idx = idx1.factorize(sort=True)
tm.assert_numpy_array_equal(arr, exp_arr)
tm.assert_index_equal(idx, exp_idx)
assert idx.freq == exp_idx.freq
def test_factorize_preserves_freq(self):
# GH#38120 freq should be preserved
idx3 = timedelta_range("1 day", periods=4, freq="s")
exp_arr = np.array([0, 1, 2, 3], dtype=np.intp)
arr, idx = idx3.factorize()
tm.assert_numpy_array_equal(arr, exp_arr)
tm.assert_index_equal(idx, idx3)
assert idx.freq == idx3.freq
arr, idx = factorize(idx3)
tm.assert_numpy_array_equal(arr, exp_arr)
tm.assert_index_equal(idx, idx3)
assert idx.freq == idx3.freq

View File

@ -0,0 +1,22 @@
from pandas import (
Index,
NaT,
Timedelta,
TimedeltaIndex,
)
import pandas._testing as tm
class TestFillNA:
def test_fillna_timedelta(self):
# GH#11343
idx = TimedeltaIndex(["1 day", NaT, "3 day"])
exp = TimedeltaIndex(["1 day", "2 day", "3 day"])
tm.assert_index_equal(idx.fillna(Timedelta("2 day")), exp)
exp = TimedeltaIndex(["1 day", "3 hour", "3 day"])
idx.fillna(Timedelta("3 hour"))
exp = Index([Timedelta("1 day"), "x", Timedelta("3 day")], dtype=object)
tm.assert_index_equal(idx.fillna("x"), exp)

View File

@ -0,0 +1,145 @@
from datetime import timedelta
import numpy as np
import pytest
from pandas._libs import lib
import pandas as pd
from pandas import (
Index,
Timedelta,
TimedeltaIndex,
timedelta_range,
)
import pandas._testing as tm
class TestTimedeltaIndexInsert:
def test_insert(self):
idx = TimedeltaIndex(["4day", "1day", "2day"], name="idx")
result = idx.insert(2, timedelta(days=5))
exp = TimedeltaIndex(["4day", "1day", "5day", "2day"], name="idx")
tm.assert_index_equal(result, exp)
# insertion of non-datetime should coerce to object index
result = idx.insert(1, "inserted")
expected = Index(
[Timedelta("4day"), "inserted", Timedelta("1day"), Timedelta("2day")],
name="idx",
)
assert not isinstance(result, TimedeltaIndex)
tm.assert_index_equal(result, expected)
assert result.name == expected.name
idx = timedelta_range("1day 00:00:01", periods=3, freq="s", name="idx")
# preserve freq
expected_0 = TimedeltaIndex(
["1day", "1day 00:00:01", "1day 00:00:02", "1day 00:00:03"],
name="idx",
freq="s",
)
expected_3 = TimedeltaIndex(
["1day 00:00:01", "1day 00:00:02", "1day 00:00:03", "1day 00:00:04"],
name="idx",
freq="s",
)
# reset freq to None
expected_1_nofreq = TimedeltaIndex(
["1day 00:00:01", "1day 00:00:01", "1day 00:00:02", "1day 00:00:03"],
name="idx",
freq=None,
)
expected_3_nofreq = TimedeltaIndex(
["1day 00:00:01", "1day 00:00:02", "1day 00:00:03", "1day 00:00:05"],
name="idx",
freq=None,
)
cases = [
(0, Timedelta("1day"), expected_0),
(-3, Timedelta("1day"), expected_0),
(3, Timedelta("1day 00:00:04"), expected_3),
(1, Timedelta("1day 00:00:01"), expected_1_nofreq),
(3, Timedelta("1day 00:00:05"), expected_3_nofreq),
]
for n, d, expected in cases:
result = idx.insert(n, d)
tm.assert_index_equal(result, expected)
assert result.name == expected.name
assert result.freq == expected.freq
@pytest.mark.parametrize(
"null", [None, np.nan, np.timedelta64("NaT"), pd.NaT, pd.NA]
)
def test_insert_nat(self, null):
# GH 18295 (test missing)
idx = timedelta_range("1day", "3day")
result = idx.insert(1, null)
expected = TimedeltaIndex(["1day", pd.NaT, "2day", "3day"])
tm.assert_index_equal(result, expected)
def test_insert_invalid_na(self):
idx = TimedeltaIndex(["4day", "1day", "2day"], name="idx")
item = np.datetime64("NaT")
result = idx.insert(0, item)
expected = Index([item] + list(idx), dtype=object, name="idx")
tm.assert_index_equal(result, expected)
# Also works if we pass a different dt64nat object
item2 = np.datetime64("NaT")
result = idx.insert(0, item2)
tm.assert_index_equal(result, expected)
@pytest.mark.parametrize(
"item", [0, np.int64(0), np.float64(0), np.array(0), np.datetime64(456, "us")]
)
def test_insert_mismatched_types_raises(self, item):
# GH#33703 dont cast these to td64
tdi = TimedeltaIndex(["4day", "1day", "2day"], name="idx")
result = tdi.insert(1, item)
expected = Index(
[tdi[0], lib.item_from_zerodim(item)] + list(tdi[1:]),
dtype=object,
name="idx",
)
tm.assert_index_equal(result, expected)
def test_insert_castable_str(self):
idx = timedelta_range("1day", "3day")
result = idx.insert(0, "1 Day")
expected = TimedeltaIndex([idx[0]] + list(idx))
tm.assert_index_equal(result, expected)
def test_insert_non_castable_str(self):
idx = timedelta_range("1day", "3day")
result = idx.insert(0, "foo")
expected = Index(["foo"] + list(idx), dtype=object)
tm.assert_index_equal(result, expected)
def test_insert_empty(self):
# Corner case inserting with length zero doesn't raise IndexError
# GH#33573 for freq preservation
idx = timedelta_range("1 Day", periods=3)
td = idx[0]
result = idx[:0].insert(0, td)
assert result.freq == "D"
with pytest.raises(IndexError, match="loc must be an integer between"):
result = idx[:0].insert(1, td)
with pytest.raises(IndexError, match="loc must be an integer between"):
result = idx[:0].insert(-1, td)

View File

@ -0,0 +1,34 @@
import numpy as np
from pandas import (
TimedeltaIndex,
timedelta_range,
)
import pandas._testing as tm
class TestRepeat:
def test_repeat(self):
index = timedelta_range("1 days", periods=2, freq="D")
exp = TimedeltaIndex(["1 days", "1 days", "2 days", "2 days"])
for res in [index.repeat(2), np.repeat(index, 2)]:
tm.assert_index_equal(res, exp)
assert res.freq is None
index = TimedeltaIndex(["1 days", "NaT", "3 days"])
exp = TimedeltaIndex(
[
"1 days",
"1 days",
"1 days",
"NaT",
"NaT",
"NaT",
"3 days",
"3 days",
"3 days",
]
)
for res in [index.repeat(3), np.repeat(index, 3)]:
tm.assert_index_equal(res, exp)
assert res.freq is None

View File

@ -0,0 +1,76 @@
import pytest
from pandas.errors import NullFrequencyError
import pandas as pd
from pandas import TimedeltaIndex
import pandas._testing as tm
class TestTimedeltaIndexShift:
# -------------------------------------------------------------
# TimedeltaIndex.shift is used by __add__/__sub__
def test_tdi_shift_empty(self):
# GH#9903
idx = TimedeltaIndex([], name="xxx")
tm.assert_index_equal(idx.shift(0, freq="h"), idx)
tm.assert_index_equal(idx.shift(3, freq="h"), idx)
def test_tdi_shift_hours(self):
# GH#9903
idx = TimedeltaIndex(["5 hours", "6 hours", "9 hours"], name="xxx")
tm.assert_index_equal(idx.shift(0, freq="h"), idx)
exp = TimedeltaIndex(["8 hours", "9 hours", "12 hours"], name="xxx")
tm.assert_index_equal(idx.shift(3, freq="h"), exp)
exp = TimedeltaIndex(["2 hours", "3 hours", "6 hours"], name="xxx")
tm.assert_index_equal(idx.shift(-3, freq="h"), exp)
def test_tdi_shift_minutes(self):
# GH#9903
idx = TimedeltaIndex(["5 hours", "6 hours", "9 hours"], name="xxx")
tm.assert_index_equal(idx.shift(0, freq="min"), idx)
exp = TimedeltaIndex(["05:03:00", "06:03:00", "9:03:00"], name="xxx")
tm.assert_index_equal(idx.shift(3, freq="min"), exp)
exp = TimedeltaIndex(["04:57:00", "05:57:00", "8:57:00"], name="xxx")
tm.assert_index_equal(idx.shift(-3, freq="min"), exp)
def test_tdi_shift_int(self):
# GH#8083
tdi = pd.to_timedelta(range(5), unit="d")
trange = tdi._with_freq("infer") + pd.offsets.Hour(1)
result = trange.shift(1)
expected = TimedeltaIndex(
[
"1 days 01:00:00",
"2 days 01:00:00",
"3 days 01:00:00",
"4 days 01:00:00",
"5 days 01:00:00",
],
freq="D",
)
tm.assert_index_equal(result, expected)
def test_tdi_shift_nonstandard_freq(self):
# GH#8083
tdi = pd.to_timedelta(range(5), unit="d")
trange = tdi._with_freq("infer") + pd.offsets.Hour(1)
result = trange.shift(3, freq="2D 1s")
expected = TimedeltaIndex(
[
"6 days 01:00:03",
"7 days 01:00:03",
"8 days 01:00:03",
"9 days 01:00:03",
"10 days 01:00:03",
],
freq="D",
)
tm.assert_index_equal(result, expected)
def test_shift_no_freq(self):
# GH#19147
tdi = TimedeltaIndex(["1 days 01:00:00", "2 days 01:00:00"], freq=None)
with pytest.raises(NullFrequencyError, match="Cannot shift with no freq"):
tdi.shift(2)

View File

@ -0,0 +1,51 @@
# Arithmetic tests for TimedeltaIndex are generally about the result's `freq` attribute.
# Other cases can be shared in tests.arithmetic.test_timedelta64
import numpy as np
from pandas import (
NaT,
Timedelta,
timedelta_range,
)
import pandas._testing as tm
class TestTimedeltaIndexArithmetic:
def test_arithmetic_zero_freq(self):
# GH#51575 don't get a .freq with freq.n = 0
tdi = timedelta_range(0, periods=100, freq="ns")
result = tdi / 2
assert result.freq is None
expected = tdi[:50].repeat(2)
tm.assert_index_equal(result, expected)
result2 = tdi // 2
assert result2.freq is None
expected2 = expected
tm.assert_index_equal(result2, expected2)
result3 = tdi * 0
assert result3.freq is None
expected3 = tdi[:1].repeat(100)
tm.assert_index_equal(result3, expected3)
def test_tdi_division(self, index_or_series):
# doc example
scalar = Timedelta(days=31)
td = index_or_series(
[scalar, scalar, scalar + Timedelta(minutes=5, seconds=3), NaT],
dtype="m8[ns]",
)
result = td / np.timedelta64(1, "D")
expected = index_or_series(
[31, 31, (31 * 86400 + 5 * 60 + 3) / 86400.0, np.nan]
)
tm.assert_equal(result, expected)
result = td / np.timedelta64(1, "s")
expected = index_or_series(
[31 * 86400, 31 * 86400, 31 * 86400 + 5 * 60 + 3, np.nan]
)
tm.assert_equal(result, expected)

View File

@ -0,0 +1,291 @@
from datetime import timedelta
import numpy as np
import pytest
import pandas as pd
from pandas import (
Timedelta,
TimedeltaIndex,
timedelta_range,
to_timedelta,
)
import pandas._testing as tm
from pandas.core.arrays.timedeltas import TimedeltaArray
class TestTimedeltaIndex:
def test_closed_deprecated(self):
# GH#52628
msg = "The 'closed' keyword"
with tm.assert_produces_warning(FutureWarning, match=msg):
TimedeltaIndex([], closed=True)
def test_array_of_dt64_nat_raises(self):
# GH#39462
nat = np.datetime64("NaT", "ns")
arr = np.array([nat], dtype=object)
msg = "Invalid type for timedelta scalar"
with pytest.raises(TypeError, match=msg):
TimedeltaIndex(arr)
with pytest.raises(TypeError, match=msg):
TimedeltaArray._from_sequence(arr, dtype="m8[ns]")
with pytest.raises(TypeError, match=msg):
to_timedelta(arr)
@pytest.mark.parametrize("unit", ["Y", "y", "M"])
def test_unit_m_y_raises(self, unit):
msg = "Units 'M', 'Y', and 'y' are no longer supported"
depr_msg = "The 'unit' keyword in TimedeltaIndex construction is deprecated"
with pytest.raises(ValueError, match=msg):
with tm.assert_produces_warning(FutureWarning, match=depr_msg):
TimedeltaIndex([1, 3, 7], unit)
def test_int64_nocopy(self):
# GH#23539 check that a copy isn't made when we pass int64 data
# and copy=False
arr = np.arange(10, dtype=np.int64)
tdi = TimedeltaIndex(arr, copy=False)
assert tdi._data._ndarray.base is arr
def test_infer_from_tdi(self):
# GH#23539
# fast-path for inferring a frequency if the passed data already
# has one
tdi = timedelta_range("1 second", periods=10**7, freq="1s")
result = TimedeltaIndex(tdi, freq="infer")
assert result.freq == tdi.freq
# check that inferred_freq was not called by checking that the
# value has not been cached
assert "inferred_freq" not in getattr(result, "_cache", {})
def test_infer_from_tdi_mismatch(self):
# GH#23539
# fast-path for invalidating a frequency if the passed data already
# has one and it does not match the `freq` input
tdi = timedelta_range("1 second", periods=100, freq="1s")
depr_msg = "TimedeltaArray.__init__ is deprecated"
msg = (
"Inferred frequency .* from passed values does "
"not conform to passed frequency"
)
with pytest.raises(ValueError, match=msg):
TimedeltaIndex(tdi, freq="D")
with pytest.raises(ValueError, match=msg):
# GH#23789
with tm.assert_produces_warning(FutureWarning, match=depr_msg):
TimedeltaArray(tdi, freq="D")
with pytest.raises(ValueError, match=msg):
TimedeltaIndex(tdi._data, freq="D")
with pytest.raises(ValueError, match=msg):
with tm.assert_produces_warning(FutureWarning, match=depr_msg):
TimedeltaArray(tdi._data, freq="D")
def test_dt64_data_invalid(self):
# GH#23539
# passing tz-aware DatetimeIndex raises, naive or ndarray[datetime64]
# raise as of GH#29794
dti = pd.date_range("2016-01-01", periods=3)
msg = "cannot be converted to timedelta64"
with pytest.raises(TypeError, match=msg):
TimedeltaIndex(dti.tz_localize("Europe/Brussels"))
with pytest.raises(TypeError, match=msg):
TimedeltaIndex(dti)
with pytest.raises(TypeError, match=msg):
TimedeltaIndex(np.asarray(dti))
def test_float64_ns_rounded(self):
# GH#23539 without specifying a unit, floats are regarded as nanos,
# and fractional portions are truncated
tdi = TimedeltaIndex([2.3, 9.7])
expected = TimedeltaIndex([2, 9])
tm.assert_index_equal(tdi, expected)
# integral floats are non-lossy
tdi = TimedeltaIndex([2.0, 9.0])
expected = TimedeltaIndex([2, 9])
tm.assert_index_equal(tdi, expected)
# NaNs get converted to NaT
tdi = TimedeltaIndex([2.0, np.nan])
expected = TimedeltaIndex([Timedelta(nanoseconds=2), pd.NaT])
tm.assert_index_equal(tdi, expected)
def test_float64_unit_conversion(self):
# GH#23539
tdi = to_timedelta([1.5, 2.25], unit="D")
expected = TimedeltaIndex([Timedelta(days=1.5), Timedelta(days=2.25)])
tm.assert_index_equal(tdi, expected)
def test_construction_base_constructor(self):
arr = [Timedelta("1 days"), pd.NaT, Timedelta("3 days")]
tm.assert_index_equal(pd.Index(arr), TimedeltaIndex(arr))
tm.assert_index_equal(pd.Index(np.array(arr)), TimedeltaIndex(np.array(arr)))
arr = [np.nan, pd.NaT, Timedelta("1 days")]
tm.assert_index_equal(pd.Index(arr), TimedeltaIndex(arr))
tm.assert_index_equal(pd.Index(np.array(arr)), TimedeltaIndex(np.array(arr)))
@pytest.mark.filterwarnings(
"ignore:The 'unit' keyword in TimedeltaIndex construction:FutureWarning"
)
def test_constructor(self):
expected = TimedeltaIndex(
[
"1 days",
"1 days 00:00:05",
"2 days",
"2 days 00:00:02",
"0 days 00:00:03",
]
)
result = TimedeltaIndex(
[
"1 days",
"1 days, 00:00:05",
np.timedelta64(2, "D"),
timedelta(days=2, seconds=2),
pd.offsets.Second(3),
]
)
tm.assert_index_equal(result, expected)
expected = TimedeltaIndex(
["0 days 00:00:00", "0 days 00:00:01", "0 days 00:00:02"]
)
result = TimedeltaIndex(range(3), unit="s")
tm.assert_index_equal(result, expected)
expected = TimedeltaIndex(
["0 days 00:00:00", "0 days 00:00:05", "0 days 00:00:09"]
)
result = TimedeltaIndex([0, 5, 9], unit="s")
tm.assert_index_equal(result, expected)
expected = TimedeltaIndex(
["0 days 00:00:00.400", "0 days 00:00:00.450", "0 days 00:00:01.200"]
)
result = TimedeltaIndex([400, 450, 1200], unit="ms")
tm.assert_index_equal(result, expected)
def test_constructor_iso(self):
# GH #21877
expected = timedelta_range("1s", periods=9, freq="s")
durations = [f"P0DT0H0M{i}S" for i in range(1, 10)]
result = to_timedelta(durations)
tm.assert_index_equal(result, expected)
def test_timedelta_range_fractional_period(self):
msg = "Non-integer 'periods' in pd.date_range, pd.timedelta_range"
with tm.assert_produces_warning(FutureWarning, match=msg):
rng = timedelta_range("1 days", periods=10.5)
exp = timedelta_range("1 days", periods=10)
tm.assert_index_equal(rng, exp)
def test_constructor_coverage(self):
msg = "periods must be a number, got foo"
with pytest.raises(TypeError, match=msg):
timedelta_range(start="1 days", periods="foo", freq="D")
msg = (
r"TimedeltaIndex\(\.\.\.\) must be called with a collection of some kind, "
"'1 days' was passed"
)
with pytest.raises(TypeError, match=msg):
TimedeltaIndex("1 days")
# generator expression
gen = (timedelta(i) for i in range(10))
result = TimedeltaIndex(gen)
expected = TimedeltaIndex([timedelta(i) for i in range(10)])
tm.assert_index_equal(result, expected)
# NumPy string array
strings = np.array(["1 days", "2 days", "3 days"])
result = TimedeltaIndex(strings)
expected = to_timedelta([1, 2, 3], unit="d")
tm.assert_index_equal(result, expected)
from_ints = TimedeltaIndex(expected.asi8)
tm.assert_index_equal(from_ints, expected)
# non-conforming freq
msg = (
"Inferred frequency None from passed values does not conform to "
"passed frequency D"
)
with pytest.raises(ValueError, match=msg):
TimedeltaIndex(["1 days", "2 days", "4 days"], freq="D")
msg = (
"Of the four parameters: start, end, periods, and freq, exactly "
"three must be specified"
)
with pytest.raises(ValueError, match=msg):
timedelta_range(periods=10, freq="D")
def test_constructor_name(self):
idx = timedelta_range(start="1 days", periods=1, freq="D", name="TEST")
assert idx.name == "TEST"
# GH10025
idx2 = TimedeltaIndex(idx, name="something else")
assert idx2.name == "something else"
def test_constructor_no_precision_raises(self):
# GH-24753, GH-24739
msg = "with no precision is not allowed"
with pytest.raises(ValueError, match=msg):
TimedeltaIndex(["2000"], dtype="timedelta64")
msg = "The 'timedelta64' dtype has no unit. Please pass in"
with pytest.raises(ValueError, match=msg):
pd.Index(["2000"], dtype="timedelta64")
def test_constructor_wrong_precision_raises(self):
msg = "Supported timedelta64 resolutions are 's', 'ms', 'us', 'ns'"
with pytest.raises(ValueError, match=msg):
TimedeltaIndex(["2000"], dtype="timedelta64[D]")
# "timedelta64[us]" was unsupported pre-2.0, but now this works.
tdi = TimedeltaIndex(["2000"], dtype="timedelta64[us]")
assert tdi.dtype == "m8[us]"
def test_explicit_none_freq(self):
# Explicitly passing freq=None is respected
tdi = timedelta_range(1, periods=5)
assert tdi.freq is not None
result = TimedeltaIndex(tdi, freq=None)
assert result.freq is None
result = TimedeltaIndex(tdi._data, freq=None)
assert result.freq is None
msg = "TimedeltaArray.__init__ is deprecated"
with tm.assert_produces_warning(FutureWarning, match=msg):
tda = TimedeltaArray(tdi, freq=None)
assert tda.freq is None
def test_from_categorical(self):
tdi = timedelta_range(1, periods=5)
cat = pd.Categorical(tdi)
result = TimedeltaIndex(cat)
tm.assert_index_equal(result, tdi)
ci = pd.CategoricalIndex(tdi)
result = TimedeltaIndex(ci)
tm.assert_index_equal(result, tdi)

View File

@ -0,0 +1,71 @@
from pandas import (
TimedeltaIndex,
timedelta_range,
)
import pandas._testing as tm
class TestTimedeltaIndexDelete:
def test_delete(self):
idx = timedelta_range(start="1 Days", periods=5, freq="D", name="idx")
# preserve freq
expected_0 = timedelta_range(start="2 Days", periods=4, freq="D", name="idx")
expected_4 = timedelta_range(start="1 Days", periods=4, freq="D", name="idx")
# reset freq to None
expected_1 = TimedeltaIndex(
["1 day", "3 day", "4 day", "5 day"], freq=None, name="idx"
)
cases = {
0: expected_0,
-5: expected_0,
-1: expected_4,
4: expected_4,
1: expected_1,
}
for n, expected in cases.items():
result = idx.delete(n)
tm.assert_index_equal(result, expected)
assert result.name == expected.name
assert result.freq == expected.freq
with tm.external_error_raised((IndexError, ValueError)):
# either depending on numpy version
idx.delete(5)
def test_delete_slice(self):
idx = timedelta_range(start="1 days", periods=10, freq="D", name="idx")
# preserve freq
expected_0_2 = timedelta_range(start="4 days", periods=7, freq="D", name="idx")
expected_7_9 = timedelta_range(start="1 days", periods=7, freq="D", name="idx")
# reset freq to None
expected_3_5 = TimedeltaIndex(
["1 d", "2 d", "3 d", "7 d", "8 d", "9 d", "10d"], freq=None, name="idx"
)
cases = {
(0, 1, 2): expected_0_2,
(7, 8, 9): expected_7_9,
(3, 4, 5): expected_3_5,
}
for n, expected in cases.items():
result = idx.delete(n)
tm.assert_index_equal(result, expected)
assert result.name == expected.name
assert result.freq == expected.freq
result = idx.delete(slice(n[0], n[-1] + 1))
tm.assert_index_equal(result, expected)
assert result.name == expected.name
assert result.freq == expected.freq
def test_delete_doesnt_infer_freq(self):
# GH#30655 behavior matches DatetimeIndex
tdi = TimedeltaIndex(["1 Day", "2 Days", None, "3 Days", "4 Days"])
result = tdi.delete(2)
assert result.freq is None

View File

@ -0,0 +1,106 @@
import pytest
import pandas as pd
from pandas import (
Series,
TimedeltaIndex,
)
class TestTimedeltaIndexRendering:
def test_repr_round_days_non_nano(self):
# GH#55405
# we should get "1 days", not "1 days 00:00:00" with non-nano
tdi = TimedeltaIndex(["1 days"], freq="D").as_unit("s")
result = repr(tdi)
expected = "TimedeltaIndex(['1 days'], dtype='timedelta64[s]', freq='D')"
assert result == expected
result2 = repr(Series(tdi))
expected2 = "0 1 days\ndtype: timedelta64[s]"
assert result2 == expected2
@pytest.mark.parametrize("method", ["__repr__", "__str__"])
def test_representation(self, method):
idx1 = TimedeltaIndex([], freq="D")
idx2 = TimedeltaIndex(["1 days"], freq="D")
idx3 = TimedeltaIndex(["1 days", "2 days"], freq="D")
idx4 = TimedeltaIndex(["1 days", "2 days", "3 days"], freq="D")
idx5 = TimedeltaIndex(["1 days 00:00:01", "2 days", "3 days"])
exp1 = "TimedeltaIndex([], dtype='timedelta64[ns]', freq='D')"
exp2 = "TimedeltaIndex(['1 days'], dtype='timedelta64[ns]', freq='D')"
exp3 = "TimedeltaIndex(['1 days', '2 days'], dtype='timedelta64[ns]', freq='D')"
exp4 = (
"TimedeltaIndex(['1 days', '2 days', '3 days'], "
"dtype='timedelta64[ns]', freq='D')"
)
exp5 = (
"TimedeltaIndex(['1 days 00:00:01', '2 days 00:00:00', "
"'3 days 00:00:00'], dtype='timedelta64[ns]', freq=None)"
)
with pd.option_context("display.width", 300):
for idx, expected in zip(
[idx1, idx2, idx3, idx4, idx5], [exp1, exp2, exp3, exp4, exp5]
):
result = getattr(idx, method)()
assert result == expected
# TODO: this is a Series.__repr__ test
def test_representation_to_series(self):
idx1 = TimedeltaIndex([], freq="D")
idx2 = TimedeltaIndex(["1 days"], freq="D")
idx3 = TimedeltaIndex(["1 days", "2 days"], freq="D")
idx4 = TimedeltaIndex(["1 days", "2 days", "3 days"], freq="D")
idx5 = TimedeltaIndex(["1 days 00:00:01", "2 days", "3 days"])
exp1 = """Series([], dtype: timedelta64[ns])"""
exp2 = "0 1 days\ndtype: timedelta64[ns]"
exp3 = "0 1 days\n1 2 days\ndtype: timedelta64[ns]"
exp4 = "0 1 days\n1 2 days\n2 3 days\ndtype: timedelta64[ns]"
exp5 = (
"0 1 days 00:00:01\n"
"1 2 days 00:00:00\n"
"2 3 days 00:00:00\n"
"dtype: timedelta64[ns]"
)
with pd.option_context("display.width", 300):
for idx, expected in zip(
[idx1, idx2, idx3, idx4, idx5], [exp1, exp2, exp3, exp4, exp5]
):
result = repr(Series(idx))
assert result == expected
def test_summary(self):
# GH#9116
idx1 = TimedeltaIndex([], freq="D")
idx2 = TimedeltaIndex(["1 days"], freq="D")
idx3 = TimedeltaIndex(["1 days", "2 days"], freq="D")
idx4 = TimedeltaIndex(["1 days", "2 days", "3 days"], freq="D")
idx5 = TimedeltaIndex(["1 days 00:00:01", "2 days", "3 days"])
exp1 = "TimedeltaIndex: 0 entries\nFreq: D"
exp2 = "TimedeltaIndex: 1 entries, 1 days to 1 days\nFreq: D"
exp3 = "TimedeltaIndex: 2 entries, 1 days to 2 days\nFreq: D"
exp4 = "TimedeltaIndex: 3 entries, 1 days to 3 days\nFreq: D"
exp5 = "TimedeltaIndex: 3 entries, 1 days 00:00:01 to 3 days 00:00:00"
for idx, expected in zip(
[idx1, idx2, idx3, idx4, idx5], [exp1, exp2, exp3, exp4, exp5]
):
result = idx._summary()
assert result == expected

View File

@ -0,0 +1,72 @@
import pytest
from pandas import TimedeltaIndex
from pandas.tseries.offsets import (
DateOffset,
Day,
Hour,
MonthEnd,
)
class TestFreq:
@pytest.mark.parametrize("values", [["0 days", "2 days", "4 days"], []])
@pytest.mark.parametrize("freq", ["2D", Day(2), "48h", Hour(48)])
def test_freq_setter(self, values, freq):
# GH#20678
idx = TimedeltaIndex(values)
# can set to an offset, converting from string if necessary
idx._data.freq = freq
assert idx.freq == freq
assert isinstance(idx.freq, DateOffset)
# can reset to None
idx._data.freq = None
assert idx.freq is None
def test_with_freq_empty_requires_tick(self):
idx = TimedeltaIndex([])
off = MonthEnd(1)
msg = "TimedeltaArray/Index freq must be a Tick"
with pytest.raises(TypeError, match=msg):
idx._with_freq(off)
with pytest.raises(TypeError, match=msg):
idx._data._with_freq(off)
def test_freq_setter_errors(self):
# GH#20678
idx = TimedeltaIndex(["0 days", "2 days", "4 days"])
# setting with an incompatible freq
msg = (
"Inferred frequency 2D from passed values does not conform to "
"passed frequency 5D"
)
with pytest.raises(ValueError, match=msg):
idx._data.freq = "5D"
# setting with a non-fixed frequency
msg = r"<2 \* BusinessDays> is a non-fixed frequency"
with pytest.raises(ValueError, match=msg):
idx._data.freq = "2B"
# setting with non-freq string
with pytest.raises(ValueError, match="Invalid frequency"):
idx._data.freq = "foo"
def test_freq_view_safe(self):
# Setting the freq for one TimedeltaIndex shouldn't alter the freq
# for another that views the same data
tdi = TimedeltaIndex(["0 days", "2 days", "4 days"], freq="2D")
tda = tdi._data
tdi2 = TimedeltaIndex(tda)._with_freq(None)
assert tdi2.freq is None
# Original was not altered
assert tdi.freq == "2D"
assert tda.freq == "2D"

View File

@ -0,0 +1,347 @@
from datetime import datetime
import re
import numpy as np
import pytest
from pandas import (
Index,
NaT,
Timedelta,
TimedeltaIndex,
Timestamp,
notna,
offsets,
timedelta_range,
to_timedelta,
)
import pandas._testing as tm
class TestGetItem:
def test_getitem_slice_keeps_name(self):
# GH#4226
tdi = timedelta_range("1d", "5d", freq="h", name="timebucket")
assert tdi[1:].name == tdi.name
def test_getitem(self):
idx1 = timedelta_range("1 day", "31 day", freq="D", name="idx")
for idx in [idx1]:
result = idx[0]
assert result == Timedelta("1 day")
result = idx[0:5]
expected = timedelta_range("1 day", "5 day", freq="D", name="idx")
tm.assert_index_equal(result, expected)
assert result.freq == expected.freq
result = idx[0:10:2]
expected = timedelta_range("1 day", "9 day", freq="2D", name="idx")
tm.assert_index_equal(result, expected)
assert result.freq == expected.freq
result = idx[-20:-5:3]
expected = timedelta_range("12 day", "24 day", freq="3D", name="idx")
tm.assert_index_equal(result, expected)
assert result.freq == expected.freq
result = idx[4::-1]
expected = TimedeltaIndex(
["5 day", "4 day", "3 day", "2 day", "1 day"], freq="-1D", name="idx"
)
tm.assert_index_equal(result, expected)
assert result.freq == expected.freq
@pytest.mark.parametrize(
"key",
[
Timestamp("1970-01-01"),
Timestamp("1970-01-02"),
datetime(1970, 1, 1),
Timestamp("1970-01-03").to_datetime64(),
# non-matching NA values
np.datetime64("NaT"),
],
)
def test_timestamp_invalid_key(self, key):
# GH#20464
tdi = timedelta_range(0, periods=10)
with pytest.raises(KeyError, match=re.escape(repr(key))):
tdi.get_loc(key)
class TestGetLoc:
def test_get_loc_key_unit_mismatch(self):
idx = to_timedelta(["0 days", "1 days", "2 days"])
key = idx[1].as_unit("ms")
loc = idx.get_loc(key)
assert loc == 1
def test_get_loc_key_unit_mismatch_not_castable(self):
tdi = to_timedelta(["0 days", "1 days", "2 days"]).astype("m8[s]")
assert tdi.dtype == "m8[s]"
key = tdi[0].as_unit("ns") + Timedelta(1)
with pytest.raises(KeyError, match=r"Timedelta\('0 days 00:00:00.000000001'\)"):
tdi.get_loc(key)
assert key not in tdi
def test_get_loc(self):
idx = to_timedelta(["0 days", "1 days", "2 days"])
# GH 16909
assert idx.get_loc(idx[1].to_timedelta64()) == 1
# GH 16896
assert idx.get_loc("0 days") == 0
def test_get_loc_nat(self):
tidx = TimedeltaIndex(["1 days 01:00:00", "NaT", "2 days 01:00:00"])
assert tidx.get_loc(NaT) == 1
assert tidx.get_loc(None) == 1
assert tidx.get_loc(float("nan")) == 1
assert tidx.get_loc(np.nan) == 1
class TestGetIndexer:
def test_get_indexer(self):
idx = to_timedelta(["0 days", "1 days", "2 days"])
tm.assert_numpy_array_equal(
idx.get_indexer(idx), np.array([0, 1, 2], dtype=np.intp)
)
target = to_timedelta(["-1 hour", "12 hours", "1 day 1 hour"])
tm.assert_numpy_array_equal(
idx.get_indexer(target, "pad"), np.array([-1, 0, 1], dtype=np.intp)
)
tm.assert_numpy_array_equal(
idx.get_indexer(target, "backfill"), np.array([0, 1, 2], dtype=np.intp)
)
tm.assert_numpy_array_equal(
idx.get_indexer(target, "nearest"), np.array([0, 1, 1], dtype=np.intp)
)
res = idx.get_indexer(target, "nearest", tolerance=Timedelta("1 hour"))
tm.assert_numpy_array_equal(res, np.array([0, -1, 1], dtype=np.intp))
class TestWhere:
def test_where_doesnt_retain_freq(self):
tdi = timedelta_range("1 day", periods=3, freq="D", name="idx")
cond = [True, True, False]
expected = TimedeltaIndex([tdi[0], tdi[1], tdi[0]], freq=None, name="idx")
result = tdi.where(cond, tdi[::-1])
tm.assert_index_equal(result, expected)
def test_where_invalid_dtypes(self, fixed_now_ts):
tdi = timedelta_range("1 day", periods=3, freq="D", name="idx")
tail = tdi[2:].tolist()
i2 = Index([NaT, NaT] + tail)
mask = notna(i2)
expected = Index([NaT._value, NaT._value] + tail, dtype=object, name="idx")
assert isinstance(expected[0], int)
result = tdi.where(mask, i2.asi8)
tm.assert_index_equal(result, expected)
ts = i2 + fixed_now_ts
expected = Index([ts[0], ts[1]] + tail, dtype=object, name="idx")
result = tdi.where(mask, ts)
tm.assert_index_equal(result, expected)
per = (i2 + fixed_now_ts).to_period("D")
expected = Index([per[0], per[1]] + tail, dtype=object, name="idx")
result = tdi.where(mask, per)
tm.assert_index_equal(result, expected)
ts = fixed_now_ts
expected = Index([ts, ts] + tail, dtype=object, name="idx")
result = tdi.where(mask, ts)
tm.assert_index_equal(result, expected)
def test_where_mismatched_nat(self):
tdi = timedelta_range("1 day", periods=3, freq="D", name="idx")
cond = np.array([True, False, False])
dtnat = np.datetime64("NaT", "ns")
expected = Index([tdi[0], dtnat, dtnat], dtype=object, name="idx")
assert expected[2] is dtnat
result = tdi.where(cond, dtnat)
tm.assert_index_equal(result, expected)
class TestTake:
def test_take(self):
# GH 10295
idx1 = timedelta_range("1 day", "31 day", freq="D", name="idx")
for idx in [idx1]:
result = idx.take([0])
assert result == Timedelta("1 day")
result = idx.take([-1])
assert result == Timedelta("31 day")
result = idx.take([0, 1, 2])
expected = timedelta_range("1 day", "3 day", freq="D", name="idx")
tm.assert_index_equal(result, expected)
assert result.freq == expected.freq
result = idx.take([0, 2, 4])
expected = timedelta_range("1 day", "5 day", freq="2D", name="idx")
tm.assert_index_equal(result, expected)
assert result.freq == expected.freq
result = idx.take([7, 4, 1])
expected = timedelta_range("8 day", "2 day", freq="-3D", name="idx")
tm.assert_index_equal(result, expected)
assert result.freq == expected.freq
result = idx.take([3, 2, 5])
expected = TimedeltaIndex(["4 day", "3 day", "6 day"], name="idx")
tm.assert_index_equal(result, expected)
assert result.freq is None
result = idx.take([-3, 2, 5])
expected = TimedeltaIndex(["29 day", "3 day", "6 day"], name="idx")
tm.assert_index_equal(result, expected)
assert result.freq is None
def test_take_invalid_kwargs(self):
idx = timedelta_range("1 day", "31 day", freq="D", name="idx")
indices = [1, 6, 5, 9, 10, 13, 15, 3]
msg = r"take\(\) got an unexpected keyword argument 'foo'"
with pytest.raises(TypeError, match=msg):
idx.take(indices, foo=2)
msg = "the 'out' parameter is not supported"
with pytest.raises(ValueError, match=msg):
idx.take(indices, out=indices)
msg = "the 'mode' parameter is not supported"
with pytest.raises(ValueError, match=msg):
idx.take(indices, mode="clip")
def test_take_equiv_getitem(self):
tds = ["1day 02:00:00", "1 day 04:00:00", "1 day 10:00:00"]
idx = timedelta_range(start="1d", end="2d", freq="h", name="idx")
expected = TimedeltaIndex(tds, freq=None, name="idx")
taken1 = idx.take([2, 4, 10])
taken2 = idx[[2, 4, 10]]
for taken in [taken1, taken2]:
tm.assert_index_equal(taken, expected)
assert isinstance(taken, TimedeltaIndex)
assert taken.freq is None
assert taken.name == expected.name
def test_take_fill_value(self):
# GH 12631
idx = TimedeltaIndex(["1 days", "2 days", "3 days"], name="xxx")
result = idx.take(np.array([1, 0, -1]))
expected = TimedeltaIndex(["2 days", "1 days", "3 days"], name="xxx")
tm.assert_index_equal(result, expected)
# fill_value
result = idx.take(np.array([1, 0, -1]), fill_value=True)
expected = TimedeltaIndex(["2 days", "1 days", "NaT"], name="xxx")
tm.assert_index_equal(result, expected)
# allow_fill=False
result = idx.take(np.array([1, 0, -1]), allow_fill=False, fill_value=True)
expected = TimedeltaIndex(["2 days", "1 days", "3 days"], name="xxx")
tm.assert_index_equal(result, expected)
msg = (
"When allow_fill=True and fill_value is not None, "
"all indices must be >= -1"
)
with pytest.raises(ValueError, match=msg):
idx.take(np.array([1, 0, -2]), fill_value=True)
with pytest.raises(ValueError, match=msg):
idx.take(np.array([1, 0, -5]), fill_value=True)
msg = "index -5 is out of bounds for (axis 0 with )?size 3"
with pytest.raises(IndexError, match=msg):
idx.take(np.array([1, -5]))
class TestMaybeCastSliceBound:
@pytest.fixture(params=["increasing", "decreasing", None])
def monotonic(self, request):
return request.param
@pytest.fixture
def tdi(self, monotonic):
tdi = timedelta_range("1 Day", periods=10)
if monotonic == "decreasing":
tdi = tdi[::-1]
elif monotonic is None:
taker = np.arange(10, dtype=np.intp)
np.random.default_rng(2).shuffle(taker)
tdi = tdi.take(taker)
return tdi
def test_maybe_cast_slice_bound_invalid_str(self, tdi):
# test the low-level _maybe_cast_slice_bound and that we get the
# expected exception+message all the way up the stack
msg = (
"cannot do slice indexing on TimedeltaIndex with these "
r"indexers \[foo\] of type str"
)
with pytest.raises(TypeError, match=msg):
tdi._maybe_cast_slice_bound("foo", side="left")
with pytest.raises(TypeError, match=msg):
tdi.get_slice_bound("foo", side="left")
with pytest.raises(TypeError, match=msg):
tdi.slice_locs("foo", None, None)
def test_slice_invalid_str_with_timedeltaindex(
self, tdi, frame_or_series, indexer_sl
):
obj = frame_or_series(range(10), index=tdi)
msg = (
"cannot do slice indexing on TimedeltaIndex with these "
r"indexers \[foo\] of type str"
)
with pytest.raises(TypeError, match=msg):
indexer_sl(obj)["foo":]
with pytest.raises(TypeError, match=msg):
indexer_sl(obj)["foo":-1]
with pytest.raises(TypeError, match=msg):
indexer_sl(obj)[:"foo"]
with pytest.raises(TypeError, match=msg):
indexer_sl(obj)[tdi[0] : "foo"]
class TestContains:
def test_contains_nonunique(self):
# GH#9512
for vals in (
[0, 1, 0],
[0, 0, -1],
[0, -1, -1],
["00:01:00", "00:01:00", "00:02:00"],
["00:01:00", "00:01:00", "00:00:01"],
):
idx = TimedeltaIndex(vals)
assert idx[0] in idx
def test_contains(self):
# Checking for any NaT-like objects
# GH#13603
td = to_timedelta(range(5), unit="d") + offsets.Hour(1)
for v in [NaT, None, float("nan"), np.nan]:
assert v not in td
td = to_timedelta([NaT])
for v in [NaT, None, float("nan"), np.nan]:
assert v in td

View File

@ -0,0 +1,47 @@
import numpy as np
from pandas import (
DataFrame,
Index,
Timedelta,
timedelta_range,
)
import pandas._testing as tm
class TestJoin:
def test_append_join_nondatetimeindex(self):
rng = timedelta_range("1 days", periods=10)
idx = Index(["a", "b", "c", "d"])
result = rng.append(idx)
assert isinstance(result[0], Timedelta)
# it works
rng.join(idx, how="outer")
def test_join_self(self, join_type):
index = timedelta_range("1 day", periods=10)
joined = index.join(index, how=join_type)
tm.assert_index_equal(index, joined)
def test_does_not_convert_mixed_integer(self):
df = DataFrame(np.ones((5, 5)), columns=timedelta_range("1 day", periods=5))
cols = df.columns.join(df.index, how="outer")
joined = cols.join(df.columns)
assert cols.dtype == np.dtype("O")
assert cols.dtype == joined.dtype
tm.assert_index_equal(cols, joined)
def test_join_preserves_freq(self):
# GH#32157
tdi = timedelta_range("1 day", periods=10)
result = tdi[:5].join(tdi[5:], how="outer")
assert result.freq == tdi.freq
tm.assert_index_equal(result, tdi)
result = tdi[:5].join(tdi[6:], how="outer")
assert result.freq is None
expected = tdi.delete(5)
tm.assert_index_equal(result, expected)

View File

@ -0,0 +1,14 @@
from pandas import (
TimedeltaIndex,
timedelta_range,
)
import pandas._testing as tm
class TestTimedeltaIndexOps:
def test_infer_freq(self, freq_sample):
# GH#11018
idx = timedelta_range("1", freq=freq_sample, periods=10)
result = TimedeltaIndex(idx.asi8, freq="infer")
tm.assert_index_equal(idx, result)
assert result.freq == freq_sample

View File

@ -0,0 +1,11 @@
from pandas import timedelta_range
import pandas._testing as tm
class TestPickle:
def test_pickle_after_set_freq(self):
tdi = timedelta_range("1 day", periods=4, freq="s")
tdi = tdi._with_freq(None)
res = tm.round_trip_pickle(tdi)
tm.assert_index_equal(res, tdi)

View File

@ -0,0 +1,142 @@
"""
Tests for TimedeltaIndex methods behaving like their Timedelta counterparts
"""
import numpy as np
import pytest
from pandas._libs.tslibs.offsets import INVALID_FREQ_ERR_MSG
from pandas import (
Index,
Series,
Timedelta,
TimedeltaIndex,
timedelta_range,
)
import pandas._testing as tm
class TestVectorizedTimedelta:
def test_tdi_total_seconds(self):
# GH#10939
# test index
rng = timedelta_range("1 days, 10:11:12.100123456", periods=2, freq="s")
expt = [
1 * 86400 + 10 * 3600 + 11 * 60 + 12 + 100123456.0 / 1e9,
1 * 86400 + 10 * 3600 + 11 * 60 + 13 + 100123456.0 / 1e9,
]
tm.assert_almost_equal(rng.total_seconds(), Index(expt))
# test Series
ser = Series(rng)
s_expt = Series(expt, index=[0, 1])
tm.assert_series_equal(ser.dt.total_seconds(), s_expt)
# with nat
ser[1] = np.nan
s_expt = Series(
[1 * 86400 + 10 * 3600 + 11 * 60 + 12 + 100123456.0 / 1e9, np.nan],
index=[0, 1],
)
tm.assert_series_equal(ser.dt.total_seconds(), s_expt)
def test_tdi_total_seconds_all_nat(self):
# with both nat
ser = Series([np.nan, np.nan], dtype="timedelta64[ns]")
result = ser.dt.total_seconds()
expected = Series([np.nan, np.nan])
tm.assert_series_equal(result, expected)
def test_tdi_round(self):
td = timedelta_range(start="16801 days", periods=5, freq="30Min")
elt = td[1]
expected_rng = TimedeltaIndex(
[
Timedelta("16801 days 00:00:00"),
Timedelta("16801 days 00:00:00"),
Timedelta("16801 days 01:00:00"),
Timedelta("16801 days 02:00:00"),
Timedelta("16801 days 02:00:00"),
]
)
expected_elt = expected_rng[1]
tm.assert_index_equal(td.round(freq="h"), expected_rng)
assert elt.round(freq="h") == expected_elt
msg = INVALID_FREQ_ERR_MSG
with pytest.raises(ValueError, match=msg):
td.round(freq="foo")
with pytest.raises(ValueError, match=msg):
elt.round(freq="foo")
msg = "<MonthEnd> is a non-fixed frequency"
with pytest.raises(ValueError, match=msg):
td.round(freq="ME")
with pytest.raises(ValueError, match=msg):
elt.round(freq="ME")
@pytest.mark.parametrize(
"freq,msg",
[
("YE", "<YearEnd: month=12> is a non-fixed frequency"),
("ME", "<MonthEnd> is a non-fixed frequency"),
("foobar", "Invalid frequency: foobar"),
],
)
def test_tdi_round_invalid(self, freq, msg):
t1 = timedelta_range("1 days", periods=3, freq="1 min 2 s 3 us")
with pytest.raises(ValueError, match=msg):
t1.round(freq)
with pytest.raises(ValueError, match=msg):
# Same test for TimedeltaArray
t1._data.round(freq)
# TODO: de-duplicate with test_tdi_round
def test_round(self):
t1 = timedelta_range("1 days", periods=3, freq="1 min 2 s 3 us")
t2 = -1 * t1
t1a = timedelta_range("1 days", periods=3, freq="1 min 2 s")
t1c = TimedeltaIndex(np.array([1, 1, 1], "m8[D]")).as_unit("ns")
# note that negative times round DOWN! so don't give whole numbers
for freq, s1, s2 in [
("ns", t1, t2),
("us", t1, t2),
(
"ms",
t1a,
TimedeltaIndex(
["-1 days +00:00:00", "-2 days +23:58:58", "-2 days +23:57:56"]
),
),
(
"s",
t1a,
TimedeltaIndex(
["-1 days +00:00:00", "-2 days +23:58:58", "-2 days +23:57:56"]
),
),
("12min", t1c, TimedeltaIndex(["-1 days", "-1 days", "-1 days"])),
("h", t1c, TimedeltaIndex(["-1 days", "-1 days", "-1 days"])),
("d", t1c, -1 * t1c),
]:
r1 = t1.round(freq)
tm.assert_index_equal(r1, s1)
r2 = t2.round(freq)
tm.assert_index_equal(r2, s2)
def test_components(self):
rng = timedelta_range("1 days, 10:11:12", periods=2, freq="s")
rng.components
# with nat
s = Series(rng)
s[1] = np.nan
result = s.dt.components
assert not result.iloc[0].isna().all()
assert result.iloc[1].isna().all()

View File

@ -0,0 +1,28 @@
import numpy as np
import pytest
from pandas import (
TimedeltaIndex,
Timestamp,
)
import pandas._testing as tm
class TestSearchSorted:
def test_searchsorted_different_argument_classes(self, listlike_box):
idx = TimedeltaIndex(["1 day", "2 days", "3 days"])
result = idx.searchsorted(listlike_box(idx))
expected = np.arange(len(idx), dtype=result.dtype)
tm.assert_numpy_array_equal(result, expected)
result = idx._data.searchsorted(listlike_box(idx))
tm.assert_numpy_array_equal(result, expected)
@pytest.mark.parametrize(
"arg", [[1, 2], ["a", "b"], [Timestamp("2020-01-01", tz="Europe/London")] * 2]
)
def test_searchsorted_invalid_argument_dtype(self, arg):
idx = TimedeltaIndex(["1 day", "2 days", "3 days"])
msg = "value should be a 'Timedelta', 'NaT', or array of those. Got"
with pytest.raises(TypeError, match=msg):
idx.searchsorted(arg)

View File

@ -0,0 +1,254 @@
import numpy as np
import pytest
import pandas as pd
from pandas import (
Index,
TimedeltaIndex,
timedelta_range,
)
import pandas._testing as tm
from pandas.tseries.offsets import Hour
class TestTimedeltaIndex:
def test_union(self):
i1 = timedelta_range("1day", periods=5)
i2 = timedelta_range("3day", periods=5)
result = i1.union(i2)
expected = timedelta_range("1day", periods=7)
tm.assert_index_equal(result, expected)
i1 = Index(np.arange(0, 20, 2, dtype=np.int64))
i2 = timedelta_range(start="1 day", periods=10, freq="D")
i1.union(i2) # Works
i2.union(i1) # Fails with "AttributeError: can't set attribute"
def test_union_sort_false(self):
tdi = timedelta_range("1day", periods=5)
left = tdi[3:]
right = tdi[:3]
# Check that we are testing the desired code path
assert left._can_fast_union(right)
result = left.union(right)
tm.assert_index_equal(result, tdi)
result = left.union(right, sort=False)
expected = TimedeltaIndex(["4 Days", "5 Days", "1 Days", "2 Day", "3 Days"])
tm.assert_index_equal(result, expected)
def test_union_coverage(self):
idx = TimedeltaIndex(["3d", "1d", "2d"])
ordered = TimedeltaIndex(idx.sort_values(), freq="infer")
result = ordered.union(idx)
tm.assert_index_equal(result, ordered)
result = ordered[:0].union(ordered)
tm.assert_index_equal(result, ordered)
assert result.freq == ordered.freq
def test_union_bug_1730(self):
rng_a = timedelta_range("1 day", periods=4, freq="3h")
rng_b = timedelta_range("1 day", periods=4, freq="4h")
result = rng_a.union(rng_b)
exp = TimedeltaIndex(sorted(set(rng_a) | set(rng_b)))
tm.assert_index_equal(result, exp)
def test_union_bug_1745(self):
left = TimedeltaIndex(["1 day 15:19:49.695000"])
right = TimedeltaIndex(
["2 day 13:04:21.322000", "1 day 15:27:24.873000", "1 day 15:31:05.350000"]
)
result = left.union(right)
exp = TimedeltaIndex(sorted(set(left) | set(right)))
tm.assert_index_equal(result, exp)
def test_union_bug_4564(self):
left = timedelta_range("1 day", "30d")
right = left + pd.offsets.Minute(15)
result = left.union(right)
exp = TimedeltaIndex(sorted(set(left) | set(right)))
tm.assert_index_equal(result, exp)
def test_union_freq_infer(self):
# When taking the union of two TimedeltaIndexes, we infer
# a freq even if the arguments don't have freq. This matches
# DatetimeIndex behavior.
tdi = timedelta_range("1 Day", periods=5)
left = tdi[[0, 1, 3, 4]]
right = tdi[[2, 3, 1]]
assert left.freq is None
assert right.freq is None
result = left.union(right)
tm.assert_index_equal(result, tdi)
assert result.freq == "D"
def test_intersection_bug_1708(self):
index_1 = timedelta_range("1 day", periods=4, freq="h")
index_2 = index_1 + pd.offsets.Hour(5)
result = index_1.intersection(index_2)
assert len(result) == 0
index_1 = timedelta_range("1 day", periods=4, freq="h")
index_2 = index_1 + pd.offsets.Hour(1)
result = index_1.intersection(index_2)
expected = timedelta_range("1 day 01:00:00", periods=3, freq="h")
tm.assert_index_equal(result, expected)
assert result.freq == expected.freq
def test_intersection_equal(self, sort):
# GH 24471 Test intersection outcome given the sort keyword
# for equal indices intersection should return the original index
first = timedelta_range("1 day", periods=4, freq="h")
second = timedelta_range("1 day", periods=4, freq="h")
intersect = first.intersection(second, sort=sort)
if sort is None:
tm.assert_index_equal(intersect, second.sort_values())
tm.assert_index_equal(intersect, second)
# Corner cases
inter = first.intersection(first, sort=sort)
assert inter is first
@pytest.mark.parametrize("period_1, period_2", [(0, 4), (4, 0)])
def test_intersection_zero_length(self, period_1, period_2, sort):
# GH 24471 test for non overlap the intersection should be zero length
index_1 = timedelta_range("1 day", periods=period_1, freq="h")
index_2 = timedelta_range("1 day", periods=period_2, freq="h")
expected = timedelta_range("1 day", periods=0, freq="h")
result = index_1.intersection(index_2, sort=sort)
tm.assert_index_equal(result, expected)
def test_zero_length_input_index(self, sort):
# GH 24966 test for 0-len intersections are copied
index_1 = timedelta_range("1 day", periods=0, freq="h")
index_2 = timedelta_range("1 day", periods=3, freq="h")
result = index_1.intersection(index_2, sort=sort)
assert index_1 is not result
assert index_2 is not result
tm.assert_copy(result, index_1)
@pytest.mark.parametrize(
"rng, expected",
# if target has the same name, it is preserved
[
(
timedelta_range("1 day", periods=5, freq="h", name="idx"),
timedelta_range("1 day", periods=4, freq="h", name="idx"),
),
# if target name is different, it will be reset
(
timedelta_range("1 day", periods=5, freq="h", name="other"),
timedelta_range("1 day", periods=4, freq="h", name=None),
),
# if no overlap exists return empty index
(
timedelta_range("1 day", periods=10, freq="h", name="idx")[5:],
TimedeltaIndex([], freq="h", name="idx"),
),
],
)
def test_intersection(self, rng, expected, sort):
# GH 4690 (with tz)
base = timedelta_range("1 day", periods=4, freq="h", name="idx")
result = base.intersection(rng, sort=sort)
if sort is None:
expected = expected.sort_values()
tm.assert_index_equal(result, expected)
assert result.name == expected.name
assert result.freq == expected.freq
@pytest.mark.parametrize(
"rng, expected",
# part intersection works
[
(
TimedeltaIndex(["5 hour", "2 hour", "4 hour", "9 hour"], name="idx"),
TimedeltaIndex(["2 hour", "4 hour"], name="idx"),
),
# reordered part intersection
(
TimedeltaIndex(["2 hour", "5 hour", "5 hour", "1 hour"], name="other"),
TimedeltaIndex(["1 hour", "2 hour"], name=None),
),
# reversed index
(
TimedeltaIndex(["1 hour", "2 hour", "4 hour", "3 hour"], name="idx")[
::-1
],
TimedeltaIndex(["1 hour", "2 hour", "4 hour", "3 hour"], name="idx"),
),
],
)
def test_intersection_non_monotonic(self, rng, expected, sort):
# 24471 non-monotonic
base = TimedeltaIndex(["1 hour", "2 hour", "4 hour", "3 hour"], name="idx")
result = base.intersection(rng, sort=sort)
if sort is None:
expected = expected.sort_values()
tm.assert_index_equal(result, expected)
assert result.name == expected.name
# if reversed order, frequency is still the same
if all(base == rng[::-1]) and sort is None:
assert isinstance(result.freq, Hour)
else:
assert result.freq is None
class TestTimedeltaIndexDifference:
def test_difference_freq(self, sort):
# GH14323: Difference of TimedeltaIndex should not preserve frequency
index = timedelta_range("0 days", "5 days", freq="D")
other = timedelta_range("1 days", "4 days", freq="D")
expected = TimedeltaIndex(["0 days", "5 days"], freq=None)
idx_diff = index.difference(other, sort)
tm.assert_index_equal(idx_diff, expected)
tm.assert_attr_equal("freq", idx_diff, expected)
# preserve frequency when the difference is a contiguous
# subset of the original range
other = timedelta_range("2 days", "5 days", freq="D")
idx_diff = index.difference(other, sort)
expected = TimedeltaIndex(["0 days", "1 days"], freq="D")
tm.assert_index_equal(idx_diff, expected)
tm.assert_attr_equal("freq", idx_diff, expected)
def test_difference_sort(self, sort):
index = TimedeltaIndex(
["5 days", "3 days", "2 days", "4 days", "1 days", "0 days"]
)
other = timedelta_range("1 days", "4 days", freq="D")
idx_diff = index.difference(other, sort)
expected = TimedeltaIndex(["5 days", "0 days"], freq=None)
if sort is None:
expected = expected.sort_values()
tm.assert_index_equal(idx_diff, expected)
tm.assert_attr_equal("freq", idx_diff, expected)
other = timedelta_range("2 days", "5 days", freq="D")
idx_diff = index.difference(other, sort)
expected = TimedeltaIndex(["1 days", "0 days"], freq=None)
if sort is None:
expected = expected.sort_values()
tm.assert_index_equal(idx_diff, expected)
tm.assert_attr_equal("freq", idx_diff, expected)

View File

@ -0,0 +1,61 @@
import numpy as np
import pytest
from pandas import (
Index,
Series,
Timedelta,
timedelta_range,
)
import pandas._testing as tm
class TestTimedeltaIndex:
def test_misc_coverage(self):
rng = timedelta_range("1 day", periods=5)
result = rng.groupby(rng.days)
assert isinstance(next(iter(result.values()))[0], Timedelta)
def test_map(self):
# test_map_dictlike generally tests
rng = timedelta_range("1 day", periods=10)
f = lambda x: x.days
result = rng.map(f)
exp = Index([f(x) for x in rng], dtype=np.int64)
tm.assert_index_equal(result, exp)
def test_fields(self):
rng = timedelta_range("1 days, 10:11:12.100123456", periods=2, freq="s")
tm.assert_index_equal(rng.days, Index([1, 1], dtype=np.int64))
tm.assert_index_equal(
rng.seconds,
Index([10 * 3600 + 11 * 60 + 12, 10 * 3600 + 11 * 60 + 13], dtype=np.int32),
)
tm.assert_index_equal(
rng.microseconds,
Index([100 * 1000 + 123, 100 * 1000 + 123], dtype=np.int32),
)
tm.assert_index_equal(rng.nanoseconds, Index([456, 456], dtype=np.int32))
msg = "'TimedeltaIndex' object has no attribute '{}'"
with pytest.raises(AttributeError, match=msg.format("hours")):
rng.hours
with pytest.raises(AttributeError, match=msg.format("minutes")):
rng.minutes
with pytest.raises(AttributeError, match=msg.format("milliseconds")):
rng.milliseconds
# with nat
s = Series(rng)
s[1] = np.nan
tm.assert_series_equal(s.dt.days, Series([1, np.nan], index=[0, 1]))
tm.assert_series_equal(
s.dt.seconds, Series([10 * 3600 + 11 * 60 + 12, np.nan], index=[0, 1])
)
# preserve name (GH15589)
rng.name = "name"
assert rng.days.name == "name"

View File

@ -0,0 +1,173 @@
import numpy as np
import pytest
from pandas import (
Timedelta,
TimedeltaIndex,
timedelta_range,
to_timedelta,
)
import pandas._testing as tm
from pandas.tseries.offsets import (
Day,
Second,
)
class TestTimedeltas:
def test_timedelta_range_unit(self):
# GH#49824
tdi = timedelta_range("0 Days", periods=10, freq="100000D", unit="s")
exp_arr = (np.arange(10, dtype="i8") * 100_000).view("m8[D]").astype("m8[s]")
tm.assert_numpy_array_equal(tdi.to_numpy(), exp_arr)
def test_timedelta_range(self):
expected = to_timedelta(np.arange(5), unit="D")
result = timedelta_range("0 days", periods=5, freq="D")
tm.assert_index_equal(result, expected)
expected = to_timedelta(np.arange(11), unit="D")
result = timedelta_range("0 days", "10 days", freq="D")
tm.assert_index_equal(result, expected)
expected = to_timedelta(np.arange(5), unit="D") + Second(2) + Day()
result = timedelta_range("1 days, 00:00:02", "5 days, 00:00:02", freq="D")
tm.assert_index_equal(result, expected)
expected = to_timedelta([1, 3, 5, 7, 9], unit="D") + Second(2)
result = timedelta_range("1 days, 00:00:02", periods=5, freq="2D")
tm.assert_index_equal(result, expected)
expected = to_timedelta(np.arange(50), unit="min") * 30
result = timedelta_range("0 days", freq="30min", periods=50)
tm.assert_index_equal(result, expected)
@pytest.mark.parametrize(
"depr_unit, unit",
[
("H", "hour"),
("T", "minute"),
("t", "minute"),
("S", "second"),
("L", "millisecond"),
("l", "millisecond"),
("U", "microsecond"),
("u", "microsecond"),
("N", "nanosecond"),
("n", "nanosecond"),
],
)
def test_timedelta_units_H_T_S_L_U_N_deprecated(self, depr_unit, unit):
# GH#52536
depr_msg = (
f"'{depr_unit}' is deprecated and will be removed in a future version."
)
expected = to_timedelta(np.arange(5), unit=unit)
with tm.assert_produces_warning(FutureWarning, match=depr_msg):
result = to_timedelta(np.arange(5), unit=depr_unit)
tm.assert_index_equal(result, expected)
@pytest.mark.parametrize(
"periods, freq", [(3, "2D"), (5, "D"), (6, "19h12min"), (7, "16h"), (9, "12h")]
)
def test_linspace_behavior(self, periods, freq):
# GH 20976
result = timedelta_range(start="0 days", end="4 days", periods=periods)
expected = timedelta_range(start="0 days", end="4 days", freq=freq)
tm.assert_index_equal(result, expected)
@pytest.mark.parametrize("msg_freq, freq", [("H", "19H12min"), ("T", "19h12T")])
def test_timedelta_range_H_T_deprecated(self, freq, msg_freq):
# GH#52536
msg = f"'{msg_freq}' is deprecated and will be removed in a future version."
result = timedelta_range(start="0 days", end="4 days", periods=6)
with tm.assert_produces_warning(FutureWarning, match=msg):
expected = timedelta_range(start="0 days", end="4 days", freq=freq)
tm.assert_index_equal(result, expected)
def test_errors(self):
# not enough params
msg = (
"Of the four parameters: start, end, periods, and freq, "
"exactly three must be specified"
)
with pytest.raises(ValueError, match=msg):
timedelta_range(start="0 days")
with pytest.raises(ValueError, match=msg):
timedelta_range(end="5 days")
with pytest.raises(ValueError, match=msg):
timedelta_range(periods=2)
with pytest.raises(ValueError, match=msg):
timedelta_range()
# too many params
with pytest.raises(ValueError, match=msg):
timedelta_range(start="0 days", end="5 days", periods=10, freq="h")
@pytest.mark.parametrize(
"start, end, freq, expected_periods",
[
("1D", "10D", "2D", (10 - 1) // 2 + 1),
("2D", "30D", "3D", (30 - 2) // 3 + 1),
("2s", "50s", "5s", (50 - 2) // 5 + 1),
# tests that worked before GH 33498:
("4D", "16D", "3D", (16 - 4) // 3 + 1),
("8D", "16D", "40s", (16 * 3600 * 24 - 8 * 3600 * 24) // 40 + 1),
],
)
def test_timedelta_range_freq_divide_end(self, start, end, freq, expected_periods):
# GH 33498 only the cases where `(end % freq) == 0` used to fail
res = timedelta_range(start=start, end=end, freq=freq)
assert Timedelta(start) == res[0]
assert Timedelta(end) >= res[-1]
assert len(res) == expected_periods
def test_timedelta_range_infer_freq(self):
# https://github.com/pandas-dev/pandas/issues/35897
result = timedelta_range("0s", "1s", periods=31)
assert result.freq is None
@pytest.mark.parametrize(
"freq_depr, start, end, expected_values, expected_freq",
[
(
"3.5S",
"05:03:01",
"05:03:10",
["0 days 05:03:01", "0 days 05:03:04.500000", "0 days 05:03:08"],
"3500ms",
),
(
"2.5T",
"5 hours",
"5 hours 8 minutes",
[
"0 days 05:00:00",
"0 days 05:02:30",
"0 days 05:05:00",
"0 days 05:07:30",
],
"150s",
),
],
)
def test_timedelta_range_deprecated_freq(
self, freq_depr, start, end, expected_values, expected_freq
):
# GH#52536
msg = (
f"'{freq_depr[-1]}' is deprecated and will be removed in a future version."
)
with tm.assert_produces_warning(FutureWarning, match=msg):
result = timedelta_range(start=start, end=end, freq=freq_depr)
expected = TimedeltaIndex(
expected_values, dtype="timedelta64[ns]", freq=expected_freq
)
tm.assert_index_equal(result, expected)