【问题标题】:How to round a Pandas `DatetimeIndex`?如何舍入 Pandas `DatetimeIndex`?
【发布时间】:2012-11-26 23:31:01
【问题描述】:

我有一个pandas.DatetimeIndex,例如:

pd.date_range('2012-1-1 02:03:04.000',periods=3,freq='1ms')
>>> [2012-01-01 02:03:04, ..., 2012-01-01 02:03:04.002000]

我想将日期 (Timestamps) 四舍五入到最接近的秒数。我怎么做?预期结果类似于:

[2012-01-01 02:03:04.000000, ..., 2012-01-01 02:03:04.000000]

是否可以通过将 Numpy datetime64[ns] 舍入到秒而不更改 dtype [ns] 来实现这一点?

np.array(['2012-01-02 00:00:00.001'],dtype='datetime64[ns]')

【问题讨论】:

  • @hayden,看我的编辑。我只想四舍五入到最接近的秒数。
  • date_range 默认为日频率,我假设您的意思是 pd.date_range('2012-1-1 00:00.000',periods=2, freq='S')
  • @MattiJohn,请参阅我的更正。我的意思是 same 秒重复。
  • 很好的解决方案here

标签: date datetime numpy pandas date-format


【解决方案1】:

pandas 0.18.0 中为 DatetimeIndex、Timestamp、TimedeltaIndex 和 Timedelta 添加了round() 方法。现在我们可以执行以下操作:

In[114]: index = pd.DatetimeIndex([pd.Timestamp('2012-01-01 02:03:04.000'), pd.Timestamp('2012-01-01 02:03:04.002'), pd.Timestamp('20130712 02:03:04.500'), pd.Timestamp('2012-01-01 02:03:04.501')])

In[115]: index.values
Out[115]: 
array(['2012-01-01T02:03:04.000000000', '2012-01-01T02:03:04.002000000',
       '2013-07-12T02:03:04.500000000', '2012-01-01T02:03:04.501000000'], dtype='datetime64[ns]')

In[116]: index.round('S')
Out[116]: 
DatetimeIndex(['2012-01-01 02:03:04', '2012-01-01 02:03:04',
               '2013-07-12 02:03:04', '2012-01-01 02:03:05'],
              dtype='datetime64[ns]', freq=None)

round() 接受频率参数。它的字符串别名列在here

【讨论】:

    【解决方案2】:

    对于更一般的舍入,您可以利用 Pandas Timestamp 对象主要使用标准库 datetime.datetime API 的事实,包括 datetime.datetime.replace() 方法。

    因此,要解决您的微秒舍入问题,您可以这样做:

    import datetime
    import pandas as pd
    
    times = pd.date_range('2012-1-1 02:03:04.499',periods=3,freq='1ms')
    # Add 5e5 microseconds and truncate to simulate rounding
    times_rounded = [(x + datetime.timedelta(microseconds=5e5)).replace(microsecond=0) for x in times]
    
    from IPython.display import display
    print('Before:')
    display(list(times))
    print('After:')
    display(list(times_rounded))
    

    输出:

    Before:
    [Timestamp('2012-01-01 02:03:04.499000', offset='L'),
     Timestamp('2012-01-01 02:03:04.500000', offset='L'),
     Timestamp('2012-01-01 02:03:04.501000', offset='L')]
    After:
    [Timestamp('2012-01-01 02:03:04', offset='L'),
     Timestamp('2012-01-01 02:03:05', offset='L'),
     Timestamp('2012-01-01 02:03:05', offset='L')]
    

    您可以使用相同的技术,例如,四舍五入到最近的一天(只要您不关心闰秒等):

    times = pd.date_range('2012-1-1 08:00:00', periods=3, freq='4H')
    times_rounded = [(x + datetime.timedelta(hours=12)).replace(hour=0, second=0, microsecond=0) for x in times]
    

    受此 SO 帖子的启发:https://stackoverflow.com/a/19718411/1410871

    【讨论】:

      【解决方案3】:

      更新:如果您对 DatetimeIndex / datetime64 列执行此操作,更好的方法是直接使用 np.round 而不是通过应用/映射:

      np.round(dtindex_or_datetime_col.astype(np.int64), -9).astype('datetime64[ns]')
      

      旧答案(有更多解释):

      虽然@Matti 的答案显然是处理您的情况的正确方法,但我想我会添加一个答案,您可以如何将时间戳四舍五入到最接近的秒数:

      from pandas.lib import Timestamp
      
      t1 = Timestamp('2012-1-1 00:00:00')
      t2 = Timestamp('2012-1-1 00:00:00.000333')
      
      In [4]: t1
      Out[4]: <Timestamp: 2012-01-01 00:00:00>
      
      In [5]: t2
      Out[5]: <Timestamp: 2012-01-01 00:00:00.000333>
      
      In [6]: t2.microsecond
      Out[6]: 333
      
      In [7]: t1.value
      Out[7]: 1325376000000000000L
      
      In [8]: t2.value
      Out[8]: 1325376000000333000L
      
      # Alternatively: t2.value - t2.value % 1000000000
      In [9]: long(round(t2.value, -9)) # round milli-, micro- and nano-seconds
      Out[9]: 1325376000000000000L
      
      In [10]: Timestamp(long(round(t2.value, -9)))
      Out[10]: <Timestamp: 2012-01-01 00:00:00>
      

      因此您可以将其应用于整个索引:

      def to_the_second(ts):
          return Timestamp(long(round(ts.value, -9)))
      
      dtindex.map(to_the_second)
      

      【讨论】:

      • 看来1000000应该换成1000000000
      • 重要的是,我想在 DatetimeIndex 上做。
      • @user1579844 你当然是对的!我忘了毫秒...哎呀!我已更正此问题并添加了如何将其应用于整个 dt_index。
      • "Alternatively: t2.value - t2.value % 1000000000" 我相信这会将 t2 向下舍入,而不是最接近的。
      【解决方案4】:

      更改索引本身没有什么意义 - 因为您可以使用date_range 生成问题中所需的频率参数。

      我假设您要做的是更改包含数据的时间序列的频率,在这种情况下,您可以使用resample (documentation)。例如,如果您有以下时间序列:

      dt_index = pd.date_range('2012-1-1 00:00.001',periods=3, freq='1ms')
      ts = pd.Series(randn(3), index=dt_index)
      
      
      2012-01-01 00:00:00           0.594618
      2012-01-01 00:00:00.001000    0.874552
      2012-01-01 00:00:00.002000   -0.700076
      Freq: L
      

      然后您可以使用重新采样将频率更改为秒,指定您希望如何聚合值(平均值、总和等):

      ts.resample('S', how='sum')
      
      2012-01-01 00:00:00    0.594618
      2012-01-01 00:00:01    0.174475
      Freq: S
      

      【讨论】:

      • 这消除了一些行。我只想通过四舍五入到最接近的秒来更改索引值。
      • 啊,抱歉,我没有意识到你想要重复的值。看起来@hayden 的更新答案会做你想做的事
      猜你喜欢
      • 2022-01-26
      • 2023-04-01
      • 1970-01-01
      • 2018-06-23
      • 2017-08-16
      • 1970-01-01
      • 2015-02-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多