PyPOTS: 时间序列的Python工具箱与股票市场的应用

在这里插入图片描述

前言

Hi,我是GISerLiu🙂, 这篇文章是参加2025年5月datawhale学习赛的打卡文章!💡 文中提供了对时间序列数据和PyPOTS库的全面介绍,包含图表、代码案例和个人总结,旨在帮助读者更好地理解和应用这一强大工具。


一、时间序列数据基础

1. 时间序列的定义与特点

时间序列是按照时间顺序排列的一组观测数据,每个数据点不仅包含观测值,还伴随着明确的时间标签。时间序列最核心的特征是时间依赖性,即"过去影响现在,现在影响未来"。

时间序列数据的基本特征

时间序列数据区别于传统的"独立同分布"(i.i.d.)数据,具有以下关键特征:

  1. 时间依赖性:数据点之间存在时序关联,如今天的股价受昨天股价影响
  2. 有序性:观测值必须按时间顺序排列才有意义
  3. 时间标签:每个观测值都有对应的时间戳
  4. 不均匀性:可能存在缺失、异常或不规则间隔
  5. 趋势与周期:可能包含长期趋势、季节性模式或周期性变动


图1:时间序列的基本组成部分:趋势、季节性、周期性和随机性

与传统数据的区别

2. 时间序列的分类体系

时间序列数据可以基于不同维度进行分类,下面用表格形式系统化地进行总结:

分类维度类型定义示例
变量数量单变量时间序列仅包含一个变量的时间序列单只股票的收盘价、城市日均温度
多变量时间序列同时包含多个相关变量的时间序列气象站的温度、湿度、气压数据;患者的多种生理指标
采样间隔等间隔时间序列数据点之间的时间间隔固定每小时记录的温度、每天的股票收盘价
非等间隔时间序列数据点在时间轴上分布不规律用户登录系统的时间、不规则发生的事件记录
周期性周期性时间序列数据呈现有规律的重复模式每日温度变化、每周销售周期、每年季节变化
非周期性时间序列数据不存在明显重复模式地震发生记录、突发事件数据
趋势有趋势时间序列数据整体呈现明显的上升或下降趋势人口增长数据、科技公司股价(长期)
无趋势时间序列数据在长期内没有明显增减趋势稳定环境中的一些物理测量
数据完整性完整时间序列所有时间点都有观测值完善的自动化传感器记录
部分观测时间序列(POTS)存在缺失值的时间序列医疗记录、有故障的传感器数据
可预测性确定性时间序列未来值可以通过数学公式精确计算行星运动轨迹、物理实验数据
随机时间序列包含随机成分,只能概率性预测股票价格、天气数据

3. 实际应用领域

时间序列数据在各行各业都有广泛应用。下面列举了不同领域的典型应用场景:

①金融领域
②医疗健康

③工业物联网

④环境与气象

4. 时间序列分析的主要任务

时间序列分析包括多种核心任务,每种任务有其特定的目标和方法。以下是主要任务的系统性介绍:

① 预测(Forecasting)

目标:根据历史数据预测未来值

数学定义:给定时间序列 x 1 , x 2 , . . . , x T {x_1, x_2, ..., x_T} x1,x2,...,xT,预测未来 h h h 个时间步的值 x ^ T + 1 , x ^ T + 2 , . . . , x ^ T + h \hat{x}_{T+1}, \hat{x}_{T+2}, ..., \hat{x}_{T+h} x^T+1,x^T+2,...,x^T+h

常用方法

  • 统计方法:ARIMA、指数平滑
  • 机器学习:Prophet、随机森林
  • 深度学习:LSTM、Transformer、N-BEATS

应用场景:股票价格预测、电力负荷预测、销售预测

② 分类(Classification)

目标:对整个时间序列或特定片段分配类别标签

数学定义:学习分类器 f f f 使得 f ( X ( i ) ) = y ( i ) f(X^{(i)}) = y^{(i)} f(X(i))=y(i),其中 X ( i ) X^{(i)} X(i) 是时间序列, y ( i ) y^{(i)} y(i) 是类别标签

