1: MTC MNL Mode Choice

This example is a mode choice model built using the MTC example dataset. First we create the DB and Model objects:

d = larch.examples.MTC()
m = larch.Model(dataservice=d)

Then we can build up the utility function. We’ll use some idco Format data first, using the Model.utility.co attribute. This attribute is a dict-like object, to which we can assign LinearFunction objects for each alternative code.

from larch.roles import P, X, PX
m.utility_co[2] = P("ASC_SR2")  + P("hhinc#2") * X("hhinc")
m.utility_co[3] = P("ASC_SR3P") + P("hhinc#3") * X("hhinc")
m.utility_co[4] = P("ASC_TRAN") + P("hhinc#4") * X("hhinc")
m.utility_co[5] = P("ASC_BIKE") + P("hhinc#5") * X("hhinc")
m.utility_co[6] = P("ASC_WALK") + P("hhinc#6") * X("hhinc")

Next we’ll use some idca data, with the utility_ca attribute. This attribute is only a single LinearFunction that is applied across all alternatives using idca Format data. Because the data is structured to vary across alternatives, the parameters (and thus the structure of the LinearFunction) does not need to vary across alternatives.

m.utility_ca = PX("tottime") + PX("totcost")

Lastly, we need to identify idca Format data that gives the availability for each alternative, as well as the number of times each alternative is chosen. (In traditional discrete choice analysis, this is often 0 or 1, but it need not be binary, or even integral.)

m.availability_var = '_avail_'
m.choice_ca_var = '_choice_'

And let’s give our model a descriptive title.

m.title = "MTC Example 1 (Simple MNL)"

Having created this model, we can then estimate it:

>>> m.load_data()
>>> m.maximize_loglike()
┣ ...Optimization terminated successfully...
>>> m.calculate_parameter_covariance()
>>> m.loglike()
-3626.18...

Then the parameters can be found in the pf DataFrame:

>>> print(m.pf[['value','std err','t stat','robust std err','robust t stat']])
              value    std err  t stat  robust std err  robust t stat
hhinc#5  -1.281e-02  5.324e-03  -2.406       6.565e-03         -1.951
hhinc#4  -5.287e-03  1.829e-03  -2.891       1.769e-03         -2.988
ASC_BIKE -2.376e+00  3.045e-01  -7.804       3.607e-01         -6.588
hhinc#6  -9.686e-03  3.033e-03  -3.193       3.229e-03         -3.000
hhinc#3   3.583e-04  2.538e-03   0.141       2.806e-03          0.128
ASC_SR3P -3.725e+00  1.777e-01 -20.964       1.929e-01        -19.312
tottime  -5.134e-02  3.099e-03 -16.565       3.455e-03        -14.860
hhinc#2  -2.170e-03  1.553e-03  -1.397       1.647e-03         -1.318
totcost  -4.920e-03  2.389e-04 -20.597       2.833e-04        -17.368
ASC_WALK -2.068e-01  1.941e-01  -1.066       2.067e-01         -1.001
ASC_SR2  -2.178e+00  1.046e-01 -20.815       1.119e-01        -19.461
ASC_TRAN -6.709e-01  1.326e-01  -5.060       1.287e-01         -5.215

It is a little tough to read this report because the parameters can show up in pretty much any order, as they are not sorted when they are automatically discovered by Larch. We can use the reorder method to fix this:

m.ordering = (
        ("LOS", "totcost", "tottime", ),
        ("ASCs", "ASC.*", ),
        ("Income", "hhinc.*", ),
)

Then the report will look more reasonable (although ultimately the content is the same):

>>> print(m.pfo()[['value','std err','t stat','robust std err','robust t stat']])
                        value    std err  t stat  robust std err  robust t stat
Category Parameter
LOS      totcost   -4.920e-03  2.389e-04 -20.596       2.833e-04        -17.368
         tottime   -5.134e-02  3.099e-03 -16.565       3.455e-03        -14.860
ASCs     ASC_BIKE  -2.376e+00  3.045e-01  -7.804       3.607e-01         -6.588
         ASC_SR2   -2.178e+00  1.046e-01 -20.815       1.119e-01        -19.461
         ASC_SR3P  -3.725e+00  1.777e-01 -20.964       1.929e-01        -19.312
         ASC_TRAN  -6.709e-01  1.326e-01  -5.060       1.287e-01         -5.214
         ASC_WALK  -2.068e-01  1.941e-01  -1.065       2.067e-01         -1.001
Income   hhinc#2   -2.170e-03  1.553e-03  -1.397       1.647e-03         -1.318
         hhinc#3    3.577e-04  2.538e-03   0.141       2.806e-03          0.127
         hhinc#4   -5.286e-03  1.829e-03  -2.891       1.769e-03         -2.988
         hhinc#5   -1.281e-02  5.324e-03  -2.406       6.565e-03         -1.951
         hhinc#6   -9.686e-03  3.033e-03  -3.194       3.229e-03         -3.000

You can then access individual parameters from the model’s parameter frame (it’s just a pandas.DataFrame) with the usual pandas methods.

>>> m.pf.loc['ASC_BIKE'].value
-2.3763...

>>> m.pf.loc['totcost', 'std err']
0.00023889...

The len() function on the parameter DataFrame retrieves the number of parameters.

>>> len(m.pf)
12