Skip to content

Commit 09498fd

Browse files
committed
jreback review and misc changes
addressed jreback's review, cleaned up docstrings, added documention, update to interval_range endpoint fix, additional tests
1 parent 028c572 commit 09498fd

File tree

10 files changed

+178
-85
lines changed

10 files changed

+178
-85
lines changed

doc/source/timeseries.rst

+9
Original file line numberDiff line numberDiff line change
@@ -1705,6 +1705,15 @@ has multiplied span.
17051705
17061706
pd.PeriodIndex(start='2014-01', freq='3M', periods=4)
17071707
1708+
If ``start`` or ``end`` are ``Period`` objects, they will be used as anchor
1709+
endpoints for a ``PeriodIndex`` with frequency matching that of the
1710+
``PeriodIndex`` constructor.
1711+
1712+
.. ipython:: python
1713+
1714+
pd.PeriodIndex(start=pd.Period('2017Q1', freq='Q'),
1715+
end=pd.Period('2017Q2', freq='Q'), freq='M')
1716+
17081717
Just like ``DatetimeIndex``, a ``PeriodIndex`` can also be used to index pandas
17091718
objects:
17101719

pandas/core/indexes/datetimes.py

+28-28
Original file line numberDiff line numberDiff line change
@@ -292,8 +292,8 @@ def __new__(cls, data=None,
292292
if is_float(periods):
293293
periods = int(periods)
294294
elif not is_integer(periods):
295-
raise ValueError('Periods must be a number, got %s' %
296-
str(periods))
295+
msg = 'periods must be a number, got {periods}'
296+
raise ValueError(msg.format(periods=periods))
297297

298298
if data is None and freq is None:
299299
raise ValueError("Must provide freq argument if no data is "
@@ -412,7 +412,7 @@ def __new__(cls, data=None,
412412
def _generate(cls, start, end, periods, name, offset,
413413
tz=None, normalize=False, ambiguous='raise', closed=None):
414414
if com._count_not_none(start, end, periods) != 2:
415-
raise ValueError('Of the three parameters, start, end, and '
415+
raise ValueError('Of the three parameters: start, end, and '
416416
'periods, exactly two must be specified')
417417

418418
_normalized = True
@@ -2005,7 +2005,7 @@ def _generate_regular_range(start, end, periods, offset):
20052005
def date_range(start=None, end=None, periods=None, freq='D', tz=None,
20062006
normalize=False, name=None, closed=None, **kwargs):
20072007
"""
2008-
Return a fixed frequency datetime index, with day (calendar) as the default
2008+
Return a fixed frequency DatetimeIndex, with day (calendar) as the default
20092009
frequency
20102010
20112011
Parameters
@@ -2014,24 +2014,24 @@ def date_range(start=None, end=None, periods=None, freq='D', tz=None,
20142014
Left bound for generating dates
20152015
end : string or datetime-like, default None
20162016
Right bound for generating dates
2017-
periods : integer or None, default None
2018-
If None, must specify start and end
2017+
periods : integer, default None
2018+
Number of dates to generate
20192019
freq : string or DateOffset, default 'D' (calendar daily)
20202020
Frequency strings can have multiples, e.g. '5H'
2021-
tz : string or None
2021+
tz : string, default None
20222022
Time zone name for returning localized DatetimeIndex, for example
20232023
Asia/Hong_Kong
20242024
normalize : bool, default False
20252025
Normalize start/end dates to midnight before generating date range
2026-
name : str, default None
2027-
Name of the resulting index
2028-
closed : string or None, default None
2026+
name : string, default None
2027+
Name of the resulting DatetimeIndex
2028+
closed : string, default None
20292029
Make the interval closed with respect to the given frequency to
20302030
the 'left', 'right', or both sides (None)
20312031
20322032
Notes
20332033
-----
2034-
Of the three parameters, ``start``, ``end``, and ``periods``, exactly two
2034+
Of the three parameters: ``start``, ``end``, and ``periods``, exactly two
20352035
must be specified.
20362036
20372037
To learn more about the frequency strings, please see `this link
@@ -2049,7 +2049,7 @@ def date_range(start=None, end=None, periods=None, freq='D', tz=None,
20492049
def bdate_range(start=None, end=None, periods=None, freq='B', tz=None,
20502050
normalize=True, name=None, closed=None, **kwargs):
20512051
"""
2052-
Return a fixed frequency datetime index, with business day as the default
2052+
Return a fixed frequency DatetimeIndex, with business day as the default
20532053
frequency
20542054
20552055
Parameters
@@ -2058,24 +2058,24 @@ def bdate_range(start=None, end=None, periods=None, freq='B', tz=None,
20582058
Left bound for generating dates
20592059
end : string or datetime-like, default None
20602060
Right bound for generating dates
2061-
periods : integer or None, default None
2062-
If None, must specify start and end
2061+
periods : integer, default None
2062+
Number of dates to generate
20632063
freq : string or DateOffset, default 'B' (business daily)
2064-
Frequency strings can have multiples, e.g. '5H'
2064+
Frequency strings can have multiples, e.g. '5, default
20652065
tz : string or None
20662066
Time zone name for returning localized DatetimeIndex, for example
20672067
Asia/Beijing
20682068
normalize : bool, default False
20692069
Normalize start/end dates to midnight before generating date range
2070-
name : str, default None
2071-
Name for the resulting index
2072-
closed : string or None, default None
2070+
name : string, default None
2071+
Name of the resulting DatetimeIndex
2072+
closed : string, default None
20732073
Make the interval closed with respect to the given frequency to
20742074
the 'left', 'right', or both sides (None)
20752075
20762076
Notes
20772077
-----
2078-
Of the three parameters, ``start``, ``end``, and ``periods``, exactly two
2078+
Of the three parameters: ``start``, ``end``, and ``periods``, exactly two
20792079
must be specified.
20802080
20812081
To learn more about the frequency strings, please see `this link
@@ -2094,7 +2094,7 @@ def bdate_range(start=None, end=None, periods=None, freq='B', tz=None,
20942094
def cdate_range(start=None, end=None, periods=None, freq='C', tz=None,
20952095
normalize=True, name=None, closed=None, **kwargs):
20962096
"""
2097-
**EXPERIMENTAL** Return a fixed frequency datetime index, with
2097+
**EXPERIMENTAL** Return a fixed frequency DatetimeIndex, with
20982098
CustomBusinessDay as the default frequency
20992099
21002100
.. warning:: EXPERIMENTAL
@@ -2108,29 +2108,29 @@ def cdate_range(start=None, end=None, periods=None, freq='C', tz=None,
21082108
Left bound for generating dates
21092109
end : string or datetime-like, default None
21102110
Right bound for generating dates
2111-
periods : integer or None, default None
2112-
If None, must specify start and end
2111+
periods : integer, default None
2112+
Number of dates to generate
21132113
freq : string or DateOffset, default 'C' (CustomBusinessDay)
21142114
Frequency strings can have multiples, e.g. '5H'
2115-
tz : string or None
2115+
tz : string, default None
21162116
Time zone name for returning localized DatetimeIndex, for example
21172117
Asia/Beijing
21182118
normalize : bool, default False
21192119
Normalize start/end dates to midnight before generating date range
2120-
name : str, default None
2121-
Name for the resulting index
2122-
weekmask : str, Default 'Mon Tue Wed Thu Fri'
2120+
name : string, default None
2121+
Name of the resulting DatetimeIndex
2122+
weekmask : string, Default 'Mon Tue Wed Thu Fri'
21232123
weekmask of valid business days, passed to ``numpy.busdaycalendar``
21242124
holidays : list
21252125
list/array of dates to exclude from the set of valid business days,
21262126
passed to ``numpy.busdaycalendar``
2127-
closed : string or None, default None
2127+
closed : string, default None
21282128
Make the interval closed with respect to the given frequency to
21292129
the 'left', 'right', or both sides (None)
21302130
21312131
Notes
21322132
-----
2133-
Of the three parameters, ``start``, ``end``, and ``periods``, exactly two
2133+
Of the three parameters: ``start``, ``end``, and ``periods``, exactly two
21342134
must be specified.
21352135
21362136
To learn more about the frequency strings, please see `this link

pandas/core/indexes/interval.py

+31-17
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
is_float_dtype,
1616
is_interval_dtype,
1717
is_scalar,
18+
is_float,
1819
is_integer)
1920
from pandas.core.indexes.base import (
2021
Index, _ensure_index,
@@ -1028,48 +1029,61 @@ def func(self, other):
10281029
IntervalIndex._add_logical_methods_disabled()
10291030

10301031

1031-
def interval_range(start=None, end=None, freq=None, periods=None,
1032+
def interval_range(start=None, end=None, periods=None, freq=None,
10321033
name=None, closed='right', **kwargs):
10331034
"""
10341035
Return a fixed frequency IntervalIndex
10351036
10361037
Parameters
10371038
----------
1038-
start : string or datetime-like, default None
1039-
Left bound for generating data
1040-
end : string or datetime-like, default None
1041-
Right bound for generating data
1042-
freq : integer, string or DateOffset, default 1
1039+
start : numeric, string, or datetime-like, default None
1040+
Left bound for generating intervals
1041+
end : numeric, string, or datetime-like, default None
1042+
Right bound for generating intervals
10431043
periods : integer, default None
1044-
name : str, default None
1045-
Name of the resulting index
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+
name : string, default None
1049+
Name of the resulting IntervalIndex
10461050
closed : string, default 'right'
10471051
options are: 'left', 'right', 'both', 'neither'
10481052
10491053
Notes
10501054
-----
1051-
Of the three parameters, ``start``, ``end``, and ``periods``, exactly two
1055+
Of the three parameters: ``start``, ``end``, and ``periods``, exactly two
10521056
must be specified.
10531057
10541058
Returns
10551059
-------
10561060
rng : IntervalIndex
10571061
"""
10581062
if com._count_not_none(start, end, periods) != 2:
1059-
raise ValueError('Of the three parameters, start, end, and periods, '
1063+
raise ValueError('Of the three parameters: start, end, and periods, '
10601064
'exactly two must be specified')
10611065

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")
1070+
10621071
if freq is None:
10631072
freq = 1
1073+
1074+
if periods is None:
1075+
periods = int((end - start) // freq)
1076+
elif is_float(periods):
1077+
periods = int(periods)
1078+
elif not is_integer(periods):
1079+
msg = 'periods must be a number, got {periods}'
1080+
raise ValueError(msg.format(periods=periods))
1081+
10641082
if start is None:
10651083
start = end - periods * freq
1066-
if end is None:
1067-
end = start + periods * freq
10681084

1069-
# must all be same units or None
1070-
arr = np.array([start, end, freq])
1071-
if is_object_dtype(arr):
1072-
raise ValueError("start, end, freq need to be the same type")
1085+
# force end to be consistent with freq (truncate if freq skips over end)
1086+
end = start + periods * freq
10731087

1074-
return IntervalIndex.from_breaks(np.arange(start, end + 1, freq),
1088+
return IntervalIndex.from_breaks(np.arange(start, end + freq, freq),
10751089
name=name, closed=closed, **kwargs)

pandas/core/indexes/period.py

+35-15
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,8 @@ def __new__(cls, data=None, ordinal=None, freq=None, start=None, end=None,
199199
if is_float(periods):
200200
periods = int(periods)
201201
elif not is_integer(periods):
202-
raise ValueError('Periods must be a number, got %s' %
203-
str(periods))
202+
msg = 'periods must be a number, got {periods}'
203+
raise ValueError(msg.format(periods=periods))
204204

205205
if name is None and hasattr(data, 'name'):
206206
name = data.name
@@ -1052,7 +1052,7 @@ def tz_localize(self, tz, infer_dst=False):
10521052

10531053
def _get_ordinal_range(start, end, periods, freq, mult=1):
10541054
if com._count_not_none(start, end, periods) != 2:
1055-
raise ValueError('Of the three parameters, start, end, and periods, '
1055+
raise ValueError('Of the three parameters: start, end, and periods, '
10561056
'exactly two must be specified')
10571057

10581058
if freq is not None:
@@ -1067,9 +1067,9 @@ def _get_ordinal_range(start, end, periods, freq, mult=1):
10671067
is_end_per = isinstance(end, Period)
10681068

10691069
if is_start_per and is_end_per and start.freq != end.freq:
1070-
raise ValueError('Start and end must have same freq')
1070+
raise ValueError('start and end must have same freq')
10711071
if (start is tslib.NaT or end is tslib.NaT):
1072-
raise ValueError('Start and end must not be NaT')
1072+
raise ValueError('start and end must not be NaT')
10731073

10741074
if freq is None:
10751075
if is_start_per:
@@ -1158,23 +1158,25 @@ def pnow(freq=None):
11581158

11591159
def period_range(start=None, end=None, periods=None, freq='D', name=None):
11601160
"""
1161-
Return a fixed frequency datetime index, with day (calendar) as the default
1161+
Return a fixed frequency PeriodIndex, with day (calendar) as the default
11621162
frequency
11631163
11641164
Parameters
11651165
----------
1166-
start : starting value, period-like, optional
1167-
end : ending value, period-like, optional
1168-
periods : int, default None
1169-
Number of periods in the index
1170-
freq : str/DateOffset, default 'D'
1166+
start : string or period-like, default None
1167+
Left bound for generating periods
1168+
end : string or period-like, default None
1169+
Right bound for generating periods
1170+
periods : integer, default None
1171+
Number of periods to generate
1172+
freq : string or DateOffset, default 'D' (calendar daily)
11711173
Frequency alias
1172-
name : str, default None
1173-
Name for the resulting PeriodIndex
1174+
name : string, default None
1175+
Name of the resulting PeriodIndex
11741176
11751177
Notes
11761178
-----
1177-
Of the three parameters, ``start``, ``end``, and ``periods``, exactly two
1179+
Of the three parameters: ``start``, ``end``, and ``periods``, exactly two
11781180
must be specified.
11791181
11801182
To learn more about the frequency strings, please see `this link
@@ -1183,9 +1185,27 @@ def period_range(start=None, end=None, periods=None, freq='D', name=None):
11831185
Returns
11841186
-------
11851187
prng : PeriodIndex
1188+
1189+
Examples
1190+
--------
1191+
1192+
>>> pd.period_range(start='2017-01-01', end='2018-01-01', freq='M')
1193+
PeriodIndex(['2017-01', '2017-02', '2017-03', '2017-04', '2017-05',
1194+
'2017-06', '2017-06', '2017-07', '2017-08', '2017-09',
1195+
'2017-10', '2017-11', '2017-12', '2018-01'],
1196+
dtype='period[M]', freq='M')
1197+
1198+
If ``start`` or ``end`` are ``Period`` objects, they will be used as anchor
1199+
endpoints for a ``PeriodIndex`` with frequency matching that of the
1200+
``period_range`` constructor.
1201+
1202+
>>> pd.period_range(start=pd.Period('2017Q1', freq='Q'),
1203+
... end=pd.Period('2017Q2', freq='Q'), freq='M')
1204+
PeriodIndex(['2017-03', '2017-04', '2017-05', '2017-06'],
1205+
dtype='period[M]', freq='M')
11861206
"""
11871207
if com._count_not_none(start, end, periods) != 2:
1188-
raise ValueError('Of the three parameters, start, end, and periods, '
1208+
raise ValueError('Of the three parameters: start, end, and periods, '
11891209
'exactly two must be specified')
11901210

11911211
return PeriodIndex(start=start, end=end, periods=periods,

0 commit comments

Comments
 (0)