Model wygładzania wykładniczego Browna

mtbrowna5

Model Browna to jeden z prostych modeli prognostycznych. Zwykle stosuje się ten model dla szeregów czasowych o stałym poziomie lub bardzo słabym trendzie i umiarkowanych wahaniach przypadkowych. Jest on rozwinięciem modelu średnich ważonych.

Jeśli ktoś nie jest zainteresowany tutorialem a samym programem to można go pobrać: ModelWygladzaniaWykladniczegoBrowna_exe

(działa tylko na Windows 7+)

 

Prognozę wyznaczamy na podstawie wzoru:

mtbrowna

 

Pierwszy wyraz naszej prognozymtbrownaa  zazwyczaj przyjmujemy wartość:

1. Pierwszą wartość szeregu czasowego ( ten rodzaj określenia pierwszego wyrazu prognozy został wykorzystany w programie)

2. Średnią z trzech początkowych wartości szeregu czasowego.

3. Średnią z pięciu początkowych wyrazów szeregu czasowego.

Dzisiaj pokażę wam jak zrobić jeden z moim zdaniem najprostszy model prognostyczny. Jest on trochę cienki ale na start zawsze dobry. Polega on na wygładzeniu naszych zmiennych i ich przesunięciu. Opiera się w głównej mierze na ostatniej obserwacji i takiego doboru stałej alfa aby błąd średnio kwadratowy był jak najmniejszy. Ostatecznie ta metoda daje nam bardzo duży wachlarz prognozy. Program który tutaj zrobimy będzie pobierał dane z pliku tekstowego którego dane będą w prosty sposób wprowadzane, w kolumnie.

mtbrowna1

 

Ja mam taki plik z danymi. Teraz te dane będzie eksportować do „datagridview” i na nich wykonywać operacje. Podczepimy sobie również wykresy aby móc z wizualizować sobie zmiany.

 

Rodzaj elementu Nazwa elementu ustawienia
Form ModelBrowna Name: ModelBrowna
Text: Model Browna
Size: 1081; 582
DataGridView dgv_dane Name: dgv_dane
AllowUserToAddRows: False
AllowUserToDeleteRows: False
AllowUserToOrderColumn: True
AllowUserToResizeColumns: False
AllowUserToResizeRows: False
AutoSizeColumnsMode: Fill
Size: 566; 320
Location: 12; 29
label ie Text: „0”
Location: 105;380
Name: ie
label SumaRoznic Text: „0”
Location: 105;407
Name: SumaRoznic
label SumaKwadratow Text: „0”
Location: 105;435
Name: SumaKwadratow
label Prognoza Text: „0”
Location: 105;380
Name: Prognoza
label Mape Text: „0”
Location: 293;380
Name: Mape
label Mse Text: „0”
Location: 293;409
Name: Mse
label Rmse Text: „0”
Location: 293;436
Name: Rmse
label WspTheila Text: „0”
Location: 293;460
Name: WspTheila
label me_ Text: „0”
Location: 293;484
Name: me_
label Rmspe Text: „0”
Location: 293;508
Name: Rmspe
TextBox alfa Name: alfa
Location: 54;509
MenuStrip1 MenuStrip1 Name: MenuStrip1Dodano w menu:
Dane
– Dodaj dane
– Zakończ
Textbox FilePath Name: FilePath
Location: 123;3
Size: 560;20
Button Odswiez Name: Odswiez
Location:108;509
Chart Wykres1 Name: Wykres1
Location: 785; 35
Size: 268; 198
Ilość serii danych: 2
OpenFileDialog WczytajDane Name: WczytajDane
Chart wykres2 Name: Wykres2
Location: 587; 239
Size: 466; 300
Ilość serii: 2

Wypisałem powyżej najważniejsze elementy i ich lokalizacje, resztę labeli z opisami zostawiam wam do wypełnienia. Serie danych dodaje się tak:

