BassModel#
- class pymc_marketing.bass.model.BassModel(model_config=None, sampler_config=None)[source]#
Bass diffusion model for product adoption forecasting.
Wraps the functional
create_bass_model()inside theModelBuilderinterface, providing standardised.fit(),.save(),.load()and related methods. The underlyingpm.Modelis accessible viamodel.modelfor direct use with PyMC functions.- Parameters:
- model_config
dict, optional Dictionary with keys
"m","p","q","likelihood"mapping toPrioror a term recipe built frompymc_marketing.terms. A recipe may carry covariates throughDot; the dataset they are read from is the one passed tofit().A recipe that carries its own name must name itself after the config key, so the posterior keeps a variable named
m/p/q; compositions and nameless terms (e.g.Transform) are wrapped inNamedautomatically, so noNamedis needed here.pandqmust not vary overT— Bass uses the closed-form solution, which assumes time-constant rates, and a time-varying rate is rejected — but may carry other dimensions such asproduct, giving a per-product rate. Seedefault_model_config()for defaults.- sampler_config
dict, optional Dictionary of sampler settings (draws, tune, chains, …). See
default_sampler_config()for defaults.
- model_config
Notes
Data format#
When using
xr.Dataset, theTcoordinate is required and represents the time index. Anobserveddata variable can hold adoption counts (omit for prior-predictive only).Single-product — 1-D
observedwithTas the only dimension:xr.Dataset( {"observed": ("T", counts)}, coords={"T": np.arange(N)}, )
Multi-product —
observedwithTandproductdimensions:xr.Dataset( {"observed": (("T", "product"), counts)}, coords={"T": np.arange(N), "product": ["A", "B", "C"]}, )
Other input types (
np.ndarray,pd.Series,pd.DataFrame) are auto-converted viato_bass_dataset().Examples
Fit a single-product model
import numpy as np import arviz as az from pymc_marketing.bass import BassModel y = np.random.poisson(lam=100, size=50) model = BassModel() idata = model.fit(data=y) print(az.summary(idata, var_names=["m", "p", "q"]))
Multi-product with custom priors
import xarray as xr from pymc_extras.prior import Prior data = xr.Dataset( {"observed": (("T", "product"), np.random.poisson(100, size=(50, 3)))}, coords={"T": np.arange(50), "product": ["A", "B", "C"]}, ) model = BassModel( model_config={ "m": Prior("Normal", mu=5_000, sigma=1_000), "p": Prior("Beta", alpha=1.5, beta=20), "q": Prior("Beta", alpha=2, beta=5), "likelihood": Prior("Poisson"), }, ) idata = model.fit(data=data) print(az.summary(idata, var_names=["m", "p", "q"]))
Term recipes, with a per-product covariate
m/p/qaccept recipes composed frompymc_marketing.terms, so a rate can be scaled by a covariate read from the dataset. A recipe that names itself must use the config key as its name; compositions and nameless terms are wrapped automatically, so noNamedis needed here. Only the variables a recipe names are registered, and a covariate must vary across a dimension other thanT(pandqassume time-constant rates).import pytensor.xtensor as ptx from pymc_extras.prior import Prior from pymc_marketing.terms import Dot, Parameter, Transform data = xr.Dataset( { "observed": (("T", "product"), np.random.poisson(100, size=(50, 3))), # one covariate value per product "log_market_size": ("product", np.log([100.0, 250.0, 60.0])), }, coords={"T": np.arange(50), "product": ["A", "B", "C"]}, ) model = BassModel( model_config={ "m": Parameter("m", prior=Prior("HalfNormal", sigma=100_000)), # a per-product innovation rate, positive by construction "p": Parameter("p_base", prior=Prior("Beta", alpha=1.5, beta=20)) * Transform( Dot( var_name="log_market_size", name="p_coef", prior=Prior("Normal", mu=0, sigma=0.3), ), ptx.math.exp, ), "q": Prior("Beta", alpha=2, beta=5), "likelihood": Prior("NegativeBinomial", n=1.5, dims="product"), }, ) idata = model.fit(data=data) # p now carries a "product" dim, and the covariate is shared data print(az.summary(idata, var_names=["m", "p", "q", "p_coef"]))
Generate synthetic data and fit
Build the model without an
observedvariable (only aTcoordinate), draw a prior predictive sample, then fit to it:import xarray as xr import pymc as pm ds = xr.Dataset({"T": np.arange(50)}) model = BassModel() model.build_model(data=ds) with model.model: prior = pm.sample_prior_predictive(draws=50, random_seed=42) y_sim = prior.prior["y"].sel(draw=0, chain=0) # Now fit the model to the synthetic data idata = model.fit(data=y_sim.values)
Posterior predictive checks
Generate posterior predictive samples after fitting:
pp_data = model.sample_posterior_predictive(X=new_time_points)
The posterior contains deterministics such as
adopters,innovators,imitators, andpeakthat can be analysed directly viaidata.posterior, e.g.:azp.plot_forest(idata.posterior["peak"], combined=True)
Methods
BassModel.__init__([model_config, ...])Initialize model configuration and sampler configuration for the model.
Convert the model configuration and sampler configuration from the attributes to keyword arguments.
BassModel.build_from_idata(idata)Rebuild the model from a
DataTreeobject.BassModel.build_model([data])Build the Bass diffusion model from the given data.
Build the
fit_datagroup stored alongside the posterior.Create attributes for the inference data.
BassModel.fit([data, method, progressbar, ...])Fit the Bass diffusion model.
BassModel.graphviz(**kwargs)Get the graphviz representation of the model.
Create the model configuration and sampler configuration from the DataTree to keyword arguments.
BassModel.load(fname[, check])Create a ModelBuilder instance from a file.
BassModel.load_from_idata(idata[, check])Create a ModelBuilder instance from a DataTree object.
BassModel.plot_adoption_curve(**kwargs)Plot the posterior adoption curve with the observed data.
BassModel.plot_cumulative(**kwargs)Plot the cumulative adoption S-curve with the observed data.
BassModel.plot_decomposition(**kwargs)Plot the adoption decomposition into innovators and imitators.
BassModel.plot_peak(**kwargs)Plot the posterior distribution of the peak adoption time.
Perform transformation on the model after sampling.
BassModel.sample_posterior_predictive(X[, ...])Sample from the model's posterior predictive distribution.
BassModel.save(fname, **kwargs)Save the model's inference data to a file.
BassModel.set_idata_attrs([idata])Set attributes on a DataTree object.
BassModel.table(**model_table_kwargs)Get the summary table of the model.
Attributes
Default model configuration with weakly informative priors.
Default sampler configuration.
Get the posterior fit_result.
Generate a unique hash value for the model.
Return the name of the output variable.
Access the 'posterior' group of the DataTree.
Access the 'posterior_predictive' group of the DataTree.
Access the 'predictions' group of the DataTree.
Access the 'prior' group of the DataTree.
Access the 'prior_predictive' group of the DataTree.