Skip to content

Implement TimedeltaArray._from_sequence #23953

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 7 commits into from
Nov 29, 2018
24 changes: 15 additions & 9 deletions pandas/core/arrays/timedeltas.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,11 +163,17 @@ def _simple_new(cls, values, freq=None, dtype=_TD_DTYPE):
return result

def __new__(cls, values, freq=None, dtype=_TD_DTYPE, copy=False):
return cls._from_sequence(values, freq=freq, dtype=dtype, copy=copy)

@classmethod
def _from_sequence(cls, data, freq=None, unit=None,
dtype=_TD_DTYPE, copy=False):
if dtype != _TD_DTYPE:
raise ValueError("Only timedelta64[ns] dtype is valid.")

freq, freq_infer = dtl.maybe_infer_freq(freq)

values, inferred_freq = sequence_to_td64ns(
values, copy=copy, unit=None)
data, inferred_freq = sequence_to_td64ns(data, copy=copy, unit=unit)
if inferred_freq is not None:
if freq is not None and freq != inferred_freq:
raise ValueError('Inferred frequency {inferred} from passed '
Expand All @@ -179,13 +185,13 @@ def __new__(cls, values, freq=None, dtype=_TD_DTYPE, copy=False):
freq = inferred_freq
freq_infer = False

result = cls._simple_new(values, freq=freq)
# check that we are matching freqs
if inferred_freq is None and len(result) > 0:
if freq is not None and not freq_infer:
cls._validate_frequency(result, freq)
result = cls._simple_new(data, freq=freq)

if inferred_freq is None and freq is not None:
# this condition precludes `freq_infer`
cls._validate_frequency(result, freq)

if freq_infer:
elif freq_infer:
result.freq = to_offset(result.inferred_freq)

return result
Expand Down Expand Up @@ -666,7 +672,7 @@ def sequence_to_td64ns(data, copy=False, unit="ns", errors="raise"):
warnings.warn("Passing datetime64-dtype data to TimedeltaIndex is "
"deprecated, will raise a TypeError in a future "
"version",
FutureWarning, stacklevel=3)
FutureWarning, stacklevel=4)
data = ensure_int64(data).view(_TD_DTYPE)

else:
Expand Down
35 changes: 7 additions & 28 deletions pandas/core/indexes/timedeltas.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@

from pandas.core.arrays import datetimelike as dtl
from pandas.core.arrays.timedeltas import (
TimedeltaArrayMixin as TimedeltaArray, _is_convertible_to_td, _to_m8,
sequence_to_td64ns)
TimedeltaArrayMixin as TimedeltaArray, _is_convertible_to_td, _to_m8)
from pandas.core.base import _shared_docs
import pandas.core.common as com
from pandas.core.indexes.base import Index, _index_shared_docs
Expand Down Expand Up @@ -132,7 +131,7 @@ def _join_i8_wrapper(joinf, **kwargs):
# Constructors

def __new__(cls, data=None, unit=None, freq=None, start=None, end=None,
periods=None, closed=None, dtype=None, copy=False,
periods=None, closed=None, dtype=_TD_DTYPE, copy=False,
name=None, verify_integrity=None):

if verify_integrity is not None:
Expand All @@ -142,9 +141,8 @@ def __new__(cls, data=None, unit=None, freq=None, start=None, end=None,
else:
verify_integrity = True

freq, freq_infer = dtl.maybe_infer_freq(freq)

if data is None:
freq, freq_infer = dtl.maybe_infer_freq(freq)
warnings.warn("Creating a TimedeltaIndex by passing range "
"endpoints is deprecated. Use "
"`pandas.timedelta_range` instead.",
Expand All @@ -167,29 +165,10 @@ def __new__(cls, data=None, unit=None, freq=None, start=None, end=None,

# - Cases checked above all return/raise before reaching here - #

data, inferred_freq = sequence_to_td64ns(data, copy=copy, unit=unit)
if inferred_freq is not None:
if freq is not None and freq != inferred_freq:
raise ValueError('Inferred frequency {inferred} from passed '
'values does not conform to passed frequency '
'{passed}'
.format(inferred=inferred_freq,
passed=freq.freqstr))
elif freq_infer:
freq = inferred_freq
freq_infer = False
verify_integrity = False

subarr = cls._simple_new(data, name=name, freq=freq)
# check that we are matching freqs
if verify_integrity and len(subarr) > 0:
if freq is not None and not freq_infer:
cls._validate_frequency(subarr, freq)

if freq_infer:
subarr.freq = to_offset(subarr.inferred_freq)

return subarr
result = cls._from_sequence(data, freq=freq, unit=unit,
dtype=dtype, copy=copy)
result.name = name
return result

@classmethod
def _simple_new(cls, values, name=None, freq=None, dtype=_TD_DTYPE):
Expand Down
7 changes: 7 additions & 0 deletions pandas/tests/arrays/test_timedeltas.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
# -*- coding: utf-8 -*-

import numpy as np
import pytest

import pandas as pd
from pandas.core.arrays import TimedeltaArrayMixin as TimedeltaArray
import pandas.util.testing as tm


class TestTimedeltaArray(object):
def test_from_sequence_dtype(self):
msg = r"Only timedelta64\[ns\] dtype is valid"
with pytest.raises(ValueError, match=msg):
TimedeltaArray._from_sequence([], dtype=object)
with pytest.raises(ValueError, match=msg):
TimedeltaArray([], dtype=object)

def test_abs(self):
vals = np.array([-3600 * 10**9, 'NaT', 7200 * 10**9], dtype='m8[ns]')
Expand Down
2 changes: 1 addition & 1 deletion pandas/tests/dtypes/test_inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -1163,7 +1163,7 @@ def test_is_timedelta(self):
assert not is_timedelta64_ns_dtype('timedelta64')
assert is_timedelta64_ns_dtype('timedelta64[ns]')

tdi = TimedeltaIndex([1e14, 2e14], dtype='timedelta64')
tdi = TimedeltaIndex([1e14, 2e14], dtype='timedelta64[ns]')
assert is_timedelta64_dtype(tdi)
assert is_timedelta64_ns_dtype(tdi)
assert is_timedelta64_ns_dtype(tdi.astype('timedelta64[ns]'))
Expand Down