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

implement Timedelta mod, divmod, rmod, rdivmod, fix and test scalar methods #19365

Closed
wants to merge 19 commits into
base: master
from

Conversation

Projects
None yet
4 participants
@jbrockmendel
Member

jbrockmendel commented Jan 24, 2018

Implemented mod, divmod, rmod, rdivmod for Timedelta, along with tests. In the process turned up a few bugs. Conditional probability that there are more bugs is pretty good.

  • fixed offsets.Tick // Timedelta, Timedelta // offsets.Tick

  • fixed Timedelta [+-] timedelta64 returning timedelta64 instead of Timedelta

  • fixed Timedelta [*/] int64 returning timedelta64 instead of Timedelta

  • closes #xxxx

  • tests added / passed

  • passes git diff upstream/master -u -- "*.py" | flake8 --diff

  • whatsnew entry

expected = np.array([9, 12], dtype='m8[m]').astype('m8[ns]')
tm.assert_numpy_array_equal(result, expected)
with pytest.raises(TypeError):

This comment has been minimized.

@gfyoung

gfyoung Jan 24, 2018

Member

What's the error message?

This comment has been minimized.

@jreback

jreback Jan 29, 2018

Contributor

add a comment on why this is prohibiited, any other ops not allowed?

@@ -128,6 +128,57 @@ def test_unary_ops(self):
assert abs(-td) == td
assert abs(-td) == Timedelta('10d')
def test_mul(self):

This comment has been minimized.

@gfyoung

gfyoung Jan 24, 2018

Member

Reference your PR number under all of these added tests.

@gfyoung

This comment has been minimized.

Member

gfyoung commented Jan 24, 2018

Conditional probability that there are more bugs is pretty good

There are always more bugs 😄

Also, you probably should add a whatsnew to address all of the things that you patched.

@codecov

This comment has been minimized.

codecov bot commented Jan 24, 2018

Codecov Report

Merging #19365 into master will decrease coverage by <.01%.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff             @@
##           master   #19365      +/-   ##
==========================================
- Coverage   91.58%   91.58%   -0.01%     
==========================================
  Files         150      150              
  Lines       48862    48862              
==========================================
- Hits        44750    44748       -2     
- Misses       4112     4114       +2
Flag Coverage Δ
#multiple 89.95% <ø> (-0.01%) ⬇️
#single 41.75% <ø> (ø) ⬆️
Impacted Files Coverage Δ
pandas/util/testing.py 83.64% <0%> (-0.21%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 44c822d...c22dc47. Read the comment docs.

@@ -138,6 +191,10 @@ def test_binary_ops_nat(self):
assert (td // pd.NaT) is np.nan
assert (td // np.timedelta64('NaT')) is np.nan
# GH#19365
assert td - np.timedelta64('NaT') is pd.NaT

This comment has been minimized.

@jreback

jreback Jan 25, 2018

Contributor

test of the reverse?

assert isinstance(result, Timedelta)
assert result == Timedelta(days=20)
result = td + timedelta(days=9)

This comment has been minimized.

@jreback

jreback Jan 25, 2018

Contributor

can you test for radd as well here

assert isinstance(result, Timedelta)
assert result == Timedelta(minutes=3, seconds=20)
# Invalid Others

This comment has been minimized.

@jreback

jreback Jan 25, 2018

Contributor

can you make the error conditions separate and test all of the reverse failures e.g.
np.array([1, 2]) % td; I think pretty much every div/mod/divmod op should fail for the reverse (int/float op with td)

@@ -419,6 +419,7 @@ Datetimelike
- Bug in ``.astype()`` to non-ns timedelta units would hold the incorrect dtype (:issue:`19176`, :issue:`19223`, :issue:`12425`)
- Bug in subtracting :class:`Series` from ``NaT`` incorrectly returning ``NaT`` (:issue:`19158`)
- Bug in :func:`Series.truncate` which raises ``TypeError`` with a monotonic ``PeriodIndex`` (:issue:`17717`)
- Bug :func:`Timedelta.__mod__`, :func:`Timedelta.__divmod__` where operating with timedelta-like or numeric arguments would incorrectly raise ``TypeError`` (:issue:`19365`)

This comment has been minimized.

@jreback

jreback Jan 25, 2018

Contributor

can you make this in to a sub-section and give a couple of examples. also add a small example in the timedelta docs.

This comment has been minimized.

@jreback

jreback Jan 25, 2018

Contributor

put it in api changes section

This comment has been minimized.

@jbrockmendel

jbrockmendel Jan 25, 2018

Member

can you make this in to a sub-section and give a couple of examples.

Sure. For the ipython code blocks, do I copy/paste the traceback?

@jreback

This comment has been minimized.

Contributor

jreback commented Jan 25, 2018

also see if we have any open issues related to this (maybe)

@jbrockmendel

This comment has been minimized.

Member

jbrockmendel commented Jan 28, 2018

Thoughts on this? There are other dark corners of Timedelta I'd like to address after this is done.

.. ipython:: python
pd.Timedelta(hours=37) % datetime.timedelta(hours=2)
divmod(datetime.timedelta(hours=2), pd.Timedelta(minutes=11))

This comment has been minimized.

@jreback

jreback Jan 29, 2018

Contributor

give a comment or 2 hear, a dense block of code is not very friendly

This comment has been minimized.

@jreback

jreback Jan 29, 2018

Contributor

just give 2 examples not need to have every case covered

This comment has been minimized.

@jreback

jreback Jan 29, 2018

Contributor

don't show the array case, I find that unfriendly

.. ipython:: python
td = pd.Timedelta(hours=37)

This comment has been minimized.

@jreback

jreback Jan 29, 2018

Contributor

show the td, then in another ipython block show operations on it.

td % pd.Timedelta(hours=2)
divmod(td, np.array([2, 3], dtype='timedelta64[h]'))
Previous Behavior:

This comment has been minimized.

@jreback

jreback Jan 29, 2018

Contributor

there is no need to show previous behavior here.

Current Behavior
.. ipython:: python

This comment has been minimized.

@jreback

jreback Jan 29, 2018

Contributor

this is repetetive

@@ -313,6 +353,7 @@ Other API Changes
- :func:`DatetimeIndex.shift` and :func:`TimedeltaIndex.shift` will now raise ``NullFrequencyError`` (which subclasses ``ValueError``, which was raised in older versions) when the index object frequency is ``None`` (:issue:`19147`)
- Addition and subtraction of ``NaN`` from a :class:`Series` with ``dtype='timedelta64[ns]'`` will raise a ``TypeError` instead of treating the ``NaN`` as ``NaT`` (:issue:`19274`)
- Set operations (union, difference...) on :class:`IntervalIndex` with incompatible index types will now raise a ``TypeError`` rather than a ``ValueError`` (:issue:`19329`)
- :func:`Timedelta.__mod__`, :func:`Timedelta.__divmod__` now accept timedelta-like and numeric arguments instead of raising ``TypeError`` (:issue:`19365`)

This comment has been minimized.

@jreback

jreback Jan 29, 2018

Contributor

you are covering this above, don't repeat

This comment has been minimized.

@jbrockmendel

jbrockmendel Jan 29, 2018

Member

As in remove this line entirely b/c the "Timedelta mod method" section above exists?

expected = np.array([9, 12], dtype='m8[m]').astype('m8[ns]')
tm.assert_numpy_array_equal(result, expected)
with pytest.raises(TypeError):

This comment has been minimized.

@jreback

jreback Jan 29, 2018

Contributor

add a comment on why this is prohibiited, any other ops not allowed?

result = td + datetime(2016, 1, 1)
assert result == pd.Timestamp(2016, 1, 11)

This comment has been minimized.

@jreback

jreback Jan 29, 2018

Contributor

I KNOW we have tests for this, is there a reason you are duplicating? (rather than moving those to here)?

This comment has been minimized.

@jreback

jreback Jan 29, 2018

Contributor

test_ops_scalar. I have not problem adding tests, but please don't duplicate.

This comment has been minimized.

@jbrockmendel

jbrockmendel Jan 29, 2018

Member

I KNOW we have tests for this, is there a reason you are duplicating? (rather than moving those to here)?

ATM there is NO test for Timedelta(10, unit='d') + np.timedelta64(-4, 'D') (try it; it returns a timedelta64). More generally, there are no add/sub tests in test_timedelta.

@pytest.mark.parametrize('op', [lambda x, y: x + y,
lambda x, y: y + x])
def test_add_timedeltalike(self, op):

This comment has been minimized.

@jreback

jreback Jan 29, 2018

Contributor

these are also duplicative. pls consolidate.

This comment has been minimized.

@jbrockmendel

jbrockmendel Jan 29, 2018

Member

With what?

This comment has been minimized.

@jreback

jreback Feb 9, 2018

Contributor

tests_ops_offset. you can prob just remove that. but pls look for existing tests and modify / remove them.

This comment has been minimized.

@jbrockmendel

jbrockmendel Feb 9, 2018

Member

Done. There's some more organizing of Timedelta arith tests I'll do in the next pass. These need to be much more thorough.

td = Timedelta(hours=37)
# Array-like others
result = td % np.array([6, 5], dtype='timedelta64[h]')

This comment has been minimized.

@jreback

jreback Jan 29, 2018

Contributor

see my comment above

This comment has been minimized.

@jreback

jreback Feb 16, 2018

Contributor

same comment

assert isinstance(result, Timedelta)
assert result == Timedelta(minutes=2)
result = np.array([5, 6], dtype='m8[m]') % td

This comment has been minimized.

@jreback

jreback Jan 29, 2018

Contributor

see my comment above. is there a reason you are not testing TDI? (generally whenever you are testing array-like)? The array-like numerics should be with the TDI tests (if they are not in this module).

This comment has been minimized.

@jbrockmendel

jbrockmendel Jan 29, 2018

Member

This ties in to your comments about duplication. In general when we have A.op(B) and B.rop(A) we can either put this test in test_A, test_B, or both. My preference is to order these the same way as NotImplemented behavior goes. Timedelta doesn't know anything about TDI, so test_timedelta doesn't either. (By analogy, Series doesn't know anything about DataFrame...)

@jreback

This comment has been minimized.

Contributor

jreback commented Feb 6, 2018

can you rebase, haven't looked in a while at this.

@jbrockmendel jbrockmendel changed the title from implement mod, divmod, rmod, rdivmod, fix and test scalar methods to implement Timedelta mod, divmod, rmod, rdivmod, fix and test scalar methods Feb 8, 2018

Timedelta mod method
^^^^^^^^^^^^^^^^^^^^
mod (%) and divmod operations are now defined on Timedelta objects when operating with either timedelta-like or with numeric arguments. (:issue:`19365`)

This comment has been minimized.

@jreback

jreback Feb 9, 2018

Contributor

use double backticks. on mod, divmod, TImedelta.

result = td * 1.5
assert result == Timedelta(minutes=4, seconds=30)
result = td * np.array([3, 4], dtype='int64')

This comment has been minimized.

@jreback

jreback Feb 9, 2018

Contributor

is it easy to just return a TDI? this is very unituitive

@pytest.mark.parametrize('op', [lambda x, y: x + y,
lambda x, y: y + x])
def test_add_timedeltalike(self, op):

This comment has been minimized.

@jreback

jreback Feb 9, 2018

Contributor

tests_ops_offset. you can prob just remove that. but pls look for existing tests and modify / remove them.

@jbrockmendel

This comment has been minimized.

Member

jbrockmendel commented Feb 9, 2018

is it easy to just return a TDI? this is very unituitive

That may become viable if/when TimedeltaArray becomes a thing. For now 2D cases alone are a deal-breaker.

@jreback

This comment has been minimized.

Contributor

jreback commented Feb 10, 2018

That may become viable if/when TimedeltaArray becomes a thing. For now 2D cases alone are a deal-breaker.

2D should be banned. return a TDI.

@jbrockmendel

This comment has been minimized.

Member

jbrockmendel commented Feb 10, 2018

2D should be banned. return a TDI.

Let's call this out-of-scope. I'm not comfortable with this. Consistency would require returning Index object wherever we currently return arrays. Plus it isn't obvious that an immutable object should be returned.

@jreback

This comment has been minimized.

Contributor

jreback commented Feb 10, 2018

then make all of these NotImplemented for now (timedelta and integer ndarray). This expands the scope too much here.

@jbrockmendel

This comment has been minimized.

Member

jbrockmendel commented Feb 10, 2018

then make all of these NotImplemented for now (timedelta and integer ndarray). This expands the scope too much here.

This PR became necessary because I was trying to fix Index/Series ops wth timedelta-like. Making these ops work is preliminary to make those work.

@jreback

This comment has been minimized.

Contributor

jreback commented Feb 10, 2018

and ndarray integers arrays are bogus here. so make those raise.

@jbrockmendel

This comment has been minimized.

Member

jbrockmendel commented Feb 10, 2018

For which ops are int-dtyped ndarrays bogus?

@jreback

This comment has been minimized.

Contributor

jreback commented Feb 10, 2018

>1 ndim

@jbrockmendel

This comment has been minimized.

Member

jbrockmendel commented Feb 10, 2018

So you want Timedelta(minutes=1) * ndarray([1, 2], dtype=np.int64) to raise? That seems really counterintuitive.

@jreback

This comment has been minimized.

Contributor

jreback commented Feb 10, 2018

greater than 1 d

that should return a TDI

@jbrockmendel

This comment has been minimized.

Member

jbrockmendel commented Feb 10, 2018

@jorisvandenbossche can you weigh in on this? I'm really uncomfortable with

a) returning TDI in timedelta64[ns] cases but ndarray in datetime/int/float cases
b) returning an immutable object (TDI) when the user didn't ask for one
c) having things that "just work" for np.timedelta64 start raising when wrapped with Timedelta (operations with ndim>1 ndarrays)
d) dropping a wedge between td.__op__(frame) vs td.__op__(frame.values)

@jreback

This comment has been minimized.

Contributor

jreback commented Feb 10, 2018

@jbrockmendel you summary is not correct. an operation with a TDI and a 1D array should return a TDI. Not sure where your others points are coming from.

@jbrockmendel

This comment has been minimized.

Member

jbrockmendel commented Feb 11, 2018

Can you open this discussion in a separate issue? This fixes specific bugs and is a blocker for fixing other specific bugs, both of which I'd rather focus on.

Not sure where your others points are coming from.

You told me to make ops with 2D ndarray raise. Of course that would cause things to break that didn't in the past.

I'll give you half a point for d) as that was incorrect. The requested change would not introduce a wedge between Timedelta.__op__(DataFrame) vs Timedelta.__op__(DataFrame.values). It would cause both to raise.

@jorisvandenbossche

This comment has been minimized.

Member

jorisvandenbossche commented Feb 11, 2018

@jbrockmendel you summary is not correct. an operation with a TDI and a 1D array should return a TDI.

@jreback not sure if it was a typo or not, but I think the discussion is about Timedelta * ndarray, not TimedeltaIndex * ndarray (I agree that last case should return TimedeltaIndex, but that is already the case right now)

We consistently return ndarray of datetime64 / timedelta64 on operations with Timedelta scalars and ndarrays. So I agree that if we want to change this, we should do it for all cases, so let's keep that for a separate issue, and keep this PR focused on the original issue?

@@ -492,7 +492,14 @@ def _binary_op_method_timedeltalike(op, name):
if other.dtype.kind not in ['m', 'M']:
# raise rathering than letting numpy return wrong answer
return NotImplemented
return op(self.to_timedelta64(), other)
result = op(self.to_timedelta64(), other)
if other.ndim == 0:

This comment has been minimized.

@jorisvandenbossche

jorisvandenbossche Feb 11, 2018

Member

Wouldn't be cleaner to do elif hasattr(other, 'dtype') and other.ndim > 0, and then let the scalar numpy ones take the route of Timedelta below ? (the other = Timedelta(other) some lines below will work for a np.timedelta64

This comment has been minimized.

@jbrockmendel

jbrockmendel Feb 11, 2018

Member

I think that would be about a wash because we also need to handle the case where other is a 0-dim datetime64 array.

This comment has been minimized.

@jorisvandenbossche

jorisvandenbossche Feb 11, 2018

Member

Isn't that already catched by the is_datetime64_object(other) above? (not sure what the method exactly does, but from the name I would expect that)

This comment has been minimized.

@jorisvandenbossche

jorisvandenbossche Feb 11, 2018

Member

And from this it seems it is indeed already working correctly:

In [163]: pd.Timedelta(1) + np.datetime64(1000, 'ns')
Out[163]: Timestamp('1970-01-01 00:00:00.000001001')

This comment has been minimized.

@jbrockmendel

jbrockmendel Feb 11, 2018

Member

Yah there's some ambiguity there. The 0-dim case to watch out for is (under master, fixed in the PR):

>>> pd.Timedelta(1) + np.array('2016-01-02 03:04:05', dtype='datetime64[ns]')
numpy.datetime64('2016-01-02T03:04:05.000000001')

This comment has been minimized.

@jbrockmendel

jbrockmendel Feb 11, 2018

Member

Ah, but not sure that should be fixed?

I could see a reasonable argument that the zero-dim array op should return a zero-dim array (i.e. not a Timedelta like the PR changes it to), but given that it returns a scalar, I think the consistent thing to do is always return a Timestamp/Timedelta.

not sure we should add that much extra lines to just deal with numpy scalars vs numpy 0-dim arrays

As long as we a) aren't making spaghetti and b) are testing these new corner cases, I don't see much downside to fixing these. If there were some kind of LOC budget I'd agree with you that this would be a pretty low priority. (and if it will get this merged I'll revert this part, since this is blocking fixes to Index bugs)

This comment has been minimized.

@jorisvandenbossche

jorisvandenbossche Feb 11, 2018

Member

. (and if it will get this merged I'll revert this part, since this is blocking fixes to Index bugs)

Can you explain why this part would be reverted again, but is needed to be first merged?

This comment has been minimized.

@jbrockmendel

jbrockmendel Feb 11, 2018

Member

Poor use of pronouns on my part. "This part" refers to the zero-dimensional arrays, and is not the bug that originally motivated this PR, but was found in the process of writing tests for this PR. "this is blocking" referred to the divmod/mod and 1-d array ops, which I need to have here before I can fix e.g. pd.Index([0, 1, 2]) * pd.Timedelta(days=1)

This comment has been minimized.

@jorisvandenbossche

jorisvandenbossche Feb 12, 2018

Member

I don't see any test you added that would catch this 0-dim array case. Would it be ok to just leave that out of this PR, and only fix the numpy timedelta64 scalar case?

We can discuss how many lines of code are added and whether that is worth it, but checking the result type and the potentially converting the result, does add to the code complexity. I would rather add a is_timedelta64_object(other) check to explicitly use the normal timedelta path for those.

This comment has been minimized.

@jbrockmendel

jbrockmendel Feb 12, 2018

Member

I'm amenable to this suggestion, will update in a bit.

FYI is_timedelta64_object(obj) is equivalent to isinstance(obj, np.timedelta64). It will not catch arrays with timedelta64-dtype.

@jbrockmendel

This comment has been minimized.

Member

jbrockmendel commented Feb 12, 2018

OK, just implemented ~ the suggested changes. I'd like to do another pass after this to flesh out the tests for Timedelta arithmetic ops. If this doesn't pass muster as is, I think the next step is to strip out everything except the mod/divmod and push the other arithmetic edits+tests to a separate PR.

@jorisvandenbossche Thanks for chiming in. After looking at it I agree the handling without the ndim==0 checks is prettier. You get two internet points.

@@ -1044,6 +1047,11 @@ class Timedelta(_Timedelta):
__rsub__ = _binary_op_method_timedeltalike(lambda x, y: y - x, '__rsub__')
def __mul__(self, other):
if is_integer_object(other) or is_float_object(other):
# includes numpy scalars that would otherwise be caught by dtype
# check below

This comment has been minimized.

@jorisvandenbossche

jorisvandenbossche Feb 12, 2018

Member

another option to do this would be if hasattr(other, 'dtype') and not (is_integer_object(other) or is_float_object(other)):
that way you don't need to repeat the Timedelta(other * self.value, unit='ns') twice

This comment has been minimized.

@jbrockmendel

jbrockmendel Feb 13, 2018

Member

Good idea. I like having if/elif branches that are explicitly mutually explicit and so don't rely on the ordering.

return op(self, Timestamp(other))
# We are implicitly requiring the canonical behavior to be
# defined by Timestamp methods.
elif is_timedelta64_object(other):
return op(self, Timedelta(other))

This comment has been minimized.

@jorisvandenbossche

jorisvandenbossche Feb 13, 2018

Member

you can apply the same idea here? (add the and not is_timedelta64_object(other) on the line below)

This comment has been minimized.

@jbrockmendel

jbrockmendel Feb 13, 2018

Member

How strong is your opinion on this one? I have a slight preference for the explicitness of this ordering over the sometimes-vagueness of a catch-all.

This comment has been minimized.

@jorisvandenbossche

jorisvandenbossche Feb 15, 2018

Member

Not that strong, but the thing is that this will then again go through this method, instead of being run once.
If you want to deal with it explicitly, you can also do something like

elif is_timedelta64_object(other):
    pass  # other coerced to Timedelta at the bottom
@@ -1060,7 +1065,10 @@ class Timedelta(_Timedelta):
__rmul__ = __mul__
def __truediv__(self, other):
if hasattr(other, 'dtype'):
if is_timedelta64_object(other):
return self / Timedelta(other)

This comment has been minimized.

@jorisvandenbossche

jorisvandenbossche Feb 13, 2018

Member

same comment here (and some below as well)

jbrockmendel added some commits Feb 13, 2018

@jbrockmendel jbrockmendel referenced this pull request Feb 16, 2018

Merged

Parametrize PeriodIndex tests #19659

0 of 4 tasks complete
divmod(datetime.timedelta(hours=2), pd.Timedelta(minutes=11))
# divmod against a numeric returns a pair (Timedelta, Timedelta)
pd.Timedelta(hours=25) % 86400000000000

This comment has been minimized.

@jreback

jreback Feb 16, 2018

Contributor

missing the divmod here

td = pd.Timedelta(hours=37)
td
Current Behavior

This comment has been minimized.

@jreback

jreback Feb 16, 2018

Contributor

you don't need the Current Behavior here (as there isn't any previous)

Timedelta mod method
^^^^^^^^^^^^^^^^^^^^
``mod`` (%) and ``divmod`` operations are now defined on ``Timedelta`` objects when operating with either timedelta-like or with numeric arguments. (:issue:`19365`)

This comment has been minimized.

@jreback

jreback Feb 16, 2018

Contributor

you can put in a reference to the docs in the timedelta section.

This comment has been minimized.

@jbrockmendel

jbrockmendel Feb 17, 2018

Member

I'm unclear on what this means.

@@ -283,6 +283,18 @@ Rounded division (floor-division) of a ``timedelta64[ns]`` Series by a scalar
td // pd.Timedelta(days=3, hours=4)
pd.Timedelta(days=3, hours=4) // td

This comment has been minimized.

@jreback

jreback Feb 16, 2018

Contributor

add a ref tag here

This comment has been minimized.

@jbrockmendel

jbrockmendel Feb 16, 2018

Member

You mean a "(:issue:19365)"?

This comment has been minimized.

@jreback

jreback Feb 17, 2018

Contributor

no a section reference

This comment has been minimized.

@jbrockmendel

jbrockmendel Feb 17, 2018

Member

Do you mean something like ".. _timedeltas.divmod:" after line 297?

return Timedelta(self.value // other)
else:
return self.to_timedelta64() // other
return self.to_timedelta64() // other

This comment has been minimized.

@jreback

jreback Feb 16, 2018

Contributor

does this not need wrapping in Timedelta?

This comment has been minimized.

@jbrockmendel

jbrockmendel Feb 16, 2018

Member

@jorisvandenbossche convinced me that special-casing zero-dim np.ndarrays didn't make much sense.

This comment has been minimized.

@jreback

jreback Feb 17, 2018

Contributor

that’s not the point
it’s returning a np.timedelta64 and not a Timedelta

This comment has been minimized.

@jbrockmendel

jbrockmendel Feb 17, 2018

Member

OK, will revert. @jorisvandenbossche if you want to revisit this particular edit, mention it in #17652.

result = td * 1.5
assert result == Timedelta(minutes=4, seconds=30)
result = td * np.array([3, 4], dtype='int64')

This comment has been minimized.

@jreback

jreback Feb 16, 2018

Contributor

so you haven't answered my question here. this needs to return a TDI or raise TypeError. return a m8 array is simply not useful.

td = Timedelta(hours=37)
# Array-like others
result = td % np.array([6, 5], dtype='timedelta64[h]')

This comment has been minimized.

@jreback

jreback Feb 16, 2018

Contributor

same comment

expected = pd.TimedeltaIndex(['1H', '2H'])
tm.assert_index_equal(result, expected)
result = td % np.array([2, int(1e12)], dtype='i8')

This comment has been minimized.

@jreback

jreback Feb 16, 2018

Contributor

here as well

assert result == Timedelta(minutes=2)
result = np.array([5, 6], dtype='m8[m]') % td
expected = np.array([2, 0], dtype='m8[m]').astype('m8[ns]')

This comment has been minimized.

@jreback

jreback Feb 16, 2018

Contributor

same here

@jbrockmendel

This comment has been minimized.

Member

jbrockmendel commented Feb 16, 2018

so you haven't answered my question here. this needs to return a TDI or raise TypeError. return a m8 array is simply not useful.

I did answer by disagreeing and asking @jorisvandenbossche to weigh in. Why would an m8 array not be useful? The user clearly thinks its useful enough to pass as an input.

jbrockmendel added some commits Feb 17, 2018

@jbrockmendel jbrockmendel referenced this pull request Feb 19, 2018

Merged

ENH: implement Timedelta.__mod__ and __divmod__ #19755

3 of 4 tasks complete
@jbrockmendel

This comment has been minimized.

Member

jbrockmendel commented Feb 19, 2018

Closing in favor of #19755, #19752, plus followups.

@jorisvandenbossche jorisvandenbossche added this to the No action milestone Feb 19, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment