Sequential estimation of a choice model with a latent variableΒΆ

This example illustrates the second step of a sequential estimation procedure.

The latent variable and its structural equation are first estimated using the Gaussian MIMIC model of plot_h02_lv_mimic_gauss. The estimated structural parameters are then loaded from the saved results file and used to construct the latent-variable expression appearing in the choice model.

Only the choice-model parameters are estimated in this script. The latent variable is not re-estimated jointly with the measurement equations.

Michel Bierlaire Mon Jun 15 2026, 09:54:37

import sys

from choice_latent_variables import generate_utility_functions
from number_of_draws import NUMBER_OF_DRAWS
from one_latent_variable_spec import car_centric_attitude
from optima import (
    Choice,
    read_data,
)

import biogeme.biogeme_logging as blog
from biogeme.biogeme import BIOGEME
from biogeme.expressions import (
    Draws,
    MonteCarlo,
    MultipleSum,
    Variable,
    exp,
    log,
)
from biogeme.models import logit
from biogeme.results_processing import (
    EstimationResults,
    get_latex_estimated_parameters,
    get_latex_general_statistics,
    get_pandas_estimated_parameters,
)

logger = blog.get_screen_logger(level=blog.INFO)

Load data

database = read_data()

Read the results of the previously estimated MIMIC model.

FILE_WITH_ESTIMATION_RESULTS = 'saved_results/plot_h02_lv_mimic_gauss.yaml'
try:
    estimation_results = EstimationResults.from_yaml_file(
        filename=FILE_WITH_ESTIMATION_RESULTS
    )
except FileNotFoundError:
    print(
        f'File {FILE_WITH_ESTIMATION_RESULTS} does not exist. Sequential estimation cannot be performed.'
    )
    sys.exit()
estimated_parameters: dict[str, float] = estimation_results.get_beta_values()

Reconstruct the latent-variable expression using the parameter estimates obtained from the MIMIC model. The sequential specification keeps both the deterministic component and the random structural disturbance.

list_of_explanatory_variables = (
    car_centric_attitude.structural_equation.explanatory_variables
)

# The sigma parameter was estimated on the log scale in the MIMIC model.
# We therefore exponentiate it before scaling the structural disturbance.
terms = [
    estimated_parameters[f'struct_car_centric_attitude_{var}'] * Variable(var)
    for var in list_of_explanatory_variables
]

structural_sigma = exp(estimated_parameters['struct_car_centric_attitude_sigma_log'])
structural_error = structural_sigma * Draws(
    'car_centric_attitude_draw', 'NORMAL_MLHS_ANTI'
)

car_centric_attitude = MultipleSum(terms) + structural_error
latent_expressions = {'car_centric_attitude': car_centric_attitude}

Utility functions including the latent-variable effect.

v = generate_utility_functions(latent_expressions=latent_expressions)

Choice model estimated conditionally on the latent-variable specification.

conditional_likelihood = logit(v, None, Choice)

loglikelihood = log(MonteCarlo(conditional_likelihood))

biogeme = BIOGEME(
    database,
    loglikelihood,
    number_of_draws=NUMBER_OF_DRAWS,
    numerically_safe=True,
    calculating_second_derivatives='never',
    max_iterations=5_000,
)
biogeme.model_name = 'plot_h03_model_lv_gauss_seq'

yaml_file_name = f'saved_results/{biogeme.model_name}.yaml'
results = biogeme.estimate_or_load(yaml_file_name=yaml_file_name)

print(results.short_summary())
print(get_pandas_estimated_parameters(estimation_results=results))

general_statistics = get_latex_general_statistics(estimation_results=results)
print(general_statistics)

estimated_parameters = get_latex_estimated_parameters(estimation_results=results)
for group_name, latex_table in estimated_parameters.items():
    print(group_name if group_name else 'Estimated parameters')
    print(latex_table)
Biogeme parameters read from biogeme.toml.
Estimation results are read from saved_results/plot_h03_model_lv_gauss_seq.yaml. No estimation is performed.
Results for model plot_h03_model_lv_gauss_seq
Nbr of parameters:              8
Sample size:                    889
Excluded data:                  0
Final log likelihood:           -507.2042
Akaike Information Criterion:   1030.408
Bayesian Information Criterion: 1068.729

