Estimation and simulation of a nested logit model

We estimate a nested logit model, and we perform simulation using the estimated model.

Michel Bierlaire, EPFL Sat Jun 28 2025, 16:08:07

from IPython.core.display_functions import display

import biogeme.biogeme_logging as blog
from biogeme.biogeme import BIOGEME
from biogeme.data.optima import read_data
from biogeme.jax_calculator import get_value_c
from biogeme.models import lognested
from biogeme.results_processing import get_pandas_estimated_parameters
from scenarios import scenario

logger = blog.get_screen_logger(level=blog.INFO)
logger.info('Example plot_b02estimation')
Example plot_b02estimation

Obtain the specification for the default scenario. The definition of the scenarios is available in Specification of a nested logit model.

V, nests, choice, _ = scenario()

The choice model is a nested logit, with availability conditions For estimation, we need the log of the probability.

log_probability = lognested(util=V, availability=None, nests=nests, choice=choice)

Get the database

database = read_data()

Create the Biogeme object for estimation.

the_biogeme = BIOGEME(database, log_probability)
the_biogeme.model_name = 'b02estimation'
Default values of the Biogeme parameters are used.
File biogeme.toml has been created

Estimate the parameters. Perform bootstrapping.

results = the_biogeme.estimate(run_bootstrap=True)
*** Initial values of the parameters are obtained from the file __b02estimation.iter
Cannot read file __b02estimation.iter. Statement is ignored.
Starting values for the algorithm: {}
As the model is rather complex, we cancel the calculation of second derivatives. If you want to control the parameters, change the algorithm from "automatic" to "simple_bounds" in the TOML file.
Optimization algorithm: hybrid Newton/BFGS with simple bounds [simple_bounds]
** Optimization: BFGS with trust region for simple bounds
Iter. beta_time_fullt beta_time_other       beta_cost       mu_no_car          asc_sm  beta_dist_male beta_dist_femal beta_dist_unrep         asc_car     Function    Relgrad   Radius      Rho
    0               0               0               0               1               0               0               0               0               0      2.1e+03        1.3      0.5    0.067    -
    1             0.5             0.5            -0.5             1.5            -0.5            -0.5            -0.5            -0.5             0.5      1.4e+03      0.067      0.5     0.21    +
    2            0.32            0.39              -1             1.3           -0.22           -0.38            -0.5           -0.49            0.78      1.3e+03      0.031      0.5     0.39    +
    3            0.32            0.39              -1             1.3           -0.22           -0.38            -0.5           -0.49            0.78      1.3e+03      0.031     0.25    0.072    -
    4            0.23            0.42           -0.85             1.3           -0.14           -0.54           -0.63            -0.5            0.53      1.3e+03      0.013     0.25      0.8    +
    5          -0.021            0.34           -0.86             1.4           0.018           -0.54           -0.75           -0.53            0.72      1.3e+03      0.029     0.25     0.44    +
    6           -0.27            0.31           -0.75             1.3            0.26           -0.53           -0.78           -0.58            0.49      1.3e+03      0.027     0.25      0.3    +
    7           -0.36            0.28           -0.72             1.5            0.16           -0.78           -0.82           -0.61            0.51      1.3e+03      0.019     0.25     0.21    +
    8           -0.36            0.28           -0.72             1.5            0.16           -0.78           -0.82           -0.61            0.51      1.3e+03      0.019     0.12     -0.5    -
    9           -0.46            0.21           -0.84             1.4            0.24           -0.66            -0.8           -0.62            0.57      1.3e+03      0.011     0.12     0.57    +
   10           -0.51            0.21           -0.74             1.4            0.21           -0.67           -0.84           -0.64            0.45      1.3e+03      0.012     0.12     0.39    +
   11           -0.63            0.13           -0.76             1.5            0.17           -0.67           -0.86           -0.67            0.53      1.3e+03       0.01     0.12     0.56    +
   12           -0.63            0.13           -0.76             1.5            0.17           -0.67           -0.86           -0.67            0.53      1.3e+03       0.01    0.062   -0.036    -
   13           -0.63            0.13           -0.76             1.5            0.17           -0.67           -0.86           -0.67            0.53      1.3e+03       0.01    0.031    -0.32    -
   14           -0.67             0.1            -0.8             1.4            0.21           -0.64           -0.83            -0.7            0.49      1.3e+03     0.0046    0.031     0.58    +
   15            -0.7           0.085           -0.76             1.5            0.17           -0.67           -0.84            -0.7            0.46      1.3e+03     0.0042    0.031     0.77    +
   16           -0.73           0.054           -0.77             1.4            0.19           -0.66           -0.82            -0.7            0.45      1.3e+03     0.0038     0.31     0.92   ++
   17              -1           -0.14           -0.72             1.5            0.14           -0.71           -0.82            -0.7            0.41      1.3e+03     0.0077     0.31      0.7    +
   18            -1.4           -0.34           -0.76             1.5            0.11           -0.61           -0.87           -0.69            0.29      1.3e+03      0.013     0.31     0.27    +
   19            -1.4           -0.34           -0.76             1.5            0.11           -0.61           -0.87           -0.69            0.29      1.3e+03      0.013     0.16     -4.8    -
   20            -1.4           -0.34           -0.76             1.5            0.11           -0.61           -0.87           -0.69            0.29      1.3e+03      0.013    0.078     -0.8    -
   21            -1.4           -0.35           -0.74             1.5           0.088           -0.69           -0.84           -0.69            0.31      1.3e+03     0.0029    0.078     0.76    +
   22            -1.4           -0.35           -0.74             1.5           0.088           -0.69           -0.84           -0.69            0.31      1.3e+03     0.0029    0.039    -0.79    -
   23            -1.4           -0.35           -0.74             1.5           0.088           -0.69           -0.84           -0.69            0.31      1.3e+03     0.0029     0.02   0.0073    -
   24            -1.4           -0.37           -0.74             1.5            0.11           -0.67           -0.82           -0.67             0.3      1.3e+03     0.0026     0.02     0.41    +
   25            -1.4           -0.38           -0.72             1.5           0.094           -0.68           -0.82           -0.68            0.31      1.3e+03     0.0011     0.02     0.69    +
   26            -1.4            -0.4           -0.73             1.5           0.092           -0.67           -0.82           -0.69             0.3      1.3e+03    0.00097     0.02     0.89    +
   27            -1.4           -0.42           -0.73             1.5           0.083           -0.68           -0.82           -0.69             0.3      1.3e+03     0.0012     0.02     0.75    +
   28            -1.4           -0.44           -0.73             1.5           0.086           -0.68           -0.82           -0.69            0.29      1.3e+03    0.00073      0.2     0.92   ++
   29            -1.4           -0.44           -0.73             1.5           0.086           -0.68           -0.82           -0.69            0.29      1.3e+03    0.00073    0.098     -4.8    -
   30            -1.4           -0.44           -0.73             1.5           0.086           -0.68           -0.82           -0.69            0.29      1.3e+03    0.00073    0.049       -2    -
   31            -1.4           -0.44           -0.73             1.5           0.086           -0.68           -0.82           -0.69            0.29      1.3e+03    0.00073    0.024    -0.38    -
   32            -1.5           -0.46           -0.71             1.5           0.077           -0.67           -0.83            -0.7            0.28      1.3e+03     0.0014    0.024     0.37    +
   33            -1.5           -0.48           -0.73             1.5           0.074           -0.69           -0.82            -0.7            0.28      1.3e+03     0.0019    0.024     0.57    +
   34            -1.5           -0.48           -0.73             1.5           0.074           -0.69           -0.82            -0.7            0.28      1.3e+03     0.0019    0.012     -1.1    -
   35            -1.5           -0.49           -0.72             1.5           0.087           -0.68           -0.83            -0.7            0.28      1.3e+03     0.0014    0.012     0.54    +
   36            -1.5           -0.49           -0.72             1.5           0.087           -0.68           -0.83            -0.7            0.28      1.3e+03     0.0014   0.0061    0.066    -
   37            -1.5           -0.49           -0.72             1.5           0.081           -0.69           -0.83            -0.7            0.28      1.3e+03    0.00051   0.0061     0.88    +
   38            -1.5           -0.49           -0.72             1.5           0.078           -0.68           -0.83            -0.7            0.28      1.3e+03    0.00052   0.0061     0.83    +
   39            -1.5            -0.5           -0.72             1.5           0.074           -0.68           -0.83            -0.7            0.28      1.3e+03    0.00043   0.0061     0.85    +
   40            -1.5            -0.5           -0.72             1.5           0.074           -0.68           -0.83            -0.7            0.27      1.3e+03    0.00044    0.061     0.94   ++
   41            -1.6           -0.55           -0.72             1.5           0.064           -0.68           -0.83            -0.7            0.26      1.3e+03    0.00039     0.61     0.94   ++
   42            -1.6           -0.55           -0.72             1.5           0.064           -0.68           -0.83            -0.7            0.26      1.3e+03    0.00039    0.064      -31    -
   43            -1.6           -0.55           -0.72             1.5           0.064           -0.68           -0.83            -0.7            0.26      1.3e+03    0.00039    0.032      -21    -
   44            -1.6           -0.55           -0.72             1.5           0.064           -0.68           -0.83            -0.7            0.26      1.3e+03    0.00039    0.016     -9.4    -
   45            -1.6           -0.55           -0.72             1.5           0.064           -0.68           -0.83            -0.7            0.26      1.3e+03    0.00039    0.008     -5.4    -
   46            -1.6           -0.55           -0.72             1.5           0.064           -0.68           -0.83            -0.7            0.26      1.3e+03    0.00039    0.004     -2.9    -
   47            -1.6           -0.55           -0.72             1.5           0.064           -0.68           -0.83            -0.7            0.26      1.3e+03    0.00039    0.002     -1.2    -
   48            -1.6           -0.55           -0.72             1.5           0.062           -0.69           -0.83            -0.7            0.26      1.3e+03    0.00031    0.002     0.12    +
   49            -1.6           -0.55           -0.72             1.5           0.064           -0.69           -0.83            -0.7            0.26      1.3e+03    7.9e-05    0.002     0.68    +
   50            -1.6           -0.55           -0.72             1.5           0.064           -0.69           -0.83            -0.7            0.26      1.3e+03    5.6e-05    0.002     0.71    +
   51            -1.6           -0.55           -0.72             1.5           0.064           -0.69           -0.83            -0.7            0.26      1.3e+03    5.6e-05    0.001     -1.5    -
   52            -1.6           -0.55           -0.72             1.5           0.064           -0.69           -0.83            -0.7            0.26      1.3e+03    5.6e-05   0.0005     -0.2    -
   53            -1.6           -0.55           -0.72             1.5           0.064           -0.69           -0.83            -0.7            0.26      1.3e+03    7.3e-05   0.0005     0.35    +
   54            -1.6           -0.55           -0.72             1.5           0.064           -0.69           -0.83            -0.7            0.26      1.3e+03    3.9e-05   0.0005     0.76    +
   55            -1.6           -0.55           -0.72             1.5           0.064           -0.69           -0.83            -0.7            0.26      1.3e+03    3.8e-05   0.0005     0.86    +
   56            -1.6           -0.55           -0.72             1.5           0.064           -0.69           -0.83            -0.7            0.26      1.3e+03    3.1e-05    0.005     0.96   ++
   57            -1.6           -0.55           -0.72             1.5           0.064           -0.69           -0.83            -0.7            0.26      1.3e+03    3.1e-05   0.0025   -0.051    -
   58            -1.6           -0.55           -0.72             1.5           0.063           -0.69           -0.83            -0.7            0.26      1.3e+03    6.2e-05   0.0025     0.53    +
   59            -1.6           -0.55           -0.72             1.5           0.063           -0.69           -0.83            -0.7            0.26      1.3e+03    6.2e-05   0.0012     -1.1    -
   60            -1.6           -0.55           -0.72             1.5           0.063           -0.69           -0.83            -0.7            0.26      1.3e+03    5.7e-05   0.0012     0.21    +
   61            -1.6           -0.55           -0.72             1.5           0.063           -0.69           -0.83            -0.7            0.26      1.3e+03    5.7e-05  0.00062    -0.51    -
   62            -1.6           -0.55           -0.72             1.5           0.063           -0.69           -0.83            -0.7            0.26      1.3e+03    5.7e-05  0.00031    -0.11    -
   63            -1.6           -0.55           -0.72             1.5           0.063           -0.69           -0.83            -0.7            0.26      1.3e+03    5.6e-05  0.00031     0.48    +
   64            -1.6           -0.55           -0.72             1.5           0.063           -0.69           -0.83            -0.7            0.26      1.3e+03    2.3e-05  0.00031     0.59    +
   65            -1.6           -0.55           -0.72             1.5           0.063           -0.69           -0.83            -0.7            0.26      1.3e+03    7.6e-06  0.00031     0.87    +
   66            -1.6           -0.55           -0.72             1.5           0.063           -0.69           -0.83            -0.7            0.26      1.3e+03    1.1e-05  0.00031      0.7    +
   67            -1.6           -0.55           -0.72             1.5           0.063           -0.69           -0.83            -0.7            0.26      1.3e+03    2.8e-06  0.00031     0.94    +
