高/非规整频率DatetimeIndex构建:如何保留有效freq属性?
嘿,这个高频时间序列的坑我之前踩过!确实,pandas对规整的低频时间序列很友好,但碰到300Hz这种带无限小数步长的情况,用固定纳秒频率就容易出累积误差,而且自定义生成的索引还丢了freq属性,好多pandas功能用不了,太闹心了。下面给你几个实用的解决方案:
一、用有理频率字符串直接生成无误差且带有效freq的DatetimeIndex
pandas其实支持有理分数形式的频率定义,刚好能解决1/300秒这种无限小数步长的问题。不用硬凑纳秒数,直接写freq='1S/300'就可以了,它会精确计算每个时间点,完全避免累积误差,而且生成的DatetimeIndex会自带有效freq属性:
import pandas as pd # 生成300Hz、1秒的时间序列,start可以是任意datetime,这里用epoch时间0示例 dt_index = pd.date_range(start=pd.Timestamp(0), periods=300, freq='1S/300') # 检查最后一个时间点是否正确:应该是0 + 299*(1/300)秒 = 0.996666666...秒 print(dt_index[-1]) # 输出 Timestamp('1970-01-01 00:00:00.996666666'),完全符合预期 print(dt_index.freq) # 输出 <3333333333ns * 1/300>,是有效的频率对象
这样生成的索引不仅没有累积误差,还能正常使用to_period()这类需要freq的功能。
二、自定义'300Hz'频率别名,让代码更直观
如果觉得'1S/300'不够直观,想直接用'300Hz'作为freq别名,可以通过pandas的register_freq函数来注册自定义频率:
from pandas.tseries.frequencies import register_freq, to_offset # 注册'300Hz'对应1秒300次的频率 register_freq('300Hz', to_offset('1S/300')) # 现在可以直接用freq='300Hz'了 dt_index = pd.date_range(start=pd.Timestamp(0), periods=300, freq='300Hz') print(dt_index.freq) # 输出 <3333333333ns * 1/300>,同样有效
这样后续写代码的时候就可以直接用'300Hz',可读性更高。
三、给已有无freq的DatetimeIndex补全有效freq
如果已经有了用自定义函数生成的无freq的DatetimeIndex,也可以通过转成PeriodIndex再转回DatetimeIndex的方式补全freq属性:
# 假设这是你用date_range_fs生成的无freq索引 your_dt_index = pd.date_range(start=pd.Timestamp(0), periods=300, freq=None) # 转成PeriodIndex再转回DatetimeIndex,指定正确的频率 fixed_dt_index = pd.PeriodIndex(your_dt_index, freq='1S/300').to_timestamp() print(fixed_dt_index.freq) # 现在freq属性就有效了
为什么之前的方法会出问题?
你之前用freq='3333333ns',本质是把无限小数的步长近似成了整数纳秒,每次循环都会少加约0.333ns,300次累积下来就会偏离预期值。而有理频率是基于分数计算的,pandas会精确处理每个时间点的增量,不会有累积误差。
内容的提问来源于stack exchange,提问作者Tabs
相关产品推荐
相关产品推荐

