def create_model(self, prior: R.SdPrior, data: pd.Series): """ Args: prior: an R.SdPrior object describing the prior distribution on the residual variance paramter. data: The time series of observations as a Pandas Series. Returns: A boom.StateSpaceModel object. """ boom_data = boom.Vector(data.values) is_observed = ~data.isna() self._model = boom.StateSpaceModel(boom_data, is_observed) if prior is None: sdy = np.std(data) prior = R.SdPrior(sigma_guess=sdy, upper_limit=sdy * 1.2) boom_prior = boom.ChisqModel(prior.sample_size, prior.sigma_guess) observation_model_sampler = boom.ZeroMeanGaussianConjSampler( self._model.observation_model, boom_prior) observation_model_sampler.set_sigma_upper_limit( prior.upper_limit) self._model.observation_model.set_method(observation_model_sampler) sampler = boom.StateSpacePosteriorSampler( self._model, boom.GlobalRng.rng) self._model.set_method(sampler) self._original_series = data return self._model
def _set_posterior_sampler(self): sampler = boom.ZeroMeanGaussianConjSampler( self._state_model.error_distribution, self._sigma_prior.boom(), boom.GlobalRng.rng) if ( self._sigma_prior.upper_limit > 0 and np.isfinite(self._sigma_prior.upper_limit) ): sampler.set_sigma_upper_limit(self._sigma_prior.upper_limit) self._state_model.set_method(sampler)
def _assign_posterior_sampler(self, innovation_sd_prior: R.SdPrior): innovation_precision_prior = boom.ChisqModel( innovation_sd_prior.sigma_guess, innovation_sd_prior.sample_size) state_model_sampler = boom.ZeroMeanGaussianConjSampler( self._state_model, innovation_precision_prior, seeding_rng=boom.GlobalRng.rng) state_model_sampler.set_sigma_upper_limit( innovation_sd_prior.upper_limit) self._state_model.set_method(state_model_sampler)
def test_mcmc(self): true_sigma = 2.3 data = np.random.randn(100) * true_sigma prior = boom.ChisqModel(1.0, 1.0) self.model.set_data(boom.Vector(data)) sampler = boom.ZeroMeanGaussianConjSampler(self.model, prior) self.model.set_method(sampler) niter = 1000 draws = np.zeros(niter) for i in range(niter): self.model.sample_posterior() draws[i] = self.model.sigma self.assertNotAlmostEqual(draws[0], draws[-1])
def _create_model(self): """ Creaate self._state_model and assign it a posterior sampler. The four priors (level_sigma, slope_sigma, slope_mean, and slope_ar1) must have been created and saved to self. """ level_model = boom.ZeroMeanGaussianModel( self.level_sigma_prior.initial_value) level_sampler = boom.ZeroMeanGaussianConjSampler( level_model, self.level_sigma_prior.boom(), boom.GlobalRng.rng) level_sampler.set_sigma_upper_limit(self.level_sigma_prior.upper_limit) level_model.set_method(level_sampler) slope_model = boom.NonzeroMeanAr1Model( self.slope_mean_prior.initial_value, self.slope_ar1_prior.initial_value, self.slope_sigma_prior.initial_value) slope_sampler = boom.NonzeroMeanAr1Sampler( slope_model, self.slope_mean_prior.boom(), self.slope_ar1_prior.boom(), self.slope_sigma_prior.boom()) slope_sampler.set_sigma_upper_limit(self.slope_sigma_prior.upper_limit) if self.slope_ar1_prior.force_stationary: slope_sampler.force_stationary() if self.slope_ar1_prior.force_positive: slope_sampler.force_ar1_positive() # ---------------------------------------------------------------------- # Create the state model. self._state_model = boom.SemilocalLinearTrendStateModel( level_model, slope_model) # BOOM models can have more than one posterior sampler if the samplers # focus on different parts of the job. self._state_model.set_method(level_sampler) self._state_model.set_method(slope_sampler)
def __init__(self, y=None, level_sigma_prior: R.SdPrior = None, slope_mean_prior: R.NormalPrior = None, slope_ar1_prior: R.Ar1CoefficientPrior = None, slope_sigma_prior: R.SdPrior = None, initial_level_prior: R.NormalPrior = None, initial_slope_prior: R.NormalPrior = None, sdy: float = None, initial_y: float = None): """ Args: y: A numeric vector. The time series to be modeled. level_sigma_prior: The prior distribution for the standard deviation of the increments in the level component of state. slope.mean_prior: The prior distribution for the mean of the AR1 process for the slope component of state. slope_ar1_prior: The prior distribution for the ar1 coefficient in the slope component of state. slope_sigma_prior: The prior distribution for the standard deviation of the increments in the slope component of state. initial_level_prior: The prior distribution for the level component of state at the time of the first observation. initial_slope_prior: The prior distribution for the slope component of state at the time of the first observation. sdy: The standard deviation of y. This can be ignored if y is provided, or if all the required prior distributions are supplied directly. initial.y: The initial value of y. This can be omitted if y is provided. """ # ---------------------------------------------------------------------- # Validate all priors, generating default values as needed. if sdy is None: sdy = np.nanstd(y) if initial_y is None: initial_y = y[0] level_sigma_prior = self._validate_level_sigma_prior( level_sigma_prior, sdy) slope_mean_prior = self._validate_slope_mean_prior( slope_mean_prior, sdy) slope_ar1_prior = self._validate_slope_ar1_prior(slope_ar1_prior, sdy) slope_sigma_prior = self._validate_slope_sigma_prior( slope_sigma_prior, sdy) initial_level_prior = self._validate_initial_level_prior( initial_level_prior, initial_y, sdy) initial_slope_prior = self._validate_initial_slope_prior( initial_slope_prior, sdy) # ---------------------------------------------------------------------- # Create the component models and samplers. level_model = boom.ZeroMeanGaussianModel( level_sigma_prior.initial_value) level_sampler = boom.ZeroMeanGaussianConjSampler( level_model, level_sigma_prior.boom(), boom.GlobalRng.rng) level_sampler.set_sigma_upper_limit(level_sigma_prior.upper_limit) level_model.set_method(level_sampler) slope_model = boom.NonzeroMeanAr1Model(slope_mean_prior.initial_value, slope_ar1_prior.initial_value, slope_sigma_prior.initial_value) slope_sampler = boom.NonzeroMeanAr1Sampler(slope_model, slope_mean_prior.boom(), slope_ar1_prior.boom(), slope_sigma_prior.boom()) slope_sampler.set_sigma_upper_limit(slope_sigma_prior.upper_limit) if slope_ar1_prior.force_stationary: slope_sampler.force_stationary() if slope_ar1_prior.force_positive: slope_sampler.force_ar1_positive() # ---------------------------------------------------------------------- # Create the state model. self._state_model = boom.SemilocalLinearTrendStateModel( level_model, slope_model) self._state_model.set_method(level_sampler) self._state_model.set_method(slope_sampler) # ---------------------------------------------------------------------- # Set the initial distribution priors. self._state_model.set_initial_level_mean(initial_level_prior.mu) self._state_model.set_initial_level_sd(initial_level_prior.sigma) self._state_model.set_initial_slope_mean(initial_slope_prior.mu) self._state_model.set_initial_slope_sd(initial_slope_prior.sigma) self._state_contribution = None
def __init__(self, y, nseasons: int, season_duration: int = 1, initial_state_prior: boom.MvnModel = None, innovation_sd_prior: R.SdPrior = None, sdy: float = None): """ Args: y: The time series being modeled. This can be omitted if either (a) initial_state_prior and sdy and initial_y are passed, or (b) sdy and initial_y are passed. nseasons: The number of seasons in a cycle. season_duration: The number of time periods each season. See below. initial_state_prior: A multivariate normal distribution of dimension nseasons - 1. This is a distribution on the seasonal value at time 0 and on the nseasons-2 previous values. If None is passed then a default prior will be assumed. innovation_sd_prior: Prior distribution on the standard deviation of the innovation terms. If None, then a default prior will be assumed. sdy: The standard deviation of the time series being modeled. Details: """ self._state_model = boom.SeasonalStateModel( nseasons=nseasons, season_duration=season_duration) if initial_state_prior is None: if sdy is None: if y is None: raise Exception("One of 'y', 'sdy', or " "'initial_state_prior' must be supplied.") sdy = np.nanstd(y) initial_state_prior = self._default_initial_state_prior(sdy) if innovation_sd_prior is None: if sdy is None: if y is None: raise Exception("One of 'y', 'sdy', or " "'innovation_sd_prior' must be supplied.") sdy = np.nanstd(y) innovation_sd_prior = self._default_sigma_prior(sdy) self._state_model.set_initial_state_mean( initial_state_prior.mu) self._state_model.set_initial_state_variance( initial_state_prior.Sigma) innovation_precision_prior = boom.ChisqModel( innovation_sd_prior.sigma_guess, innovation_sd_prior.sample_size) state_model_sampler = boom.ZeroMeanGaussianConjSampler( self._state_model, innovation_precision_prior, seeding_rng=boom.GlobalRng.rng) state_model_sampler.set_sigma_upper_limit( innovation_sd_prior.upper_limit) self._state_model.set_method(state_model_sampler) self._state_contribution = None