如何将日期定格到该月的第一天?

问题描述 投票:0回答:12

我有一个带有索引列的 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')
很容易。

python date pandas numpy
12个回答
46
投票

有一个pandas问题关于地板问题

建议的方法是

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

19
投票

您可以使用时间序列偏移 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

11
投票

这就能解决问题,不需要导入。 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]')

9
投票

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

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


8
投票

这是另一种“pandonic”方式:

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

3
投票

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

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

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)


1
投票

2019 年 8 月起:

这应该有效:

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

唯一的要求是确保

date
是日期时间,我们可以通过调用
pd.to_datetime(df['date'])

来保证这一点

1
投票

喜欢米哈伊尔·文科夫的回答。添加以下代码以将列添加为时间戳值并保留时区信息

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

其中时区 = 'America/Los_Angeles' 或您想要的任何区域


0
投票

一个内胆

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")

您实际删除日期偏移的位置。


0
投票

我需要地板和天花板功能。以下对我有用:

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]

0
投票

首先添加偏移量,然后删除它。这样,该月的第一天也表现良好(并且它也应该适用于其他偏移量):

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
© www.soinside.com 2019 - 2024. All rights reserved.