Skip to content

Commit 18b6d77

Browse files
committed
Add EDF printing to smoothers #383 #387
1 parent 66f7f73 commit 18b6d77

File tree

4 files changed

+48
-9
lines changed

4 files changed

+48
-9
lines changed

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Type: Package
22
Package: sdmTMB
33
Title: Spatial and Spatiotemporal SPDE-Based GLMMs with 'TMB'
4-
Version: 0.6.0.9017
4+
Version: 0.6.0.9018
55
Authors@R: c(
66
person(c("Sean", "C."), "Anderson", , "sean@seananderson.ca",
77
role = c("aut", "cre"),

NEWS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# sdmTMB (development version)
22

3+
* Add EDF (effective degrees of freedom) printing to smoothers with
4+
`print.sdmTMB()` and `summary.sdmTMB()` if argument `edf = TRUE`
5+
is included. E.g. `print.sdmTMB(fit, edf = TRUE)`. #383 #387
6+
37
* Add `cAIC()` for calculating *conditional* AIC. Theory based on
48
<https://arxiv.org/abs/2411.14185>; also see
59
<https://doi.org/10.1002/ecy.4327>. J.T. Thorson wrote the function code.

R/caic.R

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,9 @@ cAIC <- function(object, what = c("cAIC", "EDF"), ...) {
6868

6969
#' @exportS3Method
7070
cAIC.sdmTMB <- function(object, what = c("cAIC", "EDF"), ...) {
71-
what <- match.arg(what)
71+
72+
what <- tolower(what)
73+
what <- match.arg(what, choices = c("caic", "edf"))
7274
what <- tolower(what)
7375
tmb_data <- object$tmb_data
7476

@@ -104,6 +106,10 @@ cAIC.sdmTMB <- function(object, what = c("cAIC", "EDF"), ...) {
104106
p <- length(object$model$par)
105107

106108
## use '-' for Hess because model returns negative loglikelihood
109+
if (is.null(object$tmb_random)) {
110+
cli_inform(c("This model has no random effects.", "cAIC and EDF only apply to models with random effects."))
111+
return(invisible(NULL))
112+
}
107113
Hess_new <- -Matrix::Matrix(obj_new$env$f(parDataMode, order = 1, type = "ADGrad"), sparse = TRUE)
108114
Hess_new <- Hess_new[indx, indx] ## marginal precision matrix of REs
109115

@@ -119,7 +125,21 @@ cAIC.sdmTMB <- function(object, what = c("cAIC", "EDF"), ...) {
119125
return(cAIC_out)
120126
} else if (what == "edf") {
121127
## Figure out group for each random-effect coefficient
122-
group <- factor(names(object$last.par.best[obj$env$random]))
128+
group <- names(object$last.par.best[obj$env$random])
129+
130+
convert_bsmooth2names <- function(object, model = 1) {
131+
sn <- row.names(print_smooth_effects(object, m = model, silent = TRUE)$smooth_sds)
132+
sn <- gsub("^sd", "", sn)
133+
dms <- object$smoothers$sm_dims
134+
unlist(lapply(seq_along(dms), \(i) rep(sn[i], dms[i])))
135+
136+
}
137+
s_groups <- convert_bsmooth2names(object)
138+
# smoothers always shared in delta models
139+
if (is_delta(object)) s_groups <- c(paste0("1LP-", s_groups), paste0("2LP-", s_groups))
140+
group[group == "b_smooth"] <- s_groups
141+
group <- factor(group)
142+
123143
## Calculate total EDF by group
124144
EDF <- tapply(negEDF, INDEX = group, FUN = length) - tapply(negEDF, INDEX = group, FUN = sum)
125145
return(EDF)

R/print.R

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ print_main_effects <- function(x, m = 1) {
8888
mm
8989
}
9090

91-
print_smooth_effects <- function(x, m = 1) {
91+
print_smooth_effects <- function(x, m = 1, edf = NULL, silent = FALSE) {
9292
sr <- x$sd_report
9393
sr_se <- as.list(sr, "Std. Error")
9494
sr_est <- as.list(sr, "Estimate")
@@ -122,7 +122,7 @@ print_smooth_effects <- function(x, m = 1) {
122122
"This does not affect model fitting.",
123123
"We'll use generic covariate names ('scovariate') here intead."
124124
)
125-
cli_warn(msg)
125+
if (!silent) cli_warn(msg)
126126
row.names(mm_sm) <- paste0("scovariate-", seq_len(nrow(mm_sm)))
127127
} else {
128128
mm_sm <- NULL
@@ -136,6 +136,16 @@ print_smooth_effects <- function(x, m = 1) {
136136
re_sm_mat[, 1] <- smooth_sds
137137
rownames(re_sm_mat) <- sm_names_sds
138138
colnames(re_sm_mat) <- "Std. Dev."
139+
140+
if (!is.null(edf)) {
141+
if (is_delta(x)) {
142+
lp_regex <- paste0("^", m, "LP-")
143+
edf <- edf[grepl(lp_regex, names(edf))]
144+
}
145+
edf <- round(edf, 2)
146+
re_sm_mat <- cbind(re_sm_mat, matrix(edf, ncol = 1))
147+
colnames(re_sm_mat)[2] <- "EDF"
148+
}
139149
} else {
140150
re_sm_mat <- NULL
141151
mm_sm <- NULL
@@ -312,10 +322,15 @@ print_header <- function(x) {
312322
cat(info$overall_family)
313323
}
314324

315-
print_one_model <- function(x, m = 1) {
325+
print_one_model <- function(x, m = 1, edf = FALSE, silent = FALSE) {
326+
if (edf) {
327+
.edf <- suppressMessages(cAIC(x, what = "EDF"))
328+
} else {
329+
.edf <- NULL
330+
}
316331
info <- print_model_info(x)
317332
main <- print_main_effects(x, m = m)
318-
smooth <- print_smooth_effects(x, m = m)
333+
smooth <- print_smooth_effects(x, m = m, edf = .edf, silent = silent)
319334
iid_re <- print_iid_re(x, m = m)
320335
tv <- print_time_varying(x, m = m)
321336
range <- print_range(x, m = m)
@@ -381,10 +396,10 @@ print.sdmTMB <- function(x, ...) {
381396
delta <- isTRUE(x$family$delta)
382397
print_header(x)
383398
if (delta) cat("\nDelta/hurdle model 1: -----------------------------------\n")
384-
print_one_model(x, 1)
399+
print_one_model(x, 1, ...)
385400
if (delta) {
386401
cat("\nDelta/hurdle model 2: -----------------------------------\n")
387-
print_one_model(x, 2)
402+
print_one_model(x, 2, ...)
388403
}
389404
if (delta) cat("\n")
390405
print_footer(x)

0 commit comments

Comments
 (0)