From 79c1d454bd3a9a681f6c0807ed1c600bb0a814ec Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 3 Aug 2024 18:38:24 +0200 Subject: [PATCH 1/7] Add plot-method for `check_dag()` --- DESCRIPTION | 3 +- NAMESPACE | 1 + NEWS.md | 6 +++ R/plot.check_dag.R | 99 +++++++++++++++++++++++++++++++++++++++++++ man/plot.check_dag.Rd | 41 ++++++++++++++++++ 5 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 R/plot.check_dag.R create mode 100644 man/plot.check_dag.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 12410928e..965ef4d4f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: see Title: Model Visualisation Toolbox for 'easystats' and 'ggplot2' -Version: 0.8.5 +Version: 0.8.5.1 Authors@R: c(person(given = "Daniel", family = "Lüdecke", @@ -78,6 +78,7 @@ Suggests: DHARMa, emmeans, factoextra, + ggdag, ggdist, ggraph, ggrepel, diff --git a/NAMESPACE b/NAMESPACE index 7dcf77601..3f72afdc2 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -23,6 +23,7 @@ S3method(data_plot,performance_pp_check) S3method(data_plot,point_estimate) S3method(data_plot,rope) S3method(data_plot,see_compare_parameters) +S3method(plot,check_dag) S3method(plot,datawizard_table) S3method(plot,datawizard_tables) S3method(plot,see_bayesfactor_models) diff --git a/NEWS.md b/NEWS.md index 271dd7251..4c3592757 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,9 @@ +# see (dev) + +## Canges + +- New `plot()` method for `performance::check_dag()`. + # see 0.8.5 ## Major Changes diff --git a/R/plot.check_dag.R b/R/plot.check_dag.R new file mode 100644 index 000000000..080935db2 --- /dev/null +++ b/R/plot.check_dag.R @@ -0,0 +1,99 @@ +#' Plot method for check DAGs +#' +#' The `plot()` method for the `performance::check_dag()` function. +#' +#' @param x A `check_dag` object. +#' @param size_point Numeric value specifying size of point geoms. +#' @param colors Character vector of length five, indicating the colors (in +#' hex-format) for different types of variables. +#' @param which Character string indicating which plot to show. Can be either +#' `"all"`, `"current"` or `"required"`. +#' @param ... Not used. +#' +#' @return A ggplot2-object. +#' +#' @examplesIf require("ggdag", quietly = TRUE) +#' library(performance) +#' # incorrect adjustment +#' dag <- check_dag( +#' y ~ x + b + c, +#' x ~ b, +#' outcome = "y", +#' exposure = "x" +#' ) +#' dag +#' plot(dag) +#' @export +plot.check_dag <- function(x, size_point = 15, colors = NULL, which = "all", ...) { + .data <- NULL + insight::check_if_installed(c("ggdag", "ggplot2")) + which <- match.arg(which, choices = c("all", "current", "required")) + + p1 <- suppressWarnings(ggdag::ggdag_adjust(x, stylized = TRUE)) + p2 <- suppressWarnings(ggdag::ggdag_adjustment_set(x, shadow = TRUE, stylized = TRUE)) + + # tweak data + p1$data$type <- as.character(p1$data$adjusted) + p1$data$type[vapply(p1$data$name, ggdag::is_collider, logical(1), .dag = x)] <- "collider" + p1$data$type[p1$data$name == attributes(x)$outcome] <- "outcome" + p1$data$type[p1$data$name %in% attributes(x)$exposure] <- "exposure" + p1$data$type <- factor(p1$data$type, levels = c("outcome", "exposure", "adjusted", "unadjusted", "collider")) + + p2$data$type <- as.character(p2$data$adjusted) + p2$data$type[vapply(p2$data$name, ggdag::is_collider, logical(1), .dag = x)] <- "collider" + p2$data$type[p2$data$name == attributes(x)$outcome] <- "outcome" + p2$data$type[p2$data$name %in% attributes(x)$exposure] <- "exposure" + p2$data$type <- factor(p2$data$type, levels = c("outcome", "exposure", "adjusted", "unadjusted", "collider")) + + if (is.null(colors)) { + point_colors <- see_colors(c("yellow", "cyan", "blue grey", "red", "orange")) + } else if (length(colors) != 5) { + insight::format_error("`colors` must be a character vector with five color-values.") + } else { + point_colors <- colors + } + names(point_colors) <- c("outcome", "exposure", "adjusted", "unadjusted", "collider") + + plot1 <- ggplot2::ggplot(p1$data, ggplot2::aes(x = .data$x, y = .data$y)) + + geom_point_borderless(ggplot2::aes(fill = .data$type), size = size_point) + + ggdag::geom_dag_edges( + ggplot2::aes( + xend = .data$xend, + yend = .data$yend, + edge_alpha = .data$adjusted + ) + ) + + ggdag::scale_adjusted() + + ggdag::geom_dag_label(ggplot2::aes(label = .data$name)) + + ggdag::theme_dag() + + ggplot2::scale_fill_manual(values = point_colors) + + ggplot2::ggtitle("Current model") + + ggplot2::guides(edge_alpha = "none") + + plot2 <- ggplot2::ggplot(p2$data, ggplot2::aes(x = .data$x, y = .data$y)) + + geom_point_borderless(ggplot2::aes(fill = .data$type), size = size_point) + + ggdag::geom_dag_edges( + ggplot2::aes( + xend = .data$xend, + yend = .data$yend, + edge_alpha = .data$adjusted + ) + ) + + ggdag::scale_adjusted() + + ggdag::geom_dag_label(ggplot2::aes(label = .data$name)) + + ggdag::theme_dag() + + ggplot2::scale_fill_manual(values = point_colors) + + ggplot2::ggtitle("Required model") + + ggplot2::guides(edge_alpha = "none") + + if (which == "all") { + # fix legends + plot2 <- plot2 + ggplot2::theme(legend.position = "none") + # plot + plots(plot1, plot2, n_rows = 1) + } else if (which == "current") { + plot1 + } else { + plot2 + } +} diff --git a/man/plot.check_dag.Rd b/man/plot.check_dag.Rd new file mode 100644 index 000000000..8148b812e --- /dev/null +++ b/man/plot.check_dag.Rd @@ -0,0 +1,41 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/plot.check_dag.R +\name{plot.check_dag} +\alias{plot.check_dag} +\title{Plot method for check DAGs} +\usage{ +\method{plot}{check_dag}(x, size_point = 15, colors = NULL, which = "all", ...) +} +\arguments{ +\item{x}{A \code{check_dag} object.} + +\item{size_point}{Numeric value specifying size of point geoms.} + +\item{colors}{Character vector of length five, indicating the colors (in +hex-format) for different types of variables.} + +\item{which}{Character string indicating which plot to show. Can be either +\code{"all"}, \code{"current"} or \code{"required"}.} + +\item{...}{Not used.} +} +\value{ +A ggplot2-object. +} +\description{ +The \code{plot()} method for the \code{performance::check_dag()} function. +} +\examples{ +\dontshow{if (require("ggdag", quietly = TRUE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +library(performance) +# incorrect adjustment +dag <- check_dag( + y ~ x + b + c, + x ~ b, + outcome = "y", + exposure = "x" +) +dag +plot(dag) +\dontshow{\}) # examplesIf} +} From 3ce461ec93b6525d849c48dfe030f68bc2aaac5d Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 3 Aug 2024 18:42:24 +0200 Subject: [PATCH 2/7] update class --- NAMESPACE | 2 +- R/plot.check_dag.R | 2 +- man/{plot.check_dag.Rd => plot.see_check_dag.Rd} | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) rename man/{plot.check_dag.Rd => plot.see_check_dag.Rd} (87%) diff --git a/NAMESPACE b/NAMESPACE index 3f72afdc2..7f5dce91b 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -23,7 +23,6 @@ S3method(data_plot,performance_pp_check) S3method(data_plot,point_estimate) S3method(data_plot,rope) S3method(data_plot,see_compare_parameters) -S3method(plot,check_dag) S3method(plot,datawizard_table) S3method(plot,datawizard_tables) S3method(plot,see_bayesfactor_models) @@ -31,6 +30,7 @@ S3method(plot,see_bayesfactor_parameters) S3method(plot,see_bayesfactor_savagedickey) S3method(plot,see_binned_residuals) S3method(plot,see_check_collinearity) +S3method(plot,see_check_dag) S3method(plot,see_check_distribution) S3method(plot,see_check_distribution_numeric) S3method(plot,see_check_heteroscedasticity) diff --git a/R/plot.check_dag.R b/R/plot.check_dag.R index 080935db2..67809ece2 100644 --- a/R/plot.check_dag.R +++ b/R/plot.check_dag.R @@ -24,7 +24,7 @@ #' dag #' plot(dag) #' @export -plot.check_dag <- function(x, size_point = 15, colors = NULL, which = "all", ...) { +plot.see_check_dag <- function(x, size_point = 15, colors = NULL, which = "all", ...) { .data <- NULL insight::check_if_installed(c("ggdag", "ggplot2")) which <- match.arg(which, choices = c("all", "current", "required")) diff --git a/man/plot.check_dag.Rd b/man/plot.see_check_dag.Rd similarity index 87% rename from man/plot.check_dag.Rd rename to man/plot.see_check_dag.Rd index 8148b812e..829ac91a8 100644 --- a/man/plot.check_dag.Rd +++ b/man/plot.see_check_dag.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/plot.check_dag.R -\name{plot.check_dag} -\alias{plot.check_dag} +\name{plot.see_check_dag} +\alias{plot.see_check_dag} \title{Plot method for check DAGs} \usage{ -\method{plot}{check_dag}(x, size_point = 15, colors = NULL, which = "all", ...) +\method{plot}{see_check_dag}(x, size_point = 15, colors = NULL, which = "all", ...) } \arguments{ \item{x}{A \code{check_dag} object.} From 58a09bf128eaf23ded66ad77b4e41ced37ade6e1 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 3 Aug 2024 19:13:24 +0200 Subject: [PATCH 3/7] use PR --- DESCRIPTION | 1 + 1 file changed, 1 insertion(+) diff --git a/DESCRIPTION b/DESCRIPTION index 965ef4d4f..034c4dc60 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -120,3 +120,4 @@ Config/testthat/edition: 3 Config/testthat/parallel: true Config/Needs/website: easystats/easystatstemplate Config/rcmdcheck/ignore-inconsequential-notes: true +Remotes: easystats/performance#761 From 2ad1d616ec8c48314ed233a2335bda23d3904188 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 3 Aug 2024 19:34:28 +0200 Subject: [PATCH 4/7] docs --- R/plot.check_dag.R | 3 +++ man/plot.see_check_dag.Rd | 3 +++ 2 files changed, 6 insertions(+) diff --git a/R/plot.check_dag.R b/R/plot.check_dag.R index 67809ece2..8d91f5185 100644 --- a/R/plot.check_dag.R +++ b/R/plot.check_dag.R @@ -23,6 +23,9 @@ #' ) #' dag #' plot(dag) +#' +#' # plot only model with required adjustments +#' plot(dag, which = "required") #' @export plot.see_check_dag <- function(x, size_point = 15, colors = NULL, which = "all", ...) { .data <- NULL diff --git a/man/plot.see_check_dag.Rd b/man/plot.see_check_dag.Rd index 829ac91a8..5866cba81 100644 --- a/man/plot.see_check_dag.Rd +++ b/man/plot.see_check_dag.Rd @@ -37,5 +37,8 @@ dag <- check_dag( ) dag plot(dag) + +# plot only model with required adjustments +plot(dag, which = "required") \dontshow{\}) # examplesIf} } From a5fc90687cc6c41e0728909adaa716f46a43c76e Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 3 Aug 2024 20:31:08 +0200 Subject: [PATCH 5/7] update --- R/plot.check_dag.R | 28 +++++++++++++++++++++++----- man/plot.see_check_dag.Rd | 12 +++++++++++- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/R/plot.check_dag.R b/R/plot.check_dag.R index 8d91f5185..74bd3cc06 100644 --- a/R/plot.check_dag.R +++ b/R/plot.check_dag.R @@ -8,6 +8,8 @@ #' hex-format) for different types of variables. #' @param which Character string indicating which plot to show. Can be either #' `"all"`, `"current"` or `"required"`. +#' @param check_colliders Logical indicating whether to highlight colliders. +#' Set to `FALSE` if the algorithm to detect colliders is very slow. #' @param ... Not used. #' #' @return A ggplot2-object. @@ -27,23 +29,39 @@ #' # plot only model with required adjustments #' plot(dag, which = "required") #' @export -plot.see_check_dag <- function(x, size_point = 15, colors = NULL, which = "all", ...) { +plot.see_check_dag <- function(x, + size_point = 15, + colors = NULL, + which = "all", + check_colliders = TRUE, + ...) { .data <- NULL insight::check_if_installed(c("ggdag", "ggplot2")) which <- match.arg(which, choices = c("all", "current", "required")) - p1 <- suppressWarnings(ggdag::ggdag_adjust(x, stylized = TRUE)) - p2 <- suppressWarnings(ggdag::ggdag_adjustment_set(x, shadow = TRUE, stylized = TRUE)) + # get plot data + p1 <- p2 <- suppressWarnings(ggdag::dag_adjustment_sets(x)) + adjusted_for <- attributes(x)$adjusted + + # for current plot, we need to update the "adjusted" column + p1$data$adjusted <- "unadjusted" + if (!is.null(adjusted_for)) { + p1$data$adjusted[p1$data$name %in% adjusted_for] <- "adjusted" + } # tweak data p1$data$type <- as.character(p1$data$adjusted) - p1$data$type[vapply(p1$data$name, ggdag::is_collider, logical(1), .dag = x)] <- "collider" + if (check_colliders) { + p1$data$type[vapply(p1$data$name, ggdag::is_collider, logical(1), .dag = x)] <- "collider" + } p1$data$type[p1$data$name == attributes(x)$outcome] <- "outcome" p1$data$type[p1$data$name %in% attributes(x)$exposure] <- "exposure" p1$data$type <- factor(p1$data$type, levels = c("outcome", "exposure", "adjusted", "unadjusted", "collider")) p2$data$type <- as.character(p2$data$adjusted) - p2$data$type[vapply(p2$data$name, ggdag::is_collider, logical(1), .dag = x)] <- "collider" + if (check_colliders) { + p2$data$type[vapply(p2$data$name, ggdag::is_collider, logical(1), .dag = x)] <- "collider" + } p2$data$type[p2$data$name == attributes(x)$outcome] <- "outcome" p2$data$type[p2$data$name %in% attributes(x)$exposure] <- "exposure" p2$data$type <- factor(p2$data$type, levels = c("outcome", "exposure", "adjusted", "unadjusted", "collider")) diff --git a/man/plot.see_check_dag.Rd b/man/plot.see_check_dag.Rd index 5866cba81..0b8a0d1b4 100644 --- a/man/plot.see_check_dag.Rd +++ b/man/plot.see_check_dag.Rd @@ -4,7 +4,14 @@ \alias{plot.see_check_dag} \title{Plot method for check DAGs} \usage{ -\method{plot}{see_check_dag}(x, size_point = 15, colors = NULL, which = "all", ...) +\method{plot}{see_check_dag}( + x, + size_point = 15, + colors = NULL, + which = "all", + check_colliders = TRUE, + ... +) } \arguments{ \item{x}{A \code{check_dag} object.} @@ -17,6 +24,9 @@ hex-format) for different types of variables.} \item{which}{Character string indicating which plot to show. Can be either \code{"all"}, \code{"current"} or \code{"required"}.} +\item{check_colliders}{Logical indicating whether to highlight colliders. +Set to \code{FALSE} if the algorithm to detect colliders is very slow.} + \item{...}{Not used.} } \value{ From cc54a74db924e6f043d0497d79f23f7c26827236 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 3 Aug 2024 20:34:24 +0200 Subject: [PATCH 6/7] docs --- R/plot.check_dag.R | 12 ++++++++++++ man/plot.see_check_dag.Rd | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/R/plot.check_dag.R b/R/plot.check_dag.R index 74bd3cc06..147500c89 100644 --- a/R/plot.check_dag.R +++ b/R/plot.check_dag.R @@ -28,6 +28,18 @@ #' #' # plot only model with required adjustments #' plot(dag, which = "required") +#' +#' # collider-bias? +#' dag <- check_dag( +#' y ~ x + c + d, +#' x ~ c + d, +#' b ~ x, +#' b ~ y, +#' outcome = "y", +#' exposure = "x", +#' adjusted = "c" +#' ) +#' plot(dag) #' @export plot.see_check_dag <- function(x, size_point = 15, diff --git a/man/plot.see_check_dag.Rd b/man/plot.see_check_dag.Rd index 0b8a0d1b4..f1b00acd4 100644 --- a/man/plot.see_check_dag.Rd +++ b/man/plot.see_check_dag.Rd @@ -50,5 +50,17 @@ plot(dag) # plot only model with required adjustments plot(dag, which = "required") + +# collider-bias? +dag <- check_dag( + y ~ x + c + d, + x ~ c + d, + b ~ x, + b ~ y, + outcome = "y", + exposure = "x", + adjusted = "c" +) +plot(dag) \dontshow{\}) # examplesIf} } From ecd59b3bdd82187644d10af07ce47fbf56080eb2 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 3 Aug 2024 20:48:22 +0200 Subject: [PATCH 7/7] typo --- NEWS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 4c3592757..b9abd37bf 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,6 @@ -# see (dev) +# see (development) -## Canges +## Changes - New `plot()` method for `performance::check_dag()`.