Hi,
I have a follow up question.
I am trying to fit a joint model in INLA that includes spatial random effects with gaussian outcome (blood measurements) and a censored gaussian outcome (soil measurements).
However, when I try to combine my stacks using:
> stk_joint <- inla.stack(stk_blood, stk_soil)
Error in `dplyr::bind_rows()`:
! Can't combine `..1$y.1` <double> and `..2$y.1` <list>.
Run `rlang::last_trace()` to see where the error occurred.I get an error. From what I understand, there is a mismatch in the structure of the response variables between the two stacks (numeric vs list via inla.surv).
I'm using the Rcode below with simulated data to check my method implementation.
Any suggestions on how to circumvent this problem?
# ------------------------------------------------------------------------------
#
# JOINT SPATIAL MODEL:
# log(soil) = intercept_soil + w(s)
# log(blood) = intercept
# + fixed covariates (gender, age, education, eggs)
# + beta_scaling * w(s) [baseline spatial field]
# + beta_eggs * lok_ei * w(s) [egg x space interaction]
# ------------------------------------------------------------------------------
rm(list = ls())
### Load packages
#----------------
library(INLA)
library(sp)
library(ggplot2)
library(gridExtra)
library(tidyr)
library(patchwork)
### Set seed
#-----------
set.seed(42)
# ------------------------------------------------------------------------------
# 1. SPATIAL DOMAIN & MESH
# ------------------------------------------------------------------------------
domain_size <- 100000
n_soil <- 3000
n_blood <- 500
soil_xy <- data.frame(
x = runif(n_soil, 0, domain_size),
y = runif(n_soil, 0, domain_size)
)
blood_xy <- data.frame(
X = runif(n_blood, 0, domain_size),
Y = runif(n_blood, 0, domain_size)
)
mesh <- inla.mesh.2d(
loc = rbind(as.matrix(soil_xy), as.matrix(blood_xy)),
max.edge = c(15000, 40000),
cutoff = 3000,
offset = c(10000, 30000)
)
# ------------------------------------------------------------------------------
# 2. TRUE PARAMETER VALUES
# ------------------------------------------------------------------------------
### Spatial field
#----------------
true_range_w <- 30000
true_sigma_w <- 1.2
### Copy scalings
#----------------
true_beta_blood <- 0.8
true_beta_eggs <- 0.9
### Fixed effects
#----------------
true_intercept_soil <- 2.0
true_intercept_blood <- 1.4
true_beta_gender <- 0.3
true_beta_age2 <- 0.2
true_beta_age3 <- 0.5
true_beta_educ2 <- -0.1
true_beta_educ3 <- 0.3
true_beta_eggs_fix <- 0.4
### Observation noise
#--------------------
true_sigma_obs_soil <- 0.1
true_sigma_obs_blood <- 0.1
# ------------------------------------------------------------------------------
# 3. SIMULATE SPATIAL FIELD
# ------------------------------------------------------------------------------
## Define projection matrices
#----------------------------
A_soil <- inla.spde.make.A(mesh, loc = as.matrix(soil_xy))
A_blood <- inla.spde.make.A(mesh, loc = as.matrix(blood_xy))
### Define SPDE object
#---------------------
spde_sim_obj <- inla.spde2.pcmatern(mesh = mesh,
prior.range = c(true_range_w, 0.5),
prior.sigma = c(true_sigma_w, 0.5))
### Precision matrix
#-------------------
Q_sim <- inla.spde2.precision(spde_sim_obj,
theta = c(log(true_range_w), log(true_sigma_w)))
### Sample on MESH nodes
#-----------------------
w_true_nodes <- as.vector(inla.qsample(n = 1, Q = Q_sim))
### Project to observation locations
#-----------------------------------
field_w_soil <- as.vector(A_soil %*% w_true_nodes)
field_w_blood <- as.vector(A_blood %*% w_true_nodes)
#-------------------------------------------------------------------------------
# 4. SIMULATE COVARIATES
#-------------------------------------------------------------------------------
soil <- data.frame(
x_ml72 = soil_xy$x,
y_ml72 = soil_xy$y
)
blood <- data.frame(
X = blood_xy$X,
Y = blood_xy$Y,
Gender = rbinom(n_blood, 1, 0.5),
age = sample(1:3, n_blood, replace = TRUE),
isced_hh = sample(1:3, n_blood, replace = TRUE),
lok_ei = rbinom(n_blood, 1, 0.5)
)
#-------------------------------------------------------------------------------
# 5. SIMULATE OUTCOMES
#-------------------------------------------------------------------------------
### Soil
#-------
log_soil <- true_intercept_soil + field_w_soil + rnorm(n_soil, 0, true_sigma_obs_soil)
log_soil <- pmax(exp(log_soil), 1) # apply the censoring
idx <- which(log_soil <= 1) # select the censored ones
e <- rep(1, n_soil) # 1 for uncensored
e[idx] <- 2 # 2 for censored
YY <- inla.surv(time = log_soil, event = e) # create INLA survival object
### Blood
#--------
age_b <- ifelse(blood$age == 2, true_beta_age2,
ifelse(blood$age == 3, true_beta_age3, 0))
edu_b <- ifelse(blood$isced_hh == 2, true_beta_educ2,
ifelse(blood$isced_hh == 3, true_beta_educ3, 0))
log_blood <- true_intercept_blood +
true_beta_gender * blood$Gender +
age_b + edu_b +
true_beta_eggs_fix * blood$lok_ei +
true_beta_blood * field_w_blood +
true_beta_eggs * blood$lok_ei * field_w_blood +
rnorm(n_blood, 0, true_sigma_obs_blood)
blood$pfos <- exp(log_blood)
#-------------------------------------------------------------------------------
# 6. FIT THE JOINT MODEL
#-------------------------------------------------------------------------------
spde <- inla.spde2.pcmatern(
mesh,
prior.range = c(10000, 0.1),
prior.sigma = c(1, 0.05)
)
idx_w <- inla.spde.make.index("idx_w", spde$n.spde)
idx_w_bld <- inla.spde.make.index("idx_w_bld", spde$n.spde)
idx_w_egg <- inla.spde.make.index("idx_w_egg", spde$n.spde)
A_eggs <- inla.spde.make.A(
mesh,
loc = as.matrix(cbind(blood$X, blood$Y)),
weights = blood$lok_ei
)
stk_blood <- inla.stack(
data = list(y = cbind(log(blood$pfos), NA)),
A = list(A_blood, A_eggs, 1),
effects = list(
idx_w_bld,
idx_w_egg,
data.frame(
Intercept.blood = 1,
Gender = blood$Gender,
Eggs = blood$lok_ei,
Age2 = as.integer(blood$age == 2),
Age3 = as.integer(blood$age == 3),
Education2 = as.integer(blood$isced_hh == 2),
Education3 = as.integer(blood$isced_hh == 3)
)
),
tag = "blood"
)
# !!! ERROR with the SURVIVAL OBJECT as LIST !!!
# NA changed to --> rep(NA,n_soil)
stk_soil <- inla.stack(
data = list(y = cbind(rep(NA,n_soil), YY)),
A = list(A_soil, 1),
effects = list(
idx_w,
data.frame(
Intercept.soil = rep(1, n_soil)
)
),
tag = "soil"
)
# !!! ERROR when joining the two inla.stack !!!
# ???
stk_joint <- inla.stack(stk_blood, stk_soil)
formula_joint <- y ~ -1 +
Intercept.soil + Intercept.blood +
Gender + Age2 + Age3 + Eggs + Education2 + Education3 +
f(idx_w, model = spde) +
f(idx_w_bld, copy = "idx_w", fixed = FALSE) +
f(idx_w_egg, copy = "idx_w", fixed = FALSE)
res <- inla(
formula_joint,
family = c("gaussian", "lognormalsurv"),
data = inla.stack.data(stk_joint),
control.predictor = list(A = inla.stack.A(stk_joint), compute = TRUE),
control.compute = list(dic = TRUE, waic = TRUE, config = TRUE)
)
#-------------------------------------------------------------------------------
# 7. VALIDATION
#-------------------------------------------------------------------------------
summary(res)
Kind regards,
Jonas
Op dinsdag 5 mei 2026 om 10:17:38 UTC+2 schreef Jonas: