Python使用SciPy库进行时间序列分析的完整示例详解

 更新时间:2026年10月03日 08:56:11   作者:databook  
时间序列分析听起来有点学术,但其实干起来挺直接的,本文用SciPy带你一步步从模拟全年温度数据开始,演示移动平均、低通滤波、去趋势、频谱分析、自相关和互相关的完整流程,教你轻松应对日常数据分析

时间序列分析听起来有点学术,但其实干起来挺直接的。

你有一串按时间排列的数据,想知道里面有没有趋势、有没有周期性、噪声有多大,或者两个序列之间有没有延迟关系。

SciPy 这个库在信号处理、统计检验方面提供了不少现成的工具,配合 Matplotlib 画图,基本能覆盖日常分析的大部分需求。

本文不是单纯的介绍知识点,而是直接造一组模拟的全年每日温度数据,然后一步步用它演示:

  • 怎么平滑
  • 怎么滤波
  • 怎么去趋势
  • 怎么找周期
  • 怎么看自相关和互相关

每一步都配上代码和图表,你跟着跑一遍,自然就知道 SciPy 里这些函数该怎么用了。

1. 先造一组有季节性和趋势的温度数据

我在项目上有不少真实的数据,可能会违反甲方的隐私保护,所以我们直接用 NumPy 模拟。

假设一年 365 天,温度由三部分组成:一个周期为 365 天的正弦波代表季节性,一个缓慢上升的线性趋势,再加上一点随机噪声。

这样既有规律可循,又不会太干净。

import numpy as np
import matplotlib.pyplot as plt
# 设置中文字体,避免图表中的中文显示为方框
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
np.random.seed(0)                     # 固定随机种子,保证结果可复现
days = np.arange(365)                 # 天数
seasonal = 10 * np.sin(2 * np.pi * days / 365)  # 季节性成分
trend = 0.05 * days                   # 线性趋势
noise = np.random.normal(0, 2, 365)   # 随机噪声
temperature_data = 20 + seasonal + trend + noise  # 合成温度数据
plt.plot(days, temperature_data)
plt.xlabel('天数')
plt.ylabel('温度')
plt.title('模拟的每日温度数据')
plt.show()

画出来能看到一条上下波动、整体略微上升的曲线,噪声让线条有点毛刺。

这就是我们后面所有分析的输入。

2. 用移动平均把噪声压一压

原始数据里的高频噪声会干扰我们对趋势的判断。最简单的平滑方法就是移动平均:取一个窗口,比如 7 天,算平均值,然后窗口往后滑。

NumPy 的 convolve 可以很方便地实现。

def moving_average(data, window_size):
    return np.convolve(data, np.ones(window_size) / window_size, mode='valid')
smoothed = moving_average(temperature_data, 7)
plt.plot(days, temperature_data, label='原始数据', alpha=0.5)
plt.plot(days[6:], smoothed, label='7天移动平均', linewidth=2)
plt.legend()
plt.show()

窗口取 7 天是因为温度数据通常以周为周期有波动,7 天平均能抹掉大部分随机噪声,同时保留季节性的轮廓。

从图上能明显看出平滑后的曲线更干净,上升趋势和正弦波动都清晰了。

3. 低通滤波器:比移动平均更灵活的选择

移动平均本质上是个简单的低通滤波器,但如果你想更精确地控制截止频率,SciPy 的 signal.butter 和 filtfilt 更好用。

Butterworth 滤波器可以把高于某个频率的成分衰减掉,filtfilt 做双向滤波,避免相位偏移。

from scipy.signal import butter, filtfilt
def lowpass_filter(data, cutoff, fs, order=4):
    nyquist = 0.5 * fs
    normal_cutoff = cutoff / nyquist
    b, a = butter(order, normal_cutoff, btype='low', analog=False)
    return filtfilt(b, a, data)
filtered = lowpass_filter(temperature_data, cutoff=0.1, fs=1)
plt.plot(days, temperature_data, alpha=0.5, label='原始数据')
plt.plot(days, filtered, label='低通滤波后', linewidth=2)
plt.legend()
plt.show()

