如何使用scipy.fft提取时间序列中最常见的周期频率
如何使用scipy.fft提取时间序列中最常见的周期频率
我明白你的困扰——看着时间序列里明显的12个月周期,却不知道怎么用scipy.fft把这个频率精准提取出来。别担心,咱们一步步来解决这个问题:
首先还原你的问题场景:你有一个带周期性的时间序列(可视化图如下),能看到每12个月左右就会出现涨跌循环,偶尔会在11-13个月波动,现在想通过傅里叶变换找到这个最显著的周期。

完整解决方案代码&步骤
第一步:准备数据和依赖库
先把你的数据转成NumPy数组,同时导入需要的工具库:
import numpy as np from scipy.fft import fft, fftfreq import matplotlib.pyplot as plt # 加载你的数据 data = np.array([ 11130.578853063385, 6509.723592808, 5693.928982796129, 3415.099921325464, -9299.291673374173, -3388.284173885658, -5577.9316298032745, -3509.583232678111, 2285.99065857961, 3844.3061166856014, -7383.526882168155, -4622.792125020905, 2813.586128745183, -1501.9405075290495, 8911.954971658348, 7800.444458471794, -1190.7952377053866, 4768.791467877524, 2173.593871988719, -2420.04786197912, -2304.842777539399, -3562.1788525161837, -8766.208378658988, -7655.936603573945, -5890.9298543619125, -9628.764012284291, 12124.740839506767, 12391.257220312522, 7512.253051850619, 12921.032383220418, 10063.270097922614, -1350.2599176773563, -6887.434936788105, -11116.26528794868, -10196.871058658888, -10874.172006461778, -15014.190086779208, -17837.744786550902, 15235.434771455053, 17183.25815161994, 16835.95193044929, 21063.986176551374, 17987.99577807288, -270.6290142721815, -11239.957979217992, -18724.854251002133, -11752.820614075652, -14332.597031388648, -24609.22398631297, -26155.98046224267, 18192.356438131075, 22165.14150786262, 26758.419290443217, 29284.65841543005, 25762.928479462924, 865.3393849464444, -15121.264730579132, -26306.45361387036, -13494.286360139175, -18089.58324839494, -34738.184049794625, -34718.87495981627, 21145.112760626133, 27322.030709198487, 37252.78168890166, 37846.98231395838, 33206.62103950547, 2092.870600583023, -18537.521405900694, -33955.48182565038, -15445.551953312479, -22284.152196532646, -45880.94206326016, -44229.92788481257, 24988.038646046363, 32958.71017047145, 49117.93320642349, 47304.760779654374, 40776.01828187993, 3403.573579093782, -22402.79273128273, -42361.96378730598, -17190.060741565456, -27378.2527904574, -59155.49212555031, -60122.10588005664, 26272.133100405994, 44887.192435244986, 69002.74742137044, 59037.928523261784, 42122.51604012313, 6075.663868325184, -20631.710295791454, -48088.66531781877, -23396.29341809641, -40847.479839729145, -68317.87342502769, -73679.4424942532, 28302.69374713241, 57321.16868946109, 83820.10748232565, 68399.66173487401, 44989.374076533895, 8830.088516704302, -18149.500187183363, -52028.5021898363, -31013.963236266634, -53956.5249205745, -77250.59604604884, -86642.45203443282, 30541.62328593645, 69812.47143595785, 98233.7834300242, 77385.915451272, 48189.69475295938, 11504.22579592029, -15251.799652343976, -55879.292898282, -38956.992207762654, -67210.9936142441, -86636.69916492153, -99845.12467446178, 32751.253099701484, 82656.01928819218, 113259.2399845611, 86532.20966362985, 51019.20889397171, 14289.09297146163, -11777.371574335935, -59627.30976102835, -47170.18721199697, -81027.36407627042, -96178.09587995053, -113526.93736260894, 34817.23859755824, 95927.57143777516, 128782.84687524068, 95920.65382048927, 53226.62965224956, 17272.000877533148, -7716.869736424804, -63110.06727848651, -55696.68126167806, -95538.60898488457, -105325.08525283691, -127600.17956244369, 36734.97589442811, 109601.51109750797, 144205.71977383518, 105517.48123365057, 54793.814706888734, 20380.77940730315, -3119.1108027357986, -66153.73274186133, -64702.85998743505, -110650.72884973585 ]) # 假设数据是按月采集的,采样间隔为1个月 sampling_interval = 1 # 单位:月 n = len(data)
第二步:执行傅里叶变换并计算频率
用fft计算傅里叶变换,fftfreq生成对应的频率轴(注意只保留正频率,因为FFT结果是对称的):
# 计算FFT yf = fft(data) # 生成频率轴(单位:1/月) xf = fftfreq(n, sampling_interval) # 筛选出正频率部分,避免重复计算 positive_mask = xf > 0 xf_positive = xf[positive_mask] yf_positive = np.abs(yf[positive_mask]) # 取复数的绝对值作为幅度,代表该频率的强度
第三步:提取主导周期
傅里叶变换中幅度最大的频率,就是数据里最显著的周期对应的频率,我们把它转成周期(周期=1/频率):
# 找到幅度最大的频率索引 max_amp_index = np.argmax(yf_positive) dominant_frequency = xf_positive[max_amp_index] # 计算对应的周期 dominant_period = 1 / dominant_frequency print(f"提取到的最显著周期为:{dominant_period:.2f} 个月")
第四步:可视化频谱(可选)
如果想直观看到各个频率的强度,可以画个频谱图,还能标记出我们找到的主导周期:
plt.figure(figsize=(10, 6)) plt.plot(xf_positive, yf_positive) plt.xlabel('频率 (1/月)') plt.ylabel('幅度') plt.title('时间序列频谱图') # 标记主导周期对应的点 plt.scatter(dominant_frequency, yf_positive[max_amp_index], color='red', label=f'主导周期:{dominant_period:.2f}个月') plt.legend() plt.show()
几个关键注意点
- 采样间隔:这里假设数据是按月采集的,所以采样间隔设为1个月。如果你的数据采样频率不同(比如每周一次),记得调整这个值,不然周期计算会出错。
- 正频率筛选:FFT的结果是对称的,负频率和正频率信息重复,所以只看正频率就够了。
- **幅度取




