问题
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