Optimization algorithm has converged.
Relative gradient: 2.7803605259079523e-06
Cause of termination: Relative gradient = 2.8e-06 <= 6.1e-06
Number of function evaluations: 153
Number of gradient evaluations: 85
Number of hessian evaluations: 0
Algorithm: BFGS with trust region for simple bound constraints
Number of iterations: 68
Proportion of Hessian calculation: 0/42 = 0.0%
Optimization time: 0:00:00.687495
Calculate second derivatives and BHHH
Re-estimate the model 100 times for bootstrapping

Bootstraps:   0%|          | 0/100 [00:00<?, ?it/s]

  0%|          | 0/100 [00:00<?, ?it/s]

  1%|          | 1/100 [00:04<06:57,  4.22s/it]

  3%|▎         | 3/100 [00:06<02:56,  1.82s/it]

  5%|▌         | 5/100 [00:08<02:18,  1.46s/it]

  7%|▋         | 7/100 [00:10<01:54,  1.23s/it]

  9%|▉         | 9/100 [00:12<01:42,  1.13s/it]/Users/bierlair/python_envs/venv313/lib/python3.13/site-packages/joblib/externals/loky/process_executor.py:782: UserWarning: A worker stopped while some jobs were given to the executor. This can be caused by a too short worker timeout or by a memory leak.
  warnings.warn(


 11%|█         | 11/100 [00:16<02:10,  1.47s/it]

 12%|█▏        | 12/100 [00:16<01:52,  1.28s/it]

 13%|█▎        | 13/100 [00:18<01:52,  1.29s/it]

 14%|█▍        | 14/100 [00:18<01:38,  1.15s/it]

 15%|█▌        | 15/100 [00:20<01:39,  1.17s/it]

 16%|█▌        | 16/100 [00:20<01:28,  1.06s/it]

 17%|█▋        | 17/100 [00:21<01:26,  1.04s/it]

 18%|█▊        | 18/100 [00:22<01:18,  1.04it/s]

 19%|█▉        | 19/100 [00:23<01:21,  1.01s/it]

 20%|██        | 20/100 [00:24<01:14,  1.08it/s]

 21%|██        | 21/100 [00:27<02:07,  1.61s/it]

 22%|██▏       | 22/100 [00:28<01:46,  1.36s/it]

 23%|██▎       | 23/100 [00:29<01:38,  1.27s/it]

 24%|██▍       | 24/100 [00:30<01:24,  1.11s/it]

 25%|██▌       | 25/100 [00:31<01:23,  1.12s/it]

 26%|██▌       | 26/100 [00:32<01:15,  1.03s/it]

 27%|██▋       | 27/100 [00:33<01:13,  1.01s/it]

 28%|██▊       | 28/100 [00:34<01:10,  1.03it/s]

 29%|██▉       | 29/100 [00:35<01:08,  1.04it/s]

 30%|███       | 30/100 [00:36<01:07,  1.03it/s]

 31%|███       | 31/100 [00:39<01:52,  1.62s/it]

 32%|███▏      | 32/100 [00:40<01:37,  1.44s/it]

 33%|███▎      | 33/100 [00:41<01:28,  1.31s/it]

 34%|███▍      | 34/100 [00:42<01:15,  1.14s/it]

 35%|███▌      | 35/100 [00:43<01:13,  1.13s/it]

 36%|███▌      | 36/100 [00:43<01:04,  1.01s/it]

 37%|███▋      | 37/100 [00:44<01:03,  1.01s/it]

 38%|███▊      | 38/100 [00:45<00:58,  1.06it/s]

 39%|███▉      | 39/100 [00:46<00:58,  1.05it/s]

 40%|████      | 40/100 [00:47<00:56,  1.05it/s]

 41%|████      | 41/100 [00:50<01:34,  1.60s/it]

 42%|████▏     | 42/100 [00:51<01:23,  1.44s/it]

 43%|████▎     | 43/100 [00:52<01:14,  1.31s/it]

 44%|████▍     | 44/100 [00:53<01:06,  1.18s/it]

 45%|████▌     | 45/100 [00:54<01:00,  1.11s/it]

 46%|████▌     | 46/100 [00:55<00:58,  1.08s/it]

 47%|████▋     | 47/100 [00:56<00:50,  1.05it/s]

 48%|████▊     | 48/100 [00:57<00:52,  1.00s/it]

 49%|████▉     | 49/100 [00:58<00:46,  1.09it/s]

 50%|█████     | 50/100 [00:59<00:47,  1.05it/s]

 51%|█████     | 51/100 [01:00<00:57,  1.18s/it]

 52%|█████▏    | 52/100 [01:02<00:58,  1.23s/it]

 53%|█████▎    | 53/100 [01:03<01:05,  1.39s/it]

 54%|█████▍    | 54/100 [01:04<00:59,  1.30s/it]

 55%|█████▌    | 55/100 [01:05<00:51,  1.13s/it]

 56%|█████▌    | 56/100 [01:06<00:48,  1.10s/it]

 57%|█████▋    | 57/100 [01:07<00:43,  1.01s/it]

 58%|█████▊    | 58/100 [01:08<00:42,  1.01s/it]

 59%|█████▉    | 59/100 [01:09<00:40,  1.02it/s]

 60%|██████    | 60/100 [01:10<00:36,  1.08it/s]

 61%|██████    | 61/100 [01:11<00:38,  1.02it/s]

 62%|██████▏   | 62/100 [01:12<00:33,  1.12it/s]

 63%|██████▎   | 63/100 [01:15<01:01,  1.67s/it]

 64%|██████▍   | 64/100 [01:16<00:47,  1.33s/it]

 65%|██████▌   | 65/100 [01:17<00:45,  1.29s/it]

 66%|██████▌   | 66/100 [01:17<00:37,  1.11s/it]

 67%|██████▋   | 67/100 [01:19<00:38,  1.16s/it]

 68%|██████▊   | 68/100 [01:19<00:31,  1.01it/s]

 69%|██████▉   | 69/100 [01:20<00:31,  1.01s/it]

 70%|███████   | 70/100 [01:21<00:26,  1.12it/s]

 71%|███████   | 71/100 [01:22<00:29,  1.02s/it]

 72%|███████▏  | 72/100 [01:23<00:24,  1.12it/s]

 73%|███████▎  | 73/100 [01:26<00:43,  1.63s/it]

 74%|███████▍  | 74/100 [01:27<00:34,  1.32s/it]

 75%|███████▌  | 75/100 [01:28<00:31,  1.26s/it]

 76%|███████▌  | 76/100 [01:29<00:26,  1.12s/it]

 77%|███████▋  | 77/100 [01:30<00:24,  1.07s/it]

 78%|███████▊  | 78/100 [01:31<00:22,  1.01s/it]

 79%|███████▉  | 79/100 [01:31<00:20,  1.04it/s]

 80%|████████  | 80/100 [01:32<00:18,  1.07it/s]

 81%|████████  | 81/100 [01:33<00:17,  1.06it/s]

 82%|████████▏ | 82/100 [01:34<00:16,  1.10it/s]

 83%|████████▎ | 83/100 [01:37<00:27,  1.60s/it]

 84%|████████▍ | 84/100 [01:38<00:21,  1.36s/it]

 85%|████████▌ | 85/100 [01:39<00:19,  1.27s/it]

 86%|████████▌ | 86/100 [01:40<00:15,  1.09s/it]

 87%|████████▋ | 87/100 [01:41<00:14,  1.11s/it]

 88%|████████▊ | 88/100 [01:42<00:11,  1.02it/s]

 89%|████████▉ | 89/100 [01:43<00:11,  1.01s/it]

 90%|█████████ | 90/100 [01:44<00:09,  1.08it/s]

 91%|█████████ | 91/100 [01:45<00:08,  1.03it/s]

 92%|█████████▏| 92/100 [01:45<00:07,  1.10it/s]

 93%|█████████▎| 93/100 [01:49<00:11,  1.58s/it]

 94%|█████████▍| 94/100 [01:49<00:08,  1.34s/it]

 95%|█████████▌| 95/100 [01:50<00:06,  1.24s/it]

 96%|█████████▌| 96/100 [01:51<00:04,  1.09s/it]

 97%|█████████▋| 97/100 [01:52<00:03,  1.09s/it]

 98%|█████████▊| 98/100 [01:53<00:01,  1.03it/s]

 99%|█████████▉| 99/100 [01:54<00:00,  1.03it/s]

100%|██████████| 100/100 [01:54<00:00,  1.16it/s]
100%|██████████| 100/100 [01:55<00:00,  1.16s/it]
File b02estimation.html has been generated.
File b02estimation.yaml has been generated.

Get the results in a pandas table

pandas_results = get_pandas_estimated_parameters(estimation_results=results)
display(pandas_results)
                   Name     Value  ...  Bootstrap t-stat.  Bootstrap p-value
0    beta_time_fulltime -1.596467  ...          -4.896418       9.759926e-07
1       beta_time_other -0.552686  ...          -1.673272       9.427379e-02
2             beta_cost -0.719072  ...          -5.182724       2.186688e-07
3             mu_no_car  1.523716  ...           5.679133       1.353795e-08
4                asc_sm  0.063000  ...           0.276633       7.820616e-01
5        beta_dist_male -0.687677  ...          -3.446560       5.677721e-04
6      beta_dist_female -0.832153  ...          -4.544143       5.515927e-06
7  beta_dist_unreported -0.704737  ...          -2.221318       2.632942e-02
8               asc_car  0.258607  ...           2.390527       1.682422e-02

[9 rows x 5 columns]

Simulation

simulated_choices = get_value_c(
    expression=log_probability,
    betas=results.get_beta_values(),
    database=database,
    numerically_safe=False,
    use_jit=True,
)
display(simulated_choices)
Bootstraps:   0%|          | 0/100 [01:56<?, ?it/s]
[-0.65553681 -1.42271161 -0.13348367 ... -0.37368096 -0.29814134
 -0.27030377]
loglikelihood = get_value_c(
    expression=log_probability,
    betas=results.get_beta_values(),
    database=database,
    aggregation=True,
    numerically_safe=False,
    use_jit=True,
)
print(f'Final log likelihood:     {results.final_log_likelihood}')
print(f'Simulated log likelihood: {loglikelihood}')
Final log likelihood:     -1295.1202531057634
Simulated log likelihood: -1295.1202531057634

Total running time of the script: (2 minutes 0.270 seconds)

Gallery generated by Sphinx-Gallery