Note
Go to the end to download the full example code.
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)