diff --git a/DESCRIPTION b/DESCRIPTION index aa32e0d..a7414d2 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -19,7 +19,6 @@ Imports: dagitty (>= 0.3.4), DiagrammeR (>= 1.0.11), dplyr (>= 1.1.4), - messaging (>= 0.1.0), purrr (>= 1.1.0), rlang (>= 1.1.6), stringr (>= 1.5.1), diff --git a/NAMESPACE b/NAMESPACE index ba3fbd3..75baf4a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,14 +1,22 @@ # Generated by roxygen2: do not edit by hand +export(cleanup_existing_theme) export(get_conditioned_nodes) export(parse_edges) export(parse_edgestring) export(parse_nodes) +export(qd_adjustment_sets) export(qd_dag) export(qd_save) +export(qd_select_edges) +export(qd_select_nodes) +export(qd_set_edge_attrs) +export(qd_set_node_attrs) export(qd_swig) export(qd_themes) export(qd_todagitty) +export(select_edges_by_node_alpha_id) +export(select_nodes_by_alpha_id) export(sep_opts) export(theme_qd_base) export(theme_qd_circles) diff --git a/R/graph_operations.R b/R/graph_operations.R new file mode 100644 index 0000000..5e21f8d --- /dev/null +++ b/R/graph_operations.R @@ -0,0 +1,78 @@ +#' Graph operations +#' +#' @param graph_obj A `quickdag` object output by [qd_dag()] or [qd_swig()]. +#' @param alpha_ids A vector of alphanumeric node IDs upon which to operate. +#' @param ... Passed to [DiagrammeR::set_node_attrs()] or [DiagrammeR::set_edge_attrs()] +#' argument `node_attr` or `edge_attr`, respectively. +#' [DiagrammeR::set_edge_attrs()] argument of the same name. +#' @param from_alpha A vector of alphanumeric source node IDs. +#' @param to_alpha A vector of alphanumeric destination node IDs. +#' @param set_op Passed to the `set_op` argument of [DiagrammeR::select_nodes_by_id()] +#' or [DiagrammeR::select_edges_by_node_id()]. Defaults to "union". +#' +#' @rdname graph_operations +#' @export +qd_set_node_attrs <- function(graph_obj, ..., alpha_ids) { + numids <- get_numids(graph_obj, alpha_ids) + graph_out <- DiagrammeR::set_node_attrs(graph_obj, + ..., + values = values, + nodes = numids) + graph_out +} + +#' @rdname graph_operations +#' @export +qd_set_edge_attrs <- function(graph_obj, ..., + from_alpha = NULL, to_alpha = NULL) { + from_numids <- NULL + to_numids <- NULL + if (!is.null(from_alpha)) { + from_numids <- get_numids(graph_obj, from_alpha) + } + if (!is.null(to_alpha)) { + to_numids <- get_numids(graph_obj, to_alpha) + } + graph_out <- DiagrammeR::set_edge_attrs(graph_obj, + ..., + values = values, + from = from_numids, + to = to_numids) + graph_out +} + +#' @rdname graph_operations +#' @export +select_nodes_by_alpha_id <- function(graph_obj, alpha_ids, set_op = "union") { + numids <- get_numids(graph_obj, alpha_ids) + graph_out <- DiagrammeR::select_nodes_by_id(graph_obj, + nodes = numids, + set_op = set_op) + graph_out +} + +#' @rdname graph_operations +#' @export +qd_select_nodes <- select_nodes_by_alpha_id + +#' @rdname graph_operations +#' @export +select_edges_by_node_alpha_id <- function(graph_obj, alpha_ids, set_op = "union") { + numids <- get_numids(graph_obj, alpha_ids) + graph_out <- DiagrammeR::select_edges_by_node_id(graph_obj, + nodes = numids, + set_op = set_op) + graph_out +} + +#' @rdname graph_operations +#' @export +qd_select_edges <- select_edges_by_node_alpha_id + +#' Retrieves numeric IDs given alphanumeric IDs +#' @inheritParams qd_set_node_attrs +get_numids <- function(graph_obj, alpha_ids) { + graph_obj$nodes_df |> + dplyr::filter(alpha_id %in% alpha_ids) |> + dplyr::pull(id) +} diff --git a/R/parse.R b/R/parse.R index 116882b..842928d 100644 --- a/R/parse.R +++ b/R/parse.R @@ -1,6 +1,6 @@ #' @title Parse nodes #' @inheritParams qd_dag -#' @rdname node-parsing +#' @rdname node_parsing #' @export parse_nodes <- function(edgelist) { nodes <- unlist(lapply(edgelist, @@ -12,7 +12,7 @@ parse_nodes <- function(edgelist) { #' @title Parse edges in a graph specification #' @param edgestring A single string containing some set of node-edge relationships -#' @rdname edge-parsing +#' @rdname edge_parsing #' @export parse_edgestring <- function(edgestring) { edgepat <- "\\<\\-\\>|\\-\\>|\\<\\-" @@ -52,7 +52,7 @@ parse_edgestring <- function(edgestring) { Reduce(rbind, edge_mini_dfs) } -#' @rdname edge-parsing +#' @rdname edge_parsing #' @inheritParams qd_dag #' @export parse_edges <- function(edgelist) { diff --git a/R/qd_dag.R b/R/qd_dag.R index 9a61e56..caa67ca 100644 --- a/R/qd_dag.R +++ b/R/qd_dag.R @@ -11,7 +11,7 @@ #' @param edge_aes_opts A list feeding aesthetic options for edges to #' [DiagrammeR::edge_aes()]. Defaults to empty list. #' @param format_special Render numeric elements in an alphanumeric `alpha_id` as -#' subcripts. Defaults to `TRUE`. +#' subcripts. Defaults to `FALSE`. #' @param verbose Indicate whether to print node and edge dataframes to the console. #' See Details below. Defaults to `TRUE`. #' @param check_dag Logical. Check whether the graph conforms to the rules of DAGs. @@ -31,7 +31,7 @@ #' edges <- c("A -> { B C } <- L", #' "B -> C") #' -#' # make a DAG object and render the graph using the default theme +#' # Make a DAG object and render the graph using the default theme #' g.obj <- qd_dag(edges) #' DiagrammeR::render_graph(g.obj) #' @@ -51,8 +51,11 @@ #' qd_dag <- function(edgelist, node_labs = NULL, node_aes_opts = list(), edge_aes_opts = list(), - format_special = TRUE, - verbose = FALSE, check_dag = TRUE, theme = "base", ...) { + format_special = getOption("quickdag.format_special"), + verbose = getOption("quickdag.verbose"), + check_dag = getOption("quickdag.check_dag"), + theme = getOption("quickdag.theme"), + ...) { # Identify Nodes -------------------------------------------------------- ## extract unique nodes, sort in ascending order @@ -171,5 +174,4 @@ qd_dag <- function(edgelist, node_labs = NULL, class(graph) <- c("quickdag", "dgr_graph") graph - } diff --git a/R/qd_save.R b/R/qd_save.R index 6618f29..c8beb7c 100644 --- a/R/qd_save.R +++ b/R/qd_save.R @@ -33,7 +33,8 @@ #' #' # clean up temporary directory #' file.remove(file) -qd_save <- function(graph, file_name, ..., embed = FALSE, kg = NULL) { +qd_save <- function(graph, file_name, ..., + embed = getOption("quickdag.embed"), kg = NULL) { DiagrammeR::export_graph(graph = graph, file_name = file_name, ...) if (embed == TRUE) { diff --git a/R/qd_swig.R b/R/qd_swig.R index ecd8b46..7ee1cce 100644 --- a/R/qd_swig.R +++ b/R/qd_swig.R @@ -32,8 +32,8 @@ qd_swig <- function(graph_obj, fixed_nodes, custom_values = NULL, - fixed_sep = "vlin", - sep_point_size = 15) { + fixed_sep = getOption("quickdag.swig_fixedsep"), + sep_point_size = getOption("quickdag.swig_sepsize")) { ndf <- DiagrammeR::get_node_df(graph_obj) ndf$fixed <- with(ndf, ifelse(alpha_id %in% fixed_nodes, TRUE, FALSE)) diff --git a/R/qd_todagitty.R b/R/qd_todagitty.R index ffa3d76..69b1f06 100644 --- a/R/qd_todagitty.R +++ b/R/qd_todagitty.R @@ -3,36 +3,43 @@ #' @description #' Format an edgelist and send it to dagitty to identify variable adjustment sets. #' -#' @param edgelist A vector of edge relationships. Must be strictly organized (see -#' example for format). +#' @param edgelist A vector of edge relationships. #' @param diagram_type Character identifying the diagram type. Defaults to "dag", but -#' user can specify another graph type (see dagitty documentation). +#' user can specify another graph type (see [dagitty::adjustmentSets()]). #' @param showplot Logical indicating whether to produce a dagitty plot. Defaults to #' `FALSE`. #' @param exposure Character. Specify exposure of interest. (Required) #' @param outcome Character. Specifiy outcome of interest. (Required) -#' @param ... Pass arguments to [dagitty::adjustmentSets()]. See dagitty documentation -#' for options. +#' @param ... Pass arguments to [dagitty::adjustmentSets()]. #' #' @details -#' The `exposure` and `outcome` options map to dagitty functions of the same name. +#' The `exposure` and `outcome` options map to dagitty parameters of the same name. #' +#' [qd_todagitty()] remains an alias for [qd_adjustment_sets()] to avoid breaking +#' existing scripts, as it was the original name of this function. +#' +#' @rdname qd_adjustment_sets #' @export #' @examples +#' # feed an edgelist to qd_adjustment_sets() #' edges <- c("A -> { B C D }", #' "B -> C", -#' "E -> { B C }") -#' # must pass exposure and outcome arguments to dagitty::adjustmentSets() -#' qd_todagitty(edges, exposure = "A", outcome = "C") -#' qd_todagitty(edges, exposure = "A", outcome = "C", type = "minimal") -qd_todagitty <- function(edgelist, diagram_type = "dag", showplot = FALSE, - exposure, outcome, - ...) { +#' "E -> { B C }", +#' "Y <- L -> A") +#' qd_adjustment_sets(edges, exposure = "A", outcome = "C") +#' qd_adjustment_sets(edges, exposure = "A", outcome = "C", type = "minimal") +#' +#' # if you've already created a qd_dag() object +#' dag <- qd_dag(edges) +#' qd_adjustment_sets(dag$qd_edgelist, exposure = "A", outcome = "Y") +qd_adjustment_sets <- function(edgelist, diagram_type = "dag", showplot = FALSE, + exposure, outcome, + ...) { - dagitty_obj <- dagitty::dagitty(paste(diagram_type, "{", - paste(edgelist, collapse = "; "), - "}"), - layout = TRUE) + dagitty_obj <- dagitty::dagitty( + paste(diagram_type, "{", paste(edgelist, collapse = "; "), "}"), + layout = TRUE + ) ## optional to show dagitty plot if (showplot) { @@ -40,7 +47,14 @@ qd_todagitty <- function(edgelist, diagram_type = "dag", showplot = FALSE, } ## use dagitty's algorithm to identify adjustment sets - sets <- dagitty::adjustmentSets(dagitty_obj, exposure = exposure, - outcome = outcome, ...) + sets <- dagitty::adjustmentSets(dagitty_obj, + exposure = exposure, + outcome = outcome, + ...) return(sets) } + + +#' @rdname qd_adjustment_sets +#' @export +qd_todagitty <- qd_adjustment_sets diff --git a/R/sep_opts.R b/R/sep_opts.R index ce3c980..64bb151 100644 --- a/R/sep_opts.R +++ b/R/sep_opts.R @@ -40,11 +40,11 @@ sep_opts <- function(entity = NULL, table = FALSE) { ) } - if (entity %in% names(defopts)) { + if (is.null(entity)) { + defopts + } else if (entity %in% names(defopts)) { defopts[names(defopts) == entity] - } else if (!is.null(entity)) { - entity } else { - defopts + entity } } diff --git a/R/themes.R b/R/themes.R index 3cf5683..29c733b 100644 --- a/R/themes.R +++ b/R/themes.R @@ -1,96 +1,168 @@ #' Diagram themes #' #' @description -#' Apply various pre-fabricated themes to diagrams. +#' Themes and theming utilies. #' #' @param graph_obj A DAG object created by [qd_dag()]. -#' @param conditioned A character vector indicating which nodes are conditioned upon. -#' The shape for these nodes will be set to "square". #' @param theme A character string indicating the theme to use. Defaults to "base". #' Set to `NULL` to use GraphViz defaults. -#' @param font A character vector indicating the font family to use for node labels. -#' Defaults to "serif". -#' @param ... Pass arguments to theme call (e.g., [theme_qd_base()]), such as -#' `conditioned` or `font`. +#' @param conditioned A character vector indicating which nodes are conditioned upon. +#' The shape for these nodes will be set to "rectangle". +#' @param rankdir Direction of node layout. Defaults to "LR", for left-to-right. +#' @param layout Layout engine. Defaults to "dot", which is the only engine explicitly +#' used by [quickdag]. Other options provided by [DiagrammeR] may or may not work +#' well. +#' @param shape Standard node shape for a given graph. Defaults for each theme: +#' "base" = plaintex, "circles" = circle, "pearl" = point. +#' See [Graphviz documentation](https://graphviz.org/doc/info/shapes.html) for other +#' options. +#' @param arrowhead Shape of the arrowhead. See [DiagrammeR::edge_aes()]. +#' @param arrowsize Size of the arrowhead. See [DiagrammeR::edge_aes()]. +#' @param nodepen Size (in points) of node outlines. By default, controlled +#' by package options specific to each theme, each of which defaults to 0.2. +#' @param nodewidth Width (in inches) of nodes. By default, controlled by package options +#' specific to each theme. Defaults to 0. +#' @param nodeheight Height (in inches) of nodes. By default, controlled by package +#' options specific to each theme. Defaults to 0. +#' @param edgepen Size (in points) of edges. By default, controlled +#' by package options specific to each theme, each of which defaults to 0.2. +#' @param fontname Font name for text elements of the graph. By default, controlled by +#' package options specific to each theme, each of which defaults to "Helvetica". This +#' is the default in `DiagrammeR`. +#' @param fontsize Size of text (in points). By default, controlled by package options +#' specific to each theme, each of which defaults to 5. +#' @param fontcolor Text color. By default, controlled by package options +#' specific to each theme, each of which defaults to "black". +#' @param pointsize Size of "point" shape (in points). Applies to "pearl" theme only and +#' defaults to 0.02. +#' @param pointcolor Border color for the "point" shape. Applies to "pearl" theme only +#' and defaults to "black". +#' @param pointfill Fill color for the "point" shape. Applies to "pearl" theme only +#' and defaults to "black". #' @rdname qd_themes #' @export # wrapper for theme selection -qd_themes <- function(graph_obj, theme, ...) { +qd_themes <- function(graph_obj, theme, conditioned = NULL) { select_theme <- c( - "base" = "theme_qd_base", + "base" = "theme_qd_base", "circles" = "theme_qd_circles", - "pearl" = "theme_qd_pearl" + "pearl" = "theme_qd_pearl" ) + if (!theme %in% names(select_theme)) { + stop("`theme` must be one of: ", paste(names(select_theme), collapse = ", ")) + } + + graph_obj <- graph_obj |> cleanup_existing_theme() + graph_obj$theme <- theme + + if (!is.null(conditioned)) { + graph_obj$conditioned <- conditioned + } + do.call(select_theme[theme], - args = list(graph_obj = graph_obj, ...)) + args = list(graph_obj = graph_obj, conditioned = conditioned)) } #' @rdname qd_themes #' @export -theme_qd_base <- function(graph_obj, font = "serif", ...) { - - graph_attrs <- tibble::tibble( - attr = c("rankdir", "layout"), - value = c("LR", "dot"), - attr_type = "graph" - ) - - node_attrs <- tibble::tibble( - attr = c("shape", "penwidth", "fontname", "width", "height"), - value = c("plaintext", "0.5", font, "0", "0"), - attr_type = "node" - ) - - edge_attrs <- tibble::tibble( - attr = c("arrowsize", "penwidth"), - value = c("0.4", "0.5"), - attr_type = "edge" - ) - - graph_obj$global_attrs <- dplyr::bind_rows(graph_attrs, node_attrs, edge_attrs) +theme_qd_base <- function(graph_obj, + rankdir = getOption("quickdag.base_rankdir"), + layout = getOption("quickdag.base_layout"), + shape = getOption("quickdag.base_shape"), + nodepen = getOption("quickdag.base_nodepen"), + nodewidth = getOption("quickdag.base_nodewidth"), + nodeheight = getOption("quickdag.base_nodeheight"), + edgepen = getOption("quickdag.base_edgepen"), + arrowhead = getOption("quickdag.base_arrowhead"), + arrowsize = getOption("quickdag.base_arrowsize"), + fontname = getOption("quickdag.base_fontname"), + fontsize = getOption("quickdag.base_fontsize"), + fontcolor = getOption("quickdag.base_fontcolor"), + conditioned = NULL) { - - graph_obj <- graph_obj |> get_conditioned_nodes(...) + graph_obj <- graph_obj |> + cleanup_existing_theme() |> + DiagrammeR::add_global_graph_attrs("rankdir", "LR", attr_type = "graph") |> + DiagrammeR::add_global_graph_attrs("layout", layout, attr_type = "graph") |> + ## Node aesthetics + DiagrammeR::set_node_attrs("shape", shape) |> + DiagrammeR::set_node_attrs("penwidth", nodepen) |> + DiagrammeR::set_node_attrs("fontname", fontname) |> + DiagrammeR::set_node_attrs("width", nodewidth) |> + DiagrammeR::set_node_attrs("height", nodeheight) |> + DiagrammeR::set_node_attrs("headport", "_") |> + DiagrammeR::set_node_attrs("tailport", "_") |> + ## Edge aesthetics + DiagrammeR::set_edge_attrs("arrowhead", arrowhead) |> + DiagrammeR::set_edge_attrs("arrowsize", arrowsize) |> + DiagrammeR::set_edge_attrs("penwidth", edgepen) |> + get_conditioned_nodes(conditioned = conditioned) + + graph_obj$theme <- "base" graph_obj } #' @rdname qd_themes #' @export -theme_qd_circles <- function(graph_obj, font = "serif", ...) { - # set base theme - graph_obj <- graph_obj |> theme_qd_base() +theme_qd_circles <- function(graph_obj, + nodepen = choose_option("quickdag.circles_nodepen"), + edgepen = choose_option("quickdag.circles_edgepen"), + fontname = choose_option("quickdag.circles_fontname"), + fontsize = choose_option("quickdag.circles_fontsize"), + fontcolor = choose_option("quickdag.circles_fontcolor"), + conditioned = NULL) { # tweak base theme graph_obj <- graph_obj |> - DiagrammeR::add_global_graph_attrs("shape", "circle", "node") + theme_qd_base() |> + DiagrammeR::add_global_graph_attrs("shape", "circle", "node") |> + get_conditioned_nodes(conditioned = conditioned) - graph_obj <- graph_obj |> get_conditioned_nodes(...) + graph_obj$theme <- "circles" graph_obj } #' @rdname qd_themes #' @export -theme_qd_pearl <- function(graph_obj, font = "serif", ...) { - # set base theme - graph_obj <- graph_obj |> theme_qd_base() - - # tweak base theme +theme_qd_pearl <- function(graph_obj, + pointsize = getOption("quickdag.pearl_pointsize"), + pointcolor = getOption("quickdag.pearl_pointcolor"), + pointfill = getOption("quickdag.pearl_pointfill"), + edgepen = choose_option("quickdag.pearl_edgepen"), + arrowhead = choose_option("quickdag.pearl_arrowhead"), + arrowsize = choose_option("quickdag.pearl_arrowsize"), + fontname = choose_option("quickdag.pearl_fontname"), + fontsize = choose_option("quickdag.pearl_fontsize"), + fontcolor = choose_option("quickdag.pearl_fontcolor"), + conditioned = NULL) { + + # Tweak base theme graph_obj <- graph_obj |> - # node attribute tweaks - DiagrammeR::add_global_graph_attrs("shape", "point", "node") |> - DiagrammeR::add_global_graph_attrs("width", 0.2, "node") |> - DiagrammeR::add_global_graph_attrs("height", 0.2, "node") |> - # edge attribute tweaks - DiagrammeR::add_global_graph_attrs("penwidth", 0.2, "edge") |> - DiagrammeR::add_global_graph_attrs("arrowsize", 0.2, "edge") - - if (exists("conditioned")) { - messaging::emit_message("This theme does not allow for conditioned nodes.") - } - + theme_qd_base() |> + ## Node attribute tweaks + DiagrammeR::set_node_attrs("shape", "point") |> + DiagrammeR::set_node_attrs("style", "filled") |> + DiagrammeR::set_node_attrs("color", pointcolor) |> + DiagrammeR::set_node_attrs("width", pointsize) |> + DiagrammeR::set_node_attrs("height", pointsize) |> + DiagrammeR::set_node_attrs("fixedsize", TRUE) |> + DiagrammeR::set_node_attrs("fontsize", fontsize) |> + ## Add and style external labels + DiagrammeR::set_node_attrs("fillcolor", pointfill) |> + DiagrammeR::set_node_attrs("xlabel", graph_obj$nodes_df$label) |> + ## Nuke internal node labels + DiagrammeR::set_node_attrs("fontcolor", fontcolor) |> + DiagrammeR::set_node_attrs("label", "") |> + ## Edge attribute tweaks + DiagrammeR::set_edge_attrs("penwidth", edgepen) |> + DiagrammeR::set_edge_attrs("arrowhead", arrowhead) |> + DiagrammeR::set_edge_attrs("arrowsize", arrowsize) |> + get_conditioned_nodes(conditioned = conditioned) + + graph_obj$theme <- "pearl" graph_obj } @@ -107,16 +179,27 @@ get_conditioned_nodes <- function(graph_obj, conditioned = NULL) { DiagrammeR::get_node_ids(conditions = alpha_id %in% conditioned) graph_obj <- graph_obj |> - # add default columns to node_df based on global_attrs + ## Add default columns to node_df based on global_attrs DiagrammeR::set_node_attrs("shape", default_shape) |> DiagrammeR::set_node_attrs("width", default_minwd) |> DiagrammeR::set_node_attrs("height", default_minht) |> - # select conditioned nodes and update node aesthetics + ## Select conditioned nodes and update node aesthetics DiagrammeR::select_nodes_by_id(cd_nodes) |> - DiagrammeR::set_node_attrs_ws("shape", "square") |> + DiagrammeR::set_node_attrs_ws("shape", "rectangle") |> DiagrammeR::set_node_attrs_ws("width", "0") |> DiagrammeR::set_node_attrs_ws("height", "0") |> DiagrammeR::clear_selection() } + graph_obj$conditioned <- conditioned + graph_obj +} + +#' @rdname qd_themes +#' @export +cleanup_existing_theme <- function(graph_obj) { + graph_obj$nodes_df <- graph_obj$nodes_df[, c("id", "type", "label", "alpha_id")] + ## Used for the side effect of returning an empty global_attrs table in the + ## appropriate format + graph_obj$global_attrs <- qd_dag("A", theme = NULL, check_dag = FALSE)$global_attrs graph_obj } diff --git a/R/utils.R b/R/utils.R index 88b12a1..3199998 100644 --- a/R/utils.R +++ b/R/utils.R @@ -1,2 +1,51 @@ # Put some variables in the global environment to avoid R CMD CHECK notes -utils::globalVariables(c("alpha_id", "label", "start", "end")) +utils::globalVariables(c("alpha_id", "label", "start", "end", "id", "values")) + +# Set global options +.onLoad <- function(libname, pkgname) { + opts <- list( + quickdag.check_dag = TRUE, + quickdag.embed = FALSE, + quickdag.swig_fixedsep = "vlin", + quickdag.swig_sepsize = 15, + quickdag.format_special = FALSE, + quickdag.theme = "base", + quickdag.verbose = FALSE, + quickdag.base_rankdir = "LR", + quickdag.base_layout = "dot", + quickdag.base_nodewidth = 0, + quickdag.base_shape = "plaintext", + quickdag.base_nodepen = 0.5, + quickdag.base_nodeheight = 0, + quickdag.base_edgepen = 0.5, + quickdag.base_arrowhead = "normal", + quickdag.base_arrowsize = 0.4, + quickdag.base_fontname = "Helvetica", # DiagrammeR's default + quickdag.base_fontsize = 5, + quickdag.base_fontcolor = "black", + quickdag.circles_nodepen = NA, + quickdag.circles_edgepen = NA, + quickdag.circles_fontname = NA, + quickdag.circles_fontsize = NA, + quickdag.circles_fontcolor = NA, + quickdag.pearl_pointsize = 0.02, + quickdag.pearl_pointcolor = "black", + quickdag.pearl_pointfill = "black", + quickdag.pearl_edgepen = NA, + quickdag.pearl_arrowhead = NA, + quickdag.pearl_arrowsize = 0.2, + quickdag.pearl_fontname = NA, + quickdag.pearl_fontsize = NA, + quickdag.pearl_fontcolor = NA + ) + options(opts) +} + +choose_option <- function(x) { + themeopt <- stringr::str_remove(x, "quickdag\\.(circles|pearl)_") + if (is.na(getOption(x))) { + getOption(paste0("quickdag.base_", themeopt)) + } else { + getOption(x) + } +} diff --git a/man/edge-parsing.Rd b/man/edge_parsing.Rd similarity index 100% rename from man/edge-parsing.Rd rename to man/edge_parsing.Rd diff --git a/man/get_numids.Rd b/man/get_numids.Rd new file mode 100644 index 0000000..6c77f18 --- /dev/null +++ b/man/get_numids.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/graph_operations.R +\name{get_numids} +\alias{get_numids} +\title{Retrieves numeric IDs given alphanumeric IDs} +\usage{ +get_numids(graph_obj, alpha_ids) +} +\arguments{ +\item{graph_obj}{A \code{quickdag} object output by \code{\link[=qd_dag]{qd_dag()}} or \code{\link[=qd_swig]{qd_swig()}}.} + +\item{alpha_ids}{A vector of alphanumeric node IDs upon which to operate.} +} +\description{ +Retrieves numeric IDs given alphanumeric IDs +} diff --git a/man/graph_operations.Rd b/man/graph_operations.Rd new file mode 100644 index 0000000..da5cc47 --- /dev/null +++ b/man/graph_operations.Rd @@ -0,0 +1,42 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/graph_operations.R +\name{qd_set_node_attrs} +\alias{qd_set_node_attrs} +\alias{qd_set_edge_attrs} +\alias{select_nodes_by_alpha_id} +\alias{qd_select_nodes} +\alias{select_edges_by_node_alpha_id} +\alias{qd_select_edges} +\title{Graph operations} +\usage{ +qd_set_node_attrs(graph_obj, ..., alpha_ids) + +qd_set_edge_attrs(graph_obj, ..., from_alpha = NULL, to_alpha = NULL) + +select_nodes_by_alpha_id(graph_obj, alpha_ids, set_op = "union") + +qd_select_nodes(graph_obj, alpha_ids, set_op = "union") + +select_edges_by_node_alpha_id(graph_obj, alpha_ids, set_op = "union") + +qd_select_edges(graph_obj, alpha_ids, set_op = "union") +} +\arguments{ +\item{graph_obj}{A \code{quickdag} object output by \code{\link[=qd_dag]{qd_dag()}} or \code{\link[=qd_swig]{qd_swig()}}.} + +\item{...}{Passed to \code{\link[DiagrammeR:set_node_attrs]{DiagrammeR::set_node_attrs()}} or \code{\link[DiagrammeR:set_edge_attrs]{DiagrammeR::set_edge_attrs()}} +argument \code{node_attr} or \code{edge_attr}, respectively. +\code{\link[DiagrammeR:set_edge_attrs]{DiagrammeR::set_edge_attrs()}} argument of the same name.} + +\item{alpha_ids}{A vector of alphanumeric node IDs upon which to operate.} + +\item{from_alpha}{A vector of alphanumeric source node IDs.} + +\item{to_alpha}{A vector of alphanumeric destination node IDs.} + +\item{set_op}{Passed to the \code{set_op} argument of \code{\link[DiagrammeR:select_nodes_by_id]{DiagrammeR::select_nodes_by_id()}} +or \code{\link[DiagrammeR:select_edges_by_node_id]{DiagrammeR::select_edges_by_node_id()}}. Defaults to "union".} +} +\description{ +Graph operations +} diff --git a/man/node-parsing.Rd b/man/node_parsing.Rd similarity index 100% rename from man/node-parsing.Rd rename to man/node_parsing.Rd diff --git a/man/qd_adjustment_sets.Rd b/man/qd_adjustment_sets.Rd new file mode 100644 index 0000000..d0ff60a --- /dev/null +++ b/man/qd_adjustment_sets.Rd @@ -0,0 +1,62 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/qd_todagitty.R +\name{qd_adjustment_sets} +\alias{qd_adjustment_sets} +\alias{qd_todagitty} +\title{Identify variables for adjustment} +\usage{ +qd_adjustment_sets( + edgelist, + diagram_type = "dag", + showplot = FALSE, + exposure, + outcome, + ... +) + +qd_todagitty( + edgelist, + diagram_type = "dag", + showplot = FALSE, + exposure, + outcome, + ... +) +} +\arguments{ +\item{edgelist}{A vector of edge relationships.} + +\item{diagram_type}{Character identifying the diagram type. Defaults to "dag", but +user can specify another graph type (see \code{\link[dagitty:adjustmentSets]{dagitty::adjustmentSets()}}).} + +\item{showplot}{Logical indicating whether to produce a dagitty plot. Defaults to +\code{FALSE}.} + +\item{exposure}{Character. Specify exposure of interest. (Required)} + +\item{outcome}{Character. Specifiy outcome of interest. (Required)} + +\item{...}{Pass arguments to \code{\link[dagitty:adjustmentSets]{dagitty::adjustmentSets()}}.} +} +\description{ +Format an edgelist and send it to dagitty to identify variable adjustment sets. +} +\details{ +The \code{exposure} and \code{outcome} options map to dagitty parameters of the same name. + +\code{\link[=qd_todagitty]{qd_todagitty()}} remains an alias for \code{\link[=qd_adjustment_sets]{qd_adjustment_sets()}} to avoid breaking +existing scripts, as it was the original name of this function. +} +\examples{ +# feed an edgelist to qd_adjustment_sets() +edges <- c("A -> { B C D }", + "B -> C", + "E -> { B C }", + "Y <- L -> A") +qd_adjustment_sets(edges, exposure = "A", outcome = "C") +qd_adjustment_sets(edges, exposure = "A", outcome = "C", type = "minimal") + +# if you've already created a qd_dag() object +dag <- qd_dag(edges) +qd_adjustment_sets(dag$qd_edgelist, exposure = "A", outcome = "Y") +} diff --git a/man/qd_dag.Rd b/man/qd_dag.Rd index f930617..56c6f88 100644 --- a/man/qd_dag.Rd +++ b/man/qd_dag.Rd @@ -9,10 +9,10 @@ qd_dag( node_labs = NULL, node_aes_opts = list(), edge_aes_opts = list(), - format_special = TRUE, - verbose = FALSE, - check_dag = TRUE, - theme = "base", + format_special = getOption("quickdag.format_special"), + verbose = getOption("quickdag.verbose"), + check_dag = getOption("quickdag.check_dag"), + theme = getOption("quickdag.theme"), ... ) } @@ -29,7 +29,7 @@ to \code{NULL}.} \code{\link[DiagrammeR:edge_aes]{DiagrammeR::edge_aes()}}. Defaults to empty list.} \item{format_special}{Render numeric elements in an alphanumeric \code{alpha_id} as -subcripts. Defaults to \code{TRUE}.} +subcripts. Defaults to \code{FALSE}.} \item{verbose}{Indicate whether to print node and edge dataframes to the console. See Details below. Defaults to \code{TRUE}.} @@ -56,7 +56,7 @@ the same letter as their corresponding \code{alpha_id}, which may not always be edges <- c("A -> { B C } <- L", "B -> C") -# make a DAG object and render the graph using the default theme +# Make a DAG object and render the graph using the default theme g.obj <- qd_dag(edges) DiagrammeR::render_graph(g.obj) diff --git a/man/qd_save.Rd b/man/qd_save.Rd index 0285083..33b69cd 100644 --- a/man/qd_save.Rd +++ b/man/qd_save.Rd @@ -4,7 +4,7 @@ \alias{qd_save} \title{Save graph objects as images} \usage{ -qd_save(graph, file_name, ..., embed = FALSE, kg = NULL) +qd_save(graph, file_name, ..., embed = getOption("quickdag.embed"), kg = NULL) } \arguments{ \item{graph}{A graph object produced by \code{\link[=qd_dag]{qd_dag()}}} diff --git a/man/qd_swig.Rd b/man/qd_swig.Rd index 9fb49b2..767fb04 100644 --- a/man/qd_swig.Rd +++ b/man/qd_swig.Rd @@ -8,8 +8,8 @@ qd_swig( graph_obj, fixed_nodes, custom_values = NULL, - fixed_sep = "vlin", - sep_point_size = 15 + fixed_sep = getOption("quickdag.swig_fixedsep"), + sep_point_size = getOption("quickdag.swig_sepsize") ) } \arguments{ diff --git a/man/qd_themes.Rd b/man/qd_themes.Rd index e06afe0..6f298c6 100644 --- a/man/qd_themes.Rd +++ b/man/qd_themes.Rd @@ -6,17 +6,55 @@ \alias{theme_qd_circles} \alias{theme_qd_pearl} \alias{get_conditioned_nodes} +\alias{cleanup_existing_theme} \title{Diagram themes} \usage{ -qd_themes(graph_obj, theme, ...) +qd_themes(graph_obj, theme, conditioned = NULL) -theme_qd_base(graph_obj, font = "serif", ...) +theme_qd_base( + graph_obj, + rankdir = getOption("quickdag.base_rankdir"), + layout = getOption("quickdag.base_layout"), + shape = getOption("quickdag.base_shape"), + nodepen = getOption("quickdag.base_nodepen"), + nodewidth = getOption("quickdag.base_nodewidth"), + nodeheight = getOption("quickdag.base_nodeheight"), + edgepen = getOption("quickdag.base_edgepen"), + arrowhead = getOption("quickdag.base_arrowhead"), + arrowsize = getOption("quickdag.base_arrowsize"), + fontname = getOption("quickdag.base_fontname"), + fontsize = getOption("quickdag.base_fontsize"), + fontcolor = getOption("quickdag.base_fontcolor"), + conditioned = NULL +) -theme_qd_circles(graph_obj, font = "serif", ...) +theme_qd_circles( + graph_obj, + nodepen = choose_option("quickdag.circles_nodepen"), + edgepen = choose_option("quickdag.circles_edgepen"), + fontname = choose_option("quickdag.circles_fontname"), + fontsize = choose_option("quickdag.circles_fontsize"), + fontcolor = choose_option("quickdag.circles_fontcolor"), + conditioned = NULL +) -theme_qd_pearl(graph_obj, font = "serif", ...) +theme_qd_pearl( + graph_obj, + pointsize = getOption("quickdag.pearl_pointsize"), + pointcolor = getOption("quickdag.pearl_pointcolor"), + pointfill = getOption("quickdag.pearl_pointfill"), + edgepen = choose_option("quickdag.pearl_edgepen"), + arrowhead = choose_option("quickdag.pearl_arrowhead"), + arrowsize = choose_option("quickdag.pearl_arrowsize"), + fontname = choose_option("quickdag.pearl_fontname"), + fontsize = choose_option("quickdag.pearl_fontsize"), + fontcolor = choose_option("quickdag.pearl_fontcolor"), + conditioned = NULL +) get_conditioned_nodes(graph_obj, conditioned = NULL) + +cleanup_existing_theme(graph_obj) } \arguments{ \item{graph_obj}{A DAG object created by \code{\link[=qd_dag]{qd_dag()}}.} @@ -24,15 +62,55 @@ get_conditioned_nodes(graph_obj, conditioned = NULL) \item{theme}{A character string indicating the theme to use. Defaults to "base". Set to \code{NULL} to use GraphViz defaults.} -\item{...}{Pass arguments to theme call (e.g., \code{\link[=theme_qd_base]{theme_qd_base()}}), such as -\code{conditioned} or \code{font}.} +\item{conditioned}{A character vector indicating which nodes are conditioned upon. +The shape for these nodes will be set to "rectangle".} + +\item{rankdir}{Direction of node layout. Defaults to "LR", for left-to-right.} -\item{font}{A character vector indicating the font family to use for node labels. -Defaults to "serif".} +\item{layout}{Layout engine. Defaults to "dot", which is the only engine explicitly +used by \link{quickdag}. Other options provided by \link[DiagrammeR:DiagrammeR]{DiagrammeR::DiagrammeR} may or may not work +well.} -\item{conditioned}{A character vector indicating which nodes are conditioned upon. -The shape for these nodes will be set to "square".} +\item{shape}{Standard node shape for a given graph. Defaults for each theme: +"base" = plaintex, "circles" = circle, "pearl" = point. +See \href{https://graphviz.org/doc/info/shapes.html}{Graphviz documentation} for other +options.} + +\item{nodepen}{Size (in points) of node outlines. By default, controlled +by package options specific to each theme, each of which defaults to 0.2.} + +\item{nodewidth}{Width (in inches) of nodes. By default, controlled by package options +specific to each theme. Defaults to 0.} + +\item{nodeheight}{Height (in inches) of nodes. By default, controlled by package +options specific to each theme. Defaults to 0.} + +\item{edgepen}{Size (in points) of edges. By default, controlled +by package options specific to each theme, each of which defaults to 0.2.} + +\item{arrowhead}{Shape of the arrowhead. See \code{\link[DiagrammeR:edge_aes]{DiagrammeR::edge_aes()}}.} + +\item{arrowsize}{Size of the arrowhead. See \code{\link[DiagrammeR:edge_aes]{DiagrammeR::edge_aes()}}.} + +\item{fontname}{Font name for text elements of the graph. By default, controlled by +package options specific to each theme, each of which defaults to "Helvetica". This +is the default in \code{DiagrammeR}.} + +\item{fontsize}{Size of text (in points). By default, controlled by package options +specific to each theme, each of which defaults to 5.} + +\item{fontcolor}{Text color. By default, controlled by package options +specific to each theme, each of which defaults to "black".} + +\item{pointsize}{Size of "point" shape (in points). Applies to "pearl" theme only and +defaults to 0.02.} + +\item{pointcolor}{Border color for the "point" shape. Applies to "pearl" theme only +and defaults to "black".} + +\item{pointfill}{Fill color for the "point" shape. Applies to "pearl" theme only +and defaults to "black".} } \description{ -Apply various pre-fabricated themes to diagrams. +Themes and theming utilies. } diff --git a/man/qd_todagitty.Rd b/man/qd_todagitty.Rd deleted file mode 100644 index 50350fd..0000000 --- a/man/qd_todagitty.Rd +++ /dev/null @@ -1,46 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/qd_todagitty.R -\name{qd_todagitty} -\alias{qd_todagitty} -\title{Identify variables for adjustment} -\usage{ -qd_todagitty( - edgelist, - diagram_type = "dag", - showplot = FALSE, - exposure, - outcome, - ... -) -} -\arguments{ -\item{edgelist}{A vector of edge relationships. Must be strictly organized (see -example for format).} - -\item{diagram_type}{Character identifying the diagram type. Defaults to "dag", but -user can specify another graph type (see dagitty documentation).} - -\item{showplot}{Logical indicating whether to produce a dagitty plot. Defaults to -\code{FALSE}.} - -\item{exposure}{Character. Specify exposure of interest. (Required)} - -\item{outcome}{Character. Specifiy outcome of interest. (Required)} - -\item{...}{Pass arguments to \code{\link[dagitty:adjustmentSets]{dagitty::adjustmentSets()}}. See dagitty documentation -for options.} -} -\description{ -Format an edgelist and send it to dagitty to identify variable adjustment sets. -} -\details{ -The \code{exposure} and \code{outcome} options map to dagitty functions of the same name. -} -\examples{ -edges <- c("A -> { B C D }", - "B -> C", - "E -> { B C }") -# must pass exposure and outcome arguments to dagitty::adjustmentSets() -qd_todagitty(edges, exposure = "A", outcome = "C") -qd_todagitty(edges, exposure = "A", outcome = "C", type = "minimal") -}