E argument ignored (becomes NULL) when inla() is called inside a function with inla.stack

21 views
Skip to first unread message

Cécile Souty

unread,
Apr 23, 2026, 4:36:48 AM (4 days ago) Apr 23
to R-inla discussion group

Hi,

I am experiencing a strange behavior where the E argument passed to inla() (using inla.stack) is silently ignored when the call is wrapped inside a function, leading to completely different model results.

R version: 4.5.2
INLA version: 25.10.19

Reproducible (fake) example with iris data:

library(INLA)

generate_formula = function() {
  formule = X ~ Y1 + Y2
  return(formule)
}

# Case 1: inla() called INSIDE a function
run_inla = function(dd) {
  model_stack = inla.stack(
    data = list(X = dd$Sepal.Length, E = dd$Petal.Width),
    A = 1,
    effects = list(Y1 = dd$Sepal.Width, Y2 = dd$Petal.Length),
    tag = 'est')
  formule = generate_formula() 

  print('summary(inla.stack.data(model_stack)$E)')
  print(summary(inla.stack.data(model_stack)$E)) # to check E values
  res = inla(formule,
    data = inla.stack.data(model_stack),
    family = list("xpoisson"),
    E = inla.stack.data(model_stack)$E,
    control.predictor = list(A = inla.stack.A(model_stack), compute = TRUE, link = 1),
    control.compute = list(dic = TRUE, waic = TRUE, config = TRUE))
  return(res)
}

res = run_inla(dd = iris)
res$.args$E        # NULL  : E is lost
res$summary.fixed  # wrong estimates

# Case 2: identical inla call OUTSIDE the function
model_stack2 = inla.stack(
  data = list(X = iris$Sepal.Length, E = iris$Petal.Width),
  A = 1,
  effects = list(Y1 = iris$Sepal.Width, Y2 = iris$Petal.Length),
  tag = 'est')
formule2 = generate_formula()
res2 = inla(formule2,
  data = inla.stack.data(model_stack2),
  family = list("xpoisson"),
  E = inla.stack.data(model_stack2)$E,
  control.predictor = list(A = inla.stack.A(model_stack2), compute = TRUE, link = 1),
  control.compute   = list(dic = TRUE, waic = TRUE, config = TRUE))

res2$.args$E        # correct: 150 values
res2$summary.fixed  # correct estimates


Then, we observed that res$.args$E is NULL, so in this case E is not applied to the model, but res2$.args$E contains the correct 150 values.

The problem persists regardless of how E is passed: extracting it beforehand into a local variable, passing it directly as iris$Petal.Width, or using a different column name in the stack.

I suspect this is an environment issue in the way inla() evaluates the E argument internally, possibly using a different environment or mechanism than the one used to evaluate the rest of the data argument (?). 

Is this a known issue?

Thank you for your answer !

Cécile

Cécile Souty

unread,
Apr 23, 2026, 4:49:23 AM (4 days ago) Apr 23
to R-inla discussion group
I have narrowed down the problem further. The issue only occurs when the formula is generated by an external function (generate_formula()). When the formula is defined directly inside run_inla(), E is correctly passed even when inla() is called from within a function.

An updated reproducible (fake) example with iris data:


