如何将日期设置为该月的第一个日期?
Posted
技术标签:
【中文标题】如何将日期设置为该月的第一个日期?【英文标题】: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
对象,但如果您尝试处理数百万个对象,这可能会很昂贵。
【参考方案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
【讨论】:
如果日期是当月的第一天,您的代码将不起作用 - 它会变成上个月的第一天以上是关于如何将日期设置为该月的第一个日期?的主要内容,如果未能解决你的问题,请参考以下文章