17
17
is_period_dtype ,
18
18
is_bool_dtype ,
19
19
pandas_dtype ,
20
- _ensure_int64 ,
21
20
_ensure_object )
22
21
from pandas .types .dtypes import PeriodDtype
23
22
from pandas .types .generic import ABCSeries
@@ -114,6 +113,13 @@ def wrapper(self, other):
114
113
return wrapper
115
114
116
115
116
+ def _new_PeriodIndex (cls , ** d ):
117
+ # GH13277 for unpickling
118
+ if d ['data' ].dtype == 'int64' :
119
+ values = d .pop ('data' )
120
+ return cls ._from_ordinals (values = values , ** d )
121
+
122
+
117
123
class PeriodIndex (DatelikeOps , DatetimeIndexOpsMixin , Int64Index ):
118
124
"""
119
125
Immutable ndarray holding ordinal values indicating regular periods in
@@ -209,17 +215,57 @@ def __new__(cls, data=None, ordinal=None, freq=None, start=None, end=None,
209
215
msg = 'specified freq and dtype are different'
210
216
raise IncompatibleFrequency (msg )
211
217
218
+ # coerce freq to freq object, otherwise it can be coerced elementwise
219
+ # which is slow
220
+ if freq :
221
+ freq = Period ._maybe_convert_freq (freq )
222
+
212
223
if data is None :
213
224
if ordinal is not None :
214
225
data = np .asarray (ordinal , dtype = np .int64 )
215
226
else :
216
227
data , freq = cls ._generate_range (start , end , periods ,
217
228
freq , kwargs )
218
- else :
219
- ordinal , freq = cls ._from_arraylike (data , freq , tz )
220
- data = np .array (ordinal , dtype = np .int64 , copy = copy )
229
+ return cls ._from_ordinals (data , name = name , freq = freq )
221
230
222
- return cls ._simple_new (data , name = name , freq = freq )
231
+ if isinstance (data , PeriodIndex ):
232
+ if freq is None or freq == data .freq : # no freq change
233
+ freq = data .freq
234
+ data = data ._values
235
+ else :
236
+ base1 , _ = _gfc (data .freq )
237
+ base2 , _ = _gfc (freq )
238
+ data = period .period_asfreq_arr (data ._values ,
239
+ base1 , base2 , 1 )
240
+ return cls ._simple_new (data , name = name , freq = freq )
241
+
242
+ # not array / index
243
+ if not isinstance (data , (np .ndarray , PeriodIndex ,
244
+ DatetimeIndex , Int64Index )):
245
+ if is_scalar (data ) or isinstance (data , Period ):
246
+ cls ._scalar_data_error (data )
247
+
248
+ # other iterable of some kind
249
+ if not isinstance (data , (list , tuple )):
250
+ data = list (data )
251
+
252
+ data = np .asarray (data )
253
+
254
+ # datetime other than period
255
+ if is_datetime64_dtype (data .dtype ):
256
+ data = dt64arr_to_periodarr (data , freq , tz )
257
+ return cls ._from_ordinals (data , name = name , freq = freq )
258
+
259
+ # check not floats
260
+ if infer_dtype (data ) == 'floating' and len (data ) > 0 :
261
+ raise TypeError ("PeriodIndex does not allow "
262
+ "floating point in construction" )
263
+
264
+ # anything else, likely an array of strings or periods
265
+ data = _ensure_object (data )
266
+ freq = freq or period .extract_freq (data )
267
+ data = period .extract_ordinals (data , freq )
268
+ return cls ._from_ordinals (data , name = name , freq = freq )
223
269
224
270
@classmethod
225
271
def _generate_range (cls , start , end , periods , freq , fields ):
@@ -240,85 +286,34 @@ def _generate_range(cls, start, end, periods, freq, fields):
240
286
241
287
return subarr , freq
242
288
243
- @classmethod
244
- def _from_arraylike (cls , data , freq , tz ):
245
- if freq is not None :
246
- freq = Period ._maybe_convert_freq (freq )
247
-
248
- if not isinstance (data , (np .ndarray , PeriodIndex ,
249
- DatetimeIndex , Int64Index )):
250
- if is_scalar (data ) or isinstance (data , Period ):
251
- raise ValueError ('PeriodIndex() must be called with a '
252
- 'collection of some kind, %s was passed'
253
- % repr (data ))
254
-
255
- # other iterable of some kind
256
- if not isinstance (data , (list , tuple )):
257
- data = list (data )
258
-
259
- try :
260
- data = _ensure_int64 (data )
261
- if freq is None :
262
- raise ValueError ('freq not specified' )
263
- data = np .array ([Period (x , freq = freq ) for x in data ],
264
- dtype = np .int64 )
265
- except (TypeError , ValueError ):
266
- data = _ensure_object (data )
267
-
268
- if freq is None :
269
- freq = period .extract_freq (data )
270
- data = period .extract_ordinals (data , freq )
271
- else :
272
- if isinstance (data , PeriodIndex ):
273
- if freq is None or freq == data .freq :
274
- freq = data .freq
275
- data = data ._values
276
- else :
277
- base1 , _ = _gfc (data .freq )
278
- base2 , _ = _gfc (freq )
279
- data = period .period_asfreq_arr (data ._values ,
280
- base1 , base2 , 1 )
281
- else :
282
- if is_object_dtype (data ):
283
- inferred = infer_dtype (data )
284
- if inferred == 'integer' :
285
- data = data .astype (np .int64 )
286
-
287
- if freq is None and is_object_dtype (data ):
288
- # must contain Period instance and thus extract ordinals
289
- freq = period .extract_freq (data )
290
- data = period .extract_ordinals (data , freq )
291
-
292
- if freq is None :
293
- msg = 'freq not specified and cannot be inferred'
294
- raise ValueError (msg )
295
-
296
- if data .dtype != np .int64 :
297
- if np .issubdtype (data .dtype , np .datetime64 ):
298
- data = dt64arr_to_periodarr (data , freq , tz )
299
- else :
300
- data = _ensure_object (data )
301
- data = period .extract_ordinals (data , freq )
302
-
303
- return data , freq
304
-
305
289
@classmethod
306
290
def _simple_new (cls , values , name = None , freq = None , ** kwargs ):
307
-
291
+ """
292
+ Values can be any type that can be coerced to Periods.
293
+ Ordinals in an ndarray are fastpath-ed to `_from_ordinals`
294
+ """
308
295
if not is_integer_dtype (values ):
309
296
values = np .array (values , copy = False )
310
- if ( len (values ) > 0 and is_float_dtype (values ) ):
297
+ if len (values ) > 0 and is_float_dtype (values ):
311
298
raise TypeError ("PeriodIndex can't take floats" )
312
- else :
313
- return cls (values , name = name , freq = freq , ** kwargs )
299
+ return cls (values , name = name , freq = freq , ** kwargs )
300
+
301
+ return cls ._from_ordinals (values , name , freq , ** kwargs )
302
+
303
+ @classmethod
304
+ def _from_ordinals (cls , values , name = None , freq = None , ** kwargs ):
305
+ """
306
+ Values should be int ordinals
307
+ `__new__` & `_simple_new` cooerce to ordinals and call this method
308
+ """
314
309
315
310
values = np .array (values , dtype = 'int64' , copy = False )
316
311
317
312
result = object .__new__ (cls )
318
313
result ._data = values
319
314
result .name = name
320
315
if freq is None :
321
- raise ValueError ('freq is not specified' )
316
+ raise ValueError ('freq is not specified and cannot be inferred ' )
322
317
result .freq = Period ._maybe_convert_freq (freq )
323
318
result ._reset_identity ()
324
319
return result
@@ -327,13 +322,13 @@ def _shallow_copy_with_infer(self, values=None, **kwargs):
327
322
""" we always want to return a PeriodIndex """
328
323
return self ._shallow_copy (values = values , ** kwargs )
329
324
330
- def _shallow_copy (self , values = None , ** kwargs ):
331
- if kwargs .get ('freq' ) is None :
332
- # freq must be provided
333
- kwargs ['freq' ] = self .freq
325
+ def _shallow_copy (self , values = None , freq = None , ** kwargs ):
326
+ if freq is None :
327
+ freq = self .freq
334
328
if values is None :
335
329
values = self ._values
336
- return super (PeriodIndex , self )._shallow_copy (values = values , ** kwargs )
330
+ return super (PeriodIndex , self )._shallow_copy (values = values ,
331
+ freq = freq , ** kwargs )
337
332
338
333
def _coerce_scalar_to_index (self , item ):
339
334
"""
@@ -413,7 +408,7 @@ def __array_wrap__(self, result, context=None):
413
408
return result
414
409
# the result is object dtype array of Period
415
410
# cannot pass _simple_new as it is
416
- return PeriodIndex (result , freq = self .freq , name = self .name )
411
+ return self . _shallow_copy (result , freq = self .freq , name = self .name )
417
412
418
413
@property
419
414
def _box_func (self ):
@@ -708,7 +703,7 @@ def shift(self, n):
708
703
values = self ._values + n * self .freq .n
709
704
if self .hasnans :
710
705
values [self ._isnan ] = tslib .iNaT
711
- return PeriodIndex ( data = values , name = self . name , freq = self . freq )
706
+ return self . _shallow_copy ( values = values )
712
707
713
708
@cache_readonly
714
709
def dtype (self ):
@@ -945,7 +940,8 @@ def _wrap_union_result(self, other, result):
945
940
946
941
def _apply_meta (self , rawarr ):
947
942
if not isinstance (rawarr , PeriodIndex ):
948
- rawarr = PeriodIndex (rawarr , freq = self .freq )
943
+ rawarr = PeriodIndex ._from_ordinals (rawarr , freq = self .freq ,
944
+ name = self .name )
949
945
return rawarr
950
946
951
947
def _format_native_types (self , na_rep = u ('NaT' ), date_format = None ,
0 commit comments