STAR Decomposition 一种针对极端事件的信号分解方法 论文精读加复现

news2025/3/16 4:19:10

STAR 分解🚀


       在时序预测任务中,为了情绪化信号的各种成分,例如趋势信息季节信息等往往都需要对信号进行分解。目前熟知的分解方式有很多种,经验模态分解 EMD 变分模态分解 VMD ,还有 集合经验模态分解 EEMD,自适应噪声完备集合经验模态分解 CEEMDAN 等等,但这些和小波变换分解类似,一般都不会针对异常信号和极端事件进行分解。STAR 是一个比较冷门的分解方式,是我在Paper with Code 淘宝的时候发现的,他来源于 2022 年的 一篇叫 AA-Forecast: Anomaly-Aware Forecast for Extreme Events。中提出的,个人认为这块是一个比较容易拼出一个创新点的内容。
在这里插入图片描述

文章目录

  • STAR 分解🚀
  • 1.STAR Decomposition 论文精读部分
  • 2.STAR 代码复现 手搓版
    • 2.1 数据读取
    • 2.2 提取趋势成分
    • 2.3 提取季节成分
    • 2.4 提取残差成分
    • 2.5 提取异常成分
  • 3.项目 GitHub 地址
  • 4.结束

       根据论文结构图,整个论文是分 STAR 分解,异常感知模型 Anomaly-Aware Model ,动态不确定性优化 Dynamic Uncertainty Optimization 三个部分,这里就先值关注第一个 STAR 分解的部分。
在这里插入图片描述

1.STAR Decomposition 论文精读部分


       对于这个分解方法的描述写在了论文的 3.1 部分,也不是很长,但我也是整体精读了下来,也从 Paper with Code 的官方链接上下载了代码进行对照,可以说如果其实有一些核心的地方是没有讲清楚的,所以导致看上去挺不错的东西,没啥人用,接下来就直接对 3.1 的部分精读一下,翻译的话是结合 DeepSeek 初步翻译,然后加上我自己的理解精调的。

在这里插入图片描述

  • STAR decomposition is used as a strategy to not only extract the anomalies and sudden changes of data but also decompose the complex time series to its essential components.
    STAR分解被用作一种策略,不仅用于提取数据的异常值和突变,而且还用于将复杂的时间序列分解为其基本组成部分。

  • Unfortunately, widely popular decomposition method such as STL [14] does not extract anomalies.
    不幸的是,广泛使用的 STL 方法不适用于去提取异常值

STL 信号分解方法即基于局部加权回归的季节性趋势分解法(Seasonal and Trend decomposition using Loess)。

  • Although recent works such as STR [15] and RobustSTL [16] are designed to be robust to the extreme effect of anomalies in their decomposition, they are not used to explicitly extract anomalies from the residual component.
    尽管最近的工作,如STR [15] 和 RobustSTL [16] 被设计为在其分解过程中对异常的极端效应具有鲁棒性,但它们并未被用来明确地从残差分量中提取异常。

  • To alleviate these issues, we propose STAR decomposition that are decomposes the original time series x ( k ) x^{(k)} x(k) in multiplicative manner to its seasonal s ( k ) s^{(k)} s(k) ,trend t ( k ) t^{(k)} t(k),anomalies a ( k ) a^{(k)} a(k), and residual r ( k ) r^{(k)} r(k) components:
    为了解决这个问题我们提出了 STAR 分解 ,将原始信号 x ( k ) x^{(k)} x(k) 分解为 季节成分 s ( k ) s^{(k)} s(k) ,趋势成分 t ( k ) t^{(k)} t(k),异常成分 a ( k ) a^{(k)} a(k),残差成分 r ( k ) r^{(k)} r(k) 的相乘。
    x ( k ) = s ( k ) × t ( k ) × a ( k ) × r ( k ) x^{(k)} = s^{(k)}×t^{(k)}×a^{(k)}×r^{(k)} x(k)=s(k)×t(k)×a(k)×r(k)

季节成分:是指时间序列数据中呈现出的周期性、重复性的模式,其周期通常是固定的,与季节、月份、星期、甚至一天中的不同时段等时间因素相关。

  • Such decomposition not only increases the dimensions of the original data but also supplies us withextracted anomalies.
    这种分解不仅增加了原始的数据维度,还为我们提供了提取出的异常值。

  • As shown in Figure 1:
    分解效果如图1所示:
    在这里插入图片描述

  • we begin the decomposition by approximating the trend line t ( k ) t^{(k)} t(k) with the locally weighted scatterplot smoothing (i.e. LOESS).
    我们通过局部加权散点点平滑来近似趋势线 t ( k ) t^{(k)} t(k),开始进行分解。

