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:
Pierwszy wyraz naszej prognozy 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.
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:
Po wypełnieniu wszystkiego mój program wygląda tak:
Dobra wszystko przygotowane. Zajmiemy się teraz programowaniem naszego programu. Najpierw wpiszemy stałe elementy potrzebne z czasem w naszym programie:
1 2 3 4 5 6 7 8 |
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”
Kod do ładujący dane wygląda tak:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
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”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
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:
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.
1 2 3 4 5 6 |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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: