JenniNiku / gllvm

Generalized Linear Latent Variable Models

Home Page:https://jenniniku.github.io/gllvm/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Parameter constrains for Gaussian niche modelling

hrlai opened this issue · comments

Hi dev! I am using gllvm to fit Gaussian/bell-shaped niche to my taxa. Because the count data takes log-link, I simply fitted quadratic terms in addition to first-order terms for the measured covariates. However, because I did not constrain the first-order coefficients to be positive and the second-order coefficients to be negative, sometimes I ended up with a weakly U-shape curve for some taxa. Is it possible to constrain the coefficients so that the fitted lines are inverted U, if not very flat? I understand that you have implemented this to the latent variable (latent niche models), but I couldn't find how to do the same for the measured covariates...

commented

Thanks for your question!

No, I have not added sign constraints for parameters of fixed-effects. It is something I did consider once upon a time, and then did not implement. Please note that you do not need sign constraints for the coefficients of the linear term to get to a bell-shaped function, only on the quadratic term.

Having said that, I recently implemented model-based constrained ordination, which is possible to combine with the quadratic response model, so that it does implement sign constraints for the quadratic term, but in reduced rank (the num.RR argument in the gllvm() function. For num.RR equal to the number of predictors this model has slightly more parameters than what you are asking for). This will give an ordination somewhat similar to Canonical Correspondence Analysis, but with common/unequal tolerances instead. Do note that that class of models is still under peer-review. You can find a little information on the vignette: https://jenniniku.github.io/gllvm/articles/vignette6.html.

Constrained ordination is quite prone to overfitting and numerical issues with the quadratic response model (considerably more than with unconstrained ordination it seems so far), so please use with caution, and frequent re-fitting with different initial values to ensure an optimal solution.

The code for such a model would be along the lines of:

data(spider)
y <- spider$abund
X <- spider$x

model <- gllvm(y = y, X = spider$x, 
               lv.formula = ~soil.dry + bare.sand, num.RR = 2, num.lv = 2,
               family = "poisson", quadratic = TRUE, starting.val = "res", n.init = 5)

However, in general I am in favor of num.RR < ncol(X) as to have fewer parameters included for the fixed-effects than in a multivariate GLM (i.e., instead include all predictors in the fixed-effects term, but keep num.RR relatively low). In essence, the above model performs a constrained (fixed-effects term) and residual (num.lv) ordination, where the latter accounts for information not accounted for by the former. Please take a moment to realize that this model is quite complex, and it will require a large amount of information to successfully estimate all parameters.

Feel free to let me know if you have any other questions, and I will try to help out as best as I can.

Thanks a lot @BertvanderVeen and sorry for the long silence. Was figuring how things work here. I am still figuring out the best way to avoid overparameterisation and getting good residual plots (currently following your suggestion to have num.RR < ncol(X) instead of the one in the original question.) Meanwhile, could you teach me how to make counterfactual predictions using new data? Looking at ?predict.gllvm I guess I need to use the newLV argument, but the output seems funny (all flat lines which seem to be intercept only).

Maybe this helps, using the example you showed above:

newLV <- data.frame(LV1 = seq(-2, 2, length.out = 100),
                    LV2 = 0)
yhat <- predict(model,
                newLV = newLV,
                type = "response",
                level = 0)
matplot(yhat, type = "l", lty = 1, col = 1, log = "y")
commented

I would like to help, but I am still not quite familiar with the term "counterfactual", so that I need some more details on what you are trying to do.

Note that when specifying level = 0 the model excludes the (random-effects) latent variables from the prediction entirely. I.e. if you want to simulated over the random-effects, you need to use level = 1. If there are no fixed-effects in the model present as in a model-based unconstrained ordination, then naturally level = 0 would result in a prediction only due to the intercept. Perhaps this is not completely obvious from the documentation (which says that level = 1 always uses the predicted site scores, which is not accurate I think @JenniNiku).

If your model does include fixed-effects and you are still getting intercept-only predictions, something is wrong that I will need to look at in more detail. In that case, please provide me with some details on the exact model that you are fitting (i.e. the code).

Fancy seeing you here Hao Ran! It's a small world haha =P

@BertvanderVeen is correct with what he mentions about the use level. But I suspect there may be a deeper problem with the implementation because predict.gllvm relies on matching the dimensions of the LV and theta matrix, and the latter is not a simply a matrix equal to the number of LVs when reduced-rank regression is in play. However Bert and Jenni might already be on top of that!

Ironically, I do not think it would not be too hard to solve your original problem in boral Hao Ran, by setting up priors so that the quadratic coefficients are constrained to be negative. It would still take a bit of work though...

Hi. Actually level=0 never tries to use predicted site scores and neither given 'newLV'. level=1 attempts to use model's predicted site scores or given 'newLV', so in that sense the description is not accurate. I think this works as it should be for intercept only model:
model <- gllvm(y = y, num.lv = 2, family = "poisson")
newLV <- data.frame(LV1 = seq(-2, 2, length.out = 100), LV2 = 0)
yhat <- predict(model, newLV = newLV, type = "response", level = 1)

If X-covariates are included in the model, this does not work unless you define X values to those 100 lv values and give it as 'newX' to predict(), so that it has same number of rows as 'newLV' has.

However, I noticed that even that does not work for reduced rank model atm, so there is a bug or some explanation should be given.

commented

Woops, that was a typo in my comment. The second level = 0 should say level = 1 (now edited above to be correct). I can look into the bug, or will you look into it with the changes you are currently making @JenniNiku?

Fancy seeing you here Hao Ran! It's a small world haha =P

@BertvanderVeen is correct with what he mentions about the use level. But I suspect there may be a deeper problem with the implementation because predict.gllvm relies on matching the dimensions of the LV and theta matrix, and the latter is not a simply a matrix equal to the number of LVs when reduced-rank regression is in play. However Bert and Jenni might already be on top of that!

Ironically, I do not think it would not be too hard to solve your original problem in boral Hao Ran, by setting up priors so that the quadratic coefficients are constrained to be negative. It would still take a bit of work though...

There is some code in place to make this happen, i.e. checking if (num.RR + num.lv.c)>0&num.lv>0, then modify theta. But apparently that needs some more work.

I can take a look at it next week.

The predict should work now for RR model as well in the github version of the package. For example:

data(spider)
y <- spider$abund
X <- spider$x
model <- gllvm(y = y, X = spider$x, 
               lv.formula = ~soil.dry + bare.sand, num.RR = 1, num.lv = 1,
               family = "poisson", quadratic = TRUE, starting.val = "res", n.init = 5)
newLV <- data.frame(LV1 = seq(-2, 2, length.out = 100))
newX<- data.frame(soil.dry=rep(X[1,1],100), 
  bare.sand=rep(X[1,2],100))
yhat <- predict(model, newLV = newLV, newX = newX, type = "response", level = 1)