Logit

Estimation of a logit model using sampling of alternatives.

Michel Bierlaire Fri Jul 25 2025, 17:36:23

import pandas as pd
from alternatives import ID_COLUMN, alternatives, partitions
from compare import compare
from IPython.core.display_functions import display
from specification_sampling import V, combined_variables

import biogeme.biogeme_logging as blog
from biogeme.biogeme import BIOGEME
from biogeme.results_processing import (
    EstimationResults,
    get_pandas_estimated_parameters,
)
from biogeme.sampling_of_alternatives import (
    ChoiceSetsGeneration,
    GenerateModel,
    SamplingContext,
    generate_segment_size,
)
from biogeme.tools import timeit
    ID  rating  price  ...   rest_lon    distance  downtown
0    0       1      4  ...  42.220972   71.735518       1.0
1    1       2      2  ...  50.549434  106.267205       0.0
2    2       3      3  ...  97.830520  136.298409       0.0
3    3       4      1  ...  69.152206   85.941147       0.0
4    4       4      3  ...  89.145620   96.773021       0.0
..  ..     ...    ...  ...        ...         ...       ...
95  95       4      3  ...   9.511387   84.166441       0.0
96  96       1      1  ...  92.144641   95.601366       0.0
97  97       4      2  ...  27.657518   30.440555       1.0
98  98       4      4  ...  32.303213   45.027143       1.0
99  99       4      1  ...  13.672495   25.703295       1.0

[100 rows x 16 columns]
Number of asian restaurants: 33
logger = blog.get_screen_logger(level=blog.INFO)

The data file contains several columns associated with synthetic choices. Here we arbitrarily select logit_4.

CHOICE_COLUMN = 'logit_4'
SAMPLE_SIZE = 10
PARTITION = 'asian'
MODEL_NAME = f'logit_{PARTITION}_{SAMPLE_SIZE}_alt'
FILE_NAME = f'{MODEL_NAME}.dat'
OBS_FILE = 'obs_choice.dat'
the_partition = partitions.get(PARTITION)
if the_partition is None:
    raise ValueError(f'Unknown partition: {PARTITION}')
segment_sizes = generate_segment_size(SAMPLE_SIZE, the_partition.number_of_segments())
observations = pd.read_csv(OBS_FILE)
context = SamplingContext(
    the_partition=the_partition,
    sample_sizes=segment_sizes,
    individuals=observations,
    choice_column=CHOICE_COLUMN,
    alternatives=alternatives,
    id_column=ID_COLUMN,
    biogeme_file_name=FILE_NAME,
    utility_function=V,
    combined_variables=combined_variables,
)
logger.info(context.reporting())
Size of the choice set: 100
Main partition: 2 segment(s) of size 33, 67
Main sample: 10: 5/33, 5/67
the_data_generation = ChoiceSetsGeneration(context=context)
the_model_generation = GenerateModel(context=context)
biogeme_database = the_data_generation.sample_and_merge(recycle=False)
Generating 10 + 0 alternatives for 10000 observations

  0%|          | 0/10000 [00:00<?, ?it/s]
  1%|          | 107/10000 [00:00<00:09, 1066.07it/s]
  2%|▏         | 218/10000 [00:00<00:08, 1090.19it/s]
  3%|▎         | 330/10000 [00:00<00:08, 1101.79it/s]
  4%|▍         | 442/10000 [00:00<00:08, 1108.35it/s]
  6%|▌         | 554/10000 [00:00<00:08, 1112.32it/s]
  7%|▋         | 666/10000 [00:00<00:08, 1091.67it/s]
  8%|▊         | 776/10000 [00:00<00:08, 1071.26it/s]
  9%|▉         | 884/10000 [00:00<00:08, 1045.09it/s]
 10%|▉         | 989/10000 [00:00<00:08, 1040.81it/s]
 11%|█         | 1094/10000 [00:01<00:08, 1042.98it/s]
 12%|█▏        | 1200/10000 [00:01<00:08, 1045.09it/s]
 13%|█▎        | 1306/10000 [00:01<00:08, 1047.38it/s]
 14%|█▍        | 1411/10000 [00:01<00:08, 1044.86it/s]
 15%|█▌        | 1516/10000 [00:01<00:08, 1045.15it/s]
 16%|█▌        | 1622/10000 [00:01<00:07, 1047.59it/s]
 17%|█▋        | 1728/10000 [00:01<00:07, 1050.52it/s]
 18%|█▊        | 1834/10000 [00:01<00:07, 1052.61it/s]
 19%|█▉        | 1941/10000 [00:01<00:07, 1055.21it/s]
 20%|██        | 2047/10000 [00:01<00:07, 1055.69it/s]
 22%|██▏       | 2153/10000 [00:02<00:07, 1052.07it/s]
 23%|██▎       | 2259/10000 [00:02<00:07, 1033.43it/s]
 24%|██▎       | 2363/10000 [00:02<00:07, 1019.58it/s]
 25%|██▍       | 2466/10000 [00:02<00:07, 997.27it/s]
 26%|██▌       | 2566/10000 [00:02<00:07, 994.44it/s]
 27%|██▋       | 2666/10000 [00:02<00:07, 993.11it/s]
 28%|██▊       | 2766/10000 [00:02<00:07, 974.34it/s]
 29%|██▊       | 2864/10000 [00:02<00:07, 950.31it/s]
 30%|██▉       | 2960/10000 [00:02<00:07, 934.79it/s]
 31%|███       | 3054/10000 [00:02<00:07, 924.89it/s]
 31%|███▏      | 3147/10000 [00:03<00:07, 910.28it/s]
 32%|███▏      | 3239/10000 [00:03<00:07, 870.14it/s]
 33%|███▎      | 3327/10000 [00:03<00:07, 845.42it/s]
 34%|███▍      | 3412/10000 [00:03<00:07, 832.60it/s]
 35%|███▍      | 3496/10000 [00:03<00:07, 815.08it/s]
 36%|███▌      | 3578/10000 [00:03<00:08, 775.47it/s]
 37%|███▋      | 3656/10000 [00:03<00:08, 752.00it/s]
 37%|███▋      | 3732/10000 [00:03<00:08, 736.03it/s]
 38%|███▊      | 3806/10000 [00:03<00:08, 725.34it/s]
 39%|███▉      | 3879/10000 [00:04<00:08, 706.21it/s]
 40%|███▉      | 3950/10000 [00:04<00:09, 662.77it/s]
 40%|████      | 4017/10000 [00:04<00:09, 640.88it/s]
 41%|████      | 4082/10000 [00:04<00:09, 627.29it/s]
 41%|████▏     | 4145/10000 [00:04<00:09, 618.14it/s]
 42%|████▏     | 4207/10000 [00:04<00:09, 597.86it/s]
 43%|████▎     | 4267/10000 [00:04<00:10, 562.65it/s]
 43%|████▎     | 4324/10000 [00:04<00:10, 539.45it/s]
 44%|████▍     | 4379/10000 [00:05<00:10, 524.67it/s]
 44%|████▍     | 4432/10000 [00:05<00:10, 514.76it/s]
 45%|████▍     | 4484/10000 [00:05<00:10, 506.98it/s]
 45%|████▌     | 4535/10000 [00:05<00:10, 500.07it/s]
 46%|████▌     | 4585/10000 [00:05<00:10, 496.42it/s]
 46%|████▋     | 4635/10000 [00:05<00:10, 493.48it/s]
 47%|████▋     | 4685/10000 [00:05<00:10, 492.03it/s]
 47%|████▋     | 4735/10000 [00:05<00:10, 491.02it/s]
 48%|████▊     | 4785/10000 [00:05<00:10, 490.42it/s]
 48%|████▊     | 4835/10000 [00:05<00:10, 489.84it/s]
 49%|████▉     | 4884/10000 [00:06<00:10, 489.19it/s]
 49%|████▉     | 4933/10000 [00:06<00:10, 479.01it/s]
 50%|████▉     | 4981/10000 [00:06<00:10, 474.61it/s]
 50%|█████     | 5029/10000 [00:06<00:10, 462.99it/s]
 51%|█████     | 5076/10000 [00:06<00:10, 456.64it/s]
 51%|█████     | 5122/10000 [00:06<00:10, 457.56it/s]
 52%|█████▏    | 5171/10000 [00:06<00:10, 465.88it/s]
 52%|█████▏    | 5220/10000 [00:06<00:10, 472.48it/s]
 53%|█████▎    | 5268/10000 [00:06<00:10, 470.35it/s]
 53%|█████▎    | 5316/10000 [00:06<00:09, 469.48it/s]
 54%|█████▎    | 5363/10000 [00:07<00:10, 437.51it/s]
 54%|█████▍    | 5408/10000 [00:07<00:11, 393.19it/s]
 54%|█████▍    | 5449/10000 [00:07<00:12, 362.09it/s]
 55%|█████▍    | 5487/10000 [00:07<00:13, 344.89it/s]
 55%|█████▌    | 5523/10000 [00:07<00:13, 341.40it/s]
 56%|█████▌    | 5558/10000 [00:07<00:13, 332.84it/s]
 56%|█████▌    | 5594/10000 [00:07<00:13, 338.02it/s]
 56%|█████▋    | 5629/10000 [00:07<00:12, 339.92it/s]
 57%|█████▋    | 5665/10000 [00:08<00:12, 342.49it/s]
 57%|█████▋    | 5700/10000 [00:08<00:13, 327.62it/s]
 57%|█████▋    | 5733/10000 [00:08<00:13, 317.72it/s]
 58%|█████▊    | 5766/10000 [00:08<00:13, 318.84it/s]
 58%|█████▊    | 5801/10000 [00:08<00:12, 325.44it/s]
 58%|█████▊    | 5834/10000 [00:08<00:13, 304.09it/s]
 59%|█████▊    | 5866/10000 [00:08<00:13, 306.26it/s]
 59%|█████▉    | 5909/10000 [00:08<00:12, 340.38it/s]
 60%|█████▉    | 5958/10000 [00:08<00:10, 382.99it/s]
 60%|██████    | 6006/10000 [00:08<00:09, 409.93it/s]
 61%|██████    | 6052/10000 [00:09<00:09, 424.00it/s]
 61%|██████    | 6099/10000 [00:09<00:08, 437.13it/s]
 61%|██████▏   | 6145/10000 [00:09<00:08, 443.43it/s]
 62%|██████▏   | 6192/10000 [00:09<00:08, 448.79it/s]
 62%|██████▏   | 6238/10000 [00:09<00:08, 451.59it/s]
 63%|██████▎   | 6284/10000 [00:09<00:08, 448.04it/s]
 63%|██████▎   | 6330/10000 [00:09<00:08, 448.88it/s]
 64%|██████▍   | 6378/10000 [00:09<00:07, 456.78it/s]
 64%|██████▍   | 6427/10000 [00:09<00:07, 464.27it/s]
 65%|██████▍   | 6476/10000 [00:10<00:07, 469.59it/s]
 65%|██████▌   | 6525/10000 [00:10<00:07, 473.18it/s]
 66%|██████▌   | 6574/10000 [00:10<00:07, 476.81it/s]
 66%|██████▌   | 6623/10000 [00:10<00:07, 478.15it/s]
 67%|██████▋   | 6672/10000 [00:10<00:06, 480.20it/s]
 67%|██████▋   | 6721/10000 [00:10<00:06, 481.99it/s]
 68%|██████▊   | 6770/10000 [00:10<00:06, 483.40it/s]
 68%|██████▊   | 6819/10000 [00:10<00:06, 484.76it/s]
 69%|██████▊   | 6868/10000 [00:10<00:06, 485.61it/s]
 69%|██████▉   | 6917/10000 [00:10<00:06, 486.26it/s]
 70%|██████▉   | 6966/10000 [00:11<00:06, 479.57it/s]
 70%|███████   | 7015/10000 [00:11<00:06, 482.45it/s]
 71%|███████   | 7064/10000 [00:11<00:06, 483.48it/s]
 71%|███████   | 7113/10000 [00:11<00:06, 481.14it/s]
 72%|███████▏  | 7162/10000 [00:11<00:05, 476.96it/s]
 72%|███████▏  | 7211/10000 [00:11<00:05, 480.25it/s]
 73%|███████▎  | 7260/10000 [00:11<00:05, 477.70it/s]
 73%|███████▎  | 7309/10000 [00:11<00:05, 478.40it/s]
 74%|███████▎  | 7358/10000 [00:11<00:05, 480.20it/s]
 74%|███████▍  | 7407/10000 [00:11<00:05, 482.95it/s]
 75%|███████▍  | 7456/10000 [00:12<00:05, 484.74it/s]
 75%|███████▌  | 7505/10000 [00:12<00:05, 485.18it/s]
 76%|███████▌  | 7554/10000 [00:12<00:05, 484.31it/s]
 76%|███████▌  | 7603/10000 [00:12<00:04, 484.71it/s]
 77%|███████▋  | 7652/10000 [00:12<00:04, 485.43it/s]
 77%|███████▋  | 7701/10000 [00:12<00:04, 486.18it/s]
 78%|███████▊  | 7751/10000 [00:12<00:04, 487.66it/s]
 78%|███████▊  | 7801/10000 [00:12<00:04, 488.97it/s]
 78%|███████▊  | 7850/10000 [00:12<00:04, 488.15it/s]
 79%|███████▉  | 7899/10000 [00:12<00:04, 488.03it/s]
 79%|███████▉  | 7948/10000 [00:13<00:04, 488.46it/s]
 80%|███████▉  | 7998/10000 [00:13<00:04, 489.00it/s]
 80%|████████  | 8047/10000 [00:13<00:04, 487.17it/s]
 81%|████████  | 8102/10000 [00:13<00:03, 504.41it/s]
 82%|████████▏ | 8160/10000 [00:13<00:03, 525.34it/s]
 82%|████████▏ | 8220/10000 [00:13<00:03, 547.57it/s]
 83%|████████▎ | 8280/10000 [00:13<00:03, 562.61it/s]
 83%|████████▎ | 8340/10000 [00:13<00:02, 572.73it/s]
 84%|████████▍ | 8400/10000 [00:13<00:02, 580.15it/s]
 85%|████████▍ | 8459/10000 [00:13<00:02, 577.11it/s]
 85%|████████▌ | 8519/10000 [00:14<00:02, 582.99it/s]
 86%|████████▌ | 8579/10000 [00:14<00:02, 587.99it/s]
 86%|████████▋ | 8639/10000 [00:14<00:02, 589.85it/s]
 87%|████████▋ | 8707/10000 [00:14<00:02, 615.72it/s]
 88%|████████▊ | 8777/10000 [00:14<00:01, 638.85it/s]
 88%|████████▊ | 8848/10000 [00:14<00:01, 657.80it/s]
 89%|████████▉ | 8919/10000 [00:14<00:01, 672.22it/s]
 90%|████████▉ | 8990/10000 [00:14<00:01, 682.66it/s]
 91%|█████████ | 9061/10000 [00:14<00:01, 690.01it/s]
 91%|█████████▏| 9132/10000 [00:14<00:01, 693.36it/s]
 92%|█████████▏| 9203/10000 [00:15<00:01, 696.90it/s]
 93%|█████████▎| 9274/10000 [00:15<00:01, 699.30it/s]
 93%|█████████▎| 9344/10000 [00:15<00:00, 697.48it/s]
 94%|█████████▍| 9414/10000 [00:15<00:00, 697.81it/s]
 95%|█████████▍| 9484/10000 [00:15<00:00, 697.94it/s]
 96%|█████████▌| 9554/10000 [00:15<00:00, 696.96it/s]
 96%|█████████▋| 9629/10000 [00:15<00:00, 711.58it/s]
 97%|█████████▋| 9710/10000 [00:15<00:00, 740.68it/s]
 98%|█████████▊| 9792/10000 [00:15<00:00, 762.00it/s]
 99%|█████████▊| 9874/10000 [00:15<00:00, 776.27it/s]
