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.0003 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.72 +
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.21 -
53 -1.6 -0.55 -0.72 1.5 0.064 -0.69 -0.83 -0.7 0.26 1.3e+03 7.4e-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.046 -
58 -1.6 -0.55 -0.72 1.5 0.063 -0.69 -0.83 -0.7 0.26 1.3e+03 6.1e-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.1e-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.6e-05 0.0012 0.22 +
61 -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.00062 -0.52 -
62 -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.14 -
63 -1.6 -0.55 -0.72 1.5 0.063 -0.69 -0.83 -0.7 0.26 1.3e+03 5e-05 0.00031 0.49 +
64 -1.6 -0.55 -0.72 1.5 0.063 -0.69 -0.83 -0.7 0.26 1.3e+03 2.2e-05 0.00031 0.63 +
65 -1.6 -0.55 -0.72 1.5 0.063 -0.69 -0.83 -0.7 0.26 1.3e+03 7.8e-06 0.00031 0.84 +
66 -1.6 -0.55 -0.72 1.5 0.063 -0.69 -0.83 -0.7 0.26 1.3e+03 7.2e-06 0.0031 0.91 ++
67 -1.6 -0.55 -0.72 1.5 0.063 -0.69 -0.83 -0.7 0.26 1.3e+03 7.2e-06 0.00065 -2.3 -
68 -1.6 -0.55 -0.72 1.5 0.063 -0.69 -0.83 -0.7 0.26 1.3e+03 3.3e-06 0.00065 0.86 -
Optimization algorithm has converged.
Relative gradient: 3.341601078462868e-06
Cause of termination: Relative gradient = 3.3e-06 <= 6.1e-06
Number of function evaluations: 154
Number of gradient evaluations: 85
Number of hessian evaluations: 0
Algorithm: BFGS with trust region for simple bound constraints
Number of iterations: 69
Proportion of Hessian calculation: 0/42 = 0.0%
Optimization time: 0:00:00.764584
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:46, 4.11s/it]
2%|▏ | 2/100 [00:04<02:56, 1.80s/it]
3%|▎ | 3/100 [00:06<02:56, 1.82s/it]
4%|▍ | 4/100 [00:06<01:54, 1.19s/it]
5%|▌ | 5/100 [00:08<02:11, 1.39s/it]
6%|▌ | 6/100 [00:08<01:34, 1.01s/it]
7%|▋ | 7/100 [00:10<01:54, 1.23s/it]
8%|▊ | 8/100 [00:10<01:25, 1.08it/s]
9%|▉ | 9/100 [00:12<01:45, 1.16s/it]
10%|█ | 10/100 [00:12<01:23, 1.08it/s]
11%|█ | 11/100 [00:14<01:40, 1.13s/it]
12%|█▏ | 12/100 [00:14<01:22, 1.07it/s]
13%|█▎ | 13/100 [00:15<01:33, 1.08s/it]
14%|█▍ | 14/100 [00:16<01:20, 1.07it/s]
15%|█▌ | 15/100 [00:17<01:29, 1.05s/it]
16%|█▌ | 16/100 [00:18<01:19, 1.06it/s]
17%|█▋ | 17/100 [00:20<01:32, 1.12s/it]
18%|█▊ | 18/100 [00:20<01:18, 1.04it/s]
19%|█▉ | 19/100 [00:21<01:26, 1.07s/it]
20%|██ | 20/100 [00:22<01:15, 1.06it/s]
21%|██ | 21/100 [00:24<01:28, 1.12s/it]/Users/bierlair/MyFiles/github/biogeme/.venv/lib/python3.14/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(
22%|██▏ | 22/100 [00:24<01:20, 1.03s/it]
23%|██▎ | 23/100 [00:26<01:40, 1.30s/it]
24%|██▍ | 24/100 [00:28<01:44, 1.37s/it]
25%|██▌ | 25/100 [00:29<01:31, 1.22s/it]
26%|██▌ | 26/100 [00:30<01:30, 1.22s/it]
27%|██▋ | 27/100 [00:31<01:18, 1.08s/it]
28%|██▊ | 28/100 [00:32<01:22, 1.14s/it]
29%|██▉ | 29/100 [00:34<01:36, 1.36s/it]
30%|███ | 30/100 [00:35<01:33, 1.34s/it]
31%|███ | 31/100 [00:36<01:20, 1.16s/it]
32%|███▏ | 32/100 [00:37<01:21, 1.20s/it]
33%|███▎ | 33/100 [00:38<01:11, 1.07s/it]
34%|███▍ | 34/100 [00:39<01:14, 1.13s/it]
35%|███▌ | 35/100 [00:40<01:05, 1.01s/it]
36%|███▌ | 36/100 [00:41<01:09, 1.09s/it]
37%|███▋ | 37/100 [00:42<01:02, 1.00it/s]
38%|███▊ | 38/100 [00:43<01:06, 1.08s/it]
39%|███▉ | 39/100 [00:44<01:01, 1.01s/it]
40%|████ | 40/100 [00:45<01:04, 1.07s/it]
41%|████ | 41/100 [00:46<00:57, 1.02it/s]
42%|████▏ | 42/100 [00:47<01:00, 1.04s/it]
43%|████▎ | 43/100 [00:49<01:01, 1.09s/it]
44%|████▍ | 44/100 [00:49<00:56, 1.01s/it]
45%|████▌ | 45/100 [00:51<00:58, 1.06s/it]
46%|████▌ | 46/100 [00:52<00:55, 1.03s/it]
47%|████▋ | 47/100 [00:53<00:59, 1.13s/it]
48%|████▊ | 48/100 [00:53<00:50, 1.03it/s]
49%|████▉ | 49/100 [00:55<01:01, 1.20s/it]
50%|█████ | 50/100 [00:56<00:53, 1.07s/it]
51%|█████ | 51/100 [00:58<01:03, 1.29s/it]
52%|█████▏ | 52/100 [01:00<01:10, 1.46s/it]
53%|█████▎ | 53/100 [01:00<00:55, 1.17s/it]
54%|█████▍ | 54/100 [01:02<00:59, 1.29s/it]
55%|█████▌ | 55/100 [01:04<01:07, 1.50s/it]
56%|█████▌ | 56/100 [01:05<00:58, 1.33s/it]
57%|█████▋ | 57/100 [01:06<00:54, 1.26s/it]
58%|█████▊ | 58/100 [01:07<00:51, 1.22s/it]
59%|█████▉ | 59/100 [01:08<00:45, 1.10s/it]
60%|██████ | 60/100 [01:09<00:46, 1.15s/it]
61%|██████ | 61/100 [01:10<00:40, 1.04s/it]
62%|██████▏ | 62/100 [01:11<00:41, 1.08s/it]
63%|██████▎ | 63/100 [01:12<00:37, 1.01s/it]
64%|██████▍ | 64/100 [01:13<00:39, 1.08s/it]
65%|██████▌ | 65/100 [01:14<00:34, 1.01it/s]
66%|██████▌ | 66/100 [01:15<00:37, 1.09s/it]
67%|██████▋ | 67/100 [01:16<00:32, 1.01it/s]
68%|██████▊ | 68/100 [01:17<00:34, 1.08s/it]
69%|██████▉ | 69/100 [01:18<00:29, 1.04it/s]
70%|███████ | 70/100 [01:19<00:32, 1.09s/it]
71%|███████ | 71/100 [01:20<00:30, 1.05s/it]
72%|███████▏ | 72/100 [01:22<00:32, 1.14s/it]
73%|███████▎ | 73/100 [01:22<00:26, 1.01it/s]
74%|███████▍ | 74/100 [01:24<00:29, 1.14s/it]
75%|███████▌ | 75/100 [01:25<00:26, 1.05s/it]
76%|███████▌ | 76/100 [01:26<00:26, 1.09s/it]
77%|███████▋ | 77/100 [01:27<00:25, 1.11s/it]
78%|███████▊ | 78/100 [01:28<00:25, 1.15s/it]
79%|███████▉ | 79/100 [01:30<00:31, 1.52s/it]
80%|████████ | 80/100 [01:31<00:26, 1.30s/it]
81%|████████ | 81/100 [01:33<00:29, 1.53s/it]
82%|████████▏ | 82/100 [01:35<00:27, 1.51s/it]
83%|████████▎ | 83/100 [01:35<00:20, 1.22s/it]
84%|████████▍ | 84/100 [01:37<00:21, 1.34s/it]
85%|████████▌ | 85/100 [01:37<00:15, 1.04s/it]
86%|████████▌ | 86/100 [01:39<00:17, 1.24s/it]
87%|████████▋ | 87/100 [01:39<00:12, 1.05it/s]
88%|████████▊ | 88/100 [01:41<00:14, 1.19s/it]
89%|████████▉ | 89/100 [01:41<00:10, 1.09it/s]
90%|█████████ | 90/100 [01:43<00:11, 1.17s/it]
91%|█████████ | 91/100 [01:43<00:08, 1.12it/s]
92%|█████████▏| 92/100 [01:45<00:09, 1.16s/it]
93%|█████████▎| 93/100 [01:45<00:06, 1.13it/s]
94%|█████████▍| 94/100 [01:47<00:06, 1.12s/it]
95%|█████████▌| 95/100 [01:48<00:04, 1.02it/s]
96%|█████████▌| 96/100 [01:49<00:04, 1.11s/it]
97%|█████████▋| 97/100 [01:50<00:02, 1.06it/s]
98%|█████████▊| 98/100 [01:51<00:02, 1.17s/it]
99%|█████████▉| 99/100 [01:52<00:01, 1.00s/it]
100%|██████████| 100/100 [01:53<00:00, 1.07s/it]
100%|██████████| 100/100 [01:54<00:00, 1.15s/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)
{'Estimated parameters': Name Value ... Bootstrap t-stat. Bootstrap p-value
0 beta_time_fulltime -1.596789 ... -5.157114 2.507856e-07
1 beta_time_other -0.552938 ... -1.766499 7.731223e-02
2 beta_cost -0.719076 ... -5.144178 2.686950e-07
3 mu_no_car 1.523719 ... 5.549875 2.858739e-08
4 asc_sm 0.063048 ... 0.307433 7.585141e-01
5 beta_dist_male -0.687757 ... -3.754600 1.736183e-04
6 beta_dist_female -0.832204 ... -3.642548 2.699529e-04
7 beta_dist_unreported -0.704647 ... -3.414966 6.379006e-04
8 asc_car 0.258536 ... 2.543554 1.097311e-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)
[-0.65553951 -1.42282747 -0.13347799 ... -0.37368046 -0.29812532
-0.27028396]
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.1202523802
Simulated log likelihood: -1295.1202523802
Total running time of the script: (2 minutes 0.373 seconds)