Skip to content

Commit 33ae55e

Browse files
jbrockmendelPingviinituutti
authored andcommitted
REF: Pieces broken off of pandas-dev#24024 (pandas-dev#24364)
1 parent cd3c7c5 commit 33ae55e

13 files changed

+112
-52
lines changed

pandas/core/arrays/datetimelike.py

+18-5
Original file line numberDiff line numberDiff line change
@@ -296,9 +296,22 @@ def __iter__(self):
296296

297297
@property
298298
def asi8(self):
299+
# type: () -> ndarray
300+
"""
301+
Integer representation of the values.
302+
303+
Returns
304+
-------
305+
ndarray
306+
An ndarray with int64 dtype.
307+
"""
299308
# do not cache or you'll create a memory leak
300309
return self._data.view('i8')
301310

311+
@property
312+
def _ndarray_values(self):
313+
return self._data
314+
302315
# ----------------------------------------------------------------
303316
# Rendering Methods
304317

@@ -469,7 +482,7 @@ def _isnan(self):
469482
return (self.asi8 == iNaT)
470483

471484
@property # NB: override with cache_readonly in immutable subclasses
472-
def hasnans(self):
485+
def _hasnans(self):
473486
"""
474487
return if I have any nans; enables various perf speedups
475488
"""
@@ -493,7 +506,7 @@ def _maybe_mask_results(self, result, fill_value=iNaT, convert=None):
493506
This is an internal routine
494507
"""
495508

496-
if self.hasnans:
509+
if self._hasnans:
497510
if convert:
498511
result = result.astype(convert)
499512
if fill_value is None:
@@ -696,7 +709,7 @@ def _add_delta_tdi(self, other):
696709
new_values = checked_add_with_arr(self_i8, other_i8,
697710
arr_mask=self._isnan,
698711
b_mask=other._isnan)
699-
if self.hasnans or other.hasnans:
712+
if self._hasnans or other._hasnans:
700713
mask = (self._isnan) | (other._isnan)
701714
new_values[mask] = iNaT
702715
return new_values.view('i8')
@@ -764,7 +777,7 @@ def _sub_period_array(self, other):
764777
b_mask=other._isnan)
765778

766779
new_values = np.array([self.freq.base * x for x in new_values])
767-
if self.hasnans or other.hasnans:
780+
if self._hasnans or other._hasnans:
768781
mask = (self._isnan) | (other._isnan)
769782
new_values[mask] = NaT
770783
return new_values
@@ -1085,7 +1098,7 @@ def _evaluate_compare(self, other, op):
10851098
elif lib.is_scalar(lib.item_from_zerodim(other)):
10861099
# ndarray scalar
10871100
other = [other.item()]
1088-
other = type(self)(other)
1101+
other = type(self)._from_sequence(other)
10891102

10901103
# compare
10911104
result = op(self.asi8, other.asi8)

pandas/core/arrays/datetimes.py

+22-4
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def wrapper(self, other):
115115
else:
116116
if isinstance(other, list):
117117
try:
118-
other = type(self)(other)
118+
other = type(self)._from_sequence(other)
119119
except ValueError:
120120
other = np.array(other, dtype=np.object_)
121121
elif not isinstance(other, (np.ndarray, ABCIndexClass, ABCSeries,
@@ -147,7 +147,7 @@ def wrapper(self, other):
147147
if o_mask.any():
148148
result[o_mask] = nat_result
149149

150-
if self.hasnans:
150+
if self._hasnans:
151151
result[self._isnan] = nat_result
152152

153153
return result
@@ -349,14 +349,32 @@ def _box_func(self):
349349

350350
@property
351351
def dtype(self):
352+
# type: () -> Union[np.dtype, DatetimeTZDtype]
353+
"""
354+
The dtype for the DatetimeArray.
355+
356+
Returns
357+
-------
358+
numpy.dtype or DatetimeTZDtype
359+
If the values are tz-naive, then ``np.dtype('datetime64[ns]')``
360+
is returned.
361+
362+
If the values are tz-aware, then the ``DatetimeTZDtype``
363+
is returned.
364+
"""
352365
if self.tz is None:
353366
return _NS_DTYPE
354367
return DatetimeTZDtype('ns', self.tz)
355368

356369
@property
357370
def tz(self):
358371
"""
359-
Return timezone.
372+
Return timezone, if any.
373+
374+
Returns
375+
-------
376+
datetime.tzinfo, pytz.tzinfo.BaseTZInfo, dateutil.tz.tz.tzfile, or None
377+
Returns None when the array is tz-naive.
360378
"""
361379
# GH 18595
362380
return self._tz
@@ -534,7 +552,7 @@ def _sub_datetime_arraylike(self, other):
534552
other_i8 = other.asi8
535553
new_values = checked_add_with_arr(self_i8, -other_i8,
536554
arr_mask=self._isnan)
537-
if self.hasnans or other.hasnans:
555+
if self._hasnans or other._hasnans:
538556
mask = (self._isnan) | (other._isnan)
539557
new_values[mask] = iNaT
540558
return new_values.view('timedelta64[ns]')

pandas/core/arrays/period.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def wrapper(self, other):
8484
other = Period(other, freq=self.freq)
8585
result = op(other.ordinal)
8686

87-
if self.hasnans:
87+
if self._hasnans:
8888
result[self._isnan] = nat_result
8989

9090
return result
@@ -499,7 +499,7 @@ def _time_shift(self, n, freq=None):
499499
"{cls}._time_shift"
500500
.format(cls=type(self).__name__))
501501
values = self.asi8 + n * self.freq.n
502-
if self.hasnans:
502+
if self._hasnans:
503503
values[self._isnan] = iNaT
504504
return type(self)(values, freq=self.freq)
505505

@@ -561,7 +561,7 @@ def asfreq(self, freq=None, how='E'):
561561

562562
new_data = period_asfreq_arr(ordinal, base1, base2, end)
563563

564-
if self.hasnans:
564+
if self._hasnans:
565565
new_data[self._isnan] = iNaT
566566

567567
return type(self)(new_data, freq=freq)
@@ -581,7 +581,7 @@ def _format_native_types(self, na_rep=u'NaT', date_format=None, **kwargs):
581581
else:
582582
formatter = lambda dt: u'%s' % dt
583583

584-
if self.hasnans:
584+
if self._hasnans:
585585
mask = self._isnan
586586
values[mask] = na_rep
587587
imask = ~mask
@@ -668,7 +668,7 @@ def _sub_period(self, other):
668668
new_data = asi8 - other.ordinal
669669
new_data = np.array([self.freq * x for x in new_data])
670670

671-
if self.hasnans:
671+
if self._hasnans:
672672
new_data[self._isnan] = NaT
673673

674674
return new_data
@@ -983,7 +983,7 @@ def dt64arr_to_periodarr(data, freq, tz=None):
983983
984984
"""
985985
if data.dtype != np.dtype('M8[ns]'):
986-
raise ValueError('Wrong dtype: %s' % data.dtype)
986+
raise ValueError('Wrong dtype: {dtype}'.format(dtype=data.dtype))
987987