100%|█████████▉| 9952/10000 [00:16<00:00, 774.55it/s]
100%|██████████| 10000/10000 [00:16<00:00, 602.70it/s]
Define new variables

Defining new variables...:   0%|          | 0/10 [00:00<?, ?it/s]
Defining new variables...:  10%|█         | 1/10 [00:00<00:01,  7.85it/s]
Defining new variables...:  30%|███       | 3/10 [00:00<00:00, 10.35it/s]
Defining new variables...:  50%|█████     | 5/10 [00:00<00:00, 11.28it/s]
Defining new variables...:  70%|███████   | 7/10 [00:00<00:00, 11.41it/s]
Defining new variables...:  90%|█████████ | 9/10 [00:00<00:00, 11.40it/s]
Defining new variables...: 100%|██████████| 10/10 [00:00<00:00, 11.06it/s]
File logit_asian_10_alt.dat has been created.
logprob = the_model_generation.get_logit()
the_biogeme = BIOGEME(biogeme_database, logprob)
the_biogeme.modelName = MODEL_NAME
Default values of the Biogeme parameters are used.
File biogeme.toml has been created
/Users/bierlair/MyFiles/github/biogeme/docs/source/examples/sampling/plot_b01logit.py:88: DeprecationWarning: 'modelName' is deprecated. Please use 'model_name' instead.
  the_biogeme.modelName = MODEL_NAME

