302: Itinerary Choice using Simple Nested Logit

302: Itinerary Choice using Simple Nested Logit

import larch
larch.__version__
'5.6.1'

This example is an itinerary choice model built using the example itinerary choice dataset included with Larch. See example 300 for details.

from larch.data_warehouse import example_file
itin = pd.read_csv(example_file("arc"), index_col=['id_case','id_alt'])
d = larch.DataFrames(itin, ch='choice', crack=True, autoscale_weights=True)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Input In [2], in <module>
      1 from larch.data_warehouse import example_file
----> 2 itin = pd.read_csv(example_file("arc"), index_col=['id_case','id_alt'])
      3 d = larch.DataFrames(itin, ch='choice', crack=True, autoscale_weights=True)

NameError: name 'pd' is not defined

We will be building a nested logit model, but in order to do so we need to rationalize the alternative numbers. As given, our raw itinerary choice data has a lot of alternatives, but they are not ordered or numbered in a regular way; each elemental alternative has an arbitrary code number assigned to it, and the code numbers for one case are not comparable to another case. We need to renumber the alternatives in a manner that is more suited for our application, such that based on the code number we can programatically extract a the relevant features of the alternative that we will want to use in building our nested logit model. In this example we want to test a model which has nests based on level of service. To renumber, first we will define the relevant categories and values, and establish a numbering system using a special object:

d1 = d.new_systematic_alternatives(
    groupby='nb_cnxs',
    name='alternative_code',
    padding_levels=4,
    groupby_prefixes=['Cnx'],
    overwrite=False,
    complete_features_list={'nb_cnxs':[0,1,2]},
)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Input In [3], in <module>
----> 1 d1 = d.new_systematic_alternatives(
      2     groupby='nb_cnxs',
      3     name='alternative_code',
      4     padding_levels=4,
      5     groupby_prefixes=['Cnx'],
      6     overwrite=False,
      7     complete_features_list={'nb_cnxs':[0,1,2]},
      8 )

NameError: name 'd' is not defined

If we compare the new data with the old data, we’ll see that we have created a few more alternative.

d.info()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Input In [4], in <module>
----> 1 d.info()

NameError: name 'd' is not defined
d1.info()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Input In [5], in <module>
----> 1 d1.info()

NameError: name 'd1' is not defined

Now let’s make our model. The utility function we will use is the same as the one we used for the MNL version of the model.

m = larch.Model(dataservice=d1)

v = [
    "timeperiod==2",
    "timeperiod==3",
    "timeperiod==4",
    "timeperiod==5",
    "timeperiod==6",
    "timeperiod==7",
    "timeperiod==8",
    "timeperiod==9",
    "carrier==2",
    "carrier==3",
    "carrier==4",
    "carrier==5",
    "equipment==2",
    "fare_hy",
    "fare_ly",    
    "elapsed_time",  
    "nb_cnxs",       
]
from larch.roles import PX
m.utility_ca = sum(PX(i) for i in v)

m.choice_ca_var = 'choice'
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Input In [6], in <module>
----> 1 m = larch.Model(dataservice=d1)
      3 v = [
      4     "timeperiod==2",
      5     "timeperiod==3",
   (...)
     20     "nb_cnxs",       
     21 ]
     22 from larch.roles import PX

NameError: name 'd1' is not defined

If we just end our model specification here, we will have a plain MNL model. To change to a nested logit model, all we need to do is add the nests. We can do this easily, using the special magic_nesting method, that uses the structure of the data that we defined above.

m.magic_nesting()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Input In [7], in <module>
----> 1 m.magic_nesting()

NameError: name 'm' is not defined
m.load_data()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Input In [8], in <module>
----> 1 m.load_data()

NameError: name 'm' is not defined
m.maximize_loglike()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Input In [9], in <module>
----> 1 m.maximize_loglike()

NameError: name 'm' is not defined