熊猫:改变一天

Foo*_*Bar 10 python pandas

我有一系列datetime格式,需要更改1每个条目的日期.我想到了很多简单的解决方案,但它们都不适用于我.目前,唯一真正有效的是

  • 将系列设置为索引
  • 从索引查询月份和年份
  • 使用年,月和1重建新的时间序列

它真的不是那么复杂,可以吗?有一个月的开始,但不幸的是offset,这在这里毫无用处.set()对于该方法似乎没有任何功能,并且当系列是列时,功能更少,而不是(部分)索引本身.

唯一相关的问题是这个,但那里使用的技巧在这里不适用.

Jon*_*nts 18

你可以使用.applydatetime.replace,例如:

import pandas as pd
from datetime import datetime

ps = pd.Series([datetime(2014, 1, 7), datetime(2014, 3, 13), datetime(2014, 6, 12)])
new = ps.apply(lambda dt: dt.replace(day=1))
Run Code Online (Sandbox Code Playgroud)

得到:

0   2014-01-01
1   2014-03-01
2   2014-06-01
dtype: datetime64[ns]
Run Code Online (Sandbox Code Playgroud)

  • 对于 pandas 生态系统内的矢量化解决方案,请考虑下面我的答案。 (2认同)

Kyl*_*ron 16

另一个答案是有效的,但无论何时使用apply,都会使代码速度降低很多.通过为系列编写快速矢量化日期时间替换,我能够获得8.5倍的加速.

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})
Run Code Online (Sandbox Code Playgroud)

应用:

%timeit dtseries.apply(lambda dt: dt.replace(day=1))
# 4.17 s ± 38.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Run Code Online (Sandbox Code Playgroud)

矢量:

%timeit vec_dt_replace(dtseries, day=1)
# 491 ms ± 6.48 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Run Code Online (Sandbox Code Playgroud)

请注意,您可能会尝试将日期更改为不存在的日期,例如尝试将2012-02-29更改为2013-02-29,从而面临错误.使用errors参数pd.to_datetime来忽略或强制它们.

数据生成:生成具有100万个随机日期的系列:

import pandas as pd
import numpy as np

# Generate random dates. Modified from: https://stackoverflow.com/a/50668285
def pp(start, end, n):
    start_u = start.value // 10 ** 9
    end_u = end.value // 10 ** 9

    return pd.Series(
        (10 ** 9 * np.random.randint(start_u, end_u, n)).view('M8[ns]'))

start = pd.to_datetime('2015-01-01')
end = pd.to_datetime('2018-01-01')
dtseries = pp(start, end, 1000000)
# Remove time component
dtseries = dtseries.dt.normalize()
Run Code Online (Sandbox Code Playgroud)


Cha*_*les 5

其他两个答案有效,但既不优雅也不符合pandas图书馆的精神。相反,请考虑这一点,在我的测试中,它也比 Kyle Barron 的矢量化答案略快。这是一个单行代码,不需要定义任何外部函数,被向量化并留在pandas生态系统内:

import pandas as pd
dtseries.dt.to_period('M').dt.to_timestamp()
Run Code Online (Sandbox Code Playgroud)

这种方法的额外好处是支持许多其他频率到下限,例如每周 ( 'W') 或工作日 ( 'B'),使用上述矢量化方法实施起来会更棘手。

您可以在相关文档页面中找到各种其他频率的缩写。

这当然假设这dtseries是一个日期时间系列,如果不是,您可以轻松地将其转换为pd.to_datetime(my_series).

该解决方案还允许在使用各种偏移量方面具有很大的灵活性。例如,要使用一个月的第十天:

from pandas.tseries.offsets import DateOffset
dtseries.dt.to_period('M').dt.to_timestamp() + DateOffset(days=10)
Run Code Online (Sandbox Code Playgroud)

我建议您查看文档以了解 pandas offsets。offsets pandas 提供了许多相当复杂的偏移量的支持,例如工作日、假期、营业时间等......正如@KyleBarron 和@JonClements 的答案所建议的那样,手动实现这些将非常麻烦。例如,考虑这个例子,获取从月初开始偏移 5 个工作日的日期:

from pandas.tseries.offsets import BusinessDay
dtseries.dt.to_period('M').dt.to_timestamp() + BusinessDay(n=5)
Run Code Online (Sandbox Code Playgroud)