如何舍入 Pandas `DatetimeIndex`?

Posted

技术标签:

【中文标题】如何舍入 Pandas `DatetimeIndex`?【英文标题】:How to round a 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 【参考方案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://***.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 的更新答案会做你想做的事

以上是关于如何舍入 Pandas `DatetimeIndex`?的主要内容,如果未能解决你的问题,请参考以下文章

Pandas转置重置十进制舍入

如何在包含浮点数和 Nan 或 None 值的同一列中舍入浮点值?

如何在 Pandas Dataframe Python 中使用科学计数法显示小数字?

MATLAB 舍入函数 - 它如何向上或向下舍入 0.5?

在 Pandas 条形图上用值注释条形

如何阻止 mysql 舍入浮点值?