Distribution Fitting (nemora.fit)

nemora.fit hosts the shared estimators, grouped-data solvers, and helper utilities that power the current alpha milestone. The module is designed to work with the canonical distribution registry in nemora.distributions and the shared dataclasses in nemora.core.

Key Concepts

  • FitConfig encapsulates starting values, optional bounds, and weights used during optimisation.

  • fit_inventory accepts an InventorySpec and evaluates one or more candidate distributions. Grouped estimators are selected automatically when inventory.metadata["grouped"] evaluates to True.

  • Grouped estimators for Weibull, Johnson SB, Birnbaum–Saunders, and generalised secant mixtures live under nemora.fit.grouped. They expose diagnostics describing the solver path (grouped-ls, grouped-em, grouped-mle).

  • Mixture utilities (fit_mixture_grouped, fit_mixture_samples, mixture_pdf, mixture_cdf, sample_mixture) support finite mixtures with grouped tallies or sample-level data.

  • All fitted results include a diagnostics["method"] entry (curve-fit, lmfit-model, grouped-ls, grouped-mle, …) plus residual summaries and per-fit metadata for downstream reporting.

Module-level functions return FitResult or MixtureFitResult instances from nemora.core.

import numpy as np
from nemora.fit import MixtureComponentSpec, fit_mixture_grouped

hist, edges = np.histogram(samples, bins=40)
midpoints = 0.5 * (edges[:-1] + edges[1:])
mixture = fit_mixture_grouped(
    midpoints,
    hist,
    [MixtureComponentSpec("gamma"), MixtureComponentSpec("gamma")],
    random_state=42,
)
print(mixture.components[0].weight)

API Reference

.. py:module:: nemora.fit

Shared fitting strategies for nemora.

.. py:class:: FitConfig(distribution, initial, bounds=None, weights=None) :module: nemora.fit

Bases: :py:class:object

Configuration payload describing how a candidate distribution should be fitted.

:type distribution: :sphinx_autodoc_typehints_type:\:py\:class\:\str`    :param distribution: Registry name for the distribution to evaluate.    :type initial: :sphinx_autodoc_typehints_type::py:class:`dict`\ \[:py:class:`str`, :py:class:`float`]    :param initial: Mapping of parameter names to initial values supplied to the optimiser.    :type bounds: :sphinx_autodoc_typehints_type::py:class:`dict`\ \[:py:class:`str`, :py:class:`tuple`\ \[:py:class:`float` | :py:obj:`None`, :py:class:`float` | :py:obj:`None`]] | :py:obj:`None`    :param bounds: Optional parameter bounds expressed as ``(lower, upper)`` tuples. ``None`` disables                   bounding for that parameter.    :type weights: :sphinx_autodoc_typehints_type::py:class:`~numpy.ndarray` | :py:obj:`None`` :param weights: Optional vector of observation weights (e.g., compression factors); when provided they are forwarded to the underlying optimiser.

.. py:attribute:: FitConfig.bounds :module: nemora.fit :type: dict[str, tuple[float | None, float | None]] | None

.. py:attribute:: FitConfig.distribution :module: nemora.fit :type: str

.. py:attribute:: FitConfig.initial :module: nemora.fit :type: dict[str, float]

.. py:attribute:: FitConfig.weights :module: nemora.fit :type: ~numpy.ndarray | None

.. py:class:: MixtureComponentSpec(distribution, initial=None) :module: nemora.fit :canonical: nemora.fit.mixture.MixtureComponentSpec

Bases: :py:class:object

Describe one component of a finite mixture.

.. py:attribute:: MixtureComponentSpec.distribution :module: nemora.fit :type: str

.. py:attribute:: MixtureComponentSpec.initial :module: nemora.fit :type: dict[str, float] | None

.. py:function:: default_fit_config(name, x, y) :module: nemora.fit

Construct a heuristic :class:FitConfig for the requested distribution.

The helper inspects the supplied stand table and synthesises parameter seeds and bounds that keep the optimiser on a sensible domain. The seeds are conservative—callers can refine the result or provide their own configuration via FitConfig.

:type name: :sphinx_autodoc_typehints_type:\:py\:class\:\str`    :param name: Registry name of the distribution.    :type x: :sphinx_autodoc_typehints_type::py:class:`~numpy.ndarray`    :param x: Bin midpoints or sample observations in centimetres.    :type y: :sphinx_autodoc_typehints_type::py:class:`~numpy.ndarray`` :param y: Observed tallies or density values aligned with x.

:returns: Heuristic configuration populated with seeds, bounds, and (when appropriate) scale terms. :rtype: FitConfig

.. py:function:: fit_inventory(inventory, distributions, configs, *, fitter=) :module: nemora.fit

Fit one or more candidate distributions to a stand table or grouped inventory.