Calculate the null log likelihood for reporting.

the_biogeme.calculate_null_loglikelihood({i: 1 for i in range(SAMPLE_SIZE)})
-23025.850929940458

Estimate the parameters.

try:
    results = EstimationResults.from_yaml_file(
        filename=f'saved_results/{the_biogeme.model_name}.yaml'
    )
except FileNotFoundError:
    with timeit(f'Estimate of model {the_biogeme.model_name}'):
        results = the_biogeme.estimate()
print(results.short_summary())
Results for model logit_asian_10_alt
Nbr of parameters:              11
Sample size:                    10000
Excluded data:                  0
Null log likelihood:            -23025.85
Final log likelihood:           -18461.47
Likelihood ratio test (null):           9128.762
Rho square (null):                      0.198
Rho bar square (null):                  0.198
Akaike Information Criterion:   36944.94
Bayesian Information Criterion: 37024.25
parameters_tables = get_pandas_estimated_parameters(estimation_results=results)
estimated_parameters = parameters_tables['Estimated parameters']
display(estimated_parameters)
              Name     Value  Robust std err.  Robust t-stat.  Robust p-value
0      beta_rating  0.745578         0.015276       48.807683             0.0
1       beta_price -0.397459         0.012809      -31.030521             0.0
2     beta_chinese  0.604055         0.050288       12.011878             0.0
3    beta_japanese  1.193128         0.046261       25.791428             0.0
4      beta_korean  0.704947         0.042994       16.396519             0.0
5      beta_indian  0.895273         0.042947       20.846162             0.0
6      beta_french  0.663347         0.061966       10.704956             0.0
7     beta_mexican  1.190282         0.036481       32.627696             0.0
8    beta_lebanese  0.644906         0.062411       10.333229             0.0
9   beta_ethiopian  0.437794         0.050607        8.650927             0.0
10   beta_log_dist -0.582005         0.015147      -38.423348             0.0
df, msg = compare(estimated_parameters)
print(df)
              Name  True Value  Estimated Value    T-Test
0      beta_rating        0.75         0.745578  0.289480
1       beta_price       -0.40        -0.397459 -0.198364
2     beta_chinese        0.75         0.604055  2.902167
3    beta_japanese        1.25         1.193128  1.229387
4      beta_korean        0.75         0.704947  1.047895
5      beta_indian        1.00         0.895273  2.438528
6      beta_french        0.75         0.663347  1.398390
7     beta_mexican        1.25         1.190282  1.636973
8    beta_lebanese        0.75         0.644906  1.683908
9   beta_ethiopian        0.50         0.437794  1.229203
10   beta_log_dist       -0.60        -0.582005 -1.188012
print(msg)
Parameters not estimated: ['mu_asian', 'mu_downtown']

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

Gallery generated by Sphinx-Gallery