Skip to content

Commit 42ab137

Browse files
committed
wip
1 parent 0437940 commit 42ab137

File tree

7 files changed

+86
-58
lines changed

7 files changed

+86
-58
lines changed

pandas/core/accessor.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ def f(self, *args, **kwargs):
105105

106106

107107
def delegate_names(delegate, accessors, typ, overwrite=False):
108-
# type (type, list, str, bool) -> Callable
109108
"""
110109
Add delegated names to a class using a class decorator.
111110

pandas/core/arrays/categorical.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,8 @@ def __array__(self, dtype=None):
12591259
if dtype==None (default), the same dtype as
12601260
categorical.categories.dtype
12611261
"""
1262+
# Need asarray, in case self.categories.values is an ExtensionArray
1263+
# e.g. in a PeriodIndex. More generally, any Index backed by an EA.
12621264
values = np.asarray(self.categories.values)
12631265
ret = take_1d(values, self._codes)
12641266
if dtype and not is_dtype_equal(dtype, self.categories.dtype):

pandas/core/arrays/datetimelike.py

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@
1111
from pandas._libs.tslibs.period import (
1212
Period, DIFFERENT_FREQ_INDEX, IncompatibleFrequency)
1313

14-
from pandas.errors import (
15-
NullFrequencyError, PerformanceWarning, AbstractMethodError
16-
)
14+
from pandas.errors import NullFrequencyError, PerformanceWarning
1715
from pandas import compat
1816

1917
from pandas.tseries import frequencies
@@ -63,15 +61,8 @@ def cmp_method(self, other):
6361
# comparisons, this will raise in the future
6462
with warnings.catch_warnings(record=True):
6563
warnings.filterwarnings("ignore", "elementwise", FutureWarning)
66-
67-
# XXX: temporary hack till I figure out what's going on.
68-
# For PeriodIndex.__eq__, we don't want to convert a scalar
69-
# other to a scalar ndarray.
70-
if getattr(self, '_wrap_cmp_method', True):
71-
other = np.asarray(other)
72-
7364
with np.errstate(all='ignore'):
74-
result = op(self.values, other)
65+
result = op(self.values, np.asarray(other))
7566

7667
return result
7768

@@ -85,10 +76,12 @@ class AttributesMixin(object):
8576
@property
8677
def _attributes(self):
8778
# Inheriting subclass should implement _attributes as a list of strings
79+
from pandas.errors import AbstractMethodError
8880
raise AbstractMethodError(self)
8981

9082
@classmethod
9183
def _simple_new(cls, values, **kwargs):
84+
from pandas.errors import AbstractMethodError
9285
raise AbstractMethodError(cls)
9386

9487
def _get_attributes_dict(self):
@@ -125,7 +118,7 @@ def _box_func(self):
125118
"""
126119
box function to get object from internal representation
127120
"""
128-
raise AbstractMethodError(self)
121+
raise com.AbstractMethodError(self)
129122

