-
-
Notifications
You must be signed in to change notification settings - Fork 18.4k
ENH: Support multiple opening hours intervals for BusinessHour #26628
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
Conversation
Codecov Report
@@ Coverage Diff @@
## master #26628 +/- ##
=======================================
Coverage 91.88% 91.88%
=======================================
Files 174 174
Lines 50692 50729 +37
=======================================
+ Hits 46576 46610 +34
- Misses 4116 4119 +3
Continue to review full report at Codecov.
|
Codecov Report
@@ Coverage Diff @@
## master #26628 +/- ##
=========================================
Coverage ? 91.72%
=========================================
Files ? 178
Lines ? 50808
Branches ? 0
=========================================
Hits ? 46605
Misses ? 4203
Partials ? 0
Continue to review full report at Codecov.
|
Can you reopen the original PR and push this back there? Would be preferable to maintain comment history |
Unfortunately I don't have the permission to reopen PR. |
Hmm OK. Doesn't look like I can reopen either. Not sure entirely what happened but I do see it was pushed to your master there - would be best practice going forward to not push to master as an FYI |
@WillAyd thanks for the advice. Any comment on the code changes? |
cc @gfyoung and @mroeschke who were already looking at original PR |
e3a849e
to
ec062e0
Compare
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.
One small testing comment otherwise looks pretty good to me.
doc/source/whatsnew/v0.25.0.rst
Outdated
@@ -82,7 +82,7 @@ Other Enhancements | |||
- :meth:`DataFrame.query` and :meth:`DataFrame.eval` now supports quoting column names with backticks to refer to names with spaces (:issue:`6508`) | |||
- :func:`merge_asof` now gives a more clear error message when merge keys are categoricals that are not equal (:issue:`26136`) | |||
- :meth:`pandas.core.window.Rolling` supports exponential (or Poisson) window type (:issue:`21303`) | |||
- | |||
- :class: `pandas.offsets.BusinessHour` supports multiple opening hours intervals |
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.
No space between :class:
and pandas.offsets.BusinessHour
. Also can you include the issue number this PR closes?
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.
Updated.
pandas/tseries/offsets.py
Outdated
@@ -579,9 +580,46 @@ class BusinessHourMixin(BusinessMixin): | |||
|
|||
def __init__(self, start='09:00', end='17:00', offset=timedelta(0)): | |||
# must be validated here to equality check | |||
start = liboffsets._validate_business_time(start) | |||
if _iterable_not_string(start): |
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.
use is_list_like
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.
Updated.
pandas/tseries/offsets.py
Outdated
raise ValueError('Must include at least 1 start time') | ||
else: | ||
start = np.array([start]) | ||
if _iterable_not_string(end): |
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.
same
blank line before the if
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.
Updated
pandas/tseries/offsets.py
Outdated
@@ -579,9 +580,46 @@ class BusinessHourMixin(BusinessMixin): | |||
|
|||
def __init__(self, start='09:00', end='17:00', offset=timedelta(0)): | |||
# must be validated here to equality check | |||
start = liboffsets._validate_business_time(start) | |||
if _iterable_not_string(start): | |||
start = np.asarray(start) |
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.
I would do this like
if not is_like_like(start):
start = [start]
if not len(start):
raise....
the np.asarray can be done when you validate
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.
Updated
pandas/tseries/offsets.py
Outdated
return True | ||
else: | ||
return False | ||
def _get_daytime_flag(self, start, end): |
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.
can you add a doc-string
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.
I removed this function as it's only use once and I think its clearer to just use start < end
.
pandas/tseries/offsets.py
Outdated
""" | ||
Return business hours in a day by seconds. | ||
""" | ||
if self._get_daytime_flag: | ||
if self._get_daytime_flag(start, end): | ||
# create dummy datetime to calculate businesshours in a day |
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.
do same as above, only define hour, minute in the clauses
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.
Updated
pandas/tseries/offsets.py
Outdated
bday_remain = result - bday_edge | ||
result = self._next_opening_time(other) | ||
result += bday_remain | ||
while rem != timedelta(0): |
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.
can you add some comments on what you are doing.
can you use more meaningful variable names
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.
Updated
we need to split test_offsets.py as its getting huge (can be after this PR). cc @pandas-dev/pandas-core |
Codecov Report
@@ Coverage Diff @@
## master #26628 +/- ##
=========================================
Coverage ? 92.04%
=========================================
Files ? 180
Lines ? 50741
Branches ? 0
=========================================
Hits ? 46704
Misses ? 4037
Partials ? 0
Continue to review full report at Codecov.
|
pandas/tseries/offsets.py
Outdated
@@ -576,12 +577,50 @@ def onOffset(self, dt): | |||
|
|||
|
|||
class BusinessHourMixin(BusinessMixin): | |||
start: List[dt_time] | |||
end: List[dt_time] | |||
n: int |
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.
This is to suppress the mypy errors:
"BusinessHourMixin" has no attribute "start"
"BusinessHourMixin" has no attribute "end"
"BusinessHourMixin" has no attribute "n"
However this syntax is not compatible with python 3.5...
Any advice?
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.
There's a lot of typing stuff only compatible with 3.5.2 onwards. It's an issue we need to address but orthogonal to this PR - is this causing some sort of error here?
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.
It's causing SyntaxError: invalid syntax
in python 3.5.
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.
@WillAyd any work around for this problem? Or should I just remove the type hintings?
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.
@WillAyd any suggestions?
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.
You need to use the “# type: ...” syntax. The way you have it now is unfortunately only available for parameters in function signatures in Python 3.5
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.
Thanks for the suggestion. I have removed the function signatures and it's green now.
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.
LGTM
pandas/tseries/offsets.py
Outdated
if not len(end): | ||
raise ValueError('Must include at least 1 end time') | ||
|
||
vliboffsets = np.vectorize(liboffsets._validate_business_time) |
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.
this is equivalent to start = np.array([liboffsets._validate_business_time(x) for x in start])
? Does the vectorize really make a difference?
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.
Yes they are equivalent. Is there a preference to use either way?
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.
yes, @jbrockmendel is more idiomatic here.
|
||
Parameters | ||
---------- | ||
other : datetime |
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.
is this specifically not Timestamp?
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.
No, it may not be Timestamp
.
if st.hour == dt.hour and st.minute == dt.minute: | ||
return dt + timedelta( | ||
seconds=self._get_business_hours_by_sec(st, self.end[i])) | ||
assert False |
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.
Presumably this shouldn't be reachable? ValueError?
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.
Yes, this is not reachable. Previously I raise ValueError here but later removed it following @mroeschke's suggestion #26400 (comment). assert False
was added later to prevent mypy error.
Minor comments, generally looks good. |
@howsiwei small change and can you merge master; ping on green. |
thanks @howsiwei nice patch! |
git diff upstream/master -u -- "*.py" | flake8 --diff
I'm not sure why, but after rebasing on latest master my previous PR #26400 is automatically closed. So I created a new PR here. I have edited as per all the comments there.