BayesianBLP.counterfactual_shares#

BayesianBLP.counterfactual_shares(price_change=None, *, periods=None, regions=None, n_samples=200)[source]#

Posterior shares under a counterfactual price intervention.

Holds the posterior ξ_jt (the structural shock) fixed and recomputes the share equation at the new price. This is the BLP-correct structural counterfactual: “what would shares have been if price for product X had been Y, given the realised demand shocks?”.

Parameters:
price_changedict, ndarray, or None

dict {product_name: relative_change} — multiplicative shift (e.g. {"sku_a": 0.10} raises sku_a price by 10%). Combine with periods= / regions= to scope the intervention. ndarray of shape (M, J) is accepted only when time_col was not supplied at construction (the flat-ndarray semantics are ambiguous when M = R*T). None — no change (canary; should reproduce fitted shares).

periodssequence of period labels or slice, optional

Restrict the price change to these periods (coord labels, not integer positions). Only valid when time_col is set.

regionssequence of region labels, optional

Restrict the price change to these regions (coord labels).

n_samplesint, optional

Number of posterior samples to draw the counterfactual at. None uses every chain × draw.

Returns:
xr.Dataset with s_inside (sample, market, inside_product) and
s_outside (sample, market). When time_col was set on the
model, the dataset also carries non-dimensional period and
region coordinates aligned with market so callers can
.set_index(market=("region", "period")) for tidy slicing.

Notes

We do not route this through pm.do + sample_posterior_predictive. Because the model uses the conditional decomposition ξ̃ = ρ·(σ_ξ/σ_η)·η + σ_ξ·sqrt(1-ρ²)·raw for sampler geometry, ξ would silently shift when price changes (since η = price - μ_p and the conditional mean of ξ̃ depends on η). The numpy path used here pulls the assembled xi Deterministic from the posterior and treats it as fixed, which gives the structural elasticity semantics.