Page 1 of 1

Latent class model with covariates in class allocation model

Posted: 10 Feb 2025, 12:58
by malemu588@gmail.com
Dear Stephane,

Thanks for your support in this forum.

I am running a model with many parameters and using Apollo’s search functions for starting values. Models with up to four classes run fine, but with five classes, one parameter (ercr2) has an extreme estimate (-250.99 vs. others mostly below 2), and Apollo reports "Singular Hessian, cannot calculate s.e." The starting values come from Apollo. I tried implementing your forum suggestion by fixing that parameter and re-estimating, but I received the error: Error in dim(dr_vec) <- c(n,p):

I suspect the large number of parameters may be an issue, but I would appreciate any insights. Codes are below. Thanks.

Kind regards,
Mohammed

### Clear memory
rm(list = ls())

### Set your working directory
setwd("C:/Users/zsc245/OneDrive - University of Copenhagen/NOVASOIL/Ferdinand Stuff/Main survey/Main analysis/Final for paper")
getwd()

###Install packages
###install.packages("dplyr")
###library(dplyr)

###install.packages("apollo")

### Load Apollo library
library(apollo)

### Initialise code
apollo_initialise()

### Set core controls
apollo_control = list(
modelName = "LC-MNL",
modelDescr = "LC-MNL-classes",
indivID = "id",
#mixing = TRUE,
#panelData = TRUE,
outputDirectory = "latentMNLout",
nCores = 25
)

# ################################################################# #
#### LOAD DATA AND APPLY ANY TRANSFORMATIONS ####
# ################################################################# #
database = read.csv("Ferd_data_analyse.csv",header=TRUE, na.strings=".")
###database <- as.data.frame(database)
database$female <- ifelse(database$gender==1, 1, 0)
database$educ <- ifelse(database$education==3, 1, 0)
database$supp <- ifelse(database$finansuport==1, 1, 0)
database$envlab <- ifelse(database$envlabel<=4, 1, 0)
database<-subset(database,database$protest==0)
database<-subset(database,database$postc==0)


# ################################################################# #
#### DEFINE MODEL PARAMETERS ####
# ################################################################# #

### Vector of parameters, including any that are kept fixed in estimation
#apollo_beta = model$estimate
apollo_beta=c(ASC1 = -1.7282,
ASC2 = 0.4544,
ASC3 = -2.2466,
ASC4 = -0.7735,
ASC5 = 0.4816,
eros1 = 1.5483,
eros2 = -0.0062,
eros3 = 0.8200,
eros4 = 0.9534,
eros5 = 0.6443,
pest01 = 1.5754,
pest02 = 0.9726,
pest03 = 0.8735,
pest04 = 1.6237,
pest05 = 1.0637,
carb1 = 0.6198,
carb2 = 0.7234,
carb3 = 1.0534,
carb4 = 1.1632,
carb5 = 0.1001,
erps1 = 1.8612,
erps2 = 0.3790,
erps3 = 1.4313,
erps4 = 1.3597,
erps5 = 0.7433,
ercr1 = 1.1524,
ercr2 = -1.9438,
ercr3 = 1.2695,
ercr4 = 1.5886,
ercr5 = 0.4662,
pscr1 = 0.8561,
pscr2 = 1.1112,
pscr3 = 1.1670,
pscr4 = 1.7155,
pscr5 = 0.6849,
psercr1 = 2.1870,
psercr2 = 0.4965,
psercr3 = 1.2782,
psercr4 = 2.0376,
psercr5 = 0.6306,
plant01 = 1.3194,
plant02 = -0.2691,
plant03 = 0.8183,
plant04 = 1.8685,
plant05 = 0.9673,
animal01 = 0.4955,
animal02 = 0.2182,
animal03 = 1.0008,
animal04 = 2.1295,
animal05 = 0.6525,
poll1 = 0.9064,
poll2 = 0.0747,
poll3 = 0.8762,
poll4 = 1.3980,
poll5 = 0.3886,
plantanimal01 = 0.6636,
plantanimal02 = 1.6554,
plantanimal03 = 1.3781,
plantanimal04 = 2.6684,
plantanimal05 = 1.1034,
plantpoll1 = 0.6523,
plantpoll2 = 0.7871,
plantpoll3 = 0.8940,
plantpoll4 = 2.1846,
plantpoll5 = 0.7574,
animalpoll1 = 0.5650,
animalpoll2 = 0.8603,
animalpoll3 = 0.9152,
animalpoll4 = 2.1790,
animalpoll5 = 1.3962,
plantanimalpoll1 = 0.9457,
plantanimalpoll2 = 1.0229,
plantanimalpoll3 = 1.1408,
plantanimalpoll4 = 3.3318,
plantanimalpoll5 = 1.3498,
fund151 = 0.4559,
fund152 = -0.7449,
fund153 = 0.4291,
fund154 = 0.5229,
fund155 = -0.0811,
fund301 = 0.9818,
fund302 = 0.4890,
fund303 = 0.7186,
fund304 = 0.6585,
fund305 = 0.1087,
fund451 = 0.5449,
fund452 = 0.3007,
fund453 = 0.5559,
fund454 = 0.6622,
fund455 = 0.0631,
cost1 = -0.1171,
cost2 = -0.1456,
cost3 = -0.0209,
cost4 = 0.0034,
cost5 = -0.0159,
age1 = 0.0194,
age2 = 0.0317,
age3 = 0.0067,
age4 = 0.0112,
female1 = 0.3495,
female2 = 0.0621,
female3 = 0.1540,
female4 = -0.2613,
educ1 = -0.1476,
educ2 = -0.7284,
educ3 = -0.0264,
educ4 = -0.2999,
env1 = -0.4418,
env2 = -1.3529,
env3 = 0.4687,
env4 = -1.2924,
delta1 = -0.3535,
delta2 = -0.7715,
delta3 = 0.1367,
delta4 = -0.2078)