这里采样频率 fs=1(每天一个点),截止频率设成 0.1,意思是保留周期大于 10 天的成分,把更快的波动滤掉。

结果和移动平均类似,但边缘处理更平滑,没有移动平均那种两端缺数据的问题。

4. 去趋势:把长期上升拿掉,只看季节性

温度数据里有个线性上升趋势,如果我们只想看季节性的正弦波动,可以用 scipy.signal.detrend 把线性趋势减掉。

from scipy.signal import detrend
detrended = detrend(temperature_data)
plt.plot(days, detrended)
plt.xlabel('天数')
plt.ylabel('去趋势后的温度')
plt.title('去趋势结果')
plt.show()

去趋势后的曲线围绕 0 上下波动,正弦形状更明显了。

不过要注意,如果原始数据里的趋势不是线性的,detrend 默认只去线性趋势,效果可能不够。

我们这里恰好是线性趋势,所以结果很干净。

注:细心的朋友可能发现这个【去趋势结果】图和第 1 小节创建模拟数据后的【模拟的每日温度数据】图几乎一样。那么,去趋势结果(detrend)究竟做了什么呢?

一般来说,做频谱分析(FFT)、自相关、或找周期性之前,通常先去趋势。

否则线性趋势会在低频段产生很强的能量,掩盖真正的周期信号;去趋势也让数据的均值趋于稳定(平稳性),是很多时序分析方法的前置步骤。

简单来说:我们上面的 detrend 没有改变数据的“形状”,只是把数据整体平移并拉平,让它围绕 0 波动——相当于减掉了 20 + 0.05·days 这条背景直线。

仔细看可以发现,【去趋势结果】图中 Y 轴是围绕 0 左右波动的,第 1 小节中的【模拟的每日温度数据】图是围绕 30 左右波动的。

5. 频谱分析:找出主要周期

想知道数据里到底有哪些周期成分,频谱分析是最直接的办法。

scipy.signal.periodogram 可以计算功率谱密度,横轴是频率,纵轴是功率。

频率的倒数就是周期。

from scipy.signal import periodogram
frequencies, power = periodogram(temperature_data, fs=1)
plt.semilogy(frequencies, power)
plt.xlabel('频率(周期/天)')
plt.ylabel('功率')
plt.title('周期图')
plt.show()

图上会在频率 1/365 ≈ 0.00274 附近出现一个明显的峰值,对应 365 天的年周期。

用对数纵轴是因为功率差异可能很大。

如果你看到其他小峰值,可能是噪声或者谐波,但主峰的位置直接告诉了你数据里最强的周期。

6. 自相关:看当前值和过去值的关系

自相关衡量的是时间序列和它自己延迟若干天后的相关性。如果数据有季节性,自相关会在滞后 365 天附近出现高点。

我们用 scipy.stats.pearsonr 算滞后 1 到 30 天的相关系数。

from scipy.stats import pearsonr
lags = range(1, 31)
autocorrs = [pearsonr(temperature_data[:-lag], temperature_data[lag:])[0] for lag in lags]
plt.stem(lags, autocorrs)
plt.xlabel('滞后(天)')
plt.ylabel('自相关')
plt.title('30天内的自相关')
plt.show()

因为我们的数据有正弦季节性,自相关会呈现波浪形,滞后越接近 365 的整数倍,相关性越高。

这里只画了 30 天,能看到相关性逐渐下降然后可能回升,这反映了短期的周期结构。

7. 互相关:分析两个序列之间的延迟关系

有时候我们关心两个序列是否相关,以及一个是否领先另一个。互相关就是干这个的。

我们生成一组模拟湿度数据,让它跟温度有 5 天的延迟关系,然后计算不同偏移下的相关系数。