LOESS 算法,即局部加权散点平滑法(Locally Weighted Scatterplot Smoothing),是一种用于数据拟合和曲线平滑的非参数统计方法。它通过对每个数据点附近的局部数据进行加权回归,来估计该点的平滑值。其优点是不需要对数据的分布形式做出先验假设,能够自适应地根据数据的局部特征进行拟合,对于具有复杂非线性关系的数据有较好的拟合效果,能有效去除数据中的噪声,展现出数据的潜在趋势。

  • Then, we divide the original data x ( k ) x^{(k)} x(k) by the approximated trend line to derive the detrended time series.
    接下来我门用原始信号 x ( k ) x^{(k)} x(k)除以趋势信号 t ( k ) t^{(k)} t(k),得到去除时间趋势之后的序列。

  • We then partition the detrended time series into periods of cyclic sub-series where the cycle size is determined by the time interval of the dataset.
    然后我们将去趋势的时间序列分割成周期性的子序列,其中周期大小由数据集的时间间隔决定。

  • As an example, the cycle size for a monthly dataset would be 12. Then we obtain the seasonal component
    举一个例子,假设是一个月度数据,则周期大小将是12。

  • Subsequently, the residual component r ( k ) r^{(k)} r(k) is derived by dividing the seasonal and trend segments from the original series.
    之后,残留的残差成分 r ( k ) r^{(k)} r(k) 是原始信号 x ( k ) x^{(k)} x(k) 除以趋势成分 t ( k ) t^{(k)} t(k) 和季节成分 s ( k ) s^{(k)} s(k) 得到的。

  • Note that the anomaly component a ( k ) a^{(k)} a(k) can be considered as the oddities of the dataset, which do not follow the extracted trend or seasonal components.
    值得注意的是 a ( k ) a^{(k)} a(k) 可以被视为数据集中的异常情况,这个异常情况不随季节和趋势的变化而变化。

  • Intuitively, anomalies are spread out through residual components, which also contain noise and other real-world effects.
    直观上,异常值通常在分布残差成分中,这些异常值包含噪声和其他显示的现实世界的影响。


残差成分 Residuals 和异常成分的区别 Anomalies 的区别。

残差 (Residuals)异常 (Anomalies)
定义模型无法解释的所有剩余波动显著偏离正常模式的极端值
组成噪声 + 未建模因素 + 潜在异常残差中的特殊子集
性质必然存在(模型不可能完美)偶然出现(需要特别检测)
处理分析分布特征需要单独识别和调查
示例日销售额波动 ±5%某天销售额突然暴涨200%
  • 残差 = 浪花(包含正常涟漪、小鱼跃出水面、偶尔的垃圾漂浮)
  • 异常 = 突然出现的巨型漩涡(需要立即关注的特殊现象)

  • To distinguish the anomalies from residual components, statistical metrics such as mean and variance are not the appropriate measure as they are highly sensitive to the severity level of anomaly values.
    为了区分异常值和残差分量,均值和方差等统计度量不是适当的衡量方法,因为他们对异常值的异常程度非常敏感。

假设你是班主任,要找出考试作弊的学生(异常检测):

  • 班级平均分(mean) = 所有学生分数的平均值
  • 分数波动(variance) = 学生们分数的分散程度

现在有两个作弊情况:

  1. 张三:偷偷抄答案考了100分(极端异常值)
  2. 李四:偶尔偷看考了75分(轻度异常值)

如果只用均值和方差判断:

  • 张三的100分会大幅拉高班级平均分(比如从70→78)
  • 方差也会急剧增大(分数范围从50-80变成50-100)