mtbrowna2

 

Po wypełnieniu wszystkiego mój program wygląda tak:

mtbrowna3

Dobra wszystko przygotowane. Zajmiemy się teraz programowaniem naszego programu. Najpierw wpiszemy stałe elementy potrzebne z czasem w naszym programie:

Imports System.Windows.Forms.DataVisualization.Charting

Public Class ModelBrowna
    Private filename As String ''przechowuje ścieżkę naszego pliku 
    Dim wartoscAlfa As Double = 0.4 ''początkowa wartość alfa
    Dim dane As New List(Of String) ''przechowuje nasze dane w formie tablicy
    Dim kontroler As Boolean = True
(...)

Teraz zajmiemy się wczytanie danych i załadowaniem ich do DataGridView w tym celu rozwijamy nasze menu i przyciskamy dwukrotnie na „Dodaj dane”

mtbrowna4

Kod do ładujący dane wygląda tak:

 Private Sub DodajDaneToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles DodajDaneToolStripMenuItem.Click
        'filtr danych
        WczytajDane.Filter = "csv files|;*.csv;*.txt"
        WczytajDane.Title = "Select a csv file"
        WczytajDane.FileName = ""

        Try
            With WczytajDane
                If .ShowDialog() = DialogResult.OK Then
                    '' kontroler posłuży nam do zaznaczania czy dane są wczytanie czy też nie,
                    '' daje to możliwość załadowania innych danych jeśli te nam już nie będą potrzebna.
                    If kontroler = False Then
                        dane.Clear() 'czyści tablice
                        dgv_dane.Rows.Clear() 'czyści datagridview
                        filename = .FileName 'ładuje ścieżkę pliku do zmiennej string
                        FilePath.Text = filename 'wizualizuje ścieżkę pliku w textboxie
                        alfa.Text = wartoscAlfa.ToString ' wizualizuje wzsłczynnik alfa
                        TworzTabele(filename) ' funkcja przetwarzająca nasze dane z 
                        'pliku txt i ładujące je do tablicy (nie gotowa na tym etapie)
                        Metoda_Browna(wartoscAlfa) ' funkcja tworząca metode Browna (nie gotowa na tym etapie)
                        tworz_wykres() ' funkcja ładująca dane do wykresu (nie gotowa na tym etapie)
                    Else
                        filename = .FileName
                        FilePath.Text = filename
                        alfa.Text = wartoscAlfa.ToString
                        TworzTabele(filename)
                        Metoda_Browna(wartoscAlfa)
                        tworz_wykres()
                        kontroler = False
                    End If
                End If
            End With
        Catch ex As Exception
        End Try
    End Sub

Teraz zajmiemy się załadowanie wczytanych danych do tablicy „dane” dzięki wczytanie danych do datagridview będzie dużo łatwiejsze i nie przysporzy nam problemów.

    Private Sub TworzTabele(ByVal sciezkaDoPliku As String)
        Dim i As Integer
        Dim wartosc As String() 'przechowuje dane z pliku
        Dim f As IO.File = Nothing
        Dim rodzajkodu As New IO.StreamReader(sciezkaDoPliku, System.Text.Encoding.UTF8)
        Try
            wartosc = rodzajkodu.ReadLine().Split(ControlChars.Tab) 'rodzaj separatora w tym wypadku "Enter"
            For i = 0 To wartosc.Length() - 1
                dane.Add(wartosc(i).ToString)
            Next
            While rodzajkodu.Peek() <> -1
                wartosc = rodzajkodu.ReadLine().Split()
                For i = 0 To wartosc.Length() - 1
                    dane.Add(wartosc(i).ToString) 'Ładuje dane do tablicy dane
                Next
            End While
        Catch ex As Exception
            MsgBox("Error: " & ex.Message)
        Finally
            rodzajkodu.Close()
        End Try
    End Sub

