DiagrammeR

Michael Taylor

2018/05/30

Graph Basics

library("DiagrammeR")

Let’s create a graph object with create_graph() and add some nodes and edges to it. Each node gets a new integer ID upon creation. Each edge also gets an ID starting from 1. The pipes between functions make the whole process readable and understandable.

a_graph <- create_graph() %>% 
  add_node() %>% 
  add_node() %>% 
  add_edge(from = 1, to = 2)
a_graph %>% render_graph()

We can take away an edge by using delete_edge().

b_graph <-
  a_graph %>%
  delete_edge(
    from = 1,
    to = 2)
b_graph %>% render_graph()

We can add a node to the graph while, at the same time, defining edges to or from existing nodes in the graph.

c_graph <-
  b_graph %>%
  add_node(
    from = 1,
    to = 2)
c_graph %>% render_graph()
c_graph
## DiagrammeR Graph // 3 nodes / 2 edges
##   -- directed / connected / DAG / simple
## 
##   NODES / type: <unused> / label: <unused>            info: `get_node_df()`
##     -- no additional node attributes
##   EDGES / rel: <unused>                               info: `get_edge_df()`
##     -- no additional edge attributes
##   SELECTION / <none>
##   CACHE / <none>
##   STORED DFs / <none>
##   GLOBAL ATTRS / 17 are set            info: `get_global_graph_attr_info()`
##   GRAPH ACTIONS / <none>
##   GRAPH LOG / <3 actions> -> add_edge() -> delete_edge() -> add_node()

