Estimation and simulation of a nested logit model

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

author:

Michel Bierlaire, EPFL

date:

Wed Apr 12 21:05:16 2023

from biogeme import models
import biogeme.biogeme as bio
from optima_data import database
from scenarios import scenario

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.

logprob = models.lognested(V, None, nests, Choice)

Create the Biogeme object for estimation.

the_biogeme = bio.BIOGEME(database, logprob)
the_biogeme.modelName = 'b02estimation'

Estimate the parameters. Perform bootstrapping.

the_biogeme.bootstrap_samples = 100
results = the_biogeme.estimate(run_bootstrap=True)
  0%|          | 0/100 [00:00<?, ?it/s]
  1%|          | 1/100 [00:00<00:18,  5.36it/s]
  2%|▏         | 2/100 [00:00<00:28,  3.46it/s]
  3%|▎         | 3/100 [00:00<00:26,  3.63it/s]
  4%|▍         | 4/100 [00:01<00:29,  3.21it/s]
  5%|▌         | 5/100 [00:01<00:28,  3.34it/s]
  6%|▌         | 6/100 [00:01<00:28,  3.34it/s]
  7%|▋         | 7/100 [00:01<00:26,  3.56it/s]
  8%|▊         | 8/100 [00:02<00:28,  3.28it/s]
  9%|▉         | 9/100 [00:02<00:24,  3.75it/s]
 10%|█         | 10/100 [00:02<00:26,  3.42it/s]
 11%|█         | 11/100 [00:03<00:26,  3.41it/s]
 12%|█▏        | 12/100 [00:03<00:26,  3.31it/s]
 13%|█▎        | 13/100 [00:03<00:23,  3.69it/s]
 14%|█▍        | 14/100 [00:03<00:23,  3.65it/s]
 15%|█▌        | 15/100 [00:04<00:23,  3.68it/s]
 16%|█▌        | 16/100 [00:04<00:21,  3.93it/s]
 17%|█▋        | 17/100 [00:04<00:26,  3.15it/s]
 18%|█▊        | 18/100 [00:05<00:24,  3.30it/s]
 19%|█▉        | 19/100 [00:05<00:25,  3.19it/s]
 20%|██        | 20/100 [00:05<00:26,  3.05it/s]
 21%|██        | 21/100 [00:06<00:26,  3.04it/s]
 22%|██▏       | 22/100 [00:06<00:23,  3.26it/s]
 23%|██▎       | 23/100 [00:06<00:25,  3.06it/s]
 24%|██▍       | 24/100 [00:07<00:23,  3.21it/s]
 25%|██▌       | 25/100 [00:07<00:22,  3.34it/s]
 26%|██▌       | 26/100 [00:07<00:21,  3.37it/s]
 27%|██▋       | 27/100 [00:07<00:19,  3.70it/s]
 28%|██▊       | 28/100 [00:08<00:19,  3.66it/s]
 29%|██▉       | 29/100 [00:08<00:21,  3.24it/s]
 30%|███       | 30/100 [00:08<00:22,  3.06it/s]
 31%|███       | 31/100 [00:09<00:25,  2.75it/s]
 32%|███▏      | 32/100 [00:09<00:25,  2.66it/s]
 33%|███▎      | 33/100 [00:10<00:23,  2.82it/s]
 34%|███▍      | 34/100 [00:10<00:28,  2.34it/s]
 35%|███▌      | 35/100 [00:11<00:28,  2.32it/s]
 36%|███▌      | 36/100 [00:11<00:26,  2.43it/s]
 37%|███▋      | 37/100 [00:12<00:29,  2.17it/s]
 38%|███▊      | 38/100 [00:12<00:28,  2.18it/s]
 39%|███▉      | 39/100 [00:13<00:30,  2.02it/s]
 40%|████      | 40/100 [00:13<00:28,  2.09it/s]
 41%|████      | 41/100 [00:13<00:25,  2.27it/s]
 42%|████▏     | 42/100 [00:14<00:22,  2.56it/s]
 43%|████▎     | 43/100 [00:14<00:19,  2.87it/s]
 44%|████▍     | 44/100 [00:14<00:17,  3.15it/s]
 45%|████▌     | 45/100 [00:15<00:17,  3.09it/s]
 46%|████▌     | 46/100 [00:15<00:18,  2.97it/s]
 47%|████▋     | 47/100 [00:15<00:19,  2.69it/s]
 48%|████▊     | 48/100 [00:16<00:21,  2.38it/s]
 49%|████▉     | 49/100 [00:16<00:20,  2.50it/s]
 50%|█████     | 50/100 [00:17<00:18,  2.68it/s]
 51%|█████     | 51/100 [00:17<00:19,  2.57it/s]
 52%|█████▏    | 52/100 [00:17<00:17,  2.80it/s]
 53%|█████▎    | 53/100 [00:18<00:16,  2.91it/s]
 54%|█████▍    | 54/100 [00:18<00:16,  2.81it/s]
 55%|█████▌    | 55/100 [00:18<00:16,  2.70it/s]
 56%|█████▌    | 56/100 [00:19<00:16,  2.68it/s]
 57%|█████▋    | 57/100 [00:19<00:15,  2.72it/s]
 58%|█████▊    | 58/100 [00:20<00:17,  2.43it/s]
 59%|█████▉    | 59/100 [00:20<00:18,  2.23it/s]
 60%|██████    | 60/100 [00:20<00:16,  2.43it/s]
 61%|██████    | 61/100 [00:21<00:14,  2.74it/s]
 62%|██████▏   | 62/100 [00:21<00:14,  2.62it/s]
 63%|██████▎   | 63/100 [00:22<00:14,  2.47it/s]
 64%|██████▍   | 64/100 [00:22<00:14,  2.52it/s]
 65%|██████▌   | 65/100 [00:22<00:13,  2.68it/s]
 66%|██████▌   | 66/100 [00:23<00:14,  2.42it/s]
 67%|██████▋   | 67/100 [00:23<00:14,  2.35it/s]
 68%|██████▊   | 68/100 [00:24<00:14,  2.22it/s]
 69%|██████▉   | 69/100 [00:24<00:13,  2.27it/s]
 70%|███████   | 70/100 [00:25<00:12,  2.40it/s]
 71%|███████   | 71/100 [00:25<00:12,  2.38it/s]
 72%|███████▏  | 72/100 [00:25<00:12,  2.21it/s]
 73%|███████▎  | 73/100 [00:26<00:12,  2.24it/s]
 74%|███████▍  | 74/100 [00:26<00:10,  2.37it/s]
 75%|███████▌  | 75/100 [00:27<00:09,  2.54it/s]
 76%|███████▌  | 76/100 [00:27<00:09,  2.59it/s]
 77%|███████▋  | 77/100 [00:27<00:08,  2.82it/s]
 78%|███████▊  | 78/100 [00:28<00:08,  2.73it/s]
 79%|███████▉  | 79/100 [00:28<00:06,  3.05it/s]
 80%|████████  | 80/100 [00:28<00:06,  2.97it/s]
 81%|████████  | 81/100 [00:29<00:06,  2.96it/s]
 82%|████████▏ | 82/100 [00:29<00:06,  2.86it/s]
 83%|████████▎ | 83/100 [00:29<00:05,  3.03it/s]
 84%|████████▍ | 84/100 [00:30<00:05,  2.94it/s]
 85%|████████▌ | 85/100 [00:30<00:05,  2.90it/s]
 86%|████████▌ | 86/100 [00:30<00:05,  2.66it/s]
 87%|████████▋ | 87/100 [00:31<00:04,  3.05it/s]
 88%|████████▊ | 88/100 [00:31<00:03,  3.10it/s]
 89%|████████▉ | 89/100 [00:31<00:03,  2.85it/s]
 90%|█████████ | 90/100 [00:32<00:03,  2.70it/s]
 91%|█████████ | 91/100 [00:32<00:03,  2.61it/s]
 92%|█████████▏| 92/100 [00:33<00:02,  2.77it/s]
 93%|█████████▎| 93/100 [00:33<00:02,  2.93it/s]
 94%|█████████▍| 94/100 [00:33<00:01,  3.20it/s]
 95%|█████████▌| 95/100 [00:33<00:01,  2.87it/s]
 96%|█████████▌| 96/100 [00:34<00:01,  2.82it/s]
 97%|█████████▋| 97/100 [00:34<00:01,  2.81it/s]
 98%|█████████▊| 98/100 [00:35<00:00,  2.85it/s]
 99%|█████████▉| 99/100 [00:35<00:00,  2.69it/s]
