分析时间序列数据
使用 Python 进行 AI – 时间序列数据分析
Section titled “使用 Python 进行 AI – 时间序列数据分析”在给定的输入序列中预测下一个值或状态是机器学习中的一个基本概念。本章详细解释了如何分析时间序列数据,这是一种常见的序列数据类型。
时间序列数据由按时间顺序索引的一系列数据点组成,通常以均匀的时间间隔出现。在机器学习中构建序列预测模型时,我们经常处理序列数据,而时间序列数据就是一个突出的例子。数据的排序是序列数据的一个关键特征,它将其与其他类型的数据集区分开来。
序列分析或时间序列分析的基本概念
Section titled “序列分析或时间序列分析的基本概念”序列分析,或者在此上下文中特指时间序列分析,旨在基于先前观察到的值来预测序列中的未来值。预测可以针对各种类型的序列数据:下一个数值、一个类别符号、明天的天气、句子中的下一个词等。序列分析在股票市场预测、天气预测、需求预测和异常检测等应用中具有很高的价值。
示例
考虑一个简单的值序列:A, B, C, D。序列预测模型会分析这种模式来预测下一个值 E。
这可以可视化为:[A, B, C, D] -> Sequence Prediction Model -> [E]
安装有用的软件包
Section titled “安装有用的软件包”对于使用 Python 进行时间序列数据分析,我们将主要使用以下软件包。请确保您已在 Python 环境中安装了它们。
Pandas
Section titled “Pandas”Pandas 是一个开源库,为 Python 提供了高性能、易于使用的数据结构和数据分析工具。它对于处理时间序列数据至关重要。您可以使用 pip 安装 Pandas:
pip install pandas如果您使用的是 Anaconda,可以使用 conda 安装:
conda install pandasNumPy 是 Python 中进行数值计算的基础软件包。Pandas 构建在 NumPy 之上。它通常作为 Pandas 的依赖项安装。如果尚未安装,请使用 pip 安装:
pip install numpyMatplotlib
Section titled “Matplotlib”Matplotlib 是一个全面的库,用于在 Python 中创建静态、动态和交互式可视化图表。它对于绘制时间序列数据至关重要。使用 pip 安装:
pip install matplotlibhmmlearn (用于使用隐马尔可夫模型 (HMMs) 的特定序列建模任务)
Section titled “hmmlearn (用于使用隐马尔可夫模型 (HMMs) 的特定序列建模任务)”对于使用隐马尔可夫模型 (HMMs) 的特定序列建模任务,hmmlearn 库非常有用。它提供了用于 HMMs 的算法和模型。您可以使用 pip 安装它:
pip install hmmlearn如果您使用的是 Anaconda:
conda install -c conda-forge hmmlearnyfinance (用于股票数据示例)
Section titled “yfinance (用于股票数据示例)”为了获取我们某个示例所需的金融数据,yfinance 库非常方便。使用 pip 安装它:
pip install yfinance注意:其他库如 statsmodels (用于 ARIMA、SARIMA 等经典时间序列模型) 以及深度学习框架 (TensorFlow、PyTorch) 也被广泛用于高级时间序列分析,但这超出了本节侧重于 Pandas 和 HMMs 的入门范围。
Pandas:处理、切片和提取时间序列数据中的统计信息
Section titled “Pandas:处理、切片和提取时间序列数据中的统计信息”Pandas 是在 Python 中处理时间序列数据的必备工具。主要功能包括:
- 使用
pd.date_range()创建日期范围。 - 使用
datetime对象为 Pandas Series 或 DataFrames 建立索引。 - 使用
resample()将时间序列数据重新采样到不同的频率。 - 执行基于时间的切片和选择。
以下示例演示了如何使用 Pandas 处理和切片时间序列数据。我们将使用月度北极涛动 (AO) 数据,这些数据通常可以从 NOAA 等来源下载。对于本示例,假设您已将数据下载到名为 AO_monthly.txt 的文本文件中。该文件应包含多列数据,其中一列(例如第三列)包含 AO 指数值。
您可以在美国国家海洋和大气管理局 (NOAA) 气候预测中心等来源找到此类数据。例如,搜索 ‘monthly.ao.index.b50.current.ascii’。确保数据保存为纯文本文件,其中值使用空格或制表符分隔。
处理时间序列数据
Section titled “处理时间序列数据”请按照以下步骤加载和处理时间序列数据:
- 导入必要的软件包:
import numpy as npimport pandas as pdimport matplotlib.pyplot as plt- 定义一个函数来从输入文件读取数据。此函数假设相关数据位于第三列(索引 2)。
def read_data(input_file, column_index=2): # 加载数据,假设使用空格或制表符作为分隔符 input_data = np.loadtxt(input_file, delimiter=None)
# 创建日期范围。假设数据从 1950 年 1 月开始,频率为月度。 # 根据您的数据集具体情况调整 '1950-01' 和 freq。 num_periods = input_data.shape[0] dates = pd.date_range('1950-01-01', periods=num_periods, freq='MS') # MS for Month Start
# 使用日期范围作为索引创建 Pandas Series # 确保所选列存在并包含数值数据 time_series_data = pd.Series(input_data[:, column_index], index=dates) return time_series_data- 指定输入文件路径并读取数据:
# 替换为您的数据文件的实际路径input_file = "AO_monthly.txt"
# 假设您的数据在文件中,且 AO 指数在第三列(索引 2)# 如果您的文件结构不同,请调整 read_data 中的 column_index 或函数本身try: ao_timeseries = read_data(input_file, column_index=2)except FileNotFoundError: print(f"错误:未找到文件 {input_file}。请检查路径。") # 作为演示的回退方案,如果未找到文件,则创建一些虚拟数据 print("使用虚拟数据进行演示。") dates_dummy = pd.date_range('1950-01-01', periods=100, freq='MS') data_dummy = np.random.randn(100) ao_timeseries = pd.Series(data_dummy, index=dates_dummy)except IndexError: print(f"错误:无法从列中提取数据。请检查文件格式和 column_index。") print("使用虚拟数据进行演示。") dates_dummy = pd.date_range('1950-01-01', periods=100, freq='MS') data_dummy = np.random.randn(100) ao_timeseries = pd.Series(data_dummy, index=dates_dummy)- 绘制数据并进行可视化:
plt.figure(figsize=(10, 6))ao_timeseries.plot()plt.title('月度北极涛动指数')plt.xlabel('日期')plt.ylabel('AO 指数')plt.grid(True)plt.show()这将显示北极涛动指数随时间变化的图表。x 轴代表日期,y 轴代表 AO 指数值,显示波动和趋势。
切片时间序列数据
Section titled “切片时间序列数据”切片允许您选择时间序列的特定部分。例如,要仅查看 1980 年到 1990 年的数据:
# 确保 ao_timeseries 已加载且不为空if not ao_timeseries.empty: try: ao_subset = ao_timeseries['1980':'1990'] plt.figure(figsize=(10, 6)) ao_subset.plot() plt.title('月度 AO 指数 (1980-1990)') plt.xlabel('日期') plt.ylabel('AO 指数') plt.grid(True) plt.show() except Exception as e: print(f"切片或绘图时出错: {e}")else: print("时间序列数据为空,跳过切片示例。")此代码片段将生成一个图表,重点显示严格在 1980 年初到 1990 年底之间的 AO 指数值。
从时间序列数据中提取统计信息
Section titled “从时间序列数据中提取统计信息”Pandas 可以轻松地从时间序列数据中提取常见的描述性统计信息,这对于理解其特征至关重要。
假设 ao_timeseries 是您已加载的 Pandas Series:
计算平均值:
if not ao_timeseries.empty: mean_value = ao_timeseries.mean() print(f"平均 AO 指数: {mean_value:.6f}")else: print("时间序列数据为空。")示例输出(将根据数据而异):
Mean AO Index: -0.111431Maximum
Section titled “Maximum”找到最大值:
if not ao_timeseries.empty: max_value = ao_timeseries.max() print(f"最大 AO 指数: {max_value:.6f}")else: print("时间序列数据为空。")示例输出:
Maximum AO Index: 3.495300Minimum
Section titled “Minimum”找到最小值:
if not ao_timeseries.empty: min_value = ao_timeseries.min() print(f"最小 AO 指数: {min_value:.6f}")else: print("时间序列数据为空。")示例输出:
Minimum AO Index: -4.265700一次性获取所有描述性统计信息
Section titled “一次性获取所有描述性统计信息”describe() 方法提供统计信息的摘要:
if not ao_timeseries.empty: desc_stats = ao_timeseries.describe() print("\n描述性统计信息:\n", desc_stats)else: print("时间序列数据为空。")示例输出:
Descriptive Statistics:count 817.000000mean -0.111431std 1.003151min -4.26570025% -0.64943050% -0.04274475% 0.475720max 3.495300dtype: float64Resampling
Section titled “Resampling”重新采样(Resampling)涉及改变时间序列数据的频率。这对于聚合(降采样,downsampling)或插值(升采样,upsampling)很有用。常见的参数是目标频率和聚合方法。
- 时间段(例如,‘YE’ 表示年末,‘QE’ 表示季末,‘D’ 表示天)。
- 聚合方法(例如,
mean()、median()、sum())。
使用平均值重新采样为年度频率
Section titled “使用平均值重新采样为年度频率”要使用每年内数值的平均值将月度数据重新采样为年度数据:
if not ao_timeseries.empty: # 'YE' 表示年末频率。Pandas 也接受 'A'。 ao_annual_mean = ao_timeseries.resample('YE').mean() plt.figure(figsize=(10, 6)) ao_annual_mean.plot(style='g--') plt.title('年度平均 AO 指数') plt.xlabel('年份') plt.ylabel('平均 AO 指数') plt.grid(True) plt.show()else: print("时间序列数据为空,跳过重新采样示例。")这将绘制年度平均 AO 指数,与月度数据相比,显示出更平滑的趋势。
使用中位数重新采样为年度频率
Section titled “使用中位数重新采样为年度频率”类似地,使用中位数进行聚合:
if not ao_timeseries.empty: ao_annual_median = ao_timeseries.resample('YE').median() plt.figure(figsize=(10, 6)) ao_annual_median.plot() plt.title('年度中位数 AO 指数') plt.xlabel('年份') plt.ylabel('中位数 AO 指数') plt.grid(True) plt.show()else: print("时间序列数据为空,跳过重新采样示例。")此图显示了每年的中位数 AO 指数。
滚动平均 (移动平均)
Section titled “滚动平均 (移动平均)”滚动平均(rolling mean)计算固定大小的滑动窗口内的平均值。这常用于平滑短期波动并突出长期趋势。
if not ao_timeseries.empty: # 计算 12 个月滚动平均 ao_rolling_mean_12m = ao_timeseries.rolling(window=12, center=False).mean() plt.figure(figsize=(10, 6)) ao_rolling_mean_12m.plot(style='-r') plt.title('12 个月滚动平均 AO 指数') plt.xlabel('日期') plt.ylabel('AO 指数') plt.grid(True) plt.show()else: print("时间序列数据为空,跳过滚动平均示例。")该图将显示 AO 指数的 12 个月移动平均,这有助于比原始月度数据更清晰地识别趋势。
使用隐马尔可夫模型 (HMM) 分析序列数据
Section titled “使用隐马尔可夫模型 (HMM) 分析序列数据”隐马尔可夫模型 (HMMs) 是一种广泛用于时间序列和序列数据的统计模型,其中系统被假定为一个具有未观测(隐藏)状态的马尔可夫过程。应用领域包括语音识别、生物信息学、金融建模等。
理解隐马尔可夫模型 (HMM)
Section titled “理解隐马尔可夫模型 (HMM)”HMM 是一个建立在马尔可夫链概念上的随机模型。其核心假设是,转移到未来状态的概率仅取决于当前状态,而不取决于先前的事件序列(即马尔可夫性质)。“隐藏”方面是指我们不能直接观察到状态本身,而是观察到一些在概率上依赖于当前隐藏状态的输出或观测值。
例如,在天气建模中,隐藏状态可能是“晴天”、“多云”、“下雨”,而观测值可能是“穿外套”、“带雨伞”、“戴太阳镜”。我们通过观察衣着选择来推断可能的天气状态。
在数学上,一个 HMM 由以下要素定义:
状态 (S)
Section titled “状态 (S)”一组 N 个隐藏或潜在状态:S = {s₁, s₂, …, sN}。
输出符号 / 观测值 (O)
Section titled “输出符号 / 观测值 (O)”一组 M 个可能的输出符号或观测值:O = {o₁, o₂, …, oM}。
状态转移概率矩阵 (A)
Section titled “状态转移概率矩阵 (A)”一个 N x N 矩阵,其中 Aᵢⱼ 是从状态 sᵢ 转移到状态 sⱼ 的概率。
观测发射概率矩阵 (B)
Section titled “观测发射概率矩阵 (B)”一个 N x M 矩阵,其中 Bⱼ(k) 是当系统处于状态 sⱼ 时观测到符号 oₖ 的概率。
初始状态概率分布 (Π)
Section titled “初始状态概率分布 (Π)”一个 N 维向量,其中 Πᵢ 是初始状态为 sᵢ 的概率。
因此,假设已知 S 和 O,HMM 由参数 λ = (A, B, Π) 定义。
示例:使用 HMM 分析股票市场数据
Section titled “示例:使用 HMM 分析股票市场数据”在此示例中,我们将分析历史股票市场数据,使用 HMM 对潜在的底层市场状态(例如高波动性、低波动性)进行建模。我们将使用 hmmlearn 库实现 HMM,并使用 yfinance 获取股票数据。
- 导入必要的软件包:
import numpy as npimport pandas as pdimport matplotlib.pyplot as pltimport yfinance as yffrom hmmlearn.hmm import GaussianHMMimport warningsimport datetime- 加载股票数据。我们将以英特尔 (INTC) 的数据为例进行获取。
start_date_str = '2010-01-01'end_date_str = '2020-12-31'stock_ticker = 'INTC'
# 使用 yfinance 获取数据try: stock_data = yf.download(stock_ticker, start=start_date_str, end=end_date_str) if stock_data.empty: raise ValueError("未获取到数据。请检查股票代码或日期范围。")except Exception as e: print(f"获取 {stock_ticker} 数据时出错: {e}") # 如果获取失败,则回退到使用虚拟数据 print("使用虚拟数据进行 HMM 示例演示。") dates_dummy = pd.date_range(start_date_str, end=end_date_str, freq='B') # 工作日 dummy_close = np.random.rand(len(dates_dummy)) * 100 + 50 dummy_volume = np.random.rand(len(dates_dummy)) * 1e6 + 1e5 stock_data = pd.DataFrame({'Close': dummy_close, 'Volume': dummy_volume}, index=dates_dummy)
closing_prices = stock_data['Close']volumes = stock_data['Volume']- 准备 HMM 所需的特征。我们将使用收盘价和成交量的每日百分比变化。
# 计算收盘价的每日百分比差# 确保 closing_prices 是 Pandas Series 或 NumPy arraydiff_percentages = 100.0 * closing_prices.pct_change().fillna(0) # 对于第一个值使用 fillna(0)
# 我们需要将 volumes 与 diff_percentages 对齐。# pct_change() 会使第一个值为 NaN,我们用 0 填充了它。# 确保 volumes 对齐,如果处理后长度不同,则进行调整。# 为了简单起见,确保两个特征在 pct_change 后从同一点开始。if len(diff_percentages) == len(volumes): aligned_volumes = volumeselif len(diff_percentages) == len(volumes) -1 : # 如果 pct_change 少一个值 aligned_volumes = volumes[1:]else: # 如果对齐复杂,则回退或报错 aligned_volumes = volumes[-len(diff_percentages):] # 一种常见的对齐方式
# 创建训练数据:特征作为列# 使用成交量的对数有时有助于稳定方差training_data = np.column_stack([diff_percentages, np.log(aligned_volumes + 1e-6)]) # 添加一个小的常数以避免 log(0)- 创建并训练高斯 HMM。
n_components是隐藏状态(或称为状态/机制,regimes)的数量。
# 假设有 4 个隐藏的市场状态(例如,低波动、中波动、高波动上涨、高波动下跌)num_hidden_states = 4# 'diag' 协方差意味着在每个状态内特征是独立的hmm_model = GaussianHMM(n_components=num_hidden_states, covariance_type='diag', n_iter=1000, random_state=42)
print("正在训练 HMM...")with warnings.catch_warnings(): warnings.simplefilter('ignore') # HMM 有时可能引发收敛警告 hmm_model.fit(training_data)print("HMM 训练完成。")- 预测训练数据的隐藏状态并进行可视化。
hidden_states = hmm_model.predict(training_data)
plt.figure(figsize=(15, 8))for i in range(num_hidden_states): # 绘制属于每个隐藏状态的数据点 mask = hidden_states == i plt.plot(stock_data.index[mask], closing_prices[mask], '.', label=f'State {i}', alpha=0.6)
plt.title(f'{stock_ticker} HMM 隐藏状态下的收盘价')plt.xlabel('日期')plt.ylabel('收盘价')plt.legend()plt.grid(True)plt.show()
# 将隐藏状态随时间变化绘制成序列plt.figure(figsize=(15, 4))plt.plot(stock_data.index, hidden_states, drawstyle='steps-post')plt.title(f'{stock_ticker} HMM 隐藏状态序列')plt.xlabel('日期')plt.ylabel('隐藏状态')plt.yticks(range(num_hidden_states))plt.grid(True)plt.show()第一个图表显示了根据 HMM 推断的隐藏状态进行颜色编码的收盘价。不同的状态可能对应不同的市场行为(例如,稳定期、波动期、上涨或下跌趋势)。第二个图表显示了这些隐藏状态随时间变化的序列。
注意:解释 HMM 状态的含义需要领域知识,并对每个状态的特征(特征的均值、方差)进行进一步分析。
时间序列分析是一个广阔的领域。对于更高级的主题,可以考虑探索:
- 经典模型:使用
statsmodels库的 ARIMA、SARIMA、指数平滑 (ETS) 模型。 - 状态空间模型:卡尔曼滤波器 (Kalman filters)、不可观测成分模型 (Unobserved Components Models)。
- 时间序列的机器学习:使用具有滞后特征的回归模型、树模型。
- 时间序列的深度学习:使用 TensorFlow 或 PyTorch 等库的循环神经网络 (RNNs, LSTMs, GRUs)、Transformers 模型。
- 时间序列交叉验证:scikit-learn 中的
TimeSeriesSplit等技术。