常用方法

  • 基于距离:DTW + kNN
  • 特征提取:ROCKET、TSF
  • 深度学习:CNN、LSTM、TCN

应用场景:活动识别、心律异常检测、手势识别

③ 聚类(Clustering)

目标:将相似的时间序列归为同一组

数学定义:找出划分函数 g g g,将时间序列集合划分为 $ K $ 个簇

常用方法

  • K-Means + DTW
  • 谱聚类
  • 时间卷积聚类(TICC)
  • 深度时间聚类

应用场景:客户分组、设备运行模式识别、传感器分组

④ 异常检测(Anomaly Detection)

目标:识别时间序列中的异常点或异常片段

数学定义:输出异常得分序列 s = { s 1 , . . . , s T } s = \{s_1, ..., s_T\} s={s1,...,sT} 或标签序列 y ∈ { 0 , 1 } T y \in \{0,1\}^T y{0,1}T

常用方法

  • 基于预测:预测误差阈值法
  • 基于重构:自编码器
  • 基于密度:LOF、Isolation Forest
  • 深度学习:GAN、VAE

应用场景:网络入侵检测、设备故障预警、欺诈交易识别

⑤ 插补(Imputation)

目标:估计时间序列中缺失值的真实值

数学定义:对缺失位置集合 M M M 中的每个时间点 t t t,估计 x ^ t = f ( X o b s ) \hat{x}_t = f(X_{obs}) x^t=f(Xobs)

常用方法

  • 简单方法:前向/后向填充、线性插值
  • 统计方法:KNN、EM算法、Kalman滤波
  • 深度学习:BRITS、GRU-D、SAITS

应用场景:传感器数据修复、医疗记录补全、金融数据缺失处理

与预测的区别

  • 预测:关注未来未知数据
  • 插补:关注已有数据中的缺失部分


二、PyPOTS框架介绍

1. PyPOTS的核心理念


图3:PyPOTS框架标志

PyPOTS (Python Partially-Observed Time Series) 是一个专为处理带有缺失值的时间序列数据而设计的开源工具箱。它的核心理念可以概括为:

  1. 专注缺失值处理:针对真实世界中普遍存在的缺失值问题提供系统解决方案
  2. 全流程支持:从数据准备、模型训练到评估和应用的完整支持
  3. 算法多样性:集成传统统计方法、机器学习算法和最新深度学习模型
  4. 统一接口设计:提供一致的API,便于用户在不同算法间切换和比较
  5. 可扩展架构:支持用户自定义算法并集成到现有框架中

PyPOTS的目标用户包括:

  • 数据科学家和研究人员
  • 工业领域的工程师
  • 需要处理缺失值时间序列的分析师

2. 支持的任务与算法

PyPOTS支持时间序列分析的五大核心任务,并为每种任务提供了丰富的算法选择:

①缺失值填补 (Imputation)

PyPOTS最核心的功能,通过各种算法恢复缺失的时间序列数据点:

算法类别代表算法适用场景复杂度
传统方法前向填充(LOCF)短期缺失、计算资源有限
后向填充(NOCB)短期缺失、计算资源有限
线性插值线性变化的数据
深度学习SAITS存在长期依赖的复杂数据
BRITS需要双向信息流和因果一致性
GRU-D医疗数据等带时间戳缺失
Transformer-DA高维数据、复杂关系建模很高
概率模型BTTF需要不确定性估计中等
②预测 (Forecasting)

基于不完整时间序列预测未来数值,支持独立预测或与填补联合进行:

  • 填补后预测:两阶段方法,如SAITS+LSTM、BRITS+Transformer
  • 端到端预测:直接从缺失数据学习并预测,如缺失感知LSTM
  • 概率预测:生成预测分布而非点估计,提供不确定性量化
③分类 (Classification)

将时间序列划分为不同类别,同时处理缺失值挑战:

  • RNN分类器:基于循环神经网络架构
  • Transformer分类器:利用自注意力机制捕获长距离依赖
  • 多任务学习:联合进行填补和分类