## Vector with names (in quotes) of parameters to be kept fixed at their starting value in apollo_beta, use apollo_beta_fixed = c() if none
apollo_fixed = c()


# ################################################################# #
#### DEFINE LATENT CLASS COMPONENTS ####
# ################################################################# #

apollo_lcPars = function(apollo_beta, apollo_inputs){
lcpars = list()
lcpars[["b_asc"]] = list(ASC1, ASC2, ASC3, ASC4, ASC5)
lcpars[["b_eros"]] = list(eros1, eros2, eros3, eros4, eros5)
lcpars[["b_pest"]] = list(pest01, pest02, pest03, pest04, pest05)
lcpars[["b_carb"]] = list(carb1, carb2, carb3, carb4, carb5)
lcpars[["b_erps"]] = list(erps1, erps2, erps3, erps4, erps5)
lcpars[["b_ercr"]] = list(ercr1, ercr2, ercr3, ercr4, ercr5)
lcpars[["b_pscr"]] = list(pscr1, pscr2, pscr3, pscr4, pscr5)
lcpars[["b_psercr"]] = list(psercr1, psercr2, psercr3, psercr4, psercr5)
lcpars[["b_plant"]] = list(plant01, plant02, plant03, plant04, plant05)
lcpars[["b_animal"]] = list(animal01, animal02, animal03, animal04, animal05)
lcpars[["b_poll"]] = list(poll1, poll2, poll3, poll4, poll5)
lcpars[["b_plantanimal"]] = list(plantanimal01, plantanimal02, plantanimal03, plantanimal04, plantanimal05)
lcpars[["b_plantpoll"]] = list(plantpoll1, plantpoll2, plantpoll3, plantpoll4, plantpoll5)
lcpars[["b_animalpoll"]] = list(animalpoll1, animalpoll2, animalpoll3, animalpoll4, animalpoll5)
lcpars[["b_plantanimalpoll"]] = list(plantanimalpoll1, plantanimalpoll2, plantanimalpoll3, plantanimalpoll4, plantanimalpoll5)
lcpars[["b_fund15"]] = list(fund151, fund152, fund153, fund154, fund155)
lcpars[["b_fund30"]] = list(fund301, fund302, fund303, fund304, fund305)
lcpars[["b_fund45"]] = list(fund451, fund452, fund453, fund454, fund455)
lcpars[["b_cost"]] = list(cost1, cost2, cost3, cost4, cost5)

V=list()
V[["class_a"]] = delta1 + age1 * age + female1 * female + educ1 * educ + env1 * envlab
V[["class_b"]] = delta2 + age2 * age + female2 * female + educ2 * educ + env2 * envlab
V[["class_c"]] = delta3 + age3 * age + female3 * female + educ3 * educ + env3 * envlab
V[["class_d"]] = delta4 + age4 * age + female4 * female + educ4 * educ + env4 * envlab
V[["class_e"]] = 0

classAlloc_settings = list(
classes = c(class_a=1, class_b=2, class_c=3, class_d=4, class_e=5),
utilities = V
)

lcpars[["pi_values"]] = apollo_classAlloc(classAlloc_settings)

return(lcpars)
}

