数据平滑处理算法03——中心移动平均

中心移动平均

中心移动平均主要用于时间序列分解,特别是在估计趋势-周期成分时。它的核心特点是"对称性"。

核心思想:将移动平均窗口置于数据的"中心",使得平均值能够对齐到窗口的时间中点,从而最大限度地减少滞后性。

计算方法

对于一个时间序列,其 N 期中心移动平均值是将窗口置于当前点的两侧。

  • 如果 N 是奇数(例如 N=3, 5),则直接计算对称窗口的平均值。CMAt=xt−k+...+xt+...+xt+kNCMA_t = \frac{x_{t-k} + ... + x_t+...+x_{t+k}}{N}CMAt=Nxt−k+...+xt+...+xt+k其中k=N−12k = \frac{N-1}{2}k=2N−1,这样平均值就对应着窗口正中央的时刻 t
  • 如果 N 是偶数(例如 N=4),计算一次平均后,其值会落在两个时间点之间(例如,落在 t=2.5),因此需要进行一次"居中调整",即对相邻的两个移动平均值再取一次平均,使其回到整数时间点上。

优缺点

优点

  • 无滞后性(理想情况下):因为是对称加权,所以得到的趋势线能很好地与原始数据的趋势中心对齐,不会像SMA或EMA那样产生相位滞后。
  • 能更好地保留信号的原始形状,不会扭曲峰值和谷值。

缺点

  • 在序列两端会丢失数据。例如,一个3期中心移动平均,在序列的开始和结束处各会有1个数据点无法计算。
  • 计算比SMA复杂。

适用场景:时间序列分解、季节调整(如在美国人口调查局的X-13A-S季节性调整程序中广泛使用)、需要无偏估计长期趋势的场合。


示例

数据:[2, 4, 6, 8, 10], N=3(中心)

第一个平均值(对应第2个数据点):(2+4+6)/3 = 4

第二个平均值(对应第3个数据点):(4+6+8)/3 = 6

第三个平均值(对应第4个数据点):(6+8+10)/3 = 8

平滑后的数据为:[4, 6, 8],但请注意,这些值分别对应原始数据的第2、3、4个位置,首尾各损失一个点。


C语言代码实现

c 复制代码
// 中心移动平均
void centered_moving_average(const double input[], double output[], int data_size, int window_size) {
    if (window_size > data_size || window_size <= 0) {
        printf("Error: Invalid window size\n");
        return;
    }
    
    if (window_size % 2 == 0) { //取余数
        printf("Error: Window size must be odd for centered moving average\n");
        return;
    }
    
    int offset = window_size / 2;
    
    // 填充开头无法计算的部分
    for (int i = 0; i < offset; i++) {
        output[i] = 0.0;
    }
    
    // 计算中心移动平均
    for (int i = offset; i < data_size - offset; i++) {
        double sum = 0.0;
        for (int j = -offset; j <= offset; j++) {
            sum += input[i + j];
        }
        output[i] = sum / window_size;
    }
    
    // 填充结尾无法计算的部分
    for (int i = data_size - offset; i < data_size; i++) {
        output[i] = 0.0;
    }
}

算法测试:

c 复制代码
int main(){
	//测试数据
	double data[] = {2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0, 20.0};
  	int data_size = sizeof(data) / sizeof(data[0]);
  	double cma_result[10] = {0};
  
  	printf("原始数据: \n");
  	for(int i = 0; i < data_size; i++)
		printf("%.1f ",data[i]);
  	printf("\n");

  	// 测试中心移动平均 (窗口大小=3)
  	centered_moving_average(data, cma_result, data_size, 3);
  	printf("中心移动平均 (窗口大小=3):\n");
  	for(int i = 0; i < data_size; i++)
		printf("%.1f ",sma_result[i]);
  	printf("说明: 中间 %d 个值是有效结果,首尾是填充的0\n\n", data_size - 2);
}

测试结果:

相关推荐
我命由我1234513 小时前
Android 广播 - 静态注册与动态注册对广播接收器实例创建的影响
android·java·开发语言·java-ee·android studio·android-studio·android runtime
赛姐在努力.13 小时前
【拓扑排序】-- 算法原理讲解,及实现拓扑排序,附赠热门例题
java·算法·图论
yxc_inspire13 小时前
Java学习第二天
java·面向对象
毕设源码-赖学姐13 小时前
【开题答辩全过程】以 基于net超市销售管理系统为例,包含答辩的问题和答案
java
木斯佳13 小时前
前端八股文面经大全:26届秋招滴滴校招前端一面面经-事件循环题解析
前端·状态模式
昀贝13 小时前
IDEA启动SpringBoot项目时报错:命令行过长
java·spring boot·intellij-idea
光影少年13 小时前
react状态管理都有哪些及优缺点和应用场景
前端·react.js·前端框架
roman_日积跬步-终至千里14 小时前
【LangGraph4j】LangGraph4j 核心概念与图编排原理
java·服务器·数据库
野犬寒鸦14 小时前
从零起步学习并发编程 || 第六章:ReentrantLock与synchronized 的辨析及运用
java·服务器·数据库·后端·学习·算法
wenzhangli714 小时前
ooderA2UI BridgeCode 深度解析:从设计原理到 Trae Solo Skill 实践
java·开发语言·人工智能·开源