通过均值和方差判则无法找出李四的异常


  • As one expects, the severity of the anomalies can change the mean and variance values which are unwanted.
    正如所设想的内样,异常的严重程度会以我们不期望的形式改变均值和方式。

  • To resolve this issue, we leverage the median of the residuals, which is immune to the severity of the outliers in the residual components.
    为了解决这个问题,我们利用残差的中位数,它对残差分量中的异常值的严重程度具有免疫力。

  • Next,we define robustness score ρ t ( k ) \rho_t^{(k)} ρt(k) for each observation at time t t t as:
    接下来我们为在各个 t t t 时刻的每一个观察值定义了鲁棒性因子 ρ t ( k ) \rho_t^{(k)} ρt(k) 公式如下:
    ρ t ( k ) = ∣ r t ( k ) − r ˙ ( k ) ∣ ∑ t = 1 T ∣ r t ( k ) − r ˙ ( k ) ∣ T − 1 \rho_t^{(k)} = \frac{\left| r_t^{(k)} - \dot{r}^{(k)} \right|}{\sqrt{\frac{\sum_{t = 1}^{T} \left| r_t^{(k)} - \dot{r}^{(k)} \right|}{T - 1}}} ρt(k)=T1t=1T rt(k)r˙(k) rt(k)r˙(k)

  • where ρ t ( k ) \rho_t^{(k)} ρt(k) stand for the strength of the anomalies, r t ( k ) r_t^{(k)} rt(k) is the residual at time step t t t and r ˙ ( k ) \dot{r}^{(k)} r˙(k) is the median of the residuals.
    其中 ρ t ( k ) \rho_t^{(k)} ρt(k) 代表异常强度, r t ( k ) r_t^{(k)} rt(k) 时间步 t t t 时刻的残差值, r ˙ ( k ) \dot{r}^{(k)} r˙(k) 是残差成分的中位数。

  • Note that the larger ρ t \rho_t ρt indicates that a drastic change has occurred in the trend and seasonal components. We then extract the anomalies from residuals as below:
    ρ t \rho_t ρt 较大表明趋势和季节性成分发生了剧烈的变化,我们从残差中提取异常如下。
    a t ( k ) = { 1 , ρ t ( k ) < ρ c ( k ) r t ( k ) , ρ t ( k ) ≥ ρ c ( k ) \mathbf{a}_t^{(k)} = \begin{cases} 1, & \rho_t^{(k)} < \rho_c^{(k)} \\ r_t^{(k)}, & \rho_t^{(k)} \geq \rho_c^{(k)} \end{cases} at(k)={1,rt(k),ρt(k)<ρc(k)ρt(k)ρc(k)

  • where ρ c ( k ) \rho_c^{(k)} ρc(k) is the constant threshold given by the value of a robustness score ranked in the p − p- pvalue 0.05 while the values of elements in ρ ( k ) \rho^{(k)} ρ(k) are ranked in descending order from large to small.
    其中 ρ c ( k ) \rho_c^{(k)} ρc(k) 是在降序排列的 ρ ( k ) \rho^{(k)} ρ(k) 序列中分位值为 0.05 的时候确定的。

假设有 ρ ( k ) \rho^{(k)} ρ(k) 有100个数,阈值 ρ c ( k ) \rho_c^{(k)} ρc(k) 就是第5(100×0.05)大的数字

  • Notably, when the value of the anomaly component ( a ( k ) a^{(k)} a(k)) deviates further from the value 1, it indicates an abrupt change in the trend and the seasonal component (no sign of anomalies).
    这里要明确的是,当异常值 a ( k ) a^{(k)} a(k) 的值距离 1 很远时候,它表面确实和季节性成分发生了突然的变化,但不一定是异常。

  • On the contrary, when both anomaly and residual values are equal 1 ( r t ( k ) r_t^{(k)} rt(k) = 1 and a t ( k ) a_t^{(k)} at(k) = 1), it indicates that the observed signal at time t t t explicitly follows the trend and seasonal component.
    可以对比来看的是,当异常值和残差值都等于1时,表明在时间 t t t 观察到的信号明确地遵循了趋势和季节成分。

  • Note that such important information might not be automatically inferred when additive decomposition methods are being used. This due to the fact that the values of residual components can differ from one dataset to another which requires manual effort in their detection.
    当使用加法分解方法时,这样的重要信息可能不会被自动推断出来,这是因为残差分量的值可能因数据集而异,因此需要手动筛选出异常。(个人补充:加法分解一般只能直接通过幅度大小判定异常,但由于各个数据集的数据情况不同,无法直接通过对幅度加上阈值进行直接筛选)

  • A sample result of anomaly decomposition is shown in the left-most part of Figure 1, where the observed time series data is decomposed into tis seasonal, trend, anomalies, and residual .
    异常分解的一个示例结果显示在图最左侧的部分,其中观察到的时间序列数据被分别分解为季节性、趋势、异常和残差分量。(还是开头内个图不放来他又说了一遍)

  • Each of these components holds essential information about the characteristics of the time and will be leveraged to train the forecast model.
    这些成分中的每一个都包含了关于时间序列特征的重要信息,并将被我们使用来预测模型。

  • To this end, we concatenate the derived decomposed vector of time series with the input, which includes the observed time series and its labeled extreme event.
    最终,我们将这些时间序列进行拼接作为一个输入模型的训练的特征样本,这个样本包含观测到的时间序列及标记的极端事件还有我们分解出的各个成分。

       读完之后其实我是带着疑惑的,就是实际上 x ( k ) = s ( k ) × t ( k ) × a ( k ) × r ( k ) x^{(k)} = s^{(k)}×t^{(k)}×a^{(k)}×r^{(k)} x(k)=s(k)×t(k)×a(k)×r(k) 在这个乘法项的分解里 a ( k ) a^{(k)} a(k) 这一项是怎么乘进去的其实是没有清晰给出的,如果不看代码我觉得但从这段的写作上来看,有故意隐藏核心细节的嫌疑。好在代码虽然没有注释但我也复现出来了。

2.STAR 代码复现 手搓版


为了保证复现结果的可验证性,我这里用了一个阿里的开源功率数据集。
https://tianchi.aliyun.com/dataset/159885
在这里插入图片描述
下载之后数据如下一个非常标准的风电数据集,之后我们就分解和使用实际发电功率这一项。

在这里插入图片描述

2.1 数据读取


请添加图片描述

import pandas as pd
import matplotlib.pyplot as plt

data = pd.read_excel("data.xlsx")
print(data.head())

power = data["实际发电功率"].values

plt.plot(power)
plt.show()

2.2 提取趋势成分


请添加图片描述

# 导入必要的库
import pandas as pd  # 数据处理库
import matplotlib.pyplot as plt  # 绘图库
import numpy as np  # 数值计算库
from statsmodels.nonparametric.smoothers_lowess import lowess  # 非参数平滑方法

# 设置中文字体(以 Windows 的 SimHei 为例)
plt.rcParams['font.sans-serif'] = ['SimHei']  # 设置全局字体为黑体
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

# 读取Excel数据文件
data = pd.read_excel("data.xlsx")  # 从Excel文件加载数据
power = data["实际发电功率"].values  # 提取"实际发电功率"列的值转为numpy数组
x = np.arange(len(power))  # 创建与功率数据长度相同的索引数组作为x轴

# 使用LOWESS进行平滑处理(非参数局部回归)
# frac: 平滑窗口比例(0.3表示使用30%的数据点进行局部拟合)
# it: 执行3次稳健拟合迭代(消除异常值影响)
smoothed = lowess(power, x, frac=0.3, it=3)

# 提取并处理平滑结果
rt = smoothed[:, 1]  # 提取平滑后的趋势分量(第二列为拟合值)
data_d_rt = power / rt  # 计算原始功率与趋势分量的比值(分解周期分量)

# 创建绘图画布(宽20英寸,高4英寸)
plt.figure(figsize=(20, 4))

# 绘制原始功率曲线
plt.plot(power,
         label="功率",
         color='blue',
         linestyle='-',  # 实线
         linewidth=2)  # 线宽2磅

# 绘制趋势分量曲线
plt.plot(rt,
         label="趋势分量 $r^{(k)}$",  # LaTeX格式的数学符号
         color='green',
         linestyle='--',  # 虚线
         linewidth=2)

# 保存图形到文件
plt.savefig("2.获得趋势分量.png",
            bbox_inches="tight")  # 紧凑型边界框

# 显示图形
plt.legend(loc="upper left")  # 在左上角显示图例
plt.grid(True)  # 添加网格线
plt.show()

2.3 提取季节成分


在季节分量这里,我们选择的周期的以天为周期,由于数据本身颗粒度为15分钟,所以就是96个一循环
在这里插入图片描述

# 导入所需库
import pandas as pd  # 数据处理库
import matplotlib.pyplot as plt  # 绘图库
import numpy as np  # 数值计算库
from statsmodels.nonparametric.smoothers_lowess import lowess  # LOWESS平滑算法

# 设置中文字体显示(针对Windows系统)
plt.rcParams['font.sans-serif'] = ['SimHei']  # 指定默认字体为黑体
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示异常问题

# 数据读取与预处理
data = pd.read_excel("data.xlsx")  # 从Excel文件加载数据集
power = data["实际发电功率"].values  # 将功率列转换为numpy数组
x = np.arange(len(power))  # 创建与数据长度相同的索引序列

# LOWESS趋势提取
smoothed = lowess(power, x, frac=0.3, it=3)  # 执行局部加权回归
rt = smoothed[:, 1]  # 提取趋势分量(第二列为拟合值)

# 去趋势处理
data_d_rt = power / rt  # 通过除法去除趋势(适用于乘法分解模型)

# 周期特征提取
son_sequence = data_d_rt.copy().reshape(96, -1)  # 按日周期(15分钟间隔)重塑数据为96行
sequence_len = son_sequence.shape[-1]  # 获取每个周期的天数(列数)

# 中位数计算(注意:此处实际使用均值计算,建议修改为np.median)
column_medians = np.mean(son_sequence, axis=1)  # 计算每个时间点的均值(应使用中位数)
column_medians = column_medians[:, np.newaxis]  # 添加新轴便于矩阵运算

# 季节分量重构
st = np.tile(column_medians, (1, sequence_len)).T.reshape(-1)  # 将均值延展为完整序列
# 操作解析:
# 1. np.tile进行矩阵复制 (1列→sequence_len列)
# 2. .T转置矩阵适配后续操作
# 3. reshape(-1)展平为一维季节分量

# 可视化设置
plt.figure(figsize=(20, 4))  # 创建20英寸宽、4英寸高的画布
plt.plot(st,  # 绘制季节分量
         label="中位数延展结果",  # 图例标签
         color='blue',  # 线条颜色
         linewidth=2)  # 线宽设置

# 图表美化
plt.title("中位数延展结果可视化", fontsize=16)  # 设置标题及字号
plt.xlabel("时间/索引", fontsize=14)  # X轴标签设置
plt.ylabel("中位数延展值", fontsize=14)  # Y轴标签设置(实际为均值)
plt.legend(loc='upper right', fontsize=12)  # 右上角图例
plt.grid(True, linestyle='--', alpha=0.6)  # 半透明虚线网格
plt.tight_layout()  # 自动调整元素间距
plt.show()  # 显示图形

2.4 提取残差成分


在这里插入图片描述

# 导入必要的库
import pandas as pd  # 数据处理库
import matplotlib.pyplot as plt  # 绘图库
import numpy as np  # 数值计算库
from statsmodels.nonparametric.smoothers_lowess import lowess  # LOWESS平滑算法

# 设置中文显示(适用于Windows系统)
plt.rcParams['font.sans-serif'] = ['SimHei']  # 指定默认字体为黑体
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示异常问题

# 数据加载与预处理
data = pd.read_excel("data.xlsx")  # 从Excel文件加载数据集
power = data["实际发电功率"].values  # 提取功率列数据为numpy数组
x = np.arange(len(power))  # 创建时间索引序列(0,1,2,...n)

# 趋势分量提取(LOWESS平滑)
smoothed = lowess(power, x, frac=0.3, it=3)  # 执行局部加权回归
tk = smoothed[:, 1]  # 提取趋势分量(第二列为拟合值)

# 去趋势处理(乘法分解模型)
data_d_tk = power / tk  # 原始信号/趋势分量 = 季节分量 × 残差分量

# 周期特征提取(假设每日96个15分钟间隔)
son_sequence = data_d_tk.copy().reshape(96, -1)  # 重塑为(96,天数)矩阵
sequence_len = son_sequence.shape[-1]  # 获取周期天数(列数)

# 季节分量计算(行中位数)
column_means = np.mean(son_sequence, axis=1)  # 计算每个时间点的平均值
column_means = column_means[:, np.newaxis]  # 转换为列向量便于矩阵运算

# 季节分量重构
sk = np.tile(column_means, (1, sequence_len)).T.reshape(-1)  # 延展中位数序列
# 操作分解:
# 1. np.tile复制列向量为(96,天数)矩阵
# 2. .T转置为(天数,96)
# 3. reshape(-1)展平为原始长度

# 残差分量计算
rk = data_d_tk / sk  # 残差 = 去趋势信号 / 季节分量

# 可视化设置(创建4行1列的子图布局)
plt.figure(figsize=(20, 10))  # 设置画布尺寸(宽20英寸,高10英寸)

# ---------------------------
# 子图1:原始功率信号
# ---------------------------
plt.subplot(4, 1, 1)  # 定位到第1个子图
plt.plot(x, power,
         label="原始功率信号",  # 图例标签
         color='blue',  # 信号颜色
         linewidth=2)  # 线宽设置
plt.title("原始功率信号", fontsize=14)  # 子图标题
plt.xlabel("时间/索引", fontsize=12)  # X轴标签
plt.ylabel("功率值", fontsize=12)  # Y轴标签
plt.legend(loc='upper right', fontsize=10)  # 图例位置与字号
plt.grid(True, linestyle='--', alpha=0.6)  # 半透明虚线网格

# ---------------------------
# 子图2:趋势分量 (tk)
# ---------------------------
plt.subplot(4, 1, 2)  # 定位到第2个子图
plt.plot(x, tk,
         label="趋势分量 (tk)",  # 分量名称
         color='green',  # 趋势线颜色
         linewidth=2)
plt.title("趋势分量 (tk)", fontsize=14)
plt.xlabel("时间/索引", fontsize=12)
plt.ylabel("趋势值", fontsize=12)
plt.legend(loc='upper right', fontsize=10)
plt.grid(True, linestyle='--', alpha=0.6)

# ---------------------------
# 子图3:季节分量 (sk)
# ---------------------------
plt.subplot(4, 1, 3)  # 定位到第3个子图
plt.plot(x, sk,
         label="季节分量 (sk)",  # 周期特征
         color='red',  # 季节线颜色
         linewidth=2)
plt.title("季节分量 (sk)", fontsize=14)
plt.xlabel("时间/索引", fontsize=12)
plt.ylabel("季节值", fontsize=12)
plt.legend(loc='upper right', fontsize=10)
plt.grid(True, linestyle='--', alpha=0.6)

# ---------------------------
# 子图4:残差分量 (rk)
# ---------------------------
plt.subplot(4, 1, 4)  # 定位到第4个子图
plt.plot(x, rk,
         label="残差分量 (rk)",  # 剩余波动
         color='purple',  # 残差线颜色
         linewidth=2)
plt.title("残差分量 (rk)", fontsize=14)
plt.xlabel("时间/索引", fontsize=12)
plt.ylabel("残差值", fontsize=12)
plt.legend(loc='upper right', fontsize=10)
plt.grid(True, linestyle='--', alpha=0.6)

# 全局布局调整
plt.tight_layout()  # 自动调整子图间距
plt.show()  # 渲染显示图像

2.5 提取异常成分


       这里要说一下的是,在原文中没有给出的残差成分和提取异常成分相乘的关系以及如何作用到原始信号上,在我阅读了他的代码之后,其实是通过赋1值实现的,也就是说异常成分就是残差成分中鲁棒因子大于等于阈值的点组成的,其他地方都是1,然后残差成分中的鲁棒因子小于阈值的点给变成了1,这样的话,经过这一步之后,得到的异常成分和残差成分相乘,其实就是上一步的残差成分,这部分关键逻辑在原文中被掩盖了。

在这里插入图片描述

# 导入所需库
from statistics import median  # 提供中位数计算函数
import pandas as pd  # 数据处理库
import matplotlib.pyplot as plt  # 绘图库
import numpy as np  # 数值计算库
from statsmodels.nonparametric.smoothers_lowess import lowess  # LOWESS平滑算法

# 设置中文显示(适用于Windows系统)
plt.rcParams['font.sans-serif'] = ['SimHei']  # 指定默认字体为黑体
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

# 数据加载与预处理
data = pd.read_excel("data.xlsx")  # 加载Excel数据集
power = data["实际发电功率"].values  # 提取功率数据为numpy数组
x = np.arange(len(power))  # 创建时间索引序列(0,1,2,...n)

# 趋势分量提取(LOWESS平滑)
smoothed = lowess(power, x, frac=0.3, it=3)  # 执行局部加权回归
tk = smoothed[:, 1]  # 提取趋势分量(第二列为拟合值)

# 去趋势处理(乘法分解模型)
data_d_tk = power / tk  # 原始信号/趋势分量 = 季节分量 × 残差分量

# 周期特征提取(假设每日96个15分钟间隔)
son_sequence = data_d_tk.copy().reshape(96, -1)  # 重塑为(96,天数)矩阵
sequence_len = son_sequence.shape[-1]  # 获取周期天数(列数)

# 季节分量计算(注意此处实际使用均值,建议修改为np.median)
column_medians = np.mean(son_sequence, axis=1)  # 计算每个时间点的均值
column_medians = column_medians[:, np.newaxis]  # 转换为列向量

# 季节分量重构
sk = np.tile(column_medians, (1, sequence_len)).T.reshape(-1)  # 延展为完整序列

# 残差分量计算
rk = data_d_tk / sk  # 残差 = 去趋势信号 / 季节分量

# 异常检测算法 --------------------------------------------------
# 计算残差中位数
rk_median = np.median(rk)  # 获取残差的中位数值

# 计算绝对离差(Median Absolute Deviation, MAD)
rou_abs = np.abs(rk - rk_median)  # 每个残差与中位数的绝对距离

# 计算标准化离差(类似Z-score标准化)
rou_k = rou_abs / np.sqrt(np.sum(rou_abs)/(len(rou_abs)-1))  # 自定义标准化方法

# 确定异常阈值(取95%分位数)
sorted_data = sorted(rou_k, reverse=True)  # 降序排列离差值
rou_c = sorted_data[int(0.05*len(sorted_data))-1]  # 取前5%作为异常阈值

# 生成异常指示向量
ak = rk.copy()  # 创建副本
ak[np.where(rou_k < rou_c)] = 1  # 正常点标记为1
rk[np.where(rou_k >= rou_c)] = 1  # 异常点残差设为1(需验证逻辑合理性)

# 可视化设置 ---------------------------------------------------
fig, axes = plt.subplots(5, 1, figsize=(20, 14))  # 创建5行子图布局

# 子图1:原始功率数据
axes[0].plot(power,
            label='原始功率数据',
            color='blue')
axes[0].set_ylabel('原始功率数据')
axes[0].legend()
axes[0].grid(True)

# 子图2:趋势分量
axes[1].plot(tk,
            label='趋势信息 (tk)',
            color='orange')
axes[1].set_ylabel('趋势成分')
axes[1].legend()
axes[1].grid(True)

# 子图3:季节分量
axes[2].plot(sk,
            label='季节信息分量 (sk)',
            color='green')
axes[2].set_ylabel('季节成分')
axes[2].legend()
axes[2].grid(True)

# 子图4:异常分量(需注意标记逻辑)
axes[3].plot(ak,
            label='异常分量 (ak)',
            color='red')
axes[3].set_ylabel('异常成分')
axes[3].legend()
axes[3].grid(True)

# 子图5:修正后的残差分量
axes[4].plot(rk,
            label='残差分量 (rk)',
            color='purple')
axes[4].set_ylabel('残差成分')
axes[4].legend()
axes[4].grid(True)

# 布局调整
plt.tight_layout()
plt.show()

3.项目 GitHub 地址


代码也顺手传上去了,如果懒得复制了就上去 GitHub 上直接去下一下吧。
https://github.com/StChenHaoGitHub/STAR_Decomposition

在这里插入图片描述

4.结束


       这篇内容写的也是比较累的,确实难度也不小,而且实用性也存疑,但是我要是用不了给师弟拿去整个花活凑个不实用的创新点也是可以的。这个就先这样吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2315780.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于SpringBoot + Vue 的房屋租赁系统

基于springboot的房屋租赁管理系统-带万字文档 SpringBootVue房屋租赁管理系统 送文档 本项目有前台和后台两部分、多角色模块、不同角色权限不一样 共分三种角色&#xff1a;用户、管理员、房东 管理员&#xff1a;个人中心、房屋类型管理、房屋信息管理、预约看房管理、合…

Excel中国式排名,3种方法!

大家好&#xff0c;我是小鱼。 什么是中国式排名呢&#xff1f; 举个例子比如说公司一共有10名员工进行成绩考核&#xff0c;如果9个人考核成绩都是90分&#xff0c;你是89分&#xff0c;按照国际惯用的排名法则&#xff1a;9 个人考核成绩并列第一&#xff0c;你第10名&…

Flutter:跑马灯公告栏

组件 import dart:async; import package:flutter/material.dart; import package:ducafe_ui_core/ducafe_ui_core.dart;class MarqueeNotice extends StatefulWidget {/// 公告数据列表&#xff0c;每条公告包含title和descfinal List<Map<String, String>> notic…

Jmeter下载及环境配置

Jmeter下载及环境配置 java环境变量配置配置jdk环境变量检查是否配置成功JMeter下载 java环境变量配置 访问地址&#xff1a; https://www.oracle.com/cn/java/technologies/downloads/ 注意&#xff1a;需要自己注册账号 下载完成&#xff0c;解压后的目录为&#xff1a; …

【初级篇】如何使用DeepSeek和Dify构建高效的企业级智能客服系统

在当今数字化时代,企业面临着日益增长的客户服务需求。使用Dify创建智能客服不仅能够提升客户体验,还能显著提高企业的运营效率。关于DIfy的安装部署,大家可以参考之前的文章: 【入门级篇】Dify安装+DeepSeek模型配置保姆级教程_mindie dify deepseek-CSDN博客 AI智能客服…

微信小程序threejs三维开发

微信小程序threejs开发 import * as THREE from three; const { performance, document, window, HTMLCanvasElement, requestAnimationFrame, cancelAnimationFrame, core, Event, Event0 } THREE .DHTML import Stats from three/examples/jsm/libs/stats.module.js; im…

大数据-spark3.5安装部署之standalone模式

真实工作中还是要将应用提交到集群中去执行&#xff0c;Standalone模式就是使用Spark自身节点运行的集群模式&#xff0c;体现了经典的master-slave模式。集群共三台机器&#xff0c;具体如下 u22server4spark&#xff1a; master worker u22server4spark2&#xff1a; worke…

技术视界|构建理想仿真平台,加速机器人智能化落地

在近期的 OpenLoong 线下技术分享会 上&#xff0c;松应科技联合创始人张小波进行了精彩的演讲&#xff0c;深入探讨了仿真技术在机器人智能化发展中的关键作用。他结合行业趋势&#xff0c;剖析了现有仿真平台的挑战&#xff0c;并描绘了未来理想仿真系统的设计理念与实现路径…

AutoGen多角色、多用户、多智能体对话系统

2023-03-11-AutoGen 使用【autoGenchainlitdeepSeek】实现【多角色、多用户、多智能体对话系统】 1-核心思路 01&#xff09;技术要点&#xff1a;autoGenchainlitdeepSeek02&#xff09;什么是autoGen->autogen是微软旗下的多智能体的框架03&#xff09;什么是chainlit-&g…

SQL99 多表查询

内连接&#xff1a; select name, depart_name, city from employee e join department d on e.depart_id d.depart_id join location l on d.locat_id l.locat_id; 外连接 注&#xff1a;本图取自博客园大佬"anliux"的博客&#xff0c;原帖链接&#xff1a;【学…

sql靶场5-6关(报错注入)保姆级教程

目录 sql靶场5-6关&#xff08;报错注入&#xff09;保姆级教程 1.第五关 1.步骤一&#xff08;闭合&#xff09; 2.步骤二&#xff08;列数&#xff09; 3.报错注入深解 4.报错注入格式 5.步骤三&#xff08;数据库表名&#xff09; 6.常用函数 7.步骤四&#xff08;表…

矩阵分析-浅要理解(深度学习方向)

梯度分析与最优化 在深度学习的任务中&#xff0c;我们所期望的是训练一个神经网络&#xff0c;使得预测结果与真实标签之间的误差最小化&#xff0c;这可以近似看作是一个提供梯度下降等优化找到全局最优解的凸优化问题。 奇异值分解 在信息工程领域&#xff0c;对数据处理的…

校园安全用电怎么保障?防触电装置来帮您

引言 随着教育设施的不断升级和校园用电需求的日益增长&#xff0c;校园电力系统的安全性和可靠性成为了学校管理的重要课题。三相智能安全配电装置作为一种电力管理设备&#xff0c;其在校园中的应用不仅能够提高电力系统的安全性&#xff0c;还能有效保障师生的用电安全&am…

第十五届蓝桥杯大学B组(握手问题、小球反弹、好数)

一、握手问题 思路1&#xff1a; 1)先让所有人相互握手 第一个人49次 第二个人48次 第五十个人0次 共计01249 2)减去7个没握手的 016 #include<stdio.h> int main() {int a 50*49/2 - 7*6/2;printf("%d\n",a);return 0; } 运行结果&#xf…

【教学类-43-26】20240312 数独4宫格的所有可能(图片版 576套样式,空1格-空8格,每套65534张*576小图=3千万张小图)

背景需求&#xff1a; 之前做了三宫格所有可能图片 510小图*12套6120图&#xff0c;所以3分钟就生成了 【教学类-43-25】20240311 数独3宫格的所有可能&#xff08;图片版 12套样式&#xff0c;空1格-空8格&#xff0c;每套510张&#xff0c;共6120小图&#xff09;-CSDN博客…

如何手动使用下载并且运行 QwQ-32B-GGUF

首先使用安装 pip install ModelScope 使用 ModelScope 下载对应的模型 modelScope download --model Qwen/QwQ-32B-GGUF qwq-32b-q4_k_m.gguf 第二步开始下载 ollama git clone https://githubfast.com/ggerganov/llama.cpp # githubfast.com 可以加速下载 切换到目录&am…

Spring Boot对接twilio发送邮件信息

要在Spring Boot应用程序中对接Twilio发送邮件信息&#xff0c;您可以使用Twilio的SendGrid API。以下是一个简单的步骤指南&#xff0c;帮助您完成这一过程&#xff1a; 1. 创建Twilio账户并获取API密钥 注册一个Twilio账户&#xff08;如果您还没有的话&#xff09;。在Twi…

约束优化技术:KKT条件的完整推导与应用

前言 本文隶属于专栏《机器学习数学通关指南》&#xff0c;该专栏为笔者原创&#xff0c;引用请注明来源&#xff0c;不足和错误之处请在评论区帮忙指出&#xff0c;谢谢&#xff01; 本专栏目录结构和参考文献请见《机器学习数学通关指南》 ima 知识库 知识库广场搜索&#…

对比文章相似度的余弦相似度算法的原理

近期不是项目遇到对比代码的相似度&#xff0c;来判断代码是否存在抄袭嘛。通过研究采用了余弦相似度来对比。既然接触的一个新的东西&#xff0c;怎么也得研究下吧。 一、什么是余弦相似度 利用余弦相似度对比文章相似度的原理&#xff0c;主要基于向量空间模型&#xff0c;通…

DeepSeek结合Mermaid绘图(流程图、时序图、类图、状态图、甘特图、饼图)转载

思维速览&#xff1a; 本文将详细介绍如何利用DeepSeek结合Mermaid语法绘制各类专业图表&#xff0c;帮助你提高工作效率和文档质量。 ▍DeepSeek入门使用请看&#xff1a;deepseek保姆级入门教程&#xff08;网页端使用 本地客户端部署 使用技巧&#xff09; DeepSeek官网…