Any time we add a node or edge to the graph, we can add node or edge aesthetic or data attributes. These can be styling properties (e.g., color, shape), grouping labels (e.g., type and rel), or data values that are useful for calculations and for display purposes. Most node or edge creation functions (depending on whether they create either edges, nodes, or both) have the arguments node_aes, edge_aes, node_data, and edge_data. Using these, we can call the namesake helper functions (node_aes(), edge_aes(), node_data(), and edge_data()) to specifically target the created nodes or edges and bind attribute data. An additional benefit in using the helper functions (for the node/edge aesthetic attributes especially) is that RStudio can provide inline help on attribute names and definitions when typing node_aes( or edge_aes( and pressing the TAB key.

d_graph <-
  c_graph %>%
  add_node(
    type = "type_a",
    node_aes = node_aes(
      color = "steelblue",
      fillcolor = "lightblue",
      fontcolor = "gray35"),
    node_data = node_data(
      value = 2.5)) %>%
  add_edge(
    from = 1,
    to = 3,
    rel = "interacted_with",
    edge_aes = edge_aes(
      color = "red",
      arrowhead = "vee",
      tooltip = "Red Arrow"),
    edge_data = edge_data(
      value = 2.5))
d_graph %>% render_graph()

Creating attributes and setting their values is often useful because we can further work with the attributes (e.g., mutate values or even use them during traversals). Furthermore, we can create aesthetic properties based on numerical or categorical data. This is important for when you want to display your graph diagram using the render_graph() function.

Don’t worry if attribute values weren’t set right during the creation of the associated nodes or edges. They are ways to set attribute values for existing nodes and edges. Functions are available for targeting the specific nodes/edges (i.e., making a selection) and other functions are used to set attribute values for the selected nodes or edges. Often, this can be the more efficient strategy as we can target nodes/edges based on their properties (e.g., degree, relationships to neighbors, etc.). Here is an example where we select a node based on its value attribute and modify its color node aesthetic attribute:

e_graph <-
  d_graph %>%
  select_nodes(conditions = value == 2.5) %>%
  set_node_attrs_ws(node_attr = fillcolor, value = "orange") %>%
  clear_selection()
## `select_nodes()` INFO: created a new selection of 1 node
## `clear_selection()` INFO: cleared an existing selection of 1 node

To explain this a bit, we take the graph object d_graph, select only the nodes that have a node value attribute of exactly 2.5. (We now have an active node selection.) With the selected nodes, we set their node attribute fillcolor with the value "orange". Then we deactivate the selection with clear_selection(). Now, if we view the graph with render_graph() we get this:

e_graph %>% render_graph()

There are quite a few functions that allow you to select nodes (e.g., select_nodes(), select_nodes_by_id(), select_last_nodes_created()) and edges (e.g., select_edges(), select_edges_by_edge_id(), select_last_edges_created()). With these selections, we can apply changes using functions that end with ..._ws() (with selection). As seen, node attributes could be set/replaced with set_node_attrs_ws() but we can also mutate attributes of selected nodes (mutate_node_attrs_ws()), delete selected nodes (delete_nodes_ws()), and even create a subgraph with that selection (create_subgraph_ws()). Selections of nodes or edges can be inverted (where non-selected nodes or edges become the active selection) with invert_selection(), certain nodes/edges can be removed from the active selection with the deselect_nodes()/deselect_edges(), and any selection can and should be eventually cleared with clear_selection().

We can create a graph object and add graph primitives such as paths, cycles, and trees to it.

f_graph <-
  create_graph() %>%
  add_path(n = 3) %>%
  add_cycle(n = 4) %>%
  add_balanced_tree(
    k = 2, h = 2)
f_graph %>% render_graph()

You can add one or more randomly generated graphs to a graph object. Here, let’s add a directed GNM graph with 10 nodes and 15 edges (the set_seed option makes the random graph reproducible).

g_graph <-
  create_graph() %>%
  add_gnm_graph(
    n = 15,
    m = 20,
    set_seed = 23)
g_graph %>% render_graph()

The undirected version of this graph is can be made using:

h_graph <-
  create_graph(
    directed = FALSE) %>%
  add_gnm_graph(
    n = 15,
    m = 20,
    set_seed = 23)
h_graph %>% render_graph()

We can view the graph using render_graph(). There are several layouts to choose from as well (e.g., nicely, tree, kk, fr, etc.).

h_graph %>% render_graph(layout = 'fr')
sessionInfo()
## R version 3.5.1 (2018-07-02)
## Platform: x86_64-w64-mingw32/x64 (64-bit)
## Running under: Windows 10 x64 (build 17134)
## 
## Matrix products: default
## 
## locale:
## [1] LC_COLLATE=English_Canada.1252  LC_CTYPE=English_Canada.1252   
## [3] LC_MONETARY=English_Canada.1252 LC_NUMERIC=C                   
## [5] LC_TIME=English_Canada.1252    
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] bindrcpp_0.2.2   DiagrammeR_1.0.0
## 
## loaded via a namespace (and not attached):
##  [1] Rcpp_0.12.17       plyr_1.8.4         compiler_3.5.1    
##  [4] pillar_1.2.3       RColorBrewer_1.1-2 influenceR_0.1.0  
##  [7] bindr_0.1.1        viridis_0.5.1      tools_3.5.1       
## [10] digest_0.6.15      jsonlite_1.5       viridisLite_0.3.0 
## [13] gtable_0.2.0       evaluate_0.10.1    tibble_1.4.2      
## [16] rgexf_0.15.3       pkgconfig_2.0.1    rlang_0.2.1       
## [19] igraph_1.2.2       rstudioapi_0.7     yaml_2.1.19       
## [22] blogdown_0.7       xfun_0.3           gridExtra_2.3     
## [25] downloader_0.4     dplyr_0.7.6        stringr_1.3.1     
## [28] knitr_1.20         htmlwidgets_1.2    hms_0.4.2         
## [31] grid_3.5.1         rprojroot_1.3-2    tidyselect_0.2.4  
## [34] glue_1.2.0         R6_2.2.2           Rook_1.1-1        
## [37] XML_3.98-1.13      rmarkdown_1.10     bookdown_0.7      
## [40] ggplot2_3.0.0      tidyr_0.8.1        purrr_0.2.5       
## [43] readr_1.1.1        magrittr_1.5       scales_0.5.0      
## [46] backports_1.1.2    htmltools_0.3.6    assertthat_0.2.0  
## [49] colorspace_1.3-2   brew_1.0-6         stringi_1.1.7     
## [52] visNetwork_2.0.4   lazyeval_0.2.1     munsell_0.5.0
## Adding cites for R packages using knitr
knitr::write_bib(.packages(), "packages.bib")

References