Although this vignette is compiled with the version of
spaMM shown below, the included fit results may have been
obtained with a previous version.
This vignette presents an informal tour of using the
pois4mlogit function to fit multinomial-logit models. In
these models, the probabilities \(p_{ic}\) of the different response
categories \(c=1,...,C\) for the \(i\)th multinomial draw \((n_{i1},...,n_{iC})\) are of the form \[p_{ic}=\frac{e^{\eta_{ic}}}{\sum_{c=1}^C
e^{\eta_{ic}}}\] where each \(\eta_{ic}\) takes the usual form of linear
predictors for GL(M)Ms, including fixed and random effects.
This type of models can in principle be fitted as Poisson non-linear
models (Chen and Kuo
2001), as the denominator makes \(\log(p_{ic})\) non-linear, in comparison to
each \(\eta_{ic}\). spaMM
does not have procedures for non-linear models, but the
pois4mlogit function uses surrogate Poisson
multivariate-response GLMMs (with some further adjustments) to fit the
multinomial model, from which it owes its name.
Each response category \(c\) may
depend on distinct effects, so there is one sub-model for each \(c\).
To allow flexible specification of the full statistical model, the
pois4mlogit function inherits the features of the
fitmv function for multivariate-response models (which it
calls), including in particular several features that are illustrated
below: the X2X argument to declare fixed-effect
coefficients shared about sub-models; the treatment of random-effect
terms that appear identical across sub-models as a single random effect;
and the aliases argument that allows the “same” variable
(as it appears in different sub-model formulas; e.g. a grouping variable
in a random effect term) to actually be interpreted as “different”
variables in the data, for the different sub-models.
The above expression for the \(p_{ic}\)s implies that their relative values depend only on the differences between the \(\eta_{ic}\)s, and that adding an intercept in a submodel is equivalent, in terms of relative probabilities \(p_{ic}\), to removing the value of this intercept from all other submodels. Thus the likelihood of a model with \(c\) non-zero intercepts \((I_1, I_2, \ldots, I_c)\) is equivalent to that of a model with intercepts \((0, I_2-I_1, \ldots, I_c-I_1)\), and including distinct non-zero intercepts in all submodels would make the model over-parameterized. This can also be seen by considering a binomial model: it has a single intercept for two response classes. In the above formulation, the binomial model with linear predictor \(\eta_i\) can be represented as the bivariate model with \(\eta_{ic}=\eta_i\) for \(c=1\), and \(\eta_{ic}=0\) (hence, in particular, no intercept) for \(c=2\).
Likewise, including an identical fixed-effect intercept in all submodels has no effect on the likelihood of samples, and would thus also make the model over-parameterized in comparison with a model without any intercept.
X2X argumentFor this first example, we consider a yogurt-marketing example from
the econometric literature on multinomial-logit models (Jain et al. 1994; Chen and Kuo 2001). In this example, the
data represent purchases of four yogurt brands by a panel of 100
households (id=1,…,100). A total of 2,412 purchases are
recorded; the maximum and minimum number of purchases are 185 and 4,
respectively (Chen and Kuo
2001).
chk <- try(load("saved-fits/Iyogurt.rda", envir=globalenv()),silent=TRUE)
if (inherits(chk,"try-error")) {
if (requireNamespace("Ecdat",quietly=TRUE)) {
data(Yogurt,package="Ecdat")
Iyogurt <- Yogurt
Iyogurt$Iweight <- Iyogurt$choice=="weight"
Iyogurt$Idannon <- Iyogurt$choice=="dannon"
Iyogurt$Iyoplait <- Iyogurt$choice=="yoplait"
Iyogurt$Ihiland <- Iyogurt$choice=="hiland"
# save(Iyogurt, file="Iyogurt.rda")
} else {
stop( "'Iyogurt' data not available, cannot run p4m 'Yogurt' test.\n" )
}
}We first fit it by a fixed-effect model with two predictors for each
brand :one representing the price, the the other representing the
“marketing environment” (advertisement for the brand). We impose that
the regression coefficients for these two predictors are identical
across the different brands (i.e., that price and advertizing have
identical effects on buying decisions for the four brands). This
constraint may be specified by the following value for the
X2X argument of the fitting function, which may here be
given as
(x2xfixed <- matrix(c(1,0,0,0,0,0,0,0,0,0,0,
0,0,0,1,0,0,0,0,0,0,0,
0,0,0,0,0,0,1,0,0,0,0,
0,1,0,0,1,0,0,1,0,1,0,
0,0,1,0,0,1,0,0,1,0,1),
ncol=5L, nrow=11L))
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 1 0 0 0 0
#> [2,] 0 0 0 1 0
#> [3,] 0 0 0 0 1
#> [4,] 0 1 0 0 0
#> [5,] 0 0 0 1 0
#> [6,] 0 0 0 0 1
#> [7,] 0 0 1 0 0
#> [8,] 0 0 0 1 0
#> [9,] 0 0 0 0 1
#> [10,] 0 0 0 1 0
#> [11,] 0 0 0 0 1
dimnames(x2xfixed) <- list(NULL, c("(Intercept)_1","(Intercept)_2","(Intercept)_3","feat","price"))but it can also be specified using the genX2X function,
which will build the same matrix when given the appropriate list of name
matches. This avoids filling a matrix of zeroes and ones, with hard to
catch errors.
x2xfixed <- genX2X(list("(Intercept)_1"="(Intercept)_1",
"(Intercept)_2"="(Intercept)_2",
"(Intercept)_3"="(Intercept)_3",
"feat"=c("feat.yoplait_1","feat.dannon_2","feat.weight_3","feat.hiland_4"),
"price"=c("price.yoplait_1","price.dannon_2","price.weight_3","price.hiland_4")))The variable names on the right-hand side of each element of the list
would be the names of the coefficients in a fit of a model without the
X2X argument; their indices
_1,…,_4 refer to the submodels to which the
coefficients belong. Note that the x2xfixed object returned
by genX2X() is not the matrix itself, but stores the
information necessary for the fitting function to build the matrix. The
matrix can be retrieved from the fit by
get_matrix(fit, "X2X").
To fit the model, we add a offset(.dynoffset) term to
each submodel formula. This is used internally to represent the
denominator the the expression for the \(p_{ic}\)s.
chk <- try(load("saved-fits/fefit.rda", envir=globalenv()),silent=TRUE)
if (inherits(chk,"try-error")) {
fefit <- pois4mlogit(
submodels=list(
list(Iyoplait~ offset(.dynoffset)+1+feat.yoplait+price.yoplait, family=poisson(log)),
list(Idannon~ offset(.dynoffset)+1+feat.dannon+price.dannon, family=poisson(log)),
list(Iweight~ offset(.dynoffset)+1+feat.weight+price.weight, family=poisson(log)),
list(Ihiland~ offset(.dynoffset)+0+feat.hiland+price.hiland, family=poisson(log))),
data=Iyogurt,
types=c("Iyoplait","Idannon","Iweight","Ihiland"),
X2X= x2xfixed,
progress=1
)}
fefit
#> formula_1: Iyoplait ~ offset(.dynoffset) + 1 + feat.yoplait + price.yoplait
#> formula_2: Idannon ~ offset(.dynoffset) + 1 + feat.dannon + price.dannon
#> formula_3: Iweight ~ offset(.dynoffset) + 1 + feat.weight + price.weight
#> formula_4: Ihiland ~ offset(.dynoffset) + 0 + feat.hiland + price.hiland
#> Estimation of fixed effects by ML.
#> Families: 1--4 : multinomial-logit
#> ------------ Fixed effects (beta) ------------
#> Estimate Cond. SE t-value
#> (Intercept)_1 4.4502 0.11187 39.782
#> (Intercept)_2 3.7156 0.09084 40.905
#> (Intercept)_3 3.0744 0.09375 32.793
#> feat 0.4914 0.07983 6.156
#> price -0.3666 0.01057 -34.677
#> ------------- Likelihood values -------------
#> logLik
#> logL : -2656.888This result matches the fixed-effect fit in Chen and Kuo (2001),
where the price variable was scaled differently.
Note that as explained in the Introduction, there is no intercept coefficient. Otherwise, the model would be over-parameterized.
aliases argumentFor exposition, we first consider a minimal random-effect model,
indeed the one with only a random intercept for each brand. We
want the brand-specific intercept to be drawn from the same distribution
(with a single variance), so we use a “shared” random effect term, here
(1|BRAND), across the sub-models. But then we need to be
able to specify that the BRAND value is distinct for the
different sub-models, and for this we create new variables in the data,
and refer to them using the aliases argument:
Iyogurt$subd <- "dannon"
Iyogurt$subh <- "hiland"
Iyogurt$subw <- "weight"
Iyogurt$suby <- "yoplait"
chk <- try(load("saved-fits/rdInt.rda", envir=globalenv()),silent=TRUE)
if (inherits(chk,"try-error")) {
rdInt <- pois4mlogit(
submodels=list(
list(Idannon~ offset(.dynoffset)+0+(1|BRAND), family=poisson(log)),
list(Ihiland~ offset(.dynoffset)+0+(1|BRAND), family=poisson(log)),
list(Iweight~ offset(.dynoffset)+0+(1|BRAND), family=poisson(log)),
list(Iyoplait~ offset(.dynoffset)+0+(1|BRAND), family=poisson(log))),
data=Iyogurt,
types=c("Idannon","Ihiland","Iweight","Iyoplait"),
aliases=list(BRAND=c("subd","subh","subw","suby"))
)}The aliases value implies that the grouping variable
BRAND takes the value of the variable subh in
the second submodel, i.e. the value "hiland". By the same
effect, it takes value "weight" in the third and
"yoplait" in the fourth. The BRAND term is
thus interpreted as a factor taking three different level for three
submodels. BRAND is not a variable present
in the data, and if it were present, it would be ignored by
the fitting procedure.
We can also that the syntax had the correct effect by looking at the
incidence matrix for the random effect, which has four blocks of rows
(one four each sub-model), four columns (one for each level), with
1s (appearing in black) on the right places:
In contrast to the fixed-effect model, where we would have created an unidentifiable model if we had included distinct intercepts in all four submodels, we have included the random intercept in all four submodel, which still makes an identifiable model. In comparison to the fixed-effect model, if we add the same constant to all random-effect values, this will not affect the relative values of the probabilities \(p_{ic}\) conditional on the random effect values, but this will change the probabilities of the random-effect values as drawn from a gaussian distribution, and thus this will change the marginal likelihood.
Including the random effect in all submodels has at least the benefit of avoiding the asymmetry introduced by selecting one submodel as the reference one without this effect. Here, a comparison with the fit of the model without the random effect in the first submodel would also show that the latter has a slightly lower likelihood.