Mixture of logit

Choice model with latent variable. No measurement equation for the indicators.

author:

Michel Bierlaire, EPFL

date:

Thu Apr 13 16:58:21 2023

import biogeme.biogeme_logging as blog
import biogeme.biogeme as bio
from biogeme import models
import biogeme.distributions as dist
from biogeme.expressions import (
    Beta,
    RandomVariable,
    Integrate,
    exp,
    log,
)

from read_or_estimate import read_or_estimate

from optima import (
    database,
    age_65_more,
    ScaledIncome,
    moreThanOneCar,
    moreThanOneBike,
    individualHouse,
    male,
    haveChildren,
    haveGA,
    highEducation,
    WaitingTimePT,
    Choice,
    TimePT_scaled,
    TimeCar_scaled,
    MarginalCostPT_scaled,
    CostCarCHF_scaled,
    distance_km_scaled,
    PurpHWH,
    PurpOther,
)


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

Parameters to be estimated

coef_intercept = Beta('coef_intercept', 0.0, None, None, 1)
coef_age_65_more = Beta('coef_age_65_more', 0.0, None, None, 0)
coef_haveGA = Beta('coef_haveGA', 0.0, None, None, 0)
coef_moreThanOneCar = Beta('coef_moreThanOneCar', 0.0, None, None, 0)
coef_moreThanOneBike = Beta('coef_moreThanOneBike', 0.0, None, None, 0)
coef_individualHouse = Beta('coef_individualHouse', 0.0, None, None, 0)
coef_male = Beta('coef_male', 0.0, None, None, 0)
coef_haveChildren = Beta('coef_haveChildren', 0.0, None, None, 0)
coef_highEducation = Beta('coef_highEducation', 0.0, None, None, 0)

Latent variable: structural equation.

Define a random parameter, normally distributed) designed to be used for numerical integration.

omega = RandomVariable('omega')
density = dist.normalpdf(omega)
sigma_s = Beta('sigma_s', 1, None, None, 0)

thresholds = [None, 4, 6, 8, 10, None]
formula_income = models.piecewiseFormula(variable=ScaledIncome, thresholds=thresholds)

CARLOVERS = (
    coef_intercept
    + coef_age_65_more * age_65_more
    + formula_income
    + coef_moreThanOneCar * moreThanOneCar
    + coef_moreThanOneBike * moreThanOneBike
    + coef_individualHouse * individualHouse
    + coef_male * male
    + coef_haveChildren * haveChildren
    + coef_haveGA * haveGA
    + coef_highEducation * highEducation
    + sigma_s * omega
)

Choice model: parameters.

ASC_CAR = Beta('ASC_CAR', 0.0, None, None, 0)
ASC_PT = Beta('ASC_PT', 0.0, None, None, 1)
ASC_SM = Beta('ASC_SM', 0.0, None, None, 0)
BETA_COST_HWH = Beta('BETA_COST_HWH', 0.0, None, None, 0)
BETA_COST_OTHER = Beta('BETA_COST_OTHER', 0.0, None, None, 0)
BETA_DIST = Beta('BETA_DIST', 0.0, None, None, 0)
BETA_TIME_CAR_REF = Beta('BETA_TIME_CAR_REF', -0.0001, None, 0, 0)
BETA_TIME_CAR_CL = Beta('BETA_TIME_CAR_CL', -1.0, -3, 3, 0)
BETA_TIME_PT_REF = Beta('BETA_TIME_PT_REF', -0.0001, None, 0, 0)
BETA_TIME_PT_CL = Beta('BETA_TIME_PT_CL', -1.0, -3, 3, 0)
BETA_WAITING_TIME = Beta('BETA_WAITING_TIME', 0.0, None, None, 0)

Definition of utility functions.

BETA_TIME_PT = BETA_TIME_PT_REF * exp(BETA_TIME_PT_CL * CARLOVERS)

V0 = (
    ASC_PT
    + BETA_TIME_PT * TimePT_scaled
    + BETA_WAITING_TIME * WaitingTimePT
    + BETA_COST_HWH * MarginalCostPT_scaled * PurpHWH
    + BETA_COST_OTHER * MarginalCostPT_scaled * PurpOther
)

BETA_TIME_CAR = BETA_TIME_CAR_REF * exp(BETA_TIME_CAR_CL * CARLOVERS)

V1 = (
    ASC_CAR
    + BETA_TIME_CAR * TimeCar_scaled
    + BETA_COST_HWH * CostCarCHF_scaled * PurpHWH
    + BETA_COST_OTHER * CostCarCHF_scaled * PurpOther
)

V2 = ASC_SM + BETA_DIST * distance_km_scaled

Associate utility functions with the numbering of alternatives.

V = {0: V0, 1: V1, 2: V2}

Conditional on omega, we have a logit model (called the kernel).

condprob = models.logit(V, None, Choice)

We integrate over omega using numerical integration.

loglike = log(Integrate(condprob * density, 'omega'))

Create the Biogeme object.

the_biogeme = bio.BIOGEME(database, loglike)
the_biogeme.modelName = 'b03choice_only'
File biogeme.toml has been parsed.

If estimation results are saved on file, we read them to speed up the process. If not, we estimate the parameters.

results = read_or_estimate(the_biogeme=the_biogeme, directory='saved_results')
print(f'Estimated betas: {len(results.data.betaValues)}')
print(f'Final log likelihood: {results.data.logLike:.3f}')
print(f'Output file: {results.data.htmlFileName}')
Estimated betas: 24
Final log likelihood: -1075.485
Output file: b03choice_only.html
results.getEstimatedParameters()
Value Rob. Std err Rob. t-test Rob. p-value
ASC_CAR 0.933907 0.181626 5.141910 2.719592e-07
ASC_SM 1.870388 0.764420 2.446808 1.441278e-02
BETA_COST_HWH -1.463999 0.553502 -2.644977 8.169639e-03
BETA_COST_OTHER -0.719213 0.261093 -2.754627 5.875901e-03
BETA_DIST -5.184167 2.772885 -1.869593 6.154032e-02
BETA_TIME_CAR_CL -2.038191 0.711591 -2.864274 4.179663e-03
BETA_TIME_CAR_REF -5.160340 1.895779 -2.722015 6.488523e-03
BETA_TIME_PT_CL -1.424666 0.128401 -11.095451 0.000000e+00
BETA_TIME_PT_REF -2.411777 0.612787 -3.935747 8.293822e-05
BETA_WAITING_TIME -0.039506 0.016827 -2.347735 1.888797e-02
beta_ScaledIncome_10_inf -0.009327 0.061969 -0.150511 8.803611e-01
beta_ScaledIncome_4_6 0.181499 0.283421 0.640386 5.219218e-01
beta_ScaledIncome_6_8 -0.294790 0.257292 -1.145739 2.519032e-01
beta_ScaledIncome_8_10 0.128598 0.222033 0.579184 5.624648e-01
beta_ScaledIncome_minus_inf_4 -0.089157 0.095093 -0.937574 3.484632e-01
coef_age_65_more -0.054803 0.094912 -0.577413 5.636607e-01
coef_haveChildren -0.032954 0.235116 -0.140161 8.885329e-01
coef_haveGA -0.657874 0.204498 -3.217024 1.295279e-03
coef_highEducation 0.001339 0.136040 0.009840 9.921489e-01
coef_individualHouse -0.112264 0.371466 -0.302220 7.624846e-01
coef_male 0.105574 0.234527 0.450155 6.525990e-01
coef_moreThanOneBike -0.306580 0.155313 -1.973949 4.838753e-02
coef_moreThanOneCar 0.783374 0.088790 8.822745 0.000000e+00
sigma_s 0.687676 0.068003 10.112384 0.000000e+00


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

Gallery generated by Sphinx-Gallery