【问题标题】:How to manipulate pandas datetime objects to get the last date of previous month如何操作 pandas 日期时间对象以获取上个月的最后日期
【发布时间】:2020-07-16 13:52:36
【问题描述】:

我有一个 pandas 数据框,想将所有日期转换为上个月的最后一个日期。例如“2020-02-04”应该变成“2020-01-31”,“2020-03-03”应该变成“2020-02-28”等等。我的 df 看起来像这样(在月份列中,我已经有了适合我想要的日期的月份):

In[76]: dfall[["date", "month"]]
Out[76]: 
       date    month
0 2020-02-04      1
1 2020-03-03      2
2 2020-04-02      3
3 2020-05-05      4
4 2020-06-03      5
5 2020-07-02      6

现在我尝试了这个:

import calendar
import datetime
today = datetime.now()
dfall.date = str(today.year) + "-" + str(dfall.month) + "-" + str(calendar.monthrange(today.year,dfall.month)[1])

这个想法是通过将字符串添加在一起来构建新日期。但是这段代码引发了错误:

ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

我知道错误来自这部分:str(calendar.monthrange(today.year,dfall.month)[1])(没有这部分代码运行没有错误,但结果不是我想要的)。这可能是因为 python 不知道从dfall.month 中获取哪个月份。有人知道我该如何处理这个问题吗?

【问题讨论】:

  • 如果您考虑接受或投票以奖励帮助您的人,或者如果有问题您也可以发表评论,那会很好;)@thyhmoo
  • 是的,你是对的,我对每个答案都投了赞成票,但还没有接受。我知道了:)
  • 别担心!很高兴你找到了答案:) @thyhmoo

标签: python pandas datetime


【解决方案1】:

假设'date' 列是字符串类型(使用.astype(str)strftime 否则),您可以将年月部分转换为datetime 并减去一天的timedelta

dfall['lastdaylastmonth'] = pd.to_datetime(dfall['date'].str[:-3]) - pd.Timedelta(days=1)

# dfall['lastdaylastmonth']
# 0   2020-01-31
# 1   2020-02-29
# 2   2020-03-31
# 3   2020-04-30
# 4   2020-05-31
# 5   2020-06-30
# Name: lastdaylastmonth, dtype: datetime64[ns]

【讨论】:

    【解决方案2】:
    import datetime
    from datetime import timedelta
    
    df = pd.DataFrame({"date":['2020-02-04','2020-03-03','2020-04-02','2020-05-05','2020-06-03','2020-07-02'],
                      "month": [1,2,3,4,5,6]})
    
    # Conert to data
    def change_time_format(series):
        return datetime.datetime.strptime(series,"%Y-%m-%d")
    
    df.date = df.date.apply(change_time_format)
    
    dates = list(df.date)
    previous_m_last_date = []
    for d in dates:
        days = d.day
        u_date = d - timedelta(days)
        previous_m_last_date.append(u_date)
    
    df["updated_date"] = previous_m_last_date
    df
    

    【讨论】:

      【解决方案3】:

      另一种方法:

      import datetime
      
      for index, d in df.iterrows():
          temp = d["date"]
          dtObj = datetime.datetime.strptime(temp, "%Y-%m-%d")
          newDt = dtObj - datetime.timedelta(days=dtObj.day)
          df["date"][index] = datetime.datetime.strftime(newDt, "%Y-%m-%d")
      

      【讨论】:

        【解决方案4】:

        您也可以试试这个:

        dfall.date=dfall.date.apply(lambda x: x.replace(day=1)- pd.Timedelta(days=1))
        

        如果dfall.date 是字符串类型,请尝试以下操作:

        dfall.date=pd.to_datetime(dfall.date).apply(lambda x: x.replace(day=1)- pd.Timedelta(days=1))
        

        您可以尝试Kyle Barron 制作的另一个矢量化替代方案,以避免使用df.apply(lambda x: x.replace(day=1))speeds up to 8.5x the performance

        def vec_dt_replace(series, year=None, month=None, day=None):
            return pd.to_datetime(
                {'year': series.dt.year if year is None else year,
                 'month': series.dt.month if month is None else month,
                 'day': series.dt.day if day is None else day})
        #dfall.date=pd.to_datetime(dfall.date) #(if dfall.date is type string)
        dfall.date=vec_dt_replace(dfall.date,day=1)- pd.Timedelta(days=1)
        

        如果您想保留原来的解决方案,那么:

        • str(dfall.month) 更改为dfall.month.astype(str)
        • str(calendar.monthrange(today.year,dfall.month)[1]) 更改为dfall.month.apply(lambda x:calendar.monthrange(today.year,x)[1]).astype(str)
        • 一旦你有了字符串,你应该把它转换成日期时间:pd.to_datetime(dfall.date)
        import calendar
        import datetime
        today = datetime.datetime.now()
        dfall.date = str(today.year) + "-" + dfall.month.astype(str) + "-" + dfall.month.apply(lambda x:calendar.monthrange(today.year,x)[1]).astype(str)
        dfall.date = pd.to_datetime(dfall.date)
        print(dfall)
        

        所有解决方案的输出:

        dfall[["date", "month"]]
        
                date  month
        0 2020-01-31      1
        1 2020-02-29      2
        2 2020-03-31      3
        3 2020-04-30      4
        4 2020-05-31      5
        5 2020-06-30      6
        

        【讨论】:

          【解决方案5】:

          从日期时间导入日期时间

          日期列表

          日期 = [日期时间(2020, 2, 4), 日期时间(2020, 3, 3), 日期时间(2020, 4, 2), 日期时间(2020, 5, 5), 日期时间(2020, 6, 3), 日期时间(2020, 7, 2)]

          月份列的值

          月 = [1, 2, 3, 4, 5, 6]

          创建以日期为索引的系列

          ts = pd.Series(month, index=dates)

          将日期向后移动一个月并存储在 date_col 中

          date_col = ts.shift(-1, freq='M').index

          使用修改日期作为 DataFrame 中的列

          pd.DataFrame({'Dates': date_col, 'Month': month})

          【讨论】:

            【解决方案6】:

            替代方法:

            from datetime import datetime, timeldelta
            
            def convert_date(date_str):
                date = datetime.strptime(date_str, '%Y-%m-%d')
                return (date - timedelta(days=date.day)).strftime('%Y-%m-%d')
            
            dfall.date.apply(convert_date)
            

            【讨论】:

            • 感谢您的成功,但不幸的是,格式看起来像“2020-1-31”,pandas 无法将其转换为日期时间对象。为此,它需要是“2020-01-31”。无论如何,我从下面的答案中找到了解决方案。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2022-06-10
            • 2022-11-22
            • 2017-10-25
            • 1970-01-01
            • 1970-01-01
            • 2012-05-29
            • 2021-07-11
            相关资源
            最近更新 更多