-
-
Notifications
You must be signed in to change notification settings - Fork 18.4k
move shift_months to liboffsets, unify implementations of Q/M/Y offsets, #18375
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
jreback
merged 4 commits into
pandas-dev:master
from
jbrockmendel:tslibs-offsets-shift_months
Nov 20, 2017
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
cbdc4c9
move shift_months to liboffsets, unify implementations of Q/M/Y offsets,
jbrockmendel a9d4563
Merge branch 'master' of https://github.com/pandas-dev/pandas into ts…
jbrockmendel 5f082d3
deprivatize funds, add to setup depends
jbrockmendel d865d81
require np_datetime_sources for liboffstes
jbrockmendel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
# cython: profile=False | ||
|
||
cimport cython | ||
from cython cimport Py_ssize_t | ||
|
||
import time | ||
from cpython.datetime cimport datetime, timedelta, time as dt_time | ||
|
@@ -10,6 +11,7 @@ from dateutil.relativedelta import relativedelta | |
|
||
import numpy as np | ||
cimport numpy as np | ||
from numpy cimport int64_t | ||
np.import_array() | ||
|
||
|
||
|
@@ -19,6 +21,10 @@ from pandas._libs.tslib import monthrange | |
|
||
from conversion cimport tz_convert_single, pydt_to_i8 | ||
from frequencies cimport get_freq_code | ||
from nattype cimport NPY_NAT | ||
from np_datetime cimport (pandas_datetimestruct, | ||
dtstruct_to_dt64, dt64_to_dtstruct, | ||
is_leapyear, days_per_month_table) | ||
|
||
# --------------------------------------------------------------------- | ||
# Constants | ||
|
@@ -419,13 +425,121 @@ class BaseOffset(_BaseOffset): | |
# ---------------------------------------------------------------------- | ||
# RelativeDelta Arithmetic | ||
|
||
@cython.wraparound(False) | ||
@cython.boundscheck(False) | ||
cdef inline int get_days_in_month(int year, int month) nogil: | ||
return days_per_month_table[is_leapyear(year)][month - 1] | ||
|
||
|
||
cdef inline int year_add_months(pandas_datetimestruct dts, int months) nogil: | ||
"""new year number after shifting pandas_datetimestruct number of months""" | ||
return dts.year + (dts.month + months - 1) / 12 | ||
|
||
|
||
cdef inline int month_add_months(pandas_datetimestruct dts, int months) nogil: | ||
""" | ||
New month number after shifting pandas_datetimestruct | ||
number of months. | ||
""" | ||
cdef int new_month = (dts.month + months) % 12 | ||
return 12 if new_month == 0 else new_month | ||
|
||
|
||
@cython.wraparound(False) | ||
@cython.boundscheck(False) | ||
def shift_months(int64_t[:] dtindex, int months, object day=None): | ||
""" | ||
Given an int64-based datetime index, shift all elements | ||
specified number of months using DateOffset semantics | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you doc-string things as well (here just fix the formatting to numpydoc) |
||
|
||
day: {None, 'start', 'end'} | ||
* None: day of month | ||
* 'start' 1st day of month | ||
* 'end' last day of month | ||
""" | ||
cdef: | ||
Py_ssize_t i | ||
pandas_datetimestruct dts | ||
int count = len(dtindex) | ||
int months_to_roll | ||
bint roll_check | ||
int64_t[:] out = np.empty(count, dtype='int64') | ||
|
||
if day is None: | ||
with nogil: | ||
for i in range(count): | ||
if dtindex[i] == NPY_NAT: | ||
out[i] = NPY_NAT | ||
continue | ||
|
||
dt64_to_dtstruct(dtindex[i], &dts) | ||
dts.year = year_add_months(dts, months) | ||
dts.month = month_add_months(dts, months) | ||
|
||
dts.day = min(dts.day, get_days_in_month(dts.year, dts.month)) | ||
out[i] = dtstruct_to_dt64(&dts) | ||
elif day == 'start': | ||
roll_check = False | ||
if months <= 0: | ||
months += 1 | ||
roll_check = True | ||
with nogil: | ||
for i in range(count): | ||
if dtindex[i] == NPY_NAT: | ||
out[i] = NPY_NAT | ||
continue | ||
|
||
dt64_to_dtstruct(dtindex[i], &dts) | ||
months_to_roll = months | ||
|
||
# offset semantics - if on the anchor point and going backwards | ||
# shift to next | ||
if roll_check and dts.day == 1: | ||
months_to_roll -= 1 | ||
|
||
dts.year = year_add_months(dts, months_to_roll) | ||
dts.month = month_add_months(dts, months_to_roll) | ||
dts.day = 1 | ||
|
||
out[i] = dtstruct_to_dt64(&dts) | ||
elif day == 'end': | ||
roll_check = False | ||
if months > 0: | ||
months -= 1 | ||
roll_check = True | ||
with nogil: | ||
for i in range(count): | ||
if dtindex[i] == NPY_NAT: | ||
out[i] = NPY_NAT | ||
continue | ||
|
||
dt64_to_dtstruct(dtindex[i], &dts) | ||
months_to_roll = months | ||
|
||
# similar semantics - when adding shift forward by one | ||
# month if already at an end of month | ||
if roll_check and dts.day == get_days_in_month(dts.year, | ||
dts.month): | ||
months_to_roll += 1 | ||
|
||
dts.year = year_add_months(dts, months_to_roll) | ||
dts.month = month_add_months(dts, months_to_roll) | ||
|
||
dts.day = get_days_in_month(dts.year, dts.month) | ||
out[i] = dtstruct_to_dt64(&dts) | ||
else: | ||
raise ValueError("day must be None, 'start' or 'end'") | ||
|
||
return np.asarray(out) | ||
|
||
|
||
cpdef datetime shift_month(datetime stamp, int months, object day_opt=None): | ||
""" | ||
Given a datetime (or Timestamp) `stamp`, an integer `months` and an | ||
option `day_opt`, return a new datetimelike that many months later, | ||
with day determined by `day_opt` using relativedelta semantics. | ||
|
||
Scalar analogue of tslib.shift_months | ||
Scalar analogue of shift_months | ||
|
||
Parameters | ||
---------- | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add these in setup.py