如何用Python基于时间序列PSD/FFT自动判定不存在季节性?
基于PSD/FFT自动判定时间序列无季节性的实现方案
核心判定逻辑
针对高斯噪声、单脉冲这类无季节性的信号,和存在周期性的信号的PSD特征差异,可通过以下几个可量化的规则自动判定:
- 先排除直流分量干扰:频率为0的分量对应信号均值,和季节性无关,计算峰值前先过滤掉
freqs < 1/len(y)的区间,避免直流分量干扰峰值判断 - 计算峰值突出度:取排除直流后的PSD最大值,和同区间PSD的中位数做差。高斯噪声的PSD是近似平坦的白谱,单脉冲的PSD是缓慢下降的sinc谱,两者的峰值突出度普遍低于10dB;存在显著季节性的信号峰值突出度通常在12dB以上
- 校验峰值孤立性:对筛选出的最高PSD峰值,检查其前后相邻3~5个频率点的PSD差值,如果最大差值小于3dB,说明是宽峰(单脉冲、噪声随机波动都属于这类),不属于周期性信号的窄尖峰
- 可选合理性校验:计算得到的周期长度不能超过序列总长度的1/2,否则属于无统计意义的假周期,直接判定为无季节性
补全后的可运行代码
import numpy as np def psd_time_series(y): # 修正原代码变量大小写、括号缺失问题 y_demean = y - np.mean(y) yAC = np.correlate(y_demean, y_demean, mode='full') yAC = yAC / np.max(yAC) fft_yAC = np.fft.fft(yAC) freqs = np.arange(0, len(fft_yAC)) / len(fft_yAC) psd = 10 * np.log10(np.abs(fft_yAC) / max(np.abs(fft_yAC))) return psd, freqs def determine_if_seasonal(psd, freqs, seq_len): # 1. 过滤直流分量和超低频分量(周期超过序列长度一半的无意义) valid_mask = (freqs >= 2 / seq_len) & (freqs <= 0.5) valid_psd = psd[valid_mask] valid_freqs = freqs[valid_mask] if len(valid_psd) < 5: # 序列过短无法判断,直接返回无季节性 return False, None # 2. 找最高峰值 max_psd_idx = np.argmax(valid_psd) max_psd = valid_psd[max_psd_idx] median_psd = np.median(valid_psd) # 3. 峰值突出度校验,经验阈值10dB,可根据自己的数据集调整 if (max_psd - median_psd) < 10: return False, None # 4. 峰值孤立性校验,检查前后2个点的差值 left = max(max_psd_idx - 2, 0) right = min(max_psd_idx + 2, len(valid_psd)-1) neighbor_mean = np.mean(np.concatenate([valid_psd[left:max_psd_idx], valid_psd[max_psd_idx+1:right+1]])) if (max_psd - neighbor_mean) < 3: return False, None # 所有校验通过,判定为有季节性 return True, valid_freqs[max_psd_idx] def detect_seasonality(y): psd, freqs = psd_time_series(y) seasonality, peak_freq = determine_if_seasonal(psd, freqs, len(y)) if seasonality: periodicity = round(1 / peak_freq) # 额外校验周期不能超过序列长度的1/2 if periodicity > len(y)//2: return None return periodicity else: return None
阈值调整建议
你可以根据自己的数据集特性调整两个核心阈值:
- 若你的序列普遍噪声较高,可把峰值突出度阈值下调到8~9dB,降低漏判率
- 若你对假阳性(把噪声误判为有季节性)容忍度低,可把峰值突出度阈值上调到15~18dB,提升判定严格度
- 峰值孤立性校验的相邻点范围,可以根据序列长度调整,最长不超过总频率点的2%即可
内容的提问来源于stack exchange,提问作者kspr
相关产品推荐
相关产品推荐