# ################################################################# #
#### GROUP AND VALIDATE INPUTS ####
# ################################################################# #

apollo_inputs = apollo_validateInputs()
# ################################################################# #
#### DEFINE MODEL AND LIKELIHOOD FUNCTION ####
# ################################################################# #

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()

### Define settings for MNL model component
mnl_settings = list(
alternatives = c(alt1=1, alt2=2, alt3=3),
avail = list(alt1=1, alt2=1, alt3=1),
choiceVar = choice
)

### Loop over classes
for(s in 1:5){

### Compute class-specific utilities
V=list()

V[['alt1']] = (b_eros[[s]] * erosion1 + b_pest[[s]] * pest1 + b_carb[[s]] * carbon1 + b_erps[[s]] * erosionpest1 + b_ercr[[s]] * erosioncarbon1 + b_pscr[[s]] * pestcarbon1
+ b_psercr[[s]] * pesterosioncarbon1 + b_plant[[s]] * plant1 + b_animal[[s]] * animal1 + b_poll[[s]] * pollination1 + b_plantanimal[[s]] * plantanimal1
+ b_plantpoll[[s]] * plantpollinat1 + b_animalpoll[[s]] * animalpolinat1 + b_plantanimalpoll[[s]] * plantanimalpolinat1 + b_fund15[[s]] * fiftper1
+ b_fund30[[s]] * thritper1 + b_fund45[[s]] * fourtfper1 + b_cost[[s]] * price1)
V[['alt2']] = (b_eros[[s]] * erosion2 + b_pest[[s]] * pest2 + b_carb[[s]] * carbon2 + b_erps[[s]] * erosionpest2 + b_ercr[[s]] * erosioncarbon2 + b_pscr[[s]] * pestcarbon2
+ b_psercr[[s]] * pesterosioncarbon2 + b_plant[[s]] * plant2 + b_animal[[s]] * animal2 + b_poll[[s]] * pollination2 + b_plantanimal[[s]] * plantanimal2
+ b_plantpoll[[s]] * plantpollinat2 + b_animalpoll[[s]] * animalpolinat2 + b_plantanimalpoll[[s]] * plantanimalpolinat2 + b_fund15[[s]] * fiftper2
+ b_fund30[[s]] * thritper2 + b_fund45[[s]] * fourtfper2 + b_cost[[s]] * price2)
V[['alt3']] = (b_asc[[s]])

mnl_settings$utilities = V
mnl_settings$componentName = paste0("Class_",s)

### Compute within-class choice probabilities using MNL model
P[[paste0("Class_",s)]] = apollo_mnl(mnl_settings, functionality)

### Take product across observation for same individual
P[[paste0("Class_",s)]] = apollo_panelProd(P[[paste0("Class_",s)]], apollo_inputs ,functionality)

}

### Compute latent class model probabilities
lc_settings = list(inClassProb = P, classProb=pi_values)
P[["model"]] = apollo_lc(lc_settings, apollo_inputs, functionality)


### Prepare and return outputs of function
P = apollo_prepareProb(P, apollo_inputs, functionality)
return(P)

}

### Estimate model
###apollo_beta=apollo_searchStart(apollo_beta, apollo_fixed,apollo_probabilities, apollo_inputs)

###apollo_beta=apollo_searchStart(apollo_beta, apollo_fixed,apollo_probabilities, apollo_inputs)

model = apollo_estimate(apollo_beta, apollo_fixed, apollo_probabilities, apollo_inputs)


RESULTS

Re: Latent class model with covariates in class allocation model

Posted: 27 Feb 2025, 17:37
by stephanehess
Hi

5 classes is almost always too many. Did you look at how the model fit changes when you increase classes?

Stephane

Re: Latent class model with covariates in class allocation model

Posted: 02 Mar 2025, 22:00
by malemu588@gmail.com
Hi Stephane,

Many thanks for your time and response.

The model fit didn’t change much—only around a 1% difference in AIC, BIC, and CAIC—so I decided to keep the model with four classes (see below - D is difference).

Classes #parametrs LL Adjusted R squared BIC AIC CAIC
2 43 -10296.43 0.1924 21000.25 20678.87 20681.37316 DBIC DAIC DCAIC
3 67 -9566.73 0.2477 19768.21 19267.46 19273.48311 5.8667873 6.825372953 6.807526962
4 91 -9375.92 0.2609 19613.96 18933.84 18944.99515 0.780293208 1.731520398 1.704351802
5 115 -9246.55 0.2693 19582.6 18723.09 18741.06616 0.159886122 1.11308641 1.076426726


Kind regards,
Mohammed