④聚类 (Clustering)

将相似的时间序列自动分组,适应缺失值存在的情况:

  • 填补+聚类流程:先使用SAITS等填补缺失,再应用K-Means等算法
  • 表示学习:学习时间序列的低维表示,再进行聚类
  • 端到端方法:同时学习表示和聚类结构
⑤异常检测 (Anomaly Detection)

识别时间序列中的异常点或异常片段,即使存在缺失值:

  • 基于重构:使用自编码器等重构时间序列,异常点重构误差大
  • 预测偏差:预测下一时间步,观测与预测差距大的点为异常
  • 概率模型:低概率事件被视为异常

3. 生态系统组件

PyPOTS构建了一个围绕部分观测时间序列的完整生态系统,以"咖啡制作"为主题形象地命名各组件;

TSDB (Time Series Data Beans) - 时间序列数据仓库

功能:提供标准化的时间序列数据集
特点

  • 覆盖医疗、交通、金融等172种数据集
  • 一行代码即可下载加载
  • 统一格式,便于实验比较
# TSDB使用示例
from pypots.data import load_dataset
# 加载PhysioNet数据集
X, y = load_dataset("physionet2012", return_X_y=True)
PyGrinder - 缺失机制模拟器

功能:模拟不同类型的缺失模式
支持的缺失机制

  • MCAR (Missing Completely At Random)
  • MAR (Missing At Random)
  • MNAR (Missing Not At Random)
  • Block Missing (连续块缺失)
# PyGrinder使用示例
from pypots.imputation.grinder import MAR
# 创建MAR类型的缺失值
grinder = MAR(missing_rate=0.3)
X_miss = grinder.fit_transform(X)
BenchPOTS - 算法基准评估套件

功能:标准化的模型评估流程
核心组件

  • 自动划分训练/验证/测试集
  • 自动填补缺失并训练模型
  • 自动可视化结果与导出指标
# BenchPOTS使用示例
from pypots.benchmark import TSBenchmark
# 创建基准测试
benchmark = TSBenchmark(dataset="air_quality", 
                         models=["SAITS", "BRITS", "GRU-D"],
                         metrics=["MAE", "RMSE"])
results = benchmark.run()
benchmark.visualize(results)
BrewPOTS - 教程与代码示例集合

功能:提供学习资源和使用指南
内容结构

  • 安装与入门教程
  • 各类任务的完整案例
  • 真实世界应用示例
  • 在线演示与互动环境

下表总结了PyPOTS生态系统的各个组件:

组件比喻主要功能用户价值
TSDB咖啡豆数据集仓库省去数据准备时间
PyGrinder研磨机缺失模拟构建真实测试环境
BenchPOTS评分系统模型评估标准化比较流程
BrewPOTS冲泡指南教程示例降低学习门槛

三、实战案例:股市数据分析

1.环境准备

pip install pandas pypots yfinance sklearn -i https://pypi.tuna.tsinghua.edu.cn/simple

