prophet.serialize

  1# Copyright (c) Facebook, Inc. and its affiliates.
  2
  3# This source code is licensed under the MIT license found in the
  4# LICENSE file in the root directory of this source tree.
  5
  6from __future__ import annotations
  7
  8from collections import OrderedDict
  9from copy import deepcopy
 10from io import StringIO
 11import json
 12from typing import Any, Final
 13
 14import numpy as np
 15import pandas as pd
 16
 17from prophet.__version__ import __version__
 18from prophet.forecaster import Prophet
 19
 20SIMPLE_ATTRIBUTES: Final[list[str]] = [
 21    'growth', 'n_changepoints', 'specified_changepoints', 'changepoint_range',
 22    'yearly_seasonality', 'weekly_seasonality', 'daily_seasonality',
 23    'seasonality_mode', 'seasonality_prior_scale', 'changepoint_prior_scale',
 24    'holidays_prior_scale', 'mcmc_samples', 'interval_width', 'uncertainty_samples',
 25    'y_scale', 'y_min', 'scaling', 'logistic_floor', 'country_holidays', 'component_modes',
 26    'holidays_mode'
 27]
 28
 29PD_SERIES: Final[list[str]] = ["changepoints", "history_dates", "train_holiday_names"]
 30
 31PD_TIMESTAMP: Final[list[str]] = ["start"]
 32
 33PD_TIMEDELTA: Final[list[str]] = ["t_scale"]
 34
 35PD_DATAFRAME: Final[list[str]] = ["holidays", "history", "train_component_cols"]
 36
 37NP_ARRAY: Final[list[str]] = ["changepoints_t"]
 38
 39ORDEREDDICT: Final[list[str]] = ["seasonalities", "extra_regressors"]
 40
 41
 42def model_to_dict(model: Prophet) -> dict[str, Any]:
 43    """Convert a Prophet model to a dictionary suitable for JSON serialization.
 44
 45    Model must be fitted. Skips Stan objects that are not needed for predict.
 46
 47    Can be reversed with model_from_dict.
 48
 49    Parameters
 50    ----------
 51    model: Prophet model object.
 52
 53    Returns
 54    -------
 55    dict that can be used to serialize a Prophet model as JSON or loaded back
 56    into a Prophet model.
 57    """
 58    if model.history is None:
 59        raise ValueError(
 60            "This can only be used to serialize models that have already been fit."
 61        )
 62
 63    model_dict = {
 64        attribute: getattr(model, attribute) for attribute in SIMPLE_ATTRIBUTES
 65    }
 66    # Handle attributes of non-core types
 67    for attribute in PD_SERIES:
 68        if getattr(model, attribute) is None:
 69            model_dict[attribute] = None
 70        else:
 71            model_dict[attribute] = getattr(model, attribute).to_json(
 72                orient='split', date_format='iso'
 73            )
 74    for attribute in PD_TIMESTAMP:
 75        model_dict[attribute] = getattr(model, attribute).timestamp()
 76    for attribute in PD_TIMEDELTA:
 77        model_dict[attribute] = getattr(model, attribute).total_seconds()
 78    for attribute in PD_DATAFRAME:
 79        if getattr(model, attribute) is None:
 80            model_dict[attribute] = None
 81        else:
 82            model_dict[attribute] = getattr(model, attribute).to_json(orient='table', index=False)
 83    for attribute in NP_ARRAY:
 84        model_dict[attribute] = getattr(model, attribute).tolist()
 85    for attribute in ORDEREDDICT:
 86        if attribute == 'extra_regressors':
 87            cleaned = OrderedDict()
 88            for name, props in getattr(model, attribute).items():
 89                props_copy = deepcopy(props)
 90                props_copy['predictor'] = None
 91                cleaned[name] = props_copy
 92            model_dict[attribute] = [
 93                list(cleaned.keys()),
 94                cleaned,
 95            ]
 96        else:
 97            model_dict[attribute] = [
 98                list(getattr(model, attribute).keys()),
 99                getattr(model, attribute),
100            ]
101    # Other attributes with special handling
102    # fit_kwargs -> Transform any numpy types before serializing.
103    # They do not need to be transformed back on deserializing.
104    fit_kwargs = deepcopy(model.fit_kwargs)
105    if 'init' in fit_kwargs:
106        for k, v in fit_kwargs['init'].items():
107            if isinstance(v, np.ndarray):
108                fit_kwargs['init'][k] = v.tolist()
109            elif isinstance(v, np.floating):
110                fit_kwargs['init'][k] = float(v)
111    model_dict['fit_kwargs'] = fit_kwargs
112
113    # Params (Dict[str, np.ndarray])
114    model_dict['params'] = {k: v.tolist() for k, v in model.params.items()}
115    # Attributes that are skipped: stan_fit, stan_backend
116    model_dict["__prophet_version"] = __version__
117    return model_dict
118
119
120def model_to_json(model: Prophet) -> str:
121    """Serialize a Prophet model to json string.
122
123    Model must be fitted. Skips Stan objects that are not needed for predict.
124
125    Can be deserialized with model_from_json.
126
127    Parameters
128    ----------
129    model: Prophet model object.
130
131    Returns
132    -------
133    json string that can be deserialized into a Prophet model.
134    """
135    model_json = model_to_dict(model)
136    return json.dumps(model_json)
137
138
139def _handle_simple_attributes_backwards_compat(model_dict: dict[str, Any]) -> None:
140    """Handle backwards compatibility for SIMPLE_ATTRIBUTES."""
141    # prophet<1.1.5: handle scaling parameters introduced in #2470
142    if 'scaling' not in model_dict:
143        model_dict['scaling'] = 'absmax'
144        model_dict['y_min'] = 0.
145    # prophet<1.1.5: handle holidays_mode parameter introduced in #2477
146    if 'holidays_mode' not in model_dict:
147        model_dict['holidays_mode'] = model_dict['seasonality_mode']
148
149def model_from_dict(model_dict: dict[str, Any]) -> Prophet:
150    """Recreate a Prophet model from a dictionary.
151
152    Recreates models that were converted with model_to_dict.
153
154    Parameters
155    ----------
156    model_dict: Dictionary containing model, created with model_to_dict.
157
158    Returns
159    -------
160    Prophet model.
161    """
162    model = Prophet()  # We will overwrite all attributes set in init anyway
163    # Simple types
164    _handle_simple_attributes_backwards_compat(model_dict)
165    for attribute in SIMPLE_ATTRIBUTES:
166        setattr(model, attribute, model_dict[attribute])
167    for attribute in PD_SERIES:
168        if model_dict[attribute] is None:
169            setattr(model, attribute, None)
170        else:
171            s = pd.read_json(StringIO(model_dict[attribute]), typ='series', orient='split')
172            if s.name == 'ds':
173                if len(s) == 0:
174                    s = pd.to_datetime(s)
175                s = s.dt.tz_localize(None)
176            setattr(model, attribute, s)
177    for attribute in PD_TIMESTAMP:
178        pd_ts = pd.Timestamp.fromtimestamp(model_dict[attribute], tz="UTC").tz_localize(None)
179        setattr(model, attribute, pd_ts)
180    for attribute in PD_TIMEDELTA:
181        setattr(model, attribute, pd.Timedelta(seconds=model_dict[attribute]))
182    for attribute in PD_DATAFRAME:
183        if model_dict[attribute] is None:
184            setattr(model, attribute, None)
185        else:
186            df = pd.read_json(StringIO(model_dict[attribute]), typ='frame', orient='table', convert_dates=['ds'])
187            if attribute == 'train_component_cols':
188                # Special handling because of named index column
189                df.columns.name = 'component'
190                df.index.name = 'col'
191            setattr(model, attribute, df)
192    for attribute in NP_ARRAY:
193        setattr(model, attribute, np.array(model_dict[attribute]))
194    for attribute in ORDEREDDICT:
195        key_list, unordered_dict = model_dict[attribute]
196        od = OrderedDict()
197        for key in key_list:
198            od[key] = unordered_dict[key]
199        setattr(model, attribute, od)
200    # Other attributes with special handling
201    # fit_kwargs
202    model.fit_kwargs = model_dict['fit_kwargs']
203    # Params (Dict[str, np.ndarray])
204    model.params = {k: np.array(v) for k, v in model_dict['params'].items()}
205    # Skipped attributes
206    model.stan_backend = None
207    model.stan_fit = None
208    return model
209
210
211def model_from_json(model_json: str) -> Prophet:
212    """Deserialize a Prophet model from json string.
213
214    Deserializes models that were serialized with model_to_json.
215
216    Parameters
217    ----------
218    model_json: Serialized model string
219
220    Returns
221    -------
222    Prophet model.
223    """
224    model_dict = json.loads(model_json)
225    return model_from_dict(model_dict)
SIMPLE_ATTRIBUTES: Final[list[str]] = ['growth', 'n_changepoints', 'specified_changepoints', 'changepoint_range', 'yearly_seasonality', 'weekly_seasonality', 'daily_seasonality', 'seasonality_mode', 'seasonality_prior_scale', 'changepoint_prior_scale', 'holidays_prior_scale', 'mcmc_samples', 'interval_width', 'uncertainty_samples', 'y_scale', 'y_min', 'scaling', 'logistic_floor', 'country_holidays', 'component_modes', 'holidays_mode']
PD_SERIES: Final[list[str]] = ['changepoints', 'history_dates', 'train_holiday_names']
PD_TIMESTAMP: Final[list[str]] = ['start']
PD_TIMEDELTA: Final[list[str]] = ['t_scale']
PD_DATAFRAME: Final[list[str]] = ['holidays', 'history', 'train_component_cols']
NP_ARRAY: Final[list[str]] = ['changepoints_t']
ORDEREDDICT: Final[list[str]] = ['seasonalities', 'extra_regressors']
def model_to_dict(model: prophet.Prophet) -> dict[str, typing.Any]:
 43def model_to_dict(model: Prophet) -> dict[str, Any]:
 44    """Convert a Prophet model to a dictionary suitable for JSON serialization.
 45
 46    Model must be fitted. Skips Stan objects that are not needed for predict.
 47
 48    Can be reversed with model_from_dict.
 49
 50    Parameters
 51    ----------
 52    model: Prophet model object.
 53
 54    Returns
 55    -------
 56    dict that can be used to serialize a Prophet model as JSON or loaded back
 57    into a Prophet model.
 58    """
 59    if model.history is None:
 60        raise ValueError(
 61            "This can only be used to serialize models that have already been fit."
 62        )
 63
 64    model_dict = {
 65        attribute: getattr(model, attribute) for attribute in SIMPLE_ATTRIBUTES
 66    }
 67    # Handle attributes of non-core types
 68    for attribute in PD_SERIES:
 69        if getattr(model, attribute) is None:
 70            model_dict[attribute] = None
 71        else:
 72            model_dict[attribute] = getattr(model, attribute).to_json(
 73                orient='split', date_format='iso'
 74            )
 75    for attribute in PD_TIMESTAMP:
 76        model_dict[attribute] = getattr(model, attribute).timestamp()
 77    for attribute in PD_TIMEDELTA:
 78        model_dict[attribute] = getattr(model, attribute).total_seconds()
 79    for attribute in PD_DATAFRAME:
 80        if getattr(model, attribute) is None:
 81            model_dict[attribute] = None
 82        else:
 83            model_dict[attribute] = getattr(model, attribute).to_json(orient='table', index=False)
 84    for attribute in NP_ARRAY:
 85        model_dict[attribute] = getattr(model, attribute).tolist()
 86    for attribute in ORDEREDDICT:
 87        if attribute == 'extra_regressors':
 88            cleaned = OrderedDict()
 89            for name, props in getattr(model, attribute).items():
 90                props_copy = deepcopy(props)
 91                props_copy['predictor'] = None
 92                cleaned[name] = props_copy
 93            model_dict[attribute] = [
 94                list(cleaned.keys()),
 95                cleaned,
 96            ]
 97        else:
 98            model_dict[attribute] = [
 99                list(getattr(model, attribute).keys()),
100                getattr(model, attribute),
101            ]
102    # Other attributes with special handling
103    # fit_kwargs -> Transform any numpy types before serializing.
104    # They do not need to be transformed back on deserializing.
105    fit_kwargs = deepcopy(model.fit_kwargs)
106    if 'init' in fit_kwargs:
107        for k, v in fit_kwargs['init'].items():
108            if isinstance(v, np.ndarray):
109                fit_kwargs['init'][k] = v.tolist()
110            elif isinstance(v, np.floating):
111                fit_kwargs['init'][k] = float(v)
112    model_dict['fit_kwargs'] = fit_kwargs
113
114    # Params (Dict[str, np.ndarray])
115    model_dict['params'] = {k: v.tolist() for k, v in model.params.items()}
116    # Attributes that are skipped: stan_fit, stan_backend
117    model_dict["__prophet_version"] = __version__
118    return model_dict

