Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issues with multiple possible adjustments #762

Merged
merged 4 commits into from
Aug 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Type: Package
Package: performance
Title: Assessment of Regression Models Performance
Version: 0.12.2.8
Version: 0.12.2.9
Authors@R:
c(person(given = "Daniel",
family = "Lüdecke",
Expand Down
46 changes: 40 additions & 6 deletions R/check_dag.R
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@
adjustment_not_needed = is.null(adjustment_set) && is.null(adjustment_nodes),
incorrectly_adjusted = is.null(adjustment_set) && !is.null(adjustment_nodes),
current_adjustments = adjustment_nodes,
minimal_adjustments = adjustment_set
minimal_adjustments = as.list(dagitty::adjustmentSets(dag, effect = x))
)
})

Expand All @@ -247,7 +247,11 @@

.adjust_dag <- function(dag, adjusted) {
for (i in adjusted) {
dag <- gsub(paste0("\n", i, "\n"), paste0("\n", i, " [adjusted]\n"), dag)
# first option, we just have the variable name
dag <- gsub(paste0("\n", i, "\n"), paste0("\n", i, " [adjusted]\n"), dag, fixed = TRUE)
# second option, we have the variable name with a [pos] tag when the user
# provided coords
dag <- gsub(paste0("\n", i, " [pos="), paste0("\n", i, " [adjusted,pos="), dag, fixed = TRUE)
}
dag
}
Expand Down Expand Up @@ -299,6 +303,13 @@
out <- attributes(x)$check_total
}

# missing adjustements - minimal_adjustment can be a list of different
# options for minimal adjustements, so we check here if any of the minimal
# adjustements are currently sufficient
missing_adjustments <- vapply(out$minimal_adjustments, function(i) {
!is.null(out$current_adjustments) && all(i %in% out$current_adjustments)
}, logical(1))

# build message with check results for effects -----------------------

if (isTRUE(out$adjustment_not_needed)) {
Expand All @@ -321,16 +332,39 @@
datawizard::text_concatenate(out$current_adjustments, enclose = "`"),
"."
)
} else if (length(out$current_adjustments) != length(out$minimal_adjustment)) {
} else if (!any(missing_adjustments)) {

Check warning on line 335 in R/check_dag.R

View workflow job for this annotation

GitHub Actions / lint-changed-files / lint-changed-files

file=R/check_dag.R,line=335,col=16,[if_not_else_linter] Prefer `if (A) x else y` to the less-readable `if (!A) y else x` in a simple if/else statement.

Check warning on line 335 in R/check_dag.R

View workflow job for this annotation

GitHub Actions / lint / lint

file=R/check_dag.R,line=335,col=16,[if_not_else_linter] Prefer `if (A) x else y` to the less-readable `if (!A) y else x` in a simple if/else statement.
# Scenario 3: missing adjustments
msg <- paste0(
insight::color_text("Incorrectly adjusted!", "red"),
"\nTo estimate the ", i, " effect, ",
insight::color_text("also", "italic"),
" adjust for ",
insight::color_text(datawizard::text_concatenate(out$minimal_adjustments, enclose = "`"), "yellow"),
"."
" adjust for "
)
# we may have multiple valid adjustment sets - handle this here
if (length(out$minimal_adjustments) > 1) {
msg <- paste0(
msg,
"one of the following sets:\n",
insight::color_text(
paste(
"-",
unlist(lapply(out$minimal_adjustments, paste, collapse = ", "), use.names = FALSE),
collapse = "\n"
),
"yellow"
),
"."
)
} else {
msg <- paste0(
msg,
insight::color_text(datawizard::text_concatenate(
unlist(out$minimal_adjustments, use.names = FALSE),
enclose = "`"
), "yellow"),
"."
)
}
if (is.null(out$current_adjustments)) {
msg <- paste0(msg, "\nCurrently, the model does not adjust for any variables.")
} else {
Expand Down
51 changes: 51 additions & 0 deletions tests/testthat/_snaps/check_dag.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,54 @@
All minimal sufficient adjustments to estimate the total effect were done.


# check_dag, multiple adjustment sets

Code
print(dag)
Output
# Check for correct adjustment sets
- Outcome: exam
- Exposure: podcast

Identification of direct effects

Incorrectly adjusted!
To estimate the direct effect, also adjust for one of the following sets:
- alertness, prepared
- alertness, skills_course
- mood, prepared
- mood, skills_course.
Currently, the model does not adjust for any variables.

Identification of total effects

Incorrectly adjusted!
To estimate the total effect, also adjust for one of the following sets:
- alertness, prepared
- alertness, skills_course
- mood, prepared
- mood, skills_course.
Currently, the model does not adjust for any variables.


---

Code
print(dag)
Output
# Check for correct adjustment sets
- Outcome: exam
- Exposure: podcast
- Adjustments: alertness and prepared

Identification of direct effects

Model is correctly specified.
All minimal sufficient adjustments to estimate the direct effect were done.

Identification of total effects

Model is correctly specified.
All minimal sufficient adjustments to estimate the total effect were done.


28 changes: 27 additions & 1 deletion tests/testthat/test-check_dag.R
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ test_that("check_dag", {
wt ~ disp + cyl,
wt ~ am
)
dag
expect_snapshot(print(dag))
})

Expand All @@ -65,3 +64,30 @@ test_that("check_dag, cylic error", {
regex = "Model is cyclic"
)
})


test_that("check_dag, multiple adjustment sets", {
dag <- check_dag(
podcast ~ mood + humor + skills_course,
alertness ~ mood,
mood ~ humor,
prepared ~ skills_course,
exam ~ alertness + prepared,
coords = ggdag::time_ordered_coords(),
exposure = "podcast",
outcome = "exam"
)
expect_snapshot(print(dag))
dag <- check_dag(
podcast ~ mood + humor + skills_course,
alertness ~ mood,
mood ~ humor,
prepared ~ skills_course,
exam ~ alertness + prepared,
adjusted = c("alertness", "prepared"),
exposure = "podcast",
outcome = "exam",
coords = ggdag::time_ordered_coords()
)
expect_snapshot(print(dag))
})
Loading