移動平均一

移動平均,濾波,平滑等,這些概念其實都大同小異,其作用都是希望能把信號數值中的毛刺、噪點,給去掉抹平捋順,留下真值。
這類的程序和工作做了不少,一直沒有機會總結歸納整理下。趁着這次空擋的時間,寫了一個算法調試工具,順便寫篇博客總結一下。
至於寫了算法調試工具的目的,主要是爲了提高效率。
我們一般調試算法的步驟是:

Created with Raphaël 2.2.0開始寫算法,調參數編譯下載,在標準環境中運行打印數據,複製到Excel查看效果是否OK?結束yesno

如果使用算法調試工具,那我們的流程就可以變成這樣

Created with Raphaël 2.2.0開始在標準環境下采集原始數據將原始數據導入到算法調試工具中調整算法和參數編譯下載,在標準環境中運行打印數據,複製到Excel查看效果是否OK?結束yesno

按照之前的流程,可能要走很多遍,效率不高。
有了算法調試工具後,理想情況下,一次搞定。
當然這種效率的提高,也是因爲前期工作的準備(算法調試工具的開發)。
目前市面上有很多類似的,且更好用的工具,不過自己還是想要結合自身所學,綜合考慮,自己開發一個算法調試工具。如下,目前功能還不多,慢慢增加。

在這裏插入圖片描述

簡單移動平均

原理

若依次得到一組原始測定值時,按順序取一定數量的數據並算得其全部算術平均值,得到的數據就叫做移動平均值
假設:
原始測定值爲:x1,x2,x3,x4...,xnx1,x2,x3,x4...,x_{n}
一定數量L 爲:33(移動平均的窗口長度)
則:
移動平均值:x1+x2+x33,x2+x3+x43,x3+x4+x53,x4+x5+x63......\cfrac{x1+x2+x3}{3} , \cfrac{x2+x3+x4}{3},\cfrac{x3+x4+x5}{3},\cfrac{x4+x5+x6}{3}......

代碼(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的大小。

加權移動平均

原理

主要方法是,通過給較近的數值分配較高的權重,給較遠的數值分配較低的權重。
主要目的是,在有不錯的平滑效果情況下,儘量的減少其遲滯效應,更能反映當前的真值和未來的預測值。
權重分配的方法有很多,用的比較多的是線性法指數法。以下以線性加權移動平均爲例。
假設:
原始測定值爲:x1,x2,x3,x4...,xnx1,x2,x3,x4...,x_{n}
一定數量L 爲:33(移動平均的窗口長度)
則:
線性加權移動平均值:1×x1+2×x2+3×x33,1×x2+2×x3+3×x43,1×x3+2×x4+3×x53,1×x4+2×x5+3×x63......\cfrac{1 \times x1+ 2 \times x2+3 \times x3}{3} , \cfrac{1 \times x2+2 \times x3+3 \times x4}{3},\cfrac{1 \times x3+2 \times x4+3 \times x5}{3},\cfrac{1 \times x4+2 \times x5+3 \times x6}{3}......

代碼(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();
        }

效果圖

在這裏插入圖片描述

卡夫曼自適應移動平均

原理

卡夫曼自適應移動不同於以上兩種的移動平均算法,它既能快速反應當前的真值和預測值,又能又較好的平滑效果。

原始測定值爲:x1,x2,x3,x4...,xnx1,x2,x3,x4...,x_{n}
窗口長度:LL
短(快)週期長度:fastLenfastLen
長(慢)週期長度:slowLenslowLen

波動性
vol=ii+Lxixi+1vol = \sum_{i}^{i+L} \left| x_i-x_{i+1} \right|

方向性
direction=xixiLdirection= \left| x_i-x_{i-L} \right|

效率係數
er=directionvoler = \cfrac{direction}{vol}

短週期均線係數
fastest=2fastLen+1fastest = \cfrac{2}{fastLen+1}

長週期均線係數
slowest=2slowLen+1slowest= \cfrac{2}{slowLen+1}

平滑係數
smooth=er×(fastestslowest)+slowestsmooth= er \times (fastest - slowest) + slowest
c=smooth×smoothc=smooth \times smooth

公式
ama=lastAma+c×(xilastAma);ama = lastAma + c \times (x_i - lastAma);

代碼(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();

效果圖

在這裏插入圖片描述

分析

不需要很大的窗口,就能有很好的平滑效果,且遲滯性很低。

這次分享到此結束,這類博客還有很多可以寫的,後面希望能寫出一個系列,分享更多更優更好的算法,迭代功能更強的算法調試工具。

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章