988988
if freq is None:
989989
if isinstance(data, ABCIndexClass):

pandas/core/arrays/timedeltas.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def _field_accessor(name, alias, docstring=None):
5454
def f(self):
5555
values = self.asi8
5656
result = get_timedelta_field(values, alias)
57-
if self.hasnans:
57+
if self._hasnans:
5858
result = self._maybe_mask_results(result, fill_value=None,
5959
convert='float64')
6060

@@ -102,7 +102,7 @@ def wrapper(self, other):
102102
if o_mask.any():
103103
result[o_mask] = nat_result
104104

105-
if self.hasnans:
105+
if self._hasnans:
106106
result[self._isnan] = nat_result
107107

108108
return result
@@ -714,7 +714,7 @@ def components(self):
714714

715715
columns = ['days', 'hours', 'minutes', 'seconds',
716716
'milliseconds', 'microseconds', 'nanoseconds']
717-
hasnans = self.hasnans
717+
hasnans = self._hasnans
718718
if hasnans:
719719
def f(x):
720720
if isna(x):

pandas/core/indexes/base.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,7 @@ def __array__(self, dtype=None):
682682
"""
683683
The array interface, return my values.
684684
"""
685-
return self._data.view(np.ndarray)
685+
return np.asarray(self._data, dtype=dtype)
686686