Teraz zajmiemy się jądrem naszego programu, czyli samą metodą. Po załadowaniu danych do tablicy „dane” trzeba je tylko przenieść do datagridview, co nie jest trudne. Trochę tego dużo ale myślę, że się połapiecie co i jak:

Math.Round(wartość,x) – zaokrągla naszą wartość do „x” liczb po przecinku

Math.Abs() – wartość bezwzględna

Math.Sqrt() – pierwiastek z liczby

wartość^x – podniesienie wartości do potęgi „x”

 Private Sub Metoda_Browna(ByVal alfa As Double)
        Dim dane1 As Double = 0
        Dim dane2 As Double = 0
        Dim dane3 As Double = 0
        Dim dane4 As Double = 0
        Dim dane5 As Double = 0
        Try
            dgv_dane.ColumnCount = 9
            dgv_dane.Columns(0).Name = "t"
            dgv_dane.Columns(1).Name = "yt"
            dgv_dane.Columns(2).Name = "ŷt"
            dgv_dane.Columns(3).Name = "Δŷt"
            dgv_dane.Columns(4).Name = "yt*"
            dgv_dane.Columns(5).Name = "yt-y*t"
            dgv_dane.Columns(6).Name = "(yt-y*t)^2"
            dgv_dane.Columns(7).Name = "|(yt-y*t)/yt|"
            dgv_dane.Columns(8).Name = "yt^2"
            '' Dodaje t
            For i As Integer = 0 To dane.Count - 1
                dgv_dane.Rows.Add()    'wypełnia datagridview wierszami
                dgv_dane.Rows(i).Cells(0).Value = i + 1   ' =dodaje numeracje
            Next
            '' Dodaje yt
            For i As Integer = 0 To dane.Count - 1
                dgv_dane.Rows(i).Cells(1).Value = dane(i)
            Next
            '' dodaje ŷt (wartość wygładzona) ŷt=L*(ŷt-1)+(1-L)*(yt-1)
            dgv_dane.Rows(1).Cells(2).Value = dane(0)
            For i As Integer = 2 To dane.Count - 1
                dgv_dane.Rows(i).Cells(2).Value = Math.Round(dgv_dane.Rows(i - 1).Cells(2).Value * alfa + (1 - alfa) * dane(i - 1), 2)
            Next
            '' dodaje Δŷt przyrost oceny wartości trendu (różnica ostatnich wartości wygładzonych)
            For i As Integer = 1 To dane.Count - 1
                dgv_dane.Rows(i).Cells(3).Value = Math.Round(dgv_dane.Rows(i).Cells(2).Value - dgv_dane.Rows(i - 1).Cells(2).Value, 2)
            Next
            '' dodaje yt* = L*(yt-1)+(1-L)*(y*t-1)
            For i As Integer = 1 To dane.Count
                If i = 1 Then
                    dgv_dane.Rows(i).Cells(4).Value = dane(i - 1)
                    dgv_dane.Rows(i).Cells(4).Style.BackColor = Color.LightGray
                ElseIf i = dane.Count - 1 Then
                    'dodaje ekstra wiersz
                    dgv_dane.Rows.Add()
                    dgv_dane.Rows(i).Cells(4).Value = Math.Round(alfa * dane(i - 1) + (1 - alfa) * dgv_dane.Rows(i - 1).Cells(4).Value, 2)
                    dgv_dane.Rows(i + 1).Cells(0).Value = i + 2
                    dgv_dane.Rows(i).Cells(4).Style.BackColor = Color.LightGray
                Else
                    dgv_dane.Rows(i).Cells(4).Value = Math.Round(alfa * dane(i - 1) + (1 - alfa) * dgv_dane.Rows(i - 1).Cells(4).Value, 2)
                    dgv_dane.Rows(i).Cells(4).Style.BackColor = Color.LightGray
                End If
            Next
            '' dodaje yt-y*t
            For i As Integer = 1 To dane.Count - 1
                dgv_dane.Rows(i).Cells(5).Value = Math.Round(dane(i) - dgv_dane.Rows(i).Cells(4).Value, 3)
            Next
            '' Dodaje (yt-y*t)^2
            For i As Integer = 1 To dane.Count - 1
                dgv_dane.Rows(i).Cells(6).Value = Math.Round(dgv_dane.Rows(i).Cells(5).Value ^ 2, 3)
            Next
            '' Dodaje |(yt-y*t)/yt|
            For i As Integer = 1 To dane.Count - 1
                dgv_dane.Rows(i).Cells(7).Value = Math.Round(Math.Abs(dgv_dane.Rows(i).Cells(5).Value / dane(i), 6)
            Next

            '' Dodaje vt^2
            For i As Integer = 1 To dane.Count - 1
                dgv_dane.Rows(i).Cells(8).Value = Math.Round(dane(i) ^ 2, 3)
            Next

            ''Przygotowanie zmiennych do Obliczeń
            '' Suma (yt-y*t)
            For i As Integer = 1 To dane.Count - 1
                dane1 += dgv_dane.Rows(i).Cells(5).Value
            Next
            '' Suma (yt-y*t)^2
            For i As Integer = 1 To dane.Count - 1
                dane2 += dgv_dane.Rows(i).Cells(6).Value
            Next
            '' suma |(yt-y*t)/yt|
            For i As Integer = 1 To dane.Count - 1
                dane3 += dgv_dane.Rows(i).Cells(7).Value
            Next
            '' Suma ((yt-y*t)/yt)^2
            For i As Integer = 1 To dane.Count - 1
                dane4 += dgv_dane.Rows(i).Cells(7).Value ^ 2
            Next
            '' Suma yt^2
            For i As Integer = 1 To dane.Count - 1
                dane5 += dgv_dane.Rows(i).Cells(8).Value
            Next

            ''Wyświetlanie obliczeń
            Rmspe.Text = Math.Round(Math.Sqrt(((1 / (dane.Count - 1)) * (dane4))) * 100, 2) 'RMSPE
            me_.Text = Math.Round((1 / (dane.Count - 1)) * dane1, 2) 'ME
            Mse.Text = Math.Round((dane2) / (dane.Count), 2) ' MSE
            Rmse.Text = Math.Round(Math.Sqrt(Mse.Text), 2) 'RMSE, musi być pod Mmse
            Mape.Text = Math.Round(((1 / (dane.Count - 1)) * (dane3)) * 100, 0) 'MAPE
            ie.Text = dane.Count  'ilość elementów
            Prognoza.Text = dgv_dane.Rows(dane.Count).Cells(4).Value 'nasza prognoza
            SumaRoznic.Text = dane2 'suma (yt-y*t)^2
            SumaKwadratow.Text = Math.Round(dane5, 2) 'suma yt^2
            WspTheila.Text = Math.Round(SumaRoznic.Text / SumaKwadratow.Text, 6)
        Catch ex As Exception
            MsgBox("Error building datatable: " & ex.Message)
        End Try
    End Sub

Teraz zostało nam tylko przygotować wykresy i można sprawdzić czy wszystko działa.

    Private Sub tworz_wykres()
        Wykres2.Series(0).ChartType = DataVisualization.Charting.SeriesChartType.Line
        Wykres2.Series(0).Points.Clear()
        Wykres2.Series(0).Name = "yt"
        Wykres2.Series(0).BorderWidth = 2
        For Count As Integer = 0 To dgv_dane.Rows.Count - 2
            Wykres2.Series(0).Points.AddXY(dgv_dane.Item(0, Count).Value, dgv_dane.Item(1, Count).Value)
        Next
        Wykres2.Series(1).ChartType = DataVisualization.Charting.SeriesChartType.Line
        Wykres2.Series(1).Points.Clear()
        Wykres2.Series(1).Color = Color.Red
        Wykres2.Series(1).Name = "y*t"
        Wykres2.Series(1).BorderWidth = 2
        For Count As Integer = 0 To dgv_dane.Rows.Count - 1
            Wykres2.Series(1).Points.AddXY(dgv_dane.Item(0, Count).Value, dgv_dane.Item(4, Count).Value)
        Next
        Wykres2.Legends(0).Docking = Docking.Top


        Wykres1.Series(0).ChartType = DataVisualization.Charting.SeriesChartType.Line
        Wykres1.Series(0).Points.Clear()
        Wykres1.Series(0).Name = "yt"
        Wykres1.Series(0).BorderWidth = 2
        Wykres1.Series(0).IsValueShownAsLabel = True
        Wykres1.Series(0).LabelBorderColor = Color.LightBlue

        For Count As Integer = dgv_dane.Rows.Count - 5 To dgv_dane.Rows.Count - 2
            Wykres1.Series(0).Points.AddXY(dgv_dane.Item(0, Count).Value, dgv_dane.Item(1, Count).Value)
        Next
        Wykres1.Series(1).ChartType = DataVisualization.Charting.SeriesChartType.Line
        Wykres1.Series(1).Points.Clear()
        Wykres1.Series(1).Color = Color.Red
        Wykres1.Series(1).Name = "y*t"
        Wykres1.Series(1).BorderWidth = 2
        Wykres1.Series(1).IsValueShownAsLabel = True
        Wykres1.Series(1).LabelBorderColor = Color.LightCoral
        For Count As Integer = dgv_dane.Rows.Count - 5 To dgv_dane.Rows.Count - 1
            Wykres1.Series(1).Points.AddXY(dgv_dane.Item(0, Count).Value, Math.Round(dgv_dane.Item(4, Count).Value, 0))
        Next
        Wykres1.Legends(0).Docking = Docking.Top
    End Sub

Odpalamy zobaczymy. U mnie po załadowaniu danych wszystko wyglądało tak:

mtbrowna5

Chyba jest nieźle 🙂  Teraz dodamy sobie obsługę przycisku i ja włączyłem do pracy textbox określający element alfa, czyli jeśli wejdziemy w textbox i przyciśniemy przycisk na klawiaturze w górę „↑” to nasza alfa zmieni się o +0,01 a strzałka w dół zmieni naszą alfe o -0,01 oczywiście wszystko przy odświeżaniu programu co daje nam podgląd na zmiany w czqasie rzeczywistym.

    Private Sub Odswiez_Click(sender As Object, e As EventArgs) Handles Odswiez.Click
        wartoscAlfa = alfa.Text
        dgv_dane.Rows.Clear()
        Metoda_Browna(wartoscAlfa)
        tworz_wykres()
    End Sub

i zmiana alfy.

    Private Sub alfa_KeyDown(sender As Object, e As KeyEventArgs) Handles alfa.KeyDown
        Select Case e.KeyCode
            Case Keys.Up
                wartoscAlfa += 0.01
                alfa.Text = wartoscAlfa
                dgv_dane.Rows.Clear()
                Metoda_Browna(wartoscAlfa)
                tworz_wykres()
            Case Keys.Down
                wartoscAlfa -= 0.01
                alfa.Text = wartoscAlfa
                dgv_dane.Rows.Clear()
                Metoda_Browna(wartoscAlfa)
                tworz_wykres()
        End Select
    End Sub

Myślę, że Program będzie przydatny. Na pewno jest to dobry wstęp do modeli prognostycznych. Do pobrania daje jeszcze:

Dane użyte w czasie tutoriala: dane

Cały program: ModelWygladzaniaWykladniczegoBrowna

Jak to wygląda można zobaczyć tutaj:

Permalink do tego artykułu: https://visualmonsters.cba.pl/model-wygladzania-wykladniczego-browna/

Dodaj komentarz

Twój adres email nie będzie publikowany.