【问题标题】:Pandas: Change day熊猫:改变一天
【发布时间】:2020-10-06 00:55:13
【问题描述】:

我有一个datetime 格式的系列,需要将每个条目的日期更改为1。我想了很多简单的解决方案,但没有一个对我有用。目前,唯一真正有效的是

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

不可能真的那么复杂,不是吗?有一个月的开始,但不幸的是offset,这在这里没用。该方法似乎没有 set() 函数,而当系列是一列而不是索引本身的(部分)时,功能甚至更少。

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

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    你可以使用.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))
    

    给予:

    0   2014-01-01
    1   2014-03-01
    2   2014-06-01
    dtype: datetime64[ns]
    

    【讨论】:

    • 对于留在 pandas 生态系统中的矢量化解决方案,请考虑以下我的回答。
    【解决方案2】:

    另一个答案有效,但任何时候使用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})
    

    申请:

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

    矢量化:

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

    请注意,尝试将日期更改为不存在的日期可能会遇到错误,例如尝试将 2012-02-29 更改为 2013-02-29。使用pd.to_datetimeerrors 参数忽略或强制它们。

    数据生成:生成具有 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()
    

    【讨论】:

      【解决方案3】:

      其他两个答案有效,但既不优雅也不符合pandas 库的精神。相反,请考虑这一点,在我的测试中它也比凯尔巴伦的矢量化答案稍微快一点。它是一种不需要定义任何外部函数、被矢量化并保持在pandas 生态系统中的单线:

      import pandas as pd
      dtseries.dt.to_period('M').dt.to_timestamp()
      

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

      您可以找到其他各种频率的缩写in the relevant doc page

      这当然假设dtseries 是一个日期时间序列,如果不是,您可以使用pd.to_datetime(my_series) 轻松转换它。

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

      from pandas.tseries.offsets import DateOffset
      dtseries.dt.to_period('M').dt.to_timestamp() + DateOffset(days=10)
      

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

      from pandas.tseries.offsets import BusinessDay
      dtseries.dt.to_period('M').dt.to_timestamp() + BusinessDay(n=5)
      

      【讨论】:

      • 请注意,这仅适用于您希望降至预定义频率的特殊情况。如果您想将所有日期设置为第 10 天,那么我看不到使用此功能的方法。
      • @KyleBarron 不,这仍然有效。您只需添加一个date offset。查看我的编辑:)
      • @KyleBarron 事实上,我认为我的解决方案在处理复杂的偏移时要灵活得多。例如,您将如何处理工作日案例?
      • 这些都是好点,编辑很好地解释了这一点。通过一个简单的测试,您的答案看起来比我的快 5%。我不再使用 pandas 中的日期,但我认为你的日期是最通用的。
      猜你喜欢
      • 2015-04-24
      • 1970-01-01
      • 1970-01-01
      • 2021-08-15
      • 2019-05-24
      • 2016-11-14
      • 2018-09-06
      • 1970-01-01
      相关资源
      最近更新 更多