687687
def __array_wrap__(self, result, context=None):
688688
"""
@@ -739,6 +739,8 @@ def view(self, cls=None):
739739
Parameters
740740
----------
741741
dtype : numpy dtype or pandas type
742+
Note that any integer `dtype` is treated as ``'int64'``,
743+
regardless of the sign and size.
742744
copy : bool, default True
743745
By default, astype always returns a newly allocated object.
744746
If copy is set to False and internal requirements on dtype are

pandas/core/indexes/datetimelike.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,22 @@ class DatetimeIndexOpsMixin(DatetimeLikeArrayMixin):
4040
# override DatetimeLikeArrayMixin method
4141
copy = Index.copy
4242
unique = Index.unique
43-
take = Index.take
4443

4544
# DatetimeLikeArrayMixin assumes subclasses are mutable, so these are
4645
# properties there. They can be made into cache_readonly for Index
4746
# subclasses bc they are immutable
4847
inferred_freq = cache_readonly(DatetimeLikeArrayMixin.inferred_freq.fget)
4948
_isnan = cache_readonly(DatetimeLikeArrayMixin._isnan.fget)
50-
hasnans = cache_readonly(DatetimeLikeArrayMixin.hasnans.fget)
49+
hasnans = cache_readonly(DatetimeLikeArrayMixin._hasnans.fget)
50+
_hasnans = hasnans # for index / array -agnostic code
5151
_resolution = cache_readonly(DatetimeLikeArrayMixin._resolution.fget)
5252
resolution = cache_readonly(DatetimeLikeArrayMixin.resolution.fget)
5353

54+
# A few methods that are shared
55+
_maybe_mask_results = DatetimeLikeArrayMixin._maybe_mask_results
56+
57+
# ------------------------------------------------------------------------
58+
5459
def equals(self, other):
5560
"""
5661
Determines if two Index objects contain the same elements.

pandas/core/indexes/datetimes.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -227,11 +227,11 @@ def __new__(cls, data=None,
227227
"endpoints is deprecated. Use "
228228
"`pandas.date_range` instead.",
229229
FutureWarning, stacklevel=2)
230-
result = cls._generate_range(start, end, periods,
231-
freq=freq, tz=tz, normalize=normalize,
232-
closed=closed, ambiguous=ambiguous)
233-
result.name = name
234-
return result
230+
dtarr = DatetimeArray._generate_range(
231+
start, end, periods,
232+
freq=freq, tz=tz, normalize=normalize,
233+
closed=closed, ambiguous=ambiguous)
234+
return cls(dtarr, name=name)
235235

