# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['judgyprophet', 'judgyprophet.tutorials']

package_data = \
{'': ['*'], 'judgyprophet': ['stan/*', 'stan/build/*']}

install_requires = \
['numpy>=1.22.2,<2.0.0',
 'pandas>=1',
 'pystan>=2.19.1.1,<2.20.0.0',
 'scipy>=1.8.0,<2.0.0']

setup_kwargs = {
    'name': 'judgyprophet',
    'version': '0.1.2',
    'description': 'Forecasting for knowable future events using Bayesian informative priors (forecasting with judgmental-adjustment).',
    'long_description': '# What is `judgyprophet`?\n\n`judgyprophet` is a Bayesian forecasting algorithm based on Prophet, that enables forecasting while using information known by the business about future events. The aim is to enable users to perform forecasting with *judgmental adjustment*, in a way that is mathematically as sound as possible.\n\nSome events will have a big effect on your timeseries. Some of which you are aware of ahead of time. For example:\n\n* An existing product entering a new market.\n* A price change to a product.\n\nThese events will typically cause a large change in your timeseries of e.g. product sales, which a standard statistical forecast will consistently underestimate.\n\nThe business will often have good estimates (or at least better than your statistical forecast) about how these events will affect your timeseries. But this is difficult to encode into your statistical forecasting algorithm. One option is to use a regressor, but this typically works poorly. This is because you have no data on the event before it occurs, and the statistical forecast does not know how to balance the information in your regressor and trend after the event occurs (which can lead to erratic behaviour).\n\n`judgyprophet` solves this problem by encoding the business estimate of how the event will affect the forecast (the judgmental adjustment) as a Bayesian informative prior.\n\nBefore the event occurs, this business information is used to reflect the forecast of what will happen post-event e.g. the estimated uplift in product sales once the event has happened. After the event occurs, we update what the business *thinks* will happen, with what we *see* happening in the actuals. This is done using standard Bayesian updating.\n\n\n# Installation\n\n### 1. install `judgyprophet` python package using `pip`\n\n```\npip install judgyprophet\n```\n\n### 2. compile the `STAN` model\n\n`judgyprophet` depends on `STAN`, whose models have to be compiled before running.\n\nSo to use `judgyprophet`, you have to compile the model. Do this in the shell using\n\n```\npython -c "from judgyprophet import JudgyProphet; JudgyProphet().compile()"\n```\n\nor in python using\n```python\nfrom judgyprophet import JudgyProphet\n\nJudgyProphet().compile()\n```\n\nThis will take a while. But you only have to run this once, after the initial install.\n\n# Documentation\n\nFull documentation is available on our Github Pages site [here](https://astrazeneca.github.io/judgyprophet/).\n\nScroll down for a quickstart tutorial.\n\nA runnable jupyter notebook version of the quickstart tutorial is available [here](https://github.com/AstraZeneca/judgyprophet/blob/main/tutorials/Quickstart.ipynb)\n\n# Roadmap\n\nSome things on our roadmap:\n\n* Currently `judgyprophet` `STAN` file is only tested on Unix-based Linux or Mac machines. We aim to fully test Windows machines ASAP.\n* Option to run full MCMC, rather than just L-BFGS.\n* Prediction intervals\n* Regressors/holidays\n\n\n# Quickstart Tutorial\n\nImagine your business currently operates in the US, but is launching its product in Europe. As a result it anticipates a sharp uptake in sales (which it has an estimate of). As your forecasting team, they come to you and ask you to account for this.\n\nLet\'s look at how we might do this using `judgyprophet` with some example data, where we know what happened. First let\'s plot this:\n\n\n```python\nfrom judgyprophet.tutorials.resources import get_trend_event\n\nexample_data = get_trend_event()\np = example_data.plot.line()\n```\n\n\n\n![png](docs/output_1_0.png)\n\n\n\nWe can see that product sales increased sharply from about September 2020. Suppose it was a launch in a new market, and that the business had an initial estimate of the impact in May 2020. The business expected the slope increase to be 6.\n\nLet\'s use `judgyprophet` to forecast this series from May 2020. We do this by encoding the initial business estimate as a _trend event_.\n\n\n```python\nfrom judgyprophet import JudgyProphet\nimport pandas as pd\nimport seaborn as sns\n\n# Create the expected trend events by consulting with the business\ntrend_events = [\n    {\'name\': "New market entry", \'index\': \'2020-09-01\', \'m0\': 6}\n]\n\n\n# Cutoff the data to May 2020\ndata_may2020 = example_data.loc[:"2020-05-01"]\n\n# Make the forecast with the business estimated level event\n# We have no level events, so just provide the empty list.\njp = JudgyProphet()\n# Because the event is beyond the actuals, judgyprophet throws a warning.\n#    This is just because the Bayesian model at the event has no actuals to learn from.\n#    The event is still used in predictions.\njp.fit(\n    data=data_may2020,\n    level_events=[],\n    trend_events=trend_events,\n    # Set random seed for reproducibility\n    seed=13\n)\npredictions = jp.predict(horizon=12)\n```\n\n    INFO:judgyprophet.judgyprophet:Rescaling onto 0-mean, 1-sd.\n    WARNING:judgyprophet.judgyprophet:Post-event data for trend event New market entry less than 0 points. Event deactivated in model. Event index: 2020-09-01, training data end index: 2019-06-01 00:00:00\n    WARNING:judgyprophet.utils:No active trend or level events (i.e. no event indexes overlap with data). The model will just fit a base trend to the data.\n\n\n    Initial log joint probability = -3.4521\n        Iter      log prob        ||dx||      ||grad||       alpha      alpha0  # evals  Notes\n           3      -2.92768      0.054987   8.11433e-14           1           1        7\n    Optimization terminated normally:\n      Convergence detected: gradient norm is below tolerance\n\n\nBecause we are in May 2020, the forecasting algorithm has nothing to use for learning; so just uses the business estimate. Let\'s plot the result:\n\n\n```python\nfrom judgyprophet.tutorials.resources import plot_forecast\n\nplot_forecast(\n    actuals=example_data,\n    predictions=predictions,\n    cutoff="2020-05-01",\n    events=trend_events\n)\n```\n\n    INFO:prophet:Disabling yearly seasonality. Run prophet with yearly_seasonality=True to override this.\n    INFO:prophet:Disabling weekly seasonality. Run prophet with weekly_seasonality=True to override this.\n    INFO:prophet:Disabling daily seasonality. Run prophet with daily_seasonality=True to override this.\n\n\n\n    Initial log joint probability = -17.0121\n    Iteration  1. Log joint probability =    10.4753. Improved by 27.4875.\n    Iteration  2. Log joint probability =    12.7533. Improved by 2.27796.\n    Iteration  3. Log joint probability =    25.4696. Improved by 12.7163.\n    Iteration  4. Log joint probability =     26.707. Improved by 1.2374.\n    Iteration  5. Log joint probability =    26.7075. Improved by 0.000514342.\n    Iteration  6. Log joint probability =    26.7104. Improved by 0.00296558.\n    Iteration  7. Log joint probability =    26.7122. Improved by 0.00171322.\n    Iteration  8. Log joint probability =    26.7157. Improved by 0.00351772.\n    Iteration  9. Log joint probability =    26.7159. Improved by 0.000208268.\n    Iteration 10. Log joint probability =    26.7159. Improved by 6.64977e-05.\n    Iteration 11. Log joint probability =     26.716. Improved by 6.89899e-05.\n    Iteration 12. Log joint probability =     26.716. Improved by 3.06578e-05.\n    Iteration 13. Log joint probability =     26.716. Improved by 8.91492e-07.\n    Iteration 14. Log joint probability =     26.716. Improved by 8.71052e-09.\n\n\n\n\n![png](docs/output_5_2.png)\n\n\n\nWe can see `judgyprophet` is accounting for the increased trend, but the business slightly overestimated the increase in sales due to the product launch.\n\nLet\'s fast forward to January 2021, the business want to reforecast based on their estimate, and what they\'ve seen so far for the product launch. This is where `judgyprophet` comes into its own.\n\nOnce actuals are observed after the event has taken place, `judgyprophet` updates its estimate of what the event impact is. Let\'s look at this in action:\n\n\n```python\n# Cutoff the data to January 2021\ndata_jan2021 = example_data.loc[:"2021-01-01"]\n\n# Reforecast using the new actuals, not we are at Jan 2021\njp = JudgyProphet()\njp.fit(\n    data=data_jan2021,\n    level_events=[],\n    trend_events=trend_events,\n    # Set random seed for reproducibility\n    seed=13\n)\npredictions = jp.predict(horizon=12)\n```\n\n    INFO:judgyprophet.judgyprophet:Rescaling onto 0-mean, 1-sd.\n    INFO:judgyprophet.judgyprophet:Adding trend event New market entry to model. Event index: 2020-09-01, training data start index: 2019-06-01 00:00:00, training data end index: 2021-01-01 00:00:00. Initial gradient: 6. Damping: None.\n\n\n    Initial log joint probability = -309.562\n        Iter      log prob        ||dx||      ||grad||       alpha      alpha0  # evals  Notes\n          10      -1.64341   2.10244e-05   3.61281e-06           1           1       15\n    Optimization terminated normally:\n      Convergence detected: relative gradient magnitude is below tolerance\n\n\nNow let\'s plot the results:\n\n\n```python\nplot_forecast(actuals=example_data, predictions=predictions, cutoff="2021-01-01", events=trend_events)\n```\n\n    INFO:prophet:Disabling yearly seasonality. Run prophet with yearly_seasonality=True to override this.\n    INFO:prophet:Disabling weekly seasonality. Run prophet with weekly_seasonality=True to override this.\n    INFO:prophet:Disabling daily seasonality. Run prophet with daily_seasonality=True to override this.\n\n\n\n    Initial log joint probability = -24.5881\n    Iteration  1. Log joint probability =   -1.06803. Improved by 23.5201.\n    Iteration  2. Log joint probability =    11.6215. Improved by 12.6895.\n    Iteration  3. Log joint probability =    36.5271. Improved by 24.9056.\n    Iteration  4. Log joint probability =    37.3776. Improved by 0.850488.\n    Iteration  5. Log joint probability =    37.6489. Improved by 0.271259.\n    Iteration  6. Log joint probability =    37.6547. Improved by 0.00580657.\n    Iteration  7. Log joint probability =    37.7831. Improved by 0.128419.\n    Iteration  8. Log joint probability =    37.7884. Improved by 0.00527858.\n    Iteration  9. Log joint probability =     37.789. Improved by 0.000612124.\n    Iteration 10. Log joint probability =    37.7891. Improved by 9.93823e-05.\n    Iteration 11. Log joint probability =    37.7902. Improved by 0.00112416.\n    Iteration 12. Log joint probability =    37.7902. Improved by 3.17397e-06.\n    Iteration 13. Log joint probability =    37.7902. Improved by 1.59404e-05.\n    Iteration 14. Log joint probability =    37.7902. Improved by 5.06854e-07.\n    Iteration 15. Log joint probability =    37.7902. Improved by 6.87792e-07.\n    Iteration 16. Log joint probability =    37.7902. Improved by 4.82761e-08.\n    Iteration 17. Log joint probability =    37.7902. Improved by 2.50385e-07.\n    Iteration 18. Log joint probability =    37.7902. Improved by 6.60322e-09.\n\n\n\n\n![png](docs/output_9_2.png)\n\n\n\nIn this case, once `judgyprophet` observes the data post-event, the Bayesian updating starts to realise the business estimate is a bit large, so it reduces it.\n\nThis was a simple example to demonstrate `judgyprophet`. You can add many trend events into a single forecasting horizon, add damping. You can also add level events – changes in the forecasting level; and seasonality see our other tutorials for details about this.\n',
    'author': 'Jack Baker',
    'author_email': 'jack.baker@astrazeneca.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/AstraZeneca/judgyprophet',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.8,<3.11',
}


setup(**setup_kwargs)
