移动平均,滤波,平滑等,这些概念其实都大同小异,其作用都是希望能把信号数值中的毛刺、噪点,给去掉抹平捋顺,留下真值。
这类的程序和工作做了不少,一直没有机会总结归纳整理下。趁着这次空挡的时间,写了一个算法调试工具,顺便写篇博客总结一下。
至于写了算法调试工具的目的,主要是为了提高效率。
我们一般调试算法的步骤是:
Created with Raphaël 2.2.0 开始 写算法,调参数 编译下载,在标准环境中运行 打印数据,复制到Excel 查看效果是否OK? 结束 yes no
如果使用算法调试工具,那我们的流程就可以变成这样
Created with Raphaël 2.2.0 开始 在标准环境下采集原始数据 将原始数据导入到算法调试工具中 调整算法和参数 编译下载,在标准环境中运行 打印数据,复制到Excel 查看效果是否OK? 结束 yes no
按照之前的流程,可能要走很多遍,效率不高。
有了算法调试工具后,理想情况下,一次搞定。
当然这种效率的提高,也是因为前期工作的准备(算法调试工具的开发)。
目前市面上有很多类似的,且更好用的工具,不过自己还是想要结合自身所学,综合考虑,自己开发一个算法调试工具。如下,目前功能还不多,慢慢增加。
简单移动平均
原理
若依次得到一组原始测定值时,按顺序取一定数量的数据并算得其全部算术平均值,得到的数据就叫做移动平均值。
假设:
原始测定值为:
一定数量L 为:(移动平均的窗口长度)
则:
移动平均值:
代码(C#)
int len = (int)simpleLenUpDown.Value; //移动平均的窗口长度
simpleList.Clear();//移动平均值队列
List<double> bufferList = new List<double>();//移动平均窗口队列
for (int i = 0; i < len; i++)
{
if (i >= sourceList.Count)
break;
bufferList.Add(sourceList[i]);
simpleList.Add(sourceList[i]);
}
for (int i = len; i < sourceList.Count; i++)
{
simpleList.Add(bufferList.Average());
bufferList.RemoveAt(0);
bufferList.Add(sourceList[i]);//移动
}
//刷新结果
chartControl.BeginInit();
Series series = new Series(simpleStr, ViewType.Line);
series.Label.ResolveOverlappingMode = ResolveOverlappingMode.HideOverlapped;
for (int i = 0; i < simpleList.Count; i++)
{
SeriesPoint seriesPoint = new SeriesPoint(i, simpleList[i]);
series.Points.Add(seriesPoint);
}
chartControl.Series.Add(series);
series.ArgumentScaleType = ScaleType.Numerical;
chartControl.EndInit();
效果图
移动平均的窗口长度L=5
移动平均的窗口长度L=50
分析
两张效果图已经很明细了,窗口L如果太小,则平滑效果不好。
窗口L如果太大,则会有明显的迟滞效应。
所以这种简单移动平均的应用很有局限性,需要你小心的调整这个窗口L的大小。
加权移动平均
原理
主要方法是,通过给较近的数值分配较高的权重,给较远的数值分配较低的权重。
主要目的是,在有不错的平滑效果情况下,尽量的减少其迟滞效应,更能反映当前的真值和未来的预测值。
权重分配的方法有很多,用的比较多的是线性法和指数法。以下以线性加权移动平均为例。
假设:
原始测定值为:
一定数量L 为:(移动平均的窗口长度)
则:
线性加权移动平均值:
代码(C#)
private double getWeightListAverage(List<double> bufferList)
{
double sum = 0;
int sumIndex = 0;
for (int i = 0; i < bufferList.Count; i++)
{
sumIndex += i;
sum += (i * bufferList[i]);
}
return sum / sumIndex;
}
private void weightUpdateBtn_Click(object sender, EventArgs e)
{
removeSeries(weightStr);
int len = (int)weightLenUpDown.Value;
weightList.Clear();
List<double> bufferList = new List<double>();
for (int i = 0; i < len; i++)
{
if (i >= sourceList.Count)
break;
bufferList.Add(sourceList[i]);
weightList.Add(sourceList[i]);
}
for (int i = len; i < sourceList.Count; i++)
{
weightList.Add(getWeightListAverage(bufferList));
bufferList.RemoveAt(0);
bufferList.Add(sourceList[i]);
}
chartControl.BeginInit();
Series series = new Series(weightStr, ViewType.Line);
series.Label.ResolveOverlappingMode = ResolveOverlappingMode.HideOverlapped;
for (int i = 0; i < weightList.Count; i++)
{
SeriesPoint seriesPoint = new SeriesPoint(i, weightList[i]);
series.Points.Add(seriesPoint);
}
chartControl.Series.Add(series);
series.ArgumentScaleType = ScaleType.Numerical;
chartControl.EndInit();
}
效果图
卡夫曼自适应移动平均
原理
卡夫曼自适应移动不同于以上两种的移动平均算法,它既能快速反应当前的真值和预测值,又能又较好的平滑效果。
原始测定值为:
窗口长度:
短(快)周期长度:
长(慢)周期长度:
波动性
方向性
效率系数
短周期均线系数
长周期均线系数
平滑系数
公式
代码(C#)
int len = (int)amaLenUpDown.Value;
int fastLen = (int)amaFastLenUpDown.Value;
int slowLen = (int)amaSlowLenUpDown.Value;
amaList.Clear();
double ama=0, lastAma=0;
for (int i = 0; i < len; i++)
{
if (i >= sourceList.Count)
break;
lastAma = sourceList[i];
ama = lastAma;
amaList.Add(ama);
}
for (int i = len; i < sourceList.Count; i++)
{
double direction = 0, er = 0, smooth= 0, c = 0, vol = 0;
double fastest = 2.0 / (fastLen + 1);
double slowest = 2.0 / (slowLen + 1);
for (int j = i-len; j < i-1; j++)
vol += Math.Abs(sourceList[j] - sourceList[j + 1]);
if (vol != 0)
{
direction = Math.Abs(sourceList[i] - sourceList[i - len]);
er = direction / vol;
smooth1 = er * (fastest - slowest) + slowest;
c = smooth * smooth;
ama = lastAma + c * (sourceList[i] - lastAma);
if (c>1000)
{ //防止大跳跃,大突变
ama = sourceList[i];
}
lastAma = ama;
}
else
{
ama = lastAma;
}
amaList.Add(ama);
}
chartControl.BeginInit();
Series series = new Series(amaStr, ViewType.Line);
series.Label.ResolveOverlappingMode = ResolveOverlappingMode.HideOverlapped;
for (int i = 0; i < amaList.Count; i++)
{
SeriesPoint seriesPoint = new SeriesPoint(i, amaList[i]);
series.Points.Add(seriesPoint);
}
chartControl.Series.Add(series);
series.ArgumentScaleType = ScaleType.Numerical;
chartControl.EndInit();
效果图
分析
不需要很大的窗口,就能有很好的平滑效果,且迟滞性很低。
这次分享到此结束,这类博客还有很多可以写的,后面希望能写出一个系列,分享更多更优更好的算法,迭代功能更强的算法调试工具。