Skip to content

REF: organize Timedelta, Index tests #55675

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions pandas/tests/indexes/period/test_formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@ def test_get_values_for_csv():


class TestPeriodIndexRendering:
def test_format_empty(self):
# GH#35712
empty_idx = PeriodIndex([], freq="Y")
msg = r"PeriodIndex\.format is deprecated"
with tm.assert_produces_warning(FutureWarning, match=msg):
assert empty_idx.format() == []
with tm.assert_produces_warning(FutureWarning, match=msg):
assert empty_idx.format(name=True) == [""]

def test_frame_repr(self):
df = pd.DataFrame({"A": [1, 2, 3]}, index=pd.date_range("2000", periods=3))
result = repr(df)
Expand Down
9 changes: 0 additions & 9 deletions pandas/tests/indexes/period/test_period.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,15 +272,6 @@ def test_map(self):
exp = Index([x.ordinal for x in index])
tm.assert_index_equal(result, exp)

def test_format_empty(self):
# GH35712
empty_idx = PeriodIndex([], freq="Y")
msg = r"PeriodIndex\.format is deprecated"
with tm.assert_produces_warning(FutureWarning, match=msg):
assert empty_idx.format() == []
with tm.assert_produces_warning(FutureWarning, match=msg):
assert empty_idx.format(name=True) == [""]

def test_period_index_frequency_ME_error_message(self):
msg = "Invalid frequency: 2ME"

Expand Down
8 changes: 8 additions & 0 deletions pandas/tests/indexes/test_index_new.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,14 @@ def test_constructor_dtypes_to_timedelta(self, cast_index, vals):
index = Index(vals)
assert isinstance(index, TimedeltaIndex)

def test_pass_timedeltaindex_to_index(self):
rng = timedelta_range("1 days", "10 days")
idx = Index(rng, dtype=object)

expected = Index(rng.to_pytimedelta(), dtype=object)

tm.assert_numpy_array_equal(idx.values, expected.values)


class TestIndexConstructorUnwrapping:
# Test passing different arraylike values to pd.Index
Expand Down
51 changes: 51 additions & 0 deletions pandas/tests/indexes/timedeltas/methods/test_astype.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
timedelta_range,
)
import pandas._testing as tm
from pandas.core.arrays import TimedeltaArray


class TestTimedeltaIndex:
Expand Down Expand Up @@ -95,6 +96,56 @@ def test_astype_timedelta64(self):
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
Expand Down
51 changes: 51 additions & 0 deletions pandas/tests/indexes/timedeltas/test_arithmetic.py
Original file line number Diff line number Diff line change
@@ -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)
93 changes: 0 additions & 93 deletions pandas/tests/indexes/timedeltas/test_timedelta.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
from datetime import timedelta

import numpy as np
import pytest

from pandas import (
Index,
NaT,
Series,
Timedelta,
timedelta_range,
)
import pandas._testing as tm
from pandas.core.arrays import TimedeltaArray


class TestTimedeltaIndex:
@pytest.fixture
def index(self):
return tm.makeTimedeltaIndex(10)

def test_misc_coverage(self):
rng = timedelta_range("1 day", periods=5)
result = rng.groupby(rng.days)
Expand All @@ -34,14 +26,6 @@ def test_map(self):
exp = Index([f(x) for x in rng], dtype=np.int64)
tm.assert_index_equal(result, exp)

def test_pass_TimedeltaIndex_to_index(self):
rng = timedelta_range("1 days", "10 days")
idx = Index(rng, dtype=object)

expected = Index(rng.to_pytimedelta(), dtype=object)

tm.assert_numpy_array_equal(idx.values, expected.values)

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))
Expand Down Expand Up @@ -75,80 +59,3 @@ def test_fields(self):
# preserve name (GH15589)
rng.name = "name"
assert rng.days.name == "name"

def test_freq_conversion_always_floating(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))

def test_freq_conversion(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)

# We don't support "D" reso, so we use the pre-2.0 behavior
# casting to float64
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]")

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)

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_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)
Empty file.
80 changes: 80 additions & 0 deletions pandas/tests/scalar/timedelta/methods/test_as_unit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import pytest

from pandas._libs.tslibs.dtypes import NpyDatetimeUnit
from pandas.errors import OutOfBoundsTimedelta

from pandas import Timedelta


class TestAsUnit:
def test_as_unit(self):
td = Timedelta(days=1)

assert td.as_unit("ns") is td

res = td.as_unit("us")
assert res._value == td._value // 1000
assert res._creso == NpyDatetimeUnit.NPY_FR_us.value

rt = res.as_unit("ns")
assert rt._value == td._value
assert rt._creso == td._creso

res = td.as_unit("ms")
assert res._value == td._value // 1_000_000
assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value

rt = res.as_unit("ns")
assert rt._value == td._value
assert rt._creso == td._creso

res = td.as_unit("s")
assert res._value == td._value // 1_000_000_000
assert res._creso == NpyDatetimeUnit.NPY_FR_s.value

rt = res.as_unit("ns")
assert rt._value == td._value
assert rt._creso == td._creso

def test_as_unit_overflows(self):
# microsecond that would be just out of bounds for nano
us = 9223372800000000
td = Timedelta._from_value_and_reso(us, NpyDatetimeUnit.NPY_FR_us.value)

msg = "Cannot cast 106752 days 00:00:00 to unit='ns' without overflow"
with pytest.raises(OutOfBoundsTimedelta, match=msg):
td.as_unit("ns")

res = td.as_unit("ms")
assert res._value == us // 1000
assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value

def test_as_unit_rounding(self):
td = Timedelta(microseconds=1500)
res = td.as_unit("ms")

expected = Timedelta(milliseconds=1)
assert res == expected

assert res._creso == NpyDatetimeUnit.NPY_FR_ms.value
assert res._value == 1

with pytest.raises(ValueError, match="Cannot losslessly convert units"):
td.as_unit("ms", round_ok=False)

def test_as_unit_non_nano(self):
# case where we are going neither to nor from nano
td = Timedelta(days=1).as_unit("ms")
assert td.days == 1
assert td._value == 86_400_000
assert td.components.days == 1
assert td._d == 1
assert td.total_seconds() == 86400

res = td.as_unit("us")
assert res._value == 86_400_000_000
assert res.components.days == 1
assert res.components.hours == 0
assert res._d == 1
assert res._h == 0
assert res.total_seconds() == 86400
Loading