:type inventory: :sphinx_autodoc_typehints_type:\:py\:class\:\~nemora.core.InventorySpec`    :param inventory: Inventory payload describing the bins/tallies and metadata (e.g., ``metadata['grouped']``).    :type distributions: :sphinx_autodoc_typehints_type::py:class:`~collections.abc.Iterable`\ \[:py:class:`str`]    :param distributions: Sequence of registry names to evaluate.    :type configs: :sphinx_autodoc_typehints_type::py:class:`~collections.abc.Mapping`\ \[:py:class:`str`, :py:class:`~nemora.fit.FitConfig`]    :param configs: Mapping of distribution names to :class:FitConfig instances. Missing entries are populated                    via :func:default_fit_config.    :type fitter: :sphinx_autodoc_typehints_type::py:class:`~collections.abc.Callable`\ \[\[:py:class:`~numpy.ndarray`, :py:class:`~numpy.ndarray`, :py:class:`~nemora.distributions.base.Distribution`, :py:class:`~nemora.fit.FitConfig`], :py:class:`~nemora.core.FitResult`]` :param fitter: Callable implementing the optimisation primitive. Defaults to SciPy curve_fit; grouped inventories automatically invoke specialised estimators when available.

:returns: Fitted results in the order requested. :rtype: list[FitResult]

.. py:function:: fit_mixture_grouped(bins, tallies, components, *, max_iter=200, tol=1e-06, min_weight=0.0001, random_state=None) :module: nemora.fit

Fit a finite mixture to grouped (binned) tallies via EM.

:rtype: :sphinx_autodoc_typehints_type:\:py\:class\:\~nemora.core.MixtureFitResult``

.. py:function:: fit_mixture_samples(samples, components, *, bins=None, max_iter=200, tol=1e-06, min_weight=0.0001, random_state=None) :module: nemora.fit

Wrapper that converts raw samples into grouped tallies before fitting.

:rtype: :sphinx_autodoc_typehints_type:\:py\:class\:\~nemora.core.MixtureFitResult``

.. py:function:: mixture_cdf(x, components) :module: nemora.fit

Evaluate the mixture CDF at points x.

:rtype: :sphinx_autodoc_typehints_type:\:py\:class\:\~numpy.ndarray``

.. py:function:: mixture_pdf(x, components) :module: nemora.fit

Evaluate the PDF of a fitted mixture at points x.

:rtype: :sphinx_autodoc_typehints_type:\:py\:class\:\~numpy.ndarray``

.. py:function:: sample_mixture(size, components, *, random_state=None, lower=None, upper=None, weight_overrides=None) :module: nemora.fit

Draw random samples from a fitted mixture.

:rtype: :sphinx_autodoc_typehints_type:\:py\:class\:\~numpy.ndarray``

.. py:module:: nemora.fit.grouped

Grouped-data estimators for selected distributions.

.. py:function:: get_grouped_estimator(name) :module: nemora.fit.grouped

Return a grouped estimator for the given distribution, if available.

:rtype: :sphinx_autodoc_typehints_type:\:py\:class\:\~collections.abc.Callable`\ \[\[:py:class:`~nemora.core.InventorySpec`, :py:class:`~nemora.fit.FitConfig` | :py:obj:`None`], :py:class:`~nemora.core.FitResult`] | :py:obj:`None``

.. py:module:: nemora.fit.mixture

Finite mixture fitting utilities.

.. py:class:: MixtureComponentSpec(distribution, initial=None) :module: nemora.fit.mixture

Bases: :py:class:object

Describe one component of a finite mixture.

.. py:attribute:: MixtureComponentSpec.distribution :module: nemora.fit.mixture :type: str

.. py:attribute:: MixtureComponentSpec.initial :module: nemora.fit.mixture :type: dict[str, float] | None

.. py:function:: fit_mixture_grouped(bins, tallies, components, *, max_iter=200, tol=1e-06, min_weight=0.0001, random_state=None) :module: nemora.fit.mixture

Fit a finite mixture to grouped (binned) tallies via EM.

:rtype: :sphinx_autodoc_typehints_type:\:py\:class\:\~nemora.core.MixtureFitResult``

.. py:function:: fit_mixture_samples(samples, components, *, bins=None, max_iter=200, tol=1e-06, min_weight=0.0001, random_state=None) :module: nemora.fit.mixture

Wrapper that converts raw samples into grouped tallies before fitting.

:rtype: :sphinx_autodoc_typehints_type:\:py\:class\:\~nemora.core.MixtureFitResult``

.. py:function:: mixture_cdf(x, components) :module: nemora.fit.mixture

Evaluate the mixture CDF at points x.

:rtype: :sphinx_autodoc_typehints_type:\:py\:class\:\~numpy.ndarray``

.. py:function:: mixture_pdf(x, components) :module: nemora.fit.mixture

Evaluate the PDF of a fitted mixture at points x.

:rtype: :sphinx_autodoc_typehints_type:\:py\:class\:\~numpy.ndarray``

.. py:function:: sample_mixture(size, components, *, random_state=None, lower=None, upper=None, weight_overrides=None) :module: nemora.fit.mixture

Draw random samples from a fitted mixture.

:rtype: :sphinx_autodoc_typehints_type:\:py\:class\:\~numpy.ndarray``

.. todo:: Expand this page with grouped-fitting theory notes and worked examples once the ingest and sampling modules are in place.