Skip to content

COMPAT: NaT accessors #15782

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 1 commit into from
Mar 27, 2017
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
3 changes: 2 additions & 1 deletion doc/source/whatsnew/v0.20.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,8 @@ Other API Changes
since pandas version 0.13.0 and can be done with the ``Series.str.extract``
method (:issue:`5224`). As a consequence, the ``as_indexer`` keyword is
ignored (no longer needed to specify the new behaviour) and is deprecated.

- ``NaT`` will now correctly report ``False`` for datetimelike boolean operations such as ``is_month_start`` (:issue:`15781`)
- ``NaT`` will now correctly return ``np.nan`` for ``Timedelta`` and ``Period`` accessors such as ``days`` and ``quarter`` (:issue:`15782`)

.. _whatsnew_0200.deprecations:

Expand Down
53 changes: 39 additions & 14 deletions pandas/_libs/tslib.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,30 @@ class NaTType(_NaT):
def is_leap_year(self):
return False

@property
def is_month_start(self):
return False

@property
def is_quarter_start(self):
return False

@property
def is_year_start(self):
return False

@property
def is_month_end(self):
return False

@property
def is_quarter_end(self):
return False

@property
def is_year_end(self):
return False

def __rdiv__(self, other):
return _nat_rdivide_op(self, other)

