1616 is_interval_dtype ,
1717 is_scalar ,
1818 is_float ,
19+ is_number ,
1920 is_integer )
2021from pandas .core .indexes .base import (
2122 Index , _ensure_index ,
2627 Interval , IntervalMixin , IntervalTree ,
2728 intervals_to_interval_bounds )
2829
30+ from pandas .core .indexes .datetimes import date_range
2931from pandas .core .indexes .multi import MultiIndex
3032from pandas .compat .numpy import function as nv
3133from pandas .core import common as com
3234from pandas .util ._decorators import cache_readonly , Appender
3335from pandas .core .config import get_option
36+ from pandas .tseries .offsets import DateOffset
37+ from pandas .tseries .frequencies import to_offset
3438
3539import pandas .core .indexes .base as ibase
3640_index_doc_kwargs = dict (ibase ._index_doc_kwargs )
@@ -1030,21 +1034,22 @@ def func(self, other):
10301034
10311035
10321036def interval_range (start = None , end = None , periods = None , freq = None ,
1033- name = None , closed = 'right' , ** kwargs ):
1037+ name = None , closed = 'right' ):
10341038 """
10351039 Return a fixed frequency IntervalIndex
10361040
10371041 Parameters
10381042 ----------
1039- start : numeric, string, or datetime-like, default None
1043+ start : numeric or datetime-like, default None
10401044 Left bound for generating intervals
1041- end : numeric, string, or datetime-like, default None
1045+ end : numeric or datetime-like, default None
10421046 Right bound for generating intervals
10431047 periods : integer, default None
1044- Number of intervals to generate
1045- freq : numeric, string, or DateOffset, default 1
1046- The length of each interval. Must be consistent with the
1047- type of start and end
1048+ Number of periods to generate
1049+ freq : numeric, string, or DateOffset, default None
1050+ The length of each interval. Must be consistent with the type of start
1051+ and end, e.g. 2 for numeric, or '5H' for datetime-like. Default is 1
1052+ for numeric and 'D' (calendar daily) for datetime-like.
10481053 name : string, default None
10491054 Name of the resulting IntervalIndex
10501055 closed : string, default 'right'
@@ -1058,32 +1063,107 @@ def interval_range(start=None, end=None, periods=None, freq=None,
10581063 Returns
10591064 -------
10601065 rng : IntervalIndex
1066+
1067+ Examples
1068+ --------
1069+
1070+ Numeric ``start`` and ``end`` is supported.
1071+
1072+ >>> pd.interval_range(start=0, end=5)
1073+ IntervalIndex([(0, 1], (1, 2], (2, 3], (3, 4], (4, 5]]
1074+ closed='right', dtype='interval[int64]')
1075+
1076+ Additionally, datetime-like input is also supported.
1077+
1078+ >>> pd.interval_range(start='2017-01-01', end='2017-01-04')
1079+ IntervalIndex([(2017-01-01, 2017-01-02], (2017-01-02, 2017-01-03],
1080+ (2017-01-03, 2017-01-04]]
1081+ closed='right', dtype='interval[datetime64[ns]]')
1082+
1083+ The ``freq`` parameter specifies the frequency between the left and right.
1084+ endpoints of the individual intervals within the ``IntervalIndex``. For
1085+ numeric ``start`` and ``end``, the frequency must also be numeric.
1086+
1087+ >>> pd.interval_range(start=0, periods=4, freq=1.5)
1088+ IntervalIndex([(0.0, 1.5], (1.5, 3.0], (3.0, 4.5], (4.5, 6.0]]
1089+ closed='right', dtype='interval[float64]')
1090+
1091+ Similarly, for datetime-like ``start`` and ``end``, the frequency must be
1092+ convertible to a DateOffset.
1093+
1094+ >>> pd.interval_range(start='2017-01-01', periods=3, freq='MS')
1095+ IntervalIndex([(2017-01-01, 2017-02-01], (2017-02-01, 2017-03-01],
1096+ (2017-03-01, 2017-04-01]]
1097+ closed='right', dtype='interval[datetime64[ns]]')
1098+
1099+ The ``closed`` parameter specifies which endpoints of the individual
1100+ intervals within the ``IntervalIndex`` are closed.
1101+
1102+ >>> pd.interval_range(end=5, periods=4, closed='both')
1103+ IntervalIndex([[1, 2], [2, 3], [3, 4], [4, 5]]
1104+ closed='both', dtype='interval[int64]')
10611105 """
10621106 if com ._count_not_none (start , end , periods ) != 2 :
10631107 raise ValueError ('Of the three parameters: start, end, and periods, '
10641108 'exactly two must be specified' )
10651109
1066- # must all be same units or None
1067- arr = np .array (list (com ._not_none (start , end , freq )))
1068- if is_object_dtype (arr ):
1069- raise ValueError ("start, end, freq need to be the same type" )
1110+ # assume datetime-like unless we find numeric start or end
1111+ is_datetime_interval = True
1112+
1113+ if is_number (start ):
1114+ is_datetime_interval = False
1115+ elif start is not None :
1116+ try :
1117+ start = Timestamp (start )
1118+ except (TypeError , ValueError ):
1119+ raise ValueError ('start must be numeric or datetime-like' )
10701120
1071- if freq is None :
1072- freq = 1
1121+ if is_number (end ):
1122+ is_datetime_interval = False
1123+ elif end is not None :
1124+ try :
1125+ end = Timestamp (end )
1126+ except (TypeError , ValueError ):
1127+ raise ValueError ('end must be numeric or datetime-like' )
10731128
1074- if periods is None :
1075- periods = int ((end - start ) // freq )
1076- elif is_float (periods ):
1129+ if is_float (periods ):
10771130 periods = int (periods )
1078- elif not is_integer (periods ):
1131+ elif not is_integer (periods ) and periods is not None :
10791132 msg = 'periods must be a number, got {periods}'
1080- raise ValueError (msg .format (periods = periods ))
1133+ raise TypeError (msg .format (periods = periods ))
10811134
1082- if start is None :
1083- start = end - periods * freq
1135+ if is_datetime_interval :
1136+ freq = freq or 'D'
1137+ if not isinstance (freq , DateOffset ):
1138+ try :
1139+ freq = to_offset (freq )
1140+ except ValueError :
1141+ raise ValueError ('freq must be convertible to DateOffset when '
1142+ 'start/end are datetime-like' )
1143+ else :
1144+ freq = freq or 1
10841145
1085- # force end to be consistent with freq (truncate if freq skips over end)
1086- end = start + periods * freq
1146+ # verify type compatibility
1147+ is_numeric_interval = all (map (is_number , com ._not_none (start , end , freq )))
1148+ if not is_datetime_interval and not is_numeric_interval :
1149+ raise TypeError ("start, end, freq need to be type compatible" )
1150+
1151+ if is_numeric_interval :
1152+ if periods is None :
1153+ periods = int ((end - start ) // freq )
1154+
1155+ if start is None :
1156+ start = end - periods * freq
1157+
1158+ # force end to be consistent with freq (lower if freq skips over end)
1159+ end = start + periods * freq
1160+
1161+ # end + freq for inclusive endpoint
1162+ breaks = np .arange (start , end + freq , freq )
1163+ else :
1164+ # add one to account for interval endpoints (n breaks = n-1 intervals)
1165+ if periods is not None :
1166+ periods += 1
1167+ breaks = date_range (start = start , end = end , periods = periods , freq = freq )
10871168
1088- return IntervalIndex .from_breaks (np .arange (start , end + freq , freq ),
1089- name = name , closed = closed , ** kwargs )
1169+ return IntervalIndex .from_breaks (breaks , name = name , closed = closed )
0 commit comments