236236
if is_scalar(data):
237237
raise TypeError("{cls}() must be called with a "
@@ -1473,12 +1473,12 @@ def date_range(start=None, end=None, periods=None, freq=None, tz=None,
14731473
if freq is None and com._any_none(periods, start, end):
14741474
freq = 'D'
14751475

1476-
result = DatetimeIndex._generate_range(
1476+
dtarr = DatetimeArray._generate_range(
14771477
start=start, end=end, periods=periods,
14781478
freq=freq, tz=tz, normalize=normalize,
14791479
closed=closed, **kwargs)
14801480

1481-
result.name = name
1481+
result = DatetimeIndex(dtarr, name=name)
14821482
return result
14831483

14841484

pandas/core/indexes/timedeltas.py

+14-17
Original file line numberDiff line numberDiff line change
@@ -125,15 +125,6 @@ def _join_i8_wrapper(joinf, **kwargs):
125125
_left_indexer_unique = _join_i8_wrapper(
126126
libjoin.left_join_indexer_unique_int64, with_indexers=False)
127127

128-
# define my properties & methods for delegation
129-
_other_ops = []
130-
_bool_ops = []
131-
_object_ops = ['freq']
132-
_field_ops = ['days', 'seconds', 'microseconds', 'nanoseconds']
133-
_datetimelike_ops = _field_ops + _object_ops + _bool_ops
134-
_datetimelike_methods = ["to_pytimedelta", "total_seconds",
135-
"round", "floor", "ceil"]
136-
137128
_engine_type = libindex.TimedeltaEngine
138129

139130
_comparables = ['name', 'freq']
@@ -143,6 +134,14 @@ def _join_i8_wrapper(joinf, **kwargs):
143134

144135
_freq = None
145136

137+
_box_func = TimedeltaArray._box_func
138+
_bool_ops = TimedeltaArray._bool_ops
139+
_object_ops = TimedeltaArray._object_ops
140+
_field_ops = TimedeltaArray._field_ops
141+
_datetimelike_ops = TimedeltaArray._datetimelike_ops
142+
_datetimelike_methods = TimedeltaArray._datetimelike_methods
143+
_other_ops = TimedeltaArray._other_ops
144+
146145
# -------------------------------------------------------------------
147146
# Constructors
148147

@@ -163,10 +162,9 @@ def __new__(cls, data=None, unit=None, freq=None, start=None, end=None,
163162
"endpoints is deprecated. Use "
164163
"`pandas.timedelta_range` instead.",
165164
FutureWarning, stacklevel=2)
166-
result = cls._generate_range(start, end, periods, freq,
167-
closed=closed)
168-
result.name = name
169-
return result
165+
tdarr = TimedeltaArray._generate_range(start, end, periods, freq,
166+
closed=closed)
167+
return cls(tdarr, name=name)
170168

171169
if is_scalar(data):
172170
raise TypeError('{cls}() must be called with a '
@@ -766,7 +764,6 @@ def timedelta_range(start=None, end=None, periods=None, freq=None,
766764
freq = 'D'
767765

768766
freq, freq_infer = dtl.maybe_infer_freq(freq)
769-
result = TimedeltaIndex._generate_range(start, end, periods, freq,
770-
closed=closed)
771-
result.name = name
772-
return result
767+
tdarr = TimedeltaArray._generate_range(start, end, periods, freq,
768+
closed=closed)
769+
return TimedeltaIndex(tdarr, name=name)

pandas/core/reshape/merge.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1592,8 +1592,8 @@ def _right_outer_join(x, y, max_groups):
15921592
def _factorize_keys(lk, rk, sort=True):
15931593
# Some pre-processing for non-ndarray lk / rk
15941594
if is_datetime64tz_dtype(lk) and is_datetime64tz_dtype(rk):
1595-
lk = lk.values
1596-
rk = rk.values
1595+
lk = lk._data
1596+
rk = rk._data
15971597

15981598
elif (is_categorical_dtype(lk) and
15991599
is_categorical_dtype(rk) and

pandas/core/tools/datetimes.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ def _convert_listlike_datetimes(arg, box, format, name=None, tz=None,
171171
- ndarray of Timestamps if box=False
172172
"""
173173
from pandas import DatetimeIndex
174+
from pandas.core.arrays import DatetimeArrayMixin as DatetimeArray
174175
from pandas.core.arrays.datetimes import (
175176
maybe_convert_dtype, objects_to_datetime64ns)
176177

@@ -179,14 +180,14 @@ def _convert_listlike_datetimes(arg, box, format, name=None, tz=None,
179180

180181
# these are shortcutable
181182
if is_datetime64tz_dtype(arg):
182-
if not isinstance(arg, DatetimeIndex):
183+
if not isinstance(arg, (DatetimeArray, DatetimeIndex)):
183184
return DatetimeIndex(arg, tz=tz, name=name)
184185
if tz == 'utc':
185186
arg = arg.tz_convert(None).tz_localize(tz)
186187
return arg
187188

188189
elif is_datetime64_ns_dtype(arg):
189-
if box and not isinstance(arg, DatetimeIndex):
190+
if box and not isinstance(arg, (DatetimeArray, DatetimeIndex)):
190191
try:
191192
return DatetimeIndex(arg, tz=tz, name=name)
192193
except ValueError:

pandas/tests/arrays/test_timedeltas.py

+11
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@
88
import pandas.util.testing as tm
99

1010

11+
class TestTimedeltaArrayConstructor(object):
12+
def test_copy(self):
13+
data = np.array([1, 2, 3], dtype='m8[ns]')
14+
arr = TimedeltaArray(data, copy=False)
15+
assert arr._data is data
16+
17+
arr = TimedeltaArray(data, copy=True)
18+
assert arr._data is not data
19+
assert arr._data.base is not data
20+
21+
1122
class TestTimedeltaArray(object):
1223
def test_from_sequence_dtype(self):
1324
msg = r"Only timedelta64\[ns\] dtype is valid"

0 commit comments

Comments
 (0)