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']
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.
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.
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.
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.