11""" define the IntervalIndex """
22
33import numpy as np
4+ import warnings
45
56from pandas .core .dtypes .missing import notna , isna
67from pandas .core .dtypes .generic import ABCDatetimeIndex , ABCPeriodIndex
@@ -151,6 +152,10 @@ class IntervalIndex(IntervalMixin, Index):
151152 Name to be stored in the index.
152153 copy : boolean, default False
153154 Copy the meta-data
155+ dtype : dtype or None, default None
156+ If None, dtype will be inferred
157+
158+ ..versionadded:: 0.23.0
154159
155160 Attributes
156161 ----------
@@ -167,7 +172,6 @@ class IntervalIndex(IntervalMixin, Index):
167172 from_arrays
168173 from_tuples
169174 from_breaks
170- from_intervals
171175 contains
172176
173177 Examples
@@ -181,8 +185,7 @@ class IntervalIndex(IntervalMixin, Index):
181185
182186 It may also be constructed using one of the constructor
183187 methods: :meth:`IntervalIndex.from_arrays`,
184- :meth:`IntervalIndex.from_breaks`, :meth:`IntervalIndex.from_intervals`
185- and :meth:`IntervalIndex.from_tuples`.
188+ :meth:`IntervalIndex.from_breaks`, and :meth:`IntervalIndex.from_tuples`.
186189
187190 See further examples in the doc strings of ``interval_range`` and the
188191 mentioned constructor methods.
@@ -211,8 +214,7 @@ class IntervalIndex(IntervalMixin, Index):
211214
212215 _mask = None
213216
214- def __new__ (cls , data , closed = None ,
215- name = None , copy = False , dtype = None ,
217+ def __new__ (cls , data , closed = None , name = None , copy = False , dtype = None ,
216218 fastpath = False , verify_integrity = True ):
217219
218220 if fastpath :
@@ -245,19 +247,28 @@ def __new__(cls, data, closed=None,
245247
246248 closed = closed or infer_closed
247249
248- return cls ._simple_new (left , right , closed , name ,
249- copy = copy , verify_integrity = verify_integrity )
250+ return cls ._simple_new (left , right , closed , name , copy = copy ,
251+ dtype = dtype , verify_integrity = verify_integrity )
250252
251253 @classmethod
252- def _simple_new (cls , left , right , closed = None , name = None ,
253- copy = False , verify_integrity = True ):
254+ def _simple_new (cls , left , right , closed = None , name = None , copy = False ,
255+ dtype = None , verify_integrity = True ):
254256 result = IntervalMixin .__new__ (cls )
255257
256- if closed is None :
257- closed = 'right'
258+ closed = closed or 'right'
258259 left = _ensure_index (left , copy = copy )
259260 right = _ensure_index (right , copy = copy )
260261
262+ if dtype is not None :
263+ # GH 19262: dtype must be an IntervalDtype to override inferred
264+ dtype = pandas_dtype (dtype )
265+ if not is_interval_dtype (dtype ):
266+ msg = 'dtype must be an IntervalDtype, got {dtype}'
267+ raise TypeError (msg .format (dtype = dtype ))
268+ elif dtype .subtype is not None :
269+ left = left .astype (dtype .subtype )
270+ right = right .astype (dtype .subtype )
271+
261272 # coerce dtypes to match if needed
262273 if is_float_dtype (left ) and is_integer_dtype (right ):
263274 right = right .astype (left .dtype )
@@ -304,7 +315,7 @@ def _shallow_copy(self, left=None, right=None, **kwargs):
304315 # only single value passed, could be an IntervalIndex
305316 # or array of Intervals
306317 if not isinstance (left , IntervalIndex ):
307- left = type ( self ). from_intervals (left )
318+ left = self . _constructor (left )
308319
309320 left , right = left .left , left .right
310321 else :
@@ -322,7 +333,7 @@ def _validate(self):
322333 Verify that the IntervalIndex is valid.
323334 """
324335 if self .closed not in _VALID_CLOSED :
325- raise ValueError ("invalid options for 'closed': {closed}"
336+ raise ValueError ("invalid option for 'closed': {closed}"
326337 .format (closed = self .closed ))
327338 if len (self .left ) != len (self .right ):
328339 raise ValueError ('left and right must have the same length' )
@@ -356,7 +367,7 @@ def _engine(self):
356367
357368 @property
358369 def _constructor (self ):
359- return type (self ). from_intervals
370+ return type (self )
360371
361372 def __contains__ (self , key ):
362373 """
@@ -402,7 +413,8 @@ def contains(self, key):
402413 return False
403414
404415 @classmethod
405- def from_breaks (cls , breaks , closed = 'right' , name = None , copy = False ):
416+ def from_breaks (cls , breaks , closed = 'right' , name = None , copy = False ,
417+ dtype = None ):
406418 """
407419 Construct an IntervalIndex from an array of splits
408420
@@ -417,6 +429,10 @@ def from_breaks(cls, breaks, closed='right', name=None, copy=False):
417429 Name to be stored in the index.
418430 copy : boolean, default False
419431 copy the data
432+ dtype : dtype or None, default None
433+ If None, dtype will be inferred
434+
435+ ..versionadded:: 0.23.0
420436
421437 Examples
422438 --------
@@ -430,18 +446,17 @@ def from_breaks(cls, breaks, closed='right', name=None, copy=False):
430446 interval_range : Function to create a fixed frequency IntervalIndex
431447 IntervalIndex.from_arrays : Construct an IntervalIndex from a left and
432448 right array
433- IntervalIndex.from_intervals : Construct an IntervalIndex from an array
434- of Interval objects
435449 IntervalIndex.from_tuples : Construct an IntervalIndex from a
436450 list/array of tuples
437451 """
438452 breaks = maybe_convert_platform_interval (breaks )
439453
440454 return cls .from_arrays (breaks [:- 1 ], breaks [1 :], closed ,
441- name = name , copy = copy )
455+ name = name , copy = copy , dtype = dtype )
442456
443457 @classmethod
444- def from_arrays (cls , left , right , closed = 'right' , name = None , copy = False ):
458+ def from_arrays (cls , left , right , closed = 'right' , name = None , copy = False ,
459+ dtype = None ):
445460 """
446461 Construct an IntervalIndex from a a left and right array
447462
@@ -458,6 +473,10 @@ def from_arrays(cls, left, right, closed='right', name=None, copy=False):
458473 Name to be stored in the index.
459474 copy : boolean, default False
460475 copy the data
476+ dtype : dtype or None, default None
477+ If None, dtype will be inferred
478+
479+ ..versionadded:: 0.23.0
461480
462481 Examples
463482 --------
@@ -471,22 +490,23 @@ def from_arrays(cls, left, right, closed='right', name=None, copy=False):
471490 interval_range : Function to create a fixed frequency IntervalIndex
472491 IntervalIndex.from_breaks : Construct an IntervalIndex from an array of
473492 splits
474- IntervalIndex.from_intervals : Construct an IntervalIndex from an array
475- of Interval objects
476493 IntervalIndex.from_tuples : Construct an IntervalIndex from a
477494 list/array of tuples
478495 """
479496 left = maybe_convert_platform_interval (left )
480497 right = maybe_convert_platform_interval (right )
481498
482- return cls ._simple_new (left , right , closed , name = name ,
483- copy = copy , verify_integrity = True )
499+ return cls ._simple_new (left , right , closed , name = name , copy = copy ,
500+ dtype = dtype , verify_integrity = True )
484501
485502 @classmethod
486- def from_intervals (cls , data , name = None , copy = False ):
503+ def from_intervals (cls , data , closed = None , name = None , copy = False ,
504+ dtype = None ):
487505 """
488506 Construct an IntervalIndex from a 1d array of Interval objects
489507
508+ .. deprecated:: 0.23.0
509+
490510 Parameters
491511 ----------
492512 data : array-like (1-dimensional)
@@ -496,6 +516,10 @@ def from_intervals(cls, data, name=None, copy=False):
496516 Name to be stored in the index.
497517 copy : boolean, default False
498518 by-default copy the data, this is compat only and ignored
519+ dtype : dtype or None, default None
520+ If None, dtype will be inferred
521+
522+ ..versionadded:: 0.23.0
499523
500524 Examples
501525 --------
@@ -521,16 +545,14 @@ def from_intervals(cls, data, name=None, copy=False):
521545 IntervalIndex.from_tuples : Construct an IntervalIndex from a
522546 list/array of tuples
523547 """
524- if isinstance (data , IntervalIndex ):
525- left , right , closed = data .left , data .right , data .closed
526- name = name or data .name
527- else :
528- data = maybe_convert_platform_interval (data )
529- left , right , closed = intervals_to_interval_bounds (data )
530- return cls .from_arrays (left , right , closed , name = name , copy = False )
548+ msg = ('IntervalIndex.from_intervals is deprecated and will be '
549+ 'removed in a future version; use IntervalIndex(...) instead' )
550+ warnings .warn (msg , FutureWarning , stacklevel = 2 )
551+ return cls (data , closed = closed , name = name , copy = copy , dtype = dtype )
531552
532553 @classmethod
533- def from_tuples (cls , data , closed = 'right' , name = None , copy = False ):
554+ def from_tuples (cls , data , closed = 'right' , name = None , copy = False ,
555+ dtype = None ):
534556 """
535557 Construct an IntervalIndex from a list/array of tuples
536558
@@ -545,10 +567,14 @@ def from_tuples(cls, data, closed='right', name=None, copy=False):
545567 Name to be stored in the index.
546568 copy : boolean, default False
547569 by-default copy the data, this is compat only and ignored
570+ dtype : dtype or None, default None
571+ If None, dtype will be inferred
572+
573+ ..versionadded:: 0.23.0
548574
549575 Examples
550576 --------
551- >>> pd.IntervalIndex.from_tuples([(0, 1), (1,2)])
577+ >>> pd.IntervalIndex.from_tuples([(0, 1), (1, 2)])
552578 IntervalIndex([(0, 1], (1, 2]],
553579 closed='right', dtype='interval[int64]')
554580
@@ -559,8 +585,6 @@ def from_tuples(cls, data, closed='right', name=None, copy=False):
559585 right array
560586 IntervalIndex.from_breaks : Construct an IntervalIndex from an array of
561587 splits
562- IntervalIndex.from_intervals : Construct an IntervalIndex from an array
563- of Interval objects
564588 """
565589 if len (data ):
566590 left , right = [], []
@@ -571,15 +595,22 @@ def from_tuples(cls, data, closed='right', name=None, copy=False):
571595 if isna (d ):
572596 lhs = rhs = np .nan
573597 else :
574- lhs , rhs = d
598+ try :
599+ # need list of length 2 tuples, e.g. [(0, 1), (1, 2), ...]
600+ lhs , rhs = d
601+ except ValueError :
602+ msg = ('IntervalIndex.from_tuples requires tuples of '
603+ 'length 2, got {tpl}' ).format (tpl = d )
604+ raise ValueError (msg )
605+ except TypeError :
606+ msg = ('IntervalIndex.from_tuples received an invalid '
607+ 'item, {tpl}' ).format (tpl = d )
608+ raise TypeError (msg )
575609 left .append (lhs )
576610 right .append (rhs )
577611
578- # TODO
579- # if we have nulls and we previous had *only*
580- # integer data, then we have changed the dtype
581-
582- return cls .from_arrays (left , right , closed , name = name , copy = False )
612+ return cls .from_arrays (left , right , closed , name = name , copy = False ,
613+ dtype = dtype )
583614
584615 def to_tuples (self , na_tuple = True ):
585616 """
@@ -921,7 +952,7 @@ def get_loc(self, key, method=None):
921952 Examples
922953 ---------
923954 >>> i1, i2 = pd.Interval(0, 1), pd.Interval(1, 2)
924- >>> index = pd.IntervalIndex.from_intervals ([i1, i2])
955+ >>> index = pd.IntervalIndex([i1, i2])
925956 >>> index.get_loc(1)
926957 0
927958
@@ -937,7 +968,7 @@ def get_loc(self, key, method=None):
937968 relevant intervals.
938969
939970 >>> i3 = pd.Interval(0, 2)
940- >>> overlapping_index = pd.IntervalIndex.from_intervals ([i2, i3])
971+ >>> overlapping_index = pd.IntervalIndex([i2, i3])
941972 >>> overlapping_index.get_loc(1.5)
942973 array([0, 1], dtype=int64)
943974 """
0 commit comments