generate_formula = function() { formule = X ~ Y1 + Y2 return(formule) } run_inla = function(dd, formula_function) { model_stack = inla.stack( data = list(X = dd$Sepal.Length, E = dd$Petal.Width), A = 1, effects = list(Y1 = dd$Sepal.Width, Y2 = dd$Petal.Length), tag = 'est') if (formula_function) { formule = generate_formula() # formula from external function } else { formule = X ~ Y1 + Y2 # formula defined inline } res = inla(formule, data = inla.stack.data(model_stack), family = list("xpoisson"), E = inla.stack.data(model_stack)$E, control.predictor = list(A = inla.stack.A(model_stack), compute = TRUE, link = 1), control.compute = list(dic = TRUE, waic = TRUE, config = TRUE)) return(res) } run_inla(dd = iris, formula_function = TRUE)$.args$E # NULL run_inla(dd = iris, formula_function = FALSE)$.args$E # correct

So the root cause seems to be related to the environment attached to the formula object. When the formula is created by the function generate_formula(), it carries the environment of that function (?) When inla() then tries to evaluate E using that formula's environment as context, it apparently cannot resolve the expression correctly and silently falls back to NULL.

Cécile

Havard Rue

unread,
Apr 23, 2026, 5:24:00 AM (4 days ago) Apr 23
to R-inla discussion group, Cécile Souty
Thank you, very useful. 

-- 
Håvard Rue 
--
You received this message because you are subscribed to the Google Groups "R-inla discussion group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to r-inla-discussion...@googlegroups.com.
To view this discussion, visit https://groups.google.com/d/msgid/r-inla-discussion-group/2fab8eef-4d14-44a4-8bae-633221e4e77fn%40googlegroups.com.

Finn Lindgren

unread,
Apr 23, 2026, 7:37:37 AM (4 days ago) Apr 23
to Havard Rue, R-inla discussion group, Cécile Souty
That formula generator function should probably set the formula environment to that of its calling environment, so that it acts the same as a regular formula definition.

In inlabru ive added support for more general "tidy eval" semantics, which allows it to evaluate things like E in a combined context of formula environment and data objects.
Inlabru exists partly to avoid the need for inla.stack in external code, and there are now even features in inla that will allow us to not even call inla.stack internally in inlabru.
At keast your example code would be much simpler with inlabru already, as you can remove everything to do with inla.stack from your code...

Finn

Finn Lindgren
email: finn.l...@gmail.com

Finn Lindgren

unread,
Apr 23, 2026, 7:45:20 AM (4 days ago) Apr 23
to Havard Rue, R-inla discussion group, Cécile Souty
Note: at some point i tried to solve these cases in the inla code itself, but gave up due to the messiness of R's lazy evaluation logic. The rlang tidy eval system was the only thing that worked, and that made it easy to handle throigh inlabru's own need for forwarding expressions to other functions.

Finn Lindgren
email: finn.l...@gmail.com

Elias T. Krainski

unread,
Apr 23, 2026, 7:53:02 AM (4 days ago) Apr 23
to R-inla discussion group
Thus, there are three solutions (at least):

1. Use inlabru 
2. Let E be collected from the data supplied to inla():
      E                 = E, ##  inla.stack.data(model_stack)$E
3. If you have to collect E after applying twice inla.stack.data(), add 
    environment(formule) <- parent.frame()
at the end of the generate_formula()

Elias

Elias T. Krainski

unread,
Apr 23, 2026, 8:05:55 AM (4 days ago) Apr 23
to R-inla discussion group
A big advantage of using inlabru is that it separates the data model definition. So, you can have the data model as an object and apply/fit different model components to it without the need to re-evaluate the data model multiple times. E.g.:

library(inlabru)

dm <- bru_obs(
    Y ~ .,
    family = 'xpoisson',
    E = E,
    data = data.frame(
        Y = iris$Sepal.Length,
        E = iris$Petal.Width,
        X1 = iris$Sepal.Width,
        X2 = iris$Petal.Width
    )
)

mcomps <- list(~ Intercept(1), ~ X1, ~ X2, ~ X1 + X2)

fits <- lapply(mcomps, bru, dm)

sapply(fits, function(x) x$mlik)

lapply(fits, function(x) x$summary.fix)

Cécile Souty

unread,
Apr 23, 2026, 12:14:38 PM (4 days ago) Apr 23
to R-inla discussion group

Many thanks to all of you for your explanations and solutions. I will look into inlabru in more detail.

Indeed, let E be collected from the data supplied to inla works very well : "E = E" instead of "E = inla.stack.data(model_stack)$E"

Thanks again to everyone who contributed !

Cécile

Reply all
Reply to author
Forward
0 new messages