{'Estimated parameters':                                    Name      Value  ...  BHHH t-stat.  BHHH p-value
0                choice_scale_parameter   0.078269  ...      9.136508  0.000000e+00
1                         choice_asc_pt  -8.822703  ...     -2.417221  1.563951e-02
2                   choice_beta_time_pt -12.334780  ...     -5.148918  2.619935e-07
3                        choice_asc_car  -2.885559  ...     -0.951099  3.415542e-01
4                  choice_beta_time_car -26.299528  ...     -6.514191  7.308221e-11
5  choice_beta_car_centric_attitude_car   4.356381  ...      1.300691  1.933644e-01
6                 choice_beta_dist_work  -2.616226  ...     -7.637980  2.198242e-14
7       choice_beta_dist_other_purposes  -4.142966  ...     -6.887737  5.668577e-12

[8 rows x 5 columns]}

%% General statistics
\section{General statistics}
\begin{tabular}{ll}
Number of estimated parameters & 8 \\
Sample size & 889 \\
Excluded observations & 0 \\
Init log likelihood & -5607.637 \\
Final log likelihood & -507.2042 \\
Likelihood ratio test for the init. model & 10200.87 \\
Rho-square for the init. model & 0.91 \\
Rho-square-bar for the init. model & 0.908 \\
Akaike Information Criterion & 1030.408 \\
Bayesian Information Criterion & 1068.729 \\
Final gradient norm & 7.8853E-03 \\
Number of draws & 50000 \\
Draws generation time & 0:00:13.375733 \\
Types of draws & car\_centric\_attitude\_draw: NORMAL\_MLHS\_ANTI \\
Bootstrapping time & None \\
Algorithm & \verb$BFGS with trust region for simple bound constraints$ \\
Cause of termination & \verb$Relative gradient = 2.6e-06 <= 6.1e-06$ \\
Number of function evaluations & \verb$107$ \\
Number of gradient evaluations & \verb$65$ \\
Number of hessian evaluations & \verb$0$ \\
Number of iterations & \verb$42$ \\
Optimization time & \verb$0:00:46.518227$ \\
Proportion of Hessian calculation & \verb$0/32 = 0.0%$ \\
Relative gradient & \verb$2.629e-06$ \\
\end{tabular}

Estimated parameters

\begin{tabular}{rlr@{.}lr@{.}lr@{.}lr@{.}l}
          &              &   \multicolumn{2}{l}{}         & \multicolumn{2}{l}{BHHH}  &  \multicolumn{4}{l}{}  \\
Parameter &              &   \multicolumn{2}{l}{Coeff.}   & \multicolumn{2}{l}{Asympt.}       & \multicolumn{4}{l}{}   \\
number    &  Description &   \multicolumn{2}{l}{estimate} & \multicolumn{2}{l}{std. error}    & \multicolumn{2}{l}{$t$-stat}  &  \multicolumn{2}{l}{$p$-value} \\
\hline
0 & choice\_scale\_parameter & 0&0783 & 0&00857 & 9&14 & 0&00 \\
1 & choice\_asc\_pt & -8&82 & 3&65 & -2&42 & 0&0156 \\
2 & choice\_beta\_time\_pt & -12&3 & 2&40 & -5&15 & 2&62e-07 \\
3 & choice\_asc\_car & -2&89 & 3&03 & -0&951 & 0&342 \\
4 & choice\_beta\_time\_car & -26&3 & 4&04 & -6&51 & 7&31e-11 \\
5 & choice\_beta\_car\_centric\_attitude\_car & 4&36 & 3&35 & 1&30 & 0&193 \\
6 & choice\_beta\_dist\_work & -2&62 & 0&343 & -7&64 & 2&20e-14 \\
7 & choice\_beta\_dist\_other\_purposes & -4&14 & 0&601 & -6&89 & 5&67e-12 \\

\end{tabular}

Total running time of the script: (0 minutes 1.262 seconds)

Gallery generated by Sphinx-Gallery