diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..bdf137b46a7d383dd629b6d27abd6ce339062d9e --- /dev/null +++ b/.gitignore @@ -0,0 +1,163 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +.DS_Store +*/.DS_Store \ No newline at end of file diff --git a/README.md b/README.md index f86d23a8e8ad7e9196e117a531d9ea5e92944546..e916dce4b8e70985d3ce1013a0b9a1fca80b9246 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,17 @@ --- -title: Demand Forecasting -emoji: 🏆 -colorFrom: red -colorTo: green +title: demand-forecasting +app_file: demo.py sdk: gradio -sdk_version: 3.47.1 -app_file: app.py -pinned: false +sdk_version: 3.41.0 --- +### Update conda environment +```sh +conda env update --file environment.yml --prune +``` -Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference +### Add conda environment to ipykernel +```sh +python -m ipykernel install --user --name demand-forecasting +``` + +### to run gradio app diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/__pycache__/demo.cpython-310.pyc b/__pycache__/demo.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..801d01dabf06b435e680e35d17cc750b6c1997dc Binary files /dev/null and b/__pycache__/demo.cpython-310.pyc differ diff --git a/best_models.csv b/best_models.csv new file mode 100644 index 0000000000000000000000000000000000000000..978827f24e0ed41266c3408eee5dbfa6e86d7ff7 --- /dev/null +++ b/best_models.csv @@ -0,0 +1,11 @@ +sku,best_model,characteristic,RMSE +sku-0,fft_plus,continuous,20.29778313018444 +sku-1,holt_winters_plus,continuous,48.49842843820416 +sku-2,prophet_plus,fuzzy,39.28846310729568 +sku-3,prophet_plus,fuzzy_transient,14.593201789242087 +sku-4,prophet_plus,fuzzy,10.7747925198657 +sku-5,prophet_plus,fuzzy,28.33012802382216 +sku-6,ceif_plus,fuzzy,37.84242038358283 +sku-7,holt_winters_plus,continuous,15.959770854065722 +sku-8,prophet_plus,fuzzy,13.778467035419936 +sku-9,prophet_plus,fuzzy,12.843706019437128 diff --git a/conda_installs.txt b/conda_installs.txt new file mode 100644 index 0000000000000000000000000000000000000000..39a954c706ae38a7d592b2be0e891166a18997e1 --- /dev/null +++ b/conda_installs.txt @@ -0,0 +1,13 @@ +conda install -c anaconda ipykernel -y +conda install -c anaconda urllib3 -y +conda install -c conda-forge gradio -y +conda install -c conda-forge prophet -y + +conda install -c anaconda pandas -y +conda install scikit-learn -y +conda install -c intel pyyaml -y +conda install -c conda-forge python-dotenv -y + +(if conda version of gradio doesn't work) +pip install gradio + diff --git a/data/continuous.csv b/data/continuous.csv new file mode 100644 index 0000000000000000000000000000000000000000..e30a588738374246cdeeea3b641c6b2e488b09a7 --- /dev/null +++ b/data/continuous.csv @@ -0,0 +1,33 @@ +datetime,y +2020-05-31,150.0 +2020-06-30,508.0 +2020-07-31,292.0 +2020-08-31,800.0 +2020-09-30,800.0 +2020-10-31,800.0 +2020-11-30,300.0 +2020-12-31,300.0 +2021-01-31,237.0 +2021-02-28,237.0 +2021-03-31,600.0 +2021-04-30,200.0 +2021-05-31,600.0 +2021-06-30,400.0 +2021-07-31,1300.0 +2021-08-31,2000.0 +2021-09-30,6500.0 +2021-10-31,1100.0 +2021-11-30,1000.0 +2021-12-31,2000.0 +2022-01-31,3000.0 +2022-02-28,2200.0 +2022-03-31,6800.0 +2022-04-30,2000.0 +2022-05-31,6000.0 +2022-06-30,5300.0 +2022-07-31,3000.0 +2022-08-31,2900.0 +2022-09-30,13600.0 +2022-10-31,15400.0 +2022-11-30,14800.0 +2022-12-31,4000.0 diff --git a/data/demand_forecasting_demo_data.csv b/data/demand_forecasting_demo_data.csv new file mode 100644 index 0000000000000000000000000000000000000000..2db8b5354f3697e2fd73e0e2fc6fccb52bb89f1c --- /dev/null +++ b/data/demand_forecasting_demo_data.csv @@ -0,0 +1,2573 @@ +datetime,y,sku +2018-05-06,2,sku-0 +2018-05-13,1,sku-0 +2018-05-20,7,sku-0 +2018-05-27,9,sku-0 +2018-06-03,2,sku-0 +2018-06-10,3,sku-0 +2018-06-17,9,sku-0 +2018-06-24,9,sku-0 +2018-07-01,9,sku-0 +2018-07-08,9,sku-0 +2018-07-15,9,sku-0 +2018-07-22,9,sku-0 +2018-07-29,9,sku-0 +2018-08-05,9,sku-0 +2018-08-12,9,sku-0 +2018-08-19,9,sku-0 +2018-08-26,9,sku-0 +2018-09-02,9,sku-0 +2018-09-09,9,sku-0 +2018-09-16,6,sku-0 +2018-09-23,6,sku-0 +2018-09-30,2,sku-0 +2018-10-07,9,sku-0 +2018-10-14,9,sku-0 +2018-10-21,2,sku-0 +2018-10-28,10,sku-0 +2018-11-04,3,sku-0 +2018-11-11,9,sku-0 +2018-11-18,16,sku-0 +2018-11-25,1,sku-0 +2018-12-02,10,sku-0 +2018-12-09,9,sku-0 +2018-12-16,4,sku-0 +2018-12-23,2,sku-0 +2018-12-30,1,sku-0 +2019-01-06,9,sku-0 +2019-01-13,6,sku-0 +2019-01-20,15,sku-0 +2019-01-27,2,sku-0 +2019-02-03,9,sku-0 +2019-02-10,9,sku-0 +2019-02-17,9,sku-0 +2019-02-24,10,sku-0 +2019-03-03,9,sku-0 +2019-03-10,2,sku-0 +2019-03-17,14,sku-0 +2019-03-24,1,sku-0 +2019-03-31,2,sku-0 +2019-04-07,9,sku-0 +2019-04-14,3,sku-0 +2019-04-21,3,sku-0 +2019-04-28,1,sku-0 +2019-05-05,2,sku-0 +2019-05-12,9,sku-0 +2019-05-19,13,sku-0 +2019-05-26,6,sku-0 +2019-06-02,2,sku-0 +2019-06-09,9,sku-0 +2019-06-16,9,sku-0 +2019-06-23,9,sku-0 +2019-06-30,12,sku-0 +2019-07-07,2,sku-0 +2019-07-14,1,sku-0 +2019-07-21,15,sku-0 +2019-07-28,9,sku-0 +2019-08-04,5,sku-0 +2019-08-11,9,sku-0 +2019-08-18,12,sku-0 +2019-08-25,6,sku-0 +2019-09-01,4,sku-0 +2019-09-08,9,sku-0 +2019-09-15,1,sku-0 +2019-09-22,20,sku-0 +2019-09-29,9,sku-0 +2019-10-06,9,sku-0 +2019-10-13,4,sku-0 +2019-10-20,4,sku-0 +2019-10-27,9,sku-0 +2019-11-03,17,sku-0 +2019-11-10,1,sku-0 +2019-11-17,11,sku-0 +2019-11-24,5,sku-0 +2019-12-01,7,sku-0 +2019-12-08,4,sku-0 +2019-12-15,9,sku-0 +2019-12-22,9,sku-0 +2019-12-29,13,sku-0 +2020-01-05,9,sku-0 +2020-01-12,15,sku-0 +2020-01-19,3,sku-0 +2020-01-26,3,sku-0 +2020-02-02,4,sku-0 +2020-02-09,8,sku-0 +2020-02-16,30,sku-0 +2020-02-23,9,sku-0 +2020-03-01,9,sku-0 +2020-03-08,9,sku-0 +2020-03-15,9,sku-0 +2020-03-22,8,sku-0 +2020-03-29,9,sku-0 +2020-04-05,9,sku-0 +2020-04-12,9,sku-0 +2020-04-19,9,sku-0 +2020-04-26,9,sku-0 +2020-05-03,9,sku-0 +2020-05-10,9,sku-0 +2020-05-17,9,sku-0 +2020-05-24,9,sku-0 +2020-05-31,9,sku-0 +2020-06-07,9,sku-0 +2020-06-14,20,sku-0 +2020-06-21,9,sku-0 +2020-06-28,9,sku-0 +2020-07-05,9,sku-0 +2020-07-12,9,sku-0 +2020-07-19,4,sku-0 +2020-07-26,13,sku-0 +2020-08-02,9,sku-0 +2020-08-09,9,sku-0 +2020-08-16,9,sku-0 +2020-08-23,21,sku-0 +2020-08-30,4,sku-0 +2020-09-06,9,sku-0 +2020-09-13,2,sku-0 +2020-09-20,15,sku-0 +2020-09-27,4,sku-0 +2020-10-04,9,sku-0 +2020-10-11,4,sku-0 +2020-10-18,4,sku-0 +2020-10-25,17,sku-0 +2020-11-01,16,sku-0 +2020-11-08,9,sku-0 +2020-11-15,9,sku-0 +2020-11-22,22,sku-0 +2020-11-29,1,sku-0 +2020-12-06,6,sku-0 +2020-12-13,6,sku-0 +2020-12-20,25,sku-0 +2020-12-27,10,sku-0 +2021-01-03,9,sku-0 +2021-01-10,25,sku-0 +2021-01-17,40,sku-0 +2021-01-24,40,sku-0 +2021-01-31,6,sku-0 +2021-02-07,20,sku-0 +2021-02-14,35,sku-0 +2021-02-21,9,sku-0 +2021-02-28,20,sku-0 +2021-03-07,9,sku-0 +2021-03-14,9,sku-0 +2021-03-21,50,sku-0 +2021-03-28,35,sku-0 +2021-04-04,9,sku-0 +2021-04-11,20,sku-0 +2021-04-18,20,sku-0 +2021-04-25,10,sku-0 +2021-05-02,20,sku-0 +2021-05-09,9,sku-0 +2021-05-16,9,sku-0 +2021-05-23,9,sku-0 +2021-05-30,9,sku-0 +2021-06-06,9,sku-0 +2021-06-13,9,sku-0 +2021-06-20,9,sku-0 +2021-06-27,9,sku-0 +2021-07-04,9,sku-0 +2021-07-11,5,sku-0 +2021-07-18,5,sku-0 +2021-07-25,9,sku-0 +2021-08-01,9,sku-0 +2021-08-08,9,sku-0 +2021-08-15,9,sku-0 +2021-08-22,20,sku-0 +2021-08-29,20,sku-0 +2021-09-05,2,sku-0 +2021-09-12,9,sku-0 +2021-09-19,9,sku-0 +2021-09-26,10,sku-0 +2021-10-03,9,sku-0 +2021-10-10,9,sku-0 +2021-10-17,1,sku-0 +2021-10-24,5,sku-0 +2021-10-31,10,sku-0 +2021-11-07,25,sku-0 +2021-11-14,22,sku-0 +2021-11-21,23,sku-0 +2021-11-28,7,sku-0 +2021-12-05,9,sku-0 +2021-12-12,12,sku-0 +2021-12-19,18,sku-0 +2021-12-26,9,sku-0 +2022-01-02,35,sku-0 +2022-01-09,9,sku-0 +2022-01-16,20,sku-0 +2022-01-23,20,sku-0 +2022-01-30,10,sku-0 +2022-02-06,10,sku-0 +2022-02-13,22,sku-0 +2022-02-20,49,sku-0 +2022-02-27,10,sku-0 +2022-03-06,9,sku-0 +2022-03-13,9,sku-0 +2022-03-20,9,sku-0 +2022-03-27,9,sku-0 +2022-04-03,9,sku-0 +2022-04-10,10,sku-0 +2022-04-17,9,sku-0 +2022-04-24,20,sku-0 +2022-05-01,9,sku-0 +2022-05-08,9,sku-0 +2022-05-15,9,sku-0 +2022-05-22,50,sku-0 +2022-05-29,9,sku-0 +2022-06-05,9,sku-0 +2022-06-12,30,sku-0 +2022-06-19,10,sku-0 +2022-06-26,10,sku-0 +2022-07-03,5,sku-0 +2022-07-10,30,sku-0 +2022-07-17,20,sku-0 +2022-07-24,50,sku-0 +2022-07-31,9,sku-0 +2022-08-07,100,sku-0 +2022-08-14,34,sku-0 +2022-08-21,9,sku-0 +2022-08-28,10,sku-0 +2022-09-04,42,sku-0 +2022-09-11,30,sku-0 +2022-09-18,29,sku-0 +2022-09-25,8,sku-0 +2022-10-02,10,sku-0 +2022-10-09,9,sku-0 +2022-10-16,9,sku-0 +2022-10-23,9,sku-0 +2022-10-30,9,sku-0 +2022-11-06,9,sku-0 +2022-11-13,9,sku-0 +2022-11-20,20,sku-0 +2022-11-27,10,sku-0 +2022-12-04,9,sku-0 +2022-12-11,9,sku-0 +2022-12-18,35,sku-0 +2022-12-25,60,sku-0 +2023-01-01,15,sku-0 +2023-01-08,5,sku-0 +2023-01-15,70,sku-0 +2023-01-22,20,sku-0 +2023-01-29,1,sku-0 +2023-02-05,9,sku-0 +2023-02-12,9,sku-0 +2023-02-19,100,sku-0 +2023-02-26,40,sku-0 +2023-03-05,40,sku-0 +2023-03-12,9,sku-0 +2023-03-19,5,sku-0 +2023-03-26,9,sku-0 +2023-04-02,30,sku-0 +2023-04-09,50,sku-0 +2023-04-16,9,sku-0 +2023-04-23,20,sku-0 +2018-05-06,5,sku-1 +2018-05-13,20,sku-1 +2018-05-20,31,sku-1 +2018-05-27,10,sku-1 +2018-06-03,60,sku-1 +2018-06-10,31,sku-1 +2018-06-17,31,sku-1 +2018-06-24,5,sku-1 +2018-07-01,31,sku-1 +2018-07-08,30,sku-1 +2018-07-15,31,sku-1 +2018-07-22,31,sku-1 +2018-07-29,31,sku-1 +2018-08-05,10,sku-1 +2018-08-12,31,sku-1 +2018-08-19,31,sku-1 +2018-08-26,15,sku-1 +2018-09-02,31,sku-1 +2018-09-09,31,sku-1 +2018-09-16,25,sku-1 +2018-09-23,5,sku-1 +2018-09-30,45,sku-1 +2018-10-07,20,sku-1 +2018-10-14,25,sku-1 +2018-10-21,31,sku-1 +2018-10-28,40,sku-1 +2018-11-04,20,sku-1 +2018-11-11,31,sku-1 +2018-11-18,31,sku-1 +2018-11-25,31,sku-1 +2018-12-02,40,sku-1 +2018-12-09,10,sku-1 +2018-12-16,45,sku-1 +2018-12-23,31,sku-1 +2018-12-30,15,sku-1 +2019-01-06,31,sku-1 +2019-01-13,35,sku-1 +2019-01-20,20,sku-1 +2019-01-27,15,sku-1 +2019-02-03,25,sku-1 +2019-02-10,35,sku-1 +2019-02-17,31,sku-1 +2019-02-24,50,sku-1 +2019-03-03,60,sku-1 +2019-03-10,100,sku-1 +2019-03-17,31,sku-1 +2019-03-24,31,sku-1 +2019-03-31,55,sku-1 +2019-04-07,35,sku-1 +2019-04-14,80,sku-1 +2019-04-21,10,sku-1 +2019-04-28,10,sku-1 +2019-05-05,5,sku-1 +2019-05-12,30,sku-1 +2019-05-19,60,sku-1 +2019-05-26,35,sku-1 +2019-06-02,15,sku-1 +2019-06-09,31,sku-1 +2019-06-16,31,sku-1 +2019-06-23,31,sku-1 +2019-06-30,85,sku-1 +2019-07-07,20,sku-1 +2019-07-14,25,sku-1 +2019-07-21,15,sku-1 +2019-07-28,31,sku-1 +2019-08-04,15,sku-1 +2019-08-11,20,sku-1 +2019-08-18,50,sku-1 +2019-08-25,31,sku-1 +2019-09-01,40,sku-1 +2019-09-08,10,sku-1 +2019-09-15,31,sku-1 +2019-09-22,31,sku-1 +2019-09-29,31,sku-1 +2019-10-06,75,sku-1 +2019-10-13,31,sku-1 +2019-10-20,10,sku-1 +2019-10-27,31,sku-1 +2019-11-03,45,sku-1 +2019-11-10,20,sku-1 +2019-11-17,30,sku-1 +2019-11-24,30,sku-1 +2019-12-01,60,sku-1 +2019-12-08,10,sku-1 +2019-12-15,14,sku-1 +2019-12-22,14,sku-1 +2019-12-29,40,sku-1 +2020-01-05,31,sku-1 +2020-01-12,10,sku-1 +2020-01-19,15,sku-1 +2020-01-26,31,sku-1 +2020-02-02,20,sku-1 +2020-02-09,22,sku-1 +2020-02-16,50,sku-1 +2020-02-23,100,sku-1 +2020-03-01,31,sku-1 +2020-03-08,20,sku-1 +2020-03-15,35,sku-1 +2020-03-22,114,sku-1 +2020-03-29,15,sku-1 +2020-04-05,25,sku-1 +2020-04-12,31,sku-1 +2020-04-19,31,sku-1 +2020-04-26,31,sku-1 +2020-05-03,31,sku-1 +2020-05-10,31,sku-1 +2020-05-17,15,sku-1 +2020-05-24,31,sku-1 +2020-05-31,15,sku-1 +2020-06-07,60,sku-1 +2020-06-14,32,sku-1 +2020-06-21,75,sku-1 +2020-06-28,10,sku-1 +2020-07-05,45,sku-1 +2020-07-12,90,sku-1 +2020-07-19,15,sku-1 +2020-07-26,135,sku-1 +2020-08-02,31,sku-1 +2020-08-09,31,sku-1 +2020-08-16,31,sku-1 +2020-08-23,250,sku-1 +2020-08-30,31,sku-1 +2020-09-06,31,sku-1 +2020-09-13,5,sku-1 +2020-09-20,31,sku-1 +2020-09-27,60,sku-1 +2020-10-04,15,sku-1 +2020-10-11,10,sku-1 +2020-10-18,35,sku-1 +2020-10-25,31,sku-1 +2020-11-01,15,sku-1 +2020-11-08,25,sku-1 +2020-11-15,80,sku-1 +2020-11-22,45,sku-1 +2020-11-29,25,sku-1 +2020-12-06,25,sku-1 +2020-12-13,10,sku-1 +2020-12-20,10,sku-1 +2020-12-27,15,sku-1 +2021-01-03,31,sku-1 +2021-01-10,31,sku-1 +2021-01-17,31,sku-1 +2021-01-24,15,sku-1 +2021-01-31,35,sku-1 +2021-02-07,31,sku-1 +2021-02-14,31,sku-1 +2021-02-21,31,sku-1 +2021-02-28,5,sku-1 +2021-03-07,45,sku-1 +2021-03-14,35,sku-1 +2021-03-21,57,sku-1 +2021-03-28,250,sku-1 +2021-04-04,31,sku-1 +2021-04-11,31,sku-1 +2021-04-18,31,sku-1 +2021-04-25,40,sku-1 +2021-05-02,145,sku-1 +2021-05-09,40,sku-1 +2021-05-16,31,sku-1 +2021-05-23,20,sku-1 +2021-05-30,31,sku-1 +2021-06-06,40,sku-1 +2021-06-13,30,sku-1 +2021-06-20,10,sku-1 +2021-06-27,60,sku-1 +2021-07-04,31,sku-1 +2021-07-11,100,sku-1 +2021-07-18,30,sku-1 +2021-07-25,31,sku-1 +2021-08-01,31,sku-1 +2021-08-08,31,sku-1 +2021-08-15,31,sku-1 +2021-08-22,50,sku-1 +2021-08-29,120,sku-1 +2021-09-05,100,sku-1 +2021-09-12,100,sku-1 +2021-09-19,31,sku-1 +2021-09-26,80,sku-1 +2021-10-03,31,sku-1 +2021-10-10,31,sku-1 +2021-10-17,31,sku-1 +2021-10-24,31,sku-1 +2021-10-31,20,sku-1 +2021-11-07,31,sku-1 +2021-11-14,31,sku-1 +2021-11-21,31,sku-1 +2021-11-28,28,sku-1 +2021-12-05,150,sku-1 +2021-12-12,39,sku-1 +2021-12-19,31,sku-1 +2021-12-26,31,sku-1 +2022-01-02,15,sku-1 +2022-01-09,31,sku-1 +2022-01-16,31,sku-1 +2022-01-23,95,sku-1 +2022-01-30,115,sku-1 +2022-02-06,31,sku-1 +2022-02-13,75,sku-1 +2022-02-20,122,sku-1 +2022-02-27,31,sku-1 +2022-03-06,31,sku-1 +2022-03-13,31,sku-1 +2022-03-20,31,sku-1 +2022-03-27,31,sku-1 +2022-04-03,31,sku-1 +2022-04-10,50,sku-1 +2022-04-17,40,sku-1 +2022-04-24,80,sku-1 +2022-05-01,20,sku-1 +2022-05-08,31,sku-1 +2022-05-15,20,sku-1 +2022-05-22,31,sku-1 +2022-05-29,31,sku-1 +2022-06-05,125,sku-1 +2022-06-12,250,sku-1 +2022-06-19,100,sku-1 +2022-06-26,31,sku-1 +2022-07-03,31,sku-1 +2022-07-10,31,sku-1 +2022-07-17,32,sku-1 +2022-07-24,31,sku-1 +2022-07-31,31,sku-1 +2022-08-07,90,sku-1 +2022-08-14,57,sku-1 +2022-08-21,31,sku-1 +2022-08-28,71,sku-1 +2022-09-04,138,sku-1 +2022-09-11,100,sku-1 +2022-09-18,30,sku-1 +2022-09-25,46,sku-1 +2022-10-02,50,sku-1 +2022-10-09,200,sku-1 +2022-10-16,31,sku-1 +2022-10-23,31,sku-1 +2022-10-30,31,sku-1 +2022-11-06,31,sku-1 +2022-11-13,31,sku-1 +2022-11-20,31,sku-1 +2022-11-27,31,sku-1 +2022-12-04,31,sku-1 +2022-12-11,90,sku-1 +2022-12-18,31,sku-1 +2022-12-25,60,sku-1 +2023-01-01,50,sku-1 +2023-01-08,10,sku-1 +2023-01-15,31,sku-1 +2023-01-22,50,sku-1 +2023-01-29,31,sku-1 +2023-02-05,150,sku-1 +2023-02-12,200,sku-1 +2023-02-19,80,sku-1 +2023-02-26,150,sku-1 +2023-03-05,31,sku-1 +2023-03-12,31,sku-1 +2023-03-19,90,sku-1 +2023-03-26,55,sku-1 +2023-04-02,20,sku-1 +2023-04-09,250,sku-1 +2018-05-06,11,sku-2 +2018-05-13,6,sku-2 +2018-05-20,4,sku-2 +2018-05-27,8,sku-2 +2018-06-03,1,sku-2 +2018-06-10,3,sku-2 +2018-06-17,0,sku-2 +2018-06-24,8,sku-2 +2018-07-01,3,sku-2 +2018-07-08,0,sku-2 +2018-07-15,2,sku-2 +2018-07-22,2,sku-2 +2018-07-29,0,sku-2 +2018-08-05,10,sku-2 +2018-08-12,3,sku-2 +2018-08-19,9,sku-2 +2018-08-26,5,sku-2 +2018-09-02,0,sku-2 +2018-09-09,0,sku-2 +2018-09-16,2,sku-2 +2018-09-23,10,sku-2 +2018-09-30,2,sku-2 +2018-10-07,10,sku-2 +2018-10-14,0,sku-2 +2018-10-21,0,sku-2 +2018-10-28,10,sku-2 +2018-11-04,0,sku-2 +2018-11-11,0,sku-2 +2018-11-18,7,sku-2 +2018-11-25,7,sku-2 +2018-12-02,13,sku-2 +2018-12-09,1,sku-2 +2018-12-16,1,sku-2 +2018-12-23,4,sku-2 +2018-12-30,10,sku-2 +2019-01-06,0,sku-2 +2019-01-13,6,sku-2 +2019-01-20,3,sku-2 +2019-01-27,2,sku-2 +2019-02-03,3,sku-2 +2019-02-10,5,sku-2 +2019-02-17,0,sku-2 +2019-02-24,4,sku-2 +2019-03-03,5,sku-2 +2019-03-10,0,sku-2 +2019-03-17,1,sku-2 +2019-03-24,3,sku-2 +2019-03-31,8,sku-2 +2019-04-07,6,sku-2 +2019-04-14,7,sku-2 +2019-04-21,3,sku-2 +2019-04-28,3,sku-2 +2019-05-05,2,sku-2 +2019-05-12,6,sku-2 +2019-05-19,6,sku-2 +2019-05-26,5,sku-2 +2019-06-02,0,sku-2 +2019-06-09,0,sku-2 +2019-06-16,0,sku-2 +2019-06-23,10,sku-2 +2019-06-30,4,sku-2 +2019-07-07,4,sku-2 +2019-07-14,4,sku-2 +2019-07-21,1,sku-2 +2019-07-28,0,sku-2 +2019-08-04,13,sku-2 +2019-08-11,8,sku-2 +2019-08-18,5,sku-2 +2019-08-25,4,sku-2 +2019-09-01,8,sku-2 +2019-09-08,6,sku-2 +2019-09-15,3,sku-2 +2019-09-22,1,sku-2 +2019-09-29,0,sku-2 +2019-10-06,7,sku-2 +2019-10-13,7,sku-2 +2019-10-20,20,sku-2 +2019-10-27,0,sku-2 +2019-11-03,4,sku-2 +2019-11-10,3,sku-2 +2019-11-17,3,sku-2 +2019-11-24,10,sku-2 +2019-12-01,12,sku-2 +2019-12-08,1,sku-2 +2019-12-15,5,sku-2 +2019-12-22,5,sku-2 +2019-12-29,4,sku-2 +2020-01-05,0,sku-2 +2020-01-12,10,sku-2 +2020-01-19,1,sku-2 +2020-01-26,4,sku-2 +2020-02-02,6,sku-2 +2020-02-09,5,sku-2 +2020-02-16,20,sku-2 +2020-02-23,0,sku-2 +2020-03-01,0,sku-2 +2020-03-08,0,sku-2 +2020-03-15,0,sku-2 +2020-03-22,5,sku-2 +2020-03-29,0,sku-2 +2020-04-05,0,sku-2 +2020-04-12,0,sku-2 +2020-04-19,0,sku-2 +2020-04-26,0,sku-2 +2020-05-03,0,sku-2 +2020-05-10,0,sku-2 +2020-05-17,0,sku-2 +2020-05-24,0,sku-2 +2020-05-31,0,sku-2 +2020-06-07,0,sku-2 +2020-06-14,20,sku-2 +2020-06-21,25,sku-2 +2020-06-28,0,sku-2 +2020-07-05,0,sku-2 +2020-07-12,0,sku-2 +2020-07-19,0,sku-2 +2020-07-26,30,sku-2 +2020-08-02,0,sku-2 +2020-08-09,0,sku-2 +2020-08-16,0,sku-2 +2020-08-23,55,sku-2 +2020-08-30,10,sku-2 +2020-09-06,15,sku-2 +2020-09-13,10,sku-2 +2020-09-20,20,sku-2 +2020-09-27,0,sku-2 +2020-10-04,0,sku-2 +2020-10-11,20,sku-2 +2020-10-18,10,sku-2 +2020-10-25,50,sku-2 +2020-11-01,0,sku-2 +2020-11-08,0,sku-2 +2020-11-15,20,sku-2 +2020-11-22,20,sku-2 +2020-11-29,20,sku-2 +2020-12-06,0,sku-2 +2020-12-13,0,sku-2 +2020-12-20,20,sku-2 +2020-12-27,0,sku-2 +2021-01-03,0,sku-2 +2021-01-10,0,sku-2 +2021-01-17,100,sku-2 +2021-01-24,0,sku-2 +2021-01-31,100,sku-2 +2021-02-07,0,sku-2 +2021-02-14,0,sku-2 +2021-02-21,0,sku-2 +2021-02-28,0,sku-2 +2021-03-07,0,sku-2 +2021-03-14,20,sku-2 +2021-03-21,3,sku-2 +2021-03-28,55,sku-2 +2021-04-04,0,sku-2 +2021-04-11,100,sku-2 +2021-04-18,0,sku-2 +2021-04-25,10,sku-2 +2021-05-02,40,sku-2 +2021-05-09,0,sku-2 +2021-05-16,0,sku-2 +2021-05-23,0,sku-2 +2021-05-30,30,sku-2 +2021-06-06,10,sku-2 +2021-06-13,5,sku-2 +2021-06-20,10,sku-2 +2021-06-27,30,sku-2 +2021-07-04,0,sku-2 +2021-07-11,10,sku-2 +2021-07-18,30,sku-2 +2021-07-25,0,sku-2 +2021-08-01,0,sku-2 +2021-08-08,0,sku-2 +2021-08-15,0,sku-2 +2021-08-22,35,sku-2 +2021-08-29,10,sku-2 +2021-09-05,0,sku-2 +2021-09-12,50,sku-2 +2021-09-19,0,sku-2 +2021-09-26,10,sku-2 +2021-10-03,0,sku-2 +2021-10-10,15,sku-2 +2021-10-17,20,sku-2 +2021-10-24,20,sku-2 +2021-10-31,45,sku-2 +2021-11-07,55,sku-2 +2021-11-14,27,sku-2 +2021-11-21,16,sku-2 +2021-11-28,18,sku-2 +2021-12-05,0,sku-2 +2021-12-12,0,sku-2 +2021-12-19,15,sku-2 +2021-12-26,0,sku-2 +2022-01-02,22,sku-2 +2022-01-09,0,sku-2 +2022-01-16,100,sku-2 +2022-01-23,34,sku-2 +2022-01-30,5,sku-2 +2022-02-06,70,sku-2 +2022-02-13,40,sku-2 +2022-02-20,100,sku-2 +2022-02-27,0,sku-2 +2022-03-06,50,sku-2 +2022-03-13,50,sku-2 +2022-03-20,0,sku-2 +2022-03-27,10,sku-2 +2022-04-03,0,sku-2 +2022-04-10,50,sku-2 +2022-04-17,20,sku-2 +2022-04-24,80,sku-2 +2022-05-01,30,sku-2 +2022-05-08,0,sku-2 +2022-05-15,30,sku-2 +2022-05-22,0,sku-2 +2022-05-29,20,sku-2 +2022-06-05,50,sku-2 +2022-06-12,0,sku-2 +2022-06-19,44,sku-2 +2022-06-26,50,sku-2 +2022-07-03,0,sku-2 +2022-07-10,30,sku-2 +2022-07-17,30,sku-2 +2022-07-24,6,sku-2 +2022-07-31,35,sku-2 +2022-08-07,50,sku-2 +2022-08-14,60,sku-2 +2022-08-21,0,sku-2 +2022-08-28,30,sku-2 +2022-09-04,70,sku-2 +2022-09-11,100,sku-2 +2022-09-18,0,sku-2 +2022-09-25,0,sku-2 +2022-10-02,4,sku-2 +2022-10-09,0,sku-2 +2022-10-16,0,sku-2 +2022-10-23,0,sku-2 +2022-10-30,50,sku-2 +2022-11-06,30,sku-2 +2022-11-13,0,sku-2 +2022-11-20,70,sku-2 +2022-11-27,100,sku-2 +2022-12-04,50,sku-2 +2018-05-06,1,sku-3 +2018-05-13,4,sku-3 +2018-05-20,5,sku-3 +2018-05-27,5,sku-3 +2018-06-03,1,sku-3 +2018-06-10,1,sku-3 +2018-06-17,0,sku-3 +2018-06-24,2,sku-3 +2018-07-01,0,sku-3 +2018-07-08,0,sku-3 +2018-07-15,19,sku-3 +2018-07-22,9,sku-3 +2018-07-29,1,sku-3 +2018-08-05,2,sku-3 +2018-08-12,0,sku-3 +2018-08-19,0,sku-3 +2018-08-26,7,sku-3 +2018-09-02,14,sku-3 +2018-09-09,7,sku-3 +2018-09-16,6,sku-3 +2018-09-23,5,sku-3 +2018-09-30,3,sku-3 +2018-10-07,12,sku-3 +2018-10-14,8,sku-3 +2018-10-21,4,sku-3 +2018-10-28,7,sku-3 +2018-11-04,7,sku-3 +2018-11-11,0,sku-3 +2018-11-18,11,sku-3 +2018-11-25,2,sku-3 +2018-12-02,0,sku-3 +2018-12-09,1,sku-3 +2018-12-16,1,sku-3 +2018-12-23,0,sku-3 +2018-12-30,6,sku-3 +2019-01-06,0,sku-3 +2019-01-13,3,sku-3 +2019-01-20,6,sku-3 +2019-01-27,0,sku-3 +2019-02-03,1,sku-3 +2019-02-10,0,sku-3 +2019-02-17,0,sku-3 +2019-02-24,2,sku-3 +2019-03-03,5,sku-3 +2019-03-10,9,sku-3 +2019-03-17,12,sku-3 +2019-03-24,11,sku-3 +2019-03-31,0,sku-3 +2019-04-07,12,sku-3 +2019-04-14,17,sku-3 +2019-04-21,11,sku-3 +2019-04-28,2,sku-3 +2019-05-05,1,sku-3 +2019-05-12,0,sku-3 +2019-05-19,7,sku-3 +2019-05-26,26,sku-3 +2019-06-02,1,sku-3 +2019-06-09,0,sku-3 +2019-06-16,0,sku-3 +2019-06-23,7,sku-3 +2019-06-30,11,sku-3 +2019-07-07,7,sku-3 +2019-07-14,10,sku-3 +2019-07-21,0,sku-3 +2019-07-28,0,sku-3 +2019-08-04,16,sku-3 +2019-08-11,5,sku-3 +2019-08-18,15,sku-3 +2019-08-25,4,sku-3 +2019-09-01,1,sku-3 +2019-09-08,0,sku-3 +2019-09-15,5,sku-3 +2019-09-22,3,sku-3 +2019-09-29,0,sku-3 +2019-10-06,10,sku-3 +2019-10-13,0,sku-3 +2019-10-20,0,sku-3 +2019-10-27,0,sku-3 +2019-11-03,0,sku-3 +2019-11-10,0,sku-3 +2019-11-17,2,sku-3 +2019-11-24,0,sku-3 +2019-12-01,19,sku-3 +2019-12-08,1,sku-3 +2019-12-15,5,sku-3 +2019-12-22,5,sku-3 +2019-12-29,0,sku-3 +2020-01-05,0,sku-3 +2020-01-12,0,sku-3 +2020-01-19,0,sku-3 +2020-01-26,0,sku-3 +2020-02-02,3,sku-3 +2020-02-09,12,sku-3 +2020-02-16,0,sku-3 +2020-02-23,5,sku-3 +2020-03-01,10,sku-3 +2020-03-08,5,sku-3 +2020-03-15,11,sku-3 +2020-03-22,12,sku-3 +2020-03-29,0,sku-3 +2020-04-05,6,sku-3 +2020-04-12,0,sku-3 +2020-04-19,0,sku-3 +2020-04-26,0,sku-3 +2020-05-03,0,sku-3 +2020-05-10,0,sku-3 +2020-05-17,0,sku-3 +2020-05-24,0,sku-3 +2020-05-31,0,sku-3 +2020-06-07,11,sku-3 +2020-06-14,6,sku-3 +2020-06-21,8,sku-3 +2020-06-28,0,sku-3 +2020-07-05,0,sku-3 +2020-07-12,41,sku-3 +2020-07-19,0,sku-3 +2020-07-26,4,sku-3 +2020-08-02,0,sku-3 +2020-08-09,0,sku-3 +2020-08-16,0,sku-3 +2020-08-23,47,sku-3 +2020-08-30,3,sku-3 +2020-09-06,31,sku-3 +2020-09-13,0,sku-3 +2020-09-20,2,sku-3 +2020-09-27,0,sku-3 +2020-10-04,0,sku-3 +2020-10-11,6,sku-3 +2020-10-18,9,sku-3 +2020-10-25,8,sku-3 +2020-11-01,4,sku-3 +2020-11-08,2,sku-3 +2020-11-15,30,sku-3 +2020-11-22,60,sku-3 +2020-11-29,68,sku-3 +2020-12-06,0,sku-3 +2020-12-13,0,sku-3 +2020-12-20,0,sku-3 +2020-12-27,0,sku-3 +2021-01-03,0,sku-3 +2021-01-10,0,sku-3 +2021-01-17,0,sku-3 +2021-01-24,0,sku-3 +2021-01-31,0,sku-3 +2021-02-07,6,sku-3 +2021-02-14,6,sku-3 +2021-02-21,15,sku-3 +2021-02-28,30,sku-3 +2021-03-07,0,sku-3 +2021-03-14,5,sku-3 +2021-03-21,20,sku-3 +2021-03-28,0,sku-3 +2021-04-04,0,sku-3 +2021-04-11,0,sku-3 +2021-04-18,0,sku-3 +2021-04-25,10,sku-3 +2021-05-02,10,sku-3 +2021-05-09,0,sku-3 +2021-05-16,0,sku-3 +2021-05-23,0,sku-3 +2021-05-30,0,sku-3 +2021-06-06,0,sku-3 +2021-06-13,0,sku-3 +2021-06-20,0,sku-3 +2021-06-27,0,sku-3 +2021-07-04,0,sku-3 +2021-07-11,0,sku-3 +2021-07-18,2,sku-3 +2021-07-25,0,sku-3 +2021-08-01,0,sku-3 +2021-08-08,0,sku-3 +2021-08-15,0,sku-3 +2021-08-22,0,sku-3 +2021-08-29,0,sku-3 +2021-09-05,0,sku-3 +2021-09-12,5,sku-3 +2021-09-19,0,sku-3 +2021-09-26,0,sku-3 +2021-10-03,0,sku-3 +2021-10-10,0,sku-3 +2021-10-17,10,sku-3 +2021-10-24,3,sku-3 +2021-10-31,2,sku-3 +2021-11-07,0,sku-3 +2021-11-14,0,sku-3 +2021-11-21,15,sku-3 +2021-11-28,7,sku-3 +2021-12-05,17,sku-3 +2021-12-12,0,sku-3 +2021-12-19,0,sku-3 +2021-12-26,0,sku-3 +2022-01-02,5,sku-3 +2022-01-09,10,sku-3 +2022-01-16,0,sku-3 +2022-01-23,5,sku-3 +2022-01-30,10,sku-3 +2022-02-06,5,sku-3 +2022-02-13,25,sku-3 +2022-02-20,0,sku-3 +2022-02-27,0,sku-3 +2022-03-06,0,sku-3 +2022-03-13,2,sku-3 +2022-03-20,45,sku-3 +2022-03-27,25,sku-3 +2022-04-03,0,sku-3 +2022-04-10,0,sku-3 +2022-04-17,10,sku-3 +2022-04-24,3,sku-3 +2022-05-01,9,sku-3 +2022-05-08,0,sku-3 +2022-05-15,3,sku-3 +2022-05-22,5,sku-3 +2022-05-29,30,sku-3 +2022-06-05,0,sku-3 +2022-06-12,45,sku-3 +2022-06-19,32,sku-3 +2022-06-26,10,sku-3 +2022-07-03,0,sku-3 +2022-07-10,10,sku-3 +2022-07-17,30,sku-3 +2022-07-24,25,sku-3 +2022-07-31,7,sku-3 +2022-08-07,20,sku-3 +2022-08-14,32,sku-3 +2022-08-21,25,sku-3 +2022-08-28,0,sku-3 +2022-09-04,3,sku-3 +2022-09-11,0,sku-3 +2022-09-18,0,sku-3 +2022-09-25,25,sku-3 +2022-10-02,0,sku-3 +2022-10-09,0,sku-3 +2022-10-16,0,sku-3 +2022-10-23,0,sku-3 +2022-10-30,0,sku-3 +2022-11-06,0,sku-3 +2022-11-13,0,sku-3 +2022-11-20,0,sku-3 +2022-11-27,0,sku-3 +2022-12-04,0,sku-3 +2022-12-11,0,sku-3 +2022-12-18,0,sku-3 +2022-12-25,0,sku-3 +2023-01-01,0,sku-3 +2023-01-08,0,sku-3 +2023-01-15,0,sku-3 +2023-01-22,0,sku-3 +2023-01-29,0,sku-3 +2023-02-05,0,sku-3 +2023-02-12,0,sku-3 +2023-02-19,0,sku-3 +2023-02-26,0,sku-3 +2023-03-05,0,sku-3 +2023-03-12,0,sku-3 +2023-03-19,0,sku-3 +2023-03-26,0,sku-3 +2023-04-02,0,sku-3 +2023-04-09,0,sku-3 +2023-04-16,10,sku-3 +2023-04-23,12,sku-3 +2018-05-06,2,sku-4 +2018-05-13,12,sku-4 +2018-05-20,6,sku-4 +2018-05-27,9,sku-4 +2018-06-03,5,sku-4 +2018-06-10,2,sku-4 +2018-06-17,0,sku-4 +2018-06-24,3,sku-4 +2018-07-01,1,sku-4 +2018-07-08,6,sku-4 +2018-07-15,9,sku-4 +2018-07-22,9,sku-4 +2018-07-29,9,sku-4 +2018-08-05,8,sku-4 +2018-08-12,1,sku-4 +2018-08-19,0,sku-4 +2018-08-26,2,sku-4 +2018-09-02,11,sku-4 +2018-09-09,9,sku-4 +2018-09-16,4,sku-4 +2018-09-23,24,sku-4 +2018-09-30,13,sku-4 +2018-10-07,0,sku-4 +2018-10-14,0,sku-4 +2018-10-21,0,sku-4 +2018-10-28,6,sku-4 +2018-11-04,25,sku-4 +2018-11-11,0,sku-4 +2018-11-18,12,sku-4 +2018-11-25,5,sku-4 +2018-12-02,11,sku-4 +2018-12-09,4,sku-4 +2018-12-16,2,sku-4 +2018-12-23,4,sku-4 +2018-12-30,0,sku-4 +2019-01-06,0,sku-4 +2019-01-13,4,sku-4 +2019-01-20,9,sku-4 +2019-01-27,0,sku-4 +2019-02-03,15,sku-4 +2019-02-10,4,sku-4 +2019-02-17,0,sku-4 +2019-02-24,24,sku-4 +2019-03-03,3,sku-4 +2019-03-10,1,sku-4 +2019-03-17,5,sku-4 +2019-03-24,13,sku-4 +2019-03-31,20,sku-4 +2019-04-07,0,sku-4 +2019-04-14,0,sku-4 +2019-04-21,0,sku-4 +2019-04-28,0,sku-4 +2019-05-05,0,sku-4 +2019-05-12,0,sku-4 +2019-05-19,2,sku-4 +2019-05-26,8,sku-4 +2019-06-02,0,sku-4 +2019-06-09,0,sku-4 +2019-06-16,0,sku-4 +2019-06-23,0,sku-4 +2019-06-30,2,sku-4 +2019-07-07,8,sku-4 +2019-07-14,2,sku-4 +2019-07-21,10,sku-4 +2019-07-28,0,sku-4 +2019-08-04,12,sku-4 +2019-08-11,2,sku-4 +2019-08-18,5,sku-4 +2019-08-25,0,sku-4 +2019-09-01,7,sku-4 +2019-09-08,13,sku-4 +2019-09-15,0,sku-4 +2019-09-22,0,sku-4 +2019-09-29,0,sku-4 +2019-10-06,6,sku-4 +2019-10-13,2,sku-4 +2019-10-20,10,sku-4 +2019-10-27,0,sku-4 +2019-11-03,27,sku-4 +2019-11-10,0,sku-4 +2019-11-17,12,sku-4 +2019-11-24,9,sku-4 +2019-12-01,22,sku-4 +2019-12-08,4,sku-4 +2019-12-15,0,sku-4 +2019-12-22,0,sku-4 +2019-12-29,22,sku-4 +2020-01-05,0,sku-4 +2020-01-12,5,sku-4 +2020-01-19,4,sku-4 +2020-01-26,9,sku-4 +2020-02-02,10,sku-4 +2020-02-09,8,sku-4 +2020-02-16,5,sku-4 +2020-02-23,0,sku-4 +2020-03-01,30,sku-4 +2020-03-08,0,sku-4 +2020-03-15,10,sku-4 +2020-03-22,8,sku-4 +2020-03-29,16,sku-4 +2020-04-05,10,sku-4 +2020-04-12,0,sku-4 +2020-04-19,3,sku-4 +2020-04-26,10,sku-4 +2020-05-03,0,sku-4 +2020-05-10,0,sku-4 +2020-05-17,4,sku-4 +2020-05-24,2,sku-4 +2020-05-31,4,sku-4 +2020-06-07,11,sku-4 +2020-06-14,10,sku-4 +2020-06-21,5,sku-4 +2020-06-28,10,sku-4 +2020-07-05,2,sku-4 +2020-07-12,11,sku-4 +2020-07-19,3,sku-4 +2020-07-26,44,sku-4 +2020-08-02,0,sku-4 +2020-08-09,0,sku-4 +2020-08-16,0,sku-4 +2020-08-23,140,sku-4 +2020-08-30,40,sku-4 +2020-09-06,0,sku-4 +2020-09-13,0,sku-4 +2020-09-20,24,sku-4 +2020-09-27,12,sku-4 +2020-10-04,2,sku-4 +2020-10-11,3,sku-4 +2020-10-18,13,sku-4 +2020-10-25,13,sku-4 +2020-11-01,14,sku-4 +2020-11-08,3,sku-4 +2020-11-15,10,sku-4 +2020-11-22,20,sku-4 +2020-11-29,0,sku-4 +2020-12-06,0,sku-4 +2020-12-13,0,sku-4 +2020-12-20,0,sku-4 +2020-12-27,0,sku-4 +2021-01-03,0,sku-4 +2021-01-10,0,sku-4 +2021-01-17,0,sku-4 +2021-01-24,0,sku-4 +2021-01-31,0,sku-4 +2021-02-07,0,sku-4 +2021-02-14,60,sku-4 +2021-02-21,0,sku-4 +2021-02-28,0,sku-4 +2021-03-07,0,sku-4 +2021-03-14,0,sku-4 +2021-03-21,10,sku-4 +2021-03-28,0,sku-4 +2021-04-04,0,sku-4 +2021-04-11,0,sku-4 +2021-04-18,0,sku-4 +2021-04-25,30,sku-4 +2021-05-02,9,sku-4 +2021-05-09,7,sku-4 +2021-05-16,0,sku-4 +2021-05-23,3,sku-4 +2021-05-30,5,sku-4 +2021-06-06,3,sku-4 +2021-06-13,15,sku-4 +2021-06-20,10,sku-4 +2021-06-27,32,sku-4 +2021-07-04,0,sku-4 +2021-07-11,10,sku-4 +2021-07-18,10,sku-4 +2021-07-25,0,sku-4 +2021-08-01,0,sku-4 +2021-08-08,0,sku-4 +2021-08-15,0,sku-4 +2021-08-22,0,sku-4 +2021-08-29,0,sku-4 +2021-09-05,0,sku-4 +2021-09-12,15,sku-4 +2021-09-19,10,sku-4 +2021-09-26,5,sku-4 +2021-10-03,0,sku-4 +2021-10-10,24,sku-4 +2021-10-17,18,sku-4 +2021-10-24,6,sku-4 +2021-10-31,7,sku-4 +2021-11-07,8,sku-4 +2021-11-14,25,sku-4 +2021-11-21,10,sku-4 +2021-11-28,10,sku-4 +2021-12-05,2,sku-4 +2021-12-12,2,sku-4 +2021-12-19,0,sku-4 +2021-12-26,0,sku-4 +2022-01-02,2,sku-4 +2022-01-09,4,sku-4 +2022-01-16,3,sku-4 +2022-01-23,10,sku-4 +2022-01-30,10,sku-4 +2022-02-06,0,sku-4 +2022-02-13,20,sku-4 +2022-02-20,25,sku-4 +2022-02-27,10,sku-4 +2022-03-06,29,sku-4 +2022-03-13,10,sku-4 +2022-03-20,7,sku-4 +2022-03-27,24,sku-4 +2022-04-03,3,sku-4 +2022-04-10,10,sku-4 +2022-04-17,7,sku-4 +2022-04-24,2,sku-4 +2022-05-01,0,sku-4 +2022-05-08,0,sku-4 +2022-05-15,10,sku-4 +2022-05-22,7,sku-4 +2022-05-29,9,sku-4 +2022-06-05,6,sku-4 +2022-06-12,5,sku-4 +2022-06-19,35,sku-4 +2022-06-26,20,sku-4 +2022-07-03,0,sku-4 +2022-07-10,5,sku-4 +2022-07-17,5,sku-4 +2022-07-24,9,sku-4 +2022-07-31,14,sku-4 +2022-08-07,20,sku-4 +2022-08-14,10,sku-4 +2022-08-21,10,sku-4 +2022-08-28,1,sku-4 +2022-09-04,15,sku-4 +2022-09-11,22,sku-4 +2022-09-18,10,sku-4 +2022-09-25,10,sku-4 +2022-10-02,20,sku-4 +2022-10-09,0,sku-4 +2022-10-16,0,sku-4 +2022-10-23,0,sku-4 +2022-10-30,15,sku-4 +2022-11-06,10,sku-4 +2022-11-13,0,sku-4 +2022-11-20,10,sku-4 +2022-11-27,10,sku-4 +2022-12-04,0,sku-4 +2022-12-11,0,sku-4 +2022-12-18,0,sku-4 +2022-12-25,7,sku-4 +2023-01-01,10,sku-4 +2023-01-08,10,sku-4 +2023-01-15,0,sku-4 +2023-01-22,5,sku-4 +2023-01-29,0,sku-4 +2023-02-05,7,sku-4 +2023-02-12,2,sku-4 +2023-02-19,0,sku-4 +2023-02-26,20,sku-4 +2023-03-05,13,sku-4 +2023-03-12,10,sku-4 +2023-03-19,0,sku-4 +2023-03-26,0,sku-4 +2023-04-02,10,sku-4 +2023-04-09,8,sku-4 +2023-04-16,10,sku-4 +2023-04-23,5,sku-4 +2018-05-06,14,sku-5 +2018-05-13,0,sku-5 +2018-05-20,0,sku-5 +2018-05-27,12,sku-5 +2018-06-03,8,sku-5 +2018-06-10,8,sku-5 +2018-06-17,0,sku-5 +2018-06-24,0,sku-5 +2018-07-01,0,sku-5 +2018-07-08,0,sku-5 +2018-07-15,0,sku-5 +2018-07-22,20,sku-5 +2018-07-29,33,sku-5 +2018-08-05,17,sku-5 +2018-08-12,9,sku-5 +2018-08-19,9,sku-5 +2018-08-26,2,sku-5 +2018-09-02,22,sku-5 +2018-09-09,61,sku-5 +2018-09-16,6,sku-5 +2018-09-23,6,sku-5 +2018-09-30,20,sku-5 +2018-10-07,17,sku-5 +2018-10-14,40,sku-5 +2018-10-21,0,sku-5 +2018-10-28,5,sku-5 +2018-11-04,0,sku-5 +2018-11-11,0,sku-5 +2018-11-18,58,sku-5 +2018-11-25,12,sku-5 +2018-12-02,60,sku-5 +2018-12-09,3,sku-5 +2018-12-16,28,sku-5 +2018-12-23,37,sku-5 +2018-12-30,0,sku-5 +2019-01-06,0,sku-5 +2019-01-13,3,sku-5 +2019-01-20,5,sku-5 +2019-01-27,15,sku-5 +2019-02-03,41,sku-5 +2019-02-10,0,sku-5 +2019-02-17,0,sku-5 +2019-02-24,0,sku-5 +2019-03-03,50,sku-5 +2019-03-10,8,sku-5 +2019-03-17,10,sku-5 +2019-03-24,9,sku-5 +2019-03-31,30,sku-5 +2019-04-07,10,sku-5 +2019-04-14,0,sku-5 +2019-04-21,3,sku-5 +2019-04-28,23,sku-5 +2019-05-05,20,sku-5 +2019-05-12,0,sku-5 +2019-05-19,30,sku-5 +2019-05-26,20,sku-5 +2019-06-02,0,sku-5 +2019-06-09,0,sku-5 +2019-06-16,5,sku-5 +2019-06-23,11,sku-5 +2019-06-30,0,sku-5 +2019-07-07,0,sku-5 +2019-07-14,0,sku-5 +2019-07-21,0,sku-5 +2019-07-28,40,sku-5 +2019-08-04,0,sku-5 +2019-08-11,5,sku-5 +2019-08-18,0,sku-5 +2019-08-25,12,sku-5 +2019-09-01,1,sku-5 +2019-09-08,9,sku-5 +2019-09-15,10,sku-5 +2019-09-22,0,sku-5 +2019-09-29,0,sku-5 +2019-10-06,23,sku-5 +2019-10-13,15,sku-5 +2019-10-20,13,sku-5 +2019-10-27,20,sku-5 +2019-11-03,14,sku-5 +2019-11-10,2,sku-5 +2019-11-17,8,sku-5 +2019-11-24,11,sku-5 +2019-12-01,0,sku-5 +2019-12-08,30,sku-5 +2019-12-15,9,sku-5 +2019-12-22,9,sku-5 +2019-12-29,29,sku-5 +2020-01-05,0,sku-5 +2020-01-12,26,sku-5 +2020-01-19,10,sku-5 +2020-01-26,82,sku-5 +2020-02-02,9,sku-5 +2020-02-09,20,sku-5 +2020-02-16,70,sku-5 +2020-02-23,0,sku-5 +2020-03-01,95,sku-5 +2020-03-08,0,sku-5 +2020-03-15,0,sku-5 +2020-03-22,20,sku-5 +2020-03-29,45,sku-5 +2020-04-05,140,sku-5 +2020-04-12,0,sku-5 +2020-04-19,0,sku-5 +2020-04-26,0,sku-5 +2020-05-03,0,sku-5 +2020-05-10,0,sku-5 +2020-05-17,105,sku-5 +2020-05-24,30,sku-5 +2020-05-31,15,sku-5 +2020-06-07,20,sku-5 +2020-06-14,0,sku-5 +2020-06-21,0,sku-5 +2020-06-28,0,sku-5 +2020-07-05,15,sku-5 +2020-07-12,10,sku-5 +2020-07-19,10,sku-5 +2020-07-26,110,sku-5 +2020-08-02,0,sku-5 +2020-08-09,0,sku-5 +2020-08-16,0,sku-5 +2020-08-23,20,sku-5 +2020-08-30,0,sku-5 +2020-09-06,30,sku-5 +2020-09-13,15,sku-5 +2020-09-20,0,sku-5 +2020-09-27,0,sku-5 +2020-10-04,0,sku-5 +2020-10-11,20,sku-5 +2020-10-18,6,sku-5 +2020-10-25,0,sku-5 +2020-11-01,0,sku-5 +2020-11-08,0,sku-5 +2020-11-15,13,sku-5 +2020-11-22,2,sku-5 +2020-11-29,0,sku-5 +2020-12-06,0,sku-5 +2020-12-13,1,sku-5 +2020-12-20,0,sku-5 +2020-12-27,0,sku-5 +2021-01-03,0,sku-5 +2021-01-10,20,sku-5 +2021-01-17,26,sku-5 +2021-01-24,80,sku-5 +2021-01-31,0,sku-5 +2021-02-07,30,sku-5 +2021-02-14,75,sku-5 +2021-02-21,0,sku-5 +2021-02-28,0,sku-5 +2021-03-07,0,sku-5 +2021-03-14,0,sku-5 +2021-03-21,0,sku-5 +2021-03-28,0,sku-5 +2021-04-04,0,sku-5 +2021-04-11,56,sku-5 +2021-04-18,34,sku-5 +2021-04-25,10,sku-5 +2021-05-02,39,sku-5 +2021-05-09,1,sku-5 +2021-05-16,0,sku-5 +2021-05-23,11,sku-5 +2021-05-30,24,sku-5 +2021-06-06,15,sku-5 +2021-06-13,10,sku-5 +2021-06-20,10,sku-5 +2021-06-27,28,sku-5 +2021-07-04,0,sku-5 +2021-07-11,0,sku-5 +2021-07-18,0,sku-5 +2021-07-25,9,sku-5 +2021-08-01,0,sku-5 +2021-08-08,0,sku-5 +2021-08-15,0,sku-5 +2021-08-22,0,sku-5 +2021-08-29,0,sku-5 +2021-09-05,40,sku-5 +2021-09-12,10,sku-5 +2021-09-19,0,sku-5 +2021-09-26,20,sku-5 +2021-10-03,0,sku-5 +2021-10-10,25,sku-5 +2021-10-17,15,sku-5 +2021-10-24,10,sku-5 +2021-10-31,12,sku-5 +2021-11-07,70,sku-5 +2021-11-14,30,sku-5 +2021-11-21,70,sku-5 +2021-11-28,35,sku-5 +2021-12-05,0,sku-5 +2021-12-12,0,sku-5 +2021-12-19,0,sku-5 +2021-12-26,0,sku-5 +2022-01-02,20,sku-5 +2022-01-09,0,sku-5 +2022-01-16,5,sku-5 +2022-01-23,10,sku-5 +2022-01-30,25,sku-5 +2022-02-06,5,sku-5 +2022-02-13,30,sku-5 +2022-02-20,0,sku-5 +2022-02-27,42,sku-5 +2022-03-06,0,sku-5 +2022-03-13,20,sku-5 +2022-03-20,0,sku-5 +2022-03-27,10,sku-5 +2022-04-03,50,sku-5 +2022-04-10,0,sku-5 +2022-04-17,100,sku-5 +2022-04-24,0,sku-5 +2022-05-01,0,sku-5 +2022-05-08,0,sku-5 +2022-05-15,15,sku-5 +2022-05-22,15,sku-5 +2022-05-29,10,sku-5 +2022-06-05,10,sku-5 +2022-06-12,20,sku-5 +2022-06-19,24,sku-5 +2022-06-26,0,sku-5 +2022-07-03,34,sku-5 +2022-07-10,0,sku-5 +2022-07-17,30,sku-5 +2022-07-24,44,sku-5 +2022-07-31,20,sku-5 +2022-08-07,0,sku-5 +2022-08-14,60,sku-5 +2022-08-21,46,sku-5 +2022-08-28,0,sku-5 +2022-09-04,35,sku-5 +2022-09-11,20,sku-5 +2022-09-18,0,sku-5 +2022-09-25,20,sku-5 +2022-10-02,20,sku-5 +2022-10-09,0,sku-5 +2022-10-16,20,sku-5 +2022-10-23,0,sku-5 +2022-10-30,0,sku-5 +2022-11-06,0,sku-5 +2022-11-13,0,sku-5 +2022-11-20,0,sku-5 +2022-11-27,30,sku-5 +2022-12-04,10,sku-5 +2022-12-11,60,sku-5 +2022-12-18,10,sku-5 +2022-12-25,100,sku-5 +2023-01-01,20,sku-5 +2023-01-08,10,sku-5 +2023-01-15,0,sku-5 +2023-01-22,0,sku-5 +2023-01-29,2,sku-5 +2023-02-05,0,sku-5 +2023-02-12,0,sku-5 +2023-02-19,20,sku-5 +2023-02-26,5,sku-5 +2023-03-05,45,sku-5 +2023-03-12,0,sku-5 +2023-03-19,0,sku-5 +2023-03-26,0,sku-5 +2023-04-02,0,sku-5 +2023-04-09,60,sku-5 +2023-04-16,20,sku-5 +2023-04-23,5,sku-5 +2018-05-06,31,sku-6 +2018-05-13,31,sku-6 +2018-05-20,0,sku-6 +2018-05-27,5,sku-6 +2018-06-03,6,sku-6 +2018-06-10,28,sku-6 +2018-06-17,0,sku-6 +2018-06-24,36,sku-6 +2018-07-01,4,sku-6 +2018-07-08,0,sku-6 +2018-07-15,29,sku-6 +2018-07-22,0,sku-6 +2018-07-29,10,sku-6 +2018-08-05,0,sku-6 +2018-08-12,27,sku-6 +2018-08-19,0,sku-6 +2018-08-26,6,sku-6 +2018-09-02,0,sku-6 +2018-09-09,0,sku-6 +2018-09-16,0,sku-6 +2018-09-23,11,sku-6 +2018-09-30,10,sku-6 +2018-10-07,10,sku-6 +2018-10-14,6,sku-6 +2018-10-21,4,sku-6 +2018-10-28,10,sku-6 +2018-11-04,12,sku-6 +2018-11-11,0,sku-6 +2018-11-18,37,sku-6 +2018-11-25,5,sku-6 +2018-12-02,3,sku-6 +2018-12-09,10,sku-6 +2018-12-16,0,sku-6 +2018-12-23,0,sku-6 +2018-12-30,0,sku-6 +2019-01-06,0,sku-6 +2019-01-13,0,sku-6 +2019-01-20,0,sku-6 +2019-01-27,10,sku-6 +2019-02-03,17,sku-6 +2019-02-10,32,sku-6 +2019-02-17,0,sku-6 +2019-02-24,0,sku-6 +2019-03-03,20,sku-6 +2019-03-10,0,sku-6 +2019-03-17,16,sku-6 +2019-03-24,1,sku-6 +2019-03-31,8,sku-6 +2019-04-07,4,sku-6 +2019-04-14,0,sku-6 +2019-04-21,16,sku-6 +2019-04-28,30,sku-6 +2019-05-05,4,sku-6 +2019-05-12,14,sku-6 +2019-05-19,34,sku-6 +2019-05-26,16,sku-6 +2019-06-02,0,sku-6 +2019-06-09,0,sku-6 +2019-06-16,0,sku-6 +2019-06-23,15,sku-6 +2019-06-30,42,sku-6 +2019-07-07,23,sku-6 +2019-07-14,0,sku-6 +2019-07-21,0,sku-6 +2019-07-28,0,sku-6 +2019-08-04,0,sku-6 +2019-08-11,5,sku-6 +2019-08-18,9,sku-6 +2019-08-25,10,sku-6 +2019-09-01,0,sku-6 +2019-09-08,2,sku-6 +2019-09-15,30,sku-6 +2019-09-22,2,sku-6 +2019-09-29,0,sku-6 +2019-10-06,20,sku-6 +2019-10-13,25,sku-6 +2019-10-20,17,sku-6 +2019-10-27,0,sku-6 +2019-11-03,5,sku-6 +2019-11-10,7,sku-6 +2019-11-17,6,sku-6 +2019-11-24,30,sku-6 +2019-12-01,24,sku-6 +2019-12-08,8,sku-6 +2019-12-15,0,sku-6 +2019-12-22,0,sku-6 +2019-12-29,30,sku-6 +2020-01-05,0,sku-6 +2020-01-12,10,sku-6 +2020-01-19,2,sku-6 +2020-01-26,5,sku-6 +2020-02-02,29,sku-6 +2020-02-09,0,sku-6 +2020-02-16,7,sku-6 +2020-02-23,25,sku-6 +2020-03-01,0,sku-6 +2020-03-08,0,sku-6 +2020-03-15,0,sku-6 +2020-03-22,18,sku-6 +2020-03-29,13,sku-6 +2020-04-05,58,sku-6 +2020-04-12,0,sku-6 +2020-04-19,0,sku-6 +2020-04-26,8,sku-6 +2020-05-03,1,sku-6 +2020-05-10,0,sku-6 +2020-05-17,16,sku-6 +2020-05-24,0,sku-6 +2020-05-31,0,sku-6 +2020-06-07,31,sku-6 +2020-06-14,15,sku-6 +2020-06-21,6,sku-6 +2020-06-28,11,sku-6 +2020-07-05,4,sku-6 +2020-07-12,49,sku-6 +2020-07-19,12,sku-6 +2020-07-26,59,sku-6 +2020-08-02,0,sku-6 +2020-08-09,0,sku-6 +2020-08-16,0,sku-6 +2020-08-23,149,sku-6 +2020-08-30,2,sku-6 +2020-09-06,4,sku-6 +2020-09-13,0,sku-6 +2020-09-20,115,sku-6 +2020-09-27,28,sku-6 +2020-10-04,0,sku-6 +2020-10-11,25,sku-6 +2020-10-18,17,sku-6 +2020-10-25,0,sku-6 +2020-11-01,9,sku-6 +2020-11-08,0,sku-6 +2020-11-15,13,sku-6 +2020-11-22,77,sku-6 +2020-11-29,22,sku-6 +2020-12-06,20,sku-6 +2020-12-13,16,sku-6 +2020-12-20,20,sku-6 +2020-12-27,15,sku-6 +2021-01-03,0,sku-6 +2021-01-10,1,sku-6 +2021-01-17,29,sku-6 +2021-01-24,8,sku-6 +2021-01-31,33,sku-6 +2021-02-07,0,sku-6 +2021-02-14,75,sku-6 +2021-02-21,0,sku-6 +2021-02-28,17,sku-6 +2021-03-07,0,sku-6 +2021-03-14,0,sku-6 +2021-03-21,40,sku-6 +2021-03-28,24,sku-6 +2021-04-04,20,sku-6 +2021-04-11,35,sku-6 +2021-04-18,50,sku-6 +2021-04-25,25,sku-6 +2021-05-02,15,sku-6 +2021-05-09,0,sku-6 +2021-05-16,0,sku-6 +2021-05-23,0,sku-6 +2021-05-30,0,sku-6 +2021-06-06,5,sku-6 +2021-06-13,30,sku-6 +2021-06-20,5,sku-6 +2021-06-27,65,sku-6 +2021-07-04,0,sku-6 +2021-07-11,90,sku-6 +2021-07-18,2,sku-6 +2021-07-25,0,sku-6 +2021-08-01,0,sku-6 +2021-08-08,0,sku-6 +2021-08-15,0,sku-6 +2021-08-22,50,sku-6 +2021-08-29,0,sku-6 +2021-09-05,5,sku-6 +2021-09-12,50,sku-6 +2021-09-19,20,sku-6 +2021-09-26,43,sku-6 +2021-10-03,0,sku-6 +2021-10-10,63,sku-6 +2021-10-17,20,sku-6 +2021-10-24,20,sku-6 +2021-10-31,35,sku-6 +2021-11-07,15,sku-6 +2021-11-14,12,sku-6 +2021-11-21,122,sku-6 +2021-11-28,0,sku-6 +2021-12-05,56,sku-6 +2021-12-12,0,sku-6 +2021-12-19,12,sku-6 +2021-12-26,0,sku-6 +2022-01-02,5,sku-6 +2022-01-09,0,sku-6 +2022-01-16,15,sku-6 +2022-01-23,15,sku-6 +2022-01-30,0,sku-6 +2022-02-06,10,sku-6 +2022-02-13,60,sku-6 +2022-02-20,170,sku-6 +2022-02-27,50,sku-6 +2022-03-06,0,sku-6 +2022-03-13,0,sku-6 +2022-03-20,0,sku-6 +2022-03-27,0,sku-6 +2022-04-03,0,sku-6 +2022-04-10,0,sku-6 +2022-04-17,0,sku-6 +2022-04-24,4,sku-6 +2022-05-01,11,sku-6 +2022-05-08,0,sku-6 +2022-05-15,0,sku-6 +2022-05-22,0,sku-6 +2022-05-29,0,sku-6 +2022-06-05,0,sku-6 +2022-06-12,0,sku-6 +2022-06-19,0,sku-6 +2022-06-26,0,sku-6 +2022-07-03,0,sku-6 +2022-07-10,0,sku-6 +2022-07-17,40,sku-6 +2022-07-24,50,sku-6 +2022-07-31,50,sku-6 +2022-08-07,0,sku-6 +2022-08-14,100,sku-6 +2022-08-21,0,sku-6 +2022-08-28,45,sku-6 +2022-09-04,50,sku-6 +2022-09-11,100,sku-6 +2022-09-18,50,sku-6 +2022-09-25,50,sku-6 +2022-10-02,20,sku-6 +2022-10-09,0,sku-6 +2022-10-16,0,sku-6 +2022-10-23,0,sku-6 +2022-10-30,0,sku-6 +2022-11-06,0,sku-6 +2022-11-13,0,sku-6 +2022-11-20,0,sku-6 +2022-11-27,0,sku-6 +2022-12-04,0,sku-6 +2022-12-11,20,sku-6 +2022-12-18,0,sku-6 +2022-12-25,60,sku-6 +2023-01-01,20,sku-6 +2023-01-08,10,sku-6 +2023-01-15,20,sku-6 +2023-01-22,0,sku-6 +2023-01-29,10,sku-6 +2023-02-05,0,sku-6 +2023-02-12,0,sku-6 +2023-02-19,0,sku-6 +2023-02-26,0,sku-6 +2023-03-05,20,sku-6 +2023-03-12,50,sku-6 +2023-03-19,0,sku-6 +2023-03-26,0,sku-6 +2023-04-02,10,sku-6 +2023-04-09,85,sku-6 +2023-04-16,50,sku-6 +2018-05-06,5,sku-7 +2018-05-13,9,sku-7 +2018-05-20,5,sku-7 +2018-05-27,30,sku-7 +2018-06-03,5,sku-7 +2018-06-10,12,sku-7 +2018-06-17,12,sku-7 +2018-06-24,7,sku-7 +2018-07-01,30,sku-7 +2018-07-08,12,sku-7 +2018-07-15,34,sku-7 +2018-07-22,15,sku-7 +2018-07-29,25,sku-7 +2018-08-05,5,sku-7 +2018-08-12,10,sku-7 +2018-08-19,12,sku-7 +2018-08-26,44,sku-7 +2018-09-02,45,sku-7 +2018-09-09,13,sku-7 +2018-09-16,3,sku-7 +2018-09-23,43,sku-7 +2018-09-30,13,sku-7 +2018-10-07,5,sku-7 +2018-10-14,31,sku-7 +2018-10-21,10,sku-7 +2018-10-28,45,sku-7 +2018-11-04,12,sku-7 +2018-11-11,12,sku-7 +2018-11-18,84,sku-7 +2018-11-25,12,sku-7 +2018-12-02,9,sku-7 +2018-12-09,31,sku-7 +2018-12-16,43,sku-7 +2018-12-23,1,sku-7 +2018-12-30,12,sku-7 +2019-01-06,12,sku-7 +2019-01-13,12,sku-7 +2019-01-20,12,sku-7 +2019-01-27,1,sku-7 +2019-02-03,12,sku-7 +2019-02-10,12,sku-7 +2019-02-17,12,sku-7 +2019-02-24,39,sku-7 +2019-03-03,25,sku-7 +2019-03-10,5,sku-7 +2019-03-17,11,sku-7 +2019-03-24,12,sku-7 +2019-03-31,85,sku-7 +2019-04-07,12,sku-7 +2019-04-14,12,sku-7 +2019-04-21,12,sku-7 +2019-04-28,12,sku-7 +2019-05-05,12,sku-7 +2019-05-12,12,sku-7 +2019-05-19,12,sku-7 +2019-05-26,3,sku-7 +2019-06-02,5,sku-7 +2019-06-09,12,sku-7 +2019-06-16,5,sku-7 +2019-06-23,18,sku-7 +2019-06-30,7,sku-7 +2019-07-07,10,sku-7 +2019-07-14,12,sku-7 +2019-07-21,8,sku-7 +2019-07-28,12,sku-7 +2019-08-04,32,sku-7 +2019-08-11,12,sku-7 +2019-08-18,34,sku-7 +2019-08-25,12,sku-7 +2019-09-01,12,sku-7 +2019-09-08,5,sku-7 +2019-09-15,6,sku-7 +2019-09-22,8,sku-7 +2019-09-29,12,sku-7 +2019-10-06,35,sku-7 +2019-10-13,12,sku-7 +2019-10-20,12,sku-7 +2019-10-27,12,sku-7 +2019-11-03,21,sku-7 +2019-11-10,7,sku-7 +2019-11-17,10,sku-7 +2019-11-24,16,sku-7 +2019-12-01,11,sku-7 +2019-12-08,9,sku-7 +2019-12-15,12,sku-7 +2019-12-22,12,sku-7 +2019-12-29,32,sku-7 +2020-01-05,12,sku-7 +2020-01-12,4,sku-7 +2020-01-19,15,sku-7 +2020-01-26,9,sku-7 +2020-02-02,10,sku-7 +2020-02-09,20,sku-7 +2020-02-16,13,sku-7 +2020-02-23,13,sku-7 +2020-03-01,10,sku-7 +2020-03-08,21,sku-7 +2020-03-15,17,sku-7 +2020-03-22,20,sku-7 +2020-03-29,9,sku-7 +2020-04-05,15,sku-7 +2020-04-12,12,sku-7 +2020-04-19,12,sku-7 +2020-04-26,4,sku-7 +2020-05-03,12,sku-7 +2020-05-10,12,sku-7 +2020-05-17,22,sku-7 +2020-05-24,12,sku-7 +2020-05-31,12,sku-7 +2020-06-07,12,sku-7 +2020-06-14,5,sku-7 +2020-06-21,11,sku-7 +2020-06-28,12,sku-7 +2020-07-05,5,sku-7 +2020-07-12,10,sku-7 +2020-07-19,12,sku-7 +2020-07-26,42,sku-7 +2020-08-02,12,sku-7 +2020-08-09,12,sku-7 +2020-08-16,12,sku-7 +2020-08-23,140,sku-7 +2020-08-30,55,sku-7 +2020-09-06,12,sku-7 +2020-09-13,12,sku-7 +2020-09-20,12,sku-7 +2020-09-27,12,sku-7 +2020-10-04,12,sku-7 +2020-10-11,12,sku-7 +2020-10-18,12,sku-7 +2020-10-25,12,sku-7 +2020-11-01,12,sku-7 +2020-11-08,12,sku-7 +2020-11-15,12,sku-7 +2020-11-22,12,sku-7 +2020-11-29,12,sku-7 +2020-12-06,9,sku-7 +2020-12-13,5,sku-7 +2020-12-20,12,sku-7 +2020-12-27,10,sku-7 +2021-01-03,12,sku-7 +2021-01-10,12,sku-7 +2021-01-17,12,sku-7 +2021-01-24,6,sku-7 +2021-01-31,24,sku-7 +2021-02-07,10,sku-7 +2021-02-14,82,sku-7 +2021-02-21,12,sku-7 +2021-02-28,12,sku-7 +2021-03-07,12,sku-7 +2021-03-14,12,sku-7 +2021-03-21,35,sku-7 +2021-03-28,12,sku-7 +2021-04-04,12,sku-7 +2021-04-11,12,sku-7 +2021-04-18,12,sku-7 +2021-04-25,18,sku-7 +2021-05-02,10,sku-7 +2021-05-09,12,sku-7 +2021-05-16,12,sku-7 +2021-05-23,3,sku-7 +2021-05-30,3,sku-7 +2021-06-06,5,sku-7 +2021-06-13,1,sku-7 +2021-06-20,2,sku-7 +2021-06-27,15,sku-7 +2021-07-04,15,sku-7 +2021-07-11,2,sku-7 +2021-07-18,2,sku-7 +2021-07-25,2,sku-7 +2021-08-01,10,sku-7 +2021-08-08,20,sku-7 +2021-08-15,25,sku-7 +2021-08-22,12,sku-7 +2021-08-29,12,sku-7 +2021-09-05,12,sku-7 +2021-09-12,12,sku-7 +2021-09-19,12,sku-7 +2021-09-26,5,sku-7 +2021-10-03,12,sku-7 +2021-10-10,7,sku-7 +2021-10-17,25,sku-7 +2021-10-24,10,sku-7 +2021-10-31,5,sku-7 +2021-11-07,15,sku-7 +2021-11-14,8,sku-7 +2021-11-21,14,sku-7 +2021-11-28,18,sku-7 +2021-12-05,65,sku-7 +2021-12-12,12,sku-7 +2021-12-19,12,sku-7 +2021-12-26,12,sku-7 +2022-01-02,10,sku-7 +2022-01-09,15,sku-7 +2022-01-16,1,sku-7 +2022-01-23,60,sku-7 +2022-01-30,12,sku-7 +2022-02-06,30,sku-7 +2022-02-13,20,sku-7 +2022-02-20,10,sku-7 +2022-02-27,12,sku-7 +2022-03-06,12,sku-7 +2022-03-13,12,sku-7 +2022-03-20,12,sku-7 +2022-03-27,20,sku-7 +2022-04-03,12,sku-7 +2022-04-10,25,sku-7 +2022-04-17,15,sku-7 +2022-04-24,15,sku-7 +2022-05-01,10,sku-7 +2022-05-08,12,sku-7 +2022-05-15,13,sku-7 +2022-05-22,7,sku-7 +2022-05-29,26,sku-7 +2022-06-05,18,sku-7 +2022-06-12,12,sku-7 +2022-06-19,12,sku-7 +2022-06-26,12,sku-7 +2022-07-03,35,sku-7 +2022-07-10,20,sku-7 +2022-07-17,30,sku-7 +2022-07-24,8,sku-7 +2022-07-31,12,sku-7 +2022-08-07,50,sku-7 +2022-08-14,33,sku-7 +2022-08-21,12,sku-7 +2022-08-28,12,sku-7 +2022-09-04,10,sku-7 +2022-09-11,10,sku-7 +2022-09-18,12,sku-7 +2022-09-25,20,sku-7 +2022-10-02,20,sku-7 +2022-10-09,12,sku-7 +2022-10-16,50,sku-7 +2022-10-23,12,sku-7 +2022-10-30,12,sku-7 +2022-11-06,12,sku-7 +2022-11-13,12,sku-7 +2022-11-20,12,sku-7 +2022-11-27,20,sku-7 +2022-12-04,50,sku-7 +2022-12-11,60,sku-7 +2022-12-18,10,sku-7 +2022-12-25,40,sku-7 +2023-01-01,10,sku-7 +2023-01-08,30,sku-7 +2023-01-15,12,sku-7 +2023-01-22,60,sku-7 +2023-01-29,45,sku-7 +2023-02-05,5,sku-7 +2023-02-12,10,sku-7 +2023-02-19,7,sku-7 +2023-02-26,40,sku-7 +2023-03-05,20,sku-7 +2023-03-12,25,sku-7 +2023-03-19,12,sku-7 +2023-03-26,12,sku-7 +2023-04-02,35,sku-7 +2023-04-09,12,sku-7 +2023-04-16,12,sku-7 +2023-04-23,20,sku-7 +2018-05-20,1,sku-8 +2018-05-27,16,sku-8 +2018-06-03,8,sku-8 +2018-06-10,0,sku-8 +2018-06-17,0,sku-8 +2018-06-24,7,sku-8 +2018-07-01,0,sku-8 +2018-07-08,0,sku-8 +2018-07-15,1,sku-8 +2018-07-22,2,sku-8 +2018-07-29,0,sku-8 +2018-08-05,0,sku-8 +2018-08-12,0,sku-8 +2018-08-19,0,sku-8 +2018-08-26,0,sku-8 +2018-09-02,10,sku-8 +2018-09-09,0,sku-8 +2018-09-16,6,sku-8 +2018-09-23,6,sku-8 +2018-09-30,2,sku-8 +2018-10-07,7,sku-8 +2018-10-14,0,sku-8 +2018-10-21,2,sku-8 +2018-10-28,1,sku-8 +2018-11-04,1,sku-8 +2018-11-11,0,sku-8 +2018-11-18,6,sku-8 +2018-11-25,0,sku-8 +2018-12-02,0,sku-8 +2018-12-09,3,sku-8 +2018-12-16,2,sku-8 +2018-12-23,0,sku-8 +2018-12-30,5,sku-8 +2019-01-06,0,sku-8 +2019-01-13,18,sku-8 +2019-01-20,5,sku-8 +2019-01-27,0,sku-8 +2019-02-03,12,sku-8 +2019-02-10,2,sku-8 +2019-02-17,0,sku-8 +2019-02-24,12,sku-8 +2019-03-03,6,sku-8 +2019-03-10,7,sku-8 +2019-03-17,0,sku-8 +2019-03-24,12,sku-8 +2019-03-31,9,sku-8 +2019-04-07,11,sku-8 +2019-04-14,3,sku-8 +2019-04-21,0,sku-8 +2019-04-28,3,sku-8 +2019-05-05,15,sku-8 +2019-05-12,14,sku-8 +2019-05-19,4,sku-8 +2019-05-26,4,sku-8 +2019-06-02,2,sku-8 +2019-06-09,0,sku-8 +2019-06-16,0,sku-8 +2019-06-23,5,sku-8 +2019-06-30,6,sku-8 +2019-07-07,0,sku-8 +2019-07-14,0,sku-8 +2019-07-21,22,sku-8 +2019-07-28,0,sku-8 +2019-08-04,19,sku-8 +2019-08-11,3,sku-8 +2019-08-18,7,sku-8 +2019-08-25,0,sku-8 +2019-09-01,0,sku-8 +2019-09-08,11,sku-8 +2019-09-15,7,sku-8 +2019-09-22,0,sku-8 +2019-09-29,0,sku-8 +2019-10-06,18,sku-8 +2019-10-13,1,sku-8 +2019-10-20,14,sku-8 +2019-10-27,0,sku-8 +2019-11-03,9,sku-8 +2019-11-10,3,sku-8 +2019-11-17,0,sku-8 +2019-11-24,7,sku-8 +2019-12-01,9,sku-8 +2019-12-08,1,sku-8 +2019-12-15,0,sku-8 +2019-12-22,0,sku-8 +2019-12-29,0,sku-8 +2020-01-05,0,sku-8 +2020-01-12,4,sku-8 +2020-01-19,11,sku-8 +2020-01-26,19,sku-8 +2020-02-02,8,sku-8 +2020-02-09,2,sku-8 +2020-02-16,13,sku-8 +2020-02-23,2,sku-8 +2020-03-01,16,sku-8 +2020-03-08,5,sku-8 +2020-03-15,5,sku-8 +2020-03-22,9,sku-8 +2020-03-29,21,sku-8 +2020-04-05,7,sku-8 +2020-04-12,0,sku-8 +2020-04-19,0,sku-8 +2020-04-26,17,sku-8 +2020-05-03,4,sku-8 +2020-05-10,2,sku-8 +2020-05-17,19,sku-8 +2020-05-24,0,sku-8 +2020-05-31,0,sku-8 +2020-06-07,18,sku-8 +2020-06-14,0,sku-8 +2020-06-21,15,sku-8 +2020-06-28,2,sku-8 +2020-07-05,2,sku-8 +2020-07-12,0,sku-8 +2020-07-19,9,sku-8 +2020-07-26,29,sku-8 +2020-08-02,0,sku-8 +2020-08-09,0,sku-8 +2020-08-16,0,sku-8 +2020-08-23,33,sku-8 +2020-08-30,0,sku-8 +2020-09-06,6,sku-8 +2020-09-13,25,sku-8 +2020-09-20,0,sku-8 +2020-09-27,6,sku-8 +2020-10-04,15,sku-8 +2020-10-11,21,sku-8 +2020-10-18,19,sku-8 +2020-10-25,5,sku-8 +2020-11-01,12,sku-8 +2020-11-08,0,sku-8 +2020-11-15,7,sku-8 +2020-11-22,2,sku-8 +2020-11-29,6,sku-8 +2020-12-06,9,sku-8 +2020-12-13,4,sku-8 +2020-12-20,17,sku-8 +2020-12-27,12,sku-8 +2021-01-03,0,sku-8 +2021-01-10,0,sku-8 +2021-01-17,10,sku-8 +2021-01-24,12,sku-8 +2021-01-31,5,sku-8 +2021-02-07,25,sku-8 +2021-02-14,40,sku-8 +2021-02-21,0,sku-8 +2021-02-28,0,sku-8 +2021-03-07,0,sku-8 +2021-03-14,0,sku-8 +2021-03-21,0,sku-8 +2021-03-28,0,sku-8 +2021-04-04,0,sku-8 +2021-04-11,0,sku-8 +2021-04-18,13,sku-8 +2021-04-25,10,sku-8 +2021-05-02,29,sku-8 +2021-05-09,0,sku-8 +2021-05-16,0,sku-8 +2021-05-23,0,sku-8 +2021-05-30,0,sku-8 +2021-06-06,0,sku-8 +2021-06-13,7,sku-8 +2021-06-20,5,sku-8 +2021-06-27,15,sku-8 +2021-07-04,0,sku-8 +2021-07-11,10,sku-8 +2021-07-18,5,sku-8 +2021-07-25,0,sku-8 +2021-08-01,0,sku-8 +2021-08-08,0,sku-8 +2021-08-15,0,sku-8 +2021-08-22,0,sku-8 +2021-08-29,0,sku-8 +2021-09-05,18,sku-8 +2021-09-12,15,sku-8 +2021-09-19,12,sku-8 +2021-09-26,7,sku-8 +2021-10-03,0,sku-8 +2021-10-10,10,sku-8 +2021-10-17,10,sku-8 +2021-10-24,20,sku-8 +2021-10-31,5,sku-8 +2021-11-07,16,sku-8 +2021-11-14,9,sku-8 +2021-11-21,30,sku-8 +2021-11-28,12,sku-8 +2021-12-05,0,sku-8 +2021-12-12,18,sku-8 +2021-12-19,8,sku-8 +2021-12-26,0,sku-8 +2022-01-02,5,sku-8 +2022-01-09,0,sku-8 +2022-01-16,5,sku-8 +2022-01-23,30,sku-8 +2022-01-30,20,sku-8 +2022-02-06,10,sku-8 +2022-02-13,30,sku-8 +2022-02-20,0,sku-8 +2022-02-27,0,sku-8 +2022-03-06,30,sku-8 +2022-03-13,0,sku-8 +2022-03-20,30,sku-8 +2022-03-27,0,sku-8 +2022-04-03,0,sku-8 +2022-04-10,20,sku-8 +2022-04-17,30,sku-8 +2022-04-24,0,sku-8 +2022-05-01,10,sku-8 +2022-05-08,0,sku-8 +2022-05-15,15,sku-8 +2022-05-22,10,sku-8 +2022-05-29,15,sku-8 +2022-06-05,10,sku-8 +2022-06-12,20,sku-8 +2022-06-19,3,sku-8 +2022-06-26,20,sku-8 +2022-07-03,0,sku-8 +2022-07-10,0,sku-8 +2022-07-17,20,sku-8 +2022-07-24,0,sku-8 +2022-07-31,10,sku-8 +2022-08-07,20,sku-8 +2022-08-14,20,sku-8 +2022-08-21,11,sku-8 +2022-08-28,10,sku-8 +2022-09-04,44,sku-8 +2022-09-11,20,sku-8 +2022-09-18,11,sku-8 +2022-09-25,0,sku-8 +2022-10-02,10,sku-8 +2022-10-09,0,sku-8 +2022-10-16,30,sku-8 +2022-10-23,0,sku-8 +2022-10-30,0,sku-8 +2022-11-06,0,sku-8 +2022-11-13,0,sku-8 +2022-11-20,0,sku-8 +2022-11-27,0,sku-8 +2022-12-04,0,sku-8 +2022-12-11,0,sku-8 +2022-12-18,5,sku-8 +2022-12-25,10,sku-8 +2023-01-01,5,sku-8 +2023-01-08,0,sku-8 +2023-01-15,0,sku-8 +2023-01-22,10,sku-8 +2023-01-29,15,sku-8 +2023-02-05,5,sku-8 +2023-02-12,5,sku-8 +2023-02-19,10,sku-8 +2023-02-26,2,sku-8 +2023-03-05,2,sku-8 +2023-03-12,3,sku-8 +2023-03-19,10,sku-8 +2023-03-26,22,sku-8 +2023-04-02,8,sku-8 +2023-04-09,20,sku-8 +2023-04-16,0,sku-8 +2023-04-23,5,sku-8 +2018-05-27,5,sku-9 +2018-06-03,9,sku-9 +2018-06-10,2,sku-9 +2018-06-17,0,sku-9 +2018-06-24,10,sku-9 +2018-07-01,0,sku-9 +2018-07-08,0,sku-9 +2018-07-15,2,sku-9 +2018-07-22,13,sku-9 +2018-07-29,2,sku-9 +2018-08-05,2,sku-9 +2018-08-12,11,sku-9 +2018-08-19,0,sku-9 +2018-08-26,12,sku-9 +2018-09-02,1,sku-9 +2018-09-09,2,sku-9 +2018-09-16,3,sku-9 +2018-09-23,21,sku-9 +2018-09-30,5,sku-9 +2018-10-07,3,sku-9 +2018-10-14,3,sku-9 +2018-10-21,1,sku-9 +2018-10-28,9,sku-9 +2018-11-04,12,sku-9 +2018-11-11,0,sku-9 +2018-11-18,11,sku-9 +2018-11-25,24,sku-9 +2018-12-02,3,sku-9 +2018-12-09,7,sku-9 +2018-12-16,3,sku-9 +2018-12-23,43,sku-9 +2018-12-30,7,sku-9 +2019-01-06,0,sku-9 +2019-01-13,1,sku-9 +2019-01-20,1,sku-9 +2019-01-27,5,sku-9 +2019-02-03,10,sku-9 +2019-02-10,0,sku-9 +2019-02-17,0,sku-9 +2019-02-24,3,sku-9 +2019-03-03,5,sku-9 +2019-03-10,0,sku-9 +2019-03-17,17,sku-9 +2019-03-24,6,sku-9 +2019-03-31,10,sku-9 +2019-04-07,10,sku-9 +2019-04-14,30,sku-9 +2019-04-21,0,sku-9 +2019-04-28,0,sku-9 +2019-05-05,24,sku-9 +2019-05-12,9,sku-9 +2019-05-19,10,sku-9 +2019-05-26,30,sku-9 +2019-06-02,0,sku-9 +2019-06-09,0,sku-9 +2019-06-16,0,sku-9 +2019-06-23,0,sku-9 +2019-06-30,0,sku-9 +2019-07-07,0,sku-9 +2019-07-14,0,sku-9 +2019-07-21,0,sku-9 +2019-07-28,15,sku-9 +2019-08-04,0,sku-9 +2019-08-11,0,sku-9 +2019-08-18,0,sku-9 +2019-08-25,3,sku-9 +2019-09-01,2,sku-9 +2019-09-08,5,sku-9 +2019-09-15,1,sku-9 +2019-09-22,0,sku-9 +2019-09-29,0,sku-9 +2019-10-06,8,sku-9 +2019-10-13,0,sku-9 +2019-10-20,10,sku-9 +2019-10-27,5,sku-9 +2019-11-03,8,sku-9 +2019-11-10,9,sku-9 +2019-11-17,2,sku-9 +2019-11-24,7,sku-9 +2019-12-01,22,sku-9 +2019-12-08,2,sku-9 +2019-12-15,0,sku-9 +2019-12-22,0,sku-9 +2019-12-29,13,sku-9 +2020-01-05,0,sku-9 +2020-01-12,0,sku-9 +2020-01-19,0,sku-9 +2020-01-26,0,sku-9 +2020-02-02,5,sku-9 +2020-02-09,0,sku-9 +2020-02-16,6,sku-9 +2020-02-23,13,sku-9 +2020-03-01,7,sku-9 +2020-03-08,3,sku-9 +2020-03-15,6,sku-9 +2020-03-22,5,sku-9 +2020-03-29,19,sku-9 +2020-04-05,8,sku-9 +2020-04-12,0,sku-9 +2020-04-19,0,sku-9 +2020-04-26,4,sku-9 +2020-05-03,5,sku-9 +2020-05-10,0,sku-9 +2020-05-17,4,sku-9 +2020-05-24,1,sku-9 +2020-05-31,4,sku-9 +2020-06-07,8,sku-9 +2020-06-14,10,sku-9 +2020-06-21,0,sku-9 +2020-06-28,1,sku-9 +2020-07-05,3,sku-9 +2020-07-12,9,sku-9 +2020-07-19,18,sku-9 +2020-07-26,18,sku-9 +2020-08-02,0,sku-9 +2020-08-09,0,sku-9 +2020-08-16,0,sku-9 +2020-08-23,26,sku-9 +2020-08-30,0,sku-9 +2020-09-06,0,sku-9 +2020-09-13,0,sku-9 +2020-09-20,0,sku-9 +2020-09-27,2,sku-9 +2020-10-04,7,sku-9 +2020-10-11,1,sku-9 +2020-10-18,8,sku-9 +2020-10-25,0,sku-9 +2020-11-01,12,sku-9 +2020-11-08,4,sku-9 +2020-11-15,16,sku-9 +2020-11-22,2,sku-9 +2020-11-29,3,sku-9 +2020-12-06,3,sku-9 +2020-12-13,2,sku-9 +2020-12-20,20,sku-9 +2020-12-27,2,sku-9 +2021-01-03,0,sku-9 +2021-01-10,5,sku-9 +2021-01-17,5,sku-9 +2021-01-24,40,sku-9 +2021-01-31,0,sku-9 +2021-02-07,0,sku-9 +2021-02-14,0,sku-9 +2021-02-21,0,sku-9 +2021-02-28,0,sku-9 +2021-03-07,0,sku-9 +2021-03-14,0,sku-9 +2021-03-21,10,sku-9 +2021-03-28,20,sku-9 +2021-04-04,0,sku-9 +2021-04-11,10,sku-9 +2021-04-18,8,sku-9 +2021-04-25,15,sku-9 +2021-05-02,17,sku-9 +2021-05-09,0,sku-9 +2021-05-16,0,sku-9 +2021-05-23,20,sku-9 +2021-05-30,0,sku-9 +2021-06-06,0,sku-9 +2021-06-13,10,sku-9 +2021-06-20,10,sku-9 +2021-06-27,32,sku-9 +2021-07-04,0,sku-9 +2021-07-11,15,sku-9 +2021-07-18,5,sku-9 +2021-07-25,0,sku-9 +2021-08-01,0,sku-9 +2021-08-08,0,sku-9 +2021-08-15,0,sku-9 +2021-08-22,0,sku-9 +2021-08-29,15,sku-9 +2021-09-05,22,sku-9 +2021-09-12,20,sku-9 +2021-09-19,0,sku-9 +2021-09-26,0,sku-9 +2021-10-03,0,sku-9 +2021-10-10,0,sku-9 +2021-10-17,5,sku-9 +2021-10-24,15,sku-9 +2021-10-31,10,sku-9 +2021-11-07,0,sku-9 +2021-11-14,5,sku-9 +2021-11-21,11,sku-9 +2021-11-28,25,sku-9 +2021-12-05,48,sku-9 +2021-12-12,7,sku-9 +2021-12-19,0,sku-9 +2021-12-26,5,sku-9 +2022-01-02,10,sku-9 +2022-01-09,0,sku-9 +2022-01-16,7,sku-9 +2022-01-23,15,sku-9 +2022-01-30,0,sku-9 +2022-02-06,5,sku-9 +2022-02-13,30,sku-9 +2022-02-20,20,sku-9 +2022-02-27,0,sku-9 +2022-03-06,25,sku-9 +2022-03-13,15,sku-9 +2022-03-20,15,sku-9 +2022-03-27,15,sku-9 +2022-04-03,10,sku-9 +2022-04-10,30,sku-9 +2022-04-17,0,sku-9 +2022-04-24,0,sku-9 +2022-05-01,0,sku-9 +2022-05-08,0,sku-9 +2022-05-15,20,sku-9 +2022-05-22,0,sku-9 +2022-05-29,0,sku-9 +2022-06-05,10,sku-9 +2022-06-12,0,sku-9 +2022-06-19,0,sku-9 +2022-06-26,20,sku-9 +2022-07-03,30,sku-9 +2022-07-10,0,sku-9 +2022-07-17,0,sku-9 +2022-07-24,10,sku-9 +2022-07-31,0,sku-9 +2022-08-07,30,sku-9 +2022-08-14,6,sku-9 +2022-08-21,6,sku-9 +2022-08-28,10,sku-9 +2022-09-04,27,sku-9 +2022-09-11,0,sku-9 +2022-09-18,10,sku-9 +2022-09-25,10,sku-9 +2022-10-02,10,sku-9 +2022-10-09,0,sku-9 +2022-10-16,0,sku-9 +2022-10-23,10,sku-9 +2022-10-30,20,sku-9 +2022-11-06,0,sku-9 +2022-11-13,0,sku-9 +2022-11-20,50,sku-9 +2022-11-27,0,sku-9 +2022-12-04,20,sku-9 +2022-12-11,20,sku-9 +2022-12-18,30,sku-9 +2022-12-25,20,sku-9 +2023-01-01,15,sku-9 +2023-01-08,0,sku-9 +2023-01-15,5,sku-9 +2023-01-22,0,sku-9 +2023-01-29,20,sku-9 +2023-02-05,25,sku-9 +2023-02-12,10,sku-9 +2023-02-19,30,sku-9 +2023-02-26,25,sku-9 +2023-03-05,13,sku-9 +2023-03-12,15,sku-9 +2023-03-19,5,sku-9 +2023-03-26,0,sku-9 +2023-04-02,30,sku-9 +2023-04-09,2,sku-9 +2023-04-16,30,sku-9 +2023-04-23,10,sku-9 diff --git a/data/demand_forecasting_demo_models.csv b/data/demand_forecasting_demo_models.csv new file mode 100644 index 0000000000000000000000000000000000000000..978827f24e0ed41266c3408eee5dbfa6e86d7ff7 --- /dev/null +++ b/data/demand_forecasting_demo_models.csv @@ -0,0 +1,11 @@ +sku,best_model,characteristic,RMSE +sku-0,fft_plus,continuous,20.29778313018444 +sku-1,holt_winters_plus,continuous,48.49842843820416 +sku-2,prophet_plus,fuzzy,39.28846310729568 +sku-3,prophet_plus,fuzzy_transient,14.593201789242087 +sku-4,prophet_plus,fuzzy,10.7747925198657 +sku-5,prophet_plus,fuzzy,28.33012802382216 +sku-6,ceif_plus,fuzzy,37.84242038358283 +sku-7,holt_winters_plus,continuous,15.959770854065722 +sku-8,prophet_plus,fuzzy,13.778467035419936 +sku-9,prophet_plus,fuzzy,12.843706019437128 diff --git a/data/fuzzy.csv b/data/fuzzy.csv new file mode 100644 index 0000000000000000000000000000000000000000..a3bdf98c3cd5c29289013f406d1fe90b1a2ae08f --- /dev/null +++ b/data/fuzzy.csv @@ -0,0 +1,261 @@ +datetime,y +2018-05-06,2 +2018-05-13,12 +2018-05-20,6 +2018-05-27,9 +2018-06-03,5 +2018-06-10,2 +2018-06-17,0 +2018-06-24,3 +2018-07-01,1 +2018-07-08,6 +2018-07-15,9 +2018-07-22,9 +2018-07-29,9 +2018-08-05,8 +2018-08-12,1 +2018-08-19,0 +2018-08-26,2 +2018-09-02,11 +2018-09-09,9 +2018-09-16,4 +2018-09-23,24 +2018-09-30,13 +2018-10-07,0 +2018-10-14,0 +2018-10-21,0 +2018-10-28,6 +2018-11-04,25 +2018-11-11,0 +2018-11-18,12 +2018-11-25,5 +2018-12-02,11 +2018-12-09,4 +2018-12-16,2 +2018-12-23,4 +2018-12-30,0 +2019-01-06,0 +2019-01-13,4 +2019-01-20,9 +2019-01-27,0 +2019-02-03,15 +2019-02-10,4 +2019-02-17,0 +2019-02-24,24 +2019-03-03,3 +2019-03-10,1 +2019-03-17,5 +2019-03-24,13 +2019-03-31,20 +2019-04-07,0 +2019-04-14,0 +2019-04-21,0 +2019-04-28,0 +2019-05-05,0 +2019-05-12,0 +2019-05-19,2 +2019-05-26,8 +2019-06-02,0 +2019-06-09,0 +2019-06-16,0 +2019-06-23,0 +2019-06-30,2 +2019-07-07,8 +2019-07-14,2 +2019-07-21,10 +2019-07-28,0 +2019-08-04,12 +2019-08-11,2 +2019-08-18,5 +2019-08-25,0 +2019-09-01,7 +2019-09-08,13 +2019-09-15,0 +2019-09-22,0 +2019-09-29,0 +2019-10-06,6 +2019-10-13,2 +2019-10-20,10 +2019-10-27,0 +2019-11-03,27 +2019-11-10,0 +2019-11-17,12 +2019-11-24,9 +2019-12-01,22 +2019-12-08,4 +2019-12-15,0 +2019-12-22,0 +2019-12-29,22 +2020-01-05,0 +2020-01-12,5 +2020-01-19,4 +2020-01-26,9 +2020-02-02,10 +2020-02-09,8 +2020-02-16,5 +2020-02-23,0 +2020-03-01,30 +2020-03-08,0 +2020-03-15,10 +2020-03-22,8 +2020-03-29,16 +2020-04-05,10 +2020-04-12,0 +2020-04-19,3 +2020-04-26,10 +2020-05-03,0 +2020-05-10,0 +2020-05-17,4 +2020-05-24,2 +2020-05-31,4 +2020-06-07,11 +2020-06-14,10 +2020-06-21,5 +2020-06-28,10 +2020-07-05,2 +2020-07-12,11 +2020-07-19,3 +2020-07-26,44 +2020-08-02,0 +2020-08-09,0 +2020-08-16,0 +2020-08-23,140 +2020-08-30,40 +2020-09-06,0 +2020-09-13,0 +2020-09-20,24 +2020-09-27,12 +2020-10-04,2 +2020-10-11,3 +2020-10-18,13 +2020-10-25,13 +2020-11-01,14 +2020-11-08,3 +2020-11-15,10 +2020-11-22,20 +2020-11-29,0 +2020-12-06,0 +2020-12-13,0 +2020-12-20,0 +2020-12-27,0 +2021-01-03,0 +2021-01-10,0 +2021-01-17,0 +2021-01-24,0 +2021-01-31,0 +2021-02-07,0 +2021-02-14,60 +2021-02-21,0 +2021-02-28,0 +2021-03-07,0 +2021-03-14,0 +2021-03-21,10 +2021-03-28,0 +2021-04-04,0 +2021-04-11,0 +2021-04-18,0 +2021-04-25,30 +2021-05-02,9 +2021-05-09,7 +2021-05-16,0 +2021-05-23,3 +2021-05-30,5 +2021-06-06,3 +2021-06-13,15 +2021-06-20,10 +2021-06-27,32 +2021-07-04,0 +2021-07-11,10 +2021-07-18,10 +2021-07-25,0 +2021-08-01,0 +2021-08-08,0 +2021-08-15,0 +2021-08-22,0 +2021-08-29,0 +2021-09-05,0 +2021-09-12,15 +2021-09-19,10 +2021-09-26,5 +2021-10-03,0 +2021-10-10,24 +2021-10-17,18 +2021-10-24,6 +2021-10-31,7 +2021-11-07,8 +2021-11-14,25 +2021-11-21,10 +2021-11-28,10 +2021-12-05,2 +2021-12-12,2 +2021-12-19,0 +2021-12-26,0 +2022-01-02,2 +2022-01-09,4 +2022-01-16,3 +2022-01-23,10 +2022-01-30,10 +2022-02-06,0 +2022-02-13,20 +2022-02-20,25 +2022-02-27,10 +2022-03-06,29 +2022-03-13,10 +2022-03-20,7 +2022-03-27,24 +2022-04-03,3 +2022-04-10,10 +2022-04-17,7 +2022-04-24,2 +2022-05-01,0 +2022-05-08,0 +2022-05-15,10 +2022-05-22,7 +2022-05-29,9 +2022-06-05,6 +2022-06-12,5 +2022-06-19,35 +2022-06-26,20 +2022-07-03,0 +2022-07-10,5 +2022-07-17,5 +2022-07-24,9 +2022-07-31,14 +2022-08-07,20 +2022-08-14,10 +2022-08-21,10 +2022-08-28,1 +2022-09-04,15 +2022-09-11,22 +2022-09-18,10 +2022-09-25,10 +2022-10-02,20 +2022-10-09,0 +2022-10-16,0 +2022-10-23,0 +2022-10-30,15 +2022-11-06,10 +2022-11-13,0 +2022-11-20,10 +2022-11-27,10 +2022-12-04,0 +2022-12-11,0 +2022-12-18,0 +2022-12-25,7 +2023-01-01,10 +2023-01-08,10 +2023-01-15,0 +2023-01-22,5 +2023-01-29,0 +2023-02-05,7 +2023-02-12,2 +2023-02-19,0 +2023-02-26,20 +2023-03-05,13 +2023-03-12,10 +2023-03-19,0 +2023-03-26,0 +2023-04-02,10 +2023-04-09,8 +2023-04-16,10 +2023-04-23,5 diff --git a/data/fuzzy_2.csv b/data/fuzzy_2.csv new file mode 100644 index 0000000000000000000000000000000000000000..6e7463f91510b72aa25800bacef4488e4894e82a --- /dev/null +++ b/data/fuzzy_2.csv @@ -0,0 +1,37 @@ +datetime,y +2020-01-31,50.0 +2020-02-29,0.0 +2020-03-31,300.0 +2020-04-30,0.0 +2020-05-31,500.0 +2020-06-30,1000.0 +2020-07-31,1000.0 +2020-08-31,500.0 +2020-09-30,0.0 +2020-10-31,1000.0 +2020-11-30,1000.0 +2020-12-31,500.0 +2021-01-31,525.0 +2021-02-28,750.0 +2021-03-31,250.0 +2021-04-30,0.0 +2021-05-31,0.0 +2021-06-30,975.0 +2021-07-31,975.0 +2021-08-31,1550.0 +2021-09-30,1309.0 +2021-10-31,2450.0 +2021-11-30,2360.0 +2021-12-31,3670.0 +2022-01-31,5530.0 +2022-02-28,2990.0 +2022-03-31,1050.0 +2022-04-30,2750.0 +2022-05-31,6124.0 +2022-06-30,3510.0 +2022-07-31,4000.0 +2022-08-31,3400.0 +2022-09-30,2500.0 +2022-10-31,3000.0 +2022-11-30,3800.0 +2022-12-31,2560.0 diff --git a/data/resource.md b/data/resource.md new file mode 100644 index 0000000000000000000000000000000000000000..049db10d7d562a2c543e7ac052d0b38b5aa8c0f2 --- /dev/null +++ b/data/resource.md @@ -0,0 +1 @@ +test.csv came from SKU 8972413061 from CID016 data from Isuzu \ No newline at end of file diff --git a/demo.py b/demo.py new file mode 100644 index 0000000000000000000000000000000000000000..0dc54bf4b794fab6cddbe8270a80969de1c08808 --- /dev/null +++ b/demo.py @@ -0,0 +1,121 @@ +import gradio as gr + +# from arguments import init_args +from gr_app.GradioApp import GradioApp +from gr_app import args + +app = GradioApp() + +demo = gr.Blocks(**args.block) + +with demo: + warning = gr.Warning() + gr.Markdown('# Sentient.io - Demand Forecasting') + gr.Markdown('Demo for demand forecasting pipeline') + + gr.Markdown('---') + + gr.Markdown('# Step 1 - Load Data') + with gr.Row(): + gr.Markdown(''' + Use button "Load Demo Data" for a quick demo with pre-loaded data. For uploading your own data, please follow the below requirements. + + ### Data Requirements: + - Time series data have to be in CSV format + - Data must contains datetime, y and sku columns. + - Multiple SKUs can put in to same CSV + - Time interval in data must be consistent + - Missing value have to be filled up + ''') + + with gr.Column(): + btn_load_data = gr.Button('Load Demo Data') + + gr.Markdown('------ or ------', + elem_classes="demo_app_text_center") + + file_upload_data = gr.File(**args.file_upload_data) + + df_ts_data = gr.DataFrame(**args.df_ts_data) + + gr.Markdown('---') + + gr.Markdown('# Step 2 - Model Selection') + + with gr.Row(): + gr.Markdown(''' + Train and evaluate model, identify data characteristics and select the best performing model. This step only need to run when the market regime shifted or when need to to re-select the model. + + - Click "Use Demo Data" Button if the demo data set has been loaded in Step 1 + - Else, directly proceed to model selection + - Only upload dataset if the model select process had been previously done, and you have save a copy of the CSV response. + ''') + + with gr.Column(): + btn_load_model_data = gr.Button('Use Demo Data') + btn_model_selection = gr.Button('Model Selection', variant='primary') + gr.Markdown('Upload previous model selection result (if have):') + file_upload_model_data = gr.File(**args.file_upload_model_data) + + df_model_data = gr.DataFrame() + file_model_data = gr.File() + + gr.Markdown('# Step 3 - Forecasting') + + with gr.Row(): + gr.Markdown( + 'This step only can be done when model selection process is completed.') + + with gr.Column(): + gr.Markdown(''' + ### Forecast Horizon + Max horizon will be 20% of provided data range. The unit will be same as the time series data time interval. + ''') + slider_forecast_horizon = gr.Slider(**args.slider_forecast_horizon) + + btn_forecast = gr.Button("Forecast", variant='primary') + + df_forecast = gr.DataFrame(**args.df_forecast) + file_forecast = gr.File() + + # ============= # + # = Functions = # + # ============= # + + btn_load_data.click( + app.btn_load_data__click, + [], + [df_ts_data, df_model_data, file_model_data, slider_forecast_horizon]) + + file_upload_data.upload( + app.file_upload_data__upload, + [file_upload_data], + [df_ts_data, df_model_data, file_model_data, slider_forecast_horizon]) + + file_upload_model_data.upload( + app.file_upload_model_data__upload, + [file_upload_model_data], + [df_model_data, file_model_data] + ) + + btn_load_model_data.click( + app.btn_load_model_data__click, + [], [df_model_data, file_model_data] + ) + + btn_model_selection.click( + app.btn_model_selection__click, + [], [df_model_data, file_model_data]) + + btn_forecast.click( + app.btn_forecast__click, + [], [df_forecast, file_forecast] + ) + + slider_forecast_horizon.change( + app.slider_forecast_horizon__update, + [slider_forecast_horizon], + []) + + +demo.launch() diff --git a/environment.yml b/environment.yml new file mode 100644 index 0000000000000000000000000000000000000000..e458bf33128fd1e24ac172a508cf17f31fad2f83 --- /dev/null +++ b/environment.yml @@ -0,0 +1,222 @@ +name: demand-forecasting +channels: + - plotly + - conda-forge + - anaconda + - defaults +dependencies: + - aiofiles=22.1.0 + - aiosqlite=0.18.0 + - anyio=3.5.0 + # - appnope=0.1.2 + - argon2-cffi=21.3.0 + - argon2-cffi-bindings=21.2.0 + - asttokens=2.0.5 + - attrs=22.1.0 + - babel=2.11.0 + - backcall=0.2.0 + - beautifulsoup4=4.12.2 + - blas=1.0 + - bleach=4.1.0 + - bottleneck=1.3.5 + - brotli=1.0.9 + - brotli-bin=1.0.9 + - brotlipy=0.7.0 + - bzip2=1.0.8 + - ca-certificates=2022.4.26 + - cctools_osx-arm64=949.0.1 + - certifi=2022.6.15 + - cffi=1.15.1 + - charset-normalizer=2.0.4 + - clang=14.0.6 + - clang-14=14.0.6 + - clang_osx-arm64=14.0.6 + - clangxx=14.0.6 + - clangxx_osx-arm64=14.0.6 + - cmdstan=2.31.0 + - cmdstanpy=1.1.0 + - comm=0.1.2 + - compiler-rt=14.0.6 + - compiler-rt_osx-arm64=14.0.6 + - convertdate=2.3.2 + - cryptography=41.0.3 + - cycler=0.11.0 + - debugpy=1.6.7 + - decorator=5.1.1 + - defusedxml=0.7.1 + - entrypoints=0.4 + - ephem=4.1.2 + - exceptiongroup=1.0.4 + - executing=0.8.3 + - freetype=2.12.1 + - giflib=5.2.1 + - holidays=0.29 + - icu=73.1 + - idna=3.4 + - importlib_resources=5.2.0 + - ipykernel=6.25.0 + - ipython=8.15.0 + - ipython_genutils=0.2.0 + - jedi=0.18.1 + - jinja2=3.1.2 + - joblib=1.3.2 + - jpeg=9e + - json5=0.9.6 + - jsonschema=4.17.3 + - jupyter_client=7.4.9 + - jupyter_core=5.3.0 + - jupyter_events=0.6.3 + - jupyter_server=1.23.4 + - jupyter_server_fileid=0.9.0 + - jupyter_server_ydoc=0.8.0 + - jupyter_ydoc=0.2.4 + - jupyterlab=3.6.3 + - jupyterlab_pygments=0.1.2 + - jupyterlab_server=2.22.0 + - lcms2=2.12 + - ld64_osx-arm64=530 + - ldid=2.1.5 + - lerc=3.0 + - libbrotlicommon=1.0.9 + - libbrotlidec=1.0.9 + - libbrotlienc=1.0.9 + - libclang-cpp14=14.0.6 + - libcxx=14.0.6 + - libdeflate=1.17 + - libffi=3.4.4 + # - libgfortran=5.0.0 + - libgfortran5=11.3.0 + - libiconv=1.16 + - libllvm14=14.0.6 + - libopenblas=0.3.21 + - libpng=1.6.39 + - libsodium=1.0.18 + - libtiff=4.5.1 + - libwebp=1.3.2 + - libwebp-base=1.3.2 + - libxml2=2.10.4 + - libxslt=1.1.37 + - llvm-openmp=14.0.6 + - llvm-tools=14.0.6 + - lunarcalendar=0.0.9 + - lxml=4.9.3 + - lz4-c=1.9.4 + - make=4.3 + - markupsafe=2.1.1 + - matplotlib=3.7.2 + - matplotlib-base=3.7.2 + - matplotlib-inline=0.1.6 + - mistune=0.8.4 + - munkres=1.1.4 + - nbclassic=0.5.5 + - nbclient=0.5.13 + - nbconvert=6.5.4 + - nbformat=5.9.2 + - ncurses=6.4 + - nest-asyncio=1.5.6 + - notebook=6.5.4 + - notebook-shim=0.2.2 + - numexpr=2.8.4 + - numpy=1.25.2 + - numpy-base=1.25.2 + - openssl=3.1.3 + - packaging=23.1 + - pandocfilters=1.5.0 + - parso=0.8.3 + - pexpect=4.8.0 + - pickleshare=0.7.5 + - pip=23.2.1 + - platformdirs=3.10.0 + - plotly=5.16.1 + - prometheus_client=0.14.1 + - prompt-toolkit=3.0.36 + - prophet=1.1.4 + - psutil=5.9.0 + - ptyprocess=0.7.0 + - pure_eval=0.2.2 + - pycparser=2.21 + - pygments=2.15.1 + - pymeeus=0.5.11 + - pyopenssl=23.2.0 + - pyparsing=3.0.9 + - pyrsistent=0.18.0 + - pysocks=1.7.1 + - python=3.10.12 + - python-dateutil=2.8.2 + - python-dotenv=1.0.0 + - python-fastjsonschema=2.16.2 + - python-json-logger=2.0.7 + - pytz=2023.3.post1 + - pyyaml=6.0 + - pyzmq=23.2.0 + - readline=8.2 + - requests=2.31.0 + - rfc3339-validator=0.1.4 + - rfc3986-validator=0.1.1 + - scikit-learn=1.3.0 + - scipy=1.11.1 + - seaborn=0.11.2 + - send2trash=1.8.0 + - setuptools=68.0.0 + - six=1.16.0 + - sniffio=1.2.0 + - soupsieve=2.4 + - sqlite=3.41.2 + - stack_data=0.2.0 + - tapi=1100.0.11 + - tbb=2021.8.0 + - tbb-devel=2021.8.0 + - tenacity=8.2.2 + - terminado=0.17.1 + - threadpoolctl=3.2.0 + - tinycss2=1.2.1 + - tk=8.6.12 + - tomli=2.0.1 + - tornado=6.3.2 + - tqdm=4.65.0 + - traitlets=5.7.1 + - typing-extensions=4.7.1 + - typing_extensions=4.7.1 + - urllib3=1.26.16 + - wcwidth=0.2.5 + - webencodings=0.5.1 + - websocket-client=0.58.0 + - wheel=0.38.4 + - xz=5.4.2 + - y-py=0.5.9 + - yaml=0.2.5 + - ypy-websocket=0.8.2 + - zeromq=4.3.4 + - zipp=3.11.0 + - zlib=1.2.13 + - zstd=1.5.5 + - pip: + - altair==5.0.1 + - annotated-types==0.5.0 + - click==8.1.7 + - contourpy==1.1.0 + - fastapi==0.101.1 + - ffmpy==0.3.1 + - filelock==3.12.2 + - fonttools==4.42.1 + - fsspec==2023.6.0 + - gradio==3.41.0 + - gradio-client==0.5.0 + - h11==0.14.0 + - httpcore==0.17.3 + - httpx==0.24.1 + - huggingface-hub==0.16.4 + - kiwisolver==1.4.5 + - orjson==3.9.5 + - pandas==2.0.3 + - pillow==10.0.0 + - pydantic==2.3.0 + - pydantic-core==2.6.3 + - pydub==0.25.1 + - python-multipart==0.0.6 + - semantic-version==2.10.0 + - starlette==0.27.0 + - toolz==0.12.0 + - tzdata==2023.3 + - uvicorn==0.23.2 + - websockets==11.0.3 diff --git a/forecast_result.csv b/forecast_result.csv new file mode 100644 index 0000000000000000000000000000000000000000..c12decfa0fe0ee30cd24f9360d643569e843bfcf --- /dev/null +++ b/forecast_result.csv @@ -0,0 +1,521 @@ +datetime,y,sku +2023-04-23,20,sku-0 +2023-04-30,19,sku-0 +2023-05-07,25,sku-0 +2023-05-14,27,sku-0 +2023-05-21,20,sku-0 +2023-05-28,21,sku-0 +2023-06-04,27,sku-0 +2023-06-11,27,sku-0 +2023-06-18,27,sku-0 +2023-06-25,27,sku-0 +2023-07-02,27,sku-0 +2023-07-09,27,sku-0 +2023-07-16,27,sku-0 +2023-07-23,27,sku-0 +2023-07-30,27,sku-0 +2023-08-06,27,sku-0 +2023-08-13,27,sku-0 +2023-08-20,27,sku-0 +2023-08-27,27,sku-0 +2023-09-03,24,sku-0 +2023-09-10,24,sku-0 +2023-09-17,20,sku-0 +2023-09-24,27,sku-0 +2023-10-01,27,sku-0 +2023-10-08,20,sku-0 +2023-10-15,28,sku-0 +2023-10-22,21,sku-0 +2023-10-29,27,sku-0 +2023-11-05,34,sku-0 +2023-11-12,19,sku-0 +2023-11-19,28,sku-0 +2023-11-26,27,sku-0 +2023-12-03,22,sku-0 +2023-12-10,20,sku-0 +2023-12-17,19,sku-0 +2023-12-24,27,sku-0 +2023-12-31,24,sku-0 +2024-01-07,33,sku-0 +2024-01-14,20,sku-0 +2024-01-21,27,sku-0 +2024-01-28,27,sku-0 +2024-02-04,27,sku-0 +2024-02-11,28,sku-0 +2024-02-18,27,sku-0 +2024-02-25,20,sku-0 +2024-03-03,32,sku-0 +2024-03-10,19,sku-0 +2024-03-17,20,sku-0 +2024-03-24,27,sku-0 +2024-03-31,21,sku-0 +2024-04-07,21,sku-0 +2024-04-14,19,sku-0 +2023-04-09,77,sku-1 +2023-04-16,78,sku-1 +2023-04-23,78,sku-1 +2023-04-30,79,sku-1 +2023-05-07,80,sku-1 +2023-05-14,80,sku-1 +2023-05-21,81,sku-1 +2023-05-28,82,sku-1 +2023-06-04,82,sku-1 +2023-06-11,83,sku-1 +2023-06-18,84,sku-1 +2023-06-25,84,sku-1 +2023-07-02,85,sku-1 +2023-07-09,86,sku-1 +2023-07-16,86,sku-1 +2023-07-23,87,sku-1 +2023-07-30,88,sku-1 +2023-08-06,88,sku-1 +2023-08-13,89,sku-1 +2023-08-20,90,sku-1 +2023-08-27,90,sku-1 +2023-09-03,91,sku-1 +2023-09-10,91,sku-1 +2023-09-17,92,sku-1 +2023-09-24,93,sku-1 +2023-10-01,93,sku-1 +2023-10-08,94,sku-1 +2023-10-15,95,sku-1 +2023-10-22,95,sku-1 +2023-10-29,96,sku-1 +2023-11-05,97,sku-1 +2023-11-12,97,sku-1 +2023-11-19,98,sku-1 +2023-11-26,99,sku-1 +2023-12-03,99,sku-1 +2023-12-10,100,sku-1 +2023-12-17,101,sku-1 +2023-12-24,101,sku-1 +2023-12-31,102,sku-1 +2024-01-07,103,sku-1 +2024-01-14,103,sku-1 +2024-01-21,104,sku-1 +2024-01-28,105,sku-1 +2024-02-04,105,sku-1 +2024-02-11,106,sku-1 +2024-02-18,107,sku-1 +2024-02-25,107,sku-1 +2024-03-03,108,sku-1 +2024-03-10,109,sku-1 +2024-03-17,109,sku-1 +2024-03-24,110,sku-1 +2024-03-31,111,sku-1 +2022-12-04,0,sku-2 +2022-12-11,46,sku-2 +2022-12-18,0,sku-2 +2022-12-25,46,sku-2 +2023-01-01,0,sku-2 +2023-01-08,53,sku-2 +2023-01-15,0,sku-2 +2023-01-22,46,sku-2 +2023-01-29,0,sku-2 +2023-02-05,46,sku-2 +2023-02-12,48,sku-2 +2023-02-19,0,sku-2 +2023-02-26,50,sku-2 +2023-03-05,0,sku-2 +2023-03-12,49,sku-2 +2023-03-19,0,sku-2 +2023-03-26,49,sku-2 +2023-04-02,0,sku-2 +2023-04-09,55,sku-2 +2023-04-16,0,sku-2 +2023-04-23,49,sku-2 +2023-04-30,0,sku-2 +2023-05-07,49,sku-2 +2023-05-14,51,sku-2 +2023-05-21,0,sku-2 +2023-05-28,53,sku-2 +2023-06-04,0,sku-2 +2023-06-11,51,sku-2 +2023-06-18,0,sku-2 +2023-06-25,51,sku-2 +2023-07-02,0,sku-2 +2023-07-09,58,sku-2 +2023-07-16,0,sku-2 +2023-07-23,51,sku-2 +2023-07-30,0,sku-2 +2023-08-06,51,sku-2 +2023-08-13,53,sku-2 +2023-08-20,0,sku-2 +2023-08-27,55,sku-2 +2023-09-03,0,sku-2 +2023-09-10,53,sku-2 +2023-09-17,0,sku-2 +2023-09-24,53,sku-2 +2023-10-01,0,sku-2 +2023-10-08,60,sku-2 +2023-10-15,0,sku-2 +2023-10-22,54,sku-2 +2023-10-29,0,sku-2 +2023-11-05,53,sku-2 +2023-11-12,56,sku-2 +2023-11-19,0,sku-2 +2023-11-26,58,sku-2 +2023-04-23,0,sku-3 +2023-04-30,0,sku-3 +2023-05-07,17,sku-3 +2023-05-14,0,sku-3 +2023-05-21,0,sku-3 +2023-05-28,20,sku-3 +2023-06-04,0,sku-3 +2023-06-11,18,sku-3 +2023-06-18,0,sku-3 +2023-06-25,0,sku-3 +2023-07-02,19,sku-3 +2023-07-09,0,sku-3 +2023-07-16,0,sku-3 +2023-07-23,19,sku-3 +2023-07-30,0,sku-3 +2023-08-06,0,sku-3 +2023-08-13,0,sku-3 +2023-08-20,14,sku-3 +2023-08-27,0,sku-3 +2023-09-03,0,sku-3 +2023-09-10,19,sku-3 +2023-09-17,0,sku-3 +2023-09-24,0,sku-3 +2023-10-01,18,sku-3 +2023-10-08,0,sku-3 +2023-10-15,0,sku-3 +2023-10-22,21,sku-3 +2023-10-29,0,sku-3 +2023-11-05,0,sku-3 +2023-11-12,19,sku-3 +2023-11-19,0,sku-3 +2023-11-26,0,sku-3 +2023-12-03,20,sku-3 +2023-12-10,0,sku-3 +2023-12-17,0,sku-3 +2023-12-24,19,sku-3 +2023-12-31,0,sku-3 +2024-01-07,0,sku-3 +2024-01-14,0,sku-3 +2024-01-21,15,sku-3 +2024-01-28,0,sku-3 +2024-02-04,0,sku-3 +2024-02-11,20,sku-3 +2024-02-18,0,sku-3 +2024-02-25,0,sku-3 +2024-03-03,19,sku-3 +2024-03-10,0,sku-3 +2024-03-17,0,sku-3 +2024-03-24,21,sku-3 +2024-03-31,0,sku-3 +2024-04-07,0,sku-3 +2024-04-14,19,sku-3 +2023-04-23,0,sku-4 +2023-04-30,18,sku-4 +2023-05-07,12,sku-4 +2023-05-14,10,sku-4 +2023-05-21,12,sku-4 +2023-05-28,0,sku-4 +2023-06-04,11,sku-4 +2023-06-11,0,sku-4 +2023-06-18,11,sku-4 +2023-06-25,13,sku-4 +2023-07-02,0,sku-4 +2023-07-09,18,sku-4 +2023-07-16,12,sku-4 +2023-07-23,10,sku-4 +2023-07-30,12,sku-4 +2023-08-06,0,sku-4 +2023-08-13,11,sku-4 +2023-08-20,0,sku-4 +2023-08-27,11,sku-4 +2023-09-03,13,sku-4 +2023-09-10,0,sku-4 +2023-09-17,18,sku-4 +2023-09-24,12,sku-4 +2023-10-01,10,sku-4 +2023-10-08,12,sku-4 +2023-10-15,0,sku-4 +2023-10-22,12,sku-4 +2023-10-29,0,sku-4 +2023-11-05,11,sku-4 +2023-11-12,13,sku-4 +2023-11-19,0,sku-4 +2023-11-26,18,sku-4 +2023-12-03,12,sku-4 +2023-12-10,10,sku-4 +2023-12-17,12,sku-4 +2023-12-24,0,sku-4 +2023-12-31,12,sku-4 +2024-01-07,0,sku-4 +2024-01-14,11,sku-4 +2024-01-21,13,sku-4 +2024-01-28,0,sku-4 +2024-02-04,18,sku-4 +2024-02-11,12,sku-4 +2024-02-18,10,sku-4 +2024-02-25,12,sku-4 +2024-03-03,0,sku-4 +2024-03-10,12,sku-4 +2024-03-17,0,sku-4 +2024-03-24,11,sku-4 +2024-03-31,13,sku-4 +2024-04-07,0,sku-4 +2024-04-14,18,sku-4 +2023-04-23,0,sku-5 +2023-04-30,25,sku-5 +2023-05-07,28,sku-5 +2023-05-14,0,sku-5 +2023-05-21,25,sku-5 +2023-05-28,0,sku-5 +2023-06-04,34,sku-5 +2023-06-11,0,sku-5 +2023-06-18,38,sku-5 +2023-06-25,0,sku-5 +2023-07-02,39,sku-5 +2023-07-09,0,sku-5 +2023-07-16,23,sku-5 +2023-07-23,0,sku-5 +2023-07-30,25,sku-5 +2023-08-06,28,sku-5 +2023-08-13,0,sku-5 +2023-08-20,25,sku-5 +2023-08-27,0,sku-5 +2023-09-03,35,sku-5 +2023-09-10,0,sku-5 +2023-09-17,38,sku-5 +2023-09-24,0,sku-5 +2023-10-01,39,sku-5 +2023-10-08,0,sku-5 +2023-10-15,24,sku-5 +2023-10-22,0,sku-5 +2023-10-29,26,sku-5 +2023-11-05,29,sku-5 +2023-11-12,0,sku-5 +2023-11-19,26,sku-5 +2023-11-26,0,sku-5 +2023-12-03,35,sku-5 +2023-12-10,0,sku-5 +2023-12-17,39,sku-5 +2023-12-24,0,sku-5 +2023-12-31,39,sku-5 +2024-01-07,0,sku-5 +2024-01-14,24,sku-5 +2024-01-21,0,sku-5 +2024-01-28,26,sku-5 +2024-02-04,29,sku-5 +2024-02-11,0,sku-5 +2024-02-18,26,sku-5 +2024-02-25,0,sku-5 +2024-03-03,35,sku-5 +2024-03-10,0,sku-5 +2024-03-17,39,sku-5 +2024-03-24,0,sku-5 +2024-03-31,40,sku-5 +2024-04-07,0,sku-5 +2024-04-14,25,sku-5 +2023-04-16,0,sku-6 +2023-04-23,0,sku-6 +2023-04-30,0,sku-6 +2023-05-07,65,sku-6 +2023-05-14,0,sku-6 +2023-05-21,0,sku-6 +2023-05-28,0,sku-6 +2023-06-04,65,sku-6 +2023-06-11,0,sku-6 +2023-06-18,0,sku-6 +2023-06-25,0,sku-6 +2023-07-02,39,sku-6 +2023-07-09,0,sku-6 +2023-07-16,0,sku-6 +2023-07-23,0,sku-6 +2023-07-30,40,sku-6 +2023-08-06,0,sku-6 +2023-08-13,0,sku-6 +2023-08-20,0,sku-6 +2023-08-27,62,sku-6 +2023-09-03,0,sku-6 +2023-09-10,0,sku-6 +2023-09-17,0,sku-6 +2023-09-24,70,sku-6 +2023-10-01,0,sku-6 +2023-10-08,0,sku-6 +2023-10-15,0,sku-6 +2023-10-22,0,sku-6 +2023-10-29,0,sku-6 +2023-11-05,38,sku-6 +2023-11-12,0,sku-6 +2023-11-19,0,sku-6 +2023-11-26,0,sku-6 +2023-12-03,63,sku-6 +2023-12-10,0,sku-6 +2023-12-17,0,sku-6 +2023-12-24,0,sku-6 +2023-12-31,0,sku-6 +2024-01-07,0,sku-6 +2024-01-14,0,sku-6 +2024-01-21,0,sku-6 +2024-01-28,0,sku-6 +2024-02-04,44,sku-6 +2024-02-11,0,sku-6 +2024-02-18,0,sku-6 +2024-02-25,0,sku-6 +2024-03-03,0,sku-6 +2024-03-10,61,sku-6 +2024-03-17,0,sku-6 +2024-03-24,0,sku-6 +2024-03-31,0,sku-6 +2024-04-07,40,sku-6 +2023-04-23,17,sku-7 +2023-04-30,17,sku-7 +2023-05-07,17,sku-7 +2023-05-14,17,sku-7 +2023-05-21,17,sku-7 +2023-05-28,17,sku-7 +2023-06-04,17,sku-7 +2023-06-11,17,sku-7 +2023-06-18,17,sku-7 +2023-06-25,17,sku-7 +2023-07-02,17,sku-7 +2023-07-09,17,sku-7 +2023-07-16,17,sku-7 +2023-07-23,17,sku-7 +2023-07-30,17,sku-7 +2023-08-06,17,sku-7 +2023-08-13,17,sku-7 +2023-08-20,17,sku-7 +2023-08-27,17,sku-7 +2023-09-03,17,sku-7 +2023-09-10,17,sku-7 +2023-09-17,17,sku-7 +2023-09-24,17,sku-7 +2023-10-01,17,sku-7 +2023-10-08,17,sku-7 +2023-10-15,17,sku-7 +2023-10-22,17,sku-7 +2023-10-29,17,sku-7 +2023-11-05,17,sku-7 +2023-11-12,17,sku-7 +2023-11-19,17,sku-7 +2023-11-26,17,sku-7 +2023-12-03,17,sku-7 +2023-12-10,17,sku-7 +2023-12-17,17,sku-7 +2023-12-24,17,sku-7 +2023-12-31,17,sku-7 +2024-01-07,17,sku-7 +2024-01-14,17,sku-7 +2024-01-21,17,sku-7 +2024-01-28,17,sku-7 +2024-02-04,17,sku-7 +2024-02-11,17,sku-7 +2024-02-18,17,sku-7 +2024-02-25,17,sku-7 +2024-03-03,17,sku-7 +2024-03-10,17,sku-7 +2024-03-17,17,sku-7 +2024-03-24,17,sku-7 +2024-03-31,17,sku-7 +2024-04-07,17,sku-7 +2024-04-14,17,sku-7 +2023-04-23,15,sku-8 +2023-04-30,0,sku-8 +2023-05-07,16,sku-8 +2023-05-14,18,sku-8 +2023-05-21,0,sku-8 +2023-05-28,17,sku-8 +2023-06-04,15,sku-8 +2023-06-11,0,sku-8 +2023-06-18,17,sku-8 +2023-06-25,13,sku-8 +2023-07-02,16,sku-8 +2023-07-09,0,sku-8 +2023-07-16,16,sku-8 +2023-07-23,18,sku-8 +2023-07-30,0,sku-8 +2023-08-06,18,sku-8 +2023-08-13,15,sku-8 +2023-08-20,0,sku-8 +2023-08-27,17,sku-8 +2023-09-03,13,sku-8 +2023-09-10,16,sku-8 +2023-09-17,0,sku-8 +2023-09-24,16,sku-8 +2023-10-01,18,sku-8 +2023-10-08,0,sku-8 +2023-10-15,18,sku-8 +2023-10-22,15,sku-8 +2023-10-29,0,sku-8 +2023-11-05,18,sku-8 +2023-11-12,14,sku-8 +2023-11-19,17,sku-8 +2023-11-26,0,sku-8 +2023-12-03,17,sku-8 +2023-12-10,19,sku-8 +2023-12-17,0,sku-8 +2023-12-24,18,sku-8 +2023-12-31,16,sku-8 +2024-01-07,0,sku-8 +2024-01-14,18,sku-8 +2024-01-21,14,sku-8 +2024-01-28,17,sku-8 +2024-02-04,0,sku-8 +2024-02-11,17,sku-8 +2024-02-18,19,sku-8 +2024-02-25,0,sku-8 +2024-03-03,19,sku-8 +2024-03-10,16,sku-8 +2024-03-17,0,sku-8 +2024-03-24,18,sku-8 +2024-03-31,14,sku-8 +2024-04-07,17,sku-8 +2024-04-14,0,sku-8 +2023-04-23,0,sku-9 +2023-04-30,19,sku-9 +2023-05-07,0,sku-9 +2023-05-14,17,sku-9 +2023-05-21,0,sku-9 +2023-05-28,21,sku-9 +2023-06-04,0,sku-9 +2023-06-11,19,sku-9 +2023-06-18,0,sku-9 +2023-06-25,17,sku-9 +2023-07-02,0,sku-9 +2023-07-09,19,sku-9 +2023-07-16,0,sku-9 +2023-07-23,19,sku-9 +2023-07-30,0,sku-9 +2023-08-06,20,sku-9 +2023-08-13,0,sku-9 +2023-08-20,18,sku-9 +2023-08-27,0,sku-9 +2023-09-03,21,sku-9 +2023-09-10,0,sku-9 +2023-09-17,20,sku-9 +2023-09-24,0,sku-9 +2023-10-01,18,sku-9 +2023-10-08,0,sku-9 +2023-10-15,20,sku-9 +2023-10-22,0,sku-9 +2023-10-29,20,sku-9 +2023-11-05,0,sku-9 +2023-11-12,20,sku-9 +2023-11-19,0,sku-9 +2023-11-26,18,sku-9 +2023-12-03,0,sku-9 +2023-12-10,22,sku-9 +2023-12-17,0,sku-9 +2023-12-24,20,sku-9 +2023-12-31,0,sku-9 +2024-01-07,18,sku-9 +2024-01-14,0,sku-9 +2024-01-21,20,sku-9 +2024-01-28,0,sku-9 +2024-02-04,20,sku-9 +2024-02-11,0,sku-9 +2024-02-18,21,sku-9 +2024-02-25,0,sku-9 +2024-03-03,19,sku-9 +2024-03-10,0,sku-9 +2024-03-17,22,sku-9 +2024-03-24,0,sku-9 +2024-03-31,21,sku-9 +2024-04-07,0,sku-9 +2024-04-14,19,sku-9 diff --git a/gr_app/GradioApp.py b/gr_app/GradioApp.py new file mode 100644 index 0000000000000000000000000000000000000000..eb1c960a07588a53ac9fb54630e4b582c08ff352 --- /dev/null +++ b/gr_app/GradioApp.py @@ -0,0 +1,162 @@ +import pandas as pd +import math +from src.main import DemandForecasting + +import gradio as gr + + +class GradioApp(): + def __init__(self): + self.forecaster = DemandForecasting() + + self.ts_data = None # Time series data for model training and forecasting + self.model_data = None + self.skus = None + + self.forecast_horizon = 1 + + def __set_ts_data(self, path): + self.ts_data = pd.read_csv(path) + self.skus = self.ts_data['sku'].unique() + + self.model_data = pd.DataFrame( + { + 'sku': self.skus, + 'best_model': '', + 'characteristic': '', + 'RMSE': '' + } + ) + + def __set_model(self, model_df): + if (self.skus is None): + raise gr.Error( + 'Incorrect SKUs, time series data must be loaded and SKUs must match.') + if (set(self.skus) - set(model_df['sku']) != set()): + raise gr.Error( + 'SKUs in provided model select data does not match SKUs in timeseries data.' + ) + + self.model_data = model_df + + def btn_load_data__click(self): + print('btn_load_data__click') + self.__set_ts_data('./data/demand_forecasting_demo_data.csv') + + return (self.update__df_ts_data(), + self.update__df_model_data(), + self.update__file_model_data(), + self.update__slider_forecast_horizon()) + + def file_upload_data__upload(self, file): + self.__set_ts_data(file.name) + + return (self.update__df_ts_data(), + self.update__df_model_data(), + self.update__file_model_data(), + self.update__slider_forecast_horizon()) + + def file_upload_model_data__upload(self, file): + model_df = pd.read_csv(file.name) + self.__set_model(model_df) + + return (self.update__df_model_data(), + self.update__file_model_data()) + + def btn_load_model_data__click(self): + + model_df = pd.read_csv( + './data/demand_forecasting_demo_models.csv') + self.__set_model(model_df) + + return (self.update__df_model_data(), + self.update__file_model_data()) + + def btn_model_selection__click(self): + print('btn_model_selection__click') + for sku in self.skus: + print('Selecting model ', sku) + data = self.ts_data[self.ts_data['sku'] == sku] + + # ----------------- # + # Feature Selection # + # ----------------- # + res = self.forecaster.forecast( + data, 0, model='all', run_test=True) + + self.model_data.loc[self.model_data['sku'] == + sku, 'characteristic'] = res['characteristic'] + + self.model_data.loc[self.model_data['sku'] == + sku, 'best_model'] = res['forecast'][0]['model'] + self.model_data.loc[self.model_data['sku'] == + sku, 'RMSE'] = math.round(res['forecast'][0]['RMSE'], 2) + + return (self.update__df_model_data(), + self.update__file_model_data()) + + def slider_forecast_horizon__update(self, slider): + # print('slider_forecast_horizon__update ', slider) + self.forecast_horizon = slider + + def btn_forecast__click(self): + # ----------- # + # Forecasting # + # ----------- # + forecasts = [] + for sku in self.skus: + print('Forecasting ', sku) + data = self.ts_data[self.ts_data['sku'] == sku] + + # Drop sku column first, for now the pipeline doesn't take this column + data = data.drop('sku', axis=1) + + model_data = self.model_data[self.model_data['sku'] == sku] + print(model_data) + model = model_data['best_model'].tolist()[0] + characteristic = model_data['characteristic'].tolist()[0] + + # ----------------- # + # Feature Selection # + # ----------------- # + print(model, characteristic) + res = self.forecaster.forecast( + data, self.forecast_horizon, model=model, run_test=False, characteristic=characteristic) + forecast = pd.DataFrame( + res['forecast'][0]['forecast'], columns=['datetime', 'y']) + forecast['sku'] = sku + forecasts.append(forecast) + + self.forecast = pd.concat(forecasts) + + return (self.update__df_forecast(), + self.update__file_forecast()) + + # ======== # + # Updaters # + # ======== # + + def update__file_model_data(self): + self.model_data.to_csv('./best_models.csv', index=False) + return gr.File.update(value='./best_models.csv') + + def update__df_model_data(self): + return gr.DataFrame.update(value=self.model_data) + + def update__df_ts_data(self): + return gr.DataFrame.update(value=self.ts_data) + + def update__slider_forecast_horizon(self): + sku = self.skus[0] + + max_horizon = int( + self.ts_data[self.ts_data['sku'] == sku].shape[0] * 0.2) + + return gr.Slider.update(maximum=max_horizon) + + def update__df_forecast(self): + return gr.DataFrame.update(self.forecast) + + def update__file_forecast(self): + self.forecast.to_csv('./forecast_result.csv', index=False) + return gr.File.update(value='./forecast_result.csv') diff --git a/gr_app/__init__.py b/gr_app/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/gr_app/__init__.py @@ -0,0 +1 @@ + diff --git a/gr_app/__pycache__/GradioApp.cpython-310.pyc b/gr_app/__pycache__/GradioApp.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..061b915ad06bd6008a5ba8c7dd70454596e26eab Binary files /dev/null and b/gr_app/__pycache__/GradioApp.cpython-310.pyc differ diff --git a/gr_app/__pycache__/__init__.cpython-310.pyc b/gr_app/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..91e1eddf7c715cc93227e96a8dbb18282e03fcf1 Binary files /dev/null and b/gr_app/__pycache__/__init__.cpython-310.pyc differ diff --git a/gr_app/__pycache__/args.cpython-310.pyc b/gr_app/__pycache__/args.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..717a7a58ca28344f18887bfb2ae1ceefbd5bde4c Binary files /dev/null and b/gr_app/__pycache__/args.cpython-310.pyc differ diff --git a/gr_app/args.py b/gr_app/args.py new file mode 100644 index 0000000000000000000000000000000000000000..c783d78cf9aefc1463f60ce61179b51d9ef4193b --- /dev/null +++ b/gr_app/args.py @@ -0,0 +1,16 @@ +block = { + 'css': + ''' + .demo_app_group {padding: 1rem !important; color:red} + + .demo_app_text_center {text-align: center} + ''' +} + +df_ts_data = {'height': 200} +df_forecast = {'height': 200} + +file_upload_data = {'height': 80} +file_upload_model_data = {'height': 80} + +slider_forecast_horizon = {'label': '', 'minimum': 1, 'step': 1, 'interactive':True} diff --git a/model.csv b/model.csv new file mode 100644 index 0000000000000000000000000000000000000000..c834b836e56213929d2cd9f5750ca14d8eba1b2c --- /dev/null +++ b/model.csv @@ -0,0 +1,11 @@ +,sku,best_model,characteristic,RMSE +0,sku-0,fft_plus,continuous,20.29778313018444 +1,sku-1,holt_winters_plus,continuous,48.49842843820416 +2,sku-2,prophet_plus,fuzzy,39.28846310729568 +3,sku-3,prophet_plus,fuzzy_transient,14.593201789242087 +4,sku-4,prophet_plus,fuzzy,10.7747925198657 +5,sku-5,prophet_plus,fuzzy,28.33012802382216 +6,sku-6,ceif_plus,fuzzy,37.84242038358283 +7,sku-7,holt_winters_plus,continuous,15.959770854065722 +8,sku-8,prophet_plus,fuzzy,13.778467035419936 +9,sku-9,prophet_plus,fuzzy,12.843706019437128 diff --git a/notebooks/res.txt b/notebooks/res.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/notebooks/test.ipynb b/notebooks/test.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..bd0ebb25a33b35a8adb15f726b0ef2499634d6ca --- /dev/null +++ b/notebooks/test.ipynb @@ -0,0 +1,828 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/qiaozhang/miniconda3/envs/demand-forecasting/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "apikey still available, logged in\n" + ] + } + ], + "source": [ + "# To call functions outside of this folder\n", + "import sys \n", + "sys.path.insert(0, '..')\n", + "\n", + "# Load libraries \n", + "import pandas as pd\n", + "import json\n", + "import matplotlib.pyplot as plt\n", + "\n", + "# Load main demand forecasting class\n", + "from src.main import DemandForecasting\n", + "\n", + "df = DemandForecasting()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "ts = pd.read_csv('../data/fuzzy.csv')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Start profiling, note, predictability been disabled\n", + "Change point detection\n", + "callindg model: prophet_plus\n", + "has_idsc_model\n" + ] + } + ], + "source": [ + "# Step 1 - evaluate RMSE\n", + "# res = df.forecast(ts, 10, model='all', run_test=False, characteristic='fuzzy')\n", + "\n", + "# Step 2 - forecast\n", + "res = df.forecast(ts, 30, model='prophet_plus', characteristic='fuzzy')" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'characteristic': 'fuzzy',\n", + " 'forecast': [{'model': 'prophet_plus',\n", + " 'forecast': {'datetime': DatetimeIndex(['2023-04-23', '2023-04-30', '2023-05-07', '2023-05-14',\n", + " '2023-05-21', '2023-05-28', '2023-06-04', '2023-06-11',\n", + " '2023-06-18', '2023-06-25', '2023-07-02', '2023-07-09',\n", + " '2023-07-16', '2023-07-23', '2023-07-30', '2023-08-06',\n", + " '2023-08-13', '2023-08-20', '2023-08-27', '2023-09-03',\n", + " '2023-09-10', '2023-09-17', '2023-09-24', '2023-10-01',\n", + " '2023-10-08', '2023-10-15', '2023-10-22', '2023-10-29',\n", + " '2023-11-05', '2023-11-12'],\n", + " dtype='datetime64[ns]', freq='W-SUN'),\n", + " 'y': dict_values([0, 18, 12, 10, 12, 0, 11, 0, 11, 13, 0, 18, 12, 10, 12, 0, 11, 0, 11, 13, 0, 18, 12, 10, 12, 0, 12, 0, 11, 13])},\n", + " 'raw': {'prediction_result': {'predicted_value': {'2023-04-24': 0,\n", + " '2023-04-25': 18,\n", + " '2023-04-26': 12,\n", + " '2023-04-27': 10,\n", + " '2023-04-28': 12,\n", + " '2023-04-29': 0,\n", + " '2023-04-30': 11,\n", + " '2023-05-01': 0,\n", + " '2023-05-02': 11,\n", + " '2023-05-03': 13,\n", + " '2023-05-04': 0,\n", + " '2023-05-05': 18,\n", + " '2023-05-06': 12,\n", + " '2023-05-07': 10,\n", + " '2023-05-08': 12,\n", + " '2023-05-09': 0,\n", + " '2023-05-10': 11,\n", + " '2023-05-11': 0,\n", + " '2023-05-12': 11,\n", + " '2023-05-13': 13,\n", + " '2023-05-14': 0,\n", + " '2023-05-15': 18,\n", + " '2023-05-16': 12,\n", + " '2023-05-17': 10,\n", + " '2023-05-18': 12,\n", + " '2023-05-19': 0,\n", + " '2023-05-20': 12,\n", + " '2023-05-21': 0,\n", + " '2023-05-22': 11,\n", + " '2023-05-23': 13},\n", + " 'interval_metrics': {'interval_rmse': 1.5164425186469757,\n", + " 'interval_mae': 0.9669727996291655,\n", + " 'interval_smape': 1.732136035567733},\n", + " 'quantity_metrics': {'quantity_rmse': 15.549524453161835,\n", + " 'quantity_mae': 9.35978138752326,\n", + " 'quantity_smape': 0.6888599339319311}},\n", + " 'request_timestamp': '2023-10-15 19:09:48',\n", + " 'engine_code': 'Foretell_Pred_Prophet_Intermittent'}}]}" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
datetimey
02023-04-230
12023-04-3018
22023-05-0712
32023-05-1410
42023-05-2112
52023-05-280
62023-06-0411
72023-06-110
82023-06-1811
92023-06-2513
102023-07-020
112023-07-0918
122023-07-1612
132023-07-2310
142023-07-3012
152023-08-060
162023-08-1311
172023-08-200
182023-08-2711
192023-09-0313
202023-09-100
212023-09-1718
222023-09-2412
232023-10-0110
242023-10-0812
252023-10-150
262023-10-2212
272023-10-290
282023-11-0511
292023-11-1213
\n", + "
" + ], + "text/plain": [ + " datetime y\n", + "0 2023-04-23 0\n", + "1 2023-04-30 18\n", + "2 2023-05-07 12\n", + "3 2023-05-14 10\n", + "4 2023-05-21 12\n", + "5 2023-05-28 0\n", + "6 2023-06-04 11\n", + "7 2023-06-11 0\n", + "8 2023-06-18 11\n", + "9 2023-06-25 13\n", + "10 2023-07-02 0\n", + "11 2023-07-09 18\n", + "12 2023-07-16 12\n", + "13 2023-07-23 10\n", + "14 2023-07-30 12\n", + "15 2023-08-06 0\n", + "16 2023-08-13 11\n", + "17 2023-08-20 0\n", + "18 2023-08-27 11\n", + "19 2023-09-03 13\n", + "20 2023-09-10 0\n", + "21 2023-09-17 18\n", + "22 2023-09-24 12\n", + "23 2023-10-01 10\n", + "24 2023-10-08 12\n", + "25 2023-10-15 0\n", + "26 2023-10-22 12\n", + "27 2023-10-29 0\n", + "28 2023-11-05 11\n", + "29 2023-11-12 13" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pd.DataFrame(res['forecast'][0]['forecast'], columns=['datetime', 'y'])" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "prophet_plus\n" + ] + }, + { + "ename": "KeyError", + "evalue": "'interm_scores'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[7], line 5\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[39mprint\u001b[39m(r[\u001b[39m'\u001b[39m\u001b[39mmodel\u001b[39m\u001b[39m'\u001b[39m])\n\u001b[1;32m 3\u001b[0m \u001b[39m# print(r['RMSE'])\u001b[39;00m\n\u001b[1;32m 4\u001b[0m \u001b[39m# print(r['order_quantity_RMSE'])\u001b[39;00m\n\u001b[0;32m----> 5\u001b[0m \u001b[39mprint\u001b[39m(r[\u001b[39m'\u001b[39;49m\u001b[39minterm_scores\u001b[39;49m\u001b[39m'\u001b[39;49m])\n\u001b[1;32m 6\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39m'\u001b[39m\u001b[39m________\u001b[39m\u001b[39m'\u001b[39m)\n\u001b[1;32m 7\u001b[0m r[\u001b[39m'\u001b[39m\u001b[39mtest\u001b[39m\u001b[39m'\u001b[39m]\u001b[39m.\u001b[39mplot(title\u001b[39m=\u001b[39mr[\u001b[39m'\u001b[39m\u001b[39mmodel\u001b[39m\u001b[39m'\u001b[39m] \u001b[39m+\u001b[39m \u001b[39m'\u001b[39m\u001b[39m-test\u001b[39m\u001b[39m'\u001b[39m)\n", + "\u001b[0;31mKeyError\u001b[0m: 'interm_scores'" + ] + } + ], + "source": [ + "for r in res['forecast']:\n", + " print(r['model'])\n", + " # print(r['RMSE'])\n", + " # print(r['order_quantity_RMSE'])\n", + " print(r['interm_scores'])\n", + " print('________')\n", + " r['test'].plot(title=r['model'] + '-test')" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "ename": "IndexError", + "evalue": "list index out of range", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m/Users/qiaozhang/Desktop/sentient-dev/snr_demand-forecasting/notebooks/test.ipynb Cell 10\u001b[0m line \u001b[0;36m1\n\u001b[0;32m----> 1\u001b[0m res[\u001b[39m4\u001b[39;49m][\u001b[39m'\u001b[39m\u001b[39mtest_raw\u001b[39m\u001b[39m'\u001b[39m]\n", + "\u001b[0;31mIndexError\u001b[0m: list index out of range" + ] + } + ], + "source": [ + "res[4]['test_raw']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "11:17:02 - cmdstanpy - INFO - Chain [1] start processing\n", + "11:17:02 - cmdstanpy - INFO - Chain [1] done processing\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "callindg model: prophet\n", + " ds trend yhat_lower yhat_upper trend_lower trend_upper \\\n", + "260 2023-04-30 8.921487 -7.908574 21.012119 8.921487 8.921487 \n", + "261 2023-05-07 8.931226 -6.567967 22.604438 8.931226 8.931226 \n", + "262 2023-05-14 8.940965 -8.081720 21.526347 8.940905 8.941007 \n", + "263 2023-05-21 8.950703 -9.985601 19.493650 8.950543 8.950831 \n", + "264 2023-05-28 8.960442 -8.813073 20.526393 8.960129 8.960702 \n", + "265 2023-06-04 8.970180 -6.966844 23.563550 8.969667 8.970604 \n", + "266 2023-06-11 8.979919 -4.608789 25.118377 8.979231 8.980524 \n", + "267 2023-06-18 8.989657 -4.180691 25.485273 8.988761 8.990488 \n", + "268 2023-06-25 8.999396 -4.686704 24.961761 8.998250 9.000445 \n", + "269 2023-07-02 9.009134 -5.880469 23.125865 9.007781 9.010505 \n", + "270 2023-07-09 9.018873 -5.090580 23.769587 9.017264 9.020526 \n", + "271 2023-07-16 9.028611 -4.761036 25.748781 9.026645 9.030525 \n", + "272 2023-07-23 9.038350 -4.907963 24.736967 9.036085 9.040552 \n", + "273 2023-07-30 9.048088 -4.570258 23.745148 9.045508 9.050722 \n", + "274 2023-08-06 9.057827 -5.827376 22.932313 9.054935 9.060731 \n", + "275 2023-08-13 9.067565 -4.220147 24.956558 9.064331 9.070780 \n", + "276 2023-08-20 9.077304 -0.864349 29.034652 9.073628 9.081006 \n", + "277 2023-08-27 9.087042 3.470909 32.343086 9.082870 9.091054 \n", + "278 2023-09-03 9.096781 1.198728 30.637736 9.092259 9.101139 \n", + "279 2023-09-10 9.106520 -1.561115 27.531066 9.101419 9.111209 \n", + "280 2023-09-17 9.116258 -6.184726 24.202682 9.110719 9.121436 \n", + "281 2023-09-24 9.125997 -4.579644 23.752222 9.120080 9.131616 \n", + "282 2023-10-01 9.135735 -5.178492 25.012735 9.129285 9.141805 \n", + "283 2023-10-08 9.145474 -5.666373 22.611290 9.138581 9.152031 \n", + "284 2023-10-15 9.155212 -6.963614 21.842661 9.147855 9.162345 \n", + "285 2023-10-22 9.164951 -6.763386 23.055701 9.157131 9.172489 \n", + "286 2023-10-29 9.174689 -5.170320 24.787756 9.166426 9.182695 \n", + "287 2023-11-05 9.184428 -2.337336 27.885403 9.175632 9.192803 \n", + "288 2023-11-12 9.194166 -1.331529 29.762405 9.184876 9.203052 \n", + "289 2023-11-19 9.203905 -2.375162 27.729052 9.194147 9.213316 \n", + "\n", + " additive_terms additive_terms_lower additive_terms_upper yearly \\\n", + "260 -2.521643 -2.521643 -2.521643 -2.521643 \n", + "261 -1.628433 -1.628433 -1.628433 -1.628433 \n", + "262 -2.544671 -2.544671 -2.544671 -2.544671 \n", + "263 -3.980016 -3.980016 -3.980016 -3.980016 \n", + "264 -3.655285 -3.655285 -3.655285 -3.655285 \n", + "265 -1.186016 -1.186016 -1.186016 -1.186016 \n", + "266 1.337140 1.337140 1.337140 1.337140 \n", + "267 1.789455 1.789455 1.789455 1.789455 \n", + "268 0.451573 0.451573 0.451573 0.451573 \n", + "269 -0.531397 -0.531397 -0.531397 -0.531397 \n", + "270 0.094160 0.094160 0.094160 0.094160 \n", + "271 1.226812 1.226812 1.226812 1.226812 \n", + "272 1.064831 1.064831 1.064831 1.064831 \n", + "273 -0.269867 -0.269867 -0.269867 -0.269867 \n", + "274 -0.559179 -0.559179 -0.559179 -0.559179 \n", + "275 1.853473 1.853473 1.853473 1.853473 \n", + "276 5.774049 5.774049 5.774049 5.774049 \n", + "277 8.102311 8.102311 8.102311 8.102311 \n", + "278 7.022347 7.022347 7.022347 7.022347 \n", + "279 3.785691 3.785691 3.785691 3.785691 \n", + "280 1.145548 1.145548 1.145548 1.145548 \n", + "281 0.423666 0.423666 0.423666 0.423666 \n", + "282 0.590499 0.590499 0.590499 0.590499 \n", + "283 0.116618 0.116618 0.116618 0.116618 \n", + "284 -0.921952 -0.921952 -0.921952 -0.921952 \n", + "285 -1.023065 -1.023065 -1.023065 -1.023065 \n", + "286 0.705503 0.705503 0.705503 0.705503 \n", + "287 3.244306 3.244306 3.244306 3.244306 \n", + "288 4.575861 4.575861 4.575861 4.575861 \n", + "289 3.595682 3.595682 3.595682 3.595682 \n", + "\n", + " yearly_lower yearly_upper multiplicative_terms \\\n", + "260 -2.521643 -2.521643 0.0 \n", + "261 -1.628433 -1.628433 0.0 \n", + "262 -2.544671 -2.544671 0.0 \n", + "263 -3.980016 -3.980016 0.0 \n", + "264 -3.655285 -3.655285 0.0 \n", + "265 -1.186016 -1.186016 0.0 \n", + "266 1.337140 1.337140 0.0 \n", + "267 1.789455 1.789455 0.0 \n", + "268 0.451573 0.451573 0.0 \n", + "269 -0.531397 -0.531397 0.0 \n", + "270 0.094160 0.094160 0.0 \n", + "271 1.226812 1.226812 0.0 \n", + "272 1.064831 1.064831 0.0 \n", + "273 -0.269867 -0.269867 0.0 \n", + "274 -0.559179 -0.559179 0.0 \n", + "275 1.853473 1.853473 0.0 \n", + "276 5.774049 5.774049 0.0 \n", + "277 8.102311 8.102311 0.0 \n", + "278 7.022347 7.022347 0.0 \n", + "279 3.785691 3.785691 0.0 \n", + "280 1.145548 1.145548 0.0 \n", + "281 0.423666 0.423666 0.0 \n", + "282 0.590499 0.590499 0.0 \n", + "283 0.116618 0.116618 0.0 \n", + "284 -0.921952 -0.921952 0.0 \n", + "285 -1.023065 -1.023065 0.0 \n", + "286 0.705503 0.705503 0.0 \n", + "287 3.244306 3.244306 0.0 \n", + "288 4.575861 4.575861 0.0 \n", + "289 3.595682 3.595682 0.0 \n", + "\n", + " multiplicative_terms_lower multiplicative_terms_upper yhat \n", + "260 0.0 0.0 6.399845 \n", + "261 0.0 0.0 7.302793 \n", + "262 0.0 0.0 6.396294 \n", + "263 0.0 0.0 4.970687 \n", + "264 0.0 0.0 5.305157 \n", + "265 0.0 0.0 7.784164 \n", + "266 0.0 0.0 10.317059 \n", + "267 0.0 0.0 10.779112 \n", + "268 0.0 0.0 9.450969 \n", + "269 0.0 0.0 8.477737 \n", + "270 0.0 0.0 9.113033 \n", + "271 0.0 0.0 10.255423 \n", + "272 0.0 0.0 10.103181 \n", + "273 0.0 0.0 8.778221 \n", + "274 0.0 0.0 8.498648 \n", + "275 0.0 0.0 10.921038 \n", + "276 0.0 0.0 14.851353 \n", + "277 0.0 0.0 17.189353 \n", + "278 0.0 0.0 16.119128 \n", + "279 0.0 0.0 12.892211 \n", + "280 0.0 0.0 10.261806 \n", + "281 0.0 0.0 9.549662 \n", + "282 0.0 0.0 9.726234 \n", + "283 0.0 0.0 9.262091 \n", + "284 0.0 0.0 8.233260 \n", + "285 0.0 0.0 8.141886 \n", + "286 0.0 0.0 9.880192 \n", + "287 0.0 0.0 12.428733 \n", + "288 0.0 0.0 13.770027 \n", + "289 0.0 0.0 12.799587 \n" + ] + } + ], + "source": [ + "# Step 2 - forecast\n", + "res = df.forecast(ts, 30, model='prophet')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
y
2023-04-236.399845
2023-04-307.302793
2023-05-076.396294
2023-05-144.970687
2023-05-215.305157
2023-05-287.784164
2023-06-0410.317059
2023-06-1110.779112
2023-06-189.450969
2023-06-258.477737
2023-07-029.113033
2023-07-0910.255423
2023-07-1610.103181
2023-07-238.778221
2023-07-308.498648
2023-08-0610.921038
2023-08-1314.851353
2023-08-2017.189353
2023-08-2716.119128
2023-09-0312.892211
2023-09-1010.261806
2023-09-179.549662
2023-09-249.726234
2023-10-019.262091
2023-10-088.233260
2023-10-158.141886
2023-10-229.880192
2023-10-2912.428733
2023-11-0513.770027
2023-11-1212.799587
\n", + "
" + ], + "text/plain": [ + " y\n", + "2023-04-23 6.399845\n", + "2023-04-30 7.302793\n", + "2023-05-07 6.396294\n", + "2023-05-14 4.970687\n", + "2023-05-21 5.305157\n", + "2023-05-28 7.784164\n", + "2023-06-04 10.317059\n", + "2023-06-11 10.779112\n", + "2023-06-18 9.450969\n", + "2023-06-25 8.477737\n", + "2023-07-02 9.113033\n", + "2023-07-09 10.255423\n", + "2023-07-16 10.103181\n", + "2023-07-23 8.778221\n", + "2023-07-30 8.498648\n", + "2023-08-06 10.921038\n", + "2023-08-13 14.851353\n", + "2023-08-20 17.189353\n", + "2023-08-27 16.119128\n", + "2023-09-03 12.892211\n", + "2023-09-10 10.261806\n", + "2023-09-17 9.549662\n", + "2023-09-24 9.726234\n", + "2023-10-01 9.262091\n", + "2023-10-08 8.233260\n", + "2023-10-15 8.141886\n", + "2023-10-22 9.880192\n", + "2023-10-29 12.428733\n", + "2023-11-05 13.770027\n", + "2023-11-12 12.799587" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "res[0]['forecast']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "res[0]['forecast'].plot(title='forecasted')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "demand-forecasting", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/__pycache__/__init__.cpython-310.pyc b/src/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b91963eec9e5c4c54437207a599c8c4f2cfceaa0 Binary files /dev/null and b/src/__pycache__/__init__.cpython-310.pyc differ diff --git a/src/__pycache__/__init__.cpython-311.pyc b/src/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e9679b3a811f5c5fa4c974f2ad4392b2aeeaebd8 Binary files /dev/null and b/src/__pycache__/__init__.cpython-311.pyc differ diff --git a/src/__pycache__/avtive_models.cpython-310.pyc b/src/__pycache__/avtive_models.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..452fadaec3b6c3cea1e4451fb09805b2caed9d9b Binary files /dev/null and b/src/__pycache__/avtive_models.cpython-310.pyc differ diff --git a/src/__pycache__/main.cpython-310.pyc b/src/__pycache__/main.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5117713079c16f6570302e1c1b088a4a795154fd Binary files /dev/null and b/src/__pycache__/main.cpython-310.pyc differ diff --git a/src/__pycache__/main.cpython-311.pyc b/src/__pycache__/main.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f1d350648fc6a2dae160bbfc28077b2e267afe7 Binary files /dev/null and b/src/__pycache__/main.cpython-311.pyc differ diff --git a/src/avtive_models.py b/src/avtive_models.py new file mode 100644 index 0000000000000000000000000000000000000000..3ff00d9b16409dc95369b39d1e7209aaefab4826 --- /dev/null +++ b/src/avtive_models.py @@ -0,0 +1,19 @@ +''' +'ceif_plus' : 2023 Sep. currently the model is under review, so not recommended to use this mode - by idsc +''' + +active_models = { + 'intermittent': + [ + 'prophet_plus', + 'ceif_plus' + ], + 'continuous': + [ + 'fft_plus', + 'holt_winters_plus', + 'auto_arima_plus', + 'prophet', + 'prophet_plus', + ] +} diff --git a/src/forecast/Prophet.py b/src/forecast/Prophet.py new file mode 100644 index 0000000000000000000000000000000000000000..637862a7653ea2c95cde3890014724f9b9700daa --- /dev/null +++ b/src/forecast/Prophet.py @@ -0,0 +1,22 @@ +import pandas as pd +from prophet import Prophet +import numpy as np + + +class ProphetWrapper(): + def __init__(self): + pass + + def forecast(self, ts, n_predict, freq=None): + model = Prophet() + train = ts.rename(columns={'datetime': 'ds'}) + + model.fit(train) + + future = model.make_future_dataframe(periods=n_predict, freq=freq) + + forecasted = model.predict(future) + + print(forecasted[-n_predict:]) + + return forecasted[-n_predict:] diff --git a/src/forecast/__init__.py b/src/forecast/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/forecast/__pycache__/Prophet.cpython-310.pyc b/src/forecast/__pycache__/Prophet.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..084b1c8dae4f92951f51188dc898767cd9bee801 Binary files /dev/null and b/src/forecast/__pycache__/Prophet.cpython-310.pyc differ diff --git a/src/forecast/__pycache__/Prophet.cpython-311.pyc b/src/forecast/__pycache__/Prophet.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..719fc99cd689e5b5d7111d1b8f4e3fc7b25b0506 Binary files /dev/null and b/src/forecast/__pycache__/Prophet.cpython-311.pyc differ diff --git a/src/forecast/__pycache__/__init__.cpython-310.pyc b/src/forecast/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9c3890a4fa320db5d25eda3a5b9dc20da1a396bd Binary files /dev/null and b/src/forecast/__pycache__/__init__.cpython-310.pyc differ diff --git a/src/forecast/__pycache__/__init__.cpython-311.pyc b/src/forecast/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..113c4c30e492aa46612dab59235a118dfa2d8464 Binary files /dev/null and b/src/forecast/__pycache__/__init__.cpython-311.pyc differ diff --git a/src/functions/__init__.py b/src/functions/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/functions/__pycache__/__init__.cpython-310.pyc b/src/functions/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..be858ba85d39b4d3d1d38088daab2aedd4421c2c Binary files /dev/null and b/src/functions/__pycache__/__init__.cpython-310.pyc differ diff --git a/src/functions/__pycache__/__init__.cpython-311.pyc b/src/functions/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f01c9edafc7a4ba654be818e7bc2c78410aee779 Binary files /dev/null and b/src/functions/__pycache__/__init__.cpython-311.pyc differ diff --git a/src/functions/__pycache__/check_input.cpython-310.pyc b/src/functions/__pycache__/check_input.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5fd65730a3c8340dfe631fee63707f37dab5920b Binary files /dev/null and b/src/functions/__pycache__/check_input.cpython-310.pyc differ diff --git a/src/functions/__pycache__/check_input.cpython-311.pyc b/src/functions/__pycache__/check_input.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..abff691aac8a1c62f7a6c2aa07fbddb68c8250b7 Binary files /dev/null and b/src/functions/__pycache__/check_input.cpython-311.pyc differ diff --git a/src/functions/__pycache__/itmtt_scores.cpython-310.pyc b/src/functions/__pycache__/itmtt_scores.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ade9b66562382c69a17e0678416e71fdde20d32a Binary files /dev/null and b/src/functions/__pycache__/itmtt_scores.cpython-310.pyc differ diff --git a/src/functions/__pycache__/mase.cpython-310.pyc b/src/functions/__pycache__/mase.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..81028bed09c9f890d4cf24f01ebb5e6430738d9f Binary files /dev/null and b/src/functions/__pycache__/mase.cpython-310.pyc differ diff --git a/src/functions/__pycache__/order_qty_rmse.cpython-310.pyc b/src/functions/__pycache__/order_qty_rmse.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0a8d665ec92ae85b8662c696344706f20578b0fd Binary files /dev/null and b/src/functions/__pycache__/order_qty_rmse.cpython-310.pyc differ diff --git a/src/functions/check_input.py b/src/functions/check_input.py new file mode 100644 index 0000000000000000000000000000000000000000..df92bd7b3ad1b4203e4e27a9c1a4483960216398 --- /dev/null +++ b/src/functions/check_input.py @@ -0,0 +1,5 @@ +import pandas as pd + +def check_input(df): + pd.infer_freq(df) + return \ No newline at end of file diff --git a/src/functions/itmtt_scores.py b/src/functions/itmtt_scores.py new file mode 100644 index 0000000000000000000000000000000000000000..0763d488e7f5a985abe7a5da412f2598b667cc7e --- /dev/null +++ b/src/functions/itmtt_scores.py @@ -0,0 +1,29 @@ +def interm_scores(grdt_sr:list, pred_sr:list): + ## this function calculates + ## • Quantity score + ## • Quantity rate score + ## • Timing score + #print(grdt_sr, pred_sr) + lgrdt = len(grdt_sr) + assert lgrdt == len(pred_sr) + cnt_01match = 0 + grdt_value = 0 + pred_value = 0 + cnt_grdt1 = 0 + cnt_pred1 = 0 + for i in range(lgrdt): + + if (grdt_sr[i]==0 and pred_sr[i]==0) or (grdt_sr[i]> 0 and pred_sr[i]>0): + cnt_01match += 1 + + cnt_grdt1 += 1 if grdt_sr[i]>0 else 0 + cnt_pred1 += 1 if pred_sr[i]>0 else 0 + grdt_value += grdt_sr[i] + pred_value += pred_sr[i] + #print("Calculating:\nQuantity score, Quantity rate score, Timing score") + if cnt_grdt1 == 0 and cnt_pred1 == 0: # this indicate grdt_value=pred_value=0 + return 1.0, 1.0, 1.0*cnt_01match / lgrdt + else: + return 1.0*min(cnt_grdt1, cnt_pred1)/max(cnt_grdt1, cnt_pred1),\ + 1.0*min(grdt_value, pred_value)/max(grdt_value, pred_value),\ + 1.0*cnt_01match / lgrdt diff --git a/src/functions/mase.py b/src/functions/mase.py new file mode 100644 index 0000000000000000000000000000000000000000..c9993a261b286f24673198a0e0925c047e321b33 --- /dev/null +++ b/src/functions/mase.py @@ -0,0 +1,13 @@ +import numpy + + +def MASE(Actual, Predicted): + ''' + Mean Absolute Scaled Error (MASE) + ''' + values = [] + for i in range(1, len(Actual)): + values.append(abs(Actual[i] - Predicted[i]) / + (abs(Actual[i] - Actual[i - 1]) / (len(Actual) - 1))) + + return numpy.mean(values) diff --git a/src/functions/order_qty_rmse.py b/src/functions/order_qty_rmse.py new file mode 100644 index 0000000000000000000000000000000000000000..e8d9c54a7c022bfead213a21496b3b098629d435 --- /dev/null +++ b/src/functions/order_qty_rmse.py @@ -0,0 +1,13 @@ + +from sklearn.metrics import mean_squared_error +import numpy as np + + +def order_qty_rmse(actual, predicted): + actu = [] + pred = [] + for i, a in enumerate(actual): + if not a == 0: + actu.append(actual[i]) + pred.append(predicted[i]) + return np.sqrt(mean_squared_error(actu, pred)) diff --git a/src/functions/sort_res.py b/src/functions/sort_res.py new file mode 100644 index 0000000000000000000000000000000000000000..cd83262c6a2f88029ec0d28ff86e20c2447e5f33 --- /dev/null +++ b/src/functions/sort_res.py @@ -0,0 +1,6 @@ +def sort_res_by_rmse(res): + pass + + +def sort_res_by_(res): + pass \ No newline at end of file diff --git a/src/idsc/CEIF.py b/src/idsc/CEIF.py new file mode 100644 index 0000000000000000000000000000000000000000..883c98a1bd8df76faebe0a964c16d6bb2cf62db6 --- /dev/null +++ b/src/idsc/CEIF.py @@ -0,0 +1,64 @@ +import requests +import json +import pickle +import os + + +class CEIF(): + def __init__(self, apikey) -> None: + self.apikey = apikey + + def parse_res(self, res): + if str(res.status_code)[0] != '2': + raise ValueError('CEIF API Call Failed!\n', res.text) + else: + parsed_res = res.json() + # parsed_res['prediction_result'] = json.loads(parsed_res['prediction_result']) + return parsed_res + + def forecast( + self, + ts_list, + n_predict, + quantity_score_weight=None, + rate_score_weight=None, + timing_score_weight=None): + + endpoint = 'https://idsc.com.sg/foretell/prediction/time-series/intermittent/classic-explicit-intermittent' + + payloads = { + 'time_series_data': ts_list, + 'num_predict': n_predict} + + if quantity_score_weight is not None: + payloads['quantity_score_weight'] = quantity_score_weight + if rate_score_weight is not None: + payloads['rate_score_weight'] = rate_score_weight + if timing_score_weight is not None: + payloads['timing_score_weight'] = timing_score_weight + + headers = {'api-key': self.apikey} + + res = requests.post(endpoint, json=payloads, headers=headers) + # res = self.__success_api_res_test() + + return self.parse_res(res) + + def __success_api_res_test(self): + print('Warning - Using idsc ceif testing api response') + + script_dir = os.path.dirname(__file__) + rel_path = "./tests/CEIF_success_res.dict" + abs_file_path = os.path.join(script_dir, rel_path) + + with open(abs_file_path, 'rb') as f: + res = pickle.load(f) + + return res + + def __save_res(self, res): + ''' + Save the API response as local file for test + ''' + with open('test/CEIF_success_res.dict', 'wb') as f: + pickle.dump(res, f) diff --git a/src/idsc/IDSC.py b/src/idsc/IDSC.py new file mode 100644 index 0000000000000000000000000000000000000000..de0756cf9bcbf079fc781d30f4ccd9362937c7fc --- /dev/null +++ b/src/idsc/IDSC.py @@ -0,0 +1,173 @@ +import os +import yaml +from datetime import datetime, timedelta +import requests +import json + +from .Profiling import Profiling +from .Prophet import Prophet +from .CEIF import CEIF +from .fft import fft +from .holt_winters import holt_winters +from .auto_arima import auto_arima + +from dotenv import load_dotenv +load_dotenv() + + +class IDSC(): + def __init__(self) -> None: + __location__ = os.path.realpath( + os.path.join(os.getcwd(), os.path.dirname(__file__))) + + self.config_path = os.path.join(__location__, 'config.yml') + self.logged_in = False + self.timeformat = '%m/%d/%Y, %H:%M:%S' + with open(self.config_path, 'r') as file: + self.config = yaml.safe_load(file) + self.login() + + def login(self): + expire = self.config['apikey_expire'] + now = datetime.now() + + expire_date = datetime.strptime(expire, self.timeformat) + if now >= expire_date: + print('apikey expired, requesting new one.') + self.apikey = self.fetch_apikey() + self.update_config() + else: + print('apikey still available, logged in') + self.apikey = self.config['apikey'] + self.logged_in = True + + def fetch_apikey(self): + # print(os.environ) + payloads = { + 'email': os.getenv('IDSC_ACC'), + 'pwd': os.getenv('IDSC_PASS') + } + + print('IDSC Logging in ...') + login_resp = requests.post( + 'https://idsc.com.sg/user/login', + json=payloads) + + if login_resp.status_code == 200: + apikey = login_resp.json()["API_key"] + return apikey + else: + raise Exception(login_resp.json()) + + def update_config(self): + now = datetime.now() + expire = (now + timedelta(days=3)).strftime(self.timeformat) + self.config['apikey'] = self.apikey + self.config['apikey_expire'] = expire + with open(self.config_path, 'w') as f: + yaml.dump(self.config, f, default_flow_style=False) + + def profiling(self, ts): + ''' + ts_list : list of integers for IDSC profiling APIs + ''' + ts_obj = json.loads(ts) + ts_list = ts_obj['target'].values() + + profiling = Profiling(apikey=self.apikey) + + return profiling.profile(ts_list) + + # return self.profile + + def prophet(self, ts: str, n_predict, profile): + ''' + ts: time series object string + profile: refer to self.profiling response + ''' + prophet = Prophet(apikey=self.apikey) + + characteristic = profile['classification_res']['time_series_class']['overall_characteristic'] + inter_order_cpi = profile['change_point_res']['inter_order_cpi'] + + if characteristic == 'continuous': + res = prophet.continuous( + ts, n_predict, inter_order_cpi=inter_order_cpi) + else: + res = prophet.intermittent( + ts, n_predict, inter_order_cpi=inter_order_cpi) + + return res + + def ceif( + self, + ts: str, + n_predict, + quantity_score_weight=None, + rate_score_weight=None, + timing_score_weight=None): + print('IDSC ceif') + ceif = CEIF(apikey=self.apikey) + + ts_obj = json.loads(ts) + ts_list = list(ts_obj['target'].values()) + + res = ceif.forecast( + ts_list, + n_predict, + quantity_score_weight=quantity_score_weight, + rate_score_weight=rate_score_weight, + timing_score_weight=timing_score_weight) + print('IDSC ceif completed') + + return res + + def fft( + self, + ts: str, + n_predict, + num_harmonics=None): + + print('IDSC fft') + + model = fft(apikey=self.apikey) + + ts_obj = json.loads(ts) + ts_list = list(ts_obj['target'].values()) + + res = model.forecast( + ts_list, + n_predict, + num_harmonics=num_harmonics) + + return res + + def holt_winters(self, ts, n_predict, seasonal_cycle=None): + + print('IDSC holt_winters') + + model = holt_winters(apikey=self.apikey) + + ts_obj = json.loads(ts) + ts_list = list(ts_obj['target'].values()) + + res = model.forecast( + ts_list, + n_predict, + seasonal_cycle=seasonal_cycle) + + return res + + def auto_arima(self, ts, n_predict): + print('IDSC auto_arima') + + model = auto_arima(apikey=self.apikey) + ts_obj = json.loads(ts) + ts_list = list(ts_obj['target'].values()) + + res = model.forecast( + ts_list, + n_predict, + ) + + return res diff --git a/src/idsc/Profiling.py b/src/idsc/Profiling.py new file mode 100644 index 0000000000000000000000000000000000000000..fe5753fa61f5c5c6b5545f18d019e04a79afa834 --- /dev/null +++ b/src/idsc/Profiling.py @@ -0,0 +1,152 @@ +import requests +import json +import pickle +import os + + +class Profiling(): + def __init__(self, apikey): + self.apikey = apikey + pass + + def profile(self, ts): + ''' + ts: list of number of timeseries value, the y column + + return: + { + 'classification_res':{ + 'time_series_class':{ + 'overall_characteristic':'continuous' + } + }, + 'change_point_res':{'inter_order_cpi':[]}, + '', + } + ''' + print('Start profiling, note, predictability been disabled') + + # ======= # + # TESTING # + # ======= # + # return self.__load_res() + + self.change_point(list(ts)) + inter_order_cpi = self.change_point_res['inter_order_cpi'] + self.classification(list(ts), inter_order_cpi=inter_order_cpi) + + ''' + Predictability is disabled because not really required at this moment + ''' + # self.predictability(list(ts)) + + profile_res = { + 'change_point_res': self.change_point_res, + 'classification_res': self.classification_res, + # 'predictability_res': self.predictability_res + } + + # self.__save_res(profile_res) + + # print('Profiling Completed\n', profile_res) + + return profile_res + + def parse_res(self, res): + if str(res.status_code)[0] != '2': + raise ValueError('API Call Failed!\n', res.text) + else: + return res.json() + + def classification(self, ts, inter_order_cpi=None): + endpoint = 'https://idsc.com.sg/foretell/profiling/classification' + payloads = {'time_series': ts, 'inter_order_cpi': inter_order_cpi} + headers = {'api-key': self.apikey} + res = requests.post(endpoint, json=payloads, headers=headers) + + parsed_res = self.parse_res(res) + + # print('classification res',parsed_res) + # parsed_res['time_series_class'] = json.loads( + # parsed_res['time_series_class']) + + self.classification_res = parsed_res + return + + def change_point( + self, + ts, + inter_order_penalty=None, + order_qty_penalty=None): + endpoint = 'https://idsc.com.sg/foretell/profiling/cpd' + payloads = {'time_series': ts} + + if inter_order_penalty is not None: + payloads['inter_order_penalty'] = inter_order_penalty + if order_qty_penalty is not None: + payloads['order_qty_penalty'] = order_qty_penalty + + headers = {'api-key': self.apikey} + + print('Change point detection') + + res = requests.post(endpoint, json=payloads, headers=headers) + + # print('Change point detection completed') + + self.change_point_res = self.parse_res(res) + return + + def predictability( + self, + ts, + inter_order_cpi=None, + order_quantity_cpi=None, + inter_discern_param=None, + quantity_discern_param=None): + endpoint = 'https://idsc.com.sg/foretell/profiling/predictability' + + payloads = {'time_series': ts} + + if inter_order_cpi is not None: + payloads['inter_order_cpi'] = inter_order_cpi + if order_quantity_cpi is not None: + payloads['order_quantity_cpi'] = order_quantity_cpi + if inter_discern_param is not None: + payloads['inter_discern_param'] = inter_discern_param + if quantity_discern_param is not None: + payloads['quantity_discern_param'] = quantity_discern_param + + headers = {'api-key': self.apikey} + res = requests.post(endpoint, json=payloads, headers=headers) + + self.predictability_res = self.parse_res(res) + return + + # ------------------------------------------------ # + # For testing purpose - save and load API response # + # ------------------------------------------------ # + + def __save_res(self, res): + # The successful res is based on "data/test.csv" + script_dir = os.path.dirname(__file__) + res_path = "./tests/profile_test.dict" + abs_file_path = os.path.join(script_dir, res_path) + + print('Saving response to ' + res_path) + + with open(abs_file_path, 'wb') as f: + pickle.dump(res, f) + + def __load_res(self): + # The successful res is based on "data/test.csv" + script_dir = os.path.dirname(__file__) + res_path = "./tests/profile_test.dict" + abs_file_path = os.path.join(script_dir, res_path) + + print('Reading response from ' + res_path) + + with open(abs_file_path, 'rb') as f: + res = pickle.load(f) + + return res diff --git a/src/idsc/Prophet.py b/src/idsc/Prophet.py new file mode 100644 index 0000000000000000000000000000000000000000..ce093625717905ed1c3893b76b5adc42adcfe323 --- /dev/null +++ b/src/idsc/Prophet.py @@ -0,0 +1,46 @@ +import requests +import json + + +class Prophet(): + def __init__(self, apikey): + self.apikey = apikey + + def parse_res(self, res): + if str(res.status_code)[0] != '2': + raise ValueError('API Call Failed!\n', res.text) + else: + parsed_res = res.json() + # parsed_res['prediction_result'] = json.loads(parsed_res['prediction_result']) + return parsed_res + + def continuous(self, ts: str, n_predict: int, inter_order_cpi=None): + ''' + ts: JSON string that contains "SKU", "date" and "target" + ''' + endpoint = 'https://idsc.com.sg/foretell/prediction/time-series/continuous/prophet' + payloads = { + 'time_series_table': ts, + 'num_predict': n_predict} + + if inter_order_cpi is not None: + payloads['inter_order_cpi'] = inter_order_cpi + + headers = {'api-key': self.apikey} + + res = requests.post(endpoint, json=payloads, headers=headers) + return self.parse_res(res) + + def intermittent(self, ts: str, n_predict: int, inter_order_cpi=None): + endpoint = 'https://idsc.com.sg/foretell/prediction/time-series/intermittent/prophet' + payloads = { + 'time_series_table': ts, + 'num_predict': n_predict} + + if inter_order_cpi is not None: + payloads['inter_order_cpi'] = inter_order_cpi + + headers = {'api-key': self.apikey} + + res = requests.post(endpoint, json=payloads, headers=headers) + return self.parse_res(res) diff --git a/src/idsc/__init__.py b/src/idsc/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/idsc/__pycache__/CEIF.cpython-310.pyc b/src/idsc/__pycache__/CEIF.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b95e058f1b6d4e90c09f9e49271192a8e8403630 Binary files /dev/null and b/src/idsc/__pycache__/CEIF.cpython-310.pyc differ diff --git a/src/idsc/__pycache__/CEIF.cpython-311.pyc b/src/idsc/__pycache__/CEIF.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d328a0343d377509b1fc8b839f522e0d7a8849a7 Binary files /dev/null and b/src/idsc/__pycache__/CEIF.cpython-311.pyc differ diff --git a/src/idsc/__pycache__/IDSC.cpython-310.pyc b/src/idsc/__pycache__/IDSC.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc7f5828921555d1546ea68cfa145fff46cb5c66 Binary files /dev/null and b/src/idsc/__pycache__/IDSC.cpython-310.pyc differ diff --git a/src/idsc/__pycache__/IDSC.cpython-311.pyc b/src/idsc/__pycache__/IDSC.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eb57fb011f3e5e4d276d9a4ca08301c1687c48fe Binary files /dev/null and b/src/idsc/__pycache__/IDSC.cpython-311.pyc differ diff --git a/src/idsc/__pycache__/Profiling.cpython-310.pyc b/src/idsc/__pycache__/Profiling.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1462759e4e4d4b5a5d42db8aa9e21d4d6d534b85 Binary files /dev/null and b/src/idsc/__pycache__/Profiling.cpython-310.pyc differ diff --git a/src/idsc/__pycache__/Profiling.cpython-311.pyc b/src/idsc/__pycache__/Profiling.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f67f5dba24566d304f37fdf49d6eb8bc728ef84b Binary files /dev/null and b/src/idsc/__pycache__/Profiling.cpython-311.pyc differ diff --git a/src/idsc/__pycache__/Prophet.cpython-310.pyc b/src/idsc/__pycache__/Prophet.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fdcc9e21d731d850d6f57eb2bda30f982e8cb78e Binary files /dev/null and b/src/idsc/__pycache__/Prophet.cpython-310.pyc differ diff --git a/src/idsc/__pycache__/Prophet.cpython-311.pyc b/src/idsc/__pycache__/Prophet.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3c556bf18067c83c3de9864ca3e927b9446815dc Binary files /dev/null and b/src/idsc/__pycache__/Prophet.cpython-311.pyc differ diff --git a/src/idsc/__pycache__/__init__.cpython-310.pyc b/src/idsc/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..402a094762b95ff3c395d93a4c701d4ab8dc7f7d Binary files /dev/null and b/src/idsc/__pycache__/__init__.cpython-310.pyc differ diff --git a/src/idsc/__pycache__/__init__.cpython-311.pyc b/src/idsc/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bb12fddd98c8704510bece4b7c1b085b268330f3 Binary files /dev/null and b/src/idsc/__pycache__/__init__.cpython-311.pyc differ diff --git a/src/idsc/__pycache__/auto_arima.cpython-310.pyc b/src/idsc/__pycache__/auto_arima.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7359ae8a970c211be91c9820dbf86a1421d7602c Binary files /dev/null and b/src/idsc/__pycache__/auto_arima.cpython-310.pyc differ diff --git a/src/idsc/__pycache__/fft.cpython-310.pyc b/src/idsc/__pycache__/fft.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..78de8ba0d82a3c5a8f7e85b26f947267f108ab71 Binary files /dev/null and b/src/idsc/__pycache__/fft.cpython-310.pyc differ diff --git a/src/idsc/__pycache__/holt_winters.cpython-310.pyc b/src/idsc/__pycache__/holt_winters.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5145c2b3b800eec58c688c857ddd4551229c68e0 Binary files /dev/null and b/src/idsc/__pycache__/holt_winters.cpython-310.pyc differ diff --git a/src/idsc/auto_arima.py b/src/idsc/auto_arima.py new file mode 100644 index 0000000000000000000000000000000000000000..1fde38c948fd3be04b78d5513d9ba6072cb63096 --- /dev/null +++ b/src/idsc/auto_arima.py @@ -0,0 +1,58 @@ +import requests +import json +import pickle +import os + + +class auto_arima(): + def __init__(self, apikey) -> None: + self.apikey = apikey + + def parse_res(self, res): + if str(res.status_code)[0] != '2': + raise ValueError('auto_arima_plus API Call Failed!\n', res.text) + else: + parsed_res = res.json() + return parsed_res + + def forecast( + self, + ts_list, + n_predict: int, + seasonal_cycle=None): + endpoint = 'https://idsc.com.sg/foretell/prediction/time-series/continuous/arima' + + payloads = { + 'time_series_data': ts_list, + 'num_predict': n_predict} + + headers = {'api-key': self.apikey} + + res = requests.post(endpoint, json=payloads, headers=headers) + + self.__save_res(res) + + print('auto_arima_______') + print(res) + + return self.parse_res(res) + + def __save_res(self, res): + # The successful res is based on "data/test.csv" + script_dir = os.path.dirname(__file__) + rel_path = "./tests/auto_arima_success_res.dict" + abs_file_path = os.path.join(script_dir, rel_path) + + with open(abs_file_path, 'wb') as f: + pickle.dump(res, f) + + def __load_res(self): + # The successful res is based on "data/test.csv" + script_dir = os.path.dirname(__file__) + rel_path = "./tests/auto_arima_success_res.dict" + abs_file_path = os.path.join(script_dir, rel_path) + + with open(abs_file_path, 'rb') as f: + res = pickle.load(f) + + return res diff --git a/src/idsc/config.yml b/src/idsc/config.yml new file mode 100644 index 0000000000000000000000000000000000000000..ced90f5f7541b50ff932f1d1c7012561772c7fe5 --- /dev/null +++ b/src/idsc/config.yml @@ -0,0 +1,2 @@ +apikey: 0a155d98dc25061ba4ee62f2cbe369cf8da5e7fe +apikey_expire: 10/18/2023, 13:54:17 diff --git a/src/idsc/fft.py b/src/idsc/fft.py new file mode 100644 index 0000000000000000000000000000000000000000..3149eb4fadf23426fe5c26b45434bf18e57eb646 --- /dev/null +++ b/src/idsc/fft.py @@ -0,0 +1,61 @@ +import requests +import json +import pickle +import os + + +class fft(): + def __init__(self, apikey) -> None: + self.apikey = apikey + + def parse_res(self, res): + if str(res.status_code)[0] != '2': + raise ValueError('fft_plus API Call Failed!\n', res.text) + else: + parsed_res = res.json() + return parsed_res + + def forecast( + self, + ts_list, + n_predict: int, + num_harmonics=None): + endpoint = 'https://idsc.com.sg/foretell/prediction/time-series/continuous/fourier-prediction' + + payloads = { + 'time_series_data': ts_list, + 'num_predict': n_predict} + + if num_harmonics is not None: + payloads['num_harmonics'] = num_harmonics + + headers = {'api-key': self.apikey} + + res = requests.post(endpoint, json=payloads, headers=headers) + + # self.__save_res(res) + + # print('fft__________') + # print(res) + + return self.parse_res(res) + + def __save_res(self, res): + # The successful res is based on "data/test.csv" + script_dir = os.path.dirname(__file__) + rel_path = "./tests/fft_success_res.dict" + abs_file_path = os.path.join(script_dir, rel_path) + + with open(abs_file_path, 'wb') as f: + pickle.dump(res, f) + + def __load_res(self): + # The successful res is based on "data/test.csv" + script_dir = os.path.dirname(__file__) + rel_path = "./tests/fft_success_res.dict" + abs_file_path = os.path.join(script_dir, rel_path) + + with open(abs_file_path, 'rb') as f: + res = pickle.load(f) + + return res diff --git a/src/idsc/holt_winters.py b/src/idsc/holt_winters.py new file mode 100644 index 0000000000000000000000000000000000000000..d4a241669748466b08aa19ddf3f6762d9650e650 --- /dev/null +++ b/src/idsc/holt_winters.py @@ -0,0 +1,61 @@ +import requests +import json +import pickle +import os + + +class holt_winters(): + def __init__(self, apikey) -> None: + self.apikey = apikey + + def parse_res(self, res): + if str(res.status_code)[0] != '2': + raise ValueError('holt_winters_plus API Call Failed!\n', res.text) + else: + parsed_res = res.json() + return parsed_res + + def forecast( + self, + ts_list, + n_predict: int, + seasonal_cycle=None): + endpoint = 'https://idsc.com.sg/foretell/prediction/time-series/continuous/holt-winters' + + payloads = { + 'time_series_data': ts_list, + 'num_predict': n_predict} + + if seasonal_cycle is not None: + payloads['seasonal_cycle'] = seasonal_cycle + + headers = {'api-key': self.apikey} + + res = requests.post(endpoint, json=payloads, headers=headers) + + # self.__save_res(res) + + # print('holt_winters__________') + # print(res) + + return self.parse_res(res) + + def __save_res(self, res): + # The successful res is based on "data/test.csv" + script_dir = os.path.dirname(__file__) + rel_path = "./tests/holt_winters_success_res.dict" + abs_file_path = os.path.join(script_dir, rel_path) + + with open(abs_file_path, 'wb') as f: + pickle.dump(res, f) + + def __load_res(self): + # The successful res is based on "data/test.csv" + script_dir = os.path.dirname(__file__) + rel_path = "./tests/holt_winters_success_res.dict" + abs_file_path = os.path.join(script_dir, rel_path) + + with open(abs_file_path, 'rb') as f: + res = pickle.load(f) + + return res diff --git a/src/idsc/tests/CEIF_success_res.dict b/src/idsc/tests/CEIF_success_res.dict new file mode 100644 index 0000000000000000000000000000000000000000..41ffcc48d90e81900939655651c23d54d2058055 Binary files /dev/null and b/src/idsc/tests/CEIF_success_res.dict differ diff --git a/src/idsc/tests/auto_arima_success_res.dict b/src/idsc/tests/auto_arima_success_res.dict new file mode 100644 index 0000000000000000000000000000000000000000..17f84a742e0113ccb19494de71c20084a92f9cd4 Binary files /dev/null and b/src/idsc/tests/auto_arima_success_res.dict differ diff --git a/src/idsc/tests/fft_success_res.dict b/src/idsc/tests/fft_success_res.dict new file mode 100644 index 0000000000000000000000000000000000000000..7127ad8fd5ccec24d06eaaca924bb3933b76b710 Binary files /dev/null and b/src/idsc/tests/fft_success_res.dict differ diff --git a/src/idsc/tests/holt_winters_success_res.dict b/src/idsc/tests/holt_winters_success_res.dict new file mode 100644 index 0000000000000000000000000000000000000000..713fb7145c21c6d584c352de60daae98d7bc2cfc Binary files /dev/null and b/src/idsc/tests/holt_winters_success_res.dict differ diff --git a/src/idsc/tests/profile_test.dict b/src/idsc/tests/profile_test.dict new file mode 100644 index 0000000000000000000000000000000000000000..d7944720640070dd6c453f20ffd43e901540cf32 Binary files /dev/null and b/src/idsc/tests/profile_test.dict differ diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000000000000000000000000000000000000..92886034b34166d2e435802feca687003bb01f39 --- /dev/null +++ b/src/main.py @@ -0,0 +1,431 @@ +from .avtive_models import active_models +from .forecast.Prophet import ProphetWrapper +from .idsc.IDSC import IDSC + +import pandas as pd +import math +import numpy as np + +from sklearn.metrics import mean_squared_error, mean_absolute_error +from .functions.mase import MASE +from .functions.order_qty_rmse import order_qty_rmse +from .functions.itmtt_scores import interm_scores + + +# List of models to verify user input + + +class DemandForecasting(): + ''' + DemandForecasting is assuming a single SKU at each time. + + There will be a 2 step process, model selection and forecasting. + This process is identified by whether model parameter is provided + + This API's behavior was designed based on if certain information is provided, and the API itself + will decide what to do. Instead of trying to force user perform "model selection" or "actual + forecasting" the API will only check what are the models user attempted to run, as well as if user + want any test result or not. In this way, we can take care of multiple requirements without having + a lot of different end points. + ''' + + def __init__(self) -> None: + self.idsc = IDSC() + pass + + def forecast(self, + ts, + n_predict: int, + model: str or list, + freq=None, + run_test: bool = False, + characteristic=None, + m=None): + ''' + ts: timeseries object, use pd.DataFrame().to_json() to generate + example: + {"datetime": + {"0":"2018-05-06","1":"2018-05-13"}, + "y": + {"0":2,"1":12}} + n_predict: number of future values to predict + freq: optional, timeseries data frequency, if not provided, will try to inference by pandas lib + model: optional, + If not provided, consider model selection process + If model is provided, will not calculate the RMSE and will not perform test + + characteristic: optionsal + Provide model information about the data characteristic, for now, either continuous or anything else (intermittent) + If not provided, will perform profiling (relay on IDSC API) first, user are quired to track the data's characteristics + for future forecasting purpose. + + m: seasonal period value, most likely will be used for internal testing purpose. + ''' + self.idsc_profile = None + + self.characteristic = characteristic + + self.ts_df = pd.DataFrame(ts) + self.ts_df['datetime'] = pd.to_datetime(self.ts_df['datetime']) + + self.freq = freq + self.n_predict = n_predict + self.run_test = run_test + + if self.n_predict <= 0: + print('n_predict is 0, force run_test to be true') + self.run_test = True + + # Try to get the timeseries frequency based on the data + # This will be used if user did not provide freq param + self.__get_frequency() + + self.m = m + + # Convert n_predict number to timestamp based on the frequency + self.forecast_horizon = pd.date_range( + self.ts_df['datetime'].iloc[-1], periods=n_predict, freq=self.freq) + + ''' + Split 80% data for training and the rest for testing + This will only be used if rum_test param set to True + ''' + self.n_test = round(self.ts_df.shape[0] * 0.2) + self.ts_train = self.ts_df[:-self.n_test] + + self.test_truth = self.ts_df[-self.n_test:]['y'].tolist() + self.test_horizon = self.ts_df[-self.n_test:]['datetime'].tolist() + + self.__prep_idsc_ts() # prep idsc_ts, both profiling and idsc models will require this + + # ============== # + # IDSC profiling # + # ============== # + + # Default idsc characteristic, continuous or intermittent + self.idsc_characteristic = None + + if self.characteristic is None: + print('characteristic not provided, running profiling') + self.__profiling() + print('profiling completed, data characteristic is ', + self.characteristic) + + # ======= # + # TESTING # + # ======= # + "For testing purpose, only return data's characteristics" + + # return self.characteristic + + # ------------- # + # Assign models # + # ------------- # + ''' + For model parameter, user can input either string name of a particular model name, or a list of available models + if user input "all", will just call all models + ''' + model_is_str = isinstance(model, str) + + if model_is_str: + model_is_all = (model == 'all') + + if not model_is_all: + # When there is only one model name provided + self.model = [model] + + if model_is_all: + if self.characteristic == 'continuous': + self.model = active_models['continuous'] + + if self.characteristic != 'continuous': + self.model = active_models['intermittent'] + + if not model_is_str: + self.model = model + + ''' + For idsc models, the profiling process will be required + Also input data will be formated specifically for idsc + ''' + + self.has_idsc_model = any('plus' in m for m in self.model) + + if self.has_idsc_model and self.idsc_profile is None: + ''' + Running profiling if the idsc_profile is none,this is + because some idsc model request idsc profile as input + ''' + self.__profiling() + + self.__check_model() + + # =================== # + # Perform forecasting # + # =================== # + ''' + The model below should always return the forecasted result based on n_predict value + + res : { + 'model': model name, + 'forecast': the forecasted value, + 'test': test result, + 'RMSE': RMSE value to evaluate best performing model, + 'raw': keep a copy of the original model response, without any filtering + } + ''' + self.fcst_res = [] # Array storeing all results + + # -------------------------- # + # Calling forecasting models # + # -------------------------- # + # Todo: to track model time spending here + + for m in self.model: + print(f'callindg model: {m}') + getattr(self, m)() + + # ========================== # + # Rank the model by response # + # ========================== # + "For continuous data, use RMSE, for intermittent data, use average of interm scores" + + if self.run_test and self.characteristic == 'continuous': + self.fcst_res.sort(key=lambda x: x['RMSE']) + + if self.run_test and self.characteristic != 'continuous': + self.fcst_res.sort( + key=lambda x: x['avg_interm_scores'], reverse=True) + + # Return the result with lowest RMSE ranked as 1st item + self.res = {'characteristic': self.characteristic, + 'forecast': self.fcst_res} + return self.res + + def __get_frequency(self): + # Attempt to get the frequency from the provided datetime column + if pd.infer_freq(self.ts_df['datetime']) is not None: + self.freq = pd.infer_freq(self.ts_df['datetime']) + + # Always make sure the frequency is not None + if self.freq is None: + raise ValueError( + 'Unable inference freq from datetime column, please make timeseries interval consistent or provide customized frequency.') + + def __check_model(self): + all_active_models = active_models['continuous'] + \ + active_models['intermittent'] + + unknown_models = set(self.model) - set(all_active_models) + if len(unknown_models) > 0: + raise ValueError( + f'Unknown model : {unknown_models}, please use active models: {active_models}') + + if self.characteristic == 'continuous': + unsuitable_models = set(self.model) - \ + set(active_models['continuous']) + if len(unsuitable_models) > 0: + raise ValueError( + f'Unsuitable model for continuous data: {unsuitable_models}. please use continuous models: {active_models["continuous"]}') + + if self.characteristic != 'continuous': + unsuitable_models = set(self.model) - \ + set(active_models['intermittent']) + if len(unsuitable_models) > 0: + raise ValueError( + f'Unsuitable model for intermittent data: {unsuitable_models}. please use continuous models: {active_models["intermittent"]}') + + def __prep_idsc_ts(self): + # Time series configured for IDSC apis, all converted to json strings + self.idsc_ts = self.ts_df.rename( + columns={'datetime': 'date', 'y': 'target'}) + self.idsc_ts['date'] = self.idsc_ts['date'].dt.strftime('%Y-%m-%d') + self.idsc_ts = self.idsc_ts.to_json() + + self.idsc_ts_train = self.ts_train.rename( + columns={'datetime': 'date', 'y': 'target'}) + self.idsc_ts_train['date'] = self.idsc_ts_train['date'].dt.strftime( + '%Y-%m-%d') + self.idsc_ts_train = self.idsc_ts_train.to_json() + + def __profiling(self): + self.idsc_profile = self.idsc.profiling(self.idsc_ts) + characteristic = self.idsc_profile['classification_res'][ + 'time_series_class']['overall_characteristic'] + + if self.characteristic is not None and self.characteristic != characteristic: + raise ValueError( + f"Provided characteristics - {self.characteristic} is different from data's characteristics - {characteristic}. Please use the correct data characteristics.") + + self.characteristic = characteristic + + if self.run_test: + self.idsc_profile_train = self.idsc.profiling( + self.idsc_ts_train) + else: + self.idsc_profile_train = None + + # =========== # + # Core method # + # =========== # + ''' + This methods takes input of model and run the mode, test (to evaluate RMSE) and + return the processed result within this method itself. In this way, the model can + be considered as a black box, as long as the model takes ls, n_predict, **kwargs + and return as an object, this method can process it and format it correctly. + + Because sometimes actual forecasting model and test model may take different arguments + both args and test_args can be used and pass the arguments around. + ''' + + def __use_model(self, model, model_name, get_value, args=None, test_args=None): + ''' + model: the model to call + get_value: lambda, to extract the value list from the model response + ''' + ts = self.ts_df + train = self.ts_train + + res = {'model': model_name} + + # IDSC is using different input configuration + if 'plus' in model_name: + print('has_idsc_model') + ts = self.idsc_ts + train = self.idsc_ts_train + + # Pass keyword arguments to the model + if self.n_predict > 0: + if args is not None: + pred = model(ts, self.n_predict, **args) + else: + pred = model(ts, self.n_predict) + + pred_val: list = get_value(pred) + + # res['forecast'] = pd.DataFrame( + # pred_val, + # # len() required because sometimes the response is not same size as n_predict requirement + # # Same for below 'test' dataframe + # index=self.forecast_horizon[:len(pred_val)], + # columns=['y']) + res['forecast'] = {'datetime': self.forecast_horizon[:len(pred_val)], + 'y': pred_val} + res['raw'] = pred + + # Run the test set and evaluate model performance + if self.run_test: + + # If the train and test arguments are exactly the same + # Expect user only provide 1 args dictionary + test_args = args if test_args is None else test_args + + if test_args is not None: + test = model(train, self.n_test, **test_args) + else: + test = model(train, self.n_test) + + test_val: list = get_value(test) + + # Make sure test truth same size as test_val + test_truth = self.test_truth[:len(test_val)] + + res['test'] = pd.DataFrame( + { + 'truth': test_truth, + 'test': test_val + }, + index=self.test_horizon[:len(test_val)]) + + res['RMSE'] = math.sqrt( + mean_squared_error( + test_truth, list(test_val))) + # res['MASE'] = MASE(test_truth, list(test_val)) + + res['order_quantity_RMSE'] = order_qty_rmse( + test_truth, list(test_val)) + + res['inter_order_RMSE'] = mean_squared_error( + [0 if i == 0 else 1 for i in test_truth], + [0 if i == 0 else 1 for i in list(test_val)]) + + res['interm_scores'] = interm_scores( + test_truth, list(test_val)) + + # Calculate the average intermittent data score, used for sorting the forecasting response + res['avg_interm_scores'] = np.mean(res['interm_scores']) + + res['test_raw'] = test + + self.fcst_res.append(res) + + # ---------- # + # All Models # + # ---------- # + def prophet_plus(self): + model = self.idsc.prophet + model_name = 'prophet_plus' + + args = {'profile': self.idsc_profile} + + test_args = {'profile': self.idsc_profile_train} + + self.__use_model( + model, + model_name, + lambda x: x['prediction_result']['predicted_value'].values(), + args=args, + test_args=test_args + ) + + def prophet(self): + model = ProphetWrapper() + model_name = 'prophet' + + args = {'freq': self.freq} + + self.__use_model(model.forecast, + model_name, + lambda x: x['yhat'].to_list(), + args=args) + + def ceif_plus(self): + model_name = 'ceif_plus' + + self.__use_model( + self.idsc.ceif, + model_name, + lambda x: x['prediction_result']['predicted_value']) + + def fft_plus(self): + model_name = 'fft_plus' + + self.__use_model( + self.idsc.fft, + model_name, + lambda x: x['prediction_result']['predicted_value']) + + def holt_winters_plus(self): + model_name = 'holt_winters_plus' + + def get_value(x): return x['prediction_result']['predicted_value'] + + if self.m is not None: + args = {'seasonal_cycle': self.m} + self.__use_model( + self.idsc.holt_winters, + model_name, + get_value, + args=args) + else: + self.__use_model( + self.idsc.holt_winters, + model_name, + get_value) + + def auto_arima_plus(self): + model_name = 'auto_arima_plus' + model = self.idsc.auto_arima + def get_value(x): return x['prediction_result']['predicted_value'] + + self.__use_model(model, model_name, get_value) diff --git a/src/models/DemandForecastingRes.py b/src/models/DemandForecastingRes.py new file mode 100644 index 0000000000000000000000000000000000000000..9164ddf83b91695da0987c11a388fe0218491480 --- /dev/null +++ b/src/models/DemandForecastingRes.py @@ -0,0 +1,5 @@ +from typing import List, Dict + +DemandForecastingRes: dict({'RMSE': int}) + +DemandForecastingResLst: List[DemandForecastingRes] diff --git a/src/models/__init__.py b/src/models/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391