Page 1 of 1

Two level Nested Logit(NL) model specification for many alternatives

Posted: 02 Jan 2024, 14:44
by lucia
Hi Prof. Stephane Hess,

I am currently estimating a nested logit model with location choice (randomly choose three alternatives, 'alt2', 'alt3', 'alt4', from all TAZ zones, and 'alt1' is always the choosing TAZ zone for an individual ) as the upper level and utility for four different travel modes ('Auto,'' Passenger,' 'Rideshare,' 'PT') as the lower level. I encountered two questions with the model specification, and I am writing to ask for your kind suggestions.

The two questions are:
1) I have both TAZ alternative-specific variables (b_LogCensusEmploy & b_LogCensuspop) and mode-specific variables (c_AutoPassUtil, C_Rideshare, C_PTUtil). Currently, I am coding the utility function as follows:

Code: Select all

    ### List of utilities
      V = list()
      for(s in 1:4){
      V[[paste0("Auto",s)]] = 0
   
      V[[paste0("Passenger",s)]] = get(paste0("asc_AutoPassen",s)) +
        b_LogCensusEmploy* get(paste0("CensusEmploy",s )) +
        b_LogCensuspop* get(paste0("CensusPop",s )) +
        C_AutoPassUtil* get(paste0("Auto_util",s))
   
      V[[paste0("Rideshare",s)]] = get(paste0("asc_Rideshare",s))+
        b_LogCensusEmploy* get(paste0("CensusEmploy",s )) +
        b_LogCensuspop* get(paste0("CensusPop",s )) +
        C_Rideshare* get(paste0("Auto_util",s)) *0.8
   
      V[[paste0("PT",s)]] = get(paste0("asc_PT",s)) +
        b_LogCensusEmploy* get(paste0("CensusEmploy",s )) +
        b_LogCensuspop* get(paste0("CensusPop",s )) +
        C_PTUtil* get(paste0("PT_util",s))

It seems inappropriate to put both alternative-specific variables and mode-specific variables in the same format as above, but I don't know what the appropriate way is to join both alternative and mode-specific variables in the utility function.

2) For the nlStructure settings, I tried to code it as a loop as I want to join more TAZ alternatives for the next step (currently random select 3 out of 1757 TAZs, extend it to random select 219 out of 1757 TAZs afterward). I coded the tree structure based on one of the questions you answered in the forum. However, when I estimated using the following codes, the LLstart became 0. I think there should be something wrong with the tree structure setting part, but when I ran the code separately, it generated the right tree structure.

Code: Select all

    ### Specify tree structure for NL model
     list_alt = paste0("alt",1:4)
     nlStructure = matrix(paste0(rep(c('Auto','Passenger','Rideshare','PT'), times=4), rep(1:4, each=4)), ncol=4, byrow=TRUE)
     nlStructure = setNames(split(nlStructure, 1:length(list_alt)), list_alt)
     nlStructure[['root']] = as.character(list_alt)
Thank you for your time and for developing the Apollo package!

Attached code below:

Code: Select all

  rm(list = ls())
  library(apollo)
  apollo_initialise()
  
  # #################################################################
  
  #### LOAD DATA AND APPLY ANY TRANSFORMATIONS
  
  # #################################################################
  
  database = read.csv(".\\02_TAZ_availablematrix_V2.csv", header = TRUE) 

  database <- database %>% 
    mutate(jointchoice = groupe_modes)

  #View(database)
  apollo_control = list(
    modelName  ="Apollo_NL",
    modelDescr ="Two-level NL model with Location and Mode choice",
    nCores    = 4,
    panelData=FALSE,
    indivID = "AID"
  )
   
  list_loc <- unique(seq(1, 4, by=1))   
  
  apollo_beta=c(c(
                asc_AutoPassen1        = 0,
                asc_Rideshare1         = 0,
                asc_PT1                = 0,
                asc_AutoPassen2        = 0,
                asc_Rideshare2         = 0,
                asc_PT2                = 0,
                asc_AutoPassen3        = 0,
                asc_Rideshare3         = 0,
                asc_PT3                = 0,
                asc_AutoPassen4        = 0,
                asc_Rideshare4         = 0,
                asc_PT4                = 0,
                C_AutoPassUtil         = 1,
                C_Rideshare            = 1,
                C_PTUtil               = 1,
 #               b_Dist                 = 0,
                b_LogCensusEmploy      = -3,
                b_LogCensuspop         = -3),
                setNames(rep(0.5, length(list_loc)), paste0("lamda", list_loc))
  )
 

  apollo_fixed =c()
  
  ### Group and validate inputs
  apollo_inputs = apollo_validateInputs()
  
  
  apollo_probabilities=function(apollo_beta, apollo_inputs, functionality="estimate"){
    
    ### Attach inputs and detach after function exit
    apollo_attach(apollo_beta, apollo_inputs)
    on.exit(apollo_detach(apollo_beta, apollo_inputs))
    
    ### Create list of probabilities P
    
    P = list()

    ### List of utilities: these must use the same names as in nl_settings, order is irrelevant
    V = list()
    
    for(s in 1:4){
      V[[paste0("Auto",s)]] = 0
    
      V[[paste0("Passenger",s)]] = get(paste0("asc_AutoPassen",s)) + 
        b_LogCensusEmploy* get(paste0("CensusEmploy",s )) +
        b_LogCensuspop* get(paste0("CensusPop",s )) + 
        C_AutoPassUtil* get(paste0("Auto_util",s))
    
      V[[paste0("Rideshare",s)]] = get(paste0("asc_Rideshare",s))+ 
        b_LogCensusEmploy* get(paste0("CensusEmploy",s )) +
        b_LogCensuspop* get(paste0("CensusPop",s )) + 
        C_Rideshare* get(paste0("Auto_util",s)) *0.8
    
      V[[paste0("PT",s)]] = get(paste0("asc_PT",s)) + 
        b_LogCensusEmploy* get(paste0("CensusEmploy",s )) +
        b_LogCensuspop* get(paste0("CensusPop",s )) + 
        C_PTUtil* get(paste0("PT_util",s))
    
      }

     nlNests = list(root=1)
     for (loc in 1:4) {
     nlNests[[paste0("alt",loc)]] <- get(paste0("lamda", loc))
     }
    
    ### Specify tree structure for NL model
     list_alt = paste0("alt",1:4)
     nlStructure = matrix(paste0(rep(c('Auto','Passenger','Rideshare','PT'), times=4), rep(1:4, each=4)), ncol=4, byrow=TRUE)
     nlStructure = setNames(split(nlStructure, 1:length(list_alt)), list_alt)
     nlStructure[['root']] = as.character(list_alt)
      
     #nlStructure= list() 
     #nlStructure[["root"]] = c("alt1","alt2","alt3","alt4")
     #nlStructure[["alt1"]] = c("Auto1","Passenger1","Rideshare1","PT1")
     #nlStructure[["alt2"]] = c("Auto2","Passenger2","Rideshare2","PT2")
     #nlStructure[["alt3"]] = c("Auto3","Passenger3","Rideshare3","PT3")
     #nlStructure[["alt4"]] = c("Auto4","Passenger4","Rideshare4","PT4")
    
     # Define settings for NL model
      nl_settings <- list(
        alternatives = c(Auto1=1,  Passenger1=2,  Rideshare1=3,  PT1=4,
                         Auto2=5,  Passenger2=6,  Rideshare2=7,  PT2=8,
                         Auto3=9,  Passenger3=10, Rideshare3=11, PT3=12,
                         Auto4=13, Passenger4=14, Rideshare4=15, PT4=16),
      
        avail        = 1,
        choiceVar    = jointchoice,
        V            = V,
        nlNests      = nlNests,
        nlStructure  = nlStructure
      )
    
    ### Compute probabilities using NL model
    
    ## Take product across observation for same individual
    #P = apollo_panelProd(P, apollo_inputs, functionality)
    P[["model"]] = apollo_nl(nl_settings, functionality)
    ### Prepare and return outputs of function
    P = apollo_prepareProb(P, apollo_inputs, functionality)
    return(P)
  }
  
  estimate_settings <- list(maxIterations = 10000)
  
  ### Optional starting values search
  apollo_beta=apollo_searchStart(apollo_beta, apollo_fixed,apollo_probabilities, apollo_inputs)
  # #################################################################
  
  #### MODEL ESTIMATION
  
  # #################################################################
  
  NLmodel = apollo_estimate(apollo_beta, apollo_fixed, apollo_probabilities, apollo_inputs)
  #tmp=apollo_probabilities(apollo_beta,apollo_inputs,functionality="output")
}



apollo_modelOutput(NLmodel)

predictions_base = apollo_prediction(NLmodel, apollo_probabilities, apollo_inputs,
                                     prediction_settings=list(runs=30))

apollo_saveOutput(NLmodel)

Re: Two level Nested Logit(NL) model specification for many alternatives

Posted: 06 May 2024, 08:26
by stephanehess
Hi

apologies for the slow reply, your message had got lost

Did you try with lambda=1 as a starting value. Essentially, you should first see if you can replicate MNL

Stephane