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:
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:
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:
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:







