Adding one month to datetime64 with timedelta

自古美人都是妖i 提交于 2020-05-14 09:04:23

问题


When I try this:

>>> a = numpy.datetime64('1995-12-31')
>>> b = a + pandas.Timedelta(1, unit='M')
>>> print(b)

I expect to see

1996-01-31

but instead I get

1996-01-30 10:29:06.

Any idea why? Many thanks.


回答1:


A time delta of one month is the length of a year divided by 12.

You need to examine your date and add the appropriate quantity of days. Alternately, increment the month number (rolling over to the next year, if needed), and leave the day number unchanged.




回答2:


You can replace the day part to mimic the requirement.

import numpy as np
import pandas as pd
a = np.datetime64('1995-12-31')
b = a + pd.Timedelta(1, unit='M')
print(b.replace(day=pd.to_datetime(a).day))

Use .date() if you are only interested in date part




回答3:


There's an inherent ambiguity in adding a 'month' to a time, since months vary in length.

Make a date:

In [247]: a = np.array('1995-12-31','datetime64[D]')                            
In [248]: a                                                                     
Out[248]: array('1995-12-31', dtype='datetime64[D]')

adding days to that works fine:

In [249]: a + np.array(31, 'timedelta64[D]')                                    
Out[249]: numpy.datetime64('1996-01-31')

Adding a month raises an error:

In [250]: a + np.array(1, 'timedelta64[M]')                                     
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-250-a331f724d7e7> in <module>
----> 1 a + np.array(1, 'timedelta64[M]')

TypeError: Cannot get a common metadata divisor for NumPy datetime metadata [D] and [M] because they have incompatible nonlinear base time units

We could cast a as a month - then it works:

In [251]: a.astype('datetime64[M]')                                             
Out[251]: array('1995-12', dtype='datetime64[M]')
In [252]: a.astype('datetime64[M]') + np.array(1, 'timedelta64[M]')             
Out[252]: numpy.datetime64('1996-01')

Change the month in the corresponding datetime object may be cleanest way to work with this:

In [254]: b = a.item()                                                          
In [255]: b                                                                     
Out[255]: datetime.date(1995, 12, 31)

I haven't worked enough with datetime objects to make the change without looking at its docs.




回答4:


A concise way of adding a month leaving the day unchanged if possible would be truncating to months, adding 1 and then readding what was truncated:

>>> a = np.datetime64('1995-12-31')                                                              
>>> am = a.astype('M8[M]')                                                    
>>> b = (am + 1) + (a - am)                                                        
>>> b                                                                                            
numpy.datetime64('1996-01-31')                                                                   

Obviously, that does not work if the original day does not exist in the next month:

>>> a = np.datetime64('1995-01-31')
>>> am = a.astype('M8[M]')
>>> b = (am + 1) + (a - am)
>>> b
numpy.datetime64('1995-03-03')

But it is unclear what should be the answer in this case, anyway.

One possibility would be to max out at that month's last day:

>>> b = np.minimum((am + 1) + (a - am), (am + 2) - np.timedelta64(1, 'D'))
>>> b
numpy.datetime64('1995-02-28')



回答5:


As already mentioned @hpaulj in his answer:

There's an inherent ambiguity in adding a 'month' to a time, since months vary in length.

Moreover, as at version 0.25.0, Pandas dropped support for the use of the units M (months) and Y (years) in Timedelta functions.

But as stated in official pandas guide, you should use Timedelta for absolute time duration and DateOffset for relative time duration that respects calendar arithmetic, which is exactly what we need in your case:

The basic DateOffset acts similar to dateutil.relativedelta (relativedelta documentation) that shifts a date time by the corresponding calendar duration specified.

So, using your example:

In [7]: a = numpy.datetime64('1995-12-31')
      : b = pandas.Timestamp(a) + pandas.DateOffset(months=1)
      : b
Out[7]: Timestamp('1996-01-31 00:00:00')

NB: you could always use to_numpy method, if you need to convert pandas.Timestamp to numpy.datetime64.



来源:https://stackoverflow.com/questions/54794431/adding-one-month-to-datetime64-with-timedelta

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!