-
-
Notifications
You must be signed in to change notification settings - Fork 18.5k
ENH: Arithmetic with Timestamp-based intervals #36001
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
Changes from all commits
fea2974
da1ccc1
4bbdc6a
e4cae40
bb52809
1d3dd12
ed76752
5e3f616
f3fe17f
f10ed85
67e1ef8
d3a3492
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -49,6 +49,26 @@ For example: | |
buffer = io.BytesIO() | ||
data.to_csv(buffer, mode="w+b", encoding="utf-8", compression="gzip") | ||
|
||
Arithmetic with Timestamp and Timedelta-based Intervals | ||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
Arithmetic can now be performed on :class:`Interval` s having their left and right | ||
ends as :class:`Timestamp` s or :class:`Timedelta` s, like what would be possible | ||
if the ends were numeric (:issue:`35908`). | ||
|
||
Arithmetic can be performed by directly using arithmetic operators (`-` or `+`), | ||
so something like this will work: | ||
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 add something like this as an example in Interval |
||
|
||
.. ipython:: python | ||
|
||
interval = pd.Interval(pd.Timestamp("1900-01-01"), pd.Timestamp("1900-01-02")) | ||
interval - pd.Timestamp("1900-01-01") | ||
|
||
This works when endpoints are :class:`Timestamp` s or :class:`Timedelta` s. | ||
|
||
However, it should be noted that adding :class:`Timestamp` s , and subtracting :class:`Timestamp` | ||
from a :class:`Timedelta` is illegal. | ||
|
||
.. _whatsnew_120.enhancements.other: | ||
|
||
Other enhancements | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -395,6 +395,8 @@ cdef class Interval(IntervalMixin): | |
isinstance(y, numbers.Number) | ||
or PyDelta_Check(y) | ||
or is_timedelta64_object(y) | ||
or isinstance(y, _Timestamp) | ||
or isinstance(y, _Timedelta) | ||
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. this is covered by L397 |
||
): | ||
return Interval(self.left + y, self.right + y, closed=self.closed) | ||
elif ( | ||
|
@@ -413,6 +415,8 @@ cdef class Interval(IntervalMixin): | |
isinstance(y, numbers.Number) | ||
or PyDelta_Check(y) | ||
or is_timedelta64_object(y) | ||
or isinstance(y, _Timestamp) | ||
or isinstance(y, _Timedelta) | ||
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. the PyDelta_Check call makes the _Timedelta check extraneous. The remaining isinstance checks can be combined. |
||
): | ||
return Interval(self.left - y, self.right - y, closed=self.closed) | ||
return NotImplemented | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -184,6 +184,184 @@ def test_math_sub(self, closed): | |
with pytest.raises(TypeError, match=msg): | ||
interval - "foo" | ||
|
||
def test_math_sub_interval_timestamp_timestamp(self, closed): | ||
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. I'd name this |
||
# Tests for interval of timestamp - timestamp | ||
interval = Interval( | ||
Timestamp("1900-01-01"), Timestamp("1900-01-02"), closed=closed | ||
) | ||
expected = Interval( | ||
Timedelta("0 days 00:00:00"), Timedelta("1 days 00:00:00"), closed=closed | ||
) | ||
|
||
result = interval - Timestamp("1900-01-01") | ||
assert result == expected | ||
|
||
expected = Interval( | ||
interval.left - Timestamp("1900-01-01"), | ||
interval.right - Timestamp("1900-01-01"), | ||
closed=closed, | ||
) | ||
assert result == expected | ||
|
||
result = interval | ||
result -= Timestamp("1900-01-01") | ||
|
||
expected = Interval( | ||
Timedelta("0 days 00:00:00"), Timedelta("1 days 00:00:00"), closed=closed | ||
) | ||
assert result == expected | ||
|
||
expected = Interval( | ||
interval.left - Timestamp("1900-01-01"), | ||
interval.right - Timestamp("1900-01-01"), | ||
closed=closed, | ||
) | ||
assert result == expected | ||
|
||
def test_math_sub_interval_timestamp_timedelta(self, closed): | ||
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.
|
||
# Tests for interval of timestamps - timedelta | ||
interval = Interval( | ||
Timestamp("1900-01-01"), Timestamp("1900-01-02"), closed=closed | ||
) | ||
expected = Interval( | ||
Timestamp("1899-12-31"), Timestamp("1900-01-01"), closed=closed | ||
) | ||
|
||
result = interval - Timedelta("1 days 00:00:00") | ||
assert result == expected | ||
|
||
expected = Interval( | ||
interval.left - Timedelta("1 days 00:00:00"), | ||
interval.right - Timedelta("1 days 00:00:00"), | ||
closed=closed, | ||
) | ||
assert result == expected | ||
|
||
result = interval | ||
result -= Timedelta("1 days 00:00:00") | ||
|
||
expected = Interval( | ||
Timestamp("1899-12-31"), Timestamp("1900-01-01"), closed=closed | ||
) | ||
assert result == expected | ||
|
||
expected = Interval( | ||
interval.left - Timedelta("1 days 00:00:00"), | ||
interval.right - Timedelta("1 days 00:00:00"), | ||
closed=closed, | ||
) | ||
assert result == expected | ||
|
||
def test_math_add_interval_timestamp_timedelta(self, closed): | ||
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.
|
||
interval = Interval( | ||
Timestamp("1900-01-01"), Timestamp("1900-01-02"), closed=closed | ||
) | ||
expected = Interval( | ||
Timestamp("1900-01-02"), Timestamp("1900-01-03"), closed=closed | ||
) | ||
|
||
result = interval + Timedelta("1 days 00:00:00") | ||
assert result == expected | ||
|
||
result = interval | ||
result += Timedelta("1 days 00:00:00") | ||
assert result == expected | ||
|
||
expected = Interval( | ||
interval.left + Timedelta("1 days 00:00:00"), | ||
interval.right + Timedelta("1 days 00:00:00"), | ||
closed=closed, | ||
) | ||
|
||
result = interval + Timedelta("1 days 00:00:00") | ||
assert result == expected | ||
|
||
result = interval | ||
result += Timedelta("1 days 00:00:00") | ||
assert result == expected | ||
|
||
def test_math_add_interval_timedelta_timedelta(self, closed): | ||
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.
|
||
interval = Interval( | ||
Timedelta("1 days 00:00:00"), Timedelta("2 days 00:00:00"), closed=closed | ||
) | ||
expected = Interval( | ||
Timedelta("4 days 01:00:00"), Timedelta("5 days 01:00:00"), closed=closed | ||
) | ||
|
||
result = interval + Timedelta("3 days 01:00:00") | ||
assert result == expected | ||
|
||
result = interval | ||
result += Timedelta("3 days 01:00:00") | ||
assert result == expected | ||
|
||
expected = Interval( | ||
interval.left + Timedelta("3 days 01:00:00"), | ||
interval.right + Timedelta("3 days 01:00:00"), | ||
closed=closed, | ||
) | ||
|
||
result = interval + Timedelta("3 days 01:00:00") | ||
assert result == expected | ||
|
||
result = interval | ||
result += Timedelta("3 days 01:00:00") | ||
assert result == expected | ||
|
||
def test_sub_interval_imedelta_timedelta(self, closed): | ||
interval = Interval( | ||
Timedelta("1 days 00:00:00"), Timedelta("2 days 00:00:00"), closed=closed | ||
) | ||
expected = Interval( | ||
Timedelta("-3 days +23:00:00"), | ||
Timedelta("-2 days +23:00:00"), | ||
closed=closed, | ||
) | ||
|
||
result = interval - Timedelta("3 days 01:00:00") | ||
assert result == expected | ||
|
||
result = interval | ||
result -= Timedelta("3 days 01:00:00") | ||
assert result == expected | ||
|
||
expected = Interval( | ||
interval.left - Timedelta("3 days 01:00:00"), | ||
interval.right - Timedelta("3 days 01:00:00"), | ||
closed=closed, | ||
) | ||
|
||
result = interval - Timedelta("3 days 01:00:00") | ||
assert result == expected | ||
|
||
result = interval | ||
result -= Timedelta("3 days 01:00:00") | ||
assert result == expected | ||
|
||
def test_math_add_interval_timestamp_timestamp(self, closed): | ||
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.
|
||
interval = Interval( | ||
Timestamp("1900-01-01"), Timestamp("1900-01-02"), closed=closed | ||
) | ||
|
||
msg = r"unsupported operand type\(s\) for \+" | ||
with pytest.raises(TypeError, match=msg): | ||
interval = interval + Timestamp("2002-01-08") | ||
|
||
with pytest.raises(TypeError, match=msg): | ||
interval += Timestamp("2002-01-08") | ||
|
||
def test_math_sub_interval_timedelta_timestamp(self, closed): | ||
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.
|
||
interval = Interval( | ||
Timedelta("1 days 00:00:00"), Timedelta("3 days 00:00:00"), closed=closed | ||
) | ||
|
||
msg = r"unsupported operand type\(s\) for \-" | ||
with pytest.raises(TypeError, match=msg): | ||
interval = interval - Timestamp("1900-01-01") | ||
|
||
with pytest.raises(TypeError, match=msg): | ||
interval -= Timestamp("1900-01-01") | ||
|
||
def test_math_mult(self, closed): | ||
interval = Interval(0, 1, closed=closed) | ||
expected = Interval(0, 2, closed=closed) | ||
|
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 there a reason for all the spaces between backtick and "s"?