将月份添加到 pandas 中的日期时间列
Add months to a datetime column in pandas
我有一个包含 2 列的数据框 df,如下所示 -
START_DATE MONTHS
0 2015-03-21 240
1 2015-03-21 240
2 2015-03-21 240
3 2015-03-21 240
4 2015-03-21 240
5 2015-01-01 120
6 2017-01-01 240
7 NaN NaN
8 NaN NaN
9 NaN NaN
2列的数据类型都是对象。
>>> df.dtypes
START_DATE object
MONTHS object
dtype: object
现在,我想通过添加 df['START_DATE'] 和 df['MONTHS'] 创建一个新列 "Result"。所以,我做了以下 -
from dateutil.relativedelta import relativedelta
df['START_DATE'] = pd.to_datetime(df['START_DATE'])
df['MONTHS'] = df['MONTHS'].astype(float)
df['offset'] = df['MONTHS'].apply(lambda x: relativedelta(months=x))
df['Result'] = df['START_DATE'] + df['offset']
在这里,我得到以下错误 -
TypeError: incompatible type [object] for a datetime/timedelta operation
注意:想要将 df['Months'] 转换为 int 但无法工作,因为该字段有空值。
能给我一些吗directions.Thanks.
如果您的数据框很小,请使用以下内容。我使用了 axis=1
,这是按行操作。如果你的数据帧很大,它会很慢
> df['offset'] = df.dropna().apply(lambda v: relativedelta(months=int(v['MONTHS'])) + v['START_DATE'], axis=1)
> df
START_DATE MONTHS offset
0 2015-03-21 240 2035-03-21
1 2015-03-21 240 2035-03-21
2 2015-03-21 240 2035-03-21
3 2015-03-21 240 2035-03-21
4 2015-03-21 240 2035-03-21
5 2015-01-01 120 2025-01-01
6 2017-01-01 240 2037-01-01
7 NaT NaN NaT
8 NaT NaN NaT
9 NaT NaN NaT
这里有一种不用 dateutil.relativedelta
的方法。请注意,我将 MONTHS
转换为整数(并且仅在删除空值之后,因为 int
不接受空值)因为我想每年除以 12 个月,利用以下事实商是年的增量,modulo/remainder 是月的增量。
import pandas as pd
df = pd.DataFrame({'START_DATE':['2015-03-21','2015-03-21','2015-03-21','2015-03-21',
'2015-03-21','2015-01-01','2017-01-01', None,None,None],
'MONTHS':[240,240,240,240,240,120,240,None,None,None]},
dtype='object') # replicate example data
df.dropna(inplace=True) # drop nulls so can convert MONTHS to int
df['START_DATE'] = pd.to_datetime(df['START_DATE'])
df['MONTHS'] = df.MONTHS.astype(int)
df.apply(lambda x: pd.datetime(x.START_DATE.year + x.MONTHS / 12,
x.START_DATE.month + x.MONTHS % 12,
x.START_DATE.day), axis=1)
这是执行此操作的矢量化方法,因此应该非常高效。请注意,它不处理月份交叉/结束(并且不能很好地处理 DST 更改。我相信这就是你得到时间的原因)。
In [32]: df['START_DATE'] + df['MONTHS'].values.astype("timedelta64[M]")
Out[32]:
0 2035-03-20 20:24:00
1 2035-03-20 20:24:00
2 2035-03-20 20:24:00
3 2035-03-20 20:24:00
4 2035-03-20 20:24:00
5 2024-12-31 10:12:00
6 2036-12-31 20:24:00
7 NaT
8 NaT
9 NaT
Name: START_DATE, dtype: datetime64[ns]
如果您需要精确的 MonthEnd/Begin 处理,这是一种合适的方法。 (使用MonthsOffset得到同一天)
In [33]: df.dropna().apply(lambda x: x['START_DATE'] + pd.offsets.MonthEnd(x['MONTHS']), axis=1)
Out[33]:
0 2035-02-28
1 2035-02-28
2 2035-02-28
3 2035-02-28
4 2035-02-28
5 2024-12-31
6 2036-12-31
dtype: datetime64[ns]
这是另一个矢量化 numpy 解决方案:
In [111]: mask = (df.START_DATE.notnull() & df.MONTHS.notnull())
In [112]: df.loc[mask, 'Result'] = (
...: df.START_DATE.loc[mask].values.astype('M8[M]') + \
...: (df.MONTHS.loc[mask].values.astype(int) * np.timedelta64(1, 'M'))
...: ).astype('M8[D]') - np.timedelta64(1, 'D')
...:
In [113]: df
Out[113]:
START_DATE MONTHS Result
0 2015-03-21 240.0 2035-02-28
1 2015-03-21 240.0 2035-02-28
2 2015-03-21 240.0 2035-02-28
3 2015-03-21 240.0 2035-02-28
4 2015-03-21 240.0 2035-02-28
5 2015-01-01 120.0 2024-12-31
6 2017-01-01 240.0 2036-12-31
7 NaT NaN NaT
8 NaT NaN NaT
9 NaT NaN NaT
作为对 Jeff 的回应,我认为这在不是 12 的倍数的月份不能正常工作。就像我的初始日期是 '2020-05-04 (yyyy-mm-dd) 和月份一样57. 但是添加了 2025-02-01(而不是 2025-02-04)。
init_workbook['CALC_DATE']= init_workbook['STRTDATE']+init_workbook['MONTHS'].values.astype("timedelta64[M]")
>>> init_workbook.head(4)
MONTHS STRTDATE CALC_DATE
0 12 2020-05-04 2021-05-04
1 12 2020-05-04 2021-05-04
2 57 2020-05-04 2025-02-01
3 34 2020-05-20 2023-03-20
现在,如果日期大于 12,那么它会给出正确的结果,但如果日期 <12,那就是它失败的地方
我有一个包含 2 列的数据框 df,如下所示 -
START_DATE MONTHS
0 2015-03-21 240
1 2015-03-21 240
2 2015-03-21 240
3 2015-03-21 240
4 2015-03-21 240
5 2015-01-01 120
6 2017-01-01 240
7 NaN NaN
8 NaN NaN
9 NaN NaN
2列的数据类型都是对象。
>>> df.dtypes
START_DATE object
MONTHS object
dtype: object
现在,我想通过添加 df['START_DATE'] 和 df['MONTHS'] 创建一个新列 "Result"。所以,我做了以下 -
from dateutil.relativedelta import relativedelta
df['START_DATE'] = pd.to_datetime(df['START_DATE'])
df['MONTHS'] = df['MONTHS'].astype(float)
df['offset'] = df['MONTHS'].apply(lambda x: relativedelta(months=x))
df['Result'] = df['START_DATE'] + df['offset']
在这里,我得到以下错误 -
TypeError: incompatible type [object] for a datetime/timedelta operation
注意:想要将 df['Months'] 转换为 int 但无法工作,因为该字段有空值。
能给我一些吗directions.Thanks.
如果您的数据框很小,请使用以下内容。我使用了 axis=1
,这是按行操作。如果你的数据帧很大,它会很慢
> df['offset'] = df.dropna().apply(lambda v: relativedelta(months=int(v['MONTHS'])) + v['START_DATE'], axis=1)
> df
START_DATE MONTHS offset
0 2015-03-21 240 2035-03-21
1 2015-03-21 240 2035-03-21
2 2015-03-21 240 2035-03-21
3 2015-03-21 240 2035-03-21
4 2015-03-21 240 2035-03-21
5 2015-01-01 120 2025-01-01
6 2017-01-01 240 2037-01-01
7 NaT NaN NaT
8 NaT NaN NaT
9 NaT NaN NaT
这里有一种不用 dateutil.relativedelta
的方法。请注意,我将 MONTHS
转换为整数(并且仅在删除空值之后,因为 int
不接受空值)因为我想每年除以 12 个月,利用以下事实商是年的增量,modulo/remainder 是月的增量。
import pandas as pd
df = pd.DataFrame({'START_DATE':['2015-03-21','2015-03-21','2015-03-21','2015-03-21',
'2015-03-21','2015-01-01','2017-01-01', None,None,None],
'MONTHS':[240,240,240,240,240,120,240,None,None,None]},
dtype='object') # replicate example data
df.dropna(inplace=True) # drop nulls so can convert MONTHS to int
df['START_DATE'] = pd.to_datetime(df['START_DATE'])
df['MONTHS'] = df.MONTHS.astype(int)
df.apply(lambda x: pd.datetime(x.START_DATE.year + x.MONTHS / 12,
x.START_DATE.month + x.MONTHS % 12,
x.START_DATE.day), axis=1)
这是执行此操作的矢量化方法,因此应该非常高效。请注意,它不处理月份交叉/结束(并且不能很好地处理 DST 更改。我相信这就是你得到时间的原因)。
In [32]: df['START_DATE'] + df['MONTHS'].values.astype("timedelta64[M]")
Out[32]:
0 2035-03-20 20:24:00
1 2035-03-20 20:24:00
2 2035-03-20 20:24:00
3 2035-03-20 20:24:00
4 2035-03-20 20:24:00
5 2024-12-31 10:12:00
6 2036-12-31 20:24:00
7 NaT
8 NaT
9 NaT
Name: START_DATE, dtype: datetime64[ns]
如果您需要精确的 MonthEnd/Begin 处理,这是一种合适的方法。 (使用MonthsOffset得到同一天)
In [33]: df.dropna().apply(lambda x: x['START_DATE'] + pd.offsets.MonthEnd(x['MONTHS']), axis=1)
Out[33]:
0 2035-02-28
1 2035-02-28
2 2035-02-28
3 2035-02-28
4 2035-02-28
5 2024-12-31
6 2036-12-31
dtype: datetime64[ns]
这是另一个矢量化 numpy 解决方案:
In [111]: mask = (df.START_DATE.notnull() & df.MONTHS.notnull())
In [112]: df.loc[mask, 'Result'] = (
...: df.START_DATE.loc[mask].values.astype('M8[M]') + \
...: (df.MONTHS.loc[mask].values.astype(int) * np.timedelta64(1, 'M'))
...: ).astype('M8[D]') - np.timedelta64(1, 'D')
...:
In [113]: df
Out[113]:
START_DATE MONTHS Result
0 2015-03-21 240.0 2035-02-28
1 2015-03-21 240.0 2035-02-28
2 2015-03-21 240.0 2035-02-28
3 2015-03-21 240.0 2035-02-28
4 2015-03-21 240.0 2035-02-28
5 2015-01-01 120.0 2024-12-31
6 2017-01-01 240.0 2036-12-31
7 NaT NaN NaT
8 NaT NaN NaT
9 NaT NaN NaT
作为对 Jeff 的回应,我认为这在不是 12 的倍数的月份不能正常工作。就像我的初始日期是 '2020-05-04 (yyyy-mm-dd) 和月份一样57. 但是添加了 2025-02-01(而不是 2025-02-04)。
init_workbook['CALC_DATE']= init_workbook['STRTDATE']+init_workbook['MONTHS'].values.astype("timedelta64[M]")
>>> init_workbook.head(4)
MONTHS STRTDATE CALC_DATE
0 12 2020-05-04 2021-05-04
1 12 2020-05-04 2021-05-04
2 57 2020-05-04 2025-02-01
3 34 2020-05-20 2023-03-20
现在,如果日期大于 12,那么它会给出正确的结果,但如果日期 <12,那就是它失败的地方