16
16
is_interval_dtype ,
17
17
is_scalar ,
18
18
is_float ,
19
+ is_number ,
19
20
is_integer )
20
21
from pandas .core .indexes .base import (
21
22
Index , _ensure_index ,
26
27
Interval , IntervalMixin , IntervalTree ,
27
28
intervals_to_interval_bounds )
28
29
30
+ from pandas .core .indexes .datetimes import date_range
29
31
from pandas .core .indexes .multi import MultiIndex
30
32
from pandas .compat .numpy import function as nv
31
33
from pandas .core import common as com
32
34
from pandas .util ._decorators import cache_readonly , Appender
33
35
from pandas .core .config import get_option
36
+ from pandas .tseries .offsets import DateOffset
37
+ from pandas .tseries .frequencies import to_offset
34
38
35
39
import pandas .core .indexes .base as ibase
36
40
_index_doc_kwargs = dict (ibase ._index_doc_kwargs )
@@ -1030,21 +1034,22 @@ def func(self, other):
1030
1034
1031
1035
1032
1036
def interval_range (start = None , end = None , periods = None , freq = None ,
1033
- name = None , closed = 'right' , ** kwargs ):
1037
+ name = None , closed = 'right' ):
1034
1038
"""
1035
1039
Return a fixed frequency IntervalIndex
1036
1040
1037
1041
Parameters
1038
1042
----------
1039
- start : numeric, string, or datetime-like, default None
1043
+ start : numeric or datetime-like, default None
1040
1044
Left bound for generating intervals
1041
- end : numeric, string, or datetime-like, default None
1045
+ end : numeric or datetime-like, default None
1042
1046
Right bound for generating intervals
1043
1047
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.
1048
1053
name : string, default None
1049
1054
Name of the resulting IntervalIndex
1050
1055
closed : string, default 'right'
@@ -1058,32 +1063,107 @@ def interval_range(start=None, end=None, periods=None, freq=None,
1058
1063
Returns
1059
1064
-------
1060
1065
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]')
1061
1105
"""
1062
1106
if com ._count_not_none (start , end , periods ) != 2 :
1063
1107
raise ValueError ('Of the three parameters: start, end, and periods, '
1064
1108
'exactly two must be specified' )
1065
1109
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' )
1070
1120
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' )
1073
1128
1074
- if periods is None :
1075
- periods = int ((end - start ) // freq )
1076
- elif is_float (periods ):
1129
+ if is_float (periods ):
1077
1130
periods = int (periods )
1078
- elif not is_integer (periods ):
1131
+ elif not is_integer (periods ) and periods is not None :
1079
1132
msg = 'periods must be a number, got {periods}'
1080
- raise ValueError (msg .format (periods = periods ))
1133
+ raise TypeError (msg .format (periods = periods ))
1081
1134
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
1084
1145
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 )
1087
1168
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