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