convert.to_datatree()
Convert an already-drawn posterior into an ArviZ-schema xarray.DataTree.
Usage
convert.to_datatree(
rng_key,
model,
posterior,
data,
covariates,
*,
num_chains=1,
batch_size=None,
device="host",
coords=None,
time_coord=None,
posterior_dims=None,
covariate_dims=None,
obs_dims=None
)Posterior-first: callers draw their own posterior (mcmc.get_samples() for MCMC, draw_posterior() for a variational fit) and pass it in; to_datatree never draws a posterior of its own. rng_key is consumed only by the in-sample posterior-predictive draws and, when a forecast horizon is present, the forecast draws.
Parameters
rng_key: Array-
PRNG key for the in-sample predictive draws and, when a horizon is present, the forecast draws.
model: ForecastModel-
The forecasting model that produced
posterior. posterior: Mapping[str, ArrayLike]-
Posterior samples of the latent sites of the training run, with a single flattened sample axis leading (NumPyro’s
mcmc.get_samples()order, or the output of draw_posterior()). Host-committed leaves (the output ofdraw_posterior(..., device="host")) and NumPy leaves are accepted directly. Forecast-horizon keys follow the rule of forecast(): one with values (a key that ends in"_future"or contains"_future_", or"forecast") raisesValueError. One with an empty leaf (a site that the model registers on every call, recorded by the training run with a size-0 time axis) is kept in theposteriorgroup as a size-0 variable, and the predictive drivers drop it. An"obs"key is dropped, as in predict_in_sample(): it is not in theposteriorgroup, and the in-sample draws come from the latent sites. Every other key becomes aposteriorvariable, andxarray.DataTreerejects a variable name that contains"/", so a key that contains"/"raisesValueErrorbefore any draw.numpyro.handlers.scopejoins its prefix and the site name with"/"by default (trend/drift): passdivider="_"to everyscopecall of a model you export (trend_drift). The divider changes only the names: a fit drawn under"/"exports without a refit once the model usesdivider="_"and the posterior keys are renamed the same way. Both must change: the drivers substitute posterior sites by name, and forecast() and predict_in_sample() ignore a key that matches no site and draw that site from the prior with no error. to_datatree traces the in-sample model call of predict_in_sample() abstractly, with the posterior substituted as the driver does (no draw, no compile), and raisesValueErrorbefore any draw when such a key names a latentsamplesite that has no key of its own: the key equals the site name with"/"replaced by"_"(a half-applied rename,trend_driftwith a model that still hastrend/drift), or does so without a trailing"_haar"or"_dct"(the posterior of a time_reparam() fit that holdsdrift_haarbut nodrift, passed with the plain model) or without an inner"_decentered"(a fit of a site reparameterized withLocScaleReparam, such asdrift_decenteredordrift_decentered_haar, passed with a model without that reparam). Any other key that matches no site, such as_auto_latentfromPredictiveover anAutoContinuousguide or a derived quantity you add, is stored in theposteriorgroup and changes no draw. data: Array-
In-sample data shaped
(*batch, time, obs): time at axis-2, the observation dim at axis-1and any batch axes to the left (obs_dimsnames them). covariates: Array-
Covariates with time at axis
-2. Whencovariatesextends beyonddataalong the time axis (the package-wide shape convention for a forecast horizon), the trailing rows are treated as future covariates: the returned tree additionally carriespredictions(forecastobsdraws from forecast()) andpredictions_constant_datagroups. num_chains: int = 1-
Number of chains to split
posterior’s flattened sample axis into (and, identically, the in-sample/forecast predictive draws, which are drawn with the same sample count). Defaults to1(a single pseudo-chain, correct for a posterior with no chain structure, e.g. SVI or Pathfinder draws). For an MCMC posterior, pass thenum_chainsthe sampler was run with; see_reshape_chains()for the reshape contract and its divisibility requirement. Must be a positive integer. batch_size: int | None = None-
Optional chunk size that bounds how many draws touch the accelerator at once, across both the in-sample and forecast predictive sampling. When set, sampling runs in chunks of this many draws, each chunk moved to
devicebefore the next is drawn. The per-chunk accelerator footprint is a handful of(batch_size, time, series)buffers, so it scales linearly with this value times the panel width: on wide panels lower it until a chunk fits. The batch size must be strictly below the draw count for that bound to hold: at or above it, sampling falls back to the single-shot path and the full array is materialized on the default device before the single transfer. Chunking changes the PRNG stream layout of the predictive draws, so results are reproducible per(rng_key, batch_size).None(default) samples everything in one shot (the results are still moved todevice). device: jax.Device | str | None = "host"-
Where the predictive draws are moved as they are sampled, forwarded to the
deviceargument of predict_in_sample() and forecast() (the placement contract of draw_posterior()). It is resolved once and the same placement is handed to both, so an unmet"cpu"warns once per export. The default"host"keeps the predictive draws in pageable host memory (jax Arrays the tree views as NumPy without a copy, or NumPy arrays when no CPU backend is initialized), which is what bounds accelerator memory whenbatch_sizeis set; passNoneto keep the draws on the default device (chunked compute without per-chunk host transfers, for when the draws fit on the accelerator and transfers would dominate runtime). coords: Mapping[str, Collection[Any]] | None = None-
Optional extra coordinates; these take precedence over the generated
timecoordinate. They also propagate to the forecast groups, where the generated forecasttimetakes precedence instead (a usertimeentry covers the in-sample window; usetime_coordfor explicit forecast time values). Each value is a one-dimensional collection of labels (a list, a NumPy array, a pandas index) and is stored asnp.asarray(value), so the name of a pandas index is dropped. time_coord: Collection[Any] | None = None-
Optional explicit time coordinate values. Without a forecast horizon it covers the in-sample window (defaults to
range(n_time)); with a horizon it must cover the fullcovariateslength and is split into the in-sample and forecast time coordinates (the default is the integer continuation). Any one-dimensional collection is accepted (a list, a NumPy array, a pandas index) and stored asnp.asarray(time_coord). A timezone-awarepd.DatetimeIndexkeeps its zone and clock time, as an object array of awarepd.Timestampvalues (arviz_base.dict_to_dataset()cannot store a zoned datetime dtype). xarray does not treat that coordinate as a datetime:.dt,resample()and selection by a date string fail (select with apd.Timestamp), andto_netcdf()cannot write it. For a datetime coordinate passindex.tz_convert("UTC").tz_localize(None)(UTC times), orindex.tz_localize(None)(local clock times, which repeat an hour when daylight saving time ends). posterior_dims: Mapping[str, Collection[str]] | None = None-
Optional mapping from a posterior site name to its non-sample dimension names, e.g.
{"drift": ["time"]}. Sites listed here share the tree-widetimecoordinate; unlisted sites keep ArviZ’s auto-named dims. This is an explicit opt-in on purpose: inferring time-indexed sites from trace shapes is fragile (a coincidentaln_params == n_timewould misattribute the axis). Each value, likecovariate_dimsandobs_dims, is any ordered one-dimensional collection ofstr(a list, a tuple, a NumPy array, a pandas index). covariate_dims: Collection[str] | None = None-
Optional dimension names for the stored covariates, one per axis, as any ordered one-dimensional collection of
str; defaults to the 2-D("time", "covariate_dim")layout. Use this whencovariatescarries extra batch axes, e.g. a panel tensor shaped(channel, time, series)withcovariate_dims=["channel", "time", "series"]. The time axis is always-2(the package-wide convention), so its entry should be named"time"to share the tree-wide time coordinate. obs_dims: Collection[str] | None = None-
Optional dimension names for the
obsvariables ofobserved_data,posterior_predictiveandpredictions, one perdataaxis, as any ordered one-dimensional collection ofstr. Defaults to("time", "obs_dim")for 2-Ddataand names the leading batch axes of a panelobs_batch_0,obs_batch_1, …, e.g.("obs_batch_0", "time", "obs_dim")for(batch, time, obs)data. Give a batch axis the name of the matchingcovariate_dimsentry so the groups share it, e.g.obs_dims=["origin", "time", "destin"]withcovariate_dims=["origin", "time", "covariate_dim"];coordslabels andposterior_dimsentries refer to the same names. Axis-2must be named"time"(it carries the tree-wide time coordinate), the names must be distinct, and"chain"and"draw"are reserved for the sample dims.
Returns
xarray.DataTree-
A tree with
posterior((chain, draw, ...), split pernum_chains),posterior_predictive(in-sampleobs),observed_data, andconstant_datagroups. Whencovariatesextends beyonddata, alsopredictionsandpredictions_constant_datagroups (sharing the samenum_chainssplit). Theobsvariable has dimsobs_dimsinobserved_dataand(chain, draw, *obs_dims)inposterior_predictiveandpredictions.
Raises
ValueError-
If
dataorcovariateshas fewer than two axes (time must be at axis-2), ifdatahas no time steps, ifcovariatesis shorter thandataalong the time axis, if aposteriorleaf has no sample axis, if a forecast-horizon key ofposterior(one that ends in"_future"or contains"_future_", or"forecast") has a non-empty leaf, if"obs"is the only key ofposterior, if aposteriorkey contains"/"(a site scoped with the default divider ofnumpyro.handlers.scope; an empty forecast-horizon key counts, because the export keeps it as a variable; seeposteriorabove), if aposteriorkey that matches no site of the model names a latent site that has no key (a half-applied divider rename, or a time_reparam() orLocScaleReparamkey with a model without that reparam; seeposteriorabove), iftime_coordor acoordsvalue is not a one-dimensional collection of labels (a set, a dict, a scalar, a 2-D array, ragged input) or is apd.MultiIndex, iftime_coordmixes time zones (or aware and naive times), ifobs_dims,covariate_dimsor aposterior_dimsvalue is not an ordered one-dimensional collection (a set, a dict, a singlestr), iftime_coordis given but its length does not match the in-sample window plus the forecast horizon, ifnum_chainsis not a positive integer (boolincluded), ifposterior’s sample count is not evenly divisible bynum_chains, if aposteriorvalue of an innovations() site does not have one step perdatarow (a posterior drawn for a different training length), or if the in-sample draws do not have one time step perdatarow or the forecast draws one percovariatesrow pastdata(see predict_in_sample() and forecast()). Also ifobs_dimsdoes not name everydataaxis, does not name axis-2"time", repeats a name or uses"chain"or"draw", if the in-sample or forecast draws do not have one axis perobs_dimsname (the model’sobssite must have the layout ofdata), or if an in-sample draw does not have the shape ofdata(a predictor shared by the series ofdatawithout a series plate draws one column). TypeError-
If a name in
obs_dims,covariate_dimsor aposterior_dimsvalue is not astr. CovariateDimsError-
If
covariate_dimsdoes not name everycovariatesaxis. HostMemoryKindError-
If
device="pinned_host"is requested on a device that exposes no host memory kind (see_host_memory_kind()). DevicePlatformError-
If
devicenames a platform whose backend is not initialized (seeresolve_device()).
Warns
UserWarning-
If
device="cpu"is requested and the JAX CPU backend is not initialized, so the predictive draws take the NumPy path of"host"instead (once per call).
Notes
When a forecast horizon is present, rng_key is split internally into a predictive subkey and a forecast subkey, so passing the same key twice never correlates the two sample sets. When there is no horizon, rng_key is used unsplit for the in-sample predictive draw. batch_size is the built-in route to memory-bounded predictive sampling; for fully manual control over the forecast draws, build the in-sample tree with matching-length covariates and attach the horizon with add_forecast_groups().