【问题标题】:Add months to a datetime column in pandas将月份添加到熊猫中的日期时间列
【发布时间】:2015-07-31 10:43:06
【问题描述】:

我有一个包含 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'] 创建一个新列“结果”。所以,我做了以下 -

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,但由于该字段包含 Null,因此无法工作。

你能给我一些指示吗?谢谢。

【问题讨论】:

  • 您遇到了哪个错误?请发布错误报告。
  • @Borja - 嗨。在我上面的编辑中添加了错误。谢谢。

标签: python python-2.7 python-3.x pandas ipython


【解决方案1】:

回应 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,那么它会给出正确的结果,但如果日期

【讨论】:

    【解决方案2】:

    这是另一个 矢量化 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
    

    【讨论】:

      【解决方案3】:

      这是执行此操作的矢量化方式,因此应该非常高效。请注意,它不处理月份交叉/结束(并且不能很好地处理 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]
      

      【讨论】:

        【解决方案4】:

        这是一种不用dateutil.relativedelta 的方法。请注意,我将 MONTHS 转换为整数(并且仅在删除空值后,因为 int 不接受空值)因为我想每年除以 12 个月,利用商是增量以年为单位,模数/余数是以月为单位的增量。

        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)
        

        【讨论】:

          【解决方案5】:

          如果您的数据框很小,请使用以下内容。我使用了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
          

          【讨论】:

            猜你喜欢
            • 2020-12-01
            • 2017-05-22
            • 1970-01-01
            • 2020-11-28
            • 1970-01-01
            • 2021-09-22
            • 2021-12-06
            • 2022-01-07
            • 1970-01-01
            相关资源
            最近更新 更多