Results¶
Output parsing: parameter tables, spectra tables, and FITS HDU lists.
These functions transform raw posterior samples + ModelArgs
into user-friendly Table objects and FITS files.
- unite.results.count_parameters(model_fn, model_args)[source]¶
Count the number of free scalar parameters (degrees of freedom) in the model.
Traces the model with a dummy PRNG key and counts every latent (non-observed) sample site, summing the sizes of all their shapes. This gives the total number of unconstrained scalar parameters — i.e. the model degrees of freedom.
- Return type:
- Parameters:
- Returns:
- int
Total number of free scalar parameters.
Examples
>>> model_fn, model_args = builder.build() >>> print(f'Free parameters: {count_parameters(model_fn, model_args)}') Free parameters: 14
- unite.results.make_parameter_table(samples, args, *, percentiles=None)[source]¶
Build an Astropy table of posterior parameter samples in physical units.
- Return type:
- Parameters:
- samplesdict of str to ndarray
Posterior samples in physical space. When using
ModelBuilder.fit(), samples are already transformed. When callingmcmc.get_samples()directly, first pass throughtransform_reparam_samples()to convert any reparameterized (unit-space) parameters back to physical values.- argsModelArgs
Model arguments from
ModelBuilder.build().- percentilesndarray of float or None
Array of percentile values in range (0, 1), e.g.
[0.16, 0.5, 0.84]. If provided, returns one row per percentile with percentile values. IfNone(default), returns one row per posterior sample.
- Returns:
- astropy.table.QTable
If
percentilesisNone: one row per posterior sample. Ifpercentilesis provided: one row per percentile with apercentilecolumn and one column per parameter. Columns carry physical units where known:Line flux parameters —
flux_unit * canonical_wl_unitFWHM parameters — km/s
Continuum
scaleparameters —flux_unitContinuum
slope/ polynomial coefficients —flux_unit / wl_unit^nShape / index parameters (
beta,temperature, …) — raw valuesRest equivalent width columns (
rew_{line_label}) —canonical_wl_unit. One column per line whose rest-frame wavelength falls within a continuum region. Appended after all model parameters when a continuum is present.
- Parameters:
Notes
For absorption lines, the rest equivalent width is computed by numerically integrating the absorbed flux profile over the spectrum with the finest pixel grid covering the line. Use absorption REW values with caution when the covering spectrum does not fully resolve the absorption profile.
- unite.results.make_spectra_tables(samples, args, *, insert_nan=False, percentiles=None, return_hdul=False)[source]¶
- Overloads:
samples (dict[str, np.ndarray]), args (ModelArgs), insert_nan (bool), percentiles (np.ndarray | None), return_hdul (Literal[False]) → dict[str, Table]
samples (dict[str, np.ndarray]), args (ModelArgs), insert_nan (bool), percentiles (np.ndarray | None), return_hdul (Literal[True]) → fits.HDUList
- Parameters:
- Return type:
Build per-spectrum tables of model decompositions.
- Parameters:
- samplesdict of str to ndarray
Posterior samples.
- argsModelArgs
Model arguments from
ModelBuilder.build().- insert_nanbool
If
True, insert one NaN row at the midpoint wavelength between each pair of consecutive continuum regions. DefaultFalse.- percentilesndarray of float or None
Array of percentile values in range (0, 1), e.g.
[0.16, 0.5, 0.84]. If provided, collapses the sample dimension to those percentiles (shape(n_percentiles, n_pixels)). IfNone(default), returns all samples (shape(n_samples, n_pixels)).- return_hdulbool
If
True, wrap the per-spectrum tables in anHDUListand return that instead of a dict. HDU 0 is an emptyPrimaryHDU; subsequent HDUs areBinTableHDUentries whose extension names are the spectrum names (upper-cased for FITS compatibility). DefaultFalse.
- Returns:
- dict of str to astropy.table.QTable, or astropy.io.fits.HDUList
When
return_hdul=False(default): a dict keyed by spectrum name, one table per spectrum. Whenreturn_hdul=True: anHDUListwith HDU 0 empty and oneBinTableHDUper spectrum. In both cases columns carry physical units whereflux_unitwas set on the spectrum.
- Parameters:
- Return type:
- unite.results.make_hdul(samples, args, *, insert_nan=False, percentiles=None)[source]¶
Build a FITS HDU list from posterior samples.
- Return type:
- Parameters:
- samplesdict of str to ndarray
Posterior samples.
- argsModelArgs
Model arguments from
ModelBuilder.build().- insert_nanbool
Insert NaN rows between continuum regions. Default
False.- percentilesndarray of float or None
Array of percentile values in range (0, 1). If provided, output tables contain percentile rows/columns. If
None(default), output tables contain all samples.
- Returns:
- astropy.io.fits.HDUList
HDU 0: PrimaryHDU (empty, metadata in header). HDU 1: BinTableHDU from parameter table. HDU 2+: BinTableHDU per spectrum.
- Parameters:
- unite.results.freeze_from_samples(samples, args, *, cenfunc='median')[source]¶
Convert posterior samples to
Fixedpriors.For each parameter in args, wraps its central value in a
Fixedprior. Free parameters are summarised by cenfunc applied to their posterior samples. Parameters that were alreadyFixedin the original fit (such asnorm_wav_a) are passed through with their constant value, making the returned dict a complete set of frozen priors – no separate lookup of region centres or other fixed quantities is needed.Intended for re-fitting the same observation with a different model (e.g. adding or removing a line component while holding kinematics fixed). Always reuse the same
Spectraobject between fits so thatline_scaleandcontinuum_scaleare identical and the frozen amplitude values remain physically consistent.- Return type:
- Parameters:
- samplesdict
Posterior samples, e.g. from
mcmc.get_samples().- argsModelArgs
Model arguments from
build()for the original fit.- cenfuncstr or callable, optional
Central-value function. Default
'median'.If a callable, it is called with a 1-D
numpy.ndarrayof posterior samples and must return a scalar.If a string, recognised presets are:
'median'Coordinate-wise posterior median.
'mean'Coordinate-wise posterior mean.
'map'Maximum-a-posteriori sample – the single sample with the highest log-joint
samples['log_prob']. Requires'log_prob'in samples (produced automatically byModelBuilder.fit()).'mle'Maximum-likelihood sample – the single sample with the highest total log-likelihood
samples['log_likelihood']. Requires'log_likelihood'in samples (produced automatically byModelBuilder.fit()).
- Returns:
- dict[str, Fixed]
Mapping from numpyro site name to
Fixedprior. Keys cover all parameters inargs.dependency_order: free parameters frozen at the chosen central value, already-Fixed parameters passed through at their constant value.
- Raises:
- ValueError
If
cenfunc='map'is requested but'log_prob'is not in samples.- ValueError
If
cenfunc='mle'is requested but'log_likelihood'is not in samples.- ValueError
If cenfunc is an unrecognised string.
- Parameters:
Examples
Freeze all parameters at their posterior median for a re-fit on the same spectra (e.g. to add a component while holding kinematics fixed).
norm_wav_ais automatically included – no manual lookup needed:>>> frozen = freeze_from_samples(samples, args) >>> fwhm_narrow.prior = frozen['fwhm_gauss_narrow'] >>> norm_wav_token.prior = frozen['norm_wav_a'] # was already Fixed
Use the MAP sample (highest log posterior) – more robust when scale and slope parameters are correlated. Requires
samplesto contain a'log_prob'key (produced automatically byModelBuilder.fit()):>>> frozen_map = freeze_from_samples(samples, args, cenfunc='map')
Use the MLE sample (highest total log-likelihood):
>>> frozen_mle = freeze_from_samples(samples, args, cenfunc='mle')
Use the mean instead of the median:
>>> frozen = freeze_from_samples(samples, args, cenfunc='mean')
Pass a callable for custom logic (e.g. a high percentile):
>>> import numpy as np >>> frozen = freeze_from_samples(samples, args, cenfunc=lambda x: np.percentile(x, 75))
Model evaluator: decompose posterior predictions into per-line and per-region contributions.
Given posterior samples and ModelArgs, this module
reconstructs the full model prediction for each spectrum, broken down into
individual line and continuum-region contributions in original flux units.
- class unite.compute.SpectrumPrediction(wavelength, total, lines, continuum_regions, tau_profiles)[source]¶
Bases:
objectDecomposed model prediction for a single spectrum.
All arrays are in original (un-normalized) flux units.
For emission lines, each entry in
linesis the intrinsic (un-attenuated) flux profile:flux * profile. Summing all line contributions and continuum regions always reconstructstotalregardless of zorder configuration.For absorption lines, each entry in
linesis the flux removed by that absorber (negative):total - total_without_j.- Parameters:
- lines: dict[str, ndarray]¶
Per-line contributions keyed by informative line labels (e.g.
'Ha','[NII]_6585'). For emission lines: intrinsic (un-attenuated) flux profile (positive). For absorption lines: flux removed by the absorber (negative). Shape(n_samples, n_pixels)each.
- unite.compute.evaluate_model(samples, args)[source]¶
Evaluate the model for each posterior sample and decompose contributions.
Uses
jax.vmap()to evaluate all samples in a single vectorised XLA kernel launch rather than a Python loop, giving a large speed-up when the number of posterior samples is large.- Return type:
- Parameters:
- samplesdict of str to ndarray
Posterior samples as returned by
mcmc.get_samples()orPredictive. Each value has shape(n_samples,)or(n_samples, ...).- argsModelArgs
Pre-built data bundle from
ModelBuilder.build().
- Returns:
- list of SpectrumPrediction
One prediction per spectrum in
args.spectra.
- Parameters: