Source code for biogeme.models.ordered

"""Implements various models.

:author: Michel Bierlaire
:date: Fri Mar 29 17:13:14 2019
"""

import logging
from typing import Type

from biogeme.exceptions import BiogemeError
from biogeme.expressions import Beta, Expression, validate_and_convert
from biogeme.expressions.ordered import (
    OrderedBase,
    OrderedLogLogit as OrderedLogLogitExpr,
    OrderedLogProbit as OrderedLogProbitExpr,
    OrderedLogit as OrderedLogitExpr,
    OrderedProbit as OrderedProbitExpr,
)

logger = logging.getLogger(__name__)


[docs] def build_ordered_thresholds( list_of_discrete_values: list[int], first_threshold_parameter: Beta, ) -> list[Expression]: """Constructs the ordered list of thresholds for the ordered model. Given a list of discrete values, constructs the list of thresholds [tau_1_2, tau_2_3, ..., tau_{J-1_J}] such that tau_1_2 = first_tau, and tau_{j_j+1} = tau_{j-1_j} + diff_j for each intermediate j, where diff_j is a Beta parameter. :param list_of_discrete_values: list of discrete values (must be at least length 2) :param first_threshold_parameter: Beta parameter for the first threshold :return: list of Expression thresholds (length len(list_of_discrete_values) - 1) """ if len(list_of_discrete_values) < 2: raise BiogemeError('Need at least two discrete values for ordered model.') if not isinstance(first_threshold_parameter, Beta): raise BiogemeError( f'first_tau must be a Beta expression, and not a {type(first_threshold_parameter)}.' ) thresholds = [first_threshold_parameter] tau = first_threshold_parameter for val in list_of_discrete_values[1:-1]: diff = Beta( f'{first_threshold_parameter.name}_diff_{val}', 1, 0, None, 0, ) tau = tau + diff thresholds.append(tau) return thresholds
def _ordered_probs_from_thresholds( continuous_value: Expression, scale_parameter: Expression | float, list_of_discrete_values: list[int], threshold_parameters: list[Expression], expr_cls: Type[OrderedBase], ) -> dict[int, Expression]: """Internal helper to build ordered logit/probit probabilities (or log-probabilities). The scale parameter is absorbed by dividing both the latent variable and the thresholds by the scale, so that the probability formula matches the standard ordered-response specification used in the original implementation. :param continuous_value: Continuous quantity to be mapped. :param scale_parameter: Scale parameter of the continuous value (sigma). :param list_of_discrete_values: Ordered list of discrete categories. :param threshold_parameters: List of threshold expressions (length J-1). :param expr_cls: Ordered expression class to use (e.g. :class:`OrderedLogitExpr`, :class:`OrderedProbitExpr`, :class:`OrderedLogLogitExpr`, or :class:`OrderedLogProbitExpr`). :return: Dict mapping each category to its (log-)probability expression. :raises BiogemeError: If input lengths are inconsistent. """ if len(list_of_discrete_values) < 2: raise BiogemeError("Need at least two discrete values for ordered model.") if len(threshold_parameters) != len(list_of_discrete_values) - 1: raise BiogemeError( "threshold_parameters must have length len(list_of_discrete_values)-1, " f"got {len(threshold_parameters)} and {len(list_of_discrete_values)}." ) scale_expr = validate_and_convert(scale_parameter) # Rescaling: CDF is applied to (tau / sigma - eta / sigma), # matching the original formulas based on (x - tau) / sigma. eta_scaled: Expression = continuous_value / scale_expr cutpoints_scaled: list[Expression] = [ tau / scale_expr for tau in threshold_parameters ] categories = list(list_of_discrete_values) probabilities: dict[int, Expression] = {} for cat in categories: # Constant response equal to this category; the Ordered* expression # then returns P(Y = cat) or log P(Y = cat) for each observation. y_expr = validate_and_convert(cat) model_expr = expr_cls( eta=eta_scaled, cutpoints=cutpoints_scaled, y=y_expr, categories=categories, neutral_labels=None, enforce_order=False, ) probabilities[cat] = model_expr return probabilities
[docs] def ordered_logit( continuous_value: Expression, scale_parameter: Expression | float, list_of_discrete_values: list[int], reference_threshold_parameter: Beta, ) -> dict[int, Expression]: """Ordered logit model that maps a continuous quantity with discrete intervals. This builds the ordered thresholds and returns per-category probabilities using the :class:`OrderedLogitExpr` expression. :param continuous_value: Continuous quantity to be mapped. :param scale_parameter: Scale parameter of the continuous value (sigma). :param list_of_discrete_values: Ordered list of discrete categories. :param reference_threshold_parameter: Parameter for the first threshold (Beta). :return: Dict mapping each category to its probability expression. """ tau_parameters = build_ordered_thresholds( list_of_discrete_values, reference_threshold_parameter ) return _ordered_probs_from_thresholds( continuous_value=continuous_value, scale_parameter=scale_parameter, list_of_discrete_values=list_of_discrete_values, threshold_parameters=tau_parameters, expr_cls=OrderedLogitExpr, )
[docs] def ordered_probit( continuous_value: Expression, scale_parameter: Expression | float, list_of_discrete_values: list[int], reference_threshold_parameter: Beta, ) -> dict[int, Expression]: """Ordered probit model that maps a continuous quantity with discrete intervals. This builds the ordered thresholds and returns per-category probabilities using the :class:`OrderedProbitExpr` expression. :param continuous_value: Continuous quantity to be mapped. :param scale_parameter: Scale parameter of the continuous value (sigma). :param list_of_discrete_values: Ordered list of discrete categories. :param reference_threshold_parameter: Parameter for the first threshold (Beta). :return: Dict mapping each category to its probability expression. """ tau_parameters = build_ordered_thresholds( list_of_discrete_values, reference_threshold_parameter ) return _ordered_probs_from_thresholds( continuous_value=continuous_value, scale_parameter=scale_parameter, list_of_discrete_values=list_of_discrete_values, threshold_parameters=tau_parameters, expr_cls=OrderedProbitExpr, )
[docs] def ordered_logit_from_thresholds( continuous_value: Expression, scale_parameter: Expression | float, list_of_discrete_values: list[int], threshold_parameters: list[Expression], ) -> dict[int, Expression]: """Ordered logit with explicit thresholds using :class:`OrderedLogitExpr`. :param continuous_value: Continuous quantity to be mapped. :param scale_parameter: Scale parameter of the continuous value (sigma). :param list_of_discrete_values: Ordered list of discrete categories. :param threshold_parameters: List of threshold expressions (length J-1). :return: Dict mapping each category to its probability expression. """ return _ordered_probs_from_thresholds( continuous_value=continuous_value, scale_parameter=scale_parameter, list_of_discrete_values=list_of_discrete_values, threshold_parameters=threshold_parameters, expr_cls=OrderedLogitExpr, )
[docs] def ordered_probit_from_thresholds( continuous_value: Expression, scale_parameter: Expression | float, list_of_discrete_values: list[int], threshold_parameters: list[Expression], ) -> dict[int, Expression]: """Ordered probit with explicit thresholds using :class:`OrderedProbitExpr`. :param continuous_value: Continuous quantity to be mapped. :param scale_parameter: Scale parameter of the continuous value (sigma). :param list_of_discrete_values: Ordered list of discrete categories. :param threshold_parameters: List of threshold expressions (length J-1). :return: Dict mapping each category to its probability expression. """ return _ordered_probs_from_thresholds( continuous_value=continuous_value, scale_parameter=scale_parameter, list_of_discrete_values=list_of_discrete_values, threshold_parameters=threshold_parameters, expr_cls=OrderedProbitExpr, )
[docs] def log_ordered_logit( continuous_value: Expression, scale_parameter: Expression | float, list_of_discrete_values: list[int], reference_threshold_parameter: Beta, ) -> dict[int, Expression]: """Log-ordered logit model that maps a continuous quantity with discrete intervals. This builds the ordered thresholds and returns per-category log-likelihood contributions (log-probabilities) using :class:`OrderedLogLogitExpr`. :param continuous_value: Continuous quantity to be mapped. :param scale_parameter: Scale parameter of the continuous value (sigma). :param list_of_discrete_values: Ordered list of discrete categories. :param reference_threshold_parameter: Parameter for the first threshold (Beta). :return: Dict mapping each category to its log-probability expression. """ tau_parameters = build_ordered_thresholds( list_of_discrete_values, reference_threshold_parameter ) return _ordered_probs_from_thresholds( continuous_value=continuous_value, scale_parameter=scale_parameter, list_of_discrete_values=list_of_discrete_values, threshold_parameters=tau_parameters, expr_cls=OrderedLogLogitExpr, )
[docs] def log_ordered_probit( continuous_value: Expression, scale_parameter: Expression | float, list_of_discrete_values: list[int], reference_threshold_parameter: Beta, ) -> dict[int, Expression]: """Log-ordered probit model that maps a continuous quantity with discrete intervals. This builds the ordered thresholds and returns per-category log-likelihood contributions (log-probabilities) using :class:`OrderedLogProbitExpr`. :param continuous_value: Continuous quantity to be mapped. :param scale_parameter: Scale parameter of the continuous value (sigma). :param list_of_discrete_values: Ordered list of discrete categories. :param reference_threshold_parameter: Parameter for the first threshold (Beta). :return: Dict mapping each category to its log-probability expression. """ tau_parameters = build_ordered_thresholds( list_of_discrete_values, reference_threshold_parameter ) return _ordered_probs_from_thresholds( continuous_value=continuous_value, scale_parameter=scale_parameter, list_of_discrete_values=list_of_discrete_values, threshold_parameters=tau_parameters, expr_cls=OrderedLogProbitExpr, )
[docs] def log_ordered_logit_from_thresholds( continuous_value: Expression, scale_parameter: Expression | float, list_of_discrete_values: list[int], threshold_parameters: list[Expression], ) -> dict[int, Expression]: """Log-ordered logit with explicit thresholds using :class:`OrderedLogLogitExpr`. :param continuous_value: Continuous quantity to be mapped. :param scale_parameter: Scale parameter of the continuous value (sigma). :param list_of_discrete_values: Ordered list of discrete categories. :param threshold_parameters: List of threshold expressions (length J-1). :return: Dict mapping each category to its log-probability expression. """ return _ordered_probs_from_thresholds( continuous_value=continuous_value, scale_parameter=scale_parameter, list_of_discrete_values=list_of_discrete_values, threshold_parameters=threshold_parameters, expr_cls=OrderedLogLogitExpr, )
[docs] def log_ordered_probit_from_thresholds( continuous_value: Expression, scale_parameter: Expression | float, list_of_discrete_values: list[int], threshold_parameters: list[Expression], ) -> dict[int, Expression]: """Log-ordered probit with explicit thresholds using :class:`OrderedLogProbitExpr`. :param continuous_value: Continuous quantity to be mapped. :param scale_parameter: Scale parameter of the continuous value (sigma). :param list_of_discrete_values: Ordered list of discrete categories. :param threshold_parameters: List of threshold expressions (length J-1). :return: Dict mapping each category to its log-probability expression. """ return _ordered_probs_from_thresholds( continuous_value=continuous_value, scale_parameter=scale_parameter, list_of_discrete_values=list_of_discrete_values, threshold_parameters=threshold_parameters, expr_cls=OrderedLogProbitExpr, )