130123
def _box_values(self, values):
131124
"""
@@ -358,13 +351,13 @@ def _add_datelike(self, other):
358351
typ=type(other).__name__))
359352

360353
def _sub_datelike(self, other):
361-
raise AbstractMethodError(self)
354+
raise com.AbstractMethodError(self)
362355

363356
def _sub_period(self, other):
364357
return NotImplemented
365358

366359
def _add_offset(self, offset):
367-
raise AbstractMethodError(self)
360+
raise com.AbstractMethodError(self)
368361

369362
def _add_delta(self, other):
370363
return NotImplemented

pandas/core/arrays/period.py

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
is_float, is_integer, pandas_dtype, is_scalar,
2323
is_datetime64_dtype,
2424
is_categorical_dtype,
25+
is_object_dtype,
26+
is_string_dtype,
27+
is_datetime_or_timedelta_dtype,
28+
is_dtype_equal,
2529
ensure_object
2630
)
2731
from pandas.core.dtypes.dtypes import PeriodDtype
@@ -68,7 +72,7 @@ def wrapper(self, other):
6872
raise IncompatibleFrequency(msg)
6973

7074
result = op(other.ordinal)
71-
elif isinstance(other, (ABCPeriodIndex, PeriodArray)):
75+
elif isinstance(other, (ABCPeriodIndex, cls)):
7276
if other.freq != self.freq:
7377
msg = DIFFERENT_FREQ_INDEX.format(self.freqstr, other.freqstr)
7478
raise IncompatibleFrequency(msg)
@@ -186,7 +190,7 @@ def _complex_new(cls, data=None, ordinal=None, freq=None, start=None,
186190
freq, fields)
187191
return cls._from_ordinals(data, freq=freq)
188192

189-
if isinstance(data, (PeriodArray, PeriodIndex)):
193+
if isinstance(data, (cls, PeriodIndex)):
190194
if freq is None or freq == data.freq: # no freq change
191195
freq = data.freq
192196
data = data._ndarray_values
@@ -722,11 +726,36 @@ def astype(self, dtype, copy=True):
722726
# super(...), which ends up being... DatetimeIndexOpsMixin?
723727
# this is complicated.
724728
# need a pandas_astype(arr, dtype).
725-
from pandas.core.arrays import Categorical
726-
727-
if is_categorical_dtype(dtype):
729+
from pandas import Categorical
730+
731+
dtype = pandas_dtype(dtype)
732+
733+
if is_object_dtype(dtype):
734+
return np.asarray(self, dtype=object)
735+
elif is_string_dtype(dtype) and not is_categorical_dtype(dtype):
736+
return self._format_native_types()
737+
elif is_integer_dtype(dtype):
738+
return self.values.astype("i8", copy=copy)
739+
elif (is_datetime_or_timedelta_dtype(dtype) and
740+
not is_dtype_equal(self.dtype, dtype)) or is_float_dtype(dtype):
741+
# disallow conversion between datetime/timedelta,
742+
# and conversions for any datetimelike to float
743+
msg = 'Cannot cast {name} to dtype {dtype}'
744+
raise TypeError(msg.format(name=type(self).__name__, dtype=dtype))
745+
elif is_categorical_dtype(dtype):
728746
return Categorical(self, dtype=dtype)
729-
return super(PeriodArray, self).astype(dtype, copy=copy)
747+
elif is_period_dtype(dtype):
748+
return self.asfreq(dtype.freq)
749+
else:
750+
return np.asarray(self, dtype=dtype)
751+
752+
def _box_values_as_index(self):
753+
"""
754+
return object Index which contains boxed values
755+
"""
756+
# This is implemented just for astype
757+
from pandas.core.index import Index
758+
return Index(self._box_values(self.asi8), dtype=object)
730759

731760

732761
PeriodArray._add_comparison_ops()

pandas/core/indexes/base.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,9 @@ def __new__(cls, data=None, dtype=None, copy=False, name=None,
306306
(dtype is not None and is_period_dtype(dtype))):
307307
from pandas import PeriodIndex
308308
result = PeriodIndex(data, copy=copy, name=name, **kwargs)
309-
if dtype is not None and _o_dtype == dtype:
309+
if (dtype is not None and
310+
not is_period_dtype(dtype) and
311+
_o_dtype == dtype):
310312
return Index(result.to_pytimedelta(), dtype=_o_dtype)
311313
else:
312314
return result

pandas/core/indexes/period.py

Lines changed: 36 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
is_float,
1111
is_integer_dtype,
1212
is_datetime64_any_dtype,
13-
is_period_dtype,
1413
is_bool_dtype,
1514
pandas_dtype,
1615
)
@@ -91,11 +90,13 @@ def _delegate_method(self, name, *args, **kwargs):
9190
)
9291
@delegate_names(
9392
PeriodArray,
94-
PeriodArray._datetimelike_methods + [
93+
[x for x in PeriodArray._datetimelike_methods
94+
if x not in {"asfreq", "to_timestamp"}] + [
9595
'_format_native_types',
9696
'_maybe_convert_timedelta',
9797
],
9898
"method",
99+
# overwrite size, asi8, etc. but not asfreq, to_timestamp
99100
overwrite=True,
100101
)
101102
class PeriodIndex(DatelikeOps, DatetimeIndexOpsMixin,
@@ -445,6 +446,14 @@ def asof_locs(self, where, mask):
445446

446447
return result
447448

449+
def _box_values_as_index(self):
450+
"""
451+
return object Index which contains boxed values
452+
"""
453+
# TODO(DatetimeArray): remove
454+
# Have to add our name.
455+
return Index(self._data._box_values_as_index(), name=self.name)
456+
448457
@Appender(_index_shared_docs['astype'])
449458
def astype(self, dtype, copy=True, how='start'):
450459
dtype = pandas_dtype(dtype)
@@ -457,16 +466,8 @@ def astype(self, dtype, copy=True, how='start'):
457466
tz = getattr(dtype, 'tz', None)
458467
return self.to_timestamp(how=how).tz_localize(tz)
459468

460-
elif is_integer_dtype(dtype):
461-
# astype(int) -> Index, so don't dispatch
462-
return self._int64index.copy() if copy else self._int64index
463-
464-
elif is_period_dtype(dtype):
465-
return self.asfreq(freq=dtype.freq)
466-
467-
return Index(self._data.astype(dtype, copy=copy), name=self.name,
468-
dtype=dtype, # disable Index inference
469-
copy=False)
469+
result = self._data.astype(dtype, copy=copy)
470+
return Index(result, name=self.name, dtype=dtype, copy=False)
470471

471472
@Substitution(klass='PeriodIndex')
472473
@Appender(_shared_docs['searchsorted'])
@@ -499,29 +500,29 @@ def is_full(self):
499500
values = self.asi8
500501
return ((values[1:] - values[:-1]) < 2).all()
501502

502-
# year = _wrap_field_accessor('year')
503-
month = _wrap_field_accessor('month')
504-
day = _wrap_field_accessor('day')
505-
hour = _wrap_field_accessor('hour')
506-
minute = _wrap_field_accessor('minute')
507-
second = _wrap_field_accessor('second')
508-
weekofyear = _wrap_field_accessor('week')
509-
week = weekofyear
510-
dayofweek = _wrap_field_accessor('dayofweek')
511-
weekday = dayofweek
512-
dayofyear = day_of_year = _wrap_field_accessor('dayofyear')
513-
quarter = _wrap_field_accessor('quarter')
514-
qyear = _wrap_field_accessor('qyear')
515-
days_in_month = _wrap_field_accessor('days_in_month')
516-
daysinmonth = days_in_month
517-
518-
@property
519-
def start_time(self):
520-
return self.to_timestamp(how='start')
521-
522-
@property
523-
def end_time(self):
524-
return self.to_timestamp(how='end')
503+
# # year = _wrap_field_accessor('year')
504+
# month = _wrap_field_accessor('month')
505+
# day = _wrap_field_accessor('day')
506+
# hour = _wrap_field_accessor('hour')
507+
# minute = _wrap_field_accessor('minute')
508+
# second = _wrap_field_accessor('second')
509+
# weekofyear = _wrap_field_accessor('week')
510+
# week = weekofyear
511+
# dayofweek = _wrap_field_accessor('dayofweek')
512+
# weekday = dayofweek
513+
# dayofyear = day_of_year = _wrap_field_accessor('dayofyear')
514+
# quarter = _wrap_field_accessor('quarter')
515+
# qyear = _wrap_field_accessor('qyear')
516+
# days_in_month = _wrap_field_accessor('days_in_month')
517+
# daysinmonth = days_in_month
518+
#
519+
# @property
520+
# def start_time(self):
521+
# return self.to_timestamp(how='start')
522+
#
523+
# @property
524+
# def end_time(self):
525+
# return self.to_timestamp(how='end')
525526

526527
def _mpl_repr(self):
527528
# how to represent ourselves to matplotlib

pandas/tests/indexes/period/test_astype.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ class TestPeriodIndexAsType(object):
1414
def test_astype_raises(self, dtype):
1515
# GH#13149, GH#13209
1616
idx = PeriodIndex(['2016-05-16', 'NaT', NaT, np.NaN], freq='D')
17-
msg = 'Cannot cast PeriodIndex to dtype'
17+
# XXX: do we care about the name PeriodArray vs. PeriodIndex in the
18+
# exception message?
19+
msg = 'Cannot cast PeriodArray to dtype'
1820
with tm.assert_raises_regex(TypeError, msg):
1921
idx.astype(dtype)
2022

0 commit comments

Comments
 (0)