时间序列分析实战:Python 5大特性(周期/趋势/离群)代码实现与解读

📅 2026/7/8 21:30:22
时间序列分析实战:Python 5大特性(周期/趋势/离群)代码实现与解读
时间序列分析实战Python 5大特性周期/趋势/离群代码实现与解读时间序列分析是数据科学领域中一项至关重要的技能它帮助我们理解数据随时间变化的模式并基于这些模式做出预测。无论是金融市场的股票价格、电商平台的销售数据还是气象站的温度记录时间序列数据无处不在。掌握时间序列分析的核心技术意味着你能够从看似杂乱的数据中提取有价值的信息为决策提供有力支持。本文将带你深入探索时间序列分析的五大核心特性周期性、相关性、滞后性、趋势性和离群值分析。不同于简单的理论讲解我们将通过一个完整的Python实战项目从数据加载到特性分析再到结果解读手把手教你构建端到端的时间序列分析解决方案。无论你是数据分析师、机器学习工程师还是对时间序列分析感兴趣的开发者这篇文章都将为你提供实用的技术指导和代码实现。1. 环境准备与数据加载在开始时间序列分析之前我们需要搭建合适的Python环境并准备分析所需的数据集。本节将详细介绍如何配置开发环境加载时间序列数据并进行初步的数据探索。首先确保你已经安装了以下Python库这些库将在我们的分析过程中发挥关键作用# 基础数据处理库 import pandas as pd import numpy as np # 统计分析库 from statsmodels.tsa.stattools import acf, pacf from statsmodels.graphics.tsaplots import plot_acf, plot_pacf import scipy.stats as stats # 机器学习相关库 from sklearn.linear_model import LinearRegression # 可视化库 import matplotlib.pyplot as plt import seaborn as sns对于本教程我们将使用ETTh1数据集电力变压器温度数据集作为示例数据。这个数据集包含了电力变压器多个传感器的测量值是时间序列分析的经典数据集之一。# 加载数据集 df pd.read_csv(ETTh1.csv) # 将日期列转换为datetime类型 df[date] pd.to_datetime(df[date]) # 设置日期为索引 df.set_index(date, inplaceTrue) # 提取目标分析列OT列 target_series df[OT]在开始深入分析前让我们先对数据进行初步的可视化了解其基本特征plt.figure(figsize(12, 6)) target_series.plot(title原始时间序列数据) plt.xlabel(日期) plt.ylabel(值) plt.grid(True) plt.show()提示在实际项目中数据预处理如处理缺失值、异常值是必不可少的步骤。确保你的数据已经过适当清洗否则分析结果可能会受到影响。2. 周期性分析揭示数据中的重复模式周期性是时间序列数据中重复出现的模式比如每日、每周或每年的规律性变化。识别周期性对于理解数据行为和做出准确预测至关重要。我们将介绍两种强大的周期性分析方法自相关函数(ACF)和傅里叶变换。2.1 自相关函数分析自相关函数衡量时间序列与其自身在不同时间滞后版本之间的相关性。下面是计算和可视化ACF的完整代码def plot_acf_analysis(series, lags40): 绘制自相关函数图并进行周期性分析 参数: series -- 时间序列数据 lags -- 计算的最大滞后阶数 # 计算自相关 acf_values acf(series, nlagslags) # 设置绘图风格 sns.set(stylewhitegrid) # 绘制自相关图 plt.figure(figsize(12, 6)) plt.stem(acf_values) plt.axhline(y0, colorblack, linestyle-) plt.axhline(y1.96/np.sqrt(len(series)), colorgray, linestyle--) plt.axhline(y-1.96/np.sqrt(len(series)), colorgray, linestyle--) plt.xlabel(滞后阶数) plt.ylabel(自相关系数) plt.title(自相关函数(ACF)分析) plt.show() return acf_values # 执行ACF分析 acf_results plot_acf_analysis(target_series)解读ACF图的关键点显著峰值在特定滞后阶数出现的显著峰值超出虚线范围表明存在周期性峰值间隔连续峰值之间的间隔就是潜在的周期长度衰减模式周期性数据的ACF通常会缓慢衰减而非周期性数据的ACF会快速衰减至零2.2 傅里叶变换分析傅里叶变换将时间序列从时域转换到频域帮助我们识别数据中的主要频率成分def fourier_transform_analysis(series, sampling_rate1): 执行傅里叶变换并进行频谱分析 参数: series -- 时间序列数据 sampling_rate -- 采样率数据点/单位时间 # 计算快速傅里叶变换 fft np.fft.fft(series) frequencies np.fft.fftfreq(len(series), d1/sampling_rate) # 只取正频率部分 positive_freq_mask frequencies 0 frequencies frequencies[positive_freq_mask] fft fft[positive_freq_mask] # 计算振幅谱 amplitude_spectrum np.abs(fft) # 绘制频谱图 plt.figure(figsize(12, 6)) plt.plot(frequencies, amplitude_spectrum) plt.xlabel(频率 (1/单位时间)) plt.ylabel(振幅) plt.title(傅里叶变换频谱分析) plt.grid(True) plt.show() # 找出主要频率成分 dominant_freq frequencies[np.argmax(amplitude_spectrum)] print(f主要频率成分: {dominant_freq:.4f} (周期长度: {1/dominant_freq:.1f} 单位时间)) return frequencies, amplitude_spectrum # 执行傅里叶分析 freqs, amps fourier_transform_analysis(target_series)傅里叶分析的关键输出指标说明实际意义主要频率振幅最大的频率成分数据中最强的周期性信号周期长度主要频率的倒数数据重复出现的间隔时间次要峰值其他显著的频率成分数据中可能存在的其他周期性模式通过结合ACF和傅里叶变换的结果我们可以全面理解数据中的周期性特征为后续的建模和预测奠定基础。3. 相关性分析探索变量间的关系相关性分析帮助我们理解不同时间序列变量之间的关系强度。在多元时间序列分析中这种理解尤为重要因为它可以揭示变量之间的潜在联系和依赖关系。3.1 皮尔逊与斯皮尔曼相关系数我们将实现一个综合函数计算并比较两种最常用的相关系数def comprehensive_correlation_analysis(data, col1, col2): 执行全面的相关性分析包括皮尔逊和斯皮尔曼相关系数 参数: data -- 包含多列数据的DataFrame col1 -- 第一列名称 col2 -- 第二列名称 # 提取两列数据 series1 data[col1] series2 data[col2] # 计算皮尔逊相关系数线性关系 pearson_corr, pearson_p stats.pearsonr(series1, series2) # 计算斯皮尔曼相关系数单调关系 spearman_corr, spearman_p stats.spearmanr(series1, series2) # 输出结果 print(f皮尔逊相关系数: {pearson_corr:.4f} (p值: {pearson_p:.4f})) print(f斯皮尔曼相关系数: {spearman_corr:.4f} (p值: {spearman_p:.4f})) # 绘制散点图 plt.figure(figsize(10, 6)) sns.regplot(xcol1, ycol2, datadata, scatter_kws{alpha:0.3}) plt.title(f{col1} 与 {col2} 的相关性分析) plt.grid(True) plt.show() return { pearson: {correlation: pearson_corr, p_value: pearson_p}, spearman: {correlation: spearman_corr, p_value: spearman_p} } # 执行相关性分析示例分析OT列与MULL列的关系 corr_results comprehensive_correlation_analysis(df, OT, MULL)相关系数解读指南系数范围相关系数取值在-1到1之间1表示完全正相关-1表示完全负相关0表示无线性关系p值意义p值0.05表示相关性统计显著选择标准数据满足正态分布且关系线性 → 使用皮尔逊数据有异常值或关系非线性但单调 → 使用斯皮尔曼3.2 滚动相关性分析时间序列数据的关系可能随时间变化静态的相关性分析可能掩盖这种动态特性。滚动相关性可以揭示变量关系的时变特征def rolling_correlation_analysis(data, col1, col2, window_size30): 计算并可视化滚动窗口相关性 参数: data -- 包含多列数据的DataFrame col1 -- 第一列名称 col2 -- 第二列名称 window_size -- 滚动窗口大小 # 计算滚动相关性 rolling_corr data[col1].rolling(windowwindow_size).corr(data[col2]) # 可视化结果 plt.figure(figsize(12, 6)) rolling_corr.plot() plt.title(f{col1} 与 {col2} 的{window_size}天滚动相关性) plt.xlabel(日期) plt.ylabel(相关系数) plt.axhline(y0, colorblack, linestyle-) plt.grid(True) plt.show() return rolling_corr # 执行滚动相关性分析 rolling_corr rolling_correlation_analysis(df, OT, MULL)滚动相关性分析的价值识别关系稳定性恒定高相关性 vs 波动相关性检测关系突变点相关性突然变化的时点理解长期趋势相关性整体上升或下降的趋势注意窗口大小的选择会影响结果。太小的窗口会导致噪声过多太大的窗口可能掩盖短期变化。建议尝试不同窗口大小进行比较。4. 滞后性分析理解时间延迟效应滞后性分析是时间序列建模的核心它帮助我们确定当前值与过去值之间的关系。这种分析对于构建ARIMA等预测模型至关重要。4.1 自相关与偏自相关函数我们将实现一个综合函数来绘制ACF和PACF图并解释如何利用它们确定ARIMA模型的参数def lag_analysis(series, lags40): 执行全面的滞后性分析包括ACF和PACF 参数: series -- 时间序列数据 lags -- 最大滞后阶数 # 设置绘图布局 fig, (ax1, ax2) plt.subplots(2, 1, figsize(12, 10)) # 绘制ACF图 plot_acf(series, lagslags, axax1) ax1.set_title(自相关函数(ACF)) # 绘制PACF图 plot_pacf(series, lagslags, axax2, methodywm) ax2.set_title(偏自相关函数(PACF)) plt.tight_layout() plt.show() # 计算ACF和PACF值 acf_values acf(series, nlagslags) pacf_values pacf(series, nlagslags, methodywm) return acf_values, pacf_values # 执行滞后性分析 acf_vals, pacf_vals lag_analysis(target_series)ACF和PACF图的解读指南特征可能模型参数选择ACF缓慢衰减PACF在p阶后截断AR(p)模型ARIMA(p,0,0)ACF在q阶后截断PACF缓慢衰减MA(q)模型ARIMA(0,0,q)ACF和PACF都缓慢衰减ARMA(p,q)模型需要同时考虑p和qACF和PACF都没有明显截断可能需要差分先使序列平稳4.2 交叉相关性分析当分析两个不同时间序列之间的关系时交叉相关性函数(CCF)可以帮助我们确定一个序列是否领先或滞后于另一个序列def cross_correlation_analysis(series1, series2, lags20): 计算并可视化两个时间序列的交叉相关性 参数: series1 -- 第一个时间序列 series2 -- 第二个时间序列 lags -- 正负最大滞后阶数 # 计算交叉相关性 cross_corr [series1.corr(series2.shift(lag)) for lag in range(-lags, lags1)] # 可视化结果 plt.figure(figsize(12, 6)) plt.stem(range(-lags, lags1), cross_corr, use_line_collectionTrue) plt.axhline(y0, colorblack, linestyle-) plt.axhline(y1.96/np.sqrt(len(series1)), colorgray, linestyle--) plt.axhline(y-1.96/np.sqrt(len(series1)), colorgray, linestyle--) plt.xlabel(滞后阶数) plt.ylabel(交叉相关系数) plt.title(交叉相关性分析) plt.grid(True) plt.show() # 找出最显著的滞后 max_lag np.argmax(np.abs(cross_corr)) - lags print(f最显著的滞后关系出现在滞后 {max_lag} 阶) return cross_corr # 示例分析OT列与MULL列的交叉相关性 ccf_results cross_correlation_analysis(df[OT], df[MULL])交叉相关性分析的应用场景领先-滞后关系确定哪个变量领先领先多少时间单位因果关系探索结合领域知识探索潜在的因果关系特征工程为预测模型构建滞后特征5. 趋势性与离群值分析趋势性和离群值分析是时间序列分析的最后两个关键组成部分。趋势性帮助我们理解数据的长期走向而离群值分析则识别可能影响模型性能的异常点。5.1 趋势性检测与量化我们将使用多种方法来检测和量化趋势包括移动平均、线性回归和Mann-Kendall检验def trend_analysis(series, window30): 综合趋势分析移动平均、线性回归和统计检验 参数: series -- 时间序列数据 window -- 移动平均窗口大小 # 计算移动平均 rolling_mean series.rolling(windowwindow).mean() # 线性回归拟合 X np.arange(len(series)).reshape(-1, 1) y series.values.reshape(-1, 1) model LinearRegression().fit(X, y) trend_line model.predict(X) # 可视化结果 plt.figure(figsize(12, 6)) series.plot(label原始数据, alpha0.5) rolling_mean.plot(labelf{window}天移动平均, colorred) plt.plot(series.index, trend_line, label线性趋势, colorgreen, linestyle--) plt.title(趋势性分析) plt.xlabel(日期) plt.ylabel(值) plt.legend() plt.grid(True) plt.show() # 输出趋势统计量 print(f线性趋势斜率: {model.coef_[0][0]:.4f} (正值表示上升趋势负值表示下降趋势)) print(f线性趋势截距: {model.intercept_[0]:.4f}) return { rolling_mean: rolling_mean, trend_slope: model.coef_[0][0], trend_intercept: model.intercept_[0] } # 执行趋势分析 trend_results trend_analysis(target_series)趋势分析方法比较方法优点局限性适用场景移动平均简单直观平滑噪声滞后于真实趋势窗口选择主观初步趋势识别线性回归量化趋势强度和方向假设线性关系忽略非线性趋势趋势量化评估Mann-Kendall非参数不受分布影响仅检测趋势存在性不量化趋势显著性检验5.2 离群值检测与处理离群值可能由测量错误、系统故障或罕见事件引起。我们将实现三种离群值检测方法def outlier_detection(series, z_threshold3, iqr_factor1.5): 综合离群值检测Z-score、IQR和滑动窗口方法 参数: series -- 时间序列数据 z_threshold -- Z-score阈值 iqr_factor -- IQR倍数 # 方法1Z-score检测 z_scores np.abs((series - series.mean()) / series.std()) z_outliers series[z_scores z_threshold] # 方法2IQR检测 q1 series.quantile(0.25) q3 series.quantile(0.75) iqr q3 - q1 iqr_outliers series[(series (q1 - iqr_factor * iqr)) | (series (q3 iqr_factor * iqr))] # 方法3滑动窗口检测局部离群值 rolling_mean series.rolling(window30, centerTrue).mean() rolling_std series.rolling(window30, centerTrue).std() window_outliers series[np.abs(series - rolling_mean) (2 * rolling_std)] # 可视化结果 plt.figure(figsize(12, 6)) series.plot(label原始数据, alpha0.7) if not z_outliers.empty: z_outliers.plot(markero, linestyle, colorred, labelfZ-score {z_threshold}) if not iqr_outliers.empty: iqr_outliers.plot(markers, linestyle, colorgreen, labelfIQR x {iqr_factor}) if not window_outliers.empty: window_outliers.plot(marker^, linestyle, colorpurple, label滑动窗口异常) plt.title(离群值检测) plt.xlabel(日期) plt.ylabel(值) plt.legend() plt.grid(True) plt.show() return { z_score_outliers: z_outliers, iqr_outliers: iqr_outliers, window_outliers: window_outliers } # 执行离群值检测 outliers outlier_detection(target_series)离群值处理方法比较方法处理方式适用场景注意事项删除直接移除离群点离群点明显错误且占比小可能导致信息丢失替换用均值/中位数/预测值替换想保留数据点但修正异常值可能引入偏差转换使用对数/分箱等转换长尾分布的数据改变数据分布保留不处理离群点离群点包含重要信息需使用稳健模型在实际项目中我通常会先可视化离群点结合领域知识判断其性质是数据错误还是真实异常事件再决定处理方式。对于金融数据中的黑天鹅事件或工业设备数据中的故障信号这些离群点可能恰恰是最有价值的信息。