Convert a Prophet model to a dictionary suitable for JSON serialization.

Model must be fitted. Skips Stan objects that are not needed for predict.

Can be reversed with model_from_dict.

Parameters
  • model (Prophet model object.):
Returns
  • dict that can be used to serialize a Prophet model as JSON or loaded back
  • into a Prophet model.
def model_to_json(model: prophet.Prophet) -> str:
121def model_to_json(model: Prophet) -> str:
122    """Serialize a Prophet model to json string.
123
124    Model must be fitted. Skips Stan objects that are not needed for predict.
125
126    Can be deserialized with model_from_json.
127
128    Parameters
129    ----------
130    model: Prophet model object.
131
132    Returns
133    -------
134    json string that can be deserialized into a Prophet model.
135    """
136    model_json = model_to_dict(model)
137    return json.dumps(model_json)

Serialize a Prophet model to json string.

Model must be fitted. Skips Stan objects that are not needed for predict.

Can be deserialized with model_from_json.

Parameters
  • model (Prophet model object.):
Returns
  • json string that can be deserialized into a Prophet model.
def model_from_dict(model_dict: dict[str, typing.Any]) -> prophet.Prophet:
150def model_from_dict(model_dict: dict[str, Any]) -> Prophet:
151    """Recreate a Prophet model from a dictionary.
152
153    Recreates models that were converted with model_to_dict.
154
155    Parameters
156    ----------
157    model_dict: Dictionary containing model, created with model_to_dict.
158
159    Returns
160    -------
161    Prophet model.
162    """
163    model = Prophet()  # We will overwrite all attributes set in init anyway
164    # Simple types
165    _handle_simple_attributes_backwards_compat(model_dict)
166    for attribute in SIMPLE_ATTRIBUTES:
167        setattr(model, attribute, model_dict[attribute])
168    for attribute in PD_SERIES:
169        if model_dict[attribute] is None:
170            setattr(model, attribute, None)
171        else:
172            s = pd.read_json(StringIO(model_dict[attribute]), typ='series', orient='split')
173            if s.name == 'ds':
174                if len(s) == 0:
175                    s = pd.to_datetime(s)
176                s = s.dt.tz_localize(None)
177            setattr(model, attribute, s)
178    for attribute in PD_TIMESTAMP:
179        pd_ts = pd.Timestamp.fromtimestamp(model_dict[attribute], tz="UTC").tz_localize(None)
180        setattr(model, attribute, pd_ts)
181    for attribute in PD_TIMEDELTA:
182        setattr(model, attribute, pd.Timedelta(seconds=model_dict[attribute]))
183    for attribute in PD_DATAFRAME:
184        if model_dict[attribute] is None:
185            setattr(model, attribute, None)
186        else:
187            df = pd.read_json(StringIO(model_dict[attribute]), typ='frame', orient='table', convert_dates=['ds'])
188            if attribute == 'train_component_cols':
189                # Special handling because of named index column
190                df.columns.name = 'component'
191                df.index.name = 'col'
192            setattr(model, attribute, df)
193    for attribute in NP_ARRAY:
194        setattr(model, attribute, np.array(model_dict[attribute]))
195    for attribute in ORDEREDDICT:
196        key_list, unordered_dict = model_dict[attribute]
197        od = OrderedDict()
198        for key in key_list:
199            od[key] = unordered_dict[key]
200        setattr(model, attribute, od)
201    # Other attributes with special handling
202    # fit_kwargs
203    model.fit_kwargs = model_dict['fit_kwargs']
204    # Params (Dict[str, np.ndarray])
205    model.params = {k: np.array(v) for k, v in model_dict['params'].items()}
206    # Skipped attributes
207    model.stan_backend = None
208    model.stan_fit = None
209    return model

Recreate a Prophet model from a dictionary.

Recreates models that were converted with model_to_dict.

Parameters
  • model_dict (Dictionary containing model, created with model_to_dict.):
Returns
  • Prophet model.
def model_from_json(model_json: str) -> prophet.Prophet:
212def model_from_json(model_json: str) -> Prophet:
213    """Deserialize a Prophet model from json string.
214
215    Deserializes models that were serialized with model_to_json.
216
217    Parameters
218    ----------
219    model_json: Serialized model string
220
221    Returns
222    -------
223    Prophet model.
224    """
225    model_dict = json.loads(model_json)
226    return model_from_dict(model_dict)

Deserialize a Prophet model from json string.

Deserializes models that were serialized with model_to_json.

Parameters
  • model_json (Serialized model string):
Returns
  • Prophet model.