## ----setup, include = FALSE--------------------------------------------------- knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 6, fig.height = 4 ) set.seed(20260529) ## ----cran-gate, include = FALSE----------------------------------------------- # The model fits below are evaluated when the article is built locally, in CI # and for the pkgdown site (all of which set NOT_CRAN). CRAN's check farm gives # the whole check a ten-minute budget, which these fits do not fit inside, so # there the code is shown without being run. EVAL_FITS <- identical(Sys.getenv("NOT_CRAN"), "true") knitr::opts_chunk$set(eval = EVAL_FITS) # Read here, not in a gated chunk: chunk options are evaluated whether or not # the chunk runs, so a flag a chunk option reads has to exist either way. has_ggplot <- requireNamespace("ggplot2", quietly = TRUE) ## ----load, message = FALSE---------------------------------------------------- # library(tulpa) ## ----sim-gaussian------------------------------------------------------------- # n <- 400 # x <- rnorm(n) # y <- 0.5 + 1.2 * x + rnorm(n, sd = 0.8) # df <- data.frame(y = y, x = x) ## ----fit-gaussian------------------------------------------------------------- # fit <- tulpa(y ~ x, data = df, family = "gaussian", # mode = "laplace", phi = 0.8) # coef(fit) ## ----summary-gaussian--------------------------------------------------------- # summary(fit) ## ----confint-gaussian--------------------------------------------------------- # confint(fit) ## ----sim-re------------------------------------------------------------------- # g <- factor(sample(1:12, n, replace = TRUE)) # u <- rnorm(12, sd = 0.6) # df$y <- 0.5 + 1.2 * df$x + u[g] + rnorm(n, sd = 0.8) # df$g <- g # # fit_re <- tulpa(y ~ x + (1 | g), data = df, family = "gaussian", # mode = "laplace", sigma_re = 0.6, phi = 0.8) # coef(fit_re) ## ----ranef-------------------------------------------------------------------- # head(ranef(fit_re), 4) ## ----compare------------------------------------------------------------------ # m0 <- tulpa(y ~ 1, data = df, family = "gaussian", mode = "laplace", phi = 0.8) # m1 <- tulpa(y ~ x, data = df, family = "gaussian", mode = "laplace", phi = 0.8) # m2 <- tulpa(y ~ x + (1 | g), data = df, family = "gaussian", # mode = "laplace", sigma_re = 0.6, phi = 0.8) # compare_models(intercept = m0, slope = m1, slope_re = m2, criterion = "loglik") ## ----predict------------------------------------------------------------------ # nd <- data.frame(x = seq(-2, 2, length.out = 50)) # pr <- predict(fit_re, newdata = nd, se.fit = TRUE) # head(pr, 3) ## ----predict-plot, eval = has_ggplot && EVAL_FITS, fig.alt = "Predicted response across x with a 95 percent credible band"---- # library(ggplot2) # ggplot(data.frame(x = nd$x, fit = pr$fit, lo = pr$lower, hi = pr$upper), # aes(x, fit)) + # geom_ribbon(aes(ymin = lo, ymax = hi), fill = "steelblue", alpha = 0.25) + # geom_line(linewidth = 1) + # labs(x = "x", y = "predicted y") + # theme(panel.background = element_rect(fill = "transparent"), # plot.background = element_rect(fill = "transparent")) ## ----families----------------------------------------------------------------- # # Poisson counts # dp <- data.frame(y = rpois(n, exp(0.2 + 0.6 * x)), x = x) # coef(tulpa(y ~ x, data = dp, family = "poisson", mode = "laplace")) # # # Beta proportions in (0, 1), precision phi # mu <- plogis(0.1 + 0.8 * x) # db <- data.frame(y = rbeta(n, mu * 8, (1 - mu) * 8), x = x) # coef(tulpa(y ~ x, data = db, family = "beta", mode = "laplace", phi = 8)) ## ----modes, eval = FALSE------------------------------------------------------ # inference_mode_info() ## ----tiers-------------------------------------------------------------------- # df_b <- df # df_b$y <- rbinom(n, 1, plogis(-0.3 + 1.0 * df$x + u[df$g])) # # fit_lap <- tulpa(y ~ x + (1 | g), data = df_b, family = "binomial", # mode = "laplace", sigma_re = 0.6) # fit_mala <- tulpa(y ~ x + (1 | g), data = df_b, family = "binomial", # mode = "mala", sigma_re = 0.6, # control = list(n_iter = 450, warmup = 150)) ## ----tiers-compare------------------------------------------------------------ # data.frame( # term = names(coef(fit_lap)), # laplace = round(coef(fit_lap), 3), # mala = round(coef(fit_mala), 3) # ) ## ----tier-fields-------------------------------------------------------------- # c(backend = fit_mala$backend, tier = fit_mala$inference_tier) ## ----trace-plot, fig.width = 6, fig.height = 4, fig.alt = "Trace of the MALA chain for the fixed effects"---- # plot(fit_mala, type = "trace") ## ----spatial-build------------------------------------------------------------ # K <- 20 # W <- matrix(0, K, K) # for (i in 1:K) { j <- if (i < K) i + 1 else 1; W[i, j] <- W[j, i] <- 1 } # # region <- factor(sample(1:K, n, replace = TRUE)) # field <- as.numeric(scale(sin(2 * pi * (1:K) / K)))[region] # ds <- data.frame(y = rbinom(n, 1, plogis(-0.2 + 0.7 * x + field)), # x = x, region = region) # # fit_sp <- tulpa(y ~ x + spatial(region), data = ds, family = "binomial", # spatial = list(type = "icar", adjacency = W), # mode = "laplace") # coef(fit_sp) ## ----spatial-plot, eval = has_ggplot && EVAL_FITS, fig.alt = "Estimated spatial field effect per region around the ring"---- # library(ggplot2) # fe <- tail(fit_sp$mode, K) # ggplot(data.frame(region = seq_len(K), effect = fe), aes(region, effect)) + # geom_line(linewidth = 1, colour = "steelblue") + # geom_point() + # labs(x = "region", y = "field effect") + # theme(panel.background = element_rect(fill = "transparent"), # plot.background = element_rect(fill = "transparent")) ## ----temporal-fit------------------------------------------------------------- # Tt <- 30 # time <- sample(seq_len(Tt), n, replace = TRUE) # trend <- 1.2 * sin(2 * pi * seq_len(Tt) / Tt) # xt <- rnorm(n) # dt <- data.frame(y = rpois(n, exp(0.4 + 0.5 * xt + trend[time])), # x = xt, time = time) # # fit_t <- tulpa(y ~ x, data = dt, family = "poisson", # temporal = temporal_rw1("time"), mode = "auto") # coef(fit_t) ## ----temporal-plot, fig.alt = "Estimated temporal random-walk trend against the simulated truth"---- # w <- fit_t$weights / sum(fit_t$weights) # nf <- fit_t$n_fixed # te <- vapply(seq_len(Tt), # function(u) sum(w * fit_t$modes[, nf + u]), numeric(1)) # ggplot(data.frame(time = seq_len(Tt), # est = te - mean(te), # truth = trend - mean(trend)), # aes(time)) + # geom_line(aes(y = truth), linewidth = 1, colour = "grey50") + # geom_line(aes(y = est), linewidth = 1, colour = "steelblue") + # labs(x = "time", y = "trend (centred)") + # theme(panel.background = element_rect(fill = "transparent"), # plot.background = element_rect(fill = "transparent")) ## ----prior-fit---------------------------------------------------------------- # fam <- tulpa_family( # "poisson", # function(eta, params, n_obs, ...) rpois(n_obs, exp(eta[[1]])) # ) # pp <- prior_predict(y ~ x, family = fam, data = dp, n_draws = 50, # priors = tulpa_priors(beta = prior_normal(0, 1))) # pp ## ----prior-plot, fig.alt = "Prior predictive densities of the simulated response under the chosen priors"---- # sims <- data.frame( # value = unlist(pp$y), # draw = factor(rep(seq_along(pp$y), lengths(pp$y))) # ) # ggplot(sims, aes(value, group = draw)) + # geom_density(colour = "steelblue", alpha = 0.3) + # labs(x = "simulated y", y = "density") + # theme(panel.background = element_rect(fill = "transparent"), # plot.background = element_rect(fill = "transparent")) ## ----tgmrf-build, message = FALSE--------------------------------------------- # library(Matrix) # m <- 20 # D2 <- diff(diag(m), differences = 1) # R <- crossprod(D2) # RW1 structure matrix # blk <- tgmrf( # Q = function(theta) as(theta[1] * (R + 1e-4 * diag(m)), "dgCMatrix"), # prior = function(theta) dgamma(theta[1], 2, 1, log = TRUE), # init = c(tau = 1) # ) # blk ## ----tgmrf-plot, fig.alt = "Sparsity pattern of the user-defined RW1 precision matrix"---- # Q1 <- as.matrix(blk$Q(c(tau = 1))) # ix <- which(Q1 != 0, arr.ind = TRUE) # ggplot(data.frame(row = ix[, 1], col = ix[, 2]), aes(col, row)) + # geom_tile(fill = "steelblue") + # scale_y_reverse() + # labs(x = "column", y = "row") + # theme(panel.background = element_rect(fill = "transparent"), # plot.background = element_rect(fill = "transparent")) ## ----tidy--------------------------------------------------------------------- # tidy(fit_re) ## ----glance------------------------------------------------------------------- # glance(fit_mala)[c("n_samples", "logLik", "mean_accept", "n_divergent")]