100%|██████████| 100/100 [00:35<00:00,  2.63it/s]
100%|██████████| 100/100 [00:35<00:00,  2.79it/s]

Get the results in a pandas table

pandas_results = results.getEstimatedParameters()
pandas_results
Value Rob. Std err Rob. t-test Rob. p-value
ASC_CAR 0.261291 0.100112 2.609977 9.054838e-03
ASC_SM 0.059020 0.216597 0.272489 7.852463e-01
BETA_COST -0.716192 0.138371 -5.175890 2.268270e-07
BETA_DIST_FEMALE -0.831209 0.192866 -4.309772 1.634229e-05
BETA_DIST_MALE -0.686327 0.160823 -4.267581 1.976044e-05
BETA_DIST_UNREPORTED -0.702974 0.196307 -3.581000 3.422819e-04
BETA_TIME_FULLTIME -1.597087 0.332796 -4.798996 1.594633e-06
BETA_TIME_OTHER -0.577362 0.296477 -1.947409 5.148566e-02
mu_nocar 1.528532 0.305659 5.000776 5.709995e-07


Simulation

simulated_choices = logprob.getValue_c(betas=results.getBetaValues(), database=database)
simulated_choices
array([-0.65048784, -1.4326262 , -0.13259011, ..., -0.37239372,
       -0.29534586, -0.26726783])
loglikelihood = logprob.getValue_c(
    betas=results.getBetaValues(),
    database=database,
    aggregation=True,
)
print(f'Final log likelihood:     {results.data.logLike}')
print(f'Simulated log likelihood: {loglikelihood}')
Final log likelihood:     -1298.4982789719456
Simulated log likelihood: -1298.4982789719452

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

Gallery generated by Sphinx-Gallery