2.主要代码

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
PyPOTS股票分析演示 - 单文件版本
包含模拟股票数据生成、缺失值处理和预测功能
"""
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error, mean_absolute_error
import os

# 检查是否有PyPOTS库
try:
    import pypots
    from pypots.imputation import SAITS
    HAS_PYPOTS = True
    print(f"PyPOTS库版本: {pypots.__version__}")
except ImportError as e:
    HAS_PYPOTS = False
    print(f"PyPOTS库导入失败: {e}")
    print("将使用简化版本")


def generate_simulated_stock_data(tickers, start_date, end_date):
    """生成模拟股票数据,用于演示"""
    print("生成模拟股票数据用于演示...")
    
    # 创建日期范围
    start = pd.Timestamp(start_date)
    end = pd.Timestamp(end_date)
    date_range = pd.date_range(start=start, end=end, freq='B')  # 工作日
    
    # 创建模拟数据DataFrame
    simulated_data = pd.DataFrame(index=date_range)
    
    # 为每只股票生成模拟价格
    for ticker in tickers:
        # 基础价格和波动性
        if ticker == 'AAPL':
            base_price = 150
            volatility = 3.0
        elif ticker == 'MSFT':
            base_price = 300
            volatility = 5.0
        elif ticker == 'GOOGL':
            base_price = 130
            volatility = 4.0
        elif ticker == 'AMZN':
            base_price = 120
            volatility = 3.5
        elif ticker == 'META':
            base_price = 250
            volatility = 6.0
        else:
            base_price = 100
            volatility = 2.0
        
        # 模拟价格时间序列
        prices = [base_price]
        for i in range(1, len(date_range)):
            # 随机步 + 趋势 + 季节性
            random_step = np.random.normal(0, volatility)
            trend = 0.02 * (i / len(date_range))  # 微弱上升趋势
            seasonal = 5 * np.sin(2 * np.pi * i / 63)  # ~3个月周期
            
            new_price = max(prices[-1] + random_step + trend + seasonal, 10)  # 确保价格为正
            prices.append(new_price)
        
        simulated_data[ticker] = prices
    
    # 添加一些相关性
    for i in range(len(tickers)-1):
        for j in range(i+1, len(tickers)):
            # 添加市场共同影响
            market_move = np.random.normal(0, 1, len(date_range))
            simulated_data[tickers[i]] += market_move * volatility * 0.3
            simulated_data[tickers[j]] += market_move * volatility * 0.3
    
    print("模拟数据生成完成")
    return simulated_data


# 引入缺失值
def introduce_missing_values(data, missing_rate=0.2):
    """在数据中引入随机缺失值"""
    # 转换为3D数组 (1, 时间步, 特征)
    X = data.values.reshape(1, data.shape[0], data.shape[1])
    
    # 生成随机掩码
    mask = np.random.random(X.shape) < missing_rate
    X_missing = X.copy()
    X_missing[mask] = np.nan
    
    # 转回DataFrame
    missing_df = pd.DataFrame(
        X_missing[0], 
        index=data.index, 
        columns=data.columns
    )
    
    return missing_df, X


# 简化版本的预测器
class SimpleForecaster:
    """简单的预测模型"""
    
    def __init__(self, n_steps, n_features, n_pred_steps):
        self.n_steps = n_steps
        self.n_features = n_features
        self.n_pred_steps = n_pred_steps
    
    def fit(self, X_train, y_train):
        print(f"训练预测模型 (样本: {X_train.shape[0]})")
        # 简单模型,不需要实际训练
        return self
    
    def predict(self, X_test):
        print(f"预测未来值 (样本: {X_test.shape[0]})")
        batch_size = X_test.shape[0]
        predictions = np.zeros((batch_size, self.n_pred_steps, self.n_features))
        
        # 每个样本的预测:最后值+小趋势变化
        for i in range(batch_size):
            last_values = X_test[i, -1, :]
            for j in range(self.n_pred_steps):
                factor = (j + 1) / self.n_pred_steps
                random_change = np.random.normal(0, 0.01, self.n_features)
                trend = np.random.normal(0, 0.02, self.n_features) * factor
                predictions[i, j, :] = last_values + trend + random_change
        
        return predictions


# 主要分析函数
def analyze_stock_data(tickers=None, start_date=None, end_date=None, missing_rate=0.2):
    """主要分析函数"""
    if tickers is None:
        tickers = ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'META']
    
    if start_date is None:
        start_date = '2022-01-01'
    
    if end_date is None:
        end_date = '2024-01-01'
    
    # 创建输出目录
    output_dir = f"股票分析_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
    os.makedirs(output_dir, exist_ok=True)
    print(f"输出将保存到: {output_dir}")
    
    # =========================
    # 1. 加载原始数据
    # =========================
    print(f"正在生成股票数据: {tickers}")
    close_prices = generate_simulated_stock_data(tickers, start_date, end_date)
    
    # 可视化原始数据
    plt.figure(figsize=(12, 6))
    for ticker in tickers:
        plt.plot(close_prices.index, close_prices[ticker], label=ticker)
    plt.title('股票收盘价')
    plt.xlabel('日期')
    plt.ylabel('价格 (USD)')
    plt.legend()
    plt.grid(True)
    plt.savefig(f"{output_dir}/原始数据.png")
    plt.close()
    
    # =========================
    # 2. 引入缺失值
    # =========================
    X_miss_df, X = introduce_missing_values(close_prices, missing_rate)
    
    # 可视化缺失数据 (只显示最后2个月)
    time_slice = slice(close_prices.index[-1] - pd.Timedelta(days=60), close_prices.index[-1])
    
    plt.figure(figsize=(14, 10))
    for i, ticker in enumerate(tickers):
        plt.subplot(len(tickers), 1, i+1)
        plt.plot(close_prices.loc[time_slice, ticker], 'b-', label='原始数据')
        plt.plot(X_miss_df.loc[time_slice, ticker], 'r.', label='缺失数据')
        plt.title(f'{ticker} 价格')
        plt.legend()
        plt.grid(True)
    plt.tight_layout()
    plt.savefig(f"{output_dir}/缺失数据对比.png")
    plt.close()
    
    # =========================
    # 3. 填补缺失值
    # =========================
    print("\n开始填补缺失值...")
    
    if HAS_PYPOTS:
        # 使用PyPOTS的SAITS进行填补
        try:
            X_miss = np.array([X_miss_df.values]).transpose(0, 2, 1)
            
            # 准备PyPOTS输入格式(字典格式)
            train_data = {'X': X_miss}
            
            saits = SAITS(
                n_steps=X_miss.shape[1],
                n_features=X_miss.shape[2],
                n_layers=2,
                d_model=64,
                d_ffn=128,
                n_heads=4,
                d_k=32,   # 添加必需参数d_k
                d_v=32,   # 添加必需参数d_v
                dropout=0.1,
                batch_size=8,
                epochs=50,
                patience=5
            )
            
            # 训练填补模型
            print("训练SAITS填补模型...")
            saits.fit(train_data)
            
            # 填补缺失值
            print("使用SAITS填补缺失值...")
            imputed_results = saits.predict(train_data)
            
            # 从结果中提取填补数据
            if 'imputation' in imputed_results:
                imputed_X = imputed_results['imputation']
            else:
                # 如果没有'imputation'键,尝试直接使用结果
                imputed_X = imputed_results
            
            # 转换回DataFrame
            imputed_df = pd.DataFrame(
                imputed_X[0].transpose(),
                index=close_prices.index,
                columns=close_prices.columns
            )
            print("使用SAITS成功填补缺失值")
        except Exception as e:
            print(f"PyPOTS填补失败: {e}")
            print("回退到简单填补方法")
            imputed_df = X_miss_df.fillna(method='ffill').fillna(method='bfill')
    else:
        # 简单的前向填充
        imputed_df = X_miss_df.fillna(method='ffill').fillna(method='bfill')
        print("使用前向填充和后向填充代替SAITS")
    
    # 可视化填补结果
    plt.figure(figsize=(14, 10))
    for i, ticker in enumerate(tickers):
        plt.subplot(len(tickers), 1, i+1)
        plt.plot(close_prices.loc[time_slice, ticker], 'b-', 
                label='原始数据', alpha=0.7)
        plt.plot(X_miss_df.loc[time_slice, ticker], 'r.', 
                label='缺失数据')
        
        # 填补的数据点(只在缺失位置)
        missing_mask = X_miss_df.loc[time_slice, ticker].isna()
        plt.plot(imputed_df.loc[time_slice, ticker][missing_mask], 'go', 
                label='填补值')
        
        plt.title(f'{ticker} 填补结果')
        plt.legend()
        plt.grid(True)
    plt.tight_layout()
    plt.savefig(f"{output_dir}/填补结果.png")
    plt.close()
    
    # =========================
    # 4. 预测未来值
    # =========================
    print("\n开始预测未来值...")
    
    # 数据准备
    n_lookback = 30  # 使用过去30天
    n_forecast = 5   # 预测未来5天
    
    # 归一化数据
    scaler = MinMaxScaler()
    scaled_data = scaler.fit_transform(imputed_df)
    
    # 创建序列
    X_seq = []
    y_seq = []
    
    for i in range(len(scaled_data) - n_lookback - n_forecast):
        X_seq.append(scaled_data[i:i+n_lookback])
        y_seq.append(scaled_data[i+n_lookback:i+n_lookback+n_forecast])
    
    X_seq = np.array(X_seq)
    y_seq = np.array(y_seq)
    
    # 分割训练集和测试集
    train_size = int(len(X_seq) * 0.8)
    X_train, X_test = X_seq[:train_size], X_seq[train_size:]
    y_train, y_test = y_seq[:train_size], y_seq[train_size:]
    
    # 使用简化预测模型
    print("使用简化预测模型")
    forecaster = SimpleForecaster(
        n_steps=n_lookback, 
        n_features=X_seq.shape[2], 
        n_pred_steps=n_forecast
    )
    
    forecaster.fit(X_train, y_train)
    
    # 预测未来值
    y_pred = forecaster.predict(X_test)
    
    # 评估预测结果
    test_rmse = np.sqrt(mean_squared_error(y_test.reshape(-1), y_pred.reshape(-1)))
    test_mae = mean_absolute_error(y_test.reshape(-1), y_pred.reshape(-1))
    
    print(f"预测结果评估:")
    print(f"RMSE: {test_rmse:.4f}")
    print(f"MAE: {test_mae:.4f}")
    
    # 可视化预测结果
    sample_idx = 10  # 选择第10个测试样本
    
    # 获取样本数据
    input_seq = X_test[sample_idx]
    true_future = y_test[sample_idx]
    pred_future = y_pred[sample_idx]
    
    # 转回原始比例
    input_seq_orig = scaler.inverse_transform(input_seq)
    true_future_orig = scaler.inverse_transform(true_future)
    pred_future_orig = scaler.inverse_transform(pred_future)
    
    # 创建时间轴
    last_date = imputed_df.index[train_size + sample_idx + n_lookback - 1]
    input_dates = pd.date_range(end=last_date, periods=n_lookback)
    future_dates = pd.date_range(start=input_dates[-1] + pd.Timedelta(days=1), periods=n_forecast)
    
    # 可视化
    plt.figure(figsize=(15, 10))
    for i, ticker in enumerate(tickers):
        plt.subplot(len(tickers), 1, i+1)
        
        # 输入序列
        plt.plot(input_dates, input_seq_orig[:, i], 'b-', label='历史数据')
        
        # 真实未来
        plt.plot(future_dates, true_future_orig[:, i], 'g-', label='真实未来')
        
        # 预测未来
        plt.plot(future_dates, pred_future_orig[:, i], 'r--', label='预测未来')
        
        # 添加预测起点的垂直线
        plt.axvline(x=input_dates[-1], color='k', linestyle='--')
        
        plt.title(f'{ticker} 预测结果')
        plt.legend()
        plt.grid(True)
    plt.tight_layout()
    plt.savefig(f"{output_dir}/预测结果.png")
    plt.close()
    
    # =========================
    # 5. 计算每只股票的预测误差
    # =========================
    ticker_mae = {}
    ticker_rmse = {}
    
    for i, ticker in enumerate(tickers):
        # 该股票的所有测试样本
        y_true = y_test[:, :, i].flatten()
        y_predicted = y_pred[:, :, i].flatten()
        
        # 转回原始比例
        temp_true = np.zeros((len(y_true), len(tickers)))
        temp_pred = np.zeros((len(y_true), len(tickers)))
        temp_true[:, i] = y_true
        temp_pred[:, i] = y_predicted
        
        true_orig = scaler.inverse_transform(temp_true)[:, i]
        pred_orig = scaler.inverse_transform(temp_pred)[:, i]
        
        # 计算误差
        ticker_mae[ticker] = mean_absolute_error(true_orig, pred_orig)
        ticker_rmse[ticker] = np.sqrt(mean_squared_error(true_orig, pred_orig))
    
    # 可视化每只股票的误差
    plt.figure(figsize=(12, 6))
    
    tickers_list = list(ticker_mae.keys())
    mae_values = list(ticker_mae.values())
    rmse_values = list(ticker_rmse.values())
    
    x = np.arange(len(tickers_list))
    width = 0.35
    
    plt.bar(x - width/2, mae_values, width, label='MAE')
    plt.bar(x + width/2, rmse_values, width, label='RMSE')
    
    plt.xlabel('股票')
    plt.ylabel('误差 (USD)')
    plt.title('各股票预测误差')
    plt.xticks(x, tickers_list)
    plt.legend()
    plt.grid(True, axis='y')
    
    plt.tight_layout()
    plt.savefig(f"{output_dir}/预测误差.png")
    plt.close()
    
    # =========================
    # 6. 分析结论
    # =========================
    conclusions = [
        "1. 预测性能因股票而异,这可能与股票特性有关。",
        f"2. 所有股票的平均预测RMSE为: {np.mean(list(ticker_rmse.values())):.2f} USD",
        "3. PyPOTS的SAITS模型成功用于填补时间序列中的缺失值。",
        "4. 简化的预测模型可以作为基线,未来可以尝试使用PyPOTS的其他模型。"
    ]
    
    print("\n分析结论:")
    for conclusion in conclusions:
        print(conclusion)
    
    # 保存结论
    with open(f"{output_dir}/分析结论.txt", "w", encoding="utf-8") as f:
        f.write("股票分析结论\n")
        f.write("="*20 + "\n\n")
        for conclusion in conclusions:
            f.write(f"{conclusion}\n")
    
    print(f"\n分析完成! 所有结果已保存到 {output_dir} 目录")


if __name__ == "__main__":
    import argparse
    
    parser = argparse.ArgumentParser(description="股票数据分析演示")
    parser.add_argument("--tickers", nargs="+", default=['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'META'],
                       help="股票代码列表")
    parser.add_argument("--start-date", default="2022-01-01", help="开始日期 (YYYY-MM-DD)")
    parser.add_argument("--end-date", default="2024-01-01", help="结束日期 (YYYY-MM-DD)")
    parser.add_argument("--missing-rate", type=float, default=0.2, 
                       help="人为引入的缺失率")
    
    args = parser.parse_args()
    print(f"启动分析,参数: 股票={args.tickers}, 开始日期={args.start_date}, 结束日期={args.end_date}, 缺失率={args.missing_rate}")
    
    try:
        analyze_stock_data(args.tickers, args.start_date, args.end_date, args.missing_rate)
        print("分析完成!")
    except Exception as e:
        import traceback
        print(f"分析过程中出错: {e}")
        traceback.print_exc()
        print("详细错误信息如上,请检查配置和代码。")

这里的数据是模拟的

结果如下:

缺失数据对比

填补结果

预测结果

预测误差

原始数据:

分析结论:

股票分析结论

====================

  1. 预测性能因股票而异,这可能与股票特性有关。

  2. 所有股票的平均预测RMSE为: 60.87 USD

  3. PyPOTS的SAITS模型成功用于填补时间序列中的缺失值。

  4. 简化的预测模型可以作为基线,未来可以尝试使用PyPOTS的其他模型。

总结

PyPOTS为处理部分观测时间序列提供了强大而灵活的解决方案。通过本笔记的学习,我们不仅理解了时间序列数据的基本概念和分类,还深入探索了PyPOTS框架的核心组件和功能。希望本笔记能够帮助佬儿们快速入门并熟练使用PyPOTS;


文章参考

项目地址


thank_watch

如果觉得我的文章对您有帮助,三连+关注便是对我创作的最大鼓励!或者一个star🌟也可以😂.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GISer Liu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值