【问题标题】:How to floor a date to the first date of that month?如何将日期设置为该月的第一个日期?
【发布时间】:2017-07-06 05:22:51
【问题描述】:

我有一个索引列 = date 的 pandas DataFrame。

输入:

            value
date    
1986-01-31  22.93
1986-02-28  15.46

我想把日期推迟到那个月的第一天

输出:

            value
date    
1986-01-01  22.93
1986-02-01  15.46

我尝试了什么:

df.index.floor('M')
ValueError: <MonthEnd> is a non-fixed frequency

这可能是因为 df 是由 df = df.resample("M").sum()(这段代码的输出就是问题开头的输入)

我也试过df = df.resample("M", convention='start').sum()。但是,它不起作用。

我知道在 R 中,很容易调用 floor(date, 'M')

【问题讨论】:

  • 性能有问题吗?为此,我会考虑将这些值转换为 datetime 对象,但如果您尝试处理数百万个对象,这可能会很昂贵。

标签: python date pandas numpy


【解决方案1】:

您可以使用时间序列偏移 MonthBegin

from pandas.tseries.offsets import MonthBegin
df['date'] = pd.to_datetime(df['date']) - MonthBegin(1)

编辑: 上述解决方案不处理已经确定到月初的日期。这是一个替代解决方案。

这是一个带有额外测试用例的数据框:

            value
date    
1986-01-31  22.93
1986-02-28  15.46
2018-01-01  20.00
2018-02-02  25.00

使用timedelta方法,

df.index = pd.to_datetime(df.index)
df.index = df.index - pd.to_timedelta(df.index.day - 1, unit='d')


            value
date    
1986-01-01  22.93
1986-02-01  15.46
2018-01-01  20.00
2018-02-01  25.00

【讨论】:

  • 这是所有答案中唯一的 pandonic 方法(作为奖励,这是矢量化的)
  • 这种方法有一个错误:它将任何日期转换为下个月的月初,除了月初,它保持不变。即 1-1-2018 -> 1-1-2018,但 2-1-2018 -> 1-2-2018...
  • timedelta 方法是正确的,非常适合我的用例,只需进行一项修改 - 添加“dt”,因此在 to_timedelta() 中更改为“df.index.dt.day” .
【解决方案2】:
dt_1 = "2016-02-01"
def first_day(dt):
    lt_split = dt.split("-")
    return "-".join([lt_split[0], lt_split[1], "01"])

print first_day(dt_1)

对于 Panda 的 DataFrame,可以使用dt["col_name_date"].apply(first_day)

【讨论】:

    【解决方案3】:

    这样就可以解决问题,并且不需要导入。 Numpy 有一个 dtype datetime64,默认情况下 pandas 设置为 [ns],通过检查 dtype 可以看出。您可以将其更改为月份,这将通过访问 numpy 数组并更改类型从每月的第一天开始。

    df.date = pd.to_datetime(df.date.values.astype('datetime64[M]'))
    

    如果 pandas 能用他们自己的 astype() 方法来实现它会很好,但不幸的是你不能。

    以上适用于日期时间值或字符串的数据,如果您已经将数据设置为 datetime[ns] 类型,则可以省略 pd.to_datetime() 并执行以下操作:

    df.date = df.date.values.astype('datetime64[M]')
    

    【讨论】:

    • 我不知道他们在哪个版本中将它导入到 pandas astype,但目前 df.date.astype('datetime64[M]') 有效(至少 1.2.2 版)。你可以更新这个答案。
    【解决方案4】:

    这是另一种“流行”的做法:

    df.date - pd.Timedelta('1 day') * (df.date.dt.day - 1)
    

    【讨论】:

    • 这很好!另外,它适用于 dask! (与 Deo Leung 的回答相反)
    • 除了向量化之外,这也适用于时间戳的可变实例。只需使用您的时间戳更改 df.date 即可,效果很好!
    【解决方案5】:

    有一个pandas issue关于地板问题

    建议的方式是

    import pandas as pd
    pd.to_datetime(df.date).dt.to_period('M').dt.to_timestamp()
    

    【讨论】:

    • df.date.dt.to_period('M').dt.to_timestamp() 似乎足够了,不需要初始的pd.to_datetime
    【解决方案6】:

    您也可以使用字符串日期时间格式:

    df['month'] = df['date'].dt.strftime('%Y-%m-01')

    【讨论】:

      【解决方案7】:

      从 2019 年 8 月开始:

      这应该可行:

      [x.replace(day=1).date() for x in df['date']]
      

      唯一的要求是确保date 是一个日期时间,我们可以通过调用pd.to_datetime(df['date']) 来保证这一点

      【讨论】:

        【解决方案8】:

        喜欢 Mikhail Venkov 的回答。添加了下面的代码以将列添加为时间戳值并保留时区信息

            df['month'] = pd.to_datetime(df['timestamp'].dt.strftime('%Y-%m-01')).dt.tz_localize(timezone) 
        

        where timezone = 'America/Los_Angeles' 或您想要的任何区域

        【讨论】:

          【解决方案9】:

          假设您正在处理以下数据框:

          import pandas as pd
          
          df = pd.DataFrame({'MyDate': ['2021-03-11', '2021-04-26', '2021-01-17']})
          df['MyDate'] = pd.to_datetime(df.MyDate)
          

          这是:

              MyDate
          0   2021-03-11
          1   2021-04-26
          2   2021-01-17
          

          并且您想将日期截断为月份:

          df['Truncated'] = df['MyDate'] + pd.offsets.MonthBegin(-1)
          # OR    
          # df['Truncated'] = df['MyDate'] - pd.offsets.MonthBegin(1)
          df
          

          你会得到:

                MyDate  Truncated
          0 2021-03-11 2021-03-01
          1 2021-04-26 2021-04-01
          2 2021-01-17 2021-01-01
          

          重要提示:当日期已经确定为当月的第一天时,此方法不起作用,因此我们还将提供其他解决方案。

          import pandas as pd
          
          df = pd.DataFrame({'MyDate': ['2021-03-11', '2021-04-26', '2021-01-17', '2021-02-01']})
          df['MyDate'] = pd.to_datetime(df.MyDate)
          
          df['Truncated'] = df['MyDate'].dt.to_period('M').dt.to_timestamp()
          print(df)
          

          你会得到:

           MyDate  Truncated
          0 2021-03-11 2021-03-01
          1 2021-04-26 2021-04-01
          2 2021-01-17 2021-01-01
          

          最后,另一种方法可能如下:

          df['Truncated'] = df['MyDate'].dt.strftime('%Y-%m-01')
          print(df)
          

          你会得到:

              MyDate   Truncated
          0 2021-03-11  2021-03-01
          1 2021-04-26  2021-04-01
          2 2021-01-17  2021-01-01
          

          【讨论】:

          • 如果日期是当月的第一天,您的代码将不起作用 - 它会变成上个月的第一天
          猜你喜欢
          • 1970-01-01
          • 2015-09-09
          • 2014-02-11
          • 1970-01-01
          • 1970-01-01
          • 2021-12-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多