humidity_data = 70 + 0.5 * temperature_data[:-5] + np.random.normal(0, 1, 360)
shifts = range(-10, 11)
cross_corrs = [pearsonr(temperature_data[:360], np.roll(humidity_data, shift))[0] for shift in shifts]
plt.stem(shifts, cross_corrs)
plt.xlabel('偏移量')
plt.ylabel('互相关')
plt.title('温度与湿度的互相关')
plt.show()

结果会在偏移 +5 或 -5 附近出现峰值,具体方向取决于 np.roll 的定义。

这告诉我们湿度对温度有大约 5 天的滞后响应。

实际分析中,互相关能帮你判断因果方向或者找到最佳对齐方式。

8. 小结

到这里,我们用 SciPy 走了一遍时间序列分析的典型流程:

  • 造数据
  • 移动平均
  • 低通滤波
  • 去趋势
  • 频谱分析
  • 自相关
  • 互相关

每个函数都不复杂,关键是知道什么时候用哪个。

移动平均和低通滤波适合平滑和去噪,去趋势帮你分离长期和周期成分,频谱和自相关能揭示周期,互相关则用于两个序列的延迟分析。

这些技术是更高级建模(比如 ARIMA、傅里叶拟合)的基础。你可以在自己的数据上试试,把参数调一调,看看结果怎么变。

到此这篇关于Python使用SciPy库进行时间序列分析的完整示例详解的文章就介绍到这了,更多相关Python SciPy时间序列内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • django-rest-framework 自定义swagger过程详解

    django-rest-framework 自定义swagger过程详解

    这篇文章主要介绍了django-rest-framework 自定义swagger过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-07-07
  • Django框架ORM数据库操作实例详解

    Django框架ORM数据库操作实例详解

    这篇文章主要介绍了Django框架ORM数据库操作,结合实例形式详细分析了Django框架ORM数据库基本增删改查与相关函数使用技巧,需要的朋友可以参考下
    2019-11-11
  • Python 代码实现各种酷炫功能

    Python 代码实现各种酷炫功能

    这篇文章主要介绍了Python 代码实现各种酷炫功能,生成二维码、生成词云、批量抠图、文字情绪识别等功能分享,需要的小伙伴可以参考一下
    2022-03-03
  • python的import 机制是怎么实现的

    python的import 机制是怎么实现的

    这篇文章主要介绍了python的import 机制是怎么实现的,import有Python运行时的全局模块池的维护和搜索、解析与搜索模块路径的树状结构等作用,下文具体相关介绍需要的小伙伴可以参考一下
    2022-05-05
  • Python 轻松实现可视化大屏

    Python 轻松实现可视化大屏

    对于从事数据领域的小伙伴来说,当需要阐述自己观点、展示项目成果时,我们需要在最短时间内让别人知道你的想法。我相信单调乏味的语言很难让别人快速理解。最直接有效的方式就是将数据进行可视化展现
    2022-01-01
  • Windows下安装python MySQLdb遇到的问题及解决方法

    Windows下安装python MySQLdb遇到的问题及解决方法

    这篇文章主要介绍了Windows下安装python MySQLdb遇到的问题及解决方法,需要的朋友可以参考下
    2017-03-03
  • Python 多个图同时在不同窗口显示的实现方法

    Python 多个图同时在不同窗口显示的实现方法

    今天小编就为大家分享一篇Python 多个图同时在不同窗口显示的实现方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-07-07
  • 解决pycharm无法删除invalid interpreter(无效解析器)的问题

    解决pycharm无法删除invalid interpreter(无效解析器)的问题

    这篇文章主要介绍了pycharm无法删除invalid interpreter(无效解析器)的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-07-07
  • python中__slots__节约内存的具体做法

    python中__slots__节约内存的具体做法

    在本篇内容里小编给大家分享的是一篇关于python中__slots__节约内存的具体做法,有需要的朋友们可以跟着学习参考下。
    2021-07-07
  • Python实现CAN报文转换工具教程

    Python实现CAN报文转换工具教程

    这篇文章主要介绍了Python实现CAN报文转换工具教程,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-05-05

最新评论