我有一个带有索引列的 pandas DataFrame =
date
。
输入:
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')
很容易。
有一个pandas问题关于地板问题
建议的方法是
import pandas as pd
pd.to_datetime(df.date).dt.to_period('M').dt.to_timestamp()
您可以使用时间序列偏移 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
采用时间增量方法,
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
这就能解决问题,不需要导入。 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]')
您还可以使用字符串日期时间格式:
df['month'] = df['date'].dt.strftime('%Y-%m-01')
这是另一种“pandonic”方式:
df.date - pd.Timedelta('1 day') * (df.date.dt.day - 1)
假设您正在处理以下数据框:
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
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)
。
这应该有效:
[x.replace(day=1).date() for x in df['date']]
唯一的要求是确保
date
是日期时间,我们可以通过调用 pd.to_datetime(df['date'])
来保证这一点
喜欢米哈伊尔·文科夫的回答。添加以下代码以将列添加为时间戳值并保留时区信息
df['month'] = pd.to_datetime(df['timestamp'].dt.strftime('%Y-%m-01')).dt.tz_localize(timezone)
其中时区 = 'America/Los_Angeles' 或您想要的任何区域
一个内胆
df.set_index(
df.index - pd.to_timedelta(pd.to_datetime(df.index).day - 1, unit="D")
)
(有关于矢量化的警告。)
实际上我宁愿做的是首先将该索引设为适当的列,对其进行操作,然后再次将其设为索引:
In [32]: df = pd.DataFrame(
index=[datetime.date.fromisoformat("1986-01-31"), datetime.date.fromisoformat("1986-02-28")],
data={"value":[22.93, 15.46]})
In [33]: df
Out[33]:
value
1986-01-31 22.93
1986-02-28 15.46
In [34]: df2 = df.reset_index().rename(columns={"index": "date"})
In [35]: df2
Out[35]:
date value
0 1986-01-31 22.93
1 1986-02-28 15.46
In [36]: df2.date = pd.to_datetime(df2.date)
In [37]: df2.date
Out[37]:
0 1986-01-31
1 1986-02-28
Name: date, dtype: datetime64[ns]
In [38]: df2.date -= pd.to_timedelta(df2.date.dt.day - 1, unit="D")
In [39]: df2
Out[39]:
date value
0 1986-01-01 22.93
1 1986-02-01 15.46
In [40]: df2.set_index("date")
Out[40]:
value
date
1986-01-01 22.93
1986-02-01 15.46
最重要的一行是:
df2.date -= pd.to_timedelta(df2.date.dt.day - 1, unit="D")
您实际删除日期偏移的位置。
我需要地板和天花板功能。以下对我有用:
import pandas as pd
def monthfloor(ts):
offset = pd.offsets.Hour(1)
if offset.is_month_start(ts):
return ts
else:
return ts - pd.offsets.MonthBegin()
def monthceil(ts):
offset = pd.offsets.Hour(1)
if offset.is_month_end(ts):
return ts
else:
return ts + pd.offsets.MonthEnd()
begin = pd.Timestamp(2023, 4, 1)
middle = pd.Timestamp(2023, 4, 15)
end = pd.Timestamp(2023, 4, 30)
tss = pd.Series([begin, middle, end])
print(tss.map(monthfloor))
# 0 2023-04-01
# 1 2023-04-01
# 2 2023-04-01
# dtype: datetime64[ns]
print(tss.map(monthceil))
# 0 2023-04-30
# 1 2023-04-30
# 2 2023-04-30
# dtype: datetime64[ns]
首先添加偏移量,然后删除它。这样,该月的第一天也表现良好(并且它也应该适用于其他偏移量):
import pandas as pd
df = pd.DataFrame({"value": pd.date_range("2017-01-01", "2017-02-10", freq="W")})
df["floored"] = df["value"] + pd.offsets.MonthBegin() - pd.offsets.MonthBegin()
结果:
>>> print(df)
value floored
0 2017-01-01 2017-01-01
1 2017-01-08 2017-01-01
2 2017-01-15 2017-01-01
3 2017-01-22 2017-01-01
4 2017-01-29 2017-01-01
5 2017-02-05 2017-02-01