Spaces:
Sleeping
Sleeping
Kolesnikov Dmitry commited on
Commit ·
9ee5ee8
1
Parent(s): e5e9dcb
feat: Реализованное 3 задание
Browse files- .gitignore +1 -0
- Dockerfile +1 -0
- requirements.txt +15 -2
- russia_covid_dataset.csv +821 -0
- src/lab2_functions.py +626 -0
- src/lab3_functions.py +192 -0
- src/lab3_pipeline.py +955 -0
- src/lab4_functions.py +43 -0
- src/lab5_functions.py +48 -0
- src/main.py +7 -0
- src/russia_covid_dataset.csv +821 -0
- src/streamlit_app.py +0 -0
- БЫСТРЫЙ_СТАРТ.md +197 -0
- РУКОВОДСТВО.md +655 -0
- СТРУКТУРА_КОДА.md +334 -0
.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
.idea
|
Dockerfile
CHANGED
|
@@ -10,6 +10,7 @@ RUN apt-get update && apt-get install -y \
|
|
| 10 |
|
| 11 |
COPY requirements.txt ./
|
| 12 |
COPY src/ ./src/
|
|
|
|
| 13 |
|
| 14 |
RUN pip3 install -r requirements.txt
|
| 15 |
|
|
|
|
| 10 |
|
| 11 |
COPY requirements.txt ./
|
| 12 |
COPY src/ ./src/
|
| 13 |
+
COPY russia_covid_dataset.csv/ ./
|
| 14 |
|
| 15 |
RUN pip3 install -r requirements.txt
|
| 16 |
|
requirements.txt
CHANGED
|
@@ -1,3 +1,16 @@
|
|
| 1 |
-
|
| 2 |
pandas
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
streamlit
|
| 2 |
pandas
|
| 3 |
+
numpy
|
| 4 |
+
plotly
|
| 5 |
+
statsmodels
|
| 6 |
+
scipy
|
| 7 |
+
scikit-learn
|
| 8 |
+
pytz
|
| 9 |
+
pmdarima
|
| 10 |
+
arch
|
| 11 |
+
prophet
|
| 12 |
+
lightgbm
|
| 13 |
+
xgboost
|
| 14 |
+
torch
|
| 15 |
+
tensorflow
|
| 16 |
+
tbats
|
russia_covid_dataset.csv
ADDED
|
@@ -0,0 +1,821 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Date,DailyNewCases,ActiveCases,DailyNewDeaths
|
| 2 |
+
2020-2-15,,0.0,
|
| 3 |
+
2020-2-16,0.0,0.0,
|
| 4 |
+
2020-2-17,0.0,0.0,
|
| 5 |
+
2020-2-18,0.0,0.0,
|
| 6 |
+
2020-2-19,0.0,0.0,
|
| 7 |
+
2020-2-20,0.0,0.0,0.0
|
| 8 |
+
2020-2-21,0.0,0.0,0.0
|
| 9 |
+
2020-2-22,0.0,0.0,0.0
|
| 10 |
+
2020-2-23,0.0,0.0,0.0
|
| 11 |
+
2020-2-24,0.0,0.0,0.0
|
| 12 |
+
2020-2-25,0.0,0.0,0.0
|
| 13 |
+
2020-2-26,0.0,0.0,0.0
|
| 14 |
+
2020-2-27,0.0,0.0,0.0
|
| 15 |
+
2020-2-28,0.0,0.0,0.0
|
| 16 |
+
2020-2-29,0.0,0.0,0.0
|
| 17 |
+
2020-3-01,0.0,0.0,0.0
|
| 18 |
+
2020-3-02,1.0,1.0,0.0
|
| 19 |
+
2020-3-03,0.0,1.0,0.0
|
| 20 |
+
2020-3-04,0.0,1.0,0.0
|
| 21 |
+
2020-3-05,4.0,5.0,0.0
|
| 22 |
+
2020-3-06,6.0,11.0,0.0
|
| 23 |
+
2020-3-07,1.0,11.0,0.0
|
| 24 |
+
2020-3-08,3.0,14.0,0.0
|
| 25 |
+
2020-3-09,3.0,17.0,0.0
|
| 26 |
+
2020-3-10,0.0,17.0,0.0
|
| 27 |
+
2020-3-11,8.0,25.0,0.0
|
| 28 |
+
2020-3-12,6.0,31.0,0.0
|
| 29 |
+
2020-3-13,11.0,37.0,0.0
|
| 30 |
+
2020-3-14,14.0,51.0,0.0
|
| 31 |
+
2020-3-15,4.0,55.0,0.0
|
| 32 |
+
2020-3-16,30.0,85.0,0.0
|
| 33 |
+
2020-3-17,21.0,106.0,0.0
|
| 34 |
+
2020-3-18,33.0,139.0,0.0
|
| 35 |
+
2020-3-19,52.0,190.0,1.0
|
| 36 |
+
2020-3-20,54.0,240.0,0.0
|
| 37 |
+
2020-3-21,53.0,289.0,0.0
|
| 38 |
+
2020-3-22,61.0,350.0,0.0
|
| 39 |
+
2020-3-23,71.0,420.0,0.0
|
| 40 |
+
2020-3-24,57.0,472.0,0.0
|
| 41 |
+
2020-3-25,163.0,626.0,2.0
|
| 42 |
+
2020-3-26,182.0,799.0,0.0
|
| 43 |
+
2020-3-27,196.0,987.0,1.0
|
| 44 |
+
2020-3-28,228.0,1211.0,0.0
|
| 45 |
+
2020-3-29,270.0,1462.0,4.0
|
| 46 |
+
2020-3-30,302.0,1761.0,1.0
|
| 47 |
+
2020-3-31,501.0,2199.0,8.0
|
| 48 |
+
2020-4-01,440.0,2563.0,7.0
|
| 49 |
+
2020-4-02,771.0,3283.0,6.0
|
| 50 |
+
2020-4-03,601.0,3834.0,4.0
|
| 51 |
+
2020-4-04,582.0,4355.0,9.0
|
| 52 |
+
2020-4-05,658.0,4989.0,2.0
|
| 53 |
+
2020-4-06,954.0,5890.0,2.0
|
| 54 |
+
2020-4-07,1154.0,6945.0,11.0
|
| 55 |
+
2020-4-08,1175.0,8029.0,5.0
|
| 56 |
+
2020-4-09,1459.0,9357.0,13.0
|
| 57 |
+
2020-4-10,1786.0,11028.0,18.0
|
| 58 |
+
2020-4-11,1667.0,12433.0,12.0
|
| 59 |
+
2020-4-12,2186.0,14349.0,24.0
|
| 60 |
+
2020-4-13,2558.0,16710.0,18.0
|
| 61 |
+
2020-4-14,2774.0,19238.0,22.0
|
| 62 |
+
2020-4-15,3388.0,22306.0,28.0
|
| 63 |
+
2020-4-16,3448.0,25402.0,34.0
|
| 64 |
+
2020-4-17,4070.0,29145.0,41.0
|
| 65 |
+
2020-4-18,4785.0,33423.0,40.0
|
| 66 |
+
2020-4-19,6060.0,39201.0,48.0
|
| 67 |
+
2020-4-20,4268.0,43270.0,44.0
|
| 68 |
+
2020-4-21,5642.0,48434.0,51.0
|
| 69 |
+
2020-4-22,5236.0,53066.0,57.0
|
| 70 |
+
2020-4-23,4774.0,57327.0,42.0
|
| 71 |
+
2020-4-24,5849.0,62439.0,60.0
|
| 72 |
+
2020-4-25,5966.0,67657.0,66.0
|
| 73 |
+
2020-4-26,6361.0,73435.0,66.0
|
| 74 |
+
2020-4-27,6198.0,79007.0,47.0
|
| 75 |
+
2020-4-28,6411.0,84235.0,73.0
|
| 76 |
+
2020-4-29,5841.0,88141.0,105.0
|
| 77 |
+
2020-4-30,7099.0,93806.0,101.0
|
| 78 |
+
2020-5-01,7933.0,100042.0,96.0
|
| 79 |
+
2020-5-02,9623.0,107819.0,53.0
|
| 80 |
+
2020-5-03,10633.0,116768.0,58.0
|
| 81 |
+
2020-5-04,10581.0,125817.0,76.0
|
| 82 |
+
2020-5-05,10102.0,134054.0,95.0
|
| 83 |
+
2020-5-06,10559.0,143065.0,86.0
|
| 84 |
+
2020-5-07,11231.0,151732.0,88.0
|
| 85 |
+
2020-5-08,10699.0,159528.0,98.0
|
| 86 |
+
2020-5-09,10817.0,164933.0,104.0
|
| 87 |
+
2020-5-10,11012.0,173467.0,88.0
|
| 88 |
+
2020-5-11,11656.0,179534.0,94.0
|
| 89 |
+
2020-5-12,10899.0,186615.0,107.0
|
| 90 |
+
2020-5-13,10028.0,192056.0,96.0
|
| 91 |
+
2020-5-14,9974.0,196410.0,93.0
|
| 92 |
+
2020-5-15,10598.0,202199.0,113.0
|
| 93 |
+
2020-5-16,9200.0,206340.0,119.0
|
| 94 |
+
2020-5-17,9709.0,211748.0,94.0
|
| 95 |
+
2020-5-18,8926.0,217747.0,91.0
|
| 96 |
+
2020-5-19,9263.0,220974.0,115.0
|
| 97 |
+
2020-5-20,8764.0,220341.0,135.0
|
| 98 |
+
2020-5-21,8849.0,221774.0,127.0
|
| 99 |
+
2020-5-22,8894.0,223374.0,150.0
|
| 100 |
+
2020-5-23,9434.0,224558.0,139.0
|
| 101 |
+
2020-5-24,8599.0,227641.0,153.0
|
| 102 |
+
2020-5-25,8946.0,230996.0,92.0
|
| 103 |
+
2020-5-26,8915.0,227406.0,174.0
|
| 104 |
+
2020-5-27,8338.0,224504.0,161.0
|
| 105 |
+
2020-5-28,8371.0,223916.0,174.0
|
| 106 |
+
2020-5-29,8572.0,223992.0,232.0
|
| 107 |
+
2020-5-30,8952.0,224551.0,181.0
|
| 108 |
+
2020-5-31,9268.0,229267.0,138.0
|
| 109 |
+
2020-6-01,9035.0,234146.0,162.0
|
| 110 |
+
2020-6-02,8863.0,231719.0,182.0
|
| 111 |
+
2020-6-03,8536.0,231105.0,178.0
|
| 112 |
+
2020-6-04,8831.0,231101.0,169.0
|
| 113 |
+
2020-6-05,8726.0,231626.0,144.0
|
| 114 |
+
2020-6-06,8855.0,231576.0,197.0
|
| 115 |
+
2020-6-07,8984.0,235083.0,134.0
|
| 116 |
+
2020-6-08,8985.0,239999.0,112.0
|
| 117 |
+
2020-6-09,8595.0,236714.0,171.0
|
| 118 |
+
2020-6-10,8404.0,234516.0,216.0
|
| 119 |
+
2020-6-11,8779.0,234754.0,174.0
|
| 120 |
+
2020-6-12,8987.0,235338.0,183.0
|
| 121 |
+
2020-6-13,8706.0,238659.0,114.0
|
| 122 |
+
2020-6-14,8835.0,241966.0,119.0
|
| 123 |
+
2020-6-15,8246.0,245580.0,143.0
|
| 124 |
+
2020-6-16,8248.0,243868.0,193.0
|
| 125 |
+
2020-6-17,7843.0,241481.0,194.0
|
| 126 |
+
2020-6-18,7790.0,239468.0,182.0
|
| 127 |
+
2020-6-19,7972.0,236816.0,181.0
|
| 128 |
+
2020-6-20,7889.0,234358.0,161.0
|
| 129 |
+
2020-6-21,7728.0,236858.0,109.0
|
| 130 |
+
2020-6-22,7600.0,239658.0,95.0
|
| 131 |
+
2020-6-23,7425.0,234917.0,153.0
|
| 132 |
+
2020-6-24,7176.0,229546.0,154.0
|
| 133 |
+
2020-6-25,7113.0,230225.0,92.0
|
| 134 |
+
2020-6-26,6800.0,227861.0,176.0
|
| 135 |
+
2020-6-27,6852.0,225325.0,188.0
|
| 136 |
+
2020-6-28,6791.0,226277.0,104.0
|
| 137 |
+
2020-6-29,6719.0,228560.0,93.0
|
| 138 |
+
2020-6-30,6693.0,225879.0,154.0
|
| 139 |
+
2020-7-01,6556.0,221938.0,216.0
|
| 140 |
+
2020-7-02,6760.0,222504.0,147.0
|
| 141 |
+
2020-7-03,6718.0,220131.0,176.0
|
| 142 |
+
2020-7-04,6632.0,217609.0,168.0
|
| 143 |
+
2020-7-05,6736.0,220340.0,134.0
|
| 144 |
+
2020-7-06,6611.0,223237.0,135.0
|
| 145 |
+
2020-7-07,6368.0,219856.0,198.0
|
| 146 |
+
2020-7-08,6562.0,217614.0,173.0
|
| 147 |
+
2020-7-09,6509.0,215142.0,176.0
|
| 148 |
+
2020-7-10,6635.0,213851.0,174.0
|
| 149 |
+
2020-7-11,6611.0,211896.0,188.0
|
| 150 |
+
2020-7-12,6615.0,214766.0,130.0
|
| 151 |
+
2020-7-13,6537.0,218239.0,104.0
|
| 152 |
+
2020-7-14,6248.0,215508.0,175.0
|
| 153 |
+
2020-7-15,6422.0,211350.0,156.0
|
| 154 |
+
2020-7-16,6428.0,209168.0,167.0
|
| 155 |
+
2020-7-17,6406.0,207707.0,186.0
|
| 156 |
+
2020-7-18,6234.0,206327.0,124.0
|
| 157 |
+
2020-7-19,6109.0,208860.0,95.0
|
| 158 |
+
2020-7-20,5940.0,211457.0,85.0
|
| 159 |
+
2020-7-21,5842.0,208364.0,153.0
|
| 160 |
+
2020-7-22,5862.0,204392.0,165.0
|
| 161 |
+
2020-7-23,5848.0,201816.0,147.0
|
| 162 |
+
2020-7-24,5811.0,199029.0,154.0
|
| 163 |
+
2020-7-25,5871.0,196388.0,146.0
|
| 164 |
+
2020-7-26,5765.0,198966.0,77.0
|
| 165 |
+
2020-7-27,5635.0,201437.0,85.0
|
| 166 |
+
2020-7-28,5395.0,197794.0,150.0
|
| 167 |
+
2020-7-29,5475.0,194984.0,169.0
|
| 168 |
+
2020-7-30,5509.0,191042.0,129.0
|
| 169 |
+
2020-7-31,5482.0,187608.0,161.0
|
| 170 |
+
2020-8-01,5462.0,184861.0,95.0
|
| 171 |
+
2020-8-02,5427.0,186569.0,70.0
|
| 172 |
+
2020-8-03,5394.0,188464.0,79.0
|
| 173 |
+
2020-8-04,5159.0,185601.0,144.0
|
| 174 |
+
2020-8-05,5204.0,183111.0,139.0
|
| 175 |
+
2020-8-06,5267.0,180931.0,116.0
|
| 176 |
+
2020-8-07,5241.0,178818.0,119.0
|
| 177 |
+
2020-8-08,5212.0,177286.0,129.0
|
| 178 |
+
2020-8-09,5189.0,179183.0,77.0
|
| 179 |
+
2020-8-10,5118.0,180972.0,70.0
|
| 180 |
+
2020-8-11,4945.0,179293.0,130.0
|
| 181 |
+
2020-8-12,5102.0,177143.0,129.0
|
| 182 |
+
2020-8-13,5057.0,175978.0,124.0
|
| 183 |
+
2020-8-14,5065.0,174361.0,114.0
|
| 184 |
+
2020-8-15,5061.0,172856.0,119.0
|
| 185 |
+
2020-8-16,4969.0,174200.0,68.0
|
| 186 |
+
2020-8-17,4892.0,175904.0,55.0
|
| 187 |
+
2020-8-18,4748.0,173993.0,132.0
|
| 188 |
+
2020-8-19,4828.0,171909.0,117.0
|
| 189 |
+
2020-8-20,4785.0,170494.0,110.0
|
| 190 |
+
2020-8-21,4870.0,169457.0,90.0
|
| 191 |
+
2020-8-22,4921.0,168110.0,121.0
|
| 192 |
+
2020-8-23,4852.0,169727.0,73.0
|
| 193 |
+
2020-8-24,4744.0,171950.0,65.0
|
| 194 |
+
2020-8-25,4696.0,169874.0,120.0
|
| 195 |
+
2020-8-26,4676.0,168032.0,115.0
|
| 196 |
+
2020-8-27,4711.0,166211.0,121.0
|
| 197 |
+
2020-8-28,4829.0,165025.0,110.0
|
| 198 |
+
2020-8-29,4941.0,163938.0,111.0
|
| 199 |
+
2020-8-30,4980.0,166251.0,68.0
|
| 200 |
+
2020-8-31,4993.0,168756.0,83.0
|
| 201 |
+
2020-9-01,4729.0,167044.0,123.0
|
| 202 |
+
2020-9-02,4952.0,166417.0,115.0
|
| 203 |
+
2020-9-03,4995.0,165532.0,114.0
|
| 204 |
+
2020-9-04,5110.0,164709.0,121.0
|
| 205 |
+
2020-9-05,5205.0,164425.0,110.0
|
| 206 |
+
2020-9-06,5195.0,166736.0,61.0
|
| 207 |
+
2020-9-07,5185.0,169542.0,51.0
|
| 208 |
+
2020-9-08,5099.0,167747.0,122.0
|
| 209 |
+
2020-9-09,5218.0,166414.0,142.0
|
| 210 |
+
2020-9-10,5363.0,165734.0,128.0
|
| 211 |
+
2020-9-11,5504.0,165402.0,102.0
|
| 212 |
+
2020-9-12,5488.0,165343.0,119.0
|
| 213 |
+
2020-9-13,5449.0,168008.0,94.0
|
| 214 |
+
2020-9-14,5509.0,170985.0,57.0
|
| 215 |
+
2020-9-15,5529.0,170759.0,150.0
|
| 216 |
+
2020-9-16,5670.0,170488.0,132.0
|
| 217 |
+
2020-9-17,5762.0,170352.0,144.0
|
| 218 |
+
2020-9-18,5905.0,170784.0,134.0
|
| 219 |
+
2020-9-19,6065.0,171450.0,144.0
|
| 220 |
+
2020-9-20,6148.0,174624.0,79.0
|
| 221 |
+
2020-9-21,6196.0,178133.0,71.0
|
| 222 |
+
2020-9-22,6215.0,178212.0,160.0
|
| 223 |
+
2020-9-23,6431.0,178743.0,150.0
|
| 224 |
+
2020-9-24,6595.0,179059.0,149.0
|
| 225 |
+
2020-9-25,7212.0,181846.0,108.0
|
| 226 |
+
2020-9-26,7523.0,183196.0,169.0
|
| 227 |
+
2020-9-27,7867.0,187896.0,99.0
|
| 228 |
+
2020-9-28,8135.0,193268.0,61.0
|
| 229 |
+
2020-9-29,8232.0,194861.0,160.0
|
| 230 |
+
2020-9-30,8481.0,197307.0,177.0
|
| 231 |
+
2020-10-01,8945.0,200098.0,169.0
|
| 232 |
+
2020-10-02,9412.0,203270.0,186.0
|
| 233 |
+
2020-10-03,9859.0,207392.0,174.0
|
| 234 |
+
2020-10-04,10499.0,214500.0,107.0
|
| 235 |
+
2020-10-05,10888.0,222090.0,117.0
|
| 236 |
+
2020-10-06,11615.0,227265.0,188.0
|
| 237 |
+
2020-10-07,11115.0,231479.0,202.0
|
| 238 |
+
2020-10-08,11493.0,235727.0,191.0
|
| 239 |
+
2020-10-09,12126.0,240560.0,201.0
|
| 240 |
+
2020-10-10,12846.0,246428.0,197.0
|
| 241 |
+
2020-10-11,13634.0,255679.0,143.0
|
| 242 |
+
2020-10-12,13592.0,265353.0,125.0
|
| 243 |
+
2020-10-13,13868.0,271427.0,244.0
|
| 244 |
+
2020-10-14,14231.0,277499.0,239.0
|
| 245 |
+
2020-10-15,13754.0,282575.0,286.0
|
| 246 |
+
2020-10-16,15150.0,289008.0,232.0
|
| 247 |
+
2020-10-17,14922.0,295034.0,279.0
|
| 248 |
+
2020-10-18,15099.0,304571.0,185.0
|
| 249 |
+
2020-10-19,15982.0,315046.0,179.0
|
| 250 |
+
2020-10-20,16319.0,321392.0,269.0
|
| 251 |
+
2020-10-21,15700.0,325823.0,317.0
|
| 252 |
+
2020-10-22,15971.0,330076.0,290.0
|
| 253 |
+
2020-10-23,17340.0,335870.0,283.0
|
| 254 |
+
2020-10-24,16521.0,340528.0,296.0
|
| 255 |
+
2020-10-25,16710.0,349305.0,229.0
|
| 256 |
+
2020-10-26,17347.0,358859.0,219.0
|
| 257 |
+
2020-10-27,16550.0,362245.0,320.0
|
| 258 |
+
2020-10-28,16202.0,365740.0,346.0
|
| 259 |
+
2020-10-29,17717.0,368351.0,366.0
|
| 260 |
+
2020-10-30,18283.0,371760.0,355.0
|
| 261 |
+
2020-10-31,18140.0,374712.0,334.0
|
| 262 |
+
2020-11-01,18665.0,382873.0,245.0
|
| 263 |
+
2020-11-02,18257.0,390532.0,238.0
|
| 264 |
+
2020-11-03,18648.0,393494.0,355.0
|
| 265 |
+
2020-11-04,19768.0,397306.0,389.0
|
| 266 |
+
2020-11-05,19404.0,404180.0,292.0
|
| 267 |
+
2020-11-06,20582.0,407429.0,378.0
|
| 268 |
+
2020-11-07,20396.0,410658.0,364.0
|
| 269 |
+
2020-11-08,20498.0,419378.0,286.0
|
| 270 |
+
2020-11-09,21798.0,430198.0,256.0
|
| 271 |
+
2020-11-10,20977.0,435207.0,368.0
|
| 272 |
+
2020-11-11,19851.0,436010.0,432.0
|
| 273 |
+
2020-11-12,21608.0,438368.0,439.0
|
| 274 |
+
2020-11-13,21983.0,441205.0,411.0
|
| 275 |
+
2020-11-14,22702.0,444890.0,391.0
|
| 276 |
+
2020-11-15,22572.0,452654.0,352.0
|
| 277 |
+
2020-11-16,22778.0,461265.0,303.0
|
| 278 |
+
2020-11-17,22410.0,461178.0,442.0
|
| 279 |
+
2020-11-18,20985.0,456528.0,456.0
|
| 280 |
+
2020-11-19,23610.0,454102.0,463.0
|
| 281 |
+
2020-11-20,24318.0,453201.0,461.0
|
| 282 |
+
2020-11-21,24822.0,451535.0,467.0
|
| 283 |
+
2020-11-22,24581.0,457707.0,401.0
|
| 284 |
+
2020-11-23,25173.0,466517.0,361.0
|
| 285 |
+
2020-11-24,24326.0,467126.0,491.0
|
| 286 |
+
2020-11-25,23675.0,464546.0,507.0
|
| 287 |
+
2020-11-26,25487.0,464436.0,524.0
|
| 288 |
+
2020-11-27,27543.0,464801.0,496.0
|
| 289 |
+
2020-11-28,27100.0,464095.0,510.0
|
| 290 |
+
2020-11-29,26683.0,468332.0,459.0
|
| 291 |
+
2020-11-30,26338.0,477055.0,368.0
|
| 292 |
+
2020-12-01,26402.0,478125.0,569.0
|
| 293 |
+
2020-12-02,25345.0,475999.0,589.0
|
| 294 |
+
2020-12-03,28145.0,474088.0,554.0
|
| 295 |
+
2020-12-04,27403.0,472021.0,569.0
|
| 296 |
+
2020-12-05,28782.0,472651.0,508.0
|
| 297 |
+
2020-12-06,29039.0,479891.0,457.0
|
| 298 |
+
2020-12-07,28142.0,488727.0,456.0
|
| 299 |
+
2020-12-08,26097.0,489324.0,562.0
|
| 300 |
+
2020-12-09,26190.0,488689.0,559.0
|
| 301 |
+
2020-12-10,27927.0,490177.0,562.0
|
| 302 |
+
2020-12-11,28585.0,491978.0,613.0
|
| 303 |
+
2020-12-12,28137.0,493437.0,560.0
|
| 304 |
+
2020-12-13,28080.0,500752.0,488.0
|
| 305 |
+
2020-12-14,27328.0,509068.0,450.0
|
| 306 |
+
2020-12-15,26689.0,510367.0,577.0
|
| 307 |
+
2020-12-16,26509.0,509790.0,596.0
|
| 308 |
+
2020-12-17,28214.0,510977.0,587.0
|
| 309 |
+
2020-12-18,28552.0,512825.0,611.0
|
| 310 |
+
2020-12-19,28209.0,514340.0,585.0
|
| 311 |
+
2020-12-20,28948.0,521862.0,511.0
|
| 312 |
+
2020-12-21,29350.0,531014.0,493.0
|
| 313 |
+
2020-12-22,28776.0,535071.0,561.0
|
| 314 |
+
2020-12-23,27250.0,537325.0,549.0
|
| 315 |
+
2020-12-24,29935.0,539735.0,635.0
|
| 316 |
+
2020-12-25,29018.0,540793.0,563.0
|
| 317 |
+
2020-12-26,29258.0,541299.0,567.0
|
| 318 |
+
2020-12-27,28284.0,544641.0,552.0
|
| 319 |
+
2020-12-28,27787.0,551461.0,487.0
|
| 320 |
+
2020-12-29,27002.0,553027.0,562.0
|
| 321 |
+
2020-12-30,26513.0,549706.0,599.0
|
| 322 |
+
2020-12-31,27747.0,547938.0,593.0
|
| 323 |
+
2021-1-01,27039.0,548643.0,536.0
|
| 324 |
+
2021-1-02,26301.0,555600.0,447.0
|
| 325 |
+
2021-1-03,24150.0,559399.0,504.0
|
| 326 |
+
2021-1-04,23351.0,561114.0,482.0
|
| 327 |
+
2021-1-05,24246.0,562210.0,518.0
|
| 328 |
+
2021-1-06,24217.0,562927.0,445.0
|
| 329 |
+
2021-1-07,23541.0,562233.0,506.0
|
| 330 |
+
2021-1-08,23652.0,563754.0,454.0
|
| 331 |
+
2021-1-09,23309.0,562913.0,470.0
|
| 332 |
+
2021-1-10,22851.0,561228.0,456.0
|
| 333 |
+
2021-1-11,23315.0,562321.0,436.0
|
| 334 |
+
2021-1-12,22934.0,559969.0,531.0
|
| 335 |
+
2021-1-13,22850.0,553595.0,566.0
|
| 336 |
+
2021-1-14,24763.0,549832.0,570.0
|
| 337 |
+
2021-1-15,24715.0,546356.0,555.0
|
| 338 |
+
2021-1-16,24092.0,542547.0,590.0
|
| 339 |
+
2021-1-17,23586.0,542212.0,481.0
|
| 340 |
+
2021-1-18,22857.0,546265.0,471.0
|
| 341 |
+
2021-1-19,21734.0,544151.0,586.0
|
| 342 |
+
2021-1-20,21152.0,539416.0,597.0
|
| 343 |
+
2021-1-21,21887.0,533789.0,612.0
|
| 344 |
+
2021-1-22,21513.0,527404.0,580.0
|
| 345 |
+
2021-1-23,20921.0,519987.0,559.0
|
| 346 |
+
2021-1-24,21127.0,518178.0,491.0
|
| 347 |
+
2021-1-25,19290.0,518009.0,456.0
|
| 348 |
+
2021-1-26,18241.0,511888.0,564.0
|
| 349 |
+
2021-1-27,17741.0,501113.0,594.0
|
| 350 |
+
2021-1-28,19138.0,492901.0,575.0
|
| 351 |
+
2021-1-29,19238.0,485401.0,534.0
|
| 352 |
+
2021-1-30,19032.0,479419.0,512.0
|
| 353 |
+
2021-1-31,18359.0,477253.0,485.0
|
| 354 |
+
2021-2-01,17648.0,476295.0,437.0
|
| 355 |
+
2021-2-02,16643.0,470027.0,539.0
|
| 356 |
+
2021-2-03,16474.0,461153.0,526.0
|
| 357 |
+
2021-2-04,16714.0,452800.0,521.0
|
| 358 |
+
2021-2-05,16688.0,445379.0,527.0
|
| 359 |
+
2021-2-06,16627.0,438678.0,497.0
|
| 360 |
+
2021-2-07,16048.0,434410.0,432.0
|
| 361 |
+
2021-2-08,15916.0,434038.0,407.0
|
| 362 |
+
2021-2-09,15019.0,426732.0,530.0
|
| 363 |
+
2021-2-10,14494.0,418115.0,536.0
|
| 364 |
+
2021-2-11,15038.0,410639.0,553.0
|
| 365 |
+
2021-2-12,15089.0,404501.0,507.0
|
| 366 |
+
2021-2-13,14861.0,400095.0,502.0
|
| 367 |
+
2021-2-14,14185.0,398656.0,430.0
|
| 368 |
+
2021-2-15,14207.0,398534.0,394.0
|
| 369 |
+
2021-2-16,13233.0,393681.0,459.0
|
| 370 |
+
2021-2-17,12828.0,388123.0,467.0
|
| 371 |
+
2021-2-18,13447.0,382360.0,480.0
|
| 372 |
+
2021-2-19,13433.0,376686.0,470.0
|
| 373 |
+
2021-2-20,12953.0,371675.0,480.0
|
| 374 |
+
2021-2-21,12742.0,367988.0,417.0
|
| 375 |
+
2021-2-22,12604.0,367312.0,337.0
|
| 376 |
+
2021-2-23,11823.0,365762.0,417.0
|
| 377 |
+
2021-2-24,11749.0,364910.0,383.0
|
| 378 |
+
2021-2-25,11198.0,359560.0,446.0
|
| 379 |
+
2021-2-26,11086.0,354496.0,428.0
|
| 380 |
+
2021-2-27,11534.0,349571.0,439.0
|
| 381 |
+
2021-2-28,11359.0,348160.0,379.0
|
| 382 |
+
2021-3-01,11571.0,348121.0,333.0
|
| 383 |
+
2021-3-02,10565.0,343279.0,441.0
|
| 384 |
+
2021-3-03,10535.0,337668.0,452.0
|
| 385 |
+
2021-3-04,11385.0,332455.0,475.0
|
| 386 |
+
2021-3-05,11024.0,327553.0,462.0
|
| 387 |
+
2021-3-06,11022.0,323107.0,441.0
|
| 388 |
+
2021-3-07,10595.0,321758.0,368.0
|
| 389 |
+
2021-3-08,10253.0,321310.0,379.0
|
| 390 |
+
2021-3-09,9445.0,320488.0,336.0
|
| 391 |
+
2021-3-10,9079.0,315751.0,466.0
|
| 392 |
+
2021-3-11,9270.0,310556.0,459.0
|
| 393 |
+
2021-3-12,9794.0,306368.0,486.0
|
| 394 |
+
2021-3-13,9908.0,302933.0,475.0
|
| 395 |
+
2021-3-14,10083.0,303209.0,395.0
|
| 396 |
+
2021-3-15,9437.0,303975.0,404.0
|
| 397 |
+
2021-3-16,9393.0,302281.0,443.0
|
| 398 |
+
2021-3-17,8998.0,300097.0,427.0
|
| 399 |
+
2021-3-18,9803.0,297379.0,460.0
|
| 400 |
+
2021-3-19,9699.0,294298.0,443.0
|
| 401 |
+
2021-3-20,9632.0,292259.0,392.0
|
| 402 |
+
2021-3-21,9299.0,292444.0,371.0
|
| 403 |
+
2021-3-22,9284.0,293577.0,361.0
|
| 404 |
+
2021-3-23,8457.0,290747.0,427.0
|
| 405 |
+
2021-3-24,8861.0,288852.0,401.0
|
| 406 |
+
2021-3-25,9221.0,286799.0,393.0
|
| 407 |
+
2021-3-26,9167.0,284681.0,405.0
|
| 408 |
+
2021-3-27,8885.0,282842.0,387.0
|
| 409 |
+
2021-3-28,9088.0,282964.0,336.0
|
| 410 |
+
2021-3-29,8711.0,284102.0,293.0
|
| 411 |
+
2021-3-30,8277.0,282382.0,409.0
|
| 412 |
+
2021-3-31,8275.0,280073.0,408.0
|
| 413 |
+
2021-4-01,9169.0,278612.0,383.0
|
| 414 |
+
2021-4-02,8792.0,277172.0,400.0
|
| 415 |
+
2021-4-03,9021.0,276191.0,384.0
|
| 416 |
+
2021-4-04,8817.0,276439.0,357.0
|
| 417 |
+
2021-4-05,8646.0,277690.0,343.0
|
| 418 |
+
2021-4-06,8328.0,276727.0,389.0
|
| 419 |
+
2021-4-07,8294.0,275202.0,374.0
|
| 420 |
+
2021-4-08,8672.0,273951.0,365.0
|
| 421 |
+
2021-4-09,9150.0,273037.0,402.0
|
| 422 |
+
2021-4-10,8704.0,271760.0,402.0
|
| 423 |
+
2021-4-11,8702.0,272895.0,337.0
|
| 424 |
+
2021-4-12,8320.0,274282.0,277.0
|
| 425 |
+
2021-4-13,8173.0,272506.0,338.0
|
| 426 |
+
2021-4-14,8326.0,270986.0,399.0
|
| 427 |
+
2021-4-15,8944.0,269307.0,398.0
|
| 428 |
+
2021-4-16,8995.0,268796.0,397.0
|
| 429 |
+
2021-4-17,9321.0,268887.0,398.0
|
| 430 |
+
2021-4-18,8632.0,269739.0,389.0
|
| 431 |
+
2021-4-19,8589.0,271164.0,346.0
|
| 432 |
+
2021-4-20,8164.0,269318.0,379.0
|
| 433 |
+
2021-4-21,8271.0,267546.0,399.0
|
| 434 |
+
2021-4-22,8996.0,267211.0,397.0
|
| 435 |
+
2021-4-23,8840.0,266246.0,398.0
|
| 436 |
+
2021-4-24,8828.0,265421.0,399.0
|
| 437 |
+
2021-4-25,8780.0,266329.0,332.0
|
| 438 |
+
2021-4-26,8803.0,268145.0,356.0
|
| 439 |
+
2021-4-27,8053.0,267767.0,392.0
|
| 440 |
+
2021-4-28,7848.0,266808.0,387.0
|
| 441 |
+
2021-4-29,9284.0,267286.0,364.0
|
| 442 |
+
2021-4-30,8731.0,267214.0,397.0
|
| 443 |
+
2021-5-01,9270.0,267455.0,392.0
|
| 444 |
+
2021-5-02,8697.0,268471.0,342.0
|
| 445 |
+
2021-5-03,8489.0,270257.0,336.0
|
| 446 |
+
2021-5-04,7770.0,270935.0,337.0
|
| 447 |
+
2021-5-05,7975.0,271044.0,360.0
|
| 448 |
+
2021-5-06,7639.0,270544.0,351.0
|
| 449 |
+
2021-5-07,8386.0,270532.0,376.0
|
| 450 |
+
2021-5-08,8329.0,270236.0,370.0
|
| 451 |
+
2021-5-09,8419.0,270804.0,334.0
|
| 452 |
+
2021-5-10,8465.0,272174.0,321.0
|
| 453 |
+
2021-5-11,8115.0,272951.0,329.0
|
| 454 |
+
2021-5-12,8217.0,272199.0,355.0
|
| 455 |
+
2021-5-13,8380.0,270838.0,392.0
|
| 456 |
+
2021-5-14,9462.0,270151.0,393.0
|
| 457 |
+
2021-5-15,8790.0,268711.0,364.0
|
| 458 |
+
2021-5-16,8554.0,268301.0,391.0
|
| 459 |
+
2021-5-17,9328.0,270108.0,340.0
|
| 460 |
+
2021-5-18,8183.0,268955.0,364.0
|
| 461 |
+
2021-5-19,7920.0,266924.0,390.0
|
| 462 |
+
2021-5-20,9232.0,265777.0,396.0
|
| 463 |
+
2021-5-21,8937.0,264986.0,378.0
|
| 464 |
+
2021-5-22,8709.0,263964.0,386.0
|
| 465 |
+
2021-5-23,8951.0,265261.0,357.0
|
| 466 |
+
2021-5-24,8406.0,266898.0,319.0
|
| 467 |
+
2021-5-25,7884.0,265646.0,393.0
|
| 468 |
+
2021-5-26,8373.0,264478.0,406.0
|
| 469 |
+
2021-5-27,9039.0,263356.0,402.0
|
| 470 |
+
2021-5-28,9252.0,262819.0,404.0
|
| 471 |
+
2021-5-29,9289.0,262457.0,401.0
|
| 472 |
+
2021-5-30,9694.0,264410.0,355.0
|
| 473 |
+
2021-5-31,8475.0,265831.0,339.0
|
| 474 |
+
2021-6-01,9500.0,265965.0,372.0
|
| 475 |
+
2021-6-02,8832.0,265383.0,394.0
|
| 476 |
+
2021-6-03,8933.0,264540.0,393.0
|
| 477 |
+
2021-6-04,8947.0,264580.0,377.0
|
| 478 |
+
2021-6-05,9145.0,264761.0,399.0
|
| 479 |
+
2021-6-06,9163.0,266204.0,351.0
|
| 480 |
+
2021-6-07,9429.0,268547.0,330.0
|
| 481 |
+
2021-6-08,9977.0,269262.0,379.0
|
| 482 |
+
2021-6-09,10407.0,269456.0,399.0
|
| 483 |
+
2021-6-10,11699.0,270676.0,383.0
|
| 484 |
+
2021-6-11,12505.0,272597.0,396.0
|
| 485 |
+
2021-6-12,13510.0,275722.0,399.0
|
| 486 |
+
2021-6-13,14723.0,280922.0,357.0
|
| 487 |
+
2021-6-14,13721.0,285960.0,371.0
|
| 488 |
+
2021-6-15,14185.0,291169.0,379.0
|
| 489 |
+
2021-6-16,13397.0,293914.0,396.0
|
| 490 |
+
2021-6-17,14057.0,296350.0,416.0
|
| 491 |
+
2021-6-18,17262.0,302205.0,453.0
|
| 492 |
+
2021-6-19,17906.0,308961.0,466.0
|
| 493 |
+
2021-6-20,17611.0,317493.0,450.0
|
| 494 |
+
2021-6-21,17378.0,326070.0,440.0
|
| 495 |
+
2021-6-22,16715.0,331122.0,546.0
|
| 496 |
+
2021-6-23,17594.0,335508.0,548.0
|
| 497 |
+
2021-6-24,20182.0,341617.0,568.0
|
| 498 |
+
2021-6-25,20393.0,347385.0,601.0
|
| 499 |
+
2021-6-26,21665.0,354084.0,619.0
|
| 500 |
+
2021-6-27,20538.0,361295.0,599.0
|
| 501 |
+
2021-6-28,21650.0,369708.0,611.0
|
| 502 |
+
2021-6-29,20616.0,374975.0,652.0
|
| 503 |
+
2021-6-30,21042.0,378992.0,669.0
|
| 504 |
+
2021-7-01,23543.0,384935.0,672.0
|
| 505 |
+
2021-7-02,23218.0,389277.0,679.0
|
| 506 |
+
2021-7-03,24439.0,395120.0,697.0
|
| 507 |
+
2021-7-04,25142.0,404115.0,663.0
|
| 508 |
+
2021-7-05,24353.0,413274.0,654.0
|
| 509 |
+
2021-7-06,23378.0,417504.0,737.0
|
| 510 |
+
2021-7-07,23962.0,420674.0,725.0
|
| 511 |
+
2021-7-08,24818.0,423422.0,734.0
|
| 512 |
+
2021-7-09,25766.0,426630.0,726.0
|
| 513 |
+
2021-7-10,25082.0,433210.0,752.0
|
| 514 |
+
2021-7-11,25033.0,440112.0,749.0
|
| 515 |
+
2021-7-12,25140.0,448113.0,710.0
|
| 516 |
+
2021-7-13,24702.0,452469.0,780.0
|
| 517 |
+
2021-7-14,23827.0,454241.0,786.0
|
| 518 |
+
2021-7-15,25293.0,457250.0,791.0
|
| 519 |
+
2021-7-16,25704.0,460223.0,799.0
|
| 520 |
+
2021-7-17,25116.0,463115.0,787.0
|
| 521 |
+
2021-7-18,25018.0,468483.0,764.0
|
| 522 |
+
2021-7-19,24633.0,473633.0,719.0
|
| 523 |
+
2021-7-20,23770.0,474401.0,784.0
|
| 524 |
+
2021-7-21,23704.0,474738.0,783.0
|
| 525 |
+
2021-7-22,24471.0,475753.0,796.0
|
| 526 |
+
2021-7-23,23811.0,476222.0,795.0
|
| 527 |
+
2021-7-24,23947.0,477418.0,799.0
|
| 528 |
+
2021-7-25,24072.0,482033.0,779.0
|
| 529 |
+
2021-7-26,23239.0,488345.0,727.0
|
| 530 |
+
2021-7-27,23032.0,490482.0,779.0
|
| 531 |
+
2021-7-28,22420.0,491525.0,798.0
|
| 532 |
+
2021-7-29,23270.0,493162.0,799.0
|
| 533 |
+
2021-7-30,23564.0,495447.0,794.0
|
| 534 |
+
2021-7-31,23807.0,498691.0,792.0
|
| 535 |
+
2021-8-01,22804.0,503435.0,789.0
|
| 536 |
+
2021-8-02,23508.0,511265.0,785.0
|
| 537 |
+
2021-8-03,22010.0,513524.0,788.0
|
| 538 |
+
2021-8-04,22589.0,515227.0,790.0
|
| 539 |
+
2021-8-05,23120.0,517183.0,794.0
|
| 540 |
+
2021-8-06,22660.0,518910.0,792.0
|
| 541 |
+
2021-8-07,22320.0,520952.0,793.0
|
| 542 |
+
2021-8-08,22866.0,527362.0,787.0
|
| 543 |
+
2021-8-09,22160.0,534279.0,769.0
|
| 544 |
+
2021-8-10,21378.0,536136.0,792.0
|
| 545 |
+
2021-8-11,21571.0,536841.0,799.0
|
| 546 |
+
2021-8-12,21932.0,537770.0,808.0
|
| 547 |
+
2021-8-13,22277.0,539864.0,815.0
|
| 548 |
+
2021-8-14,22144.0,541639.0,819.0
|
| 549 |
+
2021-8-15,21624.0,546021.0,816.0
|
| 550 |
+
2021-8-16,20765.0,550379.0,806.0
|
| 551 |
+
2021-8-17,20958.0,552125.0,805.0
|
| 552 |
+
2021-8-18,20914.0,551527.0,799.0
|
| 553 |
+
2021-8-19,21058.0,547777.0,791.0
|
| 554 |
+
2021-8-20,20992.0,547633.0,785.0
|
| 555 |
+
2021-8-21,21000.0,547189.0,797.0
|
| 556 |
+
2021-8-22,20564.0,551577.0,762.0
|
| 557 |
+
2021-8-23,19454.0,554854.0,776.0
|
| 558 |
+
2021-8-24,18833.0,554257.0,794.0
|
| 559 |
+
2021-8-25,19536.0,553330.0,809.0
|
| 560 |
+
2021-8-26,19630.0,552479.0,820.0
|
| 561 |
+
2021-8-27,19509.0,551973.0,798.0
|
| 562 |
+
2021-8-28,19492.0,551255.0,799.0
|
| 563 |
+
2021-8-29,19286.0,552940.0,797.0
|
| 564 |
+
2021-8-30,18325.0,556293.0,792.0
|
| 565 |
+
2021-8-31,17813.0,554687.0,795.0
|
| 566 |
+
2021-9-01,18368.0,553940.0,790.0
|
| 567 |
+
2021-9-02,18985.0,553458.0,798.0
|
| 568 |
+
2021-9-03,18856.0,552825.0,799.0
|
| 569 |
+
2021-9-04,18780.0,552072.0,796.0
|
| 570 |
+
2021-9-05,18645.0,554668.0,793.0
|
| 571 |
+
2021-9-06,17856.0,557458.0,790.0
|
| 572 |
+
2021-9-07,17425.0,556845.0,795.0
|
| 573 |
+
2021-9-08,18024.0,555810.0,797.0
|
| 574 |
+
2021-9-09,18380.0,553757.0,794.0
|
| 575 |
+
2021-9-10,18341.0,554188.0,789.0
|
| 576 |
+
2021-9-11,18891.0,554395.0,796.0
|
| 577 |
+
2021-9-12,18554.0,557664.0,788.0
|
| 578 |
+
2021-9-13,18178.0,562654.0,719.0
|
| 579 |
+
2021-9-14,17837.0,563803.0,781.0
|
| 580 |
+
2021-9-15,18841.0,564813.0,792.0
|
| 581 |
+
2021-9-16,19594.0,566287.0,794.0
|
| 582 |
+
2021-9-17,19905.0,568782.0,791.0
|
| 583 |
+
2021-9-18,20329.0,572065.0,799.0
|
| 584 |
+
2021-9-19,20174.0,578028.0,793.0
|
| 585 |
+
2021-9-20,19744.0,585002.0,778.0
|
| 586 |
+
2021-9-21,19179.0,587932.0,812.0
|
| 587 |
+
2021-9-22,19706.0,590719.0,817.0
|
| 588 |
+
2021-9-23,21438.0,594770.0,820.0
|
| 589 |
+
2021-9-24,21379.0,599493.0,828.0
|
| 590 |
+
2021-9-25,22041.0,604387.0,822.0
|
| 591 |
+
2021-9-26,22498.0,612409.0,805.0
|
| 592 |
+
2021-9-27,22236.0,620353.0,779.0
|
| 593 |
+
2021-9-28,21559.0,623692.0,852.0
|
| 594 |
+
2021-9-29,22430.0,626809.0,857.0
|
| 595 |
+
2021-9-30,23888.0,631004.0,867.0
|
| 596 |
+
2021-10-01,24522.0,634684.0,887.0
|
| 597 |
+
2021-10-02,25219.0,641165.0,886.0
|
| 598 |
+
2021-10-03,25769.0,650653.0,890.0
|
| 599 |
+
2021-10-04,25781.0,661025.0,883.0
|
| 600 |
+
2021-10-05,25110.0,666672.0,895.0
|
| 601 |
+
2021-10-06,25133.0,671035.0,929.0
|
| 602 |
+
2021-10-07,27550.0,677331.0,924.0
|
| 603 |
+
2021-10-08,27246.0,683075.0,936.0
|
| 604 |
+
2021-10-09,29362.0,690420.0,968.0
|
| 605 |
+
2021-10-10,28647.0,700831.0,962.0
|
| 606 |
+
2021-10-11,29409.0,713823.0,957.0
|
| 607 |
+
2021-10-12,28190.0,720334.0,973.0
|
| 608 |
+
2021-10-13,28717.0,726266.0,984.0
|
| 609 |
+
2021-10-14,31299.0,734909.0,986.0
|
| 610 |
+
2021-10-15,32196.0,743839.0,998.0
|
| 611 |
+
2021-10-16,33208.0,754162.0,1002.0
|
| 612 |
+
2021-10-17,34303.0,768751.0,997.0
|
| 613 |
+
2021-10-18,34325.0,785647.0,998.0
|
| 614 |
+
2021-10-19,33740.0,794946.0,1015.0
|
| 615 |
+
2021-10-20,34073.0,802760.0,1028.0
|
| 616 |
+
2021-10-21,36339.0,812168.0,1036.0
|
| 617 |
+
2021-10-22,37141.0,822792.0,1064.0
|
| 618 |
+
2021-10-23,37678.0,833318.0,1075.0
|
| 619 |
+
2021-10-24,35660.0,845122.0,1072.0
|
| 620 |
+
2021-10-25,37930.0,861293.0,1069.0
|
| 621 |
+
2021-10-26,36446.0,869660.0,1106.0
|
| 622 |
+
2021-10-27,36582.0,875968.0,1123.0
|
| 623 |
+
2021-10-28,40096.0,885587.0,1159.0
|
| 624 |
+
2021-10-29,39849.0,893811.0,1163.0
|
| 625 |
+
2021-10-30,40251.0,903993.0,1160.0
|
| 626 |
+
2021-10-31,40993.0,916713.0,1158.0
|
| 627 |
+
2021-11-01,40402.0,932773.0,1155.0
|
| 628 |
+
2021-11-02,39008.0,939698.0,1178.0
|
| 629 |
+
2021-11-03,40443.0,946145.0,1189.0
|
| 630 |
+
2021-11-04,40217.0,953239.0,1195.0
|
| 631 |
+
2021-11-05,40735.0,964177.0,1192.0
|
| 632 |
+
2021-11-06,41335.0,975123.0,1188.0
|
| 633 |
+
2021-11-07,39165.0,986303.0,1179.0
|
| 634 |
+
2021-11-08,39400.0,998931.0,1190.0
|
| 635 |
+
2021-11-09,39160.0,1004844.0,1211.0
|
| 636 |
+
2021-11-10,38058.0,1007098.0,1239.0
|
| 637 |
+
2021-11-11,40759.0,1013464.0,1237.0
|
| 638 |
+
2021-11-12,40123.0,1018707.0,1235.0
|
| 639 |
+
2021-11-13,39256.0,1022920.0,1241.0
|
| 640 |
+
2021-11-14,38823.0,1030703.0,1219.0
|
| 641 |
+
2021-11-15,38420.0,1040210.0,1211.0
|
| 642 |
+
2021-11-16,36818.0,1041627.0,1240.0
|
| 643 |
+
2021-11-17,36626.0,1040618.0,1247.0
|
| 644 |
+
2021-11-18,37374.0,1040327.0,1251.0
|
| 645 |
+
2021-11-19,37156.0,1039225.0,1254.0
|
| 646 |
+
2021-11-20,37120.0,1038919.0,1254.0
|
| 647 |
+
2021-11-21,36970.0,1042133.0,1252.0
|
| 648 |
+
2021-11-22,35681.0,1047860.0,1241.0
|
| 649 |
+
2021-11-23,33996.0,1044562.0,1243.0
|
| 650 |
+
2021-11-24,33558.0,1040198.0,1240.0
|
| 651 |
+
2021-11-25,33796.0,1034306.0,1238.0
|
| 652 |
+
2021-11-26,34690.0,1031616.0,1235.0
|
| 653 |
+
2021-11-27,33946.0,1027829.0,1239.0
|
| 654 |
+
2021-11-28,33548.0,1029507.0,1224.0
|
| 655 |
+
2021-11-29,33860.0,1034458.0,1209.0
|
| 656 |
+
2021-11-30,32648.0,1032435.0,1229.0
|
| 657 |
+
2021-12-01,32837.0,1028367.0,1226.0
|
| 658 |
+
2021-12-02,33389.0,1025350.0,1221.0
|
| 659 |
+
2021-12-03,32930.0,1020549.0,1217.0
|
| 660 |
+
2021-12-04,32974.0,1017126.0,1215.0
|
| 661 |
+
2021-12-05,32602.0,1017929.0,1206.0
|
| 662 |
+
2021-12-06,32136.0,1020811.0,1184.0
|
| 663 |
+
2021-12-07,31096.0,1016110.0,1182.0
|
| 664 |
+
2021-12-08,30752.0,1008707.0,1179.0
|
| 665 |
+
2021-12-09,30209.0,1001941.0,1181.0
|
| 666 |
+
2021-12-10,30873.0,995981.0,1176.0
|
| 667 |
+
2021-12-11,30288.0,988652.0,1171.0
|
| 668 |
+
2021-12-12,29929.0,986058.0,1132.0
|
| 669 |
+
2021-12-13,29558.0,985934.0,1121.0
|
| 670 |
+
2021-12-14,28343.0,979048.0,1145.0
|
| 671 |
+
2021-12-15,28363.0,970636.0,1142.0
|
| 672 |
+
2021-12-16,28486.0,960834.0,1133.0
|
| 673 |
+
2021-12-17,27743.0,950060.0,1080.0
|
| 674 |
+
2021-12-18,27434.0,938377.0,1076.0
|
| 675 |
+
2021-12-19,27967.0,932666.0,1023.0
|
| 676 |
+
2021-12-20,27022.0,928610.0,1019.0
|
| 677 |
+
2021-12-21,25907.0,913271.0,1027.0
|
| 678 |
+
2021-12-22,25264.0,895193.0,1020.0
|
| 679 |
+
2021-12-23,25667.0,878213.0,1002.0
|
| 680 |
+
2021-12-24,24703.0,860705.0,998.0
|
| 681 |
+
2021-12-25,24946.0,842563.0,981.0
|
| 682 |
+
2021-12-26,23721.0,828031.0,968.0
|
| 683 |
+
2021-12-27,23210.0,816589.0,937.0
|
| 684 |
+
2021-12-28,21922.0,793615.0,935.0
|
| 685 |
+
2021-12-29,21119.0,771026.0,932.0
|
| 686 |
+
2021-12-30,21073.0,748169.0,926.0
|
| 687 |
+
2021-12-31,20638.0,727203.0,912.0
|
| 688 |
+
2022-1-01,19751.0,712963.0,847.0
|
| 689 |
+
2022-1-02,18233.0,703409.0,811.0
|
| 690 |
+
2022-1-03,16343.0,694880.0,835.0
|
| 691 |
+
2022-1-04,15903.0,682878.0,834.0
|
| 692 |
+
2022-1-05,15772.0,672241.0,828.0
|
| 693 |
+
2022-1-06,15316.0,663806.0,802.0
|
| 694 |
+
2022-1-07,16735.0,657719.0,787.0
|
| 695 |
+
2022-1-08,16568.0,653042.0,796.0
|
| 696 |
+
2022-1-09,16246.0,647774.0,763.0
|
| 697 |
+
2022-1-10,15830.0,642973.0,741.0
|
| 698 |
+
2022-1-11,17525.0,634499.0,783.0
|
| 699 |
+
2022-1-12,17946.0,625354.0,745.0
|
| 700 |
+
2022-1-13,21155.0,619785.0,740.0
|
| 701 |
+
2022-1-14,23820.0,617914.0,739.0
|
| 702 |
+
2022-1-15,27179.0,617786.0,723.0
|
| 703 |
+
2022-1-16,29230.0,623599.0,686.0
|
| 704 |
+
2022-1-17,30726.0,633899.0,670.0
|
| 705 |
+
2022-1-18,31252.0,639899.0,688.0
|
| 706 |
+
2022-1-19,33899.0,650180.0,698.0
|
| 707 |
+
2022-1-20,38850.0,663868.0,684.0
|
| 708 |
+
2022-1-21,49513.0,687970.0,692.0
|
| 709 |
+
2022-1-22,57212.0,718976.0,681.0
|
| 710 |
+
2022-1-23,63205.0,758457.0,679.0
|
| 711 |
+
2022-1-24,65109.0,801197.0,655.0
|
| 712 |
+
2022-1-25,67809.0,841921.0,681.0
|
| 713 |
+
2022-1-26,74692.0,887759.0,657.0
|
| 714 |
+
2022-1-27,88816.0,946156.0,665.0
|
| 715 |
+
2022-1-28,98040.0,1014017.0,673.0
|
| 716 |
+
2022-1-29,113122.0,1096461.0,668.0
|
| 717 |
+
2022-1-30,121228.0,1188128.0,617.0
|
| 718 |
+
2022-1-31,124070.0,1281447.0,621.0
|
| 719 |
+
2022-2-01,125836.0,1366319.0,663.0
|
| 720 |
+
2022-2-02,141883.0,1459098.0,678.0
|
| 721 |
+
2022-2-03,155768.0,1560475.0,667.0
|
| 722 |
+
2022-2-04,168201.0,1669545.0,682.0
|
| 723 |
+
2022-2-05,177282.0,1785606.0,714.0
|
| 724 |
+
2022-2-06,180071.0,1905433.0,661.0
|
| 725 |
+
2022-2-07,171905.0,2021046.0,609.0
|
| 726 |
+
2022-2-08,165643.0,2104803.0,698.0
|
| 727 |
+
2022-2-09,183103.0,2190074.0,669.0
|
| 728 |
+
2022-2-10,197076.0,2280357.0,701.0
|
| 729 |
+
2022-2-11,203949.0,2371348.0,722.0
|
| 730 |
+
2022-2-12,203766.0,2461727.0,729.0
|
| 731 |
+
2022-2-13,197949.0,2557402.0,706.0
|
| 732 |
+
2022-2-14,180456.0,2639990.0,683.0
|
| 733 |
+
2022-2-15,166631.0,2668036.0,704.0
|
| 734 |
+
2022-2-16,179284.0,2674104.0,748.0
|
| 735 |
+
2022-2-17,180622.0,2668854.0,790.0
|
| 736 |
+
2022-2-18,180071.0,2649772.0,784.0
|
| 737 |
+
2022-2-19,179147.0,2637023.0,798.0
|
| 738 |
+
2022-2-20,170699.0,2659681.0,745.0
|
| 739 |
+
2022-2-21,152337.0,2683789.0,735.0
|
| 740 |
+
2022-2-22,135172.0,2653872.0,796.0
|
| 741 |
+
2022-2-23,137642.0,2611526.0,785.0
|
| 742 |
+
2022-2-24,132998.0,2607329.0,762.0
|
| 743 |
+
2022-2-25,123460.0,2562692.0,787.0
|
| 744 |
+
2022-2-26,122995.0,2503551.0,793.0
|
| 745 |
+
2022-2-27,116093.0,2481279.0,769.0
|
| 746 |
+
2022-2-28,106920.0,2470724.0,733.0
|
| 747 |
+
2022-3-01,97333.0,2394458.0,786.0
|
| 748 |
+
2022-3-02,97455.0,2307599.0,784.0
|
| 749 |
+
2022-3-03,93026.0,2229155.0,781.0
|
| 750 |
+
2022-3-04,89174.0,2157046.0,776.0
|
| 751 |
+
2022-3-05,86769.0,2087340.0,750.0
|
| 752 |
+
2022-3-06,79863.0,2017399.0,744.0
|
| 753 |
+
2022-3-07,73162.0,1967274.0,668.0
|
| 754 |
+
2022-3-08,66576.0,1921442.0,652.0
|
| 755 |
+
2022-3-09,58675.0,1886740.0,645.0
|
| 756 |
+
2022-3-10,51231.0,1788672.0,665.0
|
| 757 |
+
2022-3-11,50743.0,1687531.0,674.0
|
| 758 |
+
2022-3-12,48154.0,1578204.0,630.0
|
| 759 |
+
2022-3-13,44989.0,1493968.0,596.0
|
| 760 |
+
2022-3-14,41055.0,1429247.0,533.0
|
| 761 |
+
2022-3-15,36678.0,1346995.0,558.0
|
| 762 |
+
2022-3-16,36519.0,1264929.0,576.0
|
| 763 |
+
2022-3-17,34819.0,1186573.0,561.0
|
| 764 |
+
2022-3-18,34442.0,1115427.0,524.0
|
| 765 |
+
2022-3-19,32958.0,1052332.0,495.0
|
| 766 |
+
2022-3-20,31035.0,1008258.0,434.0
|
| 767 |
+
2022-3-21,28709.0,974620.0,409.0
|
| 768 |
+
2022-3-22,26394.0,925622.0,472.0
|
| 769 |
+
2022-3-23,26826.0,881397.0,429.0
|
| 770 |
+
2022-3-24,25387.0,839890.0,418.0
|
| 771 |
+
2022-3-25,25382.0,803014.0,398.0
|
| 772 |
+
2022-3-26,24072.0,765758.0,395.0
|
| 773 |
+
2022-3-27,23280.0,741160.0,338.0
|
| 774 |
+
2022-3-28,21101.0,726180.0,335.0
|
| 775 |
+
2022-3-29,19660.0,698272.0,339.0
|
| 776 |
+
2022-3-30,20145.0,666980.0,352.0
|
| 777 |
+
2022-3-31,19277.0,635416.0,345.0
|
| 778 |
+
2022-4-01,19164.0,608240.0,342.0
|
| 779 |
+
2022-4-02,17949.0,581302.0,340.0
|
| 780 |
+
2022-4-03,16828.0,562131.0,304.0
|
| 781 |
+
2022-4-04,15291.0,544285.0,287.0
|
| 782 |
+
2022-4-05,13947.0,520457.0,316.0
|
| 783 |
+
2022-4-06,14661.0,499521.0,291.0
|
| 784 |
+
2022-4-07,14355.0,478285.0,287.0
|
| 785 |
+
2022-4-08,14311.0,454632.0,280.0
|
| 786 |
+
2022-4-09,13573.0,427817.0,288.0
|
| 787 |
+
2022-4-10,13056.0,411242.0,259.0
|
| 788 |
+
2022-4-11,11855.0,396094.0,248.0
|
| 789 |
+
2022-4-12,10910.0,375452.0,281.0
|
| 790 |
+
2022-4-13,11754.0,358804.0,267.0
|
| 791 |
+
2022-4-14,11348.0,348045.0,254.0
|
| 792 |
+
2022-4-15,11432.0,339693.0,261.0
|
| 793 |
+
2022-4-16,11095.0,331542.0,240.0
|
| 794 |
+
2022-4-17,10263.0,325076.0,233.0
|
| 795 |
+
2022-4-18,9434.0,319973.0,213.0
|
| 796 |
+
2022-4-19,8640.0,311615.0,235.0
|
| 797 |
+
2022-4-20,9195.0,304387.0,223.0
|
| 798 |
+
2022-4-21,8875.0,298883.0,197.0
|
| 799 |
+
2022-4-22,9001.0,294209.0,195.0
|
| 800 |
+
2022-4-23,8829.0,289837.0,171.0
|
| 801 |
+
2022-4-24,8446.0,287607.0,168.0
|
| 802 |
+
2022-4-25,7651.0,286244.0,159.0
|
| 803 |
+
2022-4-26,7107.0,281276.0,176.0
|
| 804 |
+
2022-4-27,7705.0,277341.0,163.0
|
| 805 |
+
2022-4-28,7681.0,273793.0,166.0
|
| 806 |
+
2022-4-29,7710.0,270301.0,161.0
|
| 807 |
+
2022-4-30,7363.0,266485.0,157.0
|
| 808 |
+
2022-5-01,7047.0,264652.0,147.0
|
| 809 |
+
2022-5-02,6207.0,263221.0,136.0
|
| 810 |
+
2022-5-03,5466.0,261510.0,125.0
|
| 811 |
+
2022-5-04,5093.0,259810.0,129.0
|
| 812 |
+
2022-5-05,5011.0,255959.0,139.0
|
| 813 |
+
2022-5-06,5541.0,251956.0,136.0
|
| 814 |
+
2022-5-07,5500.0,248675.0,132.0
|
| 815 |
+
2022-5-08,5447.0,247322.0,118.0
|
| 816 |
+
2022-5-09,5030.0,246290.0,103.0
|
| 817 |
+
2022-5-10,4531.0,245706.0,101.0
|
| 818 |
+
2022-5-11,4102.0,244667.0,98.0
|
| 819 |
+
2022-5-12,4065.0,241691.0,111.0
|
| 820 |
+
2022-5-13,4896.0,239225.0,105.0
|
| 821 |
+
2022-5-14,5047.0,236787.0,107.0
|
src/lab2_functions.py
ADDED
|
@@ -0,0 +1,626 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Функции для лабораторной работы №2: Прогнозирование временных рядов
|
| 3 |
+
"""
|
| 4 |
+
import numpy as np
|
| 5 |
+
import pandas as pd
|
| 6 |
+
from typing import List, Tuple, Dict, Optional
|
| 7 |
+
from scipy import stats
|
| 8 |
+
from scipy.stats import boxcox, boxcox_normmax
|
| 9 |
+
from statsmodels.tsa.holtwinters import ExponentialSmoothing
|
| 10 |
+
from statsmodels.stats.diagnostic import acorr_ljungbox
|
| 11 |
+
from statsmodels.tsa.stattools import adfuller, kpss
|
| 12 |
+
from sklearn.model_selection import TimeSeriesSplit
|
| 13 |
+
from sklearn.metrics import mean_absolute_error, mean_squared_error
|
| 14 |
+
import warnings
|
| 15 |
+
warnings.filterwarnings('ignore')
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def calculate_mape(y_true: np.ndarray, y_pred: np.ndarray) -> float:
|
| 19 |
+
"""Вычисляет MAPE (Mean Absolute Percentage Error)"""
|
| 20 |
+
y_true = np.array(y_true)
|
| 21 |
+
y_pred = np.array(y_pred)
|
| 22 |
+
mask = y_true != 0
|
| 23 |
+
if mask.sum() == 0:
|
| 24 |
+
return np.nan
|
| 25 |
+
return np.mean(np.abs((y_true[mask] - y_pred[mask]) / y_true[mask])) * 100
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def create_advanced_features(df: pd.DataFrame, target: str, timestamp_col: str = 'timestamp') -> pd.DataFrame:
|
| 29 |
+
"""
|
| 30 |
+
Расширенный feature engineering:
|
| 31 |
+
- Временные признаки (день недели, месяц, квартал)
|
| 32 |
+
- Циклические признаки через sin/cos
|
| 33 |
+
- Лаги: lag_1, lag_7, lag_30
|
| 34 |
+
- Скользящие статистики: mean, std, min, max по окнам 7, 30, 90
|
| 35 |
+
"""
|
| 36 |
+
df = df.copy()
|
| 37 |
+
df = df.set_index(timestamp_col).sort_index()
|
| 38 |
+
|
| 39 |
+
# Временные признаки
|
| 40 |
+
df['day_of_week'] = df.index.dayofweek
|
| 41 |
+
df['month'] = df.index.month
|
| 42 |
+
df['quarter'] = df.index.quarter
|
| 43 |
+
df['day_of_month'] = df.index.day
|
| 44 |
+
df['week_of_year'] = df.index.isocalendar().week
|
| 45 |
+
|
| 46 |
+
# Циклические признаки
|
| 47 |
+
df['day_of_week_sin'] = np.sin(2 * np.pi * df['day_of_week'] / 7)
|
| 48 |
+
df['day_of_week_cos'] = np.cos(2 * np.pi * df['day_of_week'] / 7)
|
| 49 |
+
df['month_sin'] = np.sin(2 * np.pi * df['month'] / 12)
|
| 50 |
+
df['month_cos'] = np.cos(2 * np.pi * df['month'] / 12)
|
| 51 |
+
|
| 52 |
+
# Лаги
|
| 53 |
+
for lag in [1, 7, 30]:
|
| 54 |
+
df[f'{target}_lag_{lag}'] = df[target].shift(lag)
|
| 55 |
+
|
| 56 |
+
# Скользящие статистики
|
| 57 |
+
windows = [7, 30, 90]
|
| 58 |
+
for w in windows:
|
| 59 |
+
df[f'{target}_rolling_mean_{w}'] = df[target].rolling(window=w, min_periods=1).mean()
|
| 60 |
+
df[f'{target}_rolling_std_{w}'] = df[target].rolling(window=w, min_periods=1).std()
|
| 61 |
+
df[f'{target}_rolling_min_{w}'] = df[target].rolling(window=w, min_periods=1).min()
|
| 62 |
+
df[f'{target}_rolling_max_{w}'] = df[target].rolling(window=w, min_periods=1).max()
|
| 63 |
+
|
| 64 |
+
# Коэффициент вариации (волатильность)
|
| 65 |
+
for w in [7, 30]:
|
| 66 |
+
rolling_mean = df[f'{target}_rolling_mean_{w}']
|
| 67 |
+
rolling_std = df[f'{target}_rolling_std_{w}']
|
| 68 |
+
df[f'{target}_rolling_cv_{w}'] = rolling_std / (rolling_mean + 1e-8)
|
| 69 |
+
|
| 70 |
+
return df.reset_index()
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def apply_boxcox_transform(series: pd.Series, lambda_param: Optional[float] = None) -> Tuple[pd.Series, float]:
|
| 74 |
+
"""
|
| 75 |
+
Применяет преобразование Бокса-Кокса.
|
| 76 |
+
Если lambda_param не указан, подбирает оптимальный.
|
| 77 |
+
"""
|
| 78 |
+
series_positive = series[series > 0]
|
| 79 |
+
if len(series_positive) == 0:
|
| 80 |
+
raise ValueError("Все значения должны быть положительными для преобразования Бокса-Кокса")
|
| 81 |
+
|
| 82 |
+
if lambda_param is None:
|
| 83 |
+
# Автоматический подбор lambda
|
| 84 |
+
lambda_param = boxcox_normmax(series_positive.values)
|
| 85 |
+
|
| 86 |
+
transformed_values, fitted_lambda = boxcox(series_positive.values, lmbda=lambda_param)
|
| 87 |
+
|
| 88 |
+
# Создаём новый Series с теми же индексами
|
| 89 |
+
result = pd.Series(index=series.index, dtype=float)
|
| 90 |
+
result.loc[series > 0] = transformed_values
|
| 91 |
+
|
| 92 |
+
return result, fitted_lambda
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
def inverse_boxcox_transform(transformed_series: pd.Series, lambda_param: float) -> pd.Series:
|
| 96 |
+
"""Обратное преобразование Бокса-Кокса"""
|
| 97 |
+
if lambda_param == 0:
|
| 98 |
+
return np.exp(transformed_series)
|
| 99 |
+
else:
|
| 100 |
+
return (lambda_param * transformed_series + 1) ** (1 / lambda_param)
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def inverse_transformations(
|
| 104 |
+
forecast: np.ndarray,
|
| 105 |
+
last_train_values_transformed: np.ndarray,
|
| 106 |
+
transform_info: Dict
|
| 107 |
+
) -> np.ndarray:
|
| 108 |
+
"""
|
| 109 |
+
Применяет обратные преобразования к прогнозу.
|
| 110 |
+
|
| 111 |
+
Порядок обратного преобразования должен быть обратным порядку прямого:
|
| 112 |
+
Прямое: transformation -> diff_order -> seasonal_diff
|
| 113 |
+
Обратное: seasonal_diff -> diff_order -> transformation
|
| 114 |
+
|
| 115 |
+
forecast: прогноз в преобразованном пространстве (после всех преобразований)
|
| 116 |
+
last_train_values_transformed: последние значения обучающей выборки в преобразованном пространстве (после всех преобразований)
|
| 117 |
+
transform_info: информация о применённых преобразованиях (может содержать промежуточные значения)
|
| 118 |
+
"""
|
| 119 |
+
result = forecast.copy()
|
| 120 |
+
diff_order = transform_info.get('diff_order', 0)
|
| 121 |
+
seasonal_diff = transform_info.get('seasonal_diff')
|
| 122 |
+
|
| 123 |
+
# Получаем промежуточные значения из transform_info, если они есть
|
| 124 |
+
last_values_after_diff = transform_info.get('last_values_after_diff', None)
|
| 125 |
+
last_values_after_transform = transform_info.get('last_values_after_transform', None)
|
| 126 |
+
|
| 127 |
+
# 1. Обратное сезонное дифференцирование (если было)
|
| 128 |
+
if seasonal_diff is not None and seasonal_diff > 0:
|
| 129 |
+
# Нужны последние seasonal_diff значений после transformation и diff, но до seasonal_diff
|
| 130 |
+
if last_values_after_diff is not None and len(last_values_after_diff) >= seasonal_diff:
|
| 131 |
+
last_seasonal = last_values_after_diff[-seasonal_diff:]
|
| 132 |
+
elif len(last_train_values_transformed) >= seasonal_diff:
|
| 133 |
+
# Fallback: используем последние значения (хотя это не совсем правильно)
|
| 134 |
+
last_seasonal = last_train_values_transformed[-seasonal_diff:]
|
| 135 |
+
else:
|
| 136 |
+
last_seasonal = last_train_values_transformed if len(last_train_values_transformed) > 0 else np.array([0])
|
| 137 |
+
|
| 138 |
+
for i in range(len(result)):
|
| 139 |
+
if i < len(last_seasonal):
|
| 140 |
+
result[i] = result[i] + last_seasonal[i]
|
| 141 |
+
else:
|
| 142 |
+
# Используем предыдущие прогнозы
|
| 143 |
+
result[i] = result[i] + result[i - seasonal_diff]
|
| 144 |
+
|
| 145 |
+
# 2. Обратное обычное дифференцирование (если было)
|
| 146 |
+
for _ in range(diff_order):
|
| 147 |
+
# Нужны последние diff_order значений после transformation, но до diff
|
| 148 |
+
if last_values_after_transform is not None and len(last_values_after_transform) > 0:
|
| 149 |
+
last_val = last_values_after_transform[-1]
|
| 150 |
+
elif len(last_train_values_transformed) > 0:
|
| 151 |
+
# Fallback
|
| 152 |
+
last_val = last_train_values_transformed[-1]
|
| 153 |
+
else:
|
| 154 |
+
last_val = 0
|
| 155 |
+
|
| 156 |
+
for i in range(len(result)):
|
| 157 |
+
if i == 0:
|
| 158 |
+
result[i] = result[i] + last_val
|
| 159 |
+
else:
|
| 160 |
+
result[i] = result[i] + result[i - 1]
|
| 161 |
+
|
| 162 |
+
# 3. Обратное преобразование для стабилизации дисперсии
|
| 163 |
+
if transform_info.get('transformation') == 'log':
|
| 164 |
+
result = np.exp(result)
|
| 165 |
+
elif transform_info.get('transformation') == 'boxcox':
|
| 166 |
+
lambda_param = transform_info.get('lambda')
|
| 167 |
+
if lambda_param is not None:
|
| 168 |
+
if lambda_param == 0:
|
| 169 |
+
result = np.exp(result)
|
| 170 |
+
else:
|
| 171 |
+
result = (lambda_param * result + 1) ** (1 / lambda_param)
|
| 172 |
+
|
| 173 |
+
return result
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
def apply_transformations(
|
| 177 |
+
series: pd.Series,
|
| 178 |
+
transformation: str = 'none',
|
| 179 |
+
lambda_param: Optional[float] = None,
|
| 180 |
+
diff_order: int = 0,
|
| 181 |
+
seasonal_diff: Optional[int] = None
|
| 182 |
+
) -> Tuple[pd.Series, Dict]:
|
| 183 |
+
"""
|
| 184 |
+
Применяет цепочку преобразований к ряду.
|
| 185 |
+
|
| 186 |
+
transformation: 'none', 'log', 'boxcox'
|
| 187 |
+
diff_order: порядок обычного дифференцирования
|
| 188 |
+
seasonal_diff: период сезонного дифференцирования
|
| 189 |
+
|
| 190 |
+
Возвращает преобразованный ряд и словарь с информацией о преобразованиях,
|
| 191 |
+
включая промежуточные значения для обратного преобразования.
|
| 192 |
+
"""
|
| 193 |
+
result = series.copy()
|
| 194 |
+
info = {'transformation': transformation, 'lambda': None, 'diff_order': diff_order, 'seasonal_diff': seasonal_diff}
|
| 195 |
+
|
| 196 |
+
# Преобразование для стабилизации дисперсии
|
| 197 |
+
if transformation == 'log':
|
| 198 |
+
if (result <= 0).any():
|
| 199 |
+
raise ValueError("Для лог-трансформации все значения должны быть положительными")
|
| 200 |
+
result = np.log(result)
|
| 201 |
+
elif transformation == 'boxcox':
|
| 202 |
+
result, lambda_param = apply_boxcox_transform(result, lambda_param)
|
| 203 |
+
info['lambda'] = lambda_param
|
| 204 |
+
|
| 205 |
+
# Сохраняем значения после transformation (для обратного diff)
|
| 206 |
+
result_after_transform = result.copy()
|
| 207 |
+
|
| 208 |
+
# Обычное дифференцирование
|
| 209 |
+
for _ in range(diff_order):
|
| 210 |
+
result = result.diff()
|
| 211 |
+
|
| 212 |
+
# Сохраняем значения после diff (для обратного seasonal_diff)
|
| 213 |
+
result_after_diff = result.copy()
|
| 214 |
+
|
| 215 |
+
# Сезонное дифференцирование
|
| 216 |
+
if seasonal_diff is not None and seasonal_diff > 0:
|
| 217 |
+
result = result.diff(periods=seasonal_diff)
|
| 218 |
+
|
| 219 |
+
# Сохраняем промежуточные значения для обратного преобразования
|
| 220 |
+
info['last_values_after_transform'] = result_after_transform.values[-max(diff_order, 1):] if len(result_after_transform) > 0 else np.array([])
|
| 221 |
+
info['last_values_after_diff'] = result_after_diff.values[-max(seasonal_diff if seasonal_diff else 1, 1):] if len(result_after_diff) > 0 else np.array([])
|
| 222 |
+
|
| 223 |
+
return result.dropna(), info
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
def recursive_forecast(
|
| 227 |
+
model_func,
|
| 228 |
+
train_data: pd.Series,
|
| 229 |
+
horizon: int,
|
| 230 |
+
alpha: Optional[float] = None,
|
| 231 |
+
**model_kwargs
|
| 232 |
+
) -> Tuple[np.ndarray, Optional[Tuple[np.ndarray, np.ndarray]]]:
|
| 233 |
+
"""
|
| 234 |
+
Рекурсивная стратегия прогнозирования:
|
| 235 |
+
Одна модель → итеративное использование прогнозов
|
| 236 |
+
|
| 237 |
+
Возвращает прогнозы и опционально доверительные интервалы (lower, upper)
|
| 238 |
+
"""
|
| 239 |
+
forecasts = []
|
| 240 |
+
conf_lower = [] if alpha is not None else None
|
| 241 |
+
conf_upper = [] if alpha is not None else None
|
| 242 |
+
current_data = train_data.copy()
|
| 243 |
+
|
| 244 |
+
# Определяем тип индекса для правильного добавления новых значений
|
| 245 |
+
is_datetime = pd.api.types.is_datetime64_any_dtype(current_data.index)
|
| 246 |
+
|
| 247 |
+
for h in range(horizon):
|
| 248 |
+
# Обучаем модель на текущих данных
|
| 249 |
+
model = model_func(current_data, **model_kwargs)
|
| 250 |
+
# Прогнозируем на 1 шаг вперёд
|
| 251 |
+
if alpha is not None:
|
| 252 |
+
try:
|
| 253 |
+
forecast_result = model.forecast(steps=1, alpha=alpha)
|
| 254 |
+
if isinstance(forecast_result, tuple):
|
| 255 |
+
forecast_value = forecast_result[0][0] if len(forecast_result[0]) > 0 else forecast_result[0]
|
| 256 |
+
if len(forecast_result) > 1:
|
| 257 |
+
conf_lower.append(forecast_result[1][0] if len(forecast_result[1]) > 0 else forecast_result[1])
|
| 258 |
+
conf_upper.append(forecast_result[2][0] if len(forecast_result[2]) > 0 else forecast_result[2])
|
| 259 |
+
else:
|
| 260 |
+
forecast_value = forecast_result[0] if hasattr(forecast_result, '__getitem__') else float(forecast_result)
|
| 261 |
+
except:
|
| 262 |
+
# Если доверительные интервалы не поддерживаются, используем обычный прогноз
|
| 263 |
+
forecast = model.forecast(steps=1)
|
| 264 |
+
forecast_value = forecast[0] if hasattr(forecast, '__getitem__') else float(forecast)
|
| 265 |
+
else:
|
| 266 |
+
forecast = model.forecast(steps=1)
|
| 267 |
+
forecast_value = forecast[0] if hasattr(forecast, '__getitem__') else float(forecast)
|
| 268 |
+
|
| 269 |
+
forecasts.append(forecast_value)
|
| 270 |
+
|
| 271 |
+
# Добавляем прогноз к данным для следующей итерации
|
| 272 |
+
if is_datetime:
|
| 273 |
+
# Для DatetimeIndex используем частоту или инференс
|
| 274 |
+
try:
|
| 275 |
+
freq = pd.infer_freq(current_data.index) or 'D'
|
| 276 |
+
# Используем pd.date_range для создания следующей даты
|
| 277 |
+
last_date = current_data.index[-1]
|
| 278 |
+
next_dates = pd.date_range(start=last_date, periods=2, freq=freq)
|
| 279 |
+
if len(next_dates) >= 2:
|
| 280 |
+
next_idx = next_dates[1] # Берём вторую дату (первая = last_date)
|
| 281 |
+
else:
|
| 282 |
+
# Fallback
|
| 283 |
+
next_idx = len(current_data)
|
| 284 |
+
is_datetime = False
|
| 285 |
+
except:
|
| 286 |
+
# Если не удалось определить частоту, используем числовой индекс
|
| 287 |
+
try:
|
| 288 |
+
# Пробуем простой способ через Timedelta
|
| 289 |
+
next_idx = current_data.index[-1] + pd.Timedelta(days=1)
|
| 290 |
+
except:
|
| 291 |
+
next_idx = len(current_data)
|
| 292 |
+
is_datetime = False
|
| 293 |
+
else:
|
| 294 |
+
# Для числового индекса просто увеличиваем на 1
|
| 295 |
+
next_idx = len(current_data)
|
| 296 |
+
|
| 297 |
+
if is_datetime:
|
| 298 |
+
current_data = pd.concat([current_data, pd.Series([forecast_value], index=[next_idx])])
|
| 299 |
+
else:
|
| 300 |
+
# Используем числовой индекс
|
| 301 |
+
current_data = pd.concat([current_data, pd.Series([forecast_value], index=[next_idx])])
|
| 302 |
+
|
| 303 |
+
result = np.array(forecasts)
|
| 304 |
+
if alpha is not None and conf_lower and conf_upper:
|
| 305 |
+
return result, (np.array(conf_lower), np.array(conf_upper))
|
| 306 |
+
return result, None
|
| 307 |
+
|
| 308 |
+
|
| 309 |
+
def direct_forecast(
|
| 310 |
+
model_func,
|
| 311 |
+
train_data: pd.Series,
|
| 312 |
+
horizon: int,
|
| 313 |
+
alpha: Optional[float] = None,
|
| 314 |
+
**model_kwargs
|
| 315 |
+
) -> Tuple[np.ndarray, Optional[Tuple[np.ndarray, np.ndarray]]]:
|
| 316 |
+
"""
|
| 317 |
+
Прямая стратегия прогнозирования:
|
| 318 |
+
Отдельная модель для каждого шага t+1, ..., t+h
|
| 319 |
+
|
| 320 |
+
Возвращает прогнозы и опционально доверительные интервалы (lower, upper)
|
| 321 |
+
"""
|
| 322 |
+
forecasts = []
|
| 323 |
+
conf_lower = [] if alpha is not None else None
|
| 324 |
+
conf_upper = [] if alpha is not None else None
|
| 325 |
+
|
| 326 |
+
for h in range(1, horizon + 1):
|
| 327 |
+
# Обучаем отдельную модель для шага h
|
| 328 |
+
model = model_func(train_data, **model_kwargs)
|
| 329 |
+
# Прогнозируем на h шагов вперёд и берём последний
|
| 330 |
+
if alpha is not None:
|
| 331 |
+
try:
|
| 332 |
+
forecast_result = model.forecast(steps=h, alpha=alpha)
|
| 333 |
+
if isinstance(forecast_result, tuple):
|
| 334 |
+
forecast_value = forecast_result[0][-1] if len(forecast_result[0]) > 0 else forecast_result[0]
|
| 335 |
+
if len(forecast_result) > 1:
|
| 336 |
+
conf_lower.append(forecast_result[1][-1] if len(forecast_result[1]) > 0 else forecast_result[1])
|
| 337 |
+
conf_upper.append(forecast_result[2][-1] if len(forecast_result[2]) > 0 else forecast_result[2])
|
| 338 |
+
else:
|
| 339 |
+
forecast_value = forecast_result[-1]
|
| 340 |
+
except:
|
| 341 |
+
forecast = model.forecast(steps=h)
|
| 342 |
+
forecast_value = forecast[-1]
|
| 343 |
+
else:
|
| 344 |
+
forecast = model.forecast(steps=h)
|
| 345 |
+
forecast_value = forecast[-1]
|
| 346 |
+
|
| 347 |
+
forecasts.append(forecast_value)
|
| 348 |
+
|
| 349 |
+
result = np.array(forecasts)
|
| 350 |
+
if alpha is not None and conf_lower and conf_upper:
|
| 351 |
+
return result, (np.array(conf_lower), np.array(conf_upper))
|
| 352 |
+
return result, None
|
| 353 |
+
|
| 354 |
+
|
| 355 |
+
def hybrid_forecast(
|
| 356 |
+
model_func,
|
| 357 |
+
train_data: pd.Series,
|
| 358 |
+
horizon: int,
|
| 359 |
+
recursive_steps: int = None,
|
| 360 |
+
alpha: Optional[float] = None,
|
| 361 |
+
**model_kwargs
|
| 362 |
+
) -> Tuple[np.ndarray, Optional[Tuple[np.ndarray, np.ndarray]]]:
|
| 363 |
+
"""
|
| 364 |
+
Гибридная стратегия:
|
| 365 |
+
Рекурсивная для ближайших шагов, прямая — для дальних
|
| 366 |
+
|
| 367 |
+
Возвращает прогнозы и опционально доверительные интервалы (lower, upper)
|
| 368 |
+
"""
|
| 369 |
+
if recursive_steps is None:
|
| 370 |
+
recursive_steps = max(1, horizon // 2)
|
| 371 |
+
|
| 372 |
+
forecasts = []
|
| 373 |
+
conf_lower = [] if alpha is not None else None
|
| 374 |
+
conf_upper = [] if alpha is not None else None
|
| 375 |
+
|
| 376 |
+
# Рекурсивная часть
|
| 377 |
+
recursive_result = recursive_forecast(model_func, train_data, recursive_steps, alpha=alpha, **model_kwargs)
|
| 378 |
+
if isinstance(recursive_result, tuple):
|
| 379 |
+
recursive_forecasts, recursive_conf = recursive_result
|
| 380 |
+
if recursive_conf is not None:
|
| 381 |
+
conf_lower.extend(recursive_conf[0])
|
| 382 |
+
conf_upper.extend(recursive_conf[1])
|
| 383 |
+
else:
|
| 384 |
+
recursive_forecasts = recursive_result
|
| 385 |
+
forecasts.extend(recursive_forecasts)
|
| 386 |
+
|
| 387 |
+
# Прямая часть для оставшихся шагов
|
| 388 |
+
if horizon > recursive_steps:
|
| 389 |
+
# Используем последние данные + рекурсивные прогнозы
|
| 390 |
+
is_datetime = pd.api.types.is_datetime64_any_dtype(train_data.index)
|
| 391 |
+
|
| 392 |
+
if is_datetime:
|
| 393 |
+
try:
|
| 394 |
+
freq = pd.infer_freq(train_data.index) or 'D'
|
| 395 |
+
# Используем pd.date_range для создания дат начиная с последней даты + 1 период
|
| 396 |
+
last_date = train_data.index[-1]
|
| 397 |
+
extended_index = pd.date_range(
|
| 398 |
+
start=last_date,
|
| 399 |
+
periods=len(recursive_forecasts) + 1,
|
| 400 |
+
freq=freq
|
| 401 |
+
)[1:] # Берём все даты кроме первой (которая равна last_date)
|
| 402 |
+
except:
|
| 403 |
+
# Fallback на числовой индекс
|
| 404 |
+
try:
|
| 405 |
+
# Пробуем через date_range с periods
|
| 406 |
+
last_date = train_data.index[-1]
|
| 407 |
+
extended_index = pd.date_range(
|
| 408 |
+
start=last_date,
|
| 409 |
+
periods=len(recursive_forecasts) + 1,
|
| 410 |
+
freq='D'
|
| 411 |
+
)[1:] # Берём все даты кроме первой
|
| 412 |
+
except:
|
| 413 |
+
extended_index = range(len(train_data), len(train_data) + len(recursive_forecasts))
|
| 414 |
+
else:
|
| 415 |
+
extended_index = range(len(train_data), len(train_data) + len(recursive_forecasts))
|
| 416 |
+
|
| 417 |
+
extended_data = pd.concat([
|
| 418 |
+
train_data,
|
| 419 |
+
pd.Series(recursive_forecasts, index=extended_index)
|
| 420 |
+
])
|
| 421 |
+
|
| 422 |
+
remaining_horizon = horizon - recursive_steps
|
| 423 |
+
direct_result = direct_forecast(model_func, extended_data, remaining_horizon, alpha=alpha, **model_kwargs)
|
| 424 |
+
if isinstance(direct_result, tuple):
|
| 425 |
+
direct_forecasts, direct_conf = direct_result
|
| 426 |
+
if direct_conf is not None:
|
| 427 |
+
conf_lower.extend(direct_conf[0])
|
| 428 |
+
conf_upper.extend(direct_conf[1])
|
| 429 |
+
else:
|
| 430 |
+
direct_forecasts = direct_result
|
| 431 |
+
forecasts.extend(direct_forecasts)
|
| 432 |
+
|
| 433 |
+
result = np.array(forecasts[:horizon])
|
| 434 |
+
if alpha is not None and conf_lower and conf_upper:
|
| 435 |
+
return result, (np.array(conf_lower[:horizon]), np.array(conf_upper[:horizon]))
|
| 436 |
+
return result, None
|
| 437 |
+
|
| 438 |
+
|
| 439 |
+
def create_exponential_smoothing_model(
|
| 440 |
+
train_data: pd.Series,
|
| 441 |
+
trend: Optional[str] = None,
|
| 442 |
+
seasonal: Optional[str] = None,
|
| 443 |
+
seasonal_periods: Optional[int] = None,
|
| 444 |
+
optimized: bool = True
|
| 445 |
+
) -> ExponentialSmoothing:
|
| 446 |
+
"""Создаёт и обучает модель экспоненциального сглаживания"""
|
| 447 |
+
try:
|
| 448 |
+
model = ExponentialSmoothing(
|
| 449 |
+
train_data,
|
| 450 |
+
trend=trend,
|
| 451 |
+
seasonal=seasonal,
|
| 452 |
+
seasonal_periods=seasonal_periods,
|
| 453 |
+
initialization_method='estimated' if optimized else 'simple'
|
| 454 |
+
)
|
| 455 |
+
fitted_model = model.fit(optimized=optimized)
|
| 456 |
+
return fitted_model
|
| 457 |
+
except Exception as e:
|
| 458 |
+
raise ValueError(f"Ошибка при создании модели: {e}")
|
| 459 |
+
|
| 460 |
+
|
| 461 |
+
def evaluate_forecast(y_true: np.ndarray, y_pred: np.ndarray) -> Dict[str, float]:
|
| 462 |
+
"""Вычисляет метрики качества прогноза"""
|
| 463 |
+
y_true = np.array(y_true)
|
| 464 |
+
y_pred = np.array(y_pred)
|
| 465 |
+
|
| 466 |
+
mae = mean_absolute_error(y_true, y_pred)
|
| 467 |
+
rmse = np.sqrt(mean_squared_error(y_true, y_pred))
|
| 468 |
+
mape = calculate_mape(y_true, y_pred)
|
| 469 |
+
|
| 470 |
+
return {
|
| 471 |
+
'MAE': mae,
|
| 472 |
+
'RMSE': rmse,
|
| 473 |
+
'MAPE': mape
|
| 474 |
+
}
|
| 475 |
+
|
| 476 |
+
|
| 477 |
+
def naive_forecast(train_data: pd.Series, horizon: int) -> np.ndarray:
|
| 478 |
+
"""Наивный прогноз: y[t+h] = y[t]"""
|
| 479 |
+
last_value = train_data.iloc[-1]
|
| 480 |
+
return np.full(horizon, last_value)
|
| 481 |
+
|
| 482 |
+
|
| 483 |
+
def time_series_cv_sliding_window(
|
| 484 |
+
model_func,
|
| 485 |
+
data: pd.Series,
|
| 486 |
+
train_size: int,
|
| 487 |
+
test_size: int,
|
| 488 |
+
horizon: int,
|
| 489 |
+
step: int = 1,
|
| 490 |
+
**model_kwargs
|
| 491 |
+
) -> List[Dict]:
|
| 492 |
+
"""
|
| 493 |
+
Кросс-валидация со скользящим окном (фиксированная длина обучения)
|
| 494 |
+
"""
|
| 495 |
+
results = []
|
| 496 |
+
n = len(data)
|
| 497 |
+
|
| 498 |
+
for i in range(0, n - train_size - test_size + 1, step):
|
| 499 |
+
train_end = i + train_size
|
| 500 |
+
test_end = min(train_end + test_size, n)
|
| 501 |
+
|
| 502 |
+
train_data = data.iloc[i:train_end]
|
| 503 |
+
test_data = data.iloc[train_end:test_end]
|
| 504 |
+
|
| 505 |
+
try:
|
| 506 |
+
model = model_func(train_data, **model_kwargs)
|
| 507 |
+
forecast = model.forecast(steps=min(horizon, len(test_data)))
|
| 508 |
+
|
| 509 |
+
metrics = evaluate_forecast(test_data.values[:len(forecast)], forecast)
|
| 510 |
+
metrics['fold'] = len(results) + 1
|
| 511 |
+
metrics['train_start'] = train_data.index[0]
|
| 512 |
+
metrics['train_end'] = train_data.index[-1]
|
| 513 |
+
metrics['test_start'] = test_data.index[0]
|
| 514 |
+
metrics['test_end'] = test_data.index[-1]
|
| 515 |
+
results.append(metrics)
|
| 516 |
+
except Exception as e:
|
| 517 |
+
print(f"Ошибка в фолде {len(results) + 1}: {e}")
|
| 518 |
+
|
| 519 |
+
return results
|
| 520 |
+
|
| 521 |
+
|
| 522 |
+
def time_series_cv_expanding_window(
|
| 523 |
+
model_func,
|
| 524 |
+
data: pd.Series,
|
| 525 |
+
initial_train_size: int,
|
| 526 |
+
test_size: int,
|
| 527 |
+
horizon: int,
|
| 528 |
+
step: int = 1,
|
| 529 |
+
**model_kwargs
|
| 530 |
+
) -> List[Dict]:
|
| 531 |
+
"""
|
| 532 |
+
Кросс-валидация с расширяющимся окном (обучение растёт со временем)
|
| 533 |
+
"""
|
| 534 |
+
results = []
|
| 535 |
+
n = len(data)
|
| 536 |
+
|
| 537 |
+
for i in range(initial_train_size, n - test_size + 1, step):
|
| 538 |
+
train_end = i
|
| 539 |
+
test_end = min(train_end + test_size, n)
|
| 540 |
+
|
| 541 |
+
train_data = data.iloc[:train_end]
|
| 542 |
+
test_data = data.iloc[train_end:test_end]
|
| 543 |
+
|
| 544 |
+
try:
|
| 545 |
+
model = model_func(train_data, **model_kwargs)
|
| 546 |
+
forecast = model.forecast(steps=min(horizon, len(test_data)))
|
| 547 |
+
|
| 548 |
+
metrics = evaluate_forecast(test_data.values[:len(forecast)], forecast)
|
| 549 |
+
metrics['fold'] = len(results) + 1
|
| 550 |
+
metrics['train_start'] = train_data.index[0]
|
| 551 |
+
metrics['train_end'] = train_data.index[-1]
|
| 552 |
+
metrics['test_start'] = test_data.index[0]
|
| 553 |
+
metrics['test_end'] = test_data.index[-1]
|
| 554 |
+
results.append(metrics)
|
| 555 |
+
except Exception as e:
|
| 556 |
+
print(f"Ошибка в фолде {len(results) + 1}: {e}")
|
| 557 |
+
|
| 558 |
+
return results
|
| 559 |
+
|
| 560 |
+
|
| 561 |
+
def diagnose_model_residuals(residuals: np.ndarray, lags: int = 10) -> Dict:
|
| 562 |
+
"""
|
| 563 |
+
Диагностика остатк��в модели:
|
| 564 |
+
- Тест Льюнга-Бокса на автокорреляцию
|
| 565 |
+
- Проверка нормальности (Shapiro-Wilk)
|
| 566 |
+
- Q-Q plot данные
|
| 567 |
+
"""
|
| 568 |
+
residuals_clean = residuals[~np.isnan(residuals)]
|
| 569 |
+
|
| 570 |
+
if len(residuals_clean) < 3:
|
| 571 |
+
return {'error': 'Недостаточно данных для диагностики'}
|
| 572 |
+
|
| 573 |
+
results = {}
|
| 574 |
+
|
| 575 |
+
# Тест Льюнга-Бокса
|
| 576 |
+
try:
|
| 577 |
+
lb_stat, lb_pvalue = acorr_ljungbox(residuals_clean, lags=min(lags, len(residuals_clean) - 1), return_df=False)
|
| 578 |
+
results['ljung_box'] = {
|
| 579 |
+
'statistic': float(lb_stat[-1]) if len(lb_stat) > 0 else None,
|
| 580 |
+
'pvalue': float(lb_pvalue[-1]) if len(lb_pvalue) > 0 else None,
|
| 581 |
+
'lags': lags
|
| 582 |
+
}
|
| 583 |
+
except Exception as e:
|
| 584 |
+
results['ljung_box'] = {'error': str(e)}
|
| 585 |
+
|
| 586 |
+
# Тест Шапиро-Уилка на нормальность
|
| 587 |
+
try:
|
| 588 |
+
if len(residuals_clean) <= 5000: # Ограничение для Shapiro-Wilk
|
| 589 |
+
shapiro_stat, shapiro_pvalue = stats.shapiro(residuals_clean)
|
| 590 |
+
results['shapiro_wilk'] = {
|
| 591 |
+
'statistic': float(shapiro_stat),
|
| 592 |
+
'pvalue': float(shapiro_pvalue)
|
| 593 |
+
}
|
| 594 |
+
else:
|
| 595 |
+
# Для больших выборок используем тест нормальности из scipy
|
| 596 |
+
k2_stat, k2_pvalue = stats.normaltest(residuals_clean)
|
| 597 |
+
results['normality_test'] = {
|
| 598 |
+
'statistic': float(k2_stat),
|
| 599 |
+
'pvalue': float(k2_pvalue),
|
| 600 |
+
'test': 'normaltest'
|
| 601 |
+
}
|
| 602 |
+
except Exception as e:
|
| 603 |
+
results['normality_test'] = {'error': str(e)}
|
| 604 |
+
|
| 605 |
+
# Статистики остатков
|
| 606 |
+
results['residual_stats'] = {
|
| 607 |
+
'mean': float(np.mean(residuals_clean)),
|
| 608 |
+
'std': float(np.std(residuals_clean)),
|
| 609 |
+
'min': float(np.min(residuals_clean)),
|
| 610 |
+
'max': float(np.max(residuals_clean)),
|
| 611 |
+
'count': len(residuals_clean)
|
| 612 |
+
}
|
| 613 |
+
|
| 614 |
+
# Проверка стационарности остатков
|
| 615 |
+
try:
|
| 616 |
+
adf_stat, adf_pvalue, _, _, _, _ = adfuller(residuals_clean)
|
| 617 |
+
kpss_stat, kpss_pvalue, _, _ = kpss(residuals_clean)
|
| 618 |
+
results['stationarity'] = {
|
| 619 |
+
'adf': {'statistic': float(adf_stat), 'pvalue': float(adf_pvalue)},
|
| 620 |
+
'kpss': {'statistic': float(kpss_stat), 'pvalue': float(kpss_pvalue)}
|
| 621 |
+
}
|
| 622 |
+
except Exception as e:
|
| 623 |
+
results['stationarity'] = {'error': str(e)}
|
| 624 |
+
|
| 625 |
+
return results
|
| 626 |
+
|
src/lab3_functions.py
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# TimeSeriesHomework/src/lab3_functions.py
|
| 2 |
+
"""
|
| 3 |
+
Набор вспомогательных функций для ЛР3:
|
| 4 |
+
- обёртки для SARIMAX, auto_arima (pmdarima), VAR, GARCH (arch) и т.п.
|
| 5 |
+
- forecast helpers
|
| 6 |
+
- простые метрики
|
| 7 |
+
Файл не использует абсолютных путей и предназначен для импорта в проекте.
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
import warnings
|
| 11 |
+
warnings.filterwarnings("ignore")
|
| 12 |
+
|
| 13 |
+
from typing import Tuple, Dict, Any, Optional, List
|
| 14 |
+
import numpy as np
|
| 15 |
+
import pandas as pd
|
| 16 |
+
|
| 17 |
+
# optional heavy deps
|
| 18 |
+
try:
|
| 19 |
+
import pmdarima as pm
|
| 20 |
+
PM_AVAILABLE = True
|
| 21 |
+
except Exception:
|
| 22 |
+
PM_AVAILABLE = False
|
| 23 |
+
|
| 24 |
+
try:
|
| 25 |
+
from statsmodels.tsa.statespace.sarimax import SARIMAX
|
| 26 |
+
from statsmodels.tsa.api import VAR
|
| 27 |
+
STATSMODELS_AVAILABLE = True
|
| 28 |
+
except Exception:
|
| 29 |
+
STATSMODELS_AVAILABLE = False
|
| 30 |
+
|
| 31 |
+
try:
|
| 32 |
+
from arch import arch_model
|
| 33 |
+
ARCH_AVAILABLE = True
|
| 34 |
+
except Exception:
|
| 35 |
+
ARCH_AVAILABLE = False
|
| 36 |
+
|
| 37 |
+
# sklearn metrics used for convenience (optional)
|
| 38 |
+
try:
|
| 39 |
+
from sklearn.metrics import mean_absolute_error, mean_squared_error
|
| 40 |
+
SKLEARN_AVAILABLE = True
|
| 41 |
+
except Exception:
|
| 42 |
+
SKLEARN_AVAILABLE = False
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def is_pandas_series(x: Any) -> bool:
|
| 46 |
+
return isinstance(x, (pd.Series,))
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def mae_rmse(y_true, y_pred) -> Dict[str, float]:
|
| 50 |
+
y_true = np.array(y_true)
|
| 51 |
+
y_pred = np.array(y_pred)
|
| 52 |
+
if SKLEARN_AVAILABLE:
|
| 53 |
+
mae = float(mean_absolute_error(y_true, y_pred))
|
| 54 |
+
rmse = float(np.sqrt(mean_squared_error(y_true, y_pred)))
|
| 55 |
+
else:
|
| 56 |
+
mae = float(np.mean(np.abs(y_true - y_pred)))
|
| 57 |
+
rmse = float(np.sqrt(np.mean((y_true - y_pred) ** 2)))
|
| 58 |
+
return {"MAE": mae, "RMSE": rmse}
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def fit_auto_arima(series: pd.Series, seasonal: bool = False, m: int = 1, **kwargs):
|
| 62 |
+
"""
|
| 63 |
+
Подбор ARIMA через pmdarima.auto_arima. Возвращает обученную модель pmdarima.
|
| 64 |
+
"""
|
| 65 |
+
if not PM_AVAILABLE:
|
| 66 |
+
raise ImportError("pmdarima не установлен. Установите pmdarima (pip install pmdarima).")
|
| 67 |
+
if not is_pandas_series(series):
|
| 68 |
+
series = pd.Series(series)
|
| 69 |
+
series_clean = series.dropna()
|
| 70 |
+
if series_clean.empty:
|
| 71 |
+
raise ValueError("Пустая серия передана в fit_auto_arima.")
|
| 72 |
+
model = pm.auto_arima(series_clean, seasonal=seasonal, m=m, error_action="ignore", suppress_warnings=True, **kwargs)
|
| 73 |
+
return model
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def fit_sarimax(series: pd.Series, order: Tuple[int, int, int] = (1, 0, 0),
|
| 77 |
+
seasonal_order: Tuple[int, int, int, int] = (0, 0, 0, 0),
|
| 78 |
+
enforce_stationarity: bool = False, enforce_invertibility: bool = True, **fit_kwargs):
|
| 79 |
+
"""
|
| 80 |
+
Обучает SARIMAX (statsmodels). Возвращает результат fit() (SARIMAXResults).
|
| 81 |
+
"""
|
| 82 |
+
if not STATSMODELS_AVAILABLE:
|
| 83 |
+
raise ImportError("statsmodels не установлен. Установите statsmodels.")
|
| 84 |
+
if not is_pandas_series(series):
|
| 85 |
+
series = pd.Series(series)
|
| 86 |
+
series_clean = series.dropna()
|
| 87 |
+
if series_clean.empty:
|
| 88 |
+
raise ValueError("Пустая серия передана в fit_sarimax.")
|
| 89 |
+
model = SARIMAX(series_clean, order=order, seasonal_order=seasonal_order,
|
| 90 |
+
enforce_stationarity=enforce_stationarity, enforce_invertibility=enforce_invertibility)
|
| 91 |
+
res = model.fit(disp=False, **fit_kwargs)
|
| 92 |
+
return res
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
def forecast_sarimax(fit_res, steps: int, alpha: float = 0.05) -> Tuple[np.ndarray, Tuple[np.ndarray, np.ndarray]]:
|
| 96 |
+
"""
|
| 97 |
+
Делает прогноз из обученного SARIMAX-результата (res.get_forecast).
|
| 98 |
+
Возвращает (mean, (lower, upper)) — numpy arrays длины steps.
|
| 99 |
+
"""
|
| 100 |
+
if hasattr(fit_res, "get_forecast"):
|
| 101 |
+
fc = fit_res.get_forecast(steps=steps)
|
| 102 |
+
mean = np.asarray(fc.predicted_mean)
|
| 103 |
+
try:
|
| 104 |
+
conf = fc.conf_int(alpha=alpha)
|
| 105 |
+
lower = np.asarray(conf.iloc[:, 0])
|
| 106 |
+
upper = np.asarray(conf.iloc[:, 1])
|
| 107 |
+
except Exception:
|
| 108 |
+
lower = np.full(len(mean), np.nan)
|
| 109 |
+
upper = np.full(len(mean), np.nan)
|
| 110 |
+
return mean, (lower, upper)
|
| 111 |
+
else:
|
| 112 |
+
# fallback на forecast
|
| 113 |
+
try:
|
| 114 |
+
f = fit_res.forecast(steps=steps)
|
| 115 |
+
mean = np.asarray(f)
|
| 116 |
+
lower = np.full(len(mean), np.nan)
|
| 117 |
+
upper = np.full(len(mean), np.nan)
|
| 118 |
+
return mean, (lower, upper)
|
| 119 |
+
except Exception as e:
|
| 120 |
+
raise ValueError(f"Не удалось получить прогноз из объекта результата: {e}")
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
def fit_var(df: pd.DataFrame, maxlags: int = 15):
|
| 124 |
+
"""
|
| 125 |
+
Обучает VAR на multivariate dataframe (pandas DataFrame). Возвращает fitted VARResults.
|
| 126 |
+
"""
|
| 127 |
+
if not STATSMODELS_AVAILABLE:
|
| 128 |
+
raise ImportError("statsmodels не установлен. Установите statsmodels.")
|
| 129 |
+
if not isinstance(df, pd.DataFrame):
|
| 130 |
+
raise ValueError("fit_var ожидает pd.DataFrame с несколькими числовыми колонками.")
|
| 131 |
+
df_clean = df.dropna()
|
| 132 |
+
if df_clean.shape[0] < 3:
|
| 133 |
+
raise ValueError("Недостаточно наблюдений для VAR.")
|
| 134 |
+
model = VAR(df_clean)
|
| 135 |
+
sel = model.select_order(maxlags=maxlags)
|
| 136 |
+
best_lag = None
|
| 137 |
+
try:
|
| 138 |
+
if hasattr(sel, "selected_orders"):
|
| 139 |
+
so = sel.selected_orders
|
| 140 |
+
for k in ("aic", "bic", "fpe", "hqic"):
|
| 141 |
+
val = so.get(k, None)
|
| 142 |
+
if val is not None:
|
| 143 |
+
best_lag = int(val)
|
| 144 |
+
break
|
| 145 |
+
except Exception:
|
| 146 |
+
best_lag = None
|
| 147 |
+
if best_lag is None or best_lag < 1:
|
| 148 |
+
best_lag = 1
|
| 149 |
+
fitted = model.fit(maxlags=best_lag)
|
| 150 |
+
return fitted
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
def forecast_var(fitted_var, steps: int) -> pd.DataFrame:
|
| 154 |
+
"""
|
| 155 |
+
Multi-step forecasting for VARResults. Возвращает DataFrame прогнозов (columns = variables).
|
| 156 |
+
"""
|
| 157 |
+
try:
|
| 158 |
+
forecast = fitted_var.forecast(fitted_var.endog[-fitted_var.k_ar:], steps=steps)
|
| 159 |
+
cols = fitted_var.names
|
| 160 |
+
idx = range(1, steps + 1)
|
| 161 |
+
return pd.DataFrame(forecast, columns=cols, index=idx)
|
| 162 |
+
except Exception as e:
|
| 163 |
+
raise ValueError(f"Ошибка при прогнозе VAR: {e}")
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
def fit_garch(series: pd.Series, p: int = 1, q: int = 1):
|
| 167 |
+
"""
|
| 168 |
+
Обучает GARCH(p,q) (arch package). Возвращает объект результата fit() из arch.
|
| 169 |
+
"""
|
| 170 |
+
if not ARCH_AVAILABLE:
|
| 171 |
+
raise ImportError("arch не установлен. Установите arch (pip install arch).")
|
| 172 |
+
if not is_pandas_series(series):
|
| 173 |
+
series = pd.Series(series)
|
| 174 |
+
series_clean = series.dropna()
|
| 175 |
+
if series_clean.empty:
|
| 176 |
+
raise ValueError("Пустая серия передана в fit_garch.")
|
| 177 |
+
am = arch_model(series_clean, vol="Garch", p=p, q=q, dist="normal")
|
| 178 |
+
res = am.fit(disp="off")
|
| 179 |
+
return res
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
def safe_summary(obj) -> str:
|
| 183 |
+
try:
|
| 184 |
+
return str(obj.summary())
|
| 185 |
+
except Exception:
|
| 186 |
+
return repr(obj)
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
# краткий тест при запуске модуля напрямую
|
| 190 |
+
if __name__ == "__main__":
|
| 191 |
+
print("lab3_functions: доступные функции:",
|
| 192 |
+
[n for n in dir() if n.startswith("fit_") or n.startswith("forecast_")])
|
src/lab3_pipeline.py
ADDED
|
@@ -0,0 +1,955 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# lab3_pipeline.py
|
| 2 |
+
"""
|
| 3 |
+
Полный pipeline для ЛР №3 (выполнение пунктов 3.1-3.8).
|
| 4 |
+
Сохраняйте файл в папке src проекта (TimeSeriesHomework/src/lab3_pipeline.py).
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import os
|
| 8 |
+
import math
|
| 9 |
+
import time
|
| 10 |
+
from typing import List, Dict, Any, Optional, Tuple
|
| 11 |
+
|
| 12 |
+
import numpy as np
|
| 13 |
+
import pandas as pd
|
| 14 |
+
import matplotlib.pyplot as plt
|
| 15 |
+
|
| 16 |
+
# statsmodels и основные тесты
|
| 17 |
+
try:
|
| 18 |
+
from statsmodels.tsa.stattools import adfuller, kpss
|
| 19 |
+
from statsmodels.tsa.seasonal import seasonal_decompose
|
| 20 |
+
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
|
| 21 |
+
from statsmodels.stats.diagnostic import acorr_ljungbox
|
| 22 |
+
from statsmodels.tsa.statespace.sarimax import SARIMAX
|
| 23 |
+
from statsmodels.tsa.api import VAR
|
| 24 |
+
|
| 25 |
+
STATSMODELS_AVAILABLE = True
|
| 26 |
+
except Exception as e:
|
| 27 |
+
STATSMODELS_AVAILABLE = False
|
| 28 |
+
print("warning: statsmodels not available:", e)
|
| 29 |
+
|
| 30 |
+
# optional heavy deps
|
| 31 |
+
try:
|
| 32 |
+
import pmdarima as pm
|
| 33 |
+
|
| 34 |
+
PM_AVAILABLE = True
|
| 35 |
+
except Exception:
|
| 36 |
+
PM_AVAILABLE = False
|
| 37 |
+
|
| 38 |
+
try:
|
| 39 |
+
from arch import arch_model
|
| 40 |
+
|
| 41 |
+
ARCH_AVAILABLE = True
|
| 42 |
+
except Exception:
|
| 43 |
+
ARCH_AVAILABLE = False
|
| 44 |
+
|
| 45 |
+
try:
|
| 46 |
+
from prophet import Prophet
|
| 47 |
+
|
| 48 |
+
PROPHET_AVAILABLE = True
|
| 49 |
+
except Exception:
|
| 50 |
+
PROPHET_AVAILABLE = False
|
| 51 |
+
|
| 52 |
+
# sklearn
|
| 53 |
+
try:
|
| 54 |
+
from sklearn.model_selection import TimeSeriesSplit
|
| 55 |
+
from sklearn.linear_model import LinearRegression
|
| 56 |
+
from sklearn.metrics import r2_score
|
| 57 |
+
|
| 58 |
+
SKLEARN_AVAILABLE = True
|
| 59 |
+
except Exception:
|
| 60 |
+
SKLEARN_AVAILABLE = False
|
| 61 |
+
|
| 62 |
+
# scipy (Box-Cox, Shapiro)
|
| 63 |
+
try:
|
| 64 |
+
from scipy.stats import boxcox, boxcox_normmax, shapiro
|
| 65 |
+
|
| 66 |
+
SCIPY_AVAILABLE = True
|
| 67 |
+
except Exception:
|
| 68 |
+
SCIPY_AVAILABLE = False
|
| 69 |
+
|
| 70 |
+
try:
|
| 71 |
+
from tbats import TBATS
|
| 72 |
+
TBATS_AVAILABLE = True
|
| 73 |
+
except ImportError:
|
| 74 |
+
TBATS_AVAILABLE = False
|
| 75 |
+
|
| 76 |
+
# -------------------------------------------------------------------------
|
| 77 |
+
# Metrics
|
| 78 |
+
# -------------------------------------------------------------------------
|
| 79 |
+
def mae(y_true, y_pred): return np.mean(np.abs(y_true - y_pred))
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def rmse(y_true, y_pred): return math.sqrt(np.mean((y_true - y_pred) ** 2))
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def mape(y_true, y_pred): return np.mean(np.abs((y_true - y_pred) / (y_true + 1e-9))) * 100.0
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def smape(y_true, y_pred): return 100.0 * np.mean(
|
| 89 |
+
2.0 * np.abs(y_pred - y_true) / (np.abs(y_true) + np.abs(y_pred) + 1e-9))
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
def rmsle(y_true, y_pred): return math.sqrt(np.mean((np.log1p(y_pred) - np.log1p(y_true)) ** 2))
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
def mase(y_true, y_pred, naive_ref):
|
| 96 |
+
# naive_ref: series used to compute naive diff (e.g. train series)
|
| 97 |
+
denom = np.mean(np.abs(np.diff(naive_ref)))
|
| 98 |
+
if denom == 0:
|
| 99 |
+
return np.nan
|
| 100 |
+
return np.mean(np.abs(y_true - y_pred)) / denom
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
# -------------------------------------------------------------------------
|
| 104 |
+
# IO & preprocessing utilities
|
| 105 |
+
# -------------------------------------------------------------------------
|
| 106 |
+
def load_data(path: str, timestamp_col: str = 'timestamp', tz: Optional[str] = None) -> pd.DataFrame:
|
| 107 |
+
if path.endswith('.parquet'):
|
| 108 |
+
df = pd.read_parquet(path)
|
| 109 |
+
else:
|
| 110 |
+
df = pd.read_csv(path)
|
| 111 |
+
if timestamp_col not in df.columns:
|
| 112 |
+
raise ValueError(f"timestamp column '{timestamp_col}' not found")
|
| 113 |
+
df[timestamp_col] = pd.to_datetime(df[timestamp_col], errors='coerce')
|
| 114 |
+
if tz is not None:
|
| 115 |
+
try:
|
| 116 |
+
df[timestamp_col] = df[timestamp_col].dt.tz_localize(tz)
|
| 117 |
+
except Exception:
|
| 118 |
+
try:
|
| 119 |
+
df[timestamp_col] = df[timestamp_col].dt.tz_convert(tz)
|
| 120 |
+
except Exception:
|
| 121 |
+
pass
|
| 122 |
+
df = df.sort_values(timestamp_col).drop_duplicates(subset=[timestamp_col])
|
| 123 |
+
df = df.set_index(timestamp_col)
|
| 124 |
+
return df
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
def resample_and_interpolate(df: pd.DataFrame, freq: str = 'D', method: str = 'linear') -> pd.DataFrame:
|
| 128 |
+
dfr = df.resample(freq).asfreq()
|
| 129 |
+
if method == 'linear':
|
| 130 |
+
return dfr.interpolate(method='linear')
|
| 131 |
+
elif method == 'ffill':
|
| 132 |
+
return dfr.fillna(method='ffill')
|
| 133 |
+
else:
|
| 134 |
+
return dfr.fillna(method='ffill').interpolate()
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
# -------------------------------------------------------------------------
|
| 138 |
+
# Transformations and stationarity selection
|
| 139 |
+
# -------------------------------------------------------------------------
|
| 140 |
+
def test_stationarity_pair(series: pd.Series) -> Dict[str, Dict[str, Any]]:
|
| 141 |
+
"""Возвращает результаты ADF и KPSS"""
|
| 142 |
+
res = {}
|
| 143 |
+
if not STATSMODELS_AVAILABLE:
|
| 144 |
+
raise ImportError("statsmodels required for stationarity tests")
|
| 145 |
+
s = series.dropna()
|
| 146 |
+
if len(s) < 3:
|
| 147 |
+
return {'adf': {'pvalue': np.nan}, 'kpss': {'pvalue': np.nan}}
|
| 148 |
+
adf_res = adfuller(s, autolag='AIC', regression='c')
|
| 149 |
+
kpss_res = kpss(s, nlags='auto')
|
| 150 |
+
return {'adf': {'stat': adf_res[0], 'pvalue': adf_res[1]}, 'kpss': {'stat': kpss_res[0], 'pvalue': kpss_res[1]}}
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
def try_transformations_and_choose(y_train: pd.Series, seasonal_period: int = 7):
|
| 154 |
+
"""
|
| 155 |
+
Пробуем набор преобразований:
|
| 156 |
+
- none
|
| 157 |
+
- log (если >0)
|
| 158 |
+
- boxcox (если >0 и scipy доступен)
|
| 159 |
+
- diff(1), diff(s), diff(1).diff(s)
|
| 160 |
+
Выбираем ту комбинацию, которая минимизирует конфликт ADF/KPSS:
|
| 161 |
+
критерий: ADF.pvalue < 0.05 (хочется) и KPSS.pvalue > 0.05 (хочется).
|
| 162 |
+
Возвращаем: transformed_series, meta dict (applied transformations, lambda)
|
| 163 |
+
"""
|
| 164 |
+
candidates = []
|
| 165 |
+
# original
|
| 166 |
+
candidates.append(('none', y_train))
|
| 167 |
+
# log
|
| 168 |
+
if (y_train > 0).all():
|
| 169 |
+
candidates.append(('log', np.log(y_train)))
|
| 170 |
+
# boxcox
|
| 171 |
+
if (y_train > 0).all() and SCIPY_AVAILABLE:
|
| 172 |
+
try:
|
| 173 |
+
lam = boxcox_normmax(y_train.dropna(), brack=(-2, 2))
|
| 174 |
+
bc, _ = apply_boxcox(y_train, lmbda=lam)
|
| 175 |
+
candidates.append((f'boxcox_{lam:.4f}', bc))
|
| 176 |
+
except Exception:
|
| 177 |
+
pass
|
| 178 |
+
# differenced versions
|
| 179 |
+
# diff1 of original or of transformed series
|
| 180 |
+
final_candidates = []
|
| 181 |
+
for name, ser in candidates:
|
| 182 |
+
ser_clean = ser.dropna()
|
| 183 |
+
final_candidates.append((name, 0, ser_clean)) # 0 differences
|
| 184 |
+
if len(ser_clean) > 3:
|
| 185 |
+
final_candidates.append((name, 1, ser_clean.diff(1).dropna()))
|
| 186 |
+
if seasonal_period and len(ser_clean) > seasonal_period + 3:
|
| 187 |
+
final_candidates.append((name, seasonal_period, ser_clean.diff(seasonal_period).dropna()))
|
| 188 |
+
final_candidates.append(
|
| 189 |
+
(name, ('1+s', seasonal_period), ser_clean.diff(1).diff(seasonal_period).dropna()))
|
| 190 |
+
# Evaluate candidates
|
| 191 |
+
scored = []
|
| 192 |
+
for cand in final_candidates:
|
| 193 |
+
tag = cand[0]
|
| 194 |
+
d = cand[1]
|
| 195 |
+
ser = cand[2]
|
| 196 |
+
if ser.dropna().shape[0] < 10:
|
| 197 |
+
continue
|
| 198 |
+
try:
|
| 199 |
+
tests = test_stationarity_pair(ser)
|
| 200 |
+
# score: lower is better. We want ADF.p < 0.05 and KPSS.p > 0.05.
|
| 201 |
+
adf_p = tests['adf']['pvalue'] if tests['adf']['pvalue'] is not None else 1.0
|
| 202 |
+
kpss_p = tests['kpss']['pvalue'] if tests['kpss']['pvalue'] is not None else 0.0
|
| 203 |
+
# penalty for bad ADF (want small) and bad KPSS (want big)
|
| 204 |
+
score = (adf_p) + (1.0 - kpss_p)
|
| 205 |
+
scored.append({'tag': tag, 'diff': d, 'score': score, 'adf_p': adf_p, 'kpss_p': kpss_p, 'series': ser})
|
| 206 |
+
except Exception:
|
| 207 |
+
continue
|
| 208 |
+
if not scored:
|
| 209 |
+
return y_train, {'method': 'none', 'lambda': None}
|
| 210 |
+
scored = sorted(scored, key=lambda x: x['score'])
|
| 211 |
+
best = scored[0]
|
| 212 |
+
meta = {'method': best['tag'], 'diff': best['diff'], 'adf_p': best['adf_p'], 'kpss_p': best['kpss_p']}
|
| 213 |
+
return best['series'], meta
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
def apply_boxcox(series: pd.Series, lmbda: Optional[float] = None):
|
| 217 |
+
if not SCIPY_AVAILABLE:
|
| 218 |
+
raise ImportError("scipy required for boxcox")
|
| 219 |
+
s = series.dropna()
|
| 220 |
+
if (s <= 0).any():
|
| 221 |
+
raise ValueError("Box-Cox requires positive values")
|
| 222 |
+
if lmbda is None:
|
| 223 |
+
lmbda = boxcox_normmax(s, brack=(-2, 2))
|
| 224 |
+
transformed = boxcox(s, lmbda)
|
| 225 |
+
return pd.Series(index=s.index, data=transformed), float(lmbda)
|
| 226 |
+
|
| 227 |
+
|
| 228 |
+
# -------------------------------------------------------------------------
|
| 229 |
+
# Feature engineering
|
| 230 |
+
# -------------------------------------------------------------------------
|
| 231 |
+
def make_lags(df: pd.DataFrame, col: str, lags: List[int]):
|
| 232 |
+
for l in lags:
|
| 233 |
+
df[f'{col}_lag_{l}'] = df[col].shift(l)
|
| 234 |
+
return df
|
| 235 |
+
|
| 236 |
+
|
| 237 |
+
def make_rolls(df: pd.DataFrame, col: str, windows: List[int]):
|
| 238 |
+
for w in windows:
|
| 239 |
+
df[f'{col}_roll_mean_{w}'] = df[col].rolling(window=w, min_periods=1).mean()
|
| 240 |
+
df[f'{col}_roll_std_{w}'] = df[col].rolling(window=w, min_periods=1).std()
|
| 241 |
+
df[f'{col}_roll_min_{w}'] = df[col].rolling(window=w, min_periods=1).min()
|
| 242 |
+
df[f'{col}_roll_max_{w}'] = df[col].rolling(window=w, min_periods=1).max()
|
| 243 |
+
return df
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
def make_time_features(df: pd.DataFrame):
|
| 247 |
+
idx = df.index
|
| 248 |
+
df['dayofweek'] = idx.dayofweek
|
| 249 |
+
df['month'] = idx.month
|
| 250 |
+
df['is_weekend'] = idx.dayofweek >= 5
|
| 251 |
+
df['sin_week'] = np.sin(2 * np.pi * df['dayofweek'] / 7)
|
| 252 |
+
df['cos_month'] = np.cos(2 * np.pi * (df['month'] - 1) / 12)
|
| 253 |
+
return df
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
# -------------------------------------------------------------------------
|
| 257 |
+
# Splits, CV and strategies
|
| 258 |
+
# -------------------------------------------------------------------------
|
| 259 |
+
def chronological_split(df: pd.DataFrame, frac_train=0.7, frac_val=0.15):
|
| 260 |
+
n = len(df)
|
| 261 |
+
i_train = int(n * frac_train)
|
| 262 |
+
i_val = i_train + int(n * frac_val)
|
| 263 |
+
|
| 264 |
+
train = df.iloc[:i_train].copy()
|
| 265 |
+
val = df.iloc[i_train:i_val].copy()
|
| 266 |
+
test = df.iloc[i_val:].copy()
|
| 267 |
+
|
| 268 |
+
# Проверяем непрерывность дат
|
| 269 |
+
all_data = pd.concat([train, val, test])
|
| 270 |
+
date_diff = (all_data.index[1:] - all_data.index[:-1]).value_counts()
|
| 271 |
+
|
| 272 |
+
if len(date_diff) > 1:
|
| 273 |
+
print(f"Предупреждение: обнаружены разные интервалы между датами: {date_diff.index.tolist()}")
|
| 274 |
+
|
| 275 |
+
return train, val, test
|
| 276 |
+
|
| 277 |
+
|
| 278 |
+
def expanding_window_cv(X: pd.DataFrame, y: pd.Series, model_fit_predict, initial_train_size: int, h: int,
|
| 279 |
+
n_splits: int = 5):
|
| 280 |
+
"""Expanding window: [0:t] -> [t+1:t+h]"""
|
| 281 |
+
n = len(X)
|
| 282 |
+
step = (n - initial_train_size - h) // n_splits if n_splits > 0 else h
|
| 283 |
+
metrics = []
|
| 284 |
+
for i in range(n_splits):
|
| 285 |
+
end_train = initial_train_size + i * step
|
| 286 |
+
train_X, train_y = X.iloc[:end_train], y.iloc[:end_train]
|
| 287 |
+
test_X, test_y = X.iloc[end_train:end_train + h], y.iloc[end_train:end_train + h]
|
| 288 |
+
y_pred = model_fit_predict(train_X, train_y, h)
|
| 289 |
+
metrics.append({'fold': i, 'mae': mae(test_y.values, y_pred), 'rmse': rmse(test_y.values, y_pred)})
|
| 290 |
+
return pd.DataFrame(metrics)
|
| 291 |
+
|
| 292 |
+
|
| 293 |
+
def rolling_window_cv(X: pd.DataFrame, y: pd.Series, model_fit_predict, window: int, h: int, n_splits: int = 5):
|
| 294 |
+
"""Rolling window: [t-w:t] -> [t+1:t+h]"""
|
| 295 |
+
n = len(X)
|
| 296 |
+
step = (n - window - h) // n_splits if n_splits > 0 else h
|
| 297 |
+
metrics = []
|
| 298 |
+
for i in range(n_splits):
|
| 299 |
+
start = i * step
|
| 300 |
+
end = start + window
|
| 301 |
+
train_X, train_y = X.iloc[start:end], y.iloc[start:end]
|
| 302 |
+
test_X, test_y = X.iloc[end:end + h], y.iloc[end:end + h]
|
| 303 |
+
y_pred = model_fit_predict(train_X, train_y, h)
|
| 304 |
+
metrics.append({'fold': i, 'mae': mae(test_y.values, y_pred), 'rmse': rmse(test_y.values, y_pred)})
|
| 305 |
+
return pd.DataFrame(metrics)
|
| 306 |
+
|
| 307 |
+
|
| 308 |
+
# Strategies: recursive, direct, hybrid
|
| 309 |
+
def forecast_recursive_arima(fit_res, steps: int, last_date: pd.Timestamp = None, freq: str = 'D'):
|
| 310 |
+
"""Wrapper for SARIMAX results with proper date index"""
|
| 311 |
+
if hasattr(fit_res, "get_forecast"):
|
| 312 |
+
fc = fit_res.get_forecast(steps=steps)
|
| 313 |
+
mean = np.asarray(fc.predicted_mean)
|
| 314 |
+
try:
|
| 315 |
+
conf = fc.conf_int()
|
| 316 |
+
low = np.asarray(conf.iloc[:, 0])
|
| 317 |
+
high = np.asarray(conf.iloc[:, 1])
|
| 318 |
+
except Exception:
|
| 319 |
+
low = np.full(len(mean), np.nan)
|
| 320 |
+
high = np.full(len(mean), np.nan)
|
| 321 |
+
|
| 322 |
+
# Создаем правильный индекс
|
| 323 |
+
if last_date is not None:
|
| 324 |
+
dates = create_forecast_index(last_date, steps, freq)
|
| 325 |
+
mean = pd.Series(mean, index=dates)
|
| 326 |
+
low = pd.Series(low, index=dates)
|
| 327 |
+
high = pd.Series(high, index=dates)
|
| 328 |
+
|
| 329 |
+
return mean, (low, high)
|
| 330 |
+
else:
|
| 331 |
+
mean = fit_res.forecast(steps=steps)
|
| 332 |
+
if last_date is not None:
|
| 333 |
+
dates = create_forecast_index(last_date, steps, freq)
|
| 334 |
+
mean = pd.Series(mean, index=dates)
|
| 335 |
+
return mean, (None, None)
|
| 336 |
+
|
| 337 |
+
|
| 338 |
+
# Direct strategy for SARIMAX: fit separate models for each horizon
|
| 339 |
+
def forecast_direct_arima(train_series: pd.Series, h: int, order=(1, 0, 0)):
|
| 340 |
+
if not STATSMODELS_AVAILABLE:
|
| 341 |
+
raise ImportError("statsmodels required")
|
| 342 |
+
# create shifted target for forecasting h steps ahead
|
| 343 |
+
df = train_series.to_frame("y")
|
| 344 |
+
df['y_target_h'] = df['y'].shift(-h)
|
| 345 |
+
df = df.dropna()
|
| 346 |
+
# naive approach: use previous value as predictor (simple)
|
| 347 |
+
last = train_series.iloc[-1]
|
| 348 |
+
return np.full(h, last)
|
| 349 |
+
|
| 350 |
+
|
| 351 |
+
# -------------------------------------------------------------------------
|
| 352 |
+
# Models training wrapper
|
| 353 |
+
# -------------------------------------------------------------------------
|
| 354 |
+
def fit_sarimax_simple(series: pd.Series, order=(1, 0, 0), seasonal_order=(0, 0, 0, 0), **kwargs):
|
| 355 |
+
if not STATSMODELS_AVAILABLE:
|
| 356 |
+
raise ImportError("statsmodels required")
|
| 357 |
+
m = SARIMAX(series.dropna(), order=order, seasonal_order=seasonal_order,
|
| 358 |
+
enforce_stationarity=False, enforce_invertibility=False)
|
| 359 |
+
res = m.fit(disp=False, **kwargs)
|
| 360 |
+
return res
|
| 361 |
+
|
| 362 |
+
|
| 363 |
+
def forecast_sarimax(fit_res, steps: int, alpha: float = 0.05) -> Tuple[np.ndarray, Tuple[np.ndarray, np.ndarray]]:
|
| 364 |
+
"""
|
| 365 |
+
Делает прогноз из обученного SARIMAX-результата.
|
| 366 |
+
Возвращает (mean, (lower, upper)) — numpy arrays длины steps.
|
| 367 |
+
"""
|
| 368 |
+
try:
|
| 369 |
+
if hasattr(fit_res, "get_forecast"):
|
| 370 |
+
fc = fit_res.get_forecast(steps=steps)
|
| 371 |
+
mean = np.asarray(fc.predicted_mean)
|
| 372 |
+
|
| 373 |
+
# Проверяем на NaN
|
| 374 |
+
if np.any(np.isnan(mean)):
|
| 375 |
+
# Fallback: используем простой forecast
|
| 376 |
+
mean = fit_res.forecast(steps=steps)
|
| 377 |
+
mean = np.asarray(mean)
|
| 378 |
+
|
| 379 |
+
try:
|
| 380 |
+
conf = fc.conf_int(alpha=alpha)
|
| 381 |
+
lower = np.asarray(conf.iloc[:, 0])
|
| 382 |
+
upper = np.asarray(conf.iloc[:, 1])
|
| 383 |
+
|
| 384 |
+
# Проверяем доверительные интервалы на NaN
|
| 385 |
+
if np.any(np.isnan(lower)) or np.any(np.isnan(upper)):
|
| 386 |
+
lower = np.full(len(mean), np.nan)
|
| 387 |
+
upper = np.full(len(mean), np.nan)
|
| 388 |
+
|
| 389 |
+
except Exception:
|
| 390 |
+
lower = np.full(len(mean), np.nan)
|
| 391 |
+
upper = np.full(len(mean), np.nan)
|
| 392 |
+
|
| 393 |
+
return mean, (lower, upper)
|
| 394 |
+
else:
|
| 395 |
+
# fallback на forecast
|
| 396 |
+
mean = fit_res.forecast(steps=steps)
|
| 397 |
+
mean = np.asarray(mean)
|
| 398 |
+
lower = np.full(len(mean), np.nan)
|
| 399 |
+
upper = np.full(len(mean), np.nan)
|
| 400 |
+
return mean, (lower, upper)
|
| 401 |
+
|
| 402 |
+
except Exception as e:
|
| 403 |
+
# Если все методы не сработали, возвращаем массив NaN
|
| 404 |
+
print(f"Warning: SARIMAX forecast failed: {e}")
|
| 405 |
+
mean = np.full(steps, np.nan)
|
| 406 |
+
lower = np.full(steps, np.nan)
|
| 407 |
+
upper = np.full(steps, np.nan)
|
| 408 |
+
return mean, (lower, upper)
|
| 409 |
+
|
| 410 |
+
def fit_auto_arima(series: pd.Series, seasonal=False, m=1, **kwargs):
|
| 411 |
+
if not PM_AVAILABLE:
|
| 412 |
+
raise ImportError("pmdarima not installed")
|
| 413 |
+
model = pm.auto_arima(series.dropna(), seasonal=seasonal, m=m, error_action='ignore', suppress_warnings=True,
|
| 414 |
+
**kwargs)
|
| 415 |
+
return model
|
| 416 |
+
|
| 417 |
+
|
| 418 |
+
def fit_var(df: pd.DataFrame, maxlags=15):
|
| 419 |
+
if not STATSMODELS_AVAILABLE:
|
| 420 |
+
raise ImportError("statsmodels required")
|
| 421 |
+
model = VAR(df.dropna())
|
| 422 |
+
sel = model.select_order(maxlags=maxlags)
|
| 423 |
+
best = 1
|
| 424 |
+
try:
|
| 425 |
+
so = sel.selected_orders
|
| 426 |
+
for k in ('aic', 'bic', 'fpe', 'hqic'):
|
| 427 |
+
if so.get(k) is not None:
|
| 428 |
+
best = int(so[k])
|
| 429 |
+
break
|
| 430 |
+
except Exception:
|
| 431 |
+
best = 1
|
| 432 |
+
res = model.fit(maxlags=best)
|
| 433 |
+
return res
|
| 434 |
+
|
| 435 |
+
|
| 436 |
+
def fit_garch_on_residuals(residuals, p=1, q=1):
|
| 437 |
+
if not ARCH_AVAILABLE:
|
| 438 |
+
raise ImportError("arch not installed")
|
| 439 |
+
am = arch_model(residuals, vol='Garch', p=p, q=q, dist='normal')
|
| 440 |
+
r = am.fit(disp='off')
|
| 441 |
+
return r
|
| 442 |
+
|
| 443 |
+
|
| 444 |
+
# -------------------------------------------------------------------------
|
| 445 |
+
# Diagnostics and tests
|
| 446 |
+
# -------------------------------------------------------------------------
|
| 447 |
+
def ljung_box_test(resid: np.ndarray, lags: List[int] = [10]):
|
| 448 |
+
if not STATSMODELS_AVAILABLE:
|
| 449 |
+
raise ImportError("statsmodels required")
|
| 450 |
+
res = acorr_ljungbox(resid, lags=lags, return_df=True)
|
| 451 |
+
return res
|
| 452 |
+
|
| 453 |
+
|
| 454 |
+
def shapiro_test(resid: np.ndarray):
|
| 455 |
+
if not SCIPY_AVAILABLE:
|
| 456 |
+
raise ImportError("scipy required")
|
| 457 |
+
stat, p = shapiro(resid)
|
| 458 |
+
return {'stat': stat, 'pvalue': p}
|
| 459 |
+
|
| 460 |
+
|
| 461 |
+
def simple_dm_test(e1: np.ndarray, e2: np.ndarray):
|
| 462 |
+
"""
|
| 463 |
+
Простая реализация Diebold-Mariano теста по разности квадратических ошибок.
|
| 464 |
+
Возвращает t-stat и p-value (двухсторонний).
|
| 465 |
+
Примечание: это упрощённая версия, без HAC коррекции.
|
| 466 |
+
"""
|
| 467 |
+
# use squared error loss
|
| 468 |
+
d = (e1 - e2)
|
| 469 |
+
n = len(d)
|
| 470 |
+
dbar = np.mean(d)
|
| 471 |
+
sd = np.var(d, ddof=1)
|
| 472 |
+
denom = math.sqrt(sd / n) if sd > 0 else np.nan
|
| 473 |
+
if denom == 0 or np.isnan(denom):
|
| 474 |
+
return {'stat': np.nan, 'pvalue': np.nan}
|
| 475 |
+
tstat = dbar / denom
|
| 476 |
+
# two-sided pval from Student's t approx
|
| 477 |
+
from scipy.stats import t as student_t
|
| 478 |
+
pval = 2 * (1 - student_t.cdf(abs(tstat), df=n - 1))
|
| 479 |
+
return {'stat': float(tstat), 'pvalue': float(pval)}
|
| 480 |
+
|
| 481 |
+
|
| 482 |
+
# -------------------------------------------------------------------------
|
| 483 |
+
# Report generation (HTML)
|
| 484 |
+
# -------------------------------------------------------------------------
|
| 485 |
+
def generate_report_html(out_path: str, plots: List[plt.Figure], tables: Dict[str, pd.DataFrame], title="Lab3 Report"):
|
| 486 |
+
import base64
|
| 487 |
+
from io import BytesIO
|
| 488 |
+
|
| 489 |
+
html_parts = [f"""
|
| 490 |
+
<html>
|
| 491 |
+
<head>
|
| 492 |
+
<meta charset='utf-8'>
|
| 493 |
+
<title>{title}</title>
|
| 494 |
+
<style>
|
| 495 |
+
body {{ font-family: Arial, sans-serif; margin: 20px; background-color: white; color: black; }}
|
| 496 |
+
table {{ border-collapse: collapse; width: 100%; margin: 10px 0; }}
|
| 497 |
+
th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
|
| 498 |
+
th {{ background-color: #f2f2f2; }}
|
| 499 |
+
img {{ max-width: 100%; height: auto; margin: 10px 0; }}
|
| 500 |
+
.table-container {{ overflow-x: auto; }}
|
| 501 |
+
</style>
|
| 502 |
+
</head>
|
| 503 |
+
<body>
|
| 504 |
+
<h1>{title}</h1>
|
| 505 |
+
"""]
|
| 506 |
+
|
| 507 |
+
# Таблицы
|
| 508 |
+
for name, df in tables.items():
|
| 509 |
+
html_parts.append(f"<h2>{name}</h2>")
|
| 510 |
+
html_parts.append('<div class="table-container">')
|
| 511 |
+
html_parts.append(df.to_html(classes='table table-striped', border=0, index=True))
|
| 512 |
+
html_parts.append('</div>')
|
| 513 |
+
|
| 514 |
+
# Графики как base64
|
| 515 |
+
for i, fig in enumerate(plots):
|
| 516 |
+
# Сохраняем рисунок в буфер
|
| 517 |
+
buf = BytesIO()
|
| 518 |
+
fig.savefig(buf, format='png', bbox_inches='tight', dpi=100)
|
| 519 |
+
buf.seek(0)
|
| 520 |
+
|
| 521 |
+
# Кодируем в base64
|
| 522 |
+
img_data = base64.b64encode(buf.read()).decode('utf-8')
|
| 523 |
+
html_parts.append(f'<h3>Figure {i + 1}</h3>')
|
| 524 |
+
html_parts.append(f'<img src="data:image/png;base64,{img_data}" alt="Figure {i + 1}">')
|
| 525 |
+
|
| 526 |
+
# Закрываем рисунок чтобы освободить память
|
| 527 |
+
plt.close(fig)
|
| 528 |
+
break
|
| 529 |
+
|
| 530 |
+
html_parts.append("</body></html>")
|
| 531 |
+
|
| 532 |
+
with open(out_path, 'w', encoding='utf-8') as f:
|
| 533 |
+
f.write("\n".join(html_parts))
|
| 534 |
+
print("Report saved to", out_path)
|
| 535 |
+
|
| 536 |
+
# -------------------------------------------------------------------------
|
| 537 |
+
# Main runner that orchestrates everything
|
| 538 |
+
# -------------------------------------------------------------------------
|
| 539 |
+
def evaluate_with_cv(models_dict, X, y, cv_method='expanding', n_splits=5):
|
| 540 |
+
"""Оценка моделей с кросс-валидацией"""
|
| 541 |
+
cv_results = {}
|
| 542 |
+
|
| 543 |
+
for name, model_func in models_dict.items():
|
| 544 |
+
if cv_method == 'expanding':
|
| 545 |
+
cv_scores = expanding_window_cv(X, y, model_func,
|
| 546 |
+
initial_train_size=len(X) // 2,
|
| 547 |
+
h=30, n_splits=n_splits)
|
| 548 |
+
else:
|
| 549 |
+
cv_scores = rolling_window_cv(X, y, model_func,
|
| 550 |
+
window=len(X) // 2,
|
| 551 |
+
h=30, n_splits=n_splits)
|
| 552 |
+
cv_results[name] = cv_scores
|
| 553 |
+
|
| 554 |
+
return cv_results
|
| 555 |
+
|
| 556 |
+
def run_pipeline(data_path: str, timestamp_col: str, target_col: str,
|
| 557 |
+
out_report: str = 'lab3_report.html', freq: str = 'D'):
|
| 558 |
+
|
| 559 |
+
"""
|
| 560 |
+
Главная точка запуска pipeline.
|
| 561 |
+
"""
|
| 562 |
+
print("Loading", data_path)
|
| 563 |
+
df = load_data(data_path, timestamp_col)
|
| 564 |
+
if target_col not in df.columns:
|
| 565 |
+
numeric_cols = df.select_dtypes(include=[np.number]).columns.tolist()
|
| 566 |
+
if not numeric_cols:
|
| 567 |
+
raise ValueError("No numeric columns found")
|
| 568 |
+
target_col = numeric_cols[0]
|
| 569 |
+
print("Target not found, using", target_col)
|
| 570 |
+
|
| 571 |
+
series = df[target_col].astype('float').copy()
|
| 572 |
+
series = resample_and_interpolate(series.to_frame(), freq=freq).iloc[:, 0]
|
| 573 |
+
print("Series length after resample:", len(series))
|
| 574 |
+
|
| 575 |
+
# 3.1 Preprocessing & transformation selection
|
| 576 |
+
transformed, meta = try_transformations_and_choose(series, seasonal_period=7)
|
| 577 |
+
print("Chosen transform:", meta)
|
| 578 |
+
|
| 579 |
+
# 3.2 Feature engineering
|
| 580 |
+
df_all = transformed.to_frame(name=target_col)
|
| 581 |
+
df_all = make_time_features(df_all)
|
| 582 |
+
df_all = make_lags(df_all, target_col, [1, 2, 7, 30])
|
| 583 |
+
df_all = make_rolls(df_all, target_col, [7, 30])
|
| 584 |
+
|
| 585 |
+
# dropna rows with lag features
|
| 586 |
+
df_all = df_all.dropna()
|
| 587 |
+
train, val, test = chronological_split(df_all, frac_train=0.7, frac_val=0.15)
|
| 588 |
+
y_train = train[target_col];
|
| 589 |
+
y_val = val[target_col];
|
| 590 |
+
y_test = test[target_col]
|
| 591 |
+
print("Sizes train/val/test:", len(y_train), len(y_val), len(y_test))
|
| 592 |
+
|
| 593 |
+
# 3.3 Models: benchmarks + SARIMAX + optional auto_arima + VAR
|
| 594 |
+
results = [] # each elem: dict(model, h, preds (np.array), extra)
|
| 595 |
+
horizons = [1, 7, 30]
|
| 596 |
+
|
| 597 |
+
# Определяем частоту для прогнозов
|
| 598 |
+
try:
|
| 599 |
+
inferred_freq = pd.infer_freq(y_train.index) or freq
|
| 600 |
+
except:
|
| 601 |
+
inferred_freq = freq
|
| 602 |
+
|
| 603 |
+
# Benchmarks
|
| 604 |
+
for h in horizons:
|
| 605 |
+
pred_values = np.full(h, y_train.iloc[-1])
|
| 606 |
+
pred_dates = create_forecast_index(y_train.index[-1], h, inferred_freq)
|
| 607 |
+
results.append({
|
| 608 |
+
'model': 'naive',
|
| 609 |
+
'h': h,
|
| 610 |
+
'pred': pd.Series(pred_values, index=pred_dates)
|
| 611 |
+
})
|
| 612 |
+
|
| 613 |
+
if len(y_train) >= 7:
|
| 614 |
+
seasonal_pred = seasonal_naive_forecast(y_train, season=7, steps=h)
|
| 615 |
+
seasonal_dates = create_forecast_index(y_train.index[-1], h, inferred_freq)
|
| 616 |
+
results.append({
|
| 617 |
+
'model': 'seasonal_naive',
|
| 618 |
+
'h': h,
|
| 619 |
+
'pred': pd.Series(seasonal_pred, index=seasonal_dates)
|
| 620 |
+
})
|
| 621 |
+
|
| 622 |
+
# SES/Holt (simple forecasting for 1-step and iterated for multi-step)
|
| 623 |
+
try:
|
| 624 |
+
from statsmodels.tsa.holtwinters import SimpleExpSmoothing, ExponentialSmoothing
|
| 625 |
+
# SES as simple baseline
|
| 626 |
+
ses = SimpleExpSmoothing(y_train.dropna()).fit(optimized=True)
|
| 627 |
+
for h in horizons:
|
| 628 |
+
pred = ses.forecast(h)
|
| 629 |
+
pred_dates = create_forecast_index(y_train.index[-1], h, inferred_freq)
|
| 630 |
+
results.append({
|
| 631 |
+
'model': 'SES',
|
| 632 |
+
'h': h,
|
| 633 |
+
'pred': pd.Series(pred, index=pred_dates)
|
| 634 |
+
})
|
| 635 |
+
except Exception as e:
|
| 636 |
+
print("SES skipped:", e)
|
| 637 |
+
|
| 638 |
+
# SARIMAX baseline
|
| 639 |
+
if STATSMODELS_AVAILABLE:
|
| 640 |
+
try:
|
| 641 |
+
# Проверяем, что данные подходят для SARIMAX
|
| 642 |
+
if len(y_train.dropna()) > 10 and y_train.var() > 1e-6: # достаточное количество точек и дисперсия
|
| 643 |
+
sar = fit_sarimax_simple(y_train, order=(1, 1, 1))
|
| 644 |
+
|
| 645 |
+
# Проверяем, что модель сходилась
|
| 646 |
+
if hasattr(sar, 'mle_retvals') and sar.mle_retvals.get('converged', False):
|
| 647 |
+
for h in horizons:
|
| 648 |
+
mean, (lower, upper) = forecast_sarimax(sar, steps=h)
|
| 649 |
+
|
| 650 |
+
# Проверяем, что прогнозы не все NaN
|
| 651 |
+
if not np.all(np.isnan(mean)):
|
| 652 |
+
pred_dates = create_forecast_index(y_train.index[-1], h, inferred_freq)
|
| 653 |
+
results.append({
|
| 654 |
+
'model': 'SARIMAX(1,1,1)',
|
| 655 |
+
'h': h,
|
| 656 |
+
'pred': pd.Series(mean, index=pred_dates)
|
| 657 |
+
})
|
| 658 |
+
else:
|
| 659 |
+
print(f"SARIMAX returned all NaN for horizon {h}")
|
| 660 |
+
else:
|
| 661 |
+
print("SARIMAX model did not converge")
|
| 662 |
+
else:
|
| 663 |
+
print("Insufficient data for SARIMAX")
|
| 664 |
+
except Exception as e:
|
| 665 |
+
print("SARIMAX failed:", e)
|
| 666 |
+
|
| 667 |
+
# pmdarima auto_arima
|
| 668 |
+
if PM_AVAILABLE:
|
| 669 |
+
try:
|
| 670 |
+
auto = fit_auto_arima(y_train, seasonal=False)
|
| 671 |
+
for h in horizons:
|
| 672 |
+
p = auto.predict(n_periods=h)
|
| 673 |
+
pred_dates = create_forecast_index(y_train.index[-1], h, inferred_freq)
|
| 674 |
+
results.append({
|
| 675 |
+
'model': 'auto_arima',
|
| 676 |
+
'h': h,
|
| 677 |
+
'pred': pd.Series(p, index=pred_dates)
|
| 678 |
+
})
|
| 679 |
+
except Exception as e:
|
| 680 |
+
print("auto_arima failed:", e)
|
| 681 |
+
|
| 682 |
+
# VAR if multivariate
|
| 683 |
+
if STATSMODELS_AVAILABLE and df.select_dtypes(include=[np.number]).shape[1] >= 2:
|
| 684 |
+
try:
|
| 685 |
+
num_df = df.select_dtypes(include=[np.number]).dropna()
|
| 686 |
+
var_res = fit_var(num_df, maxlags=5)
|
| 687 |
+
fut = var_res.forecast(var_res.endog[-var_res.k_ar:], steps=30)
|
| 688 |
+
# fut is array shape (30, k)
|
| 689 |
+
# wrap as predictions per horizon for the first variable
|
| 690 |
+
for h in [30]:
|
| 691 |
+
pred_dates = create_forecast_index(y_train.index[-1], h, inferred_freq)
|
| 692 |
+
results.append({
|
| 693 |
+
'model': 'VAR',
|
| 694 |
+
'h': h,
|
| 695 |
+
'pred': pd.Series(fut[:h, 0], index=pred_dates)
|
| 696 |
+
})
|
| 697 |
+
except Exception as e:
|
| 698 |
+
print("VAR failed:", e)
|
| 699 |
+
|
| 700 |
+
# TBATS модель
|
| 701 |
+
if TBATS_AVAILABLE:
|
| 702 |
+
try:
|
| 703 |
+
tbats_model = TBATS(seasonal_periods=[7, 30], use_arma_errors=True)
|
| 704 |
+
tbats_fitted = tbats_model.fit(y_train)
|
| 705 |
+
for h in horizons:
|
| 706 |
+
tbats_pred = tbats_fitted.forecast(steps=h)
|
| 707 |
+
pred_dates = create_forecast_index(y_train.index[-1], h, inferred_freq)
|
| 708 |
+
results.append({
|
| 709 |
+
'model': 'TBATS',
|
| 710 |
+
'h': h,
|
| 711 |
+
'pred': pd.Series(tbats_pred, index=pred_dates)
|
| 712 |
+
})
|
| 713 |
+
except Exception as e:
|
| 714 |
+
print("TBATS failed:", e)
|
| 715 |
+
|
| 716 |
+
# Prophet модель
|
| 717 |
+
if PROPHET_AVAILABLE:
|
| 718 |
+
try:
|
| 719 |
+
prophet_df = y_train.reset_index()
|
| 720 |
+
prophet_df.columns = ['ds', 'y']
|
| 721 |
+
prophet_model = Prophet()
|
| 722 |
+
prophet_model.fit(prophet_df)
|
| 723 |
+
future = prophet_model.make_future_dataframe(periods=max(horizons), freq=inferred_freq)
|
| 724 |
+
forecast = prophet_model.predict(future)
|
| 725 |
+
for h in horizons:
|
| 726 |
+
prophet_pred = forecast.tail(h)['yhat'].values
|
| 727 |
+
pred_dates = create_forecast_index(y_train.index[-1], h, inferred_freq)
|
| 728 |
+
results.append({
|
| 729 |
+
'model': 'Prophet',
|
| 730 |
+
'h': h,
|
| 731 |
+
'pred': pd.Series(prophet_pred, index=pred_dates)
|
| 732 |
+
})
|
| 733 |
+
except Exception as e:
|
| 734 |
+
print("Prophet failed:", e)
|
| 735 |
+
|
| 736 |
+
# GARCH на остатках SARIMAX
|
| 737 |
+
if ARCH_AVAILABLE and 'sar' in locals():
|
| 738 |
+
try:
|
| 739 |
+
garch_model = fit_garch_on_residuals(sar.resid, p=1, q=1)
|
| 740 |
+
# Прогноз волатильности можно добавить в анализ
|
| 741 |
+
except Exception as e:
|
| 742 |
+
print("GARCH failed:", e)
|
| 743 |
+
|
| 744 |
+
# 3.6 Diagnostics later for top models
|
| 745 |
+
# 3.7 Evaluate on test set
|
| 746 |
+
eval_rows = []
|
| 747 |
+
plots = []
|
| 748 |
+
|
| 749 |
+
for rec in results:
|
| 750 |
+
model_name = rec['model']
|
| 751 |
+
h = rec['h']
|
| 752 |
+
pred = rec['pred']
|
| 753 |
+
|
| 754 |
+
# Выравниваем прогнозы с тестовыми данными по времени
|
| 755 |
+
if hasattr(pred, 'index'):
|
| 756 |
+
# Для прогнозов с правильным индексом
|
| 757 |
+
aligned_pred = pred
|
| 758 |
+
# Берем только первые h точек тестовых данных для сравнения
|
| 759 |
+
y_true_aligned = y_test.iloc[:min(h, len(y_test))]
|
| 760 |
+
else:
|
| 761 |
+
# Для прогнозов без индекса (старый формат)
|
| 762 |
+
pred_values = np.asarray(pred).ravel()
|
| 763 |
+
aligned_pred = pd.Series(pred_values, index=y_test.index[:len(pred_values)])
|
| 764 |
+
y_true_aligned = y_test.iloc[:len(pred_values)]
|
| 765 |
+
|
| 766 |
+
if len(y_true_aligned) == 0:
|
| 767 |
+
continue
|
| 768 |
+
|
| 769 |
+
# Обрезаем прогноз до длины тестовых данных
|
| 770 |
+
aligned_pred = aligned_pred.iloc[:len(y_true_aligned)]
|
| 771 |
+
|
| 772 |
+
# Вычисляем метрики
|
| 773 |
+
row = {
|
| 774 |
+
'model': model_name,
|
| 775 |
+
'h': h,
|
| 776 |
+
'MAE': mae(y_true_aligned.values, aligned_pred.values),
|
| 777 |
+
'RMSE': rmse(y_true_aligned.values, aligned_pred.values),
|
| 778 |
+
'MAPE': mape(y_true_aligned.values, aligned_pred.values),
|
| 779 |
+
'SMAPE': smape(y_true_aligned.values, aligned_pred.values)
|
| 780 |
+
}
|
| 781 |
+
# MASE: use naive in-sample reference
|
| 782 |
+
row['MASE'] = mase(y_true_aligned.values, aligned_pred.values, y_train.values)
|
| 783 |
+
# R2 where possible
|
| 784 |
+
try:
|
| 785 |
+
row['R2'] = float((1 - np.sum((y_true_aligned.values - aligned_pred.values) ** 2) / np.sum(
|
| 786 |
+
(y_true_aligned.values - np.mean(y_true_aligned.values)) ** 2)))
|
| 787 |
+
except Exception:
|
| 788 |
+
row['R2'] = np.nan
|
| 789 |
+
eval_rows.append(row)
|
| 790 |
+
|
| 791 |
+
# Визуализация
|
| 792 |
+
fig, ax = plt.subplots(figsize=(8, 3))
|
| 793 |
+
|
| 794 |
+
# Показываем больше данных для контекста
|
| 795 |
+
context_points = min(200, len(y_train))
|
| 796 |
+
ax.plot(y_train.index[-context_points:], y_train.values[-context_points:],
|
| 797 |
+
label='train', alpha=0.7)
|
| 798 |
+
|
| 799 |
+
if len(val) > 0:
|
| 800 |
+
ax.plot(val.index, val.values, label='val', alpha=0.7)
|
| 801 |
+
|
| 802 |
+
ax.plot(y_test.index, y_test.values, label='test', alpha=0.7)
|
| 803 |
+
|
| 804 |
+
# Прогнозы с правильными датами
|
| 805 |
+
ax.plot(aligned_pred.index, aligned_pred.values,
|
| 806 |
+
label=f'pred_{model_name}_h{h}', linewidth=2)
|
| 807 |
+
|
| 808 |
+
ax.legend()
|
| 809 |
+
plots.append(fig)
|
| 810 |
+
|
| 811 |
+
eval_df = pd.DataFrame(eval_rows)
|
| 812 |
+
|
| 813 |
+
# Diagnostics for top-3 by RMSE
|
| 814 |
+
diag_tables = {}
|
| 815 |
+
try:
|
| 816 |
+
top3 = eval_df.sort_values('RMSE').head(3)['model'].tolist()
|
| 817 |
+
except Exception:
|
| 818 |
+
top3 = []
|
| 819 |
+
for m in top3:
|
| 820 |
+
# find corresponding fitted residuals if model was SARIMAX etc.
|
| 821 |
+
if m.startswith('SARIMAX'):
|
| 822 |
+
try:
|
| 823 |
+
resid = sar.resid.dropna()
|
| 824 |
+
lb = acorr_ljungbox(resid, lags=[10], return_df=True)
|
| 825 |
+
diag_tables[f'ljungbox_{m}'] = lb
|
| 826 |
+
if SCIPY_AVAILABLE:
|
| 827 |
+
sh = shapiro(resid)
|
| 828 |
+
diag_tables[f'shapiro_{m}'] = pd.DataFrame([{'stat': sh[0], 'pvalue': sh[1]}])
|
| 829 |
+
except Exception:
|
| 830 |
+
pass
|
| 831 |
+
|
| 832 |
+
# Diebold-Mariano pairwise for top 2 models (if available)
|
| 833 |
+
dm_table = None
|
| 834 |
+
try:
|
| 835 |
+
if len(eval_df) >= 2:
|
| 836 |
+
sorted_models = eval_df.sort_values('RMSE')
|
| 837 |
+
if len(sorted_models) >= 2:
|
| 838 |
+
m1 = sorted_models.iloc[0]['model']
|
| 839 |
+
m2 = sorted_models.iloc[1]['model']
|
| 840 |
+
# pick their predictions at h=1 (if exist)
|
| 841 |
+
pred1 = None;
|
| 842 |
+
pred2 = None
|
| 843 |
+
for rec in results:
|
| 844 |
+
if rec['model'] == m1 and rec['h'] == 1:
|
| 845 |
+
pred1 = rec['pred']
|
| 846 |
+
if rec['model'] == m2 and rec['h'] == 1:
|
| 847 |
+
pred2 = rec['pred']
|
| 848 |
+
if pred1 is not None and pred2 is not None:
|
| 849 |
+
# align lengths with test
|
| 850 |
+
y_true = y_test.values[:min(len(pred1), len(y_test))]
|
| 851 |
+
e1 = (y_true - pred1.values[:len(y_true)]) ** 2
|
| 852 |
+
e2 = (y_true - pred2.values[:len(y_true)]) ** 2
|
| 853 |
+
dm = simple_dm_test(e1, e2)
|
| 854 |
+
dm_table = pd.DataFrame(
|
| 855 |
+
[{'model1': m1, 'model2': m2, 'dm_stat': dm['stat'], 'pvalue': dm['pvalue']}])
|
| 856 |
+
except Exception:
|
| 857 |
+
dm_table = None
|
| 858 |
+
|
| 859 |
+
# Generate report
|
| 860 |
+
tables = {'evaluation': eval_df}
|
| 861 |
+
if diag_tables:
|
| 862 |
+
tables.update(diag_tables)
|
| 863 |
+
if dm_table is not None:
|
| 864 |
+
tables['dm_test'] = dm_table
|
| 865 |
+
|
| 866 |
+
generate_report_html(out_report, plots, tables, title="Lab3 Full Report")
|
| 867 |
+
print("Pipeline finished. Report:", out_report)
|
| 868 |
+
|
| 869 |
+
# Ensure we have at least some predictions
|
| 870 |
+
if not results:
|
| 871 |
+
st.warning("Все модели вернули NaN. Использую простой наивный прогноз.")
|
| 872 |
+
for h in horizons:
|
| 873 |
+
pred_values = np.full(h, y_train.iloc[-1] if len(y_train) > 0 else 0)
|
| 874 |
+
pred_dates = create_forecast_index(y_train.index[-1], h, inferred_freq)
|
| 875 |
+
results.append({
|
| 876 |
+
'model': 'fallback_naive',
|
| 877 |
+
'h': h,
|
| 878 |
+
'pred': pd.Series(pred_values, index=pred_dates)
|
| 879 |
+
})
|
| 880 |
+
|
| 881 |
+
cv_results = evaluate_with_cv({
|
| 882 |
+
'SARIMAX': lambda X, y, h: forecast_recursive(fit_sarimax_simple(y), y, h),
|
| 883 |
+
'AutoARIMA': lambda X, y, h: forecast_recursive(fit_auto_arima(y), y, h)
|
| 884 |
+
}, df_all.drop(columns=[target_col]), df_all[target_col])
|
| 885 |
+
|
| 886 |
+
|
| 887 |
+
# -------------------------
|
| 888 |
+
# helpers used in the pipeline but defined later
|
| 889 |
+
# -------------------------
|
| 890 |
+
def seasonal_naive_forecast(series: pd.Series, season: int, steps: int):
|
| 891 |
+
last = series.iloc[-season:]
|
| 892 |
+
reps = int(np.ceil(steps / season))
|
| 893 |
+
arr = np.tile(last.values, reps)[:steps]
|
| 894 |
+
return arr
|
| 895 |
+
|
| 896 |
+
|
| 897 |
+
def create_forecast_index(last_train_date: pd.Timestamp, steps: int, freq: str = 'D') -> pd.DatetimeIndex:
|
| 898 |
+
"""Создает правильный временной индекс для прогнозов"""
|
| 899 |
+
try:
|
| 900 |
+
# Если freq = 'auto', пытаемся определить частоту
|
| 901 |
+
if freq == 'auto':
|
| 902 |
+
freq = pd.infer_freq(pd.DatetimeIndex([last_train_date])) or 'D'
|
| 903 |
+
|
| 904 |
+
# Создаем индекс с правильным смещением
|
| 905 |
+
if isinstance(last_train_date, pd.Timestamp):
|
| 906 |
+
start_date = last_train_date + pd.Timedelta(days=1)
|
| 907 |
+
else:
|
| 908 |
+
start_date = last_train_date + pd.DateOffset(days=1)
|
| 909 |
+
|
| 910 |
+
return pd.date_range(
|
| 911 |
+
start=start_date,
|
| 912 |
+
periods=steps,
|
| 913 |
+
freq=freq
|
| 914 |
+
)
|
| 915 |
+
except Exception as e:
|
| 916 |
+
print(f"Warning: could not create proper date index: {e}")
|
| 917 |
+
# Fallback: числовой индекс
|
| 918 |
+
return pd.RangeIndex(start=0, stop=steps)
|
| 919 |
+
|
| 920 |
+
|
| 921 |
+
def forecast_recursive(model, series, steps, freq='D'):
|
| 922 |
+
"""Рекурсивная стратегия прогнозирования"""
|
| 923 |
+
predictions = []
|
| 924 |
+
current_series = series.copy()
|
| 925 |
+
|
| 926 |
+
for _ in range(steps):
|
| 927 |
+
if hasattr(model, 'predict'):
|
| 928 |
+
pred = model.predict(n_periods=1)
|
| 929 |
+
else:
|
| 930 |
+
pred = model.forecast(steps=1)
|
| 931 |
+
predictions.append(pred[0])
|
| 932 |
+
# Обновляем ряд для следующей итерации
|
| 933 |
+
current_series = pd.concat(
|
| 934 |
+
[current_series, pd.Series([pred[0]], index=[current_series.index[-1] + pd.Timedelta(days=1)])])
|
| 935 |
+
|
| 936 |
+
return np.array(predictions)
|
| 937 |
+
|
| 938 |
+
|
| 939 |
+
def forecast_direct(train_series, test_features, model_factory, steps):
|
| 940 |
+
"""Прямая стратегия - отдельная модель для каждого горизонта"""
|
| 941 |
+
predictions = []
|
| 942 |
+
for h in range(1, steps + 1):
|
| 943 |
+
# Создаем смещенную целевую переменную
|
| 944 |
+
y_h = train_series.shift(-h).dropna()
|
| 945 |
+
X_h = train_series.iloc[:len(y_h)]
|
| 946 |
+
|
| 947 |
+
# Обучаем модель для горизонта h
|
| 948 |
+
model = model_factory()
|
| 949 |
+
model.fit(X_h.values.reshape(-1, 1), y_h.values)
|
| 950 |
+
|
| 951 |
+
# Прогноз для горизонта h
|
| 952 |
+
pred = model.predict(train_series.values[-1:].reshape(1, -1))
|
| 953 |
+
predictions.append(pred[0])
|
| 954 |
+
|
| 955 |
+
return np.array(predictions)
|
src/lab4_functions.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# lab4_functions.py
|
| 2 |
+
"""
|
| 3 |
+
Простейший набор для ЛР №4:
|
| 4 |
+
- Feature engineering (lags, rolling)
|
| 5 |
+
- Обучение Ridge, Lasso, RandomForest, LightGBM (опционально)
|
| 6 |
+
- TimeSeriesSplit wrapper
|
| 7 |
+
"""
|
| 8 |
+
import numpy as np
|
| 9 |
+
import pandas as pd
|
| 10 |
+
from typing import List, Dict
|
| 11 |
+
from sklearn.linear_model import Ridge, Lasso
|
| 12 |
+
from sklearn.ensemble import RandomForestRegressor
|
| 13 |
+
from sklearn.model_selection import TimeSeriesSplit, cross_val_score
|
| 14 |
+
from sklearn.metrics import mean_absolute_error
|
| 15 |
+
import warnings
|
| 16 |
+
warnings.filterwarnings("ignore")
|
| 17 |
+
|
| 18 |
+
try:
|
| 19 |
+
import lightgbm as lgb
|
| 20 |
+
LGB_AVAILABLE = True
|
| 21 |
+
except Exception:
|
| 22 |
+
LGB_AVAILABLE = False
|
| 23 |
+
|
| 24 |
+
def make_lag_features(df: pd.DataFrame, target:str, lags:List[int]=[1,7,30]):
|
| 25 |
+
dfc = df.copy().set_index('timestamp').sort_index()
|
| 26 |
+
for l in lags:
|
| 27 |
+
dfc[f'{target}_lag_{l}'] = dfc[target].shift(l)
|
| 28 |
+
dfc = dfc.dropna().reset_index()
|
| 29 |
+
return dfc
|
| 30 |
+
|
| 31 |
+
def train_baselines(X_train, y_train):
|
| 32 |
+
models = {}
|
| 33 |
+
models['Ridge'] = Ridge().fit(X_train, y_train)
|
| 34 |
+
models['Lasso'] = Lasso().fit(X_train, y_train)
|
| 35 |
+
models['RF'] = RandomForestRegressor(n_estimators=100, random_state=42).fit(X_train, y_train)
|
| 36 |
+
if LGB_AVAILABLE:
|
| 37 |
+
models['LightGBM'] = lgb.LGBMRegressor(n_estimators=100).fit(X_train, y_train)
|
| 38 |
+
return models
|
| 39 |
+
|
| 40 |
+
def cv_score_ts(model, X, y, n_splits=5, scoring='neg_mean_absolute_error'):
|
| 41 |
+
tscv = TimeSeriesSplit(n_splits=n_splits)
|
| 42 |
+
scores = cross_val_score(model, X, y, cv=tscv, scoring=scoring)
|
| 43 |
+
return scores.mean()
|
src/lab5_functions.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# lab5_functions.py
|
| 2 |
+
"""
|
| 3 |
+
Минимальный LSTM example для ЛР №5 (PyTorch).
|
| 4 |
+
Если у вас нет torch — функции выбросят понятную ошибку.
|
| 5 |
+
"""
|
| 6 |
+
import numpy as np
|
| 7 |
+
import pandas as pd
|
| 8 |
+
|
| 9 |
+
try:
|
| 10 |
+
import torch
|
| 11 |
+
import torch.nn as nn
|
| 12 |
+
TORCH_AVAILABLE = True
|
| 13 |
+
except Exception:
|
| 14 |
+
TORCH_AVAILABLE = False
|
| 15 |
+
|
| 16 |
+
if TORCH_AVAILABLE:
|
| 17 |
+
class SimpleLSTM(nn.Module):
|
| 18 |
+
def __init__(self, input_size, hidden_size=64, num_layers=1, out_size=1):
|
| 19 |
+
super().__init__()
|
| 20 |
+
self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)
|
| 21 |
+
self.fc = nn.Linear(hidden_size, out_size)
|
| 22 |
+
def forward(self, x):
|
| 23 |
+
out, _ = self.lstm(x)
|
| 24 |
+
return self.fc(out[:, -1, :])
|
| 25 |
+
|
| 26 |
+
def create_sequences(series, lookback=30, horizon=1):
|
| 27 |
+
X, y = [], []
|
| 28 |
+
for i in range(len(series) - lookback - horizon + 1):
|
| 29 |
+
X.append(series[i:i+lookback])
|
| 30 |
+
y.append(series[i+lookback:i+lookback+horizon])
|
| 31 |
+
return np.array(X), np.array(y)
|
| 32 |
+
|
| 33 |
+
def train_lstm(series, lookback=30, epochs=20, lr=1e-3):
|
| 34 |
+
X, y = create_sequences(series, lookback=lookback, horizon=1)
|
| 35 |
+
X = torch.from_numpy(X).float().unsqueeze(-1) # (N, L, 1)
|
| 36 |
+
y = torch.from_numpy(y).float().squeeze(-1)
|
| 37 |
+
model = SimpleLSTM(input_size=1)
|
| 38 |
+
opt = torch.optim.Adam(model.parameters(), lr=lr)
|
| 39 |
+
loss_fn = nn.MSELoss()
|
| 40 |
+
model.train()
|
| 41 |
+
for ep in range(epochs):
|
| 42 |
+
pred = model(X)
|
| 43 |
+
loss = loss_fn(pred.squeeze(), y)
|
| 44 |
+
opt.zero_grad(); loss.backward(); opt.step()
|
| 45 |
+
return model
|
| 46 |
+
else:
|
| 47 |
+
def train_lstm(*args, **kwargs):
|
| 48 |
+
raise ImportError("PyTorch не установлен. Установите torch, чтобы использовать LSTM.")
|
src/main.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Главная страница приложения - навигация между лабораторными работами
|
| 3 |
+
Этот файл не используется, так как основное приложение находится в streamlit_app.py
|
| 4 |
+
"""
|
| 5 |
+
# Этот файл оставлен для совместимости, но основное приложение находится в streamlit_app.py
|
| 6 |
+
# Для запуска используйте: streamlit run src/streamlit_app.py
|
| 7 |
+
|
src/russia_covid_dataset.csv
ADDED
|
@@ -0,0 +1,821 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Date,DailyNewCases,ActiveCases,DailyNewDeaths
|
| 2 |
+
2020-2-15,,0.0,
|
| 3 |
+
2020-2-16,0.0,0.0,
|
| 4 |
+
2020-2-17,0.0,0.0,
|
| 5 |
+
2020-2-18,0.0,0.0,
|
| 6 |
+
2020-2-19,0.0,0.0,
|
| 7 |
+
2020-2-20,0.0,0.0,0.0
|
| 8 |
+
2020-2-21,0.0,0.0,0.0
|
| 9 |
+
2020-2-22,0.0,0.0,0.0
|
| 10 |
+
2020-2-23,0.0,0.0,0.0
|
| 11 |
+
2020-2-24,0.0,0.0,0.0
|
| 12 |
+
2020-2-25,0.0,0.0,0.0
|
| 13 |
+
2020-2-26,0.0,0.0,0.0
|
| 14 |
+
2020-2-27,0.0,0.0,0.0
|
| 15 |
+
2020-2-28,0.0,0.0,0.0
|
| 16 |
+
2020-2-29,0.0,0.0,0.0
|
| 17 |
+
2020-3-01,0.0,0.0,0.0
|
| 18 |
+
2020-3-02,1.0,1.0,0.0
|
| 19 |
+
2020-3-03,0.0,1.0,0.0
|
| 20 |
+
2020-3-04,0.0,1.0,0.0
|
| 21 |
+
2020-3-05,4.0,5.0,0.0
|
| 22 |
+
2020-3-06,6.0,11.0,0.0
|
| 23 |
+
2020-3-07,1.0,11.0,0.0
|
| 24 |
+
2020-3-08,3.0,14.0,0.0
|
| 25 |
+
2020-3-09,3.0,17.0,0.0
|
| 26 |
+
2020-3-10,0.0,17.0,0.0
|
| 27 |
+
2020-3-11,8.0,25.0,0.0
|
| 28 |
+
2020-3-12,6.0,31.0,0.0
|
| 29 |
+
2020-3-13,11.0,37.0,0.0
|
| 30 |
+
2020-3-14,14.0,51.0,0.0
|
| 31 |
+
2020-3-15,4.0,55.0,0.0
|
| 32 |
+
2020-3-16,30.0,85.0,0.0
|
| 33 |
+
2020-3-17,21.0,106.0,0.0
|
| 34 |
+
2020-3-18,33.0,139.0,0.0
|
| 35 |
+
2020-3-19,52.0,190.0,1.0
|
| 36 |
+
2020-3-20,54.0,240.0,0.0
|
| 37 |
+
2020-3-21,53.0,289.0,0.0
|
| 38 |
+
2020-3-22,61.0,350.0,0.0
|
| 39 |
+
2020-3-23,71.0,420.0,0.0
|
| 40 |
+
2020-3-24,57.0,472.0,0.0
|
| 41 |
+
2020-3-25,163.0,626.0,2.0
|
| 42 |
+
2020-3-26,182.0,799.0,0.0
|
| 43 |
+
2020-3-27,196.0,987.0,1.0
|
| 44 |
+
2020-3-28,228.0,1211.0,0.0
|
| 45 |
+
2020-3-29,270.0,1462.0,4.0
|
| 46 |
+
2020-3-30,302.0,1761.0,1.0
|
| 47 |
+
2020-3-31,501.0,2199.0,8.0
|
| 48 |
+
2020-4-01,440.0,2563.0,7.0
|
| 49 |
+
2020-4-02,771.0,3283.0,6.0
|
| 50 |
+
2020-4-03,601.0,3834.0,4.0
|
| 51 |
+
2020-4-04,582.0,4355.0,9.0
|
| 52 |
+
2020-4-05,658.0,4989.0,2.0
|
| 53 |
+
2020-4-06,954.0,5890.0,2.0
|
| 54 |
+
2020-4-07,1154.0,6945.0,11.0
|
| 55 |
+
2020-4-08,1175.0,8029.0,5.0
|
| 56 |
+
2020-4-09,1459.0,9357.0,13.0
|
| 57 |
+
2020-4-10,1786.0,11028.0,18.0
|
| 58 |
+
2020-4-11,1667.0,12433.0,12.0
|
| 59 |
+
2020-4-12,2186.0,14349.0,24.0
|
| 60 |
+
2020-4-13,2558.0,16710.0,18.0
|
| 61 |
+
2020-4-14,2774.0,19238.0,22.0
|
| 62 |
+
2020-4-15,3388.0,22306.0,28.0
|
| 63 |
+
2020-4-16,3448.0,25402.0,34.0
|
| 64 |
+
2020-4-17,4070.0,29145.0,41.0
|
| 65 |
+
2020-4-18,4785.0,33423.0,40.0
|
| 66 |
+
2020-4-19,6060.0,39201.0,48.0
|
| 67 |
+
2020-4-20,4268.0,43270.0,44.0
|
| 68 |
+
2020-4-21,5642.0,48434.0,51.0
|
| 69 |
+
2020-4-22,5236.0,53066.0,57.0
|
| 70 |
+
2020-4-23,4774.0,57327.0,42.0
|
| 71 |
+
2020-4-24,5849.0,62439.0,60.0
|
| 72 |
+
2020-4-25,5966.0,67657.0,66.0
|
| 73 |
+
2020-4-26,6361.0,73435.0,66.0
|
| 74 |
+
2020-4-27,6198.0,79007.0,47.0
|
| 75 |
+
2020-4-28,6411.0,84235.0,73.0
|
| 76 |
+
2020-4-29,5841.0,88141.0,105.0
|
| 77 |
+
2020-4-30,7099.0,93806.0,101.0
|
| 78 |
+
2020-5-01,7933.0,100042.0,96.0
|
| 79 |
+
2020-5-02,9623.0,107819.0,53.0
|
| 80 |
+
2020-5-03,10633.0,116768.0,58.0
|
| 81 |
+
2020-5-04,10581.0,125817.0,76.0
|
| 82 |
+
2020-5-05,10102.0,134054.0,95.0
|
| 83 |
+
2020-5-06,10559.0,143065.0,86.0
|
| 84 |
+
2020-5-07,11231.0,151732.0,88.0
|
| 85 |
+
2020-5-08,10699.0,159528.0,98.0
|
| 86 |
+
2020-5-09,10817.0,164933.0,104.0
|
| 87 |
+
2020-5-10,11012.0,173467.0,88.0
|
| 88 |
+
2020-5-11,11656.0,179534.0,94.0
|
| 89 |
+
2020-5-12,10899.0,186615.0,107.0
|
| 90 |
+
2020-5-13,10028.0,192056.0,96.0
|
| 91 |
+
2020-5-14,9974.0,196410.0,93.0
|
| 92 |
+
2020-5-15,10598.0,202199.0,113.0
|
| 93 |
+
2020-5-16,9200.0,206340.0,119.0
|
| 94 |
+
2020-5-17,9709.0,211748.0,94.0
|
| 95 |
+
2020-5-18,8926.0,217747.0,91.0
|
| 96 |
+
2020-5-19,9263.0,220974.0,115.0
|
| 97 |
+
2020-5-20,8764.0,220341.0,135.0
|
| 98 |
+
2020-5-21,8849.0,221774.0,127.0
|
| 99 |
+
2020-5-22,8894.0,223374.0,150.0
|
| 100 |
+
2020-5-23,9434.0,224558.0,139.0
|
| 101 |
+
2020-5-24,8599.0,227641.0,153.0
|
| 102 |
+
2020-5-25,8946.0,230996.0,92.0
|
| 103 |
+
2020-5-26,8915.0,227406.0,174.0
|
| 104 |
+
2020-5-27,8338.0,224504.0,161.0
|
| 105 |
+
2020-5-28,8371.0,223916.0,174.0
|
| 106 |
+
2020-5-29,8572.0,223992.0,232.0
|
| 107 |
+
2020-5-30,8952.0,224551.0,181.0
|
| 108 |
+
2020-5-31,9268.0,229267.0,138.0
|
| 109 |
+
2020-6-01,9035.0,234146.0,162.0
|
| 110 |
+
2020-6-02,8863.0,231719.0,182.0
|
| 111 |
+
2020-6-03,8536.0,231105.0,178.0
|
| 112 |
+
2020-6-04,8831.0,231101.0,169.0
|
| 113 |
+
2020-6-05,8726.0,231626.0,144.0
|
| 114 |
+
2020-6-06,8855.0,231576.0,197.0
|
| 115 |
+
2020-6-07,8984.0,235083.0,134.0
|
| 116 |
+
2020-6-08,8985.0,239999.0,112.0
|
| 117 |
+
2020-6-09,8595.0,236714.0,171.0
|
| 118 |
+
2020-6-10,8404.0,234516.0,216.0
|
| 119 |
+
2020-6-11,8779.0,234754.0,174.0
|
| 120 |
+
2020-6-12,8987.0,235338.0,183.0
|
| 121 |
+
2020-6-13,8706.0,238659.0,114.0
|
| 122 |
+
2020-6-14,8835.0,241966.0,119.0
|
| 123 |
+
2020-6-15,8246.0,245580.0,143.0
|
| 124 |
+
2020-6-16,8248.0,243868.0,193.0
|
| 125 |
+
2020-6-17,7843.0,241481.0,194.0
|
| 126 |
+
2020-6-18,7790.0,239468.0,182.0
|
| 127 |
+
2020-6-19,7972.0,236816.0,181.0
|
| 128 |
+
2020-6-20,7889.0,234358.0,161.0
|
| 129 |
+
2020-6-21,7728.0,236858.0,109.0
|
| 130 |
+
2020-6-22,7600.0,239658.0,95.0
|
| 131 |
+
2020-6-23,7425.0,234917.0,153.0
|
| 132 |
+
2020-6-24,7176.0,229546.0,154.0
|
| 133 |
+
2020-6-25,7113.0,230225.0,92.0
|
| 134 |
+
2020-6-26,6800.0,227861.0,176.0
|
| 135 |
+
2020-6-27,6852.0,225325.0,188.0
|
| 136 |
+
2020-6-28,6791.0,226277.0,104.0
|
| 137 |
+
2020-6-29,6719.0,228560.0,93.0
|
| 138 |
+
2020-6-30,6693.0,225879.0,154.0
|
| 139 |
+
2020-7-01,6556.0,221938.0,216.0
|
| 140 |
+
2020-7-02,6760.0,222504.0,147.0
|
| 141 |
+
2020-7-03,6718.0,220131.0,176.0
|
| 142 |
+
2020-7-04,6632.0,217609.0,168.0
|
| 143 |
+
2020-7-05,6736.0,220340.0,134.0
|
| 144 |
+
2020-7-06,6611.0,223237.0,135.0
|
| 145 |
+
2020-7-07,6368.0,219856.0,198.0
|
| 146 |
+
2020-7-08,6562.0,217614.0,173.0
|
| 147 |
+
2020-7-09,6509.0,215142.0,176.0
|
| 148 |
+
2020-7-10,6635.0,213851.0,174.0
|
| 149 |
+
2020-7-11,6611.0,211896.0,188.0
|
| 150 |
+
2020-7-12,6615.0,214766.0,130.0
|
| 151 |
+
2020-7-13,6537.0,218239.0,104.0
|
| 152 |
+
2020-7-14,6248.0,215508.0,175.0
|
| 153 |
+
2020-7-15,6422.0,211350.0,156.0
|
| 154 |
+
2020-7-16,6428.0,209168.0,167.0
|
| 155 |
+
2020-7-17,6406.0,207707.0,186.0
|
| 156 |
+
2020-7-18,6234.0,206327.0,124.0
|
| 157 |
+
2020-7-19,6109.0,208860.0,95.0
|
| 158 |
+
2020-7-20,5940.0,211457.0,85.0
|
| 159 |
+
2020-7-21,5842.0,208364.0,153.0
|
| 160 |
+
2020-7-22,5862.0,204392.0,165.0
|
| 161 |
+
2020-7-23,5848.0,201816.0,147.0
|
| 162 |
+
2020-7-24,5811.0,199029.0,154.0
|
| 163 |
+
2020-7-25,5871.0,196388.0,146.0
|
| 164 |
+
2020-7-26,5765.0,198966.0,77.0
|
| 165 |
+
2020-7-27,5635.0,201437.0,85.0
|
| 166 |
+
2020-7-28,5395.0,197794.0,150.0
|
| 167 |
+
2020-7-29,5475.0,194984.0,169.0
|
| 168 |
+
2020-7-30,5509.0,191042.0,129.0
|
| 169 |
+
2020-7-31,5482.0,187608.0,161.0
|
| 170 |
+
2020-8-01,5462.0,184861.0,95.0
|
| 171 |
+
2020-8-02,5427.0,186569.0,70.0
|
| 172 |
+
2020-8-03,5394.0,188464.0,79.0
|
| 173 |
+
2020-8-04,5159.0,185601.0,144.0
|
| 174 |
+
2020-8-05,5204.0,183111.0,139.0
|
| 175 |
+
2020-8-06,5267.0,180931.0,116.0
|
| 176 |
+
2020-8-07,5241.0,178818.0,119.0
|
| 177 |
+
2020-8-08,5212.0,177286.0,129.0
|
| 178 |
+
2020-8-09,5189.0,179183.0,77.0
|
| 179 |
+
2020-8-10,5118.0,180972.0,70.0
|
| 180 |
+
2020-8-11,4945.0,179293.0,130.0
|
| 181 |
+
2020-8-12,5102.0,177143.0,129.0
|
| 182 |
+
2020-8-13,5057.0,175978.0,124.0
|
| 183 |
+
2020-8-14,5065.0,174361.0,114.0
|
| 184 |
+
2020-8-15,5061.0,172856.0,119.0
|
| 185 |
+
2020-8-16,4969.0,174200.0,68.0
|
| 186 |
+
2020-8-17,4892.0,175904.0,55.0
|
| 187 |
+
2020-8-18,4748.0,173993.0,132.0
|
| 188 |
+
2020-8-19,4828.0,171909.0,117.0
|
| 189 |
+
2020-8-20,4785.0,170494.0,110.0
|
| 190 |
+
2020-8-21,4870.0,169457.0,90.0
|
| 191 |
+
2020-8-22,4921.0,168110.0,121.0
|
| 192 |
+
2020-8-23,4852.0,169727.0,73.0
|
| 193 |
+
2020-8-24,4744.0,171950.0,65.0
|
| 194 |
+
2020-8-25,4696.0,169874.0,120.0
|
| 195 |
+
2020-8-26,4676.0,168032.0,115.0
|
| 196 |
+
2020-8-27,4711.0,166211.0,121.0
|
| 197 |
+
2020-8-28,4829.0,165025.0,110.0
|
| 198 |
+
2020-8-29,4941.0,163938.0,111.0
|
| 199 |
+
2020-8-30,4980.0,166251.0,68.0
|
| 200 |
+
2020-8-31,4993.0,168756.0,83.0
|
| 201 |
+
2020-9-01,4729.0,167044.0,123.0
|
| 202 |
+
2020-9-02,4952.0,166417.0,115.0
|
| 203 |
+
2020-9-03,4995.0,165532.0,114.0
|
| 204 |
+
2020-9-04,5110.0,164709.0,121.0
|
| 205 |
+
2020-9-05,5205.0,164425.0,110.0
|
| 206 |
+
2020-9-06,5195.0,166736.0,61.0
|
| 207 |
+
2020-9-07,5185.0,169542.0,51.0
|
| 208 |
+
2020-9-08,5099.0,167747.0,122.0
|
| 209 |
+
2020-9-09,5218.0,166414.0,142.0
|
| 210 |
+
2020-9-10,5363.0,165734.0,128.0
|
| 211 |
+
2020-9-11,5504.0,165402.0,102.0
|
| 212 |
+
2020-9-12,5488.0,165343.0,119.0
|
| 213 |
+
2020-9-13,5449.0,168008.0,94.0
|
| 214 |
+
2020-9-14,5509.0,170985.0,57.0
|
| 215 |
+
2020-9-15,5529.0,170759.0,150.0
|
| 216 |
+
2020-9-16,5670.0,170488.0,132.0
|
| 217 |
+
2020-9-17,5762.0,170352.0,144.0
|
| 218 |
+
2020-9-18,5905.0,170784.0,134.0
|
| 219 |
+
2020-9-19,6065.0,171450.0,144.0
|
| 220 |
+
2020-9-20,6148.0,174624.0,79.0
|
| 221 |
+
2020-9-21,6196.0,178133.0,71.0
|
| 222 |
+
2020-9-22,6215.0,178212.0,160.0
|
| 223 |
+
2020-9-23,6431.0,178743.0,150.0
|
| 224 |
+
2020-9-24,6595.0,179059.0,149.0
|
| 225 |
+
2020-9-25,7212.0,181846.0,108.0
|
| 226 |
+
2020-9-26,7523.0,183196.0,169.0
|
| 227 |
+
2020-9-27,7867.0,187896.0,99.0
|
| 228 |
+
2020-9-28,8135.0,193268.0,61.0
|
| 229 |
+
2020-9-29,8232.0,194861.0,160.0
|
| 230 |
+
2020-9-30,8481.0,197307.0,177.0
|
| 231 |
+
2020-10-01,8945.0,200098.0,169.0
|
| 232 |
+
2020-10-02,9412.0,203270.0,186.0
|
| 233 |
+
2020-10-03,9859.0,207392.0,174.0
|
| 234 |
+
2020-10-04,10499.0,214500.0,107.0
|
| 235 |
+
2020-10-05,10888.0,222090.0,117.0
|
| 236 |
+
2020-10-06,11615.0,227265.0,188.0
|
| 237 |
+
2020-10-07,11115.0,231479.0,202.0
|
| 238 |
+
2020-10-08,11493.0,235727.0,191.0
|
| 239 |
+
2020-10-09,12126.0,240560.0,201.0
|
| 240 |
+
2020-10-10,12846.0,246428.0,197.0
|
| 241 |
+
2020-10-11,13634.0,255679.0,143.0
|
| 242 |
+
2020-10-12,13592.0,265353.0,125.0
|
| 243 |
+
2020-10-13,13868.0,271427.0,244.0
|
| 244 |
+
2020-10-14,14231.0,277499.0,239.0
|
| 245 |
+
2020-10-15,13754.0,282575.0,286.0
|
| 246 |
+
2020-10-16,15150.0,289008.0,232.0
|
| 247 |
+
2020-10-17,14922.0,295034.0,279.0
|
| 248 |
+
2020-10-18,15099.0,304571.0,185.0
|
| 249 |
+
2020-10-19,15982.0,315046.0,179.0
|
| 250 |
+
2020-10-20,16319.0,321392.0,269.0
|
| 251 |
+
2020-10-21,15700.0,325823.0,317.0
|
| 252 |
+
2020-10-22,15971.0,330076.0,290.0
|
| 253 |
+
2020-10-23,17340.0,335870.0,283.0
|
| 254 |
+
2020-10-24,16521.0,340528.0,296.0
|
| 255 |
+
2020-10-25,16710.0,349305.0,229.0
|
| 256 |
+
2020-10-26,17347.0,358859.0,219.0
|
| 257 |
+
2020-10-27,16550.0,362245.0,320.0
|
| 258 |
+
2020-10-28,16202.0,365740.0,346.0
|
| 259 |
+
2020-10-29,17717.0,368351.0,366.0
|
| 260 |
+
2020-10-30,18283.0,371760.0,355.0
|
| 261 |
+
2020-10-31,18140.0,374712.0,334.0
|
| 262 |
+
2020-11-01,18665.0,382873.0,245.0
|
| 263 |
+
2020-11-02,18257.0,390532.0,238.0
|
| 264 |
+
2020-11-03,18648.0,393494.0,355.0
|
| 265 |
+
2020-11-04,19768.0,397306.0,389.0
|
| 266 |
+
2020-11-05,19404.0,404180.0,292.0
|
| 267 |
+
2020-11-06,20582.0,407429.0,378.0
|
| 268 |
+
2020-11-07,20396.0,410658.0,364.0
|
| 269 |
+
2020-11-08,20498.0,419378.0,286.0
|
| 270 |
+
2020-11-09,21798.0,430198.0,256.0
|
| 271 |
+
2020-11-10,20977.0,435207.0,368.0
|
| 272 |
+
2020-11-11,19851.0,436010.0,432.0
|
| 273 |
+
2020-11-12,21608.0,438368.0,439.0
|
| 274 |
+
2020-11-13,21983.0,441205.0,411.0
|
| 275 |
+
2020-11-14,22702.0,444890.0,391.0
|
| 276 |
+
2020-11-15,22572.0,452654.0,352.0
|
| 277 |
+
2020-11-16,22778.0,461265.0,303.0
|
| 278 |
+
2020-11-17,22410.0,461178.0,442.0
|
| 279 |
+
2020-11-18,20985.0,456528.0,456.0
|
| 280 |
+
2020-11-19,23610.0,454102.0,463.0
|
| 281 |
+
2020-11-20,24318.0,453201.0,461.0
|
| 282 |
+
2020-11-21,24822.0,451535.0,467.0
|
| 283 |
+
2020-11-22,24581.0,457707.0,401.0
|
| 284 |
+
2020-11-23,25173.0,466517.0,361.0
|
| 285 |
+
2020-11-24,24326.0,467126.0,491.0
|
| 286 |
+
2020-11-25,23675.0,464546.0,507.0
|
| 287 |
+
2020-11-26,25487.0,464436.0,524.0
|
| 288 |
+
2020-11-27,27543.0,464801.0,496.0
|
| 289 |
+
2020-11-28,27100.0,464095.0,510.0
|
| 290 |
+
2020-11-29,26683.0,468332.0,459.0
|
| 291 |
+
2020-11-30,26338.0,477055.0,368.0
|
| 292 |
+
2020-12-01,26402.0,478125.0,569.0
|
| 293 |
+
2020-12-02,25345.0,475999.0,589.0
|
| 294 |
+
2020-12-03,28145.0,474088.0,554.0
|
| 295 |
+
2020-12-04,27403.0,472021.0,569.0
|
| 296 |
+
2020-12-05,28782.0,472651.0,508.0
|
| 297 |
+
2020-12-06,29039.0,479891.0,457.0
|
| 298 |
+
2020-12-07,28142.0,488727.0,456.0
|
| 299 |
+
2020-12-08,26097.0,489324.0,562.0
|
| 300 |
+
2020-12-09,26190.0,488689.0,559.0
|
| 301 |
+
2020-12-10,27927.0,490177.0,562.0
|
| 302 |
+
2020-12-11,28585.0,491978.0,613.0
|
| 303 |
+
2020-12-12,28137.0,493437.0,560.0
|
| 304 |
+
2020-12-13,28080.0,500752.0,488.0
|
| 305 |
+
2020-12-14,27328.0,509068.0,450.0
|
| 306 |
+
2020-12-15,26689.0,510367.0,577.0
|
| 307 |
+
2020-12-16,26509.0,509790.0,596.0
|
| 308 |
+
2020-12-17,28214.0,510977.0,587.0
|
| 309 |
+
2020-12-18,28552.0,512825.0,611.0
|
| 310 |
+
2020-12-19,28209.0,514340.0,585.0
|
| 311 |
+
2020-12-20,28948.0,521862.0,511.0
|
| 312 |
+
2020-12-21,29350.0,531014.0,493.0
|
| 313 |
+
2020-12-22,28776.0,535071.0,561.0
|
| 314 |
+
2020-12-23,27250.0,537325.0,549.0
|
| 315 |
+
2020-12-24,29935.0,539735.0,635.0
|
| 316 |
+
2020-12-25,29018.0,540793.0,563.0
|
| 317 |
+
2020-12-26,29258.0,541299.0,567.0
|
| 318 |
+
2020-12-27,28284.0,544641.0,552.0
|
| 319 |
+
2020-12-28,27787.0,551461.0,487.0
|
| 320 |
+
2020-12-29,27002.0,553027.0,562.0
|
| 321 |
+
2020-12-30,26513.0,549706.0,599.0
|
| 322 |
+
2020-12-31,27747.0,547938.0,593.0
|
| 323 |
+
2021-1-01,27039.0,548643.0,536.0
|
| 324 |
+
2021-1-02,26301.0,555600.0,447.0
|
| 325 |
+
2021-1-03,24150.0,559399.0,504.0
|
| 326 |
+
2021-1-04,23351.0,561114.0,482.0
|
| 327 |
+
2021-1-05,24246.0,562210.0,518.0
|
| 328 |
+
2021-1-06,24217.0,562927.0,445.0
|
| 329 |
+
2021-1-07,23541.0,562233.0,506.0
|
| 330 |
+
2021-1-08,23652.0,563754.0,454.0
|
| 331 |
+
2021-1-09,23309.0,562913.0,470.0
|
| 332 |
+
2021-1-10,22851.0,561228.0,456.0
|
| 333 |
+
2021-1-11,23315.0,562321.0,436.0
|
| 334 |
+
2021-1-12,22934.0,559969.0,531.0
|
| 335 |
+
2021-1-13,22850.0,553595.0,566.0
|
| 336 |
+
2021-1-14,24763.0,549832.0,570.0
|
| 337 |
+
2021-1-15,24715.0,546356.0,555.0
|
| 338 |
+
2021-1-16,24092.0,542547.0,590.0
|
| 339 |
+
2021-1-17,23586.0,542212.0,481.0
|
| 340 |
+
2021-1-18,22857.0,546265.0,471.0
|
| 341 |
+
2021-1-19,21734.0,544151.0,586.0
|
| 342 |
+
2021-1-20,21152.0,539416.0,597.0
|
| 343 |
+
2021-1-21,21887.0,533789.0,612.0
|
| 344 |
+
2021-1-22,21513.0,527404.0,580.0
|
| 345 |
+
2021-1-23,20921.0,519987.0,559.0
|
| 346 |
+
2021-1-24,21127.0,518178.0,491.0
|
| 347 |
+
2021-1-25,19290.0,518009.0,456.0
|
| 348 |
+
2021-1-26,18241.0,511888.0,564.0
|
| 349 |
+
2021-1-27,17741.0,501113.0,594.0
|
| 350 |
+
2021-1-28,19138.0,492901.0,575.0
|
| 351 |
+
2021-1-29,19238.0,485401.0,534.0
|
| 352 |
+
2021-1-30,19032.0,479419.0,512.0
|
| 353 |
+
2021-1-31,18359.0,477253.0,485.0
|
| 354 |
+
2021-2-01,17648.0,476295.0,437.0
|
| 355 |
+
2021-2-02,16643.0,470027.0,539.0
|
| 356 |
+
2021-2-03,16474.0,461153.0,526.0
|
| 357 |
+
2021-2-04,16714.0,452800.0,521.0
|
| 358 |
+
2021-2-05,16688.0,445379.0,527.0
|
| 359 |
+
2021-2-06,16627.0,438678.0,497.0
|
| 360 |
+
2021-2-07,16048.0,434410.0,432.0
|
| 361 |
+
2021-2-08,15916.0,434038.0,407.0
|
| 362 |
+
2021-2-09,15019.0,426732.0,530.0
|
| 363 |
+
2021-2-10,14494.0,418115.0,536.0
|
| 364 |
+
2021-2-11,15038.0,410639.0,553.0
|
| 365 |
+
2021-2-12,15089.0,404501.0,507.0
|
| 366 |
+
2021-2-13,14861.0,400095.0,502.0
|
| 367 |
+
2021-2-14,14185.0,398656.0,430.0
|
| 368 |
+
2021-2-15,14207.0,398534.0,394.0
|
| 369 |
+
2021-2-16,13233.0,393681.0,459.0
|
| 370 |
+
2021-2-17,12828.0,388123.0,467.0
|
| 371 |
+
2021-2-18,13447.0,382360.0,480.0
|
| 372 |
+
2021-2-19,13433.0,376686.0,470.0
|
| 373 |
+
2021-2-20,12953.0,371675.0,480.0
|
| 374 |
+
2021-2-21,12742.0,367988.0,417.0
|
| 375 |
+
2021-2-22,12604.0,367312.0,337.0
|
| 376 |
+
2021-2-23,11823.0,365762.0,417.0
|
| 377 |
+
2021-2-24,11749.0,364910.0,383.0
|
| 378 |
+
2021-2-25,11198.0,359560.0,446.0
|
| 379 |
+
2021-2-26,11086.0,354496.0,428.0
|
| 380 |
+
2021-2-27,11534.0,349571.0,439.0
|
| 381 |
+
2021-2-28,11359.0,348160.0,379.0
|
| 382 |
+
2021-3-01,11571.0,348121.0,333.0
|
| 383 |
+
2021-3-02,10565.0,343279.0,441.0
|
| 384 |
+
2021-3-03,10535.0,337668.0,452.0
|
| 385 |
+
2021-3-04,11385.0,332455.0,475.0
|
| 386 |
+
2021-3-05,11024.0,327553.0,462.0
|
| 387 |
+
2021-3-06,11022.0,323107.0,441.0
|
| 388 |
+
2021-3-07,10595.0,321758.0,368.0
|
| 389 |
+
2021-3-08,10253.0,321310.0,379.0
|
| 390 |
+
2021-3-09,9445.0,320488.0,336.0
|
| 391 |
+
2021-3-10,9079.0,315751.0,466.0
|
| 392 |
+
2021-3-11,9270.0,310556.0,459.0
|
| 393 |
+
2021-3-12,9794.0,306368.0,486.0
|
| 394 |
+
2021-3-13,9908.0,302933.0,475.0
|
| 395 |
+
2021-3-14,10083.0,303209.0,395.0
|
| 396 |
+
2021-3-15,9437.0,303975.0,404.0
|
| 397 |
+
2021-3-16,9393.0,302281.0,443.0
|
| 398 |
+
2021-3-17,8998.0,300097.0,427.0
|
| 399 |
+
2021-3-18,9803.0,297379.0,460.0
|
| 400 |
+
2021-3-19,9699.0,294298.0,443.0
|
| 401 |
+
2021-3-20,9632.0,292259.0,392.0
|
| 402 |
+
2021-3-21,9299.0,292444.0,371.0
|
| 403 |
+
2021-3-22,9284.0,293577.0,361.0
|
| 404 |
+
2021-3-23,8457.0,290747.0,427.0
|
| 405 |
+
2021-3-24,8861.0,288852.0,401.0
|
| 406 |
+
2021-3-25,9221.0,286799.0,393.0
|
| 407 |
+
2021-3-26,9167.0,284681.0,405.0
|
| 408 |
+
2021-3-27,8885.0,282842.0,387.0
|
| 409 |
+
2021-3-28,9088.0,282964.0,336.0
|
| 410 |
+
2021-3-29,8711.0,284102.0,293.0
|
| 411 |
+
2021-3-30,8277.0,282382.0,409.0
|
| 412 |
+
2021-3-31,8275.0,280073.0,408.0
|
| 413 |
+
2021-4-01,9169.0,278612.0,383.0
|
| 414 |
+
2021-4-02,8792.0,277172.0,400.0
|
| 415 |
+
2021-4-03,9021.0,276191.0,384.0
|
| 416 |
+
2021-4-04,8817.0,276439.0,357.0
|
| 417 |
+
2021-4-05,8646.0,277690.0,343.0
|
| 418 |
+
2021-4-06,8328.0,276727.0,389.0
|
| 419 |
+
2021-4-07,8294.0,275202.0,374.0
|
| 420 |
+
2021-4-08,8672.0,273951.0,365.0
|
| 421 |
+
2021-4-09,9150.0,273037.0,402.0
|
| 422 |
+
2021-4-10,8704.0,271760.0,402.0
|
| 423 |
+
2021-4-11,8702.0,272895.0,337.0
|
| 424 |
+
2021-4-12,8320.0,274282.0,277.0
|
| 425 |
+
2021-4-13,8173.0,272506.0,338.0
|
| 426 |
+
2021-4-14,8326.0,270986.0,399.0
|
| 427 |
+
2021-4-15,8944.0,269307.0,398.0
|
| 428 |
+
2021-4-16,8995.0,268796.0,397.0
|
| 429 |
+
2021-4-17,9321.0,268887.0,398.0
|
| 430 |
+
2021-4-18,8632.0,269739.0,389.0
|
| 431 |
+
2021-4-19,8589.0,271164.0,346.0
|
| 432 |
+
2021-4-20,8164.0,269318.0,379.0
|
| 433 |
+
2021-4-21,8271.0,267546.0,399.0
|
| 434 |
+
2021-4-22,8996.0,267211.0,397.0
|
| 435 |
+
2021-4-23,8840.0,266246.0,398.0
|
| 436 |
+
2021-4-24,8828.0,265421.0,399.0
|
| 437 |
+
2021-4-25,8780.0,266329.0,332.0
|
| 438 |
+
2021-4-26,8803.0,268145.0,356.0
|
| 439 |
+
2021-4-27,8053.0,267767.0,392.0
|
| 440 |
+
2021-4-28,7848.0,266808.0,387.0
|
| 441 |
+
2021-4-29,9284.0,267286.0,364.0
|
| 442 |
+
2021-4-30,8731.0,267214.0,397.0
|
| 443 |
+
2021-5-01,9270.0,267455.0,392.0
|
| 444 |
+
2021-5-02,8697.0,268471.0,342.0
|
| 445 |
+
2021-5-03,8489.0,270257.0,336.0
|
| 446 |
+
2021-5-04,7770.0,270935.0,337.0
|
| 447 |
+
2021-5-05,7975.0,271044.0,360.0
|
| 448 |
+
2021-5-06,7639.0,270544.0,351.0
|
| 449 |
+
2021-5-07,8386.0,270532.0,376.0
|
| 450 |
+
2021-5-08,8329.0,270236.0,370.0
|
| 451 |
+
2021-5-09,8419.0,270804.0,334.0
|
| 452 |
+
2021-5-10,8465.0,272174.0,321.0
|
| 453 |
+
2021-5-11,8115.0,272951.0,329.0
|
| 454 |
+
2021-5-12,8217.0,272199.0,355.0
|
| 455 |
+
2021-5-13,8380.0,270838.0,392.0
|
| 456 |
+
2021-5-14,9462.0,270151.0,393.0
|
| 457 |
+
2021-5-15,8790.0,268711.0,364.0
|
| 458 |
+
2021-5-16,8554.0,268301.0,391.0
|
| 459 |
+
2021-5-17,9328.0,270108.0,340.0
|
| 460 |
+
2021-5-18,8183.0,268955.0,364.0
|
| 461 |
+
2021-5-19,7920.0,266924.0,390.0
|
| 462 |
+
2021-5-20,9232.0,265777.0,396.0
|
| 463 |
+
2021-5-21,8937.0,264986.0,378.0
|
| 464 |
+
2021-5-22,8709.0,263964.0,386.0
|
| 465 |
+
2021-5-23,8951.0,265261.0,357.0
|
| 466 |
+
2021-5-24,8406.0,266898.0,319.0
|
| 467 |
+
2021-5-25,7884.0,265646.0,393.0
|
| 468 |
+
2021-5-26,8373.0,264478.0,406.0
|
| 469 |
+
2021-5-27,9039.0,263356.0,402.0
|
| 470 |
+
2021-5-28,9252.0,262819.0,404.0
|
| 471 |
+
2021-5-29,9289.0,262457.0,401.0
|
| 472 |
+
2021-5-30,9694.0,264410.0,355.0
|
| 473 |
+
2021-5-31,8475.0,265831.0,339.0
|
| 474 |
+
2021-6-01,9500.0,265965.0,372.0
|
| 475 |
+
2021-6-02,8832.0,265383.0,394.0
|
| 476 |
+
2021-6-03,8933.0,264540.0,393.0
|
| 477 |
+
2021-6-04,8947.0,264580.0,377.0
|
| 478 |
+
2021-6-05,9145.0,264761.0,399.0
|
| 479 |
+
2021-6-06,9163.0,266204.0,351.0
|
| 480 |
+
2021-6-07,9429.0,268547.0,330.0
|
| 481 |
+
2021-6-08,9977.0,269262.0,379.0
|
| 482 |
+
2021-6-09,10407.0,269456.0,399.0
|
| 483 |
+
2021-6-10,11699.0,270676.0,383.0
|
| 484 |
+
2021-6-11,12505.0,272597.0,396.0
|
| 485 |
+
2021-6-12,13510.0,275722.0,399.0
|
| 486 |
+
2021-6-13,14723.0,280922.0,357.0
|
| 487 |
+
2021-6-14,13721.0,285960.0,371.0
|
| 488 |
+
2021-6-15,14185.0,291169.0,379.0
|
| 489 |
+
2021-6-16,13397.0,293914.0,396.0
|
| 490 |
+
2021-6-17,14057.0,296350.0,416.0
|
| 491 |
+
2021-6-18,17262.0,302205.0,453.0
|
| 492 |
+
2021-6-19,17906.0,308961.0,466.0
|
| 493 |
+
2021-6-20,17611.0,317493.0,450.0
|
| 494 |
+
2021-6-21,17378.0,326070.0,440.0
|
| 495 |
+
2021-6-22,16715.0,331122.0,546.0
|
| 496 |
+
2021-6-23,17594.0,335508.0,548.0
|
| 497 |
+
2021-6-24,20182.0,341617.0,568.0
|
| 498 |
+
2021-6-25,20393.0,347385.0,601.0
|
| 499 |
+
2021-6-26,21665.0,354084.0,619.0
|
| 500 |
+
2021-6-27,20538.0,361295.0,599.0
|
| 501 |
+
2021-6-28,21650.0,369708.0,611.0
|
| 502 |
+
2021-6-29,20616.0,374975.0,652.0
|
| 503 |
+
2021-6-30,21042.0,378992.0,669.0
|
| 504 |
+
2021-7-01,23543.0,384935.0,672.0
|
| 505 |
+
2021-7-02,23218.0,389277.0,679.0
|
| 506 |
+
2021-7-03,24439.0,395120.0,697.0
|
| 507 |
+
2021-7-04,25142.0,404115.0,663.0
|
| 508 |
+
2021-7-05,24353.0,413274.0,654.0
|
| 509 |
+
2021-7-06,23378.0,417504.0,737.0
|
| 510 |
+
2021-7-07,23962.0,420674.0,725.0
|
| 511 |
+
2021-7-08,24818.0,423422.0,734.0
|
| 512 |
+
2021-7-09,25766.0,426630.0,726.0
|
| 513 |
+
2021-7-10,25082.0,433210.0,752.0
|
| 514 |
+
2021-7-11,25033.0,440112.0,749.0
|
| 515 |
+
2021-7-12,25140.0,448113.0,710.0
|
| 516 |
+
2021-7-13,24702.0,452469.0,780.0
|
| 517 |
+
2021-7-14,23827.0,454241.0,786.0
|
| 518 |
+
2021-7-15,25293.0,457250.0,791.0
|
| 519 |
+
2021-7-16,25704.0,460223.0,799.0
|
| 520 |
+
2021-7-17,25116.0,463115.0,787.0
|
| 521 |
+
2021-7-18,25018.0,468483.0,764.0
|
| 522 |
+
2021-7-19,24633.0,473633.0,719.0
|
| 523 |
+
2021-7-20,23770.0,474401.0,784.0
|
| 524 |
+
2021-7-21,23704.0,474738.0,783.0
|
| 525 |
+
2021-7-22,24471.0,475753.0,796.0
|
| 526 |
+
2021-7-23,23811.0,476222.0,795.0
|
| 527 |
+
2021-7-24,23947.0,477418.0,799.0
|
| 528 |
+
2021-7-25,24072.0,482033.0,779.0
|
| 529 |
+
2021-7-26,23239.0,488345.0,727.0
|
| 530 |
+
2021-7-27,23032.0,490482.0,779.0
|
| 531 |
+
2021-7-28,22420.0,491525.0,798.0
|
| 532 |
+
2021-7-29,23270.0,493162.0,799.0
|
| 533 |
+
2021-7-30,23564.0,495447.0,794.0
|
| 534 |
+
2021-7-31,23807.0,498691.0,792.0
|
| 535 |
+
2021-8-01,22804.0,503435.0,789.0
|
| 536 |
+
2021-8-02,23508.0,511265.0,785.0
|
| 537 |
+
2021-8-03,22010.0,513524.0,788.0
|
| 538 |
+
2021-8-04,22589.0,515227.0,790.0
|
| 539 |
+
2021-8-05,23120.0,517183.0,794.0
|
| 540 |
+
2021-8-06,22660.0,518910.0,792.0
|
| 541 |
+
2021-8-07,22320.0,520952.0,793.0
|
| 542 |
+
2021-8-08,22866.0,527362.0,787.0
|
| 543 |
+
2021-8-09,22160.0,534279.0,769.0
|
| 544 |
+
2021-8-10,21378.0,536136.0,792.0
|
| 545 |
+
2021-8-11,21571.0,536841.0,799.0
|
| 546 |
+
2021-8-12,21932.0,537770.0,808.0
|
| 547 |
+
2021-8-13,22277.0,539864.0,815.0
|
| 548 |
+
2021-8-14,22144.0,541639.0,819.0
|
| 549 |
+
2021-8-15,21624.0,546021.0,816.0
|
| 550 |
+
2021-8-16,20765.0,550379.0,806.0
|
| 551 |
+
2021-8-17,20958.0,552125.0,805.0
|
| 552 |
+
2021-8-18,20914.0,551527.0,799.0
|
| 553 |
+
2021-8-19,21058.0,547777.0,791.0
|
| 554 |
+
2021-8-20,20992.0,547633.0,785.0
|
| 555 |
+
2021-8-21,21000.0,547189.0,797.0
|
| 556 |
+
2021-8-22,20564.0,551577.0,762.0
|
| 557 |
+
2021-8-23,19454.0,554854.0,776.0
|
| 558 |
+
2021-8-24,18833.0,554257.0,794.0
|
| 559 |
+
2021-8-25,19536.0,553330.0,809.0
|
| 560 |
+
2021-8-26,19630.0,552479.0,820.0
|
| 561 |
+
2021-8-27,19509.0,551973.0,798.0
|
| 562 |
+
2021-8-28,19492.0,551255.0,799.0
|
| 563 |
+
2021-8-29,19286.0,552940.0,797.0
|
| 564 |
+
2021-8-30,18325.0,556293.0,792.0
|
| 565 |
+
2021-8-31,17813.0,554687.0,795.0
|
| 566 |
+
2021-9-01,18368.0,553940.0,790.0
|
| 567 |
+
2021-9-02,18985.0,553458.0,798.0
|
| 568 |
+
2021-9-03,18856.0,552825.0,799.0
|
| 569 |
+
2021-9-04,18780.0,552072.0,796.0
|
| 570 |
+
2021-9-05,18645.0,554668.0,793.0
|
| 571 |
+
2021-9-06,17856.0,557458.0,790.0
|
| 572 |
+
2021-9-07,17425.0,556845.0,795.0
|
| 573 |
+
2021-9-08,18024.0,555810.0,797.0
|
| 574 |
+
2021-9-09,18380.0,553757.0,794.0
|
| 575 |
+
2021-9-10,18341.0,554188.0,789.0
|
| 576 |
+
2021-9-11,18891.0,554395.0,796.0
|
| 577 |
+
2021-9-12,18554.0,557664.0,788.0
|
| 578 |
+
2021-9-13,18178.0,562654.0,719.0
|
| 579 |
+
2021-9-14,17837.0,563803.0,781.0
|
| 580 |
+
2021-9-15,18841.0,564813.0,792.0
|
| 581 |
+
2021-9-16,19594.0,566287.0,794.0
|
| 582 |
+
2021-9-17,19905.0,568782.0,791.0
|
| 583 |
+
2021-9-18,20329.0,572065.0,799.0
|
| 584 |
+
2021-9-19,20174.0,578028.0,793.0
|
| 585 |
+
2021-9-20,19744.0,585002.0,778.0
|
| 586 |
+
2021-9-21,19179.0,587932.0,812.0
|
| 587 |
+
2021-9-22,19706.0,590719.0,817.0
|
| 588 |
+
2021-9-23,21438.0,594770.0,820.0
|
| 589 |
+
2021-9-24,21379.0,599493.0,828.0
|
| 590 |
+
2021-9-25,22041.0,604387.0,822.0
|
| 591 |
+
2021-9-26,22498.0,612409.0,805.0
|
| 592 |
+
2021-9-27,22236.0,620353.0,779.0
|
| 593 |
+
2021-9-28,21559.0,623692.0,852.0
|
| 594 |
+
2021-9-29,22430.0,626809.0,857.0
|
| 595 |
+
2021-9-30,23888.0,631004.0,867.0
|
| 596 |
+
2021-10-01,24522.0,634684.0,887.0
|
| 597 |
+
2021-10-02,25219.0,641165.0,886.0
|
| 598 |
+
2021-10-03,25769.0,650653.0,890.0
|
| 599 |
+
2021-10-04,25781.0,661025.0,883.0
|
| 600 |
+
2021-10-05,25110.0,666672.0,895.0
|
| 601 |
+
2021-10-06,25133.0,671035.0,929.0
|
| 602 |
+
2021-10-07,27550.0,677331.0,924.0
|
| 603 |
+
2021-10-08,27246.0,683075.0,936.0
|
| 604 |
+
2021-10-09,29362.0,690420.0,968.0
|
| 605 |
+
2021-10-10,28647.0,700831.0,962.0
|
| 606 |
+
2021-10-11,29409.0,713823.0,957.0
|
| 607 |
+
2021-10-12,28190.0,720334.0,973.0
|
| 608 |
+
2021-10-13,28717.0,726266.0,984.0
|
| 609 |
+
2021-10-14,31299.0,734909.0,986.0
|
| 610 |
+
2021-10-15,32196.0,743839.0,998.0
|
| 611 |
+
2021-10-16,33208.0,754162.0,1002.0
|
| 612 |
+
2021-10-17,34303.0,768751.0,997.0
|
| 613 |
+
2021-10-18,34325.0,785647.0,998.0
|
| 614 |
+
2021-10-19,33740.0,794946.0,1015.0
|
| 615 |
+
2021-10-20,34073.0,802760.0,1028.0
|
| 616 |
+
2021-10-21,36339.0,812168.0,1036.0
|
| 617 |
+
2021-10-22,37141.0,822792.0,1064.0
|
| 618 |
+
2021-10-23,37678.0,833318.0,1075.0
|
| 619 |
+
2021-10-24,35660.0,845122.0,1072.0
|
| 620 |
+
2021-10-25,37930.0,861293.0,1069.0
|
| 621 |
+
2021-10-26,36446.0,869660.0,1106.0
|
| 622 |
+
2021-10-27,36582.0,875968.0,1123.0
|
| 623 |
+
2021-10-28,40096.0,885587.0,1159.0
|
| 624 |
+
2021-10-29,39849.0,893811.0,1163.0
|
| 625 |
+
2021-10-30,40251.0,903993.0,1160.0
|
| 626 |
+
2021-10-31,40993.0,916713.0,1158.0
|
| 627 |
+
2021-11-01,40402.0,932773.0,1155.0
|
| 628 |
+
2021-11-02,39008.0,939698.0,1178.0
|
| 629 |
+
2021-11-03,40443.0,946145.0,1189.0
|
| 630 |
+
2021-11-04,40217.0,953239.0,1195.0
|
| 631 |
+
2021-11-05,40735.0,964177.0,1192.0
|
| 632 |
+
2021-11-06,41335.0,975123.0,1188.0
|
| 633 |
+
2021-11-07,39165.0,986303.0,1179.0
|
| 634 |
+
2021-11-08,39400.0,998931.0,1190.0
|
| 635 |
+
2021-11-09,39160.0,1004844.0,1211.0
|
| 636 |
+
2021-11-10,38058.0,1007098.0,1239.0
|
| 637 |
+
2021-11-11,40759.0,1013464.0,1237.0
|
| 638 |
+
2021-11-12,40123.0,1018707.0,1235.0
|
| 639 |
+
2021-11-13,39256.0,1022920.0,1241.0
|
| 640 |
+
2021-11-14,38823.0,1030703.0,1219.0
|
| 641 |
+
2021-11-15,38420.0,1040210.0,1211.0
|
| 642 |
+
2021-11-16,36818.0,1041627.0,1240.0
|
| 643 |
+
2021-11-17,36626.0,1040618.0,1247.0
|
| 644 |
+
2021-11-18,37374.0,1040327.0,1251.0
|
| 645 |
+
2021-11-19,37156.0,1039225.0,1254.0
|
| 646 |
+
2021-11-20,37120.0,1038919.0,1254.0
|
| 647 |
+
2021-11-21,36970.0,1042133.0,1252.0
|
| 648 |
+
2021-11-22,35681.0,1047860.0,1241.0
|
| 649 |
+
2021-11-23,33996.0,1044562.0,1243.0
|
| 650 |
+
2021-11-24,33558.0,1040198.0,1240.0
|
| 651 |
+
2021-11-25,33796.0,1034306.0,1238.0
|
| 652 |
+
2021-11-26,34690.0,1031616.0,1235.0
|
| 653 |
+
2021-11-27,33946.0,1027829.0,1239.0
|
| 654 |
+
2021-11-28,33548.0,1029507.0,1224.0
|
| 655 |
+
2021-11-29,33860.0,1034458.0,1209.0
|
| 656 |
+
2021-11-30,32648.0,1032435.0,1229.0
|
| 657 |
+
2021-12-01,32837.0,1028367.0,1226.0
|
| 658 |
+
2021-12-02,33389.0,1025350.0,1221.0
|
| 659 |
+
2021-12-03,32930.0,1020549.0,1217.0
|
| 660 |
+
2021-12-04,32974.0,1017126.0,1215.0
|
| 661 |
+
2021-12-05,32602.0,1017929.0,1206.0
|
| 662 |
+
2021-12-06,32136.0,1020811.0,1184.0
|
| 663 |
+
2021-12-07,31096.0,1016110.0,1182.0
|
| 664 |
+
2021-12-08,30752.0,1008707.0,1179.0
|
| 665 |
+
2021-12-09,30209.0,1001941.0,1181.0
|
| 666 |
+
2021-12-10,30873.0,995981.0,1176.0
|
| 667 |
+
2021-12-11,30288.0,988652.0,1171.0
|
| 668 |
+
2021-12-12,29929.0,986058.0,1132.0
|
| 669 |
+
2021-12-13,29558.0,985934.0,1121.0
|
| 670 |
+
2021-12-14,28343.0,979048.0,1145.0
|
| 671 |
+
2021-12-15,28363.0,970636.0,1142.0
|
| 672 |
+
2021-12-16,28486.0,960834.0,1133.0
|
| 673 |
+
2021-12-17,27743.0,950060.0,1080.0
|
| 674 |
+
2021-12-18,27434.0,938377.0,1076.0
|
| 675 |
+
2021-12-19,27967.0,932666.0,1023.0
|
| 676 |
+
2021-12-20,27022.0,928610.0,1019.0
|
| 677 |
+
2021-12-21,25907.0,913271.0,1027.0
|
| 678 |
+
2021-12-22,25264.0,895193.0,1020.0
|
| 679 |
+
2021-12-23,25667.0,878213.0,1002.0
|
| 680 |
+
2021-12-24,24703.0,860705.0,998.0
|
| 681 |
+
2021-12-25,24946.0,842563.0,981.0
|
| 682 |
+
2021-12-26,23721.0,828031.0,968.0
|
| 683 |
+
2021-12-27,23210.0,816589.0,937.0
|
| 684 |
+
2021-12-28,21922.0,793615.0,935.0
|
| 685 |
+
2021-12-29,21119.0,771026.0,932.0
|
| 686 |
+
2021-12-30,21073.0,748169.0,926.0
|
| 687 |
+
2021-12-31,20638.0,727203.0,912.0
|
| 688 |
+
2022-1-01,19751.0,712963.0,847.0
|
| 689 |
+
2022-1-02,18233.0,703409.0,811.0
|
| 690 |
+
2022-1-03,16343.0,694880.0,835.0
|
| 691 |
+
2022-1-04,15903.0,682878.0,834.0
|
| 692 |
+
2022-1-05,15772.0,672241.0,828.0
|
| 693 |
+
2022-1-06,15316.0,663806.0,802.0
|
| 694 |
+
2022-1-07,16735.0,657719.0,787.0
|
| 695 |
+
2022-1-08,16568.0,653042.0,796.0
|
| 696 |
+
2022-1-09,16246.0,647774.0,763.0
|
| 697 |
+
2022-1-10,15830.0,642973.0,741.0
|
| 698 |
+
2022-1-11,17525.0,634499.0,783.0
|
| 699 |
+
2022-1-12,17946.0,625354.0,745.0
|
| 700 |
+
2022-1-13,21155.0,619785.0,740.0
|
| 701 |
+
2022-1-14,23820.0,617914.0,739.0
|
| 702 |
+
2022-1-15,27179.0,617786.0,723.0
|
| 703 |
+
2022-1-16,29230.0,623599.0,686.0
|
| 704 |
+
2022-1-17,30726.0,633899.0,670.0
|
| 705 |
+
2022-1-18,31252.0,639899.0,688.0
|
| 706 |
+
2022-1-19,33899.0,650180.0,698.0
|
| 707 |
+
2022-1-20,38850.0,663868.0,684.0
|
| 708 |
+
2022-1-21,49513.0,687970.0,692.0
|
| 709 |
+
2022-1-22,57212.0,718976.0,681.0
|
| 710 |
+
2022-1-23,63205.0,758457.0,679.0
|
| 711 |
+
2022-1-24,65109.0,801197.0,655.0
|
| 712 |
+
2022-1-25,67809.0,841921.0,681.0
|
| 713 |
+
2022-1-26,74692.0,887759.0,657.0
|
| 714 |
+
2022-1-27,88816.0,946156.0,665.0
|
| 715 |
+
2022-1-28,98040.0,1014017.0,673.0
|
| 716 |
+
2022-1-29,113122.0,1096461.0,668.0
|
| 717 |
+
2022-1-30,121228.0,1188128.0,617.0
|
| 718 |
+
2022-1-31,124070.0,1281447.0,621.0
|
| 719 |
+
2022-2-01,125836.0,1366319.0,663.0
|
| 720 |
+
2022-2-02,141883.0,1459098.0,678.0
|
| 721 |
+
2022-2-03,155768.0,1560475.0,667.0
|
| 722 |
+
2022-2-04,168201.0,1669545.0,682.0
|
| 723 |
+
2022-2-05,177282.0,1785606.0,714.0
|
| 724 |
+
2022-2-06,180071.0,1905433.0,661.0
|
| 725 |
+
2022-2-07,171905.0,2021046.0,609.0
|
| 726 |
+
2022-2-08,165643.0,2104803.0,698.0
|
| 727 |
+
2022-2-09,183103.0,2190074.0,669.0
|
| 728 |
+
2022-2-10,197076.0,2280357.0,701.0
|
| 729 |
+
2022-2-11,203949.0,2371348.0,722.0
|
| 730 |
+
2022-2-12,203766.0,2461727.0,729.0
|
| 731 |
+
2022-2-13,197949.0,2557402.0,706.0
|
| 732 |
+
2022-2-14,180456.0,2639990.0,683.0
|
| 733 |
+
2022-2-15,166631.0,2668036.0,704.0
|
| 734 |
+
2022-2-16,179284.0,2674104.0,748.0
|
| 735 |
+
2022-2-17,180622.0,2668854.0,790.0
|
| 736 |
+
2022-2-18,180071.0,2649772.0,784.0
|
| 737 |
+
2022-2-19,179147.0,2637023.0,798.0
|
| 738 |
+
2022-2-20,170699.0,2659681.0,745.0
|
| 739 |
+
2022-2-21,152337.0,2683789.0,735.0
|
| 740 |
+
2022-2-22,135172.0,2653872.0,796.0
|
| 741 |
+
2022-2-23,137642.0,2611526.0,785.0
|
| 742 |
+
2022-2-24,132998.0,2607329.0,762.0
|
| 743 |
+
2022-2-25,123460.0,2562692.0,787.0
|
| 744 |
+
2022-2-26,122995.0,2503551.0,793.0
|
| 745 |
+
2022-2-27,116093.0,2481279.0,769.0
|
| 746 |
+
2022-2-28,106920.0,2470724.0,733.0
|
| 747 |
+
2022-3-01,97333.0,2394458.0,786.0
|
| 748 |
+
2022-3-02,97455.0,2307599.0,784.0
|
| 749 |
+
2022-3-03,93026.0,2229155.0,781.0
|
| 750 |
+
2022-3-04,89174.0,2157046.0,776.0
|
| 751 |
+
2022-3-05,86769.0,2087340.0,750.0
|
| 752 |
+
2022-3-06,79863.0,2017399.0,744.0
|
| 753 |
+
2022-3-07,73162.0,1967274.0,668.0
|
| 754 |
+
2022-3-08,66576.0,1921442.0,652.0
|
| 755 |
+
2022-3-09,58675.0,1886740.0,645.0
|
| 756 |
+
2022-3-10,51231.0,1788672.0,665.0
|
| 757 |
+
2022-3-11,50743.0,1687531.0,674.0
|
| 758 |
+
2022-3-12,48154.0,1578204.0,630.0
|
| 759 |
+
2022-3-13,44989.0,1493968.0,596.0
|
| 760 |
+
2022-3-14,41055.0,1429247.0,533.0
|
| 761 |
+
2022-3-15,36678.0,1346995.0,558.0
|
| 762 |
+
2022-3-16,36519.0,1264929.0,576.0
|
| 763 |
+
2022-3-17,34819.0,1186573.0,561.0
|
| 764 |
+
2022-3-18,34442.0,1115427.0,524.0
|
| 765 |
+
2022-3-19,32958.0,1052332.0,495.0
|
| 766 |
+
2022-3-20,31035.0,1008258.0,434.0
|
| 767 |
+
2022-3-21,28709.0,974620.0,409.0
|
| 768 |
+
2022-3-22,26394.0,925622.0,472.0
|
| 769 |
+
2022-3-23,26826.0,881397.0,429.0
|
| 770 |
+
2022-3-24,25387.0,839890.0,418.0
|
| 771 |
+
2022-3-25,25382.0,803014.0,398.0
|
| 772 |
+
2022-3-26,24072.0,765758.0,395.0
|
| 773 |
+
2022-3-27,23280.0,741160.0,338.0
|
| 774 |
+
2022-3-28,21101.0,726180.0,335.0
|
| 775 |
+
2022-3-29,19660.0,698272.0,339.0
|
| 776 |
+
2022-3-30,20145.0,666980.0,352.0
|
| 777 |
+
2022-3-31,19277.0,635416.0,345.0
|
| 778 |
+
2022-4-01,19164.0,608240.0,342.0
|
| 779 |
+
2022-4-02,17949.0,581302.0,340.0
|
| 780 |
+
2022-4-03,16828.0,562131.0,304.0
|
| 781 |
+
2022-4-04,15291.0,544285.0,287.0
|
| 782 |
+
2022-4-05,13947.0,520457.0,316.0
|
| 783 |
+
2022-4-06,14661.0,499521.0,291.0
|
| 784 |
+
2022-4-07,14355.0,478285.0,287.0
|
| 785 |
+
2022-4-08,14311.0,454632.0,280.0
|
| 786 |
+
2022-4-09,13573.0,427817.0,288.0
|
| 787 |
+
2022-4-10,13056.0,411242.0,259.0
|
| 788 |
+
2022-4-11,11855.0,396094.0,248.0
|
| 789 |
+
2022-4-12,10910.0,375452.0,281.0
|
| 790 |
+
2022-4-13,11754.0,358804.0,267.0
|
| 791 |
+
2022-4-14,11348.0,348045.0,254.0
|
| 792 |
+
2022-4-15,11432.0,339693.0,261.0
|
| 793 |
+
2022-4-16,11095.0,331542.0,240.0
|
| 794 |
+
2022-4-17,10263.0,325076.0,233.0
|
| 795 |
+
2022-4-18,9434.0,319973.0,213.0
|
| 796 |
+
2022-4-19,8640.0,311615.0,235.0
|
| 797 |
+
2022-4-20,9195.0,304387.0,223.0
|
| 798 |
+
2022-4-21,8875.0,298883.0,197.0
|
| 799 |
+
2022-4-22,9001.0,294209.0,195.0
|
| 800 |
+
2022-4-23,8829.0,289837.0,171.0
|
| 801 |
+
2022-4-24,8446.0,287607.0,168.0
|
| 802 |
+
2022-4-25,7651.0,286244.0,159.0
|
| 803 |
+
2022-4-26,7107.0,281276.0,176.0
|
| 804 |
+
2022-4-27,7705.0,277341.0,163.0
|
| 805 |
+
2022-4-28,7681.0,273793.0,166.0
|
| 806 |
+
2022-4-29,7710.0,270301.0,161.0
|
| 807 |
+
2022-4-30,7363.0,266485.0,157.0
|
| 808 |
+
2022-5-01,7047.0,264652.0,147.0
|
| 809 |
+
2022-5-02,6207.0,263221.0,136.0
|
| 810 |
+
2022-5-03,5466.0,261510.0,125.0
|
| 811 |
+
2022-5-04,5093.0,259810.0,129.0
|
| 812 |
+
2022-5-05,5011.0,255959.0,139.0
|
| 813 |
+
2022-5-06,5541.0,251956.0,136.0
|
| 814 |
+
2022-5-07,5500.0,248675.0,132.0
|
| 815 |
+
2022-5-08,5447.0,247322.0,118.0
|
| 816 |
+
2022-5-09,5030.0,246290.0,103.0
|
| 817 |
+
2022-5-10,4531.0,245706.0,101.0
|
| 818 |
+
2022-5-11,4102.0,244667.0,98.0
|
| 819 |
+
2022-5-12,4065.0,241691.0,111.0
|
| 820 |
+
2022-5-13,4896.0,239225.0,105.0
|
| 821 |
+
2022-5-14,5047.0,236787.0,107.0
|
src/streamlit_app.py
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
БЫСТРЫЙ_СТАРТ.md
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ⚡ Быстрый старт
|
| 2 |
+
|
| 3 |
+
## 🚀 Запуск программы
|
| 4 |
+
|
| 5 |
+
```bash
|
| 6 |
+
pip install -r requirements.txt
|
| 7 |
+
streamlit run src/streamlit_app.py
|
| 8 |
+
```
|
| 9 |
+
|
| 10 |
+
Откройте браузер: `http://localhost:8501`
|
| 11 |
+
|
| 12 |
+
---
|
| 13 |
+
|
| 14 |
+
## 📋 ЛР №1: Пошаговая инструкция
|
| 15 |
+
|
| 16 |
+
### Шаг 1: Загрузка данных
|
| 17 |
+
- Загрузите CSV файл или выберите пример
|
| 18 |
+
- Укажите колонку с датами
|
| 19 |
+
|
| 20 |
+
### Шаг 2: Предобработка
|
| 21 |
+
- Нажмите **"Run Preprocessing"**
|
| 22 |
+
- Оставьте настройки по умолчанию (или измените при необходимости)
|
| 23 |
+
|
| 24 |
+
### Шаг 3: Статистика
|
| 25 |
+
- Посмотрите таблицу дескриптивной статистики
|
| 26 |
+
- Изучите гистограммы и матрицу корреляций
|
| 27 |
+
|
| 28 |
+
### Шаг 4: Стационарность
|
| 29 |
+
- Выберите целевую переменную
|
| 30 |
+
- Нажмите **"Run stationarity tests"**
|
| 31 |
+
- Если нестационарен → нажмите **"Apply diff & Re-test"** с `diff_order=1`
|
| 32 |
+
|
| 33 |
+
### Шаг 5: Лаги
|
| 34 |
+
- Укажите лаги: `1,7,30`
|
| 35 |
+
- Укажите окна: `7,30`
|
| 36 |
+
- Нажмите **"Generate lags & rolls"**
|
| 37 |
+
|
| 38 |
+
### Шаг 6: ACF/PACF
|
| 39 |
+
- Выберите целевую переменную
|
| 40 |
+
- Установите `max_lag=40`
|
| 41 |
+
- Посмотрите графики и значимые лаги
|
| 42 |
+
|
| 43 |
+
### Шаг 7: Декомпозиция
|
| 44 |
+
- Выберите модель: `additive`
|
| 45 |
+
- Укажите период: `7` (для недельной) или `30` (для месячной)
|
| 46 |
+
- Нажмите **"Run decomposition"**
|
| 47 |
+
|
| 48 |
+
### Шаг 8: Отчёт
|
| 49 |
+
- Нажмите **"Сгенерировать и показать отчёт"**
|
| 50 |
+
- Скачайте HTML-отчёт
|
| 51 |
+
|
| 52 |
+
---
|
| 53 |
+
|
| 54 |
+
## 🔮 ЛР №2: Пошаговая инструкция
|
| 55 |
+
|
| 56 |
+
### Шаг 1: Подготовка
|
| 57 |
+
- Убедитесь, что данные из ЛР №1 загружены
|
| 58 |
+
- Выберите целевую переменную
|
| 59 |
+
- Установите горизонт прогнозирования (h): `7` или `30`
|
| 60 |
+
|
| 61 |
+
### Шаг 2: Декомпозиция
|
| 62 |
+
- Выберите период сезонности: `7` или `30`
|
| 63 |
+
- Нажмите **"Выполнить декомпозицию"**
|
| 64 |
+
|
| 65 |
+
### Шаг 3: Преобразования (опционально)
|
| 66 |
+
- Начните с `none` и `diff_order=0`
|
| 67 |
+
- Если модель плохая → попробуйте `diff_order=1`
|
| 68 |
+
|
| 69 |
+
### Шаг 4: Модели
|
| 70 |
+
- Выберите стратегии: `recursive` (для начала)
|
| 71 |
+
- Выберите модели: `SES`, `Holt_add`
|
| 72 |
+
- Нажмите **"Применить преобразования и построить модели"**
|
| 73 |
+
|
| 74 |
+
### Шаг 5: Результаты
|
| 75 |
+
- Посмотрите таблицу метрик
|
| 76 |
+
- Найдите модель с наименьшими MAE, RMSE, MAPE
|
| 77 |
+
- Посмотрите график прогнозов
|
| 78 |
+
|
| 79 |
+
### Шаг 6: Диагностика
|
| 80 |
+
- Выберите лучшую модель
|
| 81 |
+
- Нажмите **"Выполнить диагностику остатков"**
|
| 82 |
+
- Проверьте, что p-value тестов > 0.05
|
| 83 |
+
|
| 84 |
+
### Шаг 7: Выводы
|
| 85 |
+
- Посмотрите итоговую таблицу
|
| 86 |
+
- Выберите лучшую модель
|
| 87 |
+
- Скачайте прогнозы и параметры модели
|
| 88 |
+
|
| 89 |
+
---
|
| 90 |
+
|
| 91 |
+
## 📊 Что означают метрики
|
| 92 |
+
|
| 93 |
+
| Метрика | Что означает | Чем меньше, тем лучше |
|
| 94 |
+
|---------|-------------|------------------------|
|
| 95 |
+
| **MAE** | Средняя абсолютная ошибка | ✅ |
|
| 96 |
+
| **RMSE** | Корень из средней квадратичной ошибки | ✅ |
|
| 97 |
+
| **MAPE** | Средняя абсолютная процентная ошибка (%) | ✅ |
|
| 98 |
+
|
| 99 |
+
**Пример:** MAPE = 10% означает, что в среднем ошибка 10%
|
| 100 |
+
|
| 101 |
+
---
|
| 102 |
+
|
| 103 |
+
## 🎯 Тесты стационарности
|
| 104 |
+
|
| 105 |
+
| Тест | Стационарен если | Нестационарен если |
|
| 106 |
+
|------|------------------|---------------------|
|
| 107 |
+
| **ADF** | p-value < 0.05 ✅ | p-value >= 0.05 ❌ |
|
| 108 |
+
| **KPSS** | p-value > 0.05 ✅ | p-value <= 0.05 ❌ |
|
| 109 |
+
|
| 110 |
+
**Если нестационарен:** примените `diff_order=1`
|
| 111 |
+
|
| 112 |
+
---
|
| 113 |
+
|
| 114 |
+
## 🔍 Корреляции
|
| 115 |
+
|
| 116 |
+
| Значение | Интерпретация |
|
| 117 |
+
|----------|---------------|
|
| 118 |
+
| **1.0** | Полная прямая связь |
|
| 119 |
+
| **0.7-1.0** | Сильная связь |
|
| 120 |
+
| **0.3-0.7** | Умеренная связь |
|
| 121 |
+
| **0.0-0.3** | Слабая связь |
|
| 122 |
+
| **0.0** | Нет связи |
|
| 123 |
+
| **-1.0** | Полная обратная связь |
|
| 124 |
+
|
| 125 |
+
**Проблема:** Если два признака коррелируют > 0.8 → мультиколлинеарность
|
| 126 |
+
|
| 127 |
+
---
|
| 128 |
+
|
| 129 |
+
## 🎨 Модели экспоненциального сглаживания
|
| 130 |
+
|
| 131 |
+
| Модель | Когда использовать |
|
| 132 |
+
|--------|-------------------|
|
| 133 |
+
| **SES** | Стационарный ряд без тренда |
|
| 134 |
+
| **Holt Additive** | Ряд с линейным трендом |
|
| 135 |
+
| **Holt Multiplicative** | Ряд с экспоненциальным трендом |
|
| 136 |
+
|
| 137 |
+
---
|
| 138 |
+
|
| 139 |
+
## 🚦 Стратегии прогнозирования
|
| 140 |
+
|
| 141 |
+
| Стратегия | Когда использовать |
|
| 142 |
+
|-----------|-------------------|
|
| 143 |
+
| **Recursive** | Короткий горизонт (h < 7) |
|
| 144 |
+
| **Direct** | Длинный горизонт (h >= 30) |
|
| 145 |
+
| **Hybrid** | Средний горизонт (7-30) |
|
| 146 |
+
|
| 147 |
+
---
|
| 148 |
+
|
| 149 |
+
## ⚠️ Частые ошибки
|
| 150 |
+
|
| 151 |
+
1. **"Недостаточно данных"**
|
| 152 |
+
- Решение: уменьшите размер обучающей выборки
|
| 153 |
+
|
| 154 |
+
2. **"Для лог-трансформации все значения должны быть положительными"**
|
| 155 |
+
- **Причина:** В данных есть нули или отрицательные значения
|
| 156 |
+
- **Решения:**
|
| 157 |
+
- ✅ **Используйте Box-Cox** вместо логарифма (автоматически обработает проблему)
|
| 158 |
+
- ✅ **Используйте только дифференцирование** (diff_order=1) без логарифма
|
| 159 |
+
- ✅ **Включите автоматический сдвиг** - программа предложит сдвинуть данные
|
| 160 |
+
- **Как исправить:**
|
| 161 |
+
1. Посмотрите статистику данных в сообщении об ошибке
|
| 162 |
+
2. Включите чекбокс "Автоматически сдвинуть данные"
|
| 163 |
+
3. Или измените тип преобразования на "boxcox"
|
| 164 |
+
|
| 165 |
+
3. **"Ошибка при преобразовании"**
|
| 166 |
+
- Решение: убедитесь, что все значения положительные (для log/boxcox)
|
| 167 |
+
|
| 168 |
+
4. **"Модель работает плохо"**
|
| 169 |
+
- Решение: попробуйте `diff_order=1` или другую модель
|
| 170 |
+
|
| 171 |
+
5. **"Остатки имеют автокорреляцию"**
|
| 172 |
+
- Решение: попробуйте другую модель или стратегию
|
| 173 |
+
|
| 174 |
+
---
|
| 175 |
+
|
| 176 |
+
## 💡 Советы
|
| 177 |
+
|
| 178 |
+
1. ✅ Начинайте с простого (SES, recursive, без преобразований)
|
| 179 |
+
2. ✅ Сравнивайте с наивным прогнозом (baseline)
|
| 180 |
+
3. ✅ Проверяйте диагностику остатков
|
| 181 |
+
4. ✅ Экспериментируйте с разными моделями
|
| 182 |
+
5. ✅ Сохраняйте результаты (CSV, HTML-отчёты)
|
| 183 |
+
|
| 184 |
+
---
|
| 185 |
+
|
| 186 |
+
## 📁 Что скачивать
|
| 187 |
+
|
| 188 |
+
- **final_dataset.csv** - очищенные данные (ЛР №1)
|
| 189 |
+
- **dataset_with_lags.csv** - данные с лагами (ЛР №1)
|
| 190 |
+
- **ts_report.html** - HTML-отчёт (ЛР №1)
|
| 191 |
+
- **forecast_*.csv** - прогнозы моделей (ЛР №2)
|
| 192 |
+
- **model_params_*.csv** - параметры моделей (ЛР №2)
|
| 193 |
+
|
| 194 |
+
---
|
| 195 |
+
|
| 196 |
+
**Подробное руководство:** см. `РУКОВОДСТВО.md`
|
| 197 |
+
|
РУКОВОДСТВО.md
ADDED
|
@@ -0,0 +1,655 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 📚 Руководство по использованию программы для анализа временных рядов
|
| 2 |
+
|
| 3 |
+
## 🎯 Общая структура проекта
|
| 4 |
+
|
| 5 |
+
Ваш проект состоит из двух лабораторных работ, объединённых в одно веб-приложение:
|
| 6 |
+
|
| 7 |
+
### Файлы проекта:
|
| 8 |
+
- **`src/streamlit_app.py`** - главный файл веб-приложения (1655 строк)
|
| 9 |
+
- **`src/lab2_functions.py`** - функции для лабораторной работы №2 (604 строки)
|
| 10 |
+
- **`src/main.py`** - не используется (можно игнорировать)
|
| 11 |
+
- **`russia_covid_dataset.csv`** - пример данных (COVID-19 по России)
|
| 12 |
+
|
| 13 |
+
---
|
| 14 |
+
|
| 15 |
+
## 🚀 Как запустить программу
|
| 16 |
+
|
| 17 |
+
1. **Установите зависимости:**
|
| 18 |
+
```bash
|
| 19 |
+
pip install -r requirements.txt
|
| 20 |
+
```
|
| 21 |
+
|
| 22 |
+
2. **Запустите приложение:**
|
| 23 |
+
```bash
|
| 24 |
+
streamlit run src/streamlit_app.py
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
3. **Откройте браузер:**
|
| 28 |
+
- Программа автоматически откроется на `http://localhost:8501`
|
| 29 |
+
- Или откройте вручную этот адрес
|
| 30 |
+
|
| 31 |
+
---
|
| 32 |
+
|
| 33 |
+
## 📖 ЛАБОРАТОРНАЯ РАБОТА №1: Введение в анализ временных рядов
|
| 34 |
+
|
| 35 |
+
### Что делает эта работа?
|
| 36 |
+
|
| 37 |
+
Эта работа учит вас **"читать"** временной ряд - понимать его структуру, находить закономерности, выявлять проблемы.
|
| 38 |
+
|
| 39 |
+
### Порядок работы (по этапам):
|
| 40 |
+
|
| 41 |
+
#### **Этап 1: Загрузка данных**
|
| 42 |
+
1. В боковой панели нажмите "Загрузите CSV/Parquet"
|
| 43 |
+
2. Или выберите предзагруженный пример (если есть `russia_covid_dataset.csv`)
|
| 44 |
+
3. Укажите колонку с датами (программа попытается найти её автоматически)
|
| 45 |
+
|
| 46 |
+
**Что происходит:** Программа загружает ваш файл и показывает первые строки.
|
| 47 |
+
|
| 48 |
+
---
|
| 49 |
+
|
| 50 |
+
#### **Этап 2: Предобработка данных (Preprocessing)**
|
| 51 |
+
|
| 52 |
+
**Настройки в боковой панели:**
|
| 53 |
+
- **Как трактовать tz-naive метки?** - как обрабатывать даты без временной зоны
|
| 54 |
+
- **Заполнение пропусков (числ.)** - что делать с пропущенными числами:
|
| 55 |
+
- `interpolate` - заполнить интерполяцией (рекомендуется)
|
| 56 |
+
- `drop` - удалить строки
|
| 57 |
+
- `rolling` - заполнить скользящим средним
|
| 58 |
+
- **Обработка выбросов** - что делать с аномальными значениями:
|
| 59 |
+
- `interpolate` - заменить интерполяцией
|
| 60 |
+
- `winsorize` - обрезать экстремальные значения
|
| 61 |
+
- `drop` - удалить
|
| 62 |
+
- `mark` - только отметить
|
| 63 |
+
- **Ресемплить к частоте** - изменить частоту данных (D=день, W=неделя, M=месяц)
|
| 64 |
+
|
| 65 |
+
**Что делать:**
|
| 66 |
+
1. Нажмите кнопку **"Run Preprocessing"**
|
| 67 |
+
2. Программа покажет:
|
| 68 |
+
- Сколько строк было до/после обработки
|
| 69 |
+
- Сколько пропусков найдено и обработано
|
| 70 |
+
- Сколько выбросов обнаружено
|
| 71 |
+
|
| 72 |
+
**Результат:** Очищенный датасет, готовый к анализу.
|
| 73 |
+
|
| 74 |
+
---
|
| 75 |
+
|
| 76 |
+
#### **Этап 3: Описательная статистика и визуализация**
|
| 77 |
+
|
| 78 |
+
**Что показывается:**
|
| 79 |
+
|
| 80 |
+
1. **Таблица дескриптивной статистики:**
|
| 81 |
+
- `count` - количество наблюдений
|
| 82 |
+
- `mean` - среднее значение
|
| 83 |
+
- `median` - медиана (середина)
|
| 84 |
+
- `std` - стандартное отклонение (разброс)
|
| 85 |
+
- `min/max` - минимум/максимум
|
| 86 |
+
- `q1/q3` - первый и третий квартили (25% и 75%)
|
| 87 |
+
- `skew` - асимметрия (если >0, хвост справа)
|
| 88 |
+
- `kurtosis` - эксцесс (острота распределения)
|
| 89 |
+
|
| 90 |
+
2. **Гистограммы и Boxplot:**
|
| 91 |
+
- **Гистограмма** - показывает распределение значений (как часто встречается каждое значение)
|
| 92 |
+
- **Boxplot** - показывает медиану, квартили и выбросы
|
| 93 |
+
|
| 94 |
+
3. **Матрица корреляций:**
|
| 95 |
+
- Показывает, насколько связаны между собой признаки
|
| 96 |
+
- Значения от -1 до 1:
|
| 97 |
+
- **1** = полная прямая связь
|
| 98 |
+
- **0** = нет связи
|
| 99 |
+
- **-1** = полная ��братная связь
|
| 100 |
+
- **Важно:** Если два признака сильно коррелируют (>0.8), это может быть проблемой (мультиколлинеарность)
|
| 101 |
+
|
| 102 |
+
**Как интерпретировать:**
|
| 103 |
+
- Если `std` большой относительно `mean` - данные сильно разбросаны
|
| 104 |
+
- Если `skew` далёк от 0 - распределение несимметрично
|
| 105 |
+
- Сильные корреляции (>0.7) указывают на зависимость признаков
|
| 106 |
+
|
| 107 |
+
---
|
| 108 |
+
|
| 109 |
+
#### **Этап 4: Проверка на стационарность**
|
| 110 |
+
|
| 111 |
+
**Что такое стационарность?**
|
| 112 |
+
Стационарный ряд = ряд без тренда, с постоянной дисперсией. Многие модели требуют стационарности.
|
| 113 |
+
|
| 114 |
+
**Что показывается:**
|
| 115 |
+
|
| 116 |
+
1. **График ряда с rolling mean:**
|
| 117 |
+
- Если линия скользящего среднего не горизонтальна → есть тренд (нестационарен)
|
| 118 |
+
- Если линия скользящего среднего горизонтальна → тренда нет (стационарен)
|
| 119 |
+
|
| 120 |
+
2. **График rolling std:**
|
| 121 |
+
- Если линия не горизонтальна → дисперсия меняется (нестационарен)
|
| 122 |
+
- Если горизонтальна → дисперсия постоянна
|
| 123 |
+
|
| 124 |
+
3. **Статистические тесты:**
|
| 125 |
+
- **ADF (Augmented Dickey-Fuller):**
|
| 126 |
+
- p-value < 0.05 → ряд стационарен ✅
|
| 127 |
+
- p-value >= 0.05 → ряд нестационарен ❌
|
| 128 |
+
- **KPSS:**
|
| 129 |
+
- p-value > 0.05 → ряд стационарен ✅
|
| 130 |
+
- p-value <= 0.05 → ряд нестационарен ❌
|
| 131 |
+
|
| 132 |
+
**Что делать, если ряд нестационарен:**
|
| 133 |
+
1. Нажмите "Apply diff & Re-test" с `diff_order=1`
|
| 134 |
+
2. Это применит дифференцирование (убирает тренд)
|
| 135 |
+
3. Повторите тесты
|
| 136 |
+
|
| 137 |
+
---
|
| 138 |
+
|
| 139 |
+
#### **Этап 5: Создание лагов и скользящих статистик**
|
| 140 |
+
|
| 141 |
+
**Что такое лаги?**
|
| 142 |
+
Лаг = значение переменной в прошлом. Например, `target_lag_7` = значение target 7 дней назад.
|
| 143 |
+
|
| 144 |
+
**Что создаётся:**
|
| 145 |
+
- **Лаги:** `target_lag_1`, `target_lag_7`, `target_lag_30`
|
| 146 |
+
- **Скользящие статистики:**
|
| 147 |
+
- `target_rolling_mean_7` - среднее за последние 7 дней
|
| 148 |
+
- `target_rolling_std_7` - стандартное отклонение за последние 7 дней
|
| 149 |
+
|
| 150 |
+
**Как использовать:**
|
| 151 |
+
1. Укажите целевую переменную (target)
|
| 152 |
+
2. Укажите лаги через запятую (например: `1,7,30`)
|
| 153 |
+
3. Укажите окна для скользящих (например: `7,30`)
|
| 154 |
+
4. Нажмите "Generate lags & rolls"
|
| 155 |
+
|
| 156 |
+
**Что показывается:**
|
| 157 |
+
- Таблица корреляций лагов с target - какие лаги наиболее информативны
|
| 158 |
+
- Heatmap корреляций - визуализация всех корреляций
|
| 159 |
+
- VIF (Variance Inflation Factor) - проверка мультиколлинеарности:
|
| 160 |
+
- VIF < 5 → нормально
|
| 161 |
+
- VIF 5-10 → умеренная мультиколлинеарность
|
| 162 |
+
- VIF > 10 → сильная мультиколлинеарность (проблема)
|
| 163 |
+
|
| 164 |
+
---
|
| 165 |
+
|
| 166 |
+
#### **Этап 6: ACF и PACF**
|
| 167 |
+
|
| 168 |
+
**Что это такое?**
|
| 169 |
+
- **ACF (Autocorrelation Function)** - корреляция ряда с его лагами
|
| 170 |
+
- **PACF (Partial Autocorrelation Function)** - "чистая" корреляция с лагом
|
| 171 |
+
|
| 172 |
+
**Как интерпретировать графики:**
|
| 173 |
+
|
| 174 |
+
1. **ACF:**
|
| 175 |
+
- Плавное затухание → возможный порядок MA(q)
|
| 176 |
+
- Резкий обрыв → возможный порядок AR(p)
|
| 177 |
+
|
| 178 |
+
2. **PACF:**
|
| 179 |
+
- Резкий обрыв на лаге p → возможный порядок AR(p)
|
| 180 |
+
- Плавное затухание → возможный порядок MA(q)
|
| 181 |
+
|
| 182 |
+
3. **Значимые лаги:**
|
| 183 |
+
- Лаги, выходящие за доверительный интервал (синие линии) → статистически значимы
|
| 184 |
+
- Эти лаги важны для моделирования
|
| 185 |
+
|
| 186 |
+
**Что делать:**
|
| 187 |
+
- Посмотрите, какие лаги значимы
|
| 188 |
+
- Запомните порядок обрыва в PACF - это может быть порядок AR модели
|
| 189 |
+
|
| 190 |
+
---
|
| 191 |
+
|
| 192 |
+
#### **Этап 7: Декомпозиция временного ряда**
|
| 193 |
+
|
| 194 |
+
**Что такое декомпозиция?**
|
| 195 |
+
Разложение ряда на компоненты:
|
| 196 |
+
- **Observed** - исходный ряд
|
| 197 |
+
- **Trend** - тренд (долгосрочная тенденция)
|
| 198 |
+
- **Seasonal** - сезонность (периодические колебания)
|
| 199 |
+
- **Residual** - остатки (случайные колебания)
|
| 200 |
+
|
| 201 |
+
**Модели декомпозиции:**
|
| 202 |
+
- **Additive (аддитивная):** `value = trend + seasonal + residual`
|
| 203 |
+
- Используйте, если амплитуда сезонности постоянна
|
| 204 |
+
- **Multiplicative (мультипликативная):** `value = trend × seasonal × residual`
|
| 205 |
+
- Используйте, если амплитуда сезонности растёт со временем
|
| 206 |
+
|
| 207 |
+
**Как использовать:**
|
| 208 |
+
1. Выберите целевую переменную
|
| 209 |
+
2. Выберите модель (additive/multiplicative)
|
| 210 |
+
3. Укажите период сезонности:
|
| 211 |
+
- 7 - для недельной сезонности
|
| 212 |
+
- 30 - для месячной
|
| 213 |
+
- 365 - для годовой
|
| 214 |
+
4. Нажмите "Run decomposition"
|
| 215 |
+
|
| 216 |
+
**Что показывается:**
|
| 217 |
+
- Графики всех компонентов
|
| 218 |
+
- Анализ тренда (растёт/падает)
|
| 219 |
+
- Амплитуда сезонности
|
| 220 |
+
- Диагностика остатков (должны быть стационарны и случайны)
|
| 221 |
+
|
| 222 |
+
**Как интерпретировать:**
|
| 223 |
+
- Если остатки стационарны (ADF/KPSS тесты) → декомпозиция хорошая ✅
|
| 224 |
+
- Если остатки нестационарны → попробуйте другой период или модель ❌
|
| 225 |
+
|
| 226 |
+
---
|
| 227 |
+
|
| 228 |
+
#### **Этап 8: Генерация отчёта**
|
| 229 |
+
|
| 230 |
+
**Что делает:**
|
| 231 |
+
Собирает все графики и таблицы в один HTML-отчёт.
|
| 232 |
+
|
| 233 |
+
**Как использовать:**
|
| 234 |
+
1. Настройте параметры в разделе "Параметры для отчёта"
|
| 235 |
+
2. Нажмите "Сгенерировать и показать отчёт"
|
| 236 |
+
3. Просмотрите результаты во вкладках
|
| 237 |
+
4. Скачайте HTML-отчёт
|
| 238 |
+
|
| 239 |
+
---
|
| 240 |
+
|
| 241 |
+
## 🔮 ЛАБОРАТОРНАЯ РАБОТА №2: Прогнозирование временных рядов
|
| 242 |
+
|
| 243 |
+
### Что делает эта работа?
|
| 244 |
+
|
| 245 |
+
Эта работа учит строить **модели для прогнозирования** будущих значений временного ряда.
|
| 246 |
+
|
| 247 |
+
### Порядок работы:
|
| 248 |
+
|
| 249 |
+
#### **Этап 1: Декомпозиция и анализ остатков**
|
| 250 |
+
|
| 251 |
+
**Что делать:**
|
| 252 |
+
1. Убедитесь, что данные из ЛР №1 загружены
|
| 253 |
+
2. Выберите целевую переменную в боковой панели
|
| 254 |
+
3. Укажите горизонт прогнозирования (h) - на сколько шагов вперёд прогнозировать
|
| 255 |
+
4. Настройте размер обучающей выборки (рекомендуется ≥500)
|
| 256 |
+
5. Нажмите "Выполнить декомпозицию"
|
| 257 |
+
|
| 258 |
+
**Что показывается:**
|
| 259 |
+
- Графики компонентов (observed, trend, seasonal, residuals)
|
| 260 |
+
- Анализ остатков (должны быть стационарны)
|
| 261 |
+
- ACF/PACF остатков (не должно быть значимых лагов)
|
| 262 |
+
|
| 263 |
+
**Как интерпретировать:**
|
| 264 |
+
- Если остатки стационарны и не имеют автокорреляции → декомпозиция хорошая ✅
|
| 265 |
+
- Если нет → попробуйте другой период или модель
|
| 266 |
+
|
| 267 |
+
---
|
| 268 |
+
|
| 269 |
+
#### **Этап 2: Feature Engineering**
|
| 270 |
+
|
| 271 |
+
**Что создаётся:**
|
| 272 |
+
- Временные признаки (день недели, месяц, квартал)
|
| 273 |
+
- Циклические признаки (sin/cos для периодичности)
|
| 274 |
+
- Лаги (lag_1, lag_7, lag_30)
|
| 275 |
+
- Скользящие статистики (mean, std, min, max)
|
| 276 |
+
|
| 277 |
+
**Как использовать:**
|
| 278 |
+
1. Нажмите "Создать расширенные признаки"
|
| 279 |
+
2. Просмотрите созданные признаки
|
| 280 |
+
3. Скачайте датасет с признаками (опционально)
|
| 281 |
+
|
| 282 |
+
---
|
| 283 |
+
|
| 284 |
+
#### **Этап 3: Стратегии прогнозирования**
|
| 285 |
+
|
| 286 |
+
**Три стратегии:**
|
| 287 |
+
|
| 288 |
+
1. **Recursive (рекурсивная):**
|
| 289 |
+
- Одна модель
|
| 290 |
+
- Использует свои предыдущие прогнозы
|
| 291 |
+
- ✅ Хорошо для короткого горизонта (h < 7)
|
| 292 |
+
- ❌ Ошибка накапливается на длинном горизонте
|
| 293 |
+
|
| 294 |
+
2. **Direct (прямая):**
|
| 295 |
+
- Отдельная модель для каждого шага
|
| 296 |
+
- Прогнозы независимы
|
| 297 |
+
- ✅ Хорошо для длинного горизонта (h >= 30)
|
| 298 |
+
- ❌ Требует больше вычислений
|
| 299 |
+
|
| 300 |
+
3. **Hybrid (гибридная):**
|
| 301 |
+
- Комбинация: рекурсивная для ближних шагов, прямая для дальних
|
| 302 |
+
- ✅ Баланс ме��ду точностью и скоростью
|
| 303 |
+
|
| 304 |
+
**Как использовать:**
|
| 305 |
+
- Выберите стратегии в разделе "Этап 6-7"
|
| 306 |
+
- Можно выбрать несколько для сравнения
|
| 307 |
+
|
| 308 |
+
---
|
| 309 |
+
|
| 310 |
+
#### **Этап 4: Кросс-валидация**
|
| 311 |
+
|
| 312 |
+
**Что такое кросс-валидация?**
|
| 313 |
+
Проверка качества модели на разных частях данных без утечки будущего.
|
| 314 |
+
|
| 315 |
+
**Методы:**
|
| 316 |
+
1. **Sliding window (скользящее окно):**
|
| 317 |
+
- Фиксированная длина обучения
|
| 318 |
+
- Окно "скользит" по времени
|
| 319 |
+
|
| 320 |
+
2. **Expanding window (расширяющееся окно):**
|
| 321 |
+
- Длина обучения растёт
|
| 322 |
+
- Более реалистично для реальных задач
|
| 323 |
+
|
| 324 |
+
3. **TimeSeriesSplit:**
|
| 325 |
+
- Стандартный метод из sklearn
|
| 326 |
+
- Прогрессивное разбиение
|
| 327 |
+
|
| 328 |
+
**Как использовать:**
|
| 329 |
+
1. Выберите метод
|
| 330 |
+
2. Настройте размеры train/test
|
| 331 |
+
3. Нажмите "Выполнить кросс-валидацию"
|
| 332 |
+
|
| 333 |
+
**Что показывается:**
|
| 334 |
+
- Таблица метрик по фолдам
|
| 335 |
+
- Средние метрики
|
| 336 |
+
- График метрик по фолдам
|
| 337 |
+
|
| 338 |
+
**Как интерпретировать:**
|
| 339 |
+
- Если метрики стабильны по фолдам → модель надёжна ✅
|
| 340 |
+
- Если метрики сильно меняются → модель нестабильна ❌
|
| 341 |
+
|
| 342 |
+
---
|
| 343 |
+
|
| 344 |
+
#### **Этап 5: Преобразования к стационарности**
|
| 345 |
+
|
| 346 |
+
**Зачем нужно?**
|
| 347 |
+
Многие модели требуют стационарных данных.
|
| 348 |
+
|
| 349 |
+
**Типы преобразований:**
|
| 350 |
+
|
| 351 |
+
1. **None (без преобразования):**
|
| 352 |
+
- Используйте, если данные уже стационарны
|
| 353 |
+
|
| 354 |
+
2. **Log (логарифм):**
|
| 355 |
+
- Стабилизирует дисперсию
|
| 356 |
+
- Требует положительные значения
|
| 357 |
+
- Используйте, если дисперсия растёт со временем
|
| 358 |
+
|
| 359 |
+
3. **Box-Cox:**
|
| 360 |
+
- Автоматический подбор преобразования
|
| 361 |
+
- Включает логарифм как частный случай
|
| 362 |
+
- ✅ Рекомендуется, если не уверены
|
| 363 |
+
|
| 364 |
+
4. **Дифференцирование:**
|
| 365 |
+
- `diff_order=1` - убирает тренд (первая разность)
|
| 366 |
+
- `diff_order=2` - убирает квадратичный тренд
|
| 367 |
+
- `seasonal_diff=7` - убирает сезонность (для недельной)
|
| 368 |
+
|
| 369 |
+
**Как использовать:**
|
| 370 |
+
1. Начните с `none` и `diff_order=0`
|
| 371 |
+
2. Если модель плохая, попробуйте `diff_order=1`
|
| 372 |
+
3. Если дисперсия нестабильна, попробуйте `log` или `boxcox`
|
| 373 |
+
|
| 374 |
+
**Что показывается:**
|
| 375 |
+
- Результаты тестов ADF/KPSS после преобразования
|
| 376 |
+
- Если p-value ADF < 0.05 и p-value KPSS > 0.05 → стационарен ✅
|
| 377 |
+
|
| 378 |
+
---
|
| 379 |
+
|
| 380 |
+
#### **Этап 6-7: Модели экспоненциального сглаживания**
|
| 381 |
+
|
| 382 |
+
**Три модели:**
|
| 383 |
+
|
| 384 |
+
1. **SES (Simple Exponential Smoothing):**
|
| 385 |
+
- Простое сглаживание без тренда
|
| 386 |
+
- ✅ Хорошо для стационарных рядов
|
| 387 |
+
- ❌ Не учитывает тренд
|
| 388 |
+
|
| 389 |
+
2. **Holt Additive:**
|
| 390 |
+
- С аддитивным трендом (линейный рост/падение)
|
| 391 |
+
- ✅ Хорошо для рядов с линейным трендом
|
| 392 |
+
- Формула: `level + trend`
|
| 393 |
+
|
| 394 |
+
3. **Holt Multiplicative:**
|
| 395 |
+
- С мультипликативным трендом (экспоненциальный рост/падение)
|
| 396 |
+
- ✅ Хорошо для рядов с экспоненциальным трендом
|
| 397 |
+
- ❌ Требует положительные значения
|
| 398 |
+
- Формула: `level × trend`
|
| 399 |
+
|
| 400 |
+
**Как использовать:**
|
| 401 |
+
1. Выберите стратегии прогнозирования
|
| 402 |
+
2. Выберите модели (можно несколько)
|
| 403 |
+
3. Нажмите "Применить преобразования и построить модели"
|
| 404 |
+
|
| 405 |
+
**Что показывается:**
|
| 406 |
+
- Таблица сравнения метрик (MAE, RMSE, MAPE)
|
| 407 |
+
- График прогнозов всех моделей
|
| 408 |
+
- Сравнение с наивным прогнозом (baseline)
|
| 409 |
+
|
| 410 |
+
**Метрики качества:**
|
| 411 |
+
|
| 412 |
+
1. **MAE (Mean Absolute Error):**
|
| 413 |
+
- Средняя абсолютная ошибка
|
| 414 |
+
- Чем меньше, тем лучше
|
| 415 |
+
- Интерпретация: среднее отклонение прогноза от реальности
|
| 416 |
+
|
| 417 |
+
2. **RMSE (Root Mean Squared Error):**
|
| 418 |
+
- Корень из средней квадратичной ошибки
|
| 419 |
+
- Чем меньше, те�� лучше
|
| 420 |
+
- Более чувствительна к большим ошибкам
|
| 421 |
+
- Интерпретация: типичное отклонение (с учётом больших ошибок)
|
| 422 |
+
|
| 423 |
+
3. **MAPE (Mean Absolute Percentage Error):**
|
| 424 |
+
- Средняя абсолютная процентная ошибка
|
| 425 |
+
- Чем меньше, тем лучше
|
| 426 |
+
- Интерпретация: ошибка в процентах
|
| 427 |
+
- Пример: MAPE=5% означает, что в среднем ошибка 5%
|
| 428 |
+
|
| 429 |
+
**Как интерпретировать:**
|
| 430 |
+
- Сравните метрики моделей
|
| 431 |
+
- Выберите модель с наименьшими MAE, RMSE, MAPE
|
| 432 |
+
- Убедитесь, что модель лучше наивного прогноза
|
| 433 |
+
|
| 434 |
+
---
|
| 435 |
+
|
| 436 |
+
#### **Этап 7: Диагностика остатков**
|
| 437 |
+
|
| 438 |
+
**Что проверяется:**
|
| 439 |
+
|
| 440 |
+
1. **Тест Льюнга-Бокса:**
|
| 441 |
+
- Проверяет автокорреляцию в остатках
|
| 442 |
+
- p-value < 0.05 → есть автокорреляция (плохо) ❌
|
| 443 |
+
- p-value >= 0.05 → нет автокорреляции (хорошо) ✅
|
| 444 |
+
|
| 445 |
+
2. **Тест Шапиро-Уилка:**
|
| 446 |
+
- Проверяет нормальность распределения остатков
|
| 447 |
+
- p-value < 0.05 → не нормально (плохо) ❌
|
| 448 |
+
- p-value >= 0.05 → нормально (хорошо) ✅
|
| 449 |
+
|
| 450 |
+
3. **Графики:**
|
| 451 |
+
- **Остатки vs Прогнозы:** должны быть случайными (гомоскедастичность)
|
| 452 |
+
- **Q-Q Plot:** точки должны лежать на прямой (нормальность)
|
| 453 |
+
- **Временной ряд остатков:** не должно быть тренда
|
| 454 |
+
|
| 455 |
+
**Как использовать:**
|
| 456 |
+
1. Выберите модель для диагностики
|
| 457 |
+
2. Нажмите "Выполнить диагностику остатков"
|
| 458 |
+
|
| 459 |
+
**Как интерпретировать:**
|
| 460 |
+
- Если все тесты пройдены → модель адекватна ✅
|
| 461 |
+
- Если есть проблемы → попробуйте другую модель или добавьте преобразования
|
| 462 |
+
|
| 463 |
+
---
|
| 464 |
+
|
| 465 |
+
#### **Этап 8: Сравнительный анализ и выводы**
|
| 466 |
+
|
| 467 |
+
**Что показывается:**
|
| 468 |
+
- Итоговая таблица метрик всех моделей
|
| 469 |
+
- Лучшие модели по каждой метрике
|
| 470 |
+
- Рекомендации по выбору модели
|
| 471 |
+
|
| 472 |
+
**Как использовать:**
|
| 473 |
+
1. Посмотрите на таблицу метрик
|
| 474 |
+
2. Найдите модель с наименьшими ошибками
|
| 475 |
+
3. Проверьте диагностику остатков для этой модели
|
| 476 |
+
4. Используйте эту модель для прогнозирования
|
| 477 |
+
|
| 478 |
+
**Рекомендации:**
|
| 479 |
+
- **Короткий горизонт (h < 7):** рекурсивная стратегия
|
| 480 |
+
- **Длинный горизонт (h >= 30):** прямая или гибридная стратегия
|
| 481 |
+
- **Нестационарный ряд:** используйте дифференцирование (diff_order=1)
|
| 482 |
+
- **Нестабильная дисперсия:** используйте Box-Cox преобразование
|
| 483 |
+
|
| 484 |
+
---
|
| 485 |
+
|
| 486 |
+
## 📊 Что означают числа и метрики
|
| 487 |
+
|
| 488 |
+
### Статистические метрики:
|
| 489 |
+
|
| 490 |
+
- **Mean (среднее):** среднее значение всех наблюдений
|
| 491 |
+
- **Median (медиана):** значение в середине (50% наблюдений меньше)
|
| 492 |
+
- **Std (стандартное отклонение):** мера разброса данных
|
| 493 |
+
- Маленькое std → данные сконцентрированы
|
| 494 |
+
- Большое std → данные разбросаны
|
| 495 |
+
- **Min/Max:** минимальное и максимальное значения
|
| 496 |
+
- **Q1/Q3 (квартили):** 25% и 75% наблюдений меньше этого значения
|
| 497 |
+
- **Skew (асимметрия):**
|
| 498 |
+
- 0 = симметричное распределение
|
| 499 |
+
- >0 = хвост справа (больше больших значений)
|
| 500 |
+
- <0 = хвост слева (больше маленьких значений)
|
| 501 |
+
- **Kurtosis (эксцесс):**
|
| 502 |
+
- 0 = нормальное распределение
|
| 503 |
+
- >0 = более острое распределение (больше экстремальных значений)
|
| 504 |
+
- <0 = более плоское распределение
|
| 505 |
+
|
| 506 |
+
### Метрики качества прогноза:
|
| 507 |
+
|
| 508 |
+
- **MAE:** средняя абсолютная ошибка (в тех же единицах, что и данные)
|
| 509 |
+
- **RMSE:** корень из средней квадратичной ошибки (более чувствительна к большим ошибкам)
|
| 510 |
+
- **MAPE:** средняя абсолютная процентная ошибка (в процентах)
|
| 511 |
+
|
| 512 |
+
**Пример интерпретации:**
|
| 513 |
+
- Если MAPE = 10%, это означает, что в среднем прогноз отклоняется на 10% от реальности
|
| 514 |
+
- Если MAE = 100, это означает, что в среднем прогноз отклоняется на 100 единиц
|
| 515 |
+
|
| 516 |
+
### Тесты стационарности:
|
| 517 |
+
|
| 518 |
+
- **ADF p-value:**
|
| 519 |
+
- < 0.05 → ряд стационарен ✅
|
| 520 |
+
- >= 0.05 → ряд нестационарен ❌
|
| 521 |
+
- **KPSS p-value:**
|
| 522 |
+
- > 0.05 → ряд стационарен ✅
|
| 523 |
+
- <= 0.05 → ряд нестационарен ❌
|
| 524 |
+
|
| 525 |
+
### Корреляции:
|
| 526 |
+
|
| 527 |
+
- **1.0:** полная прямая связь (когда один растёт, другой тоже растёт)
|
| 528 |
+
- **0.0:** нет связи
|
| 529 |
+
- **-1.0:** полная обратная связь (когда один растёт, другой падает)
|
| 530 |
+
- **0.7-1.0:** сильная связь
|
| 531 |
+
- **0.3-0.7:** умеренная связь
|
| 532 |
+
- **0.0-0.3:** слабая связь
|
| 533 |
+
|
| 534 |
+
---
|
| 535 |
+
|
| 536 |
+
## 🎓 Типичный workflow (порядок работы)
|
| 537 |
+
|
| 538 |
+
### Для ЛР №1:
|
| 539 |
+
|
| 540 |
+
1. Загрузите данные
|
| 541 |
+
2. Запустите предобработку
|
| 542 |
+
3. Посмотрите описательную статистику
|
| 543 |
+
4. Проверьте стационарность
|
| 544 |
+
5. Создайте лаги и скользящие статистики
|
| 545 |
+
6. Постройте ACF/PACF
|
| 546 |
+
7. Выполните декомпозицию
|
| 547 |
+
8. Сгенерируйте отчёт
|
| 548 |
+
|
| 549 |
+
### Для ЛР №2:
|
| 550 |
+
|
| 551 |
+
1. Убедитесь, что данные из ЛР №1 загружены
|
| 552 |
+
2. Выполните декомпозицию
|
| 553 |
+
3. (Опционально) Создайте расширенные признаки
|
| 554 |
+
4. (Опционально) Выполните кросс-валидацию
|
| 555 |
+
5. Настройте преобразования (начните с none, diff_order=0)
|
| 556 |
+
6. Постройте модели (выберите несколько для сравнения)
|
| 557 |
+
7. Проверьте диагностику остатков для лучшей модели
|
| 558 |
+
8. Посмотрите итоговые результаты и выберите лучшую модель
|
| 559 |
+
|
| 560 |
+
---
|
| 561 |
+
|
| 562 |
+
## ⚠️ Частые проблемы и решения
|
| 563 |
+
|
| 564 |
+
### Проблема: "Недостаточно данных"
|
| 565 |
+
**Решение:** Уменьшите размер обучающей выборки или увеличьте тестовую
|
| 566 |
+
|
| 567 |
+
### Проблема: "Для лог-трансформации все значения должны быть положительными"
|
| 568 |
+
**Причина:**
|
| 569 |
+
Логарифм можно применять только к положительным числам. Если в данных есть нули или отрицательные значения, логарифм не может быть вычислен.
|
| 570 |
+
|
| 571 |
+
**Что покажет программа:**
|
| 572 |
+
- Количество неположительных значений
|
| 573 |
+
- Минимальное и максимальное значения
|
| 574 |
+
- График распределения данных
|
| 575 |
+
- Предложение автоматического сдвига
|
| 576 |
+
|
| 577 |
+
**Решения:**
|
| 578 |
+
|
| 579 |
+
1. **Используйте Box-Cox преобразование** (рекомендуется):
|
| 580 |
+
- Измените тип преобразования с `log` на `boxcox`
|
| 581 |
+
- Box-Cox автоматически обработает проблему (требует только положительные значения, но может работать с нулями через сдвиг)
|
| 582 |
+
|
| 583 |
+
2. **Используйте только дифференцирование**:
|
| 584 |
+
- Оставьте `transformation='none'`
|
| 585 |
+
- Установите `diff_order=1`
|
| 586 |
+
- Это уберёт тренд без необходимости логарифма
|
| 587 |
+
|
| 588 |
+
3. **Автоматический сдвиг данных**:
|
| 589 |
+
- Включите чекбокс "Автоматически сдвинуть данные"
|
| 590 |
+
- Программа добавит константу ко всем значениям, чтобы сделать их положительными
|
| 591 |
+
- Минимум станет равным 1 (или больше)
|
| 592 |
+
|
| 593 |
+
4. **Вручную сдвиньте данные** (перед загрузкой):
|
| 594 |
+
- Если данные содержат отрицательные значения или нули
|
| 595 |
+
- Добавьте константу ко всем значениям в CSV файле
|
| 596 |
+
- Например: `new_value = old_value + abs(min_value) + 1`
|
| 597 |
+
|
| 598 |
+
**Как выбрать решение:**
|
| 599 |
+
- Если данные близки к нулю → используйте Box-Cox
|
| 600 |
+
- Если нужно сохранить интерпретацию → используйте только дифференцирование
|
| 601 |
+
- Если данные уже в логарифмической шкале → проверьте, не применяли ли в�� логарифм дважды
|
| 602 |
+
|
| 603 |
+
### Проблема: "Ошибка при преобразовании" (общая)
|
| 604 |
+
**Решение:**
|
| 605 |
+
- Для логарифма: убедитесь, что все значения положительные (см. выше)
|
| 606 |
+
- Для Box-Cox: убедитесь, что все значения положительные (или используйте автоматический сдвиг)
|
| 607 |
+
|
| 608 |
+
### Проблема: "Модель работает плохо"
|
| 609 |
+
**Решение:**
|
| 610 |
+
1. Попробуйте дифференцирование (diff_order=1)
|
| 611 |
+
2. Попробуйте Box-Cox преобразование
|
| 612 |
+
3. Попробуйте другую модель (например, Holt вместо SES)
|
| 613 |
+
4. Проверьте, стационарен ли ряд
|
| 614 |
+
|
| 615 |
+
### Проблема: "Остатки имеют автокорреляцию"
|
| 616 |
+
**Решение:**
|
| 617 |
+
1. Попробуйте другую модель
|
| 618 |
+
2. Добавьте больше лагов
|
| 619 |
+
3. Попробуйте другую стратегию прогнозирования
|
| 620 |
+
|
| 621 |
+
---
|
| 622 |
+
|
| 623 |
+
## 💡 Советы
|
| 624 |
+
|
| 625 |
+
1. **Начинайте просто:** сначала попробуйте модель без преобразований
|
| 626 |
+
2. **Сравнивайте с baseline:** убедитесь, что ваша модель лучше наивного прогноза
|
| 627 |
+
3. **Проверяйте диагностику:** хорошая модель должна иметь "хорошие" остатки
|
| 628 |
+
4. **Экспериментируйте:** пробуйте разные комбинации моделей и стратегий
|
| 629 |
+
5. **Документируйте:** записывайте, что пробовали и какие результаты получили
|
| 630 |
+
|
| 631 |
+
---
|
| 632 |
+
|
| 633 |
+
## 📝 Что делать с результатами
|
| 634 |
+
|
| 635 |
+
1. **Сохраните очищенные данные:** скачайте `final_dataset.csv`
|
| 636 |
+
2. **Сохраните прогнозы:** скачайте прогнозы лучшей модели
|
| 637 |
+
3. **Сохраните параметры модели:** скачайте параметры для воспроизведения
|
| 638 |
+
4. **Создайте отчёт:** используйте HTML-отчёт из ЛР №1
|
| 639 |
+
5. **Задокументируйте выводы:** запишите, какая модель лучше и почему
|
| 640 |
+
|
| 641 |
+
---
|
| 642 |
+
|
| 643 |
+
## 🎯 Ключевые выводы
|
| 644 |
+
|
| 645 |
+
- **ЛР №1** учит понимать структуру данных
|
| 646 |
+
- **ЛР №2** учит строить модели для прогнозирования
|
| 647 |
+
- **Метрики** показывают качество модели
|
| 648 |
+
- **Диагностика** проверяет адекватность модели
|
| 649 |
+
- **Преобразования** нужны для стационарности
|
| 650 |
+
- **Стратегии** влияют на качество прогноза
|
| 651 |
+
|
| 652 |
+
---
|
| 653 |
+
|
| 654 |
+
**Удачи в работе! 🚀**
|
| 655 |
+
|
СТРУКТУРА_КОДА.md
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🏗️ Структура кода проекта
|
| 2 |
+
|
| 3 |
+
## 📁 Файловая структура
|
| 4 |
+
|
| 5 |
+
```
|
| 6 |
+
TimeSeriesHomework/
|
| 7 |
+
├── src/
|
| 8 |
+
│ ├── streamlit_app.py # Главное веб-приложение (1655 строк)
|
| 9 |
+
│ ├── lab2_functions.py # Функции для ЛР №2 (604 строки)
|
| 10 |
+
│ ├── main.py # Не используется (можно игнорировать)
|
| 11 |
+
│ └── russia_covid_dataset.csv # Пример данных
|
| 12 |
+
├── requirements.txt # Зависимости Python
|
| 13 |
+
├── README.md # Описание проекта
|
| 14 |
+
├── РУКОВОДСТВО.md # Подробное руководство (этот файл)
|
| 15 |
+
├── БЫСТРЫЙ_СТАРТ.md # Краткая шпаргалка
|
| 16 |
+
└── СТРУКТУРА_КОДА.md # Этот файл
|
| 17 |
+
```
|
| 18 |
+
|
| 19 |
+
---
|
| 20 |
+
|
| 21 |
+
## 🔍 streamlit_app.py - Главное приложение
|
| 22 |
+
|
| 23 |
+
### Структура файла:
|
| 24 |
+
|
| 25 |
+
```
|
| 26 |
+
1. Импорты (строки 1-34)
|
| 27 |
+
├── Библиотеки: pandas, numpy, streamlit, plotly
|
| 28 |
+
├── Статистика: statsmodels, scipy
|
| 29 |
+
└── Импорт функций из lab2_functions.py
|
| 30 |
+
|
| 31 |
+
2. Утилиты (строки 45-94)
|
| 32 |
+
├── detect_date_column() - поиск колонки с датами
|
| 33 |
+
├── try_parse_dates() - парсинг дат
|
| 34 |
+
├── localize_to_moscow() - приведение к часовому поясу
|
| 35 |
+
├── detect_outliers_iqr() - поиск выбросов
|
| 36 |
+
└── winsorize_series() - обработка выбросов
|
| 37 |
+
|
| 38 |
+
3. Предобработка (строки 96-195)
|
| 39 |
+
└── preprocess_timeseries() - основная функция очистки данных
|
| 40 |
+
├── Парсинг дат
|
| 41 |
+
├── Обработка пропусков
|
| 42 |
+
├── Обработка выбросов
|
| 43 |
+
└── Ресемплирование
|
| 44 |
+
|
| 45 |
+
4. Описательная статистика (строки 198-217)
|
| 46 |
+
└── descriptive_statistics() - расчёт статистик
|
| 47 |
+
|
| 48 |
+
5. Стационарность (строки 220-234)
|
| 49 |
+
├── run_adf() - тест Дики-Фуллера
|
| 50 |
+
└── run_kpss() - тест KPSS
|
| 51 |
+
|
| 52 |
+
6. Лаги и скользящие (строки 237-274)
|
| 53 |
+
├── create_lags_and_rolls() - создание лагов
|
| 54 |
+
├── compute_lag_correlations() - корреляции лагов
|
| 55 |
+
└── compute_vif() - проверка мультиколлинеарности
|
| 56 |
+
|
| 57 |
+
7. ACF/PACF (строки 277-316)
|
| 58 |
+
├── get_acf_pacf_with_conf() - расчёт ACF/PACF
|
| 59 |
+
├── significant_lags_from_conf() - значимые лаги
|
| 60 |
+
└── plotly_acf_pacf() - визуализация
|
| 61 |
+
|
| 62 |
+
8. Генерация отчёта (строки 319-359)
|
| 63 |
+
└── generate_html_report() - создание HTML-отчёта
|
| 64 |
+
|
| 65 |
+
9. Функция ЛР №1 (строки 362-844)
|
| 66 |
+
└── render_lab1() - весь интерфейс ЛР №1
|
| 67 |
+
├── Загрузка данных
|
| 68 |
+
├── Предобработка
|
| 69 |
+
├── Описательная статистика
|
| 70 |
+
├── Стационарность
|
| 71 |
+
├── Лаги и скользящие
|
| 72 |
+
├── ACF/PACF
|
| 73 |
+
├── Декомпозиция
|
| 74 |
+
└── Генерация отчёта
|
| 75 |
+
|
| 76 |
+
10. Функция ЛР №2 (строки 846-1646)
|
| 77 |
+
└── render_lab2() - весь интерфейс ЛР №2
|
| 78 |
+
├── Декомпозиция
|
| 79 |
+
├── Feature Engineering
|
| 80 |
+
├── Кросс-валидация
|
| 81 |
+
├── Преобразования
|
| 82 |
+
├── Модели экспоненциального сглаживания
|
| 83 |
+
├── Диагностика остатков
|
| 84 |
+
└── Сравнительный анализ
|
| 85 |
+
|
| 86 |
+
11. Главный код (строки 1648-1654)
|
| 87 |
+
└── Выбор лабораторной работы через sidebar
|
| 88 |
+
```
|
| 89 |
+
|
| 90 |
+
### Поток данных в ЛР №1:
|
| 91 |
+
|
| 92 |
+
```
|
| 93 |
+
Загрузка CSV
|
| 94 |
+
↓
|
| 95 |
+
preprocess_timeseries()
|
| 96 |
+
↓
|
| 97 |
+
df_clean (очищенные данные)
|
| 98 |
+
↓
|
| 99 |
+
descriptive_statistics() → Таблица статистик
|
| 100 |
+
↓
|
| 101 |
+
run_adf() / run_kpss() → Тесты стационарности
|
| 102 |
+
↓
|
| 103 |
+
create_lags_and_rolls() → Датасет с лагами
|
| 104 |
+
↓
|
| 105 |
+
get_acf_pacf_with_conf() → Графики ACF/PACF
|
| 106 |
+
↓
|
| 107 |
+
seasonal_decompose() → Декомпозиция
|
| 108 |
+
↓
|
| 109 |
+
generate_html_report() → HTML-отчёт
|
| 110 |
+
```
|
| 111 |
+
|
| 112 |
+
### Поток данных в ЛР №2:
|
| 113 |
+
|
| 114 |
+
```
|
| 115 |
+
df_clean (из ЛР №1)
|
| 116 |
+
↓
|
| 117 |
+
Разделение на train/test
|
| 118 |
+
↓
|
| 119 |
+
seasonal_decompose() → Декомпозиция
|
| 120 |
+
↓
|
| 121 |
+
create_advanced_features() → Расширенные признаки (опционально)
|
| 122 |
+
↓
|
| 123 |
+
apply_transformations() → Преобразования к стационарности
|
| 124 |
+
↓
|
| 125 |
+
create_exponential_smoothing_model() → Модель
|
| 126 |
+
↓
|
| 127 |
+
recursive_forecast() / direct_forecast() / hybrid_forecast() → Прогноз
|
| 128 |
+
↓
|
| 129 |
+
inverse_transformations() → Обратное преобразование
|
| 130 |
+
↓
|
| 131 |
+
evaluate_forecast() → Метрики (MAE, RMSE, MAPE)
|
| 132 |
+
↓
|
| 133 |
+
diagnose_model_residuals() → Диагностика остатков
|
| 134 |
+
```
|
| 135 |
+
|
| 136 |
+
---
|
| 137 |
+
|
| 138 |
+
## 🔧 lab2_functions.py - Функции для ЛР №2
|
| 139 |
+
|
| 140 |
+
### Структура файла:
|
| 141 |
+
|
| 142 |
+
```
|
| 143 |
+
1. Метрики (строки 18-25)
|
| 144 |
+
└── calculate_mape() - расчёт MAPE
|
| 145 |
+
|
| 146 |
+
2. Feature Engineering (строки 28-70)
|
| 147 |
+
└── create_advanced_features() - создание признаков
|
| 148 |
+
├── Временные признаки (день недели, месяц)
|
| 149 |
+
├── Циклические признаки (sin/cos)
|
| 150 |
+
├── Лаги (lag_1, lag_7, lag_30)
|
| 151 |
+
└── Скользящие статистики (mean, std, min, max)
|
| 152 |
+
|
| 153 |
+
3. Преобразования (строки 73-223)
|
| 154 |
+
├── apply_boxcox_transform() - преобразование Бокса-Кокса
|
| 155 |
+
├── inverse_boxcox_transform() - обратное преобразование
|
| 156 |
+
├── apply_transformations() - цепочка преобразований
|
| 157 |
+
└── inverse_transformations() - обратная цепочка
|
| 158 |
+
|
| 159 |
+
4. Стратегии прогнозирования (строки 226-413)
|
| 160 |
+
├── recursive_forecast() - рекурсивная стратегия
|
| 161 |
+
├── direct_forecast() - прямая стратегия
|
| 162 |
+
└── hybrid_forecast() - гибридная стратегия
|
| 163 |
+
|
| 164 |
+
5. Модели (строки 416-435)
|
| 165 |
+
└── create_exponential_smoothing_model() - создание модели
|
| 166 |
+
|
| 167 |
+
6. Оценка качества (строки 438-457)
|
| 168 |
+
├── evaluate_forecast() - метрики качества
|
| 169 |
+
└── naive_forecast() - наивный прогноз (baseline)
|
| 170 |
+
|
| 171 |
+
7. Кросс-валидация (строки 460-535)
|
| 172 |
+
├── time_series_cv_sliding_window() - скользящее окно
|
| 173 |
+
└── time_series_cv_expanding_window() - расширяющееся окно
|
| 174 |
+
|
| 175 |
+
8. Диагностика (строки 538-602)
|
| 176 |
+
└── diagnose_model_residuals() - диагностика остатков
|
| 177 |
+
├── Тест Льюнга-Бокса (автокорреляция)
|
| 178 |
+
├── Тест Шапиро-Уилка (нормальность)
|
| 179 |
+
└── Тесты стационарности (ADF/KPSS)
|
| 180 |
+
```
|
| 181 |
+
|
| 182 |
+
### Зависимости между функциями:
|
| 183 |
+
|
| 184 |
+
```
|
| 185 |
+
apply_transformations()
|
| 186 |
+
↓
|
| 187 |
+
create_exponential_smoothing_model()
|
| 188 |
+
↓
|
| 189 |
+
recursive_forecast() / direct_forecast() / hybrid_forecast()
|
| 190 |
+
↓
|
| 191 |
+
inverse_transformations()
|
| 192 |
+
↓
|
| 193 |
+
evaluate_forecast()
|
| 194 |
+
↓
|
| 195 |
+
diagnose_model_residuals()
|
| 196 |
+
```
|
| 197 |
+
|
| 198 |
+
---
|
| 199 |
+
|
| 200 |
+
## 🔄 Как работает программа
|
| 201 |
+
|
| 202 |
+
### 1. Запуск приложения
|
| 203 |
+
|
| 204 |
+
```python
|
| 205 |
+
# streamlit_app.py, строка 1648
|
| 206 |
+
if lab_choice == "ЛР №1: ...":
|
| 207 |
+
render_lab1()
|
| 208 |
+
elif lab_choice == "ЛР №2: ...":
|
| 209 |
+
render_lab2()
|
| 210 |
+
```
|
| 211 |
+
|
| 212 |
+
### 2. Выбор лабораторной работы
|
| 213 |
+
|
| 214 |
+
Пользователь выбирает в sidebar (боковая панель):
|
| 215 |
+
- "ЛР №1: Введение в анализ временных рядов"
|
| 216 |
+
- "ЛР №2: Прогнозирование временных рядов"
|
| 217 |
+
|
| 218 |
+
### 3. Сохранение состояния
|
| 219 |
+
|
| 220 |
+
Программа использует `st.session_state` для сохранения:
|
| 221 |
+
- `df_in` - исходные данные
|
| 222 |
+
- `df_clean` - очищенные данные
|
| 223 |
+
- `df_lags` - данные с лагами
|
| 224 |
+
- `decomp` - результат декомпозиции
|
| 225 |
+
- `forecast_results` - результаты прогнозирования
|
| 226 |
+
- `models` - обученные модели
|
| 227 |
+
|
| 228 |
+
### 4. Обработка данных
|
| 229 |
+
|
| 230 |
+
Все преобразования применяются последовательно:
|
| 231 |
+
1. Загрузка → `df_in`
|
| 232 |
+
2. Предобработка → `df_clean`
|
| 233 |
+
3. Создание лагов → `df_lags`
|
| 234 |
+
4. Преобразования → `s_train_transformed`
|
| 235 |
+
5. Прогнозирование → `forecast`
|
| 236 |
+
6. Обратное преобразование → `forecast_original`
|
| 237 |
+
|
| 238 |
+
---
|
| 239 |
+
|
| 240 |
+
## 📊 Ключевые функции и их назначение
|
| 241 |
+
|
| 242 |
+
### Предобработка:
|
| 243 |
+
|
| 244 |
+
| Функция | Что делает |
|
| 245 |
+
|---------|------------|
|
| 246 |
+
| `preprocess_timeseries()` | Основная функция очистки данных |
|
| 247 |
+
| `detect_outliers_iqr()` | Находит выбросы методом IQR |
|
| 248 |
+
| `winsorize_series()` | Обрезает экстремальные значения |
|
| 249 |
+
|
| 250 |
+
### Анализ:
|
| 251 |
+
|
| 252 |
+
| Функция | Что делает |
|
| 253 |
+
|---------|------------|
|
| 254 |
+
| `descriptive_statistics()` | Расчёт статистик (mean, std, etc.) |
|
| 255 |
+
| `run_adf()` | Тест стационарности ADF |
|
| 256 |
+
| `run_kpss()` | Тест стационарности KPSS |
|
| 257 |
+
| `get_acf_pacf_with_conf()` | Расчёт ACF/PACF с доверительными интервалами |
|
| 258 |
+
|
| 259 |
+
### Прогнозирование:
|
| 260 |
+
|
| 261 |
+
| Функция | Что делает |
|
| 262 |
+
|---------|------------|
|
| 263 |
+
| `apply_transformations()` | Применяет преобразования (log, boxcox, diff) |
|
| 264 |
+
| `create_exponential_smoothing_model()` | Создаёт модель экспоненциального сглаживания |
|
| 265 |
+
| `recursive_forecast()` | Рекурсивная стратегия прогнозирования |
|
| 266 |
+
| `direct_forecast()` | Прямая стратегия прогнозирования |
|
| 267 |
+
| `hybrid_forecast()` | Гибридная стратегия прогнозирования |
|
| 268 |
+
| `inverse_transformations()` | Обратное преобразование прогнозов |
|
| 269 |
+
|
| 270 |
+
### Оценка:
|
| 271 |
+
|
| 272 |
+
| Функция | Что делает |
|
| 273 |
+
|---------|------------|
|
| 274 |
+
| `evaluate_forecast()` | Расчёт метрик (MAE, RMSE, MAPE) |
|
| 275 |
+
| `diagnose_model_residuals()` | Диагностика остатков модели |
|
| 276 |
+
|
| 277 |
+
---
|
| 278 |
+
|
| 279 |
+
## 🎯 Где что искать
|
| 280 |
+
|
| 281 |
+
### Если нужно изменить предобработку:
|
| 282 |
+
→ `streamlit_app.py`, функция `preprocess_timeseries()` (строки 97-195)
|
| 283 |
+
|
| 284 |
+
### Если нужно добавить новую метрику:
|
| 285 |
+
→ `lab2_functions.py`, функция `evaluate_forecast()` (строки 438-451)
|
| 286 |
+
|
| 287 |
+
### Если нужно изменить визуализацию:
|
| 288 |
+
→ `streamlit_app.py`, функции `render_lab1()` или `render_lab2()`
|
| 289 |
+
|
| 290 |
+
### Если нужно добавить новую модель:
|
| 291 |
+
→ `lab2_functions.py`, функция `create_exponential_smoothing_model()` (строки 416-435)
|
| 292 |
+
|
| 293 |
+
### Если нужно изменить стратегию прогнозирования:
|
| 294 |
+
→ `lab2_functions.py`, функции `recursive_forecast()`, `direct_forecast()`, `hybrid_forecast()`
|
| 295 |
+
|
| 296 |
+
---
|
| 297 |
+
|
| 298 |
+
## 🔗 Связи между модулями
|
| 299 |
+
|
| 300 |
+
```
|
| 301 |
+
streamlit_app.py
|
| 302 |
+
│
|
| 303 |
+
├── Импортирует функции из lab2_functions.py
|
| 304 |
+
│ ├── create_advanced_features()
|
| 305 |
+
│ ├── apply_transformations()
|
| 306 |
+
│ ├── recursive_forecast()
|
| 307 |
+
│ ├── direct_forecast()
|
| 308 |
+
│ ├── hybrid_forecast()
|
| 309 |
+
│ ├── create_exponential_smoothing_model()
|
| 310 |
+
│ ├── evaluate_forecast()
|
| 311 |
+
│ └── diagnose_model_residuals()
|
| 312 |
+
│
|
| 313 |
+
└── Использует библиотеки:
|
| 314 |
+
├── pandas - работа с данными
|
| 315 |
+
├── numpy - численные вычисления
|
| 316 |
+
├── streamlit - веб-интерфейс
|
| 317 |
+
├── plotly - интерактивные графики
|
| 318 |
+
├── statsmodels - статистические модели
|
| 319 |
+
└── scipy - научные вычисления
|
| 320 |
+
```
|
| 321 |
+
|
| 322 |
+
---
|
| 323 |
+
|
| 324 |
+
## 📝 Примечания
|
| 325 |
+
|
| 326 |
+
1. **session_state** используется для сохранения состояния между перезагрузками страницы
|
| 327 |
+
2. **Обработка ошибок** - большинство функций имеют try/except блоки
|
| 328 |
+
3. **Визуализация** - используется Plotly для интерактивных графиков
|
| 329 |
+
4. **Модульность** - функции разделены по назначению (предобработка, анализ, прогнозирование)
|
| 330 |
+
|
| 331 |
+
---
|
| 332 |
+
|
| 333 |
+
**Для подробного понимания работы программы см. `РУКОВОДСТВО.md`**
|
| 334 |
+
|