Expand Down Expand Up @@ -3799,8 +3823,9 @@ def array_strptime(ndarray[object] values, object fmt,
# these by definition return np.nan
fields = ['year', 'quarter', 'month', 'day', 'hour',
'minute', 'second', 'millisecond', 'microsecond', 'nanosecond',
'week', 'dayofyear', 'days_in_month', 'daysinmonth', 'dayofweek',
'weekday_name']
'week', 'dayofyear', 'weekofyear', 'days_in_month', 'daysinmonth',
'dayofweek', 'weekday_name', 'days', 'seconds', 'microseconds',
'nanoseconds', 'qyear', 'quarter']
for field in fields:
prop = property(fget=lambda self: np.nan)
setattr(NaTType, field, prop)
Expand Down Expand Up @@ -4810,7 +4835,7 @@ def get_start_end_field(ndarray[int64_t] dtindex, object field,
if field == 'is_month_start':
if is_business:
for i in range(count):
if dtindex[i] == NPY_NAT: out[i] = -1; continue
if dtindex[i] == NPY_NAT: out[i] = 0; continue

pandas_datetime_to_datetimestruct(
dtindex[i], PANDAS_FR_ns, &dts)
Expand All @@ -4823,7 +4848,7 @@ def get_start_end_field(ndarray[int64_t] dtindex, object field,
return out.view(bool)
else:
for i in range(count):
if dtindex[i] == NPY_NAT: out[i] = -1; continue
if dtindex[i] == NPY_NAT: out[i] = 0; continue

pandas_datetime_to_datetimestruct(
dtindex[i], PANDAS_FR_ns, &dts)
Expand All @@ -4836,7 +4861,7 @@ def get_start_end_field(ndarray[int64_t] dtindex, object field,
elif field == 'is_month_end':
if is_business:
for i in range(count):
if dtindex[i] == NPY_NAT: out[i] = -1; continue
if dtindex[i] == NPY_NAT: out[i] = 0; continue

pandas_datetime_to_datetimestruct(
dtindex[i], PANDAS_FR_ns, &dts)
Expand All @@ -4854,7 +4879,7 @@ def get_start_end_field(ndarray[int64_t] dtindex, object field,
return out.view(bool)
else:
for i in range(count):
if dtindex[i] == NPY_NAT: out[i] = -1; continue
if dtindex[i] == NPY_NAT: out[i] = 0; continue

pandas_datetime_to_datetimestruct(
dtindex[i], PANDAS_FR_ns, &dts)
Expand All @@ -4871,7 +4896,7 @@ def get_start_end_field(ndarray[int64_t] dtindex, object field,
elif field == 'is_quarter_start':
if is_business:
for i in range(count):
if dtindex[i] == NPY_NAT: out[i] = -1; continue
if dtindex[i] == NPY_NAT: out[i] = 0; continue

pandas_datetime_to_datetimestruct(
dtindex[i], PANDAS_FR_ns, &dts)
Expand All @@ -4885,7 +4910,7 @@ def get_start_end_field(ndarray[int64_t] dtindex, object field,
return out.view(bool)
else:
for i in range(count):
if dtindex[i] == NPY_NAT: out[i] = -1; continue
if dtindex[i] == NPY_NAT: out[i] = 0; continue

pandas_datetime_to_datetimestruct(
dtindex[i], PANDAS_FR_ns, &dts)
Expand All @@ -4898,7 +4923,7 @@ def get_start_end_field(ndarray[int64_t] dtindex, object field,
elif field == 'is_quarter_end':
if is_business:
for i in range(count):
if dtindex[i] == NPY_NAT: out[i] = -1; continue
if dtindex[i] == NPY_NAT: out[i] = 0; continue

pandas_datetime_to_datetimestruct(
dtindex[i], PANDAS_FR_ns, &dts)
Expand All @@ -4917,7 +4942,7 @@ def get_start_end_field(ndarray[int64_t] dtindex, object field,
return out.view(bool)
else:
for i in range(count):
if dtindex[i] == NPY_NAT: out[i] = -1; continue
if dtindex[i] == NPY_NAT: out[i] = 0; continue

pandas_datetime_to_datetimestruct(
dtindex[i], PANDAS_FR_ns, &dts)
Expand All @@ -4934,7 +4959,7 @@ def get_start_end_field(ndarray[int64_t] dtindex, object field,
elif field == 'is_year_start':
if is_business:
for i in range(count):
if dtindex[i] == NPY_NAT: out[i] = -1; continue
if dtindex[i] == NPY_NAT: out[i] = 0; continue

pandas_datetime_to_datetimestruct(
dtindex[i], PANDAS_FR_ns, &dts)
Expand All @@ -4948,7 +4973,7 @@ def get_start_end_field(ndarray[int64_t] dtindex, object field,
return out.view(bool)
else:
for i in range(count):
if dtindex[i] == NPY_NAT: out[i] = -1; continue
if dtindex[i] == NPY_NAT: out[i] = 0; continue

pandas_datetime_to_datetimestruct(
dtindex[i], PANDAS_FR_ns, &dts)
Expand All @@ -4961,7 +4986,7 @@ def get_start_end_field(ndarray[int64_t] dtindex, object field,
elif field == 'is_year_end':
if is_business:
for i in range(count):
if dtindex[i] == NPY_NAT: out[i] = -1; continue
if dtindex[i] == NPY_NAT: out[i] = 0; continue

pandas_datetime_to_datetimestruct(
dtindex[i], PANDAS_FR_ns, &dts)
Expand All @@ -4980,7 +5005,7 @@ def get_start_end_field(ndarray[int64_t] dtindex, object field,
return out.view(bool)
else:
for i in range(count):
if dtindex[i] == NPY_NAT: out[i] = -1; continue
if dtindex[i] == NPY_NAT: out[i] = 0; continue

pandas_datetime_to_datetimestruct(
dtindex[i], PANDAS_FR_ns, &dts)
Expand Down
9 changes: 2 additions & 7 deletions pandas/tests/indexes/datetimes/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,19 +259,14 @@ def test_datetimeindex_accessors(self):
dti.name = 'name'

# non boolean accessors -> return Index
for accessor in ['year', 'month', 'day', 'hour', 'minute',
'second', 'microsecond', 'nanosecond',
'dayofweek', 'dayofyear', 'weekofyear',
'quarter', 'weekday_name']:
for accessor in DatetimeIndex._field_ops:
res = getattr(dti, accessor)
assert len(res) == 365
assert isinstance(res, Index)
assert res.name == 'name'

# boolean accessors -> return array
for accessor in ['is_month_start', 'is_month_end',
'is_quarter_start', 'is_quarter_end',
'is_year_start', 'is_year_end']:
for accessor in DatetimeIndex._bool_ops:
res = getattr(dti, accessor)
assert len(res) == 365
assert isinstance(res, np.ndarray)
Expand Down
13 changes: 4 additions & 9 deletions pandas/tests/indexes/datetimes/test_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,10 @@ def setUp(self):
self.not_valid_objs = [o for o in self.objs if not mask(o)]

def test_ops_properties(self):
self.check_ops_properties(
['year', 'month', 'day', 'hour', 'minute', 'second', 'weekofyear',
'week', 'dayofweek', 'dayofyear', 'quarter'])
self.check_ops_properties(['date', 'time', 'microsecond', 'nanosecond',
'is_month_start', 'is_month_end',
'is_quarter_start',
'is_quarter_end', 'is_year_start',
'is_year_end', 'weekday_name'],
lambda x: isinstance(x, DatetimeIndex))
f = lambda x: isinstance(x, DatetimeIndex)
self.check_ops_properties(DatetimeIndex._field_ops, f)
self.check_ops_properties(DatetimeIndex._object_ops, f)
self.check_ops_properties(DatetimeIndex._bool_ops, f)

def test_ops_properties_basic(self):

Expand Down
9 changes: 4 additions & 5 deletions pandas/tests/indexes/period/test_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@ def setUp(self):
self.not_valid_objs = [o for o in self.objs if not mask(o)]

def test_ops_properties(self):
self.check_ops_properties(
['year', 'month', 'day', 'hour', 'minute', 'second', 'weekofyear',
'week', 'dayofweek', 'dayofyear', 'quarter'])
self.check_ops_properties(['qyear'],
lambda x: isinstance(x, PeriodIndex))
f = lambda x: isinstance(x, PeriodIndex)
self.check_ops_properties(PeriodIndex._field_ops, f)
self.check_ops_properties(PeriodIndex._object_ops, f)
self.check_ops_properties(PeriodIndex._bool_ops, f)

def test_asobject_tolist(self):
idx = pd.period_range(start='2013-01-01', periods=4, freq='M',
Expand Down
4 changes: 2 additions & 2 deletions pandas/tests/indexes/period/test_period.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,8 +394,8 @@ def test_fields(self):

def _check_all_fields(self, periodindex):
fields = ['year', 'month', 'day', 'hour', 'minute', 'second',
'weekofyear', 'week', 'dayofweek', 'weekday', 'dayofyear',
'quarter', 'qyear', 'days_in_month', 'is_leap_year']
'weekofyear', 'week', 'dayofweek', 'dayofyear',
'quarter', 'qyear', 'days_in_month']

periods = list(periodindex)
s = pd.Series(periodindex)
Expand Down
6 changes: 3 additions & 3 deletions pandas/tests/indexes/timedeltas/test_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ def setUp(self):
self.not_valid_objs = []

def test_ops_properties(self):
self.check_ops_properties(['days', 'hours', 'minutes', 'seconds',
'milliseconds'])
self.check_ops_properties(['microseconds', 'nanoseconds'])
f = lambda x: isinstance(x, TimedeltaIndex)
self.check_ops_properties(TimedeltaIndex._field_ops, f)
self.check_ops_properties(TimedeltaIndex._object_ops, f)

def test_asobject_tolist(self):
idx = timedelta_range(start='1 days', periods=4, freq='D', name='idx')
Expand Down
Loading