Skip to content

Commit 094edcc

Browse files
committed
Use insight::get_model()
1 parent ad5c2be commit 094edcc

File tree

6 files changed

+232
-145
lines changed

6 files changed

+232
-145
lines changed

R/estimate_contrast_methods.R

Lines changed: 159 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
#' @export
2-
estimate_contrasts.estimate_predicted <- function(model,
3-
contrast = NULL,
4-
by = NULL,
5-
predict = "response",
6-
ci = 0.95,
7-
p_adjust = "none",
8-
comparison = "pairwise",
9-
verbose = TRUE,
10-
...) {
2+
estimate_contrasts.estimate_predicted <- function(
3+
model,
4+
contrast = NULL,
5+
by = NULL,
6+
predict = "response",
7+
ci = 0.95,
8+
p_adjust = "none",
9+
comparison = "pairwise",
10+
verbose = TRUE,
11+
...
12+
) {
1113
# sanity check
1214
if (inherits(comparison, "formula")) {
1315
comparison <- all.vars(comparison)[1]
@@ -20,7 +22,7 @@ estimate_contrasts.estimate_predicted <- function(model,
2022
# the "model" object is an object of class "estimate_predicted", we want
2123
# to copy that into a separate object, for clearer names
2224
predictions <- object <- model
23-
model <- attributes(object)$model
25+
model <- insight::get_model(object)
2426
datagrid <- attributes(object)$datagrid
2527

2628
# sanity check - user-defined by-variables may not be in the data
@@ -80,12 +82,7 @@ estimate_contrasts.estimate_predicted <- function(model,
8082
se_from_predictions <- tryCatch(
8183
{
8284
# arguments for predict(), to get SE on response scale for non-Gaussian models
83-
my_args <- list(
84-
model,
85-
newdata = datagrid,
86-
type = predict,
87-
se.fit = TRUE
88-
)
85+
my_args <- list(model, newdata = datagrid, type = predict, se.fit = TRUE)
8986
# for mixed models, need to set re.form to NULL or NA
9087
if (insight::is_mixed_model(model)) {
9188
my_args$re.form <- NULL
@@ -120,6 +117,7 @@ estimate_contrasts.estimate_predicted <- function(model,
120117
}
121118

122119
# compute contrasts or comparisons
120+
# fmt: skip
123121
out <- switch(comparison,
124122
pairwise = .compute_comparisons(predictions, dof, vcov_matrix, datagrid, contrast, by, crit_factor),
125123
interaction = .compute_interactions(predictions, dof, vcov_matrix, datagrid, contrast, by, crit_factor)
@@ -143,7 +141,9 @@ estimate_contrasts.estimate_predicted <- function(model,
143141
to_remove <- unique(out[[i]])
144142
for (j in to_remove) {
145143
if (all(c("Level1", "Level2") %in% colnames(out))) {
144+
# fmt: skip
146145
levels(out$Level1) <- insight::trim_ws(gsub(j, "", levels(out$Level1), fixed = TRUE))
146+
# fmt: skip
147147
levels(out$Level2) <- insight::trim_ws(gsub(j, "", levels(out$Level2), fixed = TRUE))
148148
} else if ("Parameter" %in% colnames(out)) {
149149
out$Parameter <- insight::trim_ws(gsub(j, "", out$Parameter, fixed = TRUE))
@@ -181,7 +181,15 @@ estimate_contrasts.estimate_predicted <- function(model,
181181

182182

183183
# pairwise comparisons ----------------------------------------------------
184-
.compute_comparisons <- function(predictions, dof, vcov_matrix, datagrid, contrast, by, crit_factor) {
184+
.compute_comparisons <- function(
185+
predictions,
186+
dof,
187+
vcov_matrix,
188+
datagrid,
189+
contrast,
190+
by,
191+
crit_factor
192+
) {
185193
# we need the focal terms and all unique values from the datagrid
186194
focal_terms <- unique(c(contrast, by))
187195

@@ -254,54 +262,57 @@ estimate_contrasts.estimate_predicted <- function(model,
254262
}
255263

256264
# now we iterate over all pairs and try to find the corresponding predictions
257-
out <- do.call(rbind, lapply(seq_len(nrow(pairs_data[[1]])), function(i) {
258-
pos1 <- predictions[[focal_terms[1]]] == pairs_data[[1]][i, 1]
259-
pos2 <- predictions[[focal_terms[1]]] == pairs_data[[2]][i, 1]
260-
261-
# for all focal terms, make sure we only keep the matching pairs
262-
if (length(focal_terms) > 1) {
263-
for (j in 2:length(focal_terms)) {
264-
pos1 <- pos1 & predictions[[focal_terms[j]]] == pairs_data[[1]][i, j]
265-
pos2 <- pos2 & predictions[[focal_terms[j]]] == pairs_data[[2]][i, j]
265+
out <- do.call(
266+
rbind,
267+
lapply(seq_len(nrow(pairs_data[[1]])), function(i) {
268+
pos1 <- predictions[[focal_terms[1]]] == pairs_data[[1]][i, 1]
269+
pos2 <- predictions[[focal_terms[1]]] == pairs_data[[2]][i, 1]
270+
271+
# for all focal terms, make sure we only keep the matching pairs
272+
if (length(focal_terms) > 1) {
273+
for (j in 2:length(focal_terms)) {
274+
pos1 <- pos1 & predictions[[focal_terms[j]]] == pairs_data[[1]][i, j]
275+
pos2 <- pos2 & predictions[[focal_terms[j]]] == pairs_data[[2]][i, j]
276+
}
266277
}
267-
}
268-
# once we have found the correct rows for the pairs, we can calculate
269-
# the contrast. We need the predicted values first
270-
predicted1 <- predictions$Predicted[pos1]
271-
predicted2 <- predictions$Predicted[pos2]
272-
273-
# we then create labels for the pairs. "result" is a data frame with
274-
# the labels (of the pairwise contrasts) as columns.
275-
result <- data.frame(
276-
Parameter = paste(
277-
paste0("(", paste(pairs_data[[1]][i, contrast_pos], collapse = " "), ")"),
278-
paste0("(", paste(pairs_data[[2]][i, contrast_pos], collapse = " "), ")"),
279-
sep = "-"
280-
),
281-
stringsAsFactors = FALSE
282-
)
283-
# we then add the contrast and the standard error. for linear models, the
284-
# SE is sqrt(se1^2 + se2^2).
285-
result$Difference <- predicted1 - predicted2
286-
# sum of squared standard errors
287-
sum_se_squared <- predictions$SE[pos1]^2 + predictions$SE[pos2]^2
288-
# for non-Gaussian models, we subtract the covariance of the two predictions
289-
# but only if the vcov_matrix is not NULL and has the correct dimensions
290-
correct_row_dims <- nrow(vcov_matrix) > 0 && all(nrow(vcov_matrix) >= which(pos1))
291-
correct_col_dims <- ncol(vcov_matrix) > 0 && all(ncol(vcov_matrix) >= which(pos2))
292-
if (is.null(vcov_matrix) || !correct_row_dims || !correct_col_dims) {
293-
vcov_sub <- 0
294-
} else {
295-
vcov_sub <- vcov_matrix[which(pos1), which(pos2)]^2
296-
}
297-
# Avoid negative values in sqrt()
298-
if (vcov_sub >= sum_se_squared) {
299-
result$SE <- sqrt(sum_se_squared)
300-
} else {
301-
result$SE <- sqrt(sum_se_squared - vcov_sub)
302-
}
303-
result
304-
}))
278+
# once we have found the correct rows for the pairs, we can calculate
279+
# the contrast. We need the predicted values first
280+
predicted1 <- predictions$Predicted[pos1]
281+
predicted2 <- predictions$Predicted[pos2]
282+
283+
# we then create labels for the pairs. "result" is a data frame with
284+
# the labels (of the pairwise contrasts) as columns.
285+
result <- data.frame(
286+
Parameter = paste(
287+
paste0("(", paste(pairs_data[[1]][i, contrast_pos], collapse = " "), ")"),
288+
paste0("(", paste(pairs_data[[2]][i, contrast_pos], collapse = " "), ")"),
289+
sep = "-"
290+
),
291+
stringsAsFactors = FALSE
292+
)
293+
# we then add the contrast and the standard error. for linear models, the
294+
# SE is sqrt(se1^2 + se2^2).
295+
result$Difference <- predicted1 - predicted2
296+
# sum of squared standard errors
297+
sum_se_squared <- predictions$SE[pos1]^2 + predictions$SE[pos2]^2
298+
# for non-Gaussian models, we subtract the covariance of the two predictions
299+
# but only if the vcov_matrix is not NULL and has the correct dimensions
300+
correct_row_dims <- nrow(vcov_matrix) > 0 && all(nrow(vcov_matrix) >= which(pos1))
301+
correct_col_dims <- ncol(vcov_matrix) > 0 && all(ncol(vcov_matrix) >= which(pos2))
302+
if (is.null(vcov_matrix) || !correct_row_dims || !correct_col_dims) {
303+
vcov_sub <- 0
304+
} else {
305+
vcov_sub <- vcov_matrix[which(pos1), which(pos2)]^2
306+
}
307+
# Avoid negative values in sqrt()
308+
if (vcov_sub >= sum_se_squared) {
309+
result$SE <- sqrt(sum_se_squared)
310+
} else {
311+
result$SE <- sqrt(sum_se_squared - vcov_sub)
312+
}
313+
result
314+
})
315+
)
305316
# add CI and p-values
306317
out$CI_low <- out$Difference - stats::qt(crit_factor, df = dof) * out$SE
307318
out$CI_high <- out$Difference + stats::qt(crit_factor, df = dof) * out$SE
@@ -313,10 +324,13 @@ estimate_contrasts.estimate_predicted <- function(model,
313324
idx <- rep_len(TRUE, nrow(out))
314325
for (filter_by in by) {
315326
# create index with "by" variables for each comparison pair
316-
filter_data <- do.call(cbind, lapply(pairs_data, function(i) {
317-
colnames(i) <- focal_terms
318-
i[filter_by]
319-
}))
327+
filter_data <- do.call(
328+
cbind,
329+
lapply(pairs_data, function(i) {
330+
colnames(i) <- focal_terms
331+
i[filter_by]
332+
})
333+
)
320334
# check which pairs have identical values - these are the rows we want to keep
321335
idx <- idx & unname(apply(filter_data, 1, function(r) r[1] == r[2]))
322336
}
@@ -333,14 +347,24 @@ estimate_contrasts.estimate_predicted <- function(model,
333347

334348

335349
# interaction contrasts ----------------------------------------------------
336-
.compute_interactions <- function(predictions, dof, vcov_matrix, datagrid, contrast, by, crit_factor) {
350+
.compute_interactions <- function(
351+
predictions,
352+
dof,
353+
vcov_matrix,
354+
datagrid,
355+
contrast,
356+
by,
357+
crit_factor
358+
) {
337359
# we need the focal terms and all unique values from the datagrid
338360
focal_terms <- c(contrast, by)
339361
at_list <- lapply(datagrid[focal_terms], unique)
340362

341363
## TODO: interaction contrasts currently only work for two focal terms
342364
if (length(focal_terms) != 2) {
343-
insight::format_error("Interaction contrasts currently only work for two focal terms.")
365+
insight::format_error(
366+
"Interaction contrasts currently only work for two focal terms."
367+
)
344368
}
345369

346370
# create pairwise combinations of first focal term
@@ -370,60 +394,72 @@ estimate_contrasts.estimate_predicted <- function(model,
370394
pairs_focal2$Freq <- NULL
371395

372396
# now we iterate over all pairs and try to find the corresponding predictions
373-
out <- do.call(rbind, lapply(seq_len(nrow(pairs_focal1)), function(i) {
374-
# differences between levels of first focal term
375-
pos1 <- predictions[[focal_terms[1]]] == pairs_focal1[i, 1]
376-
pos2 <- predictions[[focal_terms[1]]] == pairs_focal1[i, 2]
377-
378-
do.call(rbind, lapply(seq_len(nrow(pairs_focal2)), function(j) {
379-
# difference between levels of first focal term, *within* first
380-
# level of second focal term
381-
pos_1a <- pos1 & predictions[[focal_terms[2]]] == pairs_focal2[j, 1]
382-
pos_1b <- pos2 & predictions[[focal_terms[2]]] == pairs_focal2[j, 1]
383-
# difference between levels of first focal term, *within* second
384-
# level of second focal term
385-
pos_2a <- pos1 & predictions[[focal_terms[2]]] == pairs_focal2[j, 2]
386-
pos_2b <- pos2 & predictions[[focal_terms[2]]] == pairs_focal2[j, 2]
387-
# once we have found the correct rows for the pairs, we can calculate
388-
# the contrast. We need the predicted values first
389-
predicted1 <- predictions$Predicted[pos_1a] - predictions$Predicted[pos_1b]
390-
predicted2 <- predictions$Predicted[pos_2a] - predictions$Predicted[pos_2b]
391-
# we then create labels for the pairs. "result" is a data frame with
392-
# the labels (of the pairwise contrasts) as columns.
393-
result <- data.frame(
394-
a = paste(pairs_focal1[i, 1], pairs_focal1[i, 2], sep = "-"),
395-
b = paste(pairs_focal2[j, 1], pairs_focal2[j, 2], sep = " and "),
396-
stringsAsFactors = FALSE
397+
out <- do.call(
398+
rbind,
399+
lapply(seq_len(nrow(pairs_focal1)), function(i) {
400+
# differences between levels of first focal term
401+
pos1 <- predictions[[focal_terms[1]]] == pairs_focal1[i, 1]
402+
pos2 <- predictions[[focal_terms[1]]] == pairs_focal1[i, 2]
403+
404+
do.call(
405+
rbind,
406+
lapply(seq_len(nrow(pairs_focal2)), function(j) {
407+
# difference between levels of first focal term, *within* first
408+
# level of second focal term
409+
pos_1a <- pos1 & predictions[[focal_terms[2]]] == pairs_focal2[j, 1]
410+
pos_1b <- pos2 & predictions[[focal_terms[2]]] == pairs_focal2[j, 1]
411+
# difference between levels of first focal term, *within* second
412+
# level of second focal term
413+
pos_2a <- pos1 & predictions[[focal_terms[2]]] == pairs_focal2[j, 2]
414+
pos_2b <- pos2 & predictions[[focal_terms[2]]] == pairs_focal2[j, 2]
415+
# once we have found the correct rows for the pairs, we can calculate
416+
# the contrast. We need the predicted values first
417+
predicted1 <- predictions$Predicted[pos_1a] - predictions$Predicted[pos_1b]
418+
predicted2 <- predictions$Predicted[pos_2a] - predictions$Predicted[pos_2b]
419+
# we then create labels for the pairs. "result" is a data frame with
420+
# the labels (of the pairwise contrasts) as columns.
421+
result <- data.frame(
422+
a = paste(pairs_focal1[i, 1], pairs_focal1[i, 2], sep = "-"),
423+
b = paste(pairs_focal2[j, 1], pairs_focal2[j, 2], sep = " and "),
424+
stringsAsFactors = FALSE
425+
)
426+
colnames(result) <- focal_terms
427+
# we then add the contrast and the standard error. for linear models, the
428+
# SE is sqrt(se1^2 + se2^2)
429+
result$Difference <- predicted1 - predicted2
430+
sum_se_squared <- sum(
431+
predictions$SE[pos_1a]^2,
432+
predictions$SE[pos_1b]^2,
433+
predictions$SE[pos_2a]^2,
434+
predictions$SE[pos_2b]^2
435+
)
436+
# for non-Gaussian models, we subtract the covariance of the two predictions
437+
# but only if the vcov_matrix is not NULL and has the correct dimensions
438+
correct_row_dims <- nrow(vcov_matrix) > 0 &&
439+
all(nrow(vcov_matrix) >= which(pos_1a)) &&
440+
all(nrow(vcov_matrix) >= which(pos_2a)) # nolint
441+
correct_col_dims <- ncol(vcov_matrix) > 0 &&
442+
all(ncol(vcov_matrix) >= which(pos_1b)) &&
443+
all(ncol(vcov_matrix) >= which(pos_2b)) # nolint
444+
if (is.null(vcov_matrix) || !correct_row_dims || !correct_col_dims) {
445+
vcov_sub <- 0
446+
} else {
447+
vcov_sub <- sum(
448+
vcov_matrix[which(pos_1a), which(pos_1b)]^2,
449+
vcov_matrix[which(pos_2a), which(pos_2b)]^2
450+
)
451+
}
452+
# Avoid negative values in sqrt()
453+
if (vcov_sub >= sum_se_squared) {
454+
result$SE <- sqrt(sum_se_squared)
455+
} else {
456+
result$SE <- sqrt(sum_se_squared - vcov_sub)
457+
}
458+
result
459+
})
397460
)
398-
colnames(result) <- focal_terms
399-
# we then add the contrast and the standard error. for linear models, the
400-
# SE is sqrt(se1^2 + se2^2)
401-
result$Difference <- predicted1 - predicted2
402-
sum_se_squared <- sum(
403-
predictions$SE[pos_1a]^2, predictions$SE[pos_1b]^2,
404-
predictions$SE[pos_2a]^2, predictions$SE[pos_2b]^2
405-
)
406-
# for non-Gaussian models, we subtract the covariance of the two predictions
407-
# but only if the vcov_matrix is not NULL and has the correct dimensions
408-
correct_row_dims <- nrow(vcov_matrix) > 0 && all(nrow(vcov_matrix) >= which(pos_1a)) && all(nrow(vcov_matrix) >= which(pos_2a)) # nolint
409-
correct_col_dims <- ncol(vcov_matrix) > 0 && all(ncol(vcov_matrix) >= which(pos_1b)) && all(ncol(vcov_matrix) >= which(pos_2b)) # nolint
410-
if (is.null(vcov_matrix) || !correct_row_dims || !correct_col_dims) {
411-
vcov_sub <- 0
412-
} else {
413-
vcov_sub <- sum(
414-
vcov_matrix[which(pos_1a), which(pos_1b)]^2,
415-
vcov_matrix[which(pos_2a), which(pos_2b)]^2
416-
)
417-
}
418-
# Avoid negative values in sqrt()
419-
if (vcov_sub >= sum_se_squared) {
420-
result$SE <- sqrt(sum_se_squared)
421-
} else {
422-
result$SE <- sqrt(sum_se_squared - vcov_sub)
423-
}
424-
result
425-
}))
426-
}))
461+
})
462+
)
427463
# add CI and p-values
428464
out$CI_low <- out$Difference - stats::qt(crit_factor, df = dof) * out$SE
429465
out$CI_high <- out$Difference + stats::qt(crit_factor, df = dof) * out$SE

R/p_adjust.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@
213213
insight::check_if_installed("marginaleffects", minimum_version = "0.29.0")
214214
model <- marginaleffects::components(x, "model")
215215
} else {
216-
model <- attributes(x)$model
216+
model <- insight::get_model(x)
217217
}
218218

219219
# variance-covariance matrix, to adjust p-values

0 commit comments

Comments
 (0)