Search code examples
rgraphvizdiagrammer

weight attribute of edges not working on `DiagrammeR` R package


I am trying to add some edge attributes to a GraphViz graph using the DiagrammeR package in R. In particular, I want to add the attributes arrowhead and weight, but only the former is added to the graph but not the latter. The example below is not real one, but a super-reduced version of it for the sake of reproducibility and clarity.

library(tidyverse)
library(DiagrammeR)
# create nodes
nd <- tibble(
  label =  c("A", "B", "C", "D"), 
  rank  =  c("1", "0", "2", "2")
)

node <- create_node_df(
  n     = nrow(nd),
  label = nd$label,
  id    =  c(1:nrow(nd)),
  # rank  = nd$rank
)

# create edges
ed <- tribble(
  ~from, ~to, ~arrowhead, ~weight,
  1,     2,   'normal',       "5",
  1,     3,   'normal',       "1",
  3,     4,   'normal',       "1",
  3,     2,   'normal',       "1"
)
edge <- create_edge_df(
  from      = ed$from,
  to        = ed$to,
  arrowhead = ed$arrowhead,
  weight    = ed$weight
)


# create graph
graph <-
  create_graph(
    nodes_df   = node,
    edges_df   = edge) %>%
  add_global_graph_attrs(
    attr       = "overlap",
    value      = "false",
    attr_type  = "graph") %>%
  add_global_graph_attrs(
    attr       = "layout",
    value      = "dot",
    attr_type  = "graph")

Yet, when I generate the dot code, the weight attribute is not part of the edges but arrowhead is.

graph %>%
  generate_dot() %>%
  cat()

#> digraph {
#> 
#> graph [layout = 'dot',
#>        outputorder = 'edgesfirst',
#>        bgcolor = 'white',
#>        overlap = 'false']
#> 
#> node [fontname = 'Helvetica',
#>       fontsize = '10',
#>       shape = 'circle',
#>       fixedsize = 'true',
#>       width = '0.5',
#>       style = 'filled',
#>       fillcolor = 'aliceblue',
#>       color = 'gray70',
#>       fontcolor = 'gray50']
#> 
#> edge [fontname = 'Helvetica',
#>      fontsize = '8',
#>      len = '1.5',
#>      color = 'gray80',
#>      arrowsize = '0.5']
#> 
#>   '1' [label = 'A'] 
#>   '2' [label = 'B'] 
#>   '3' [label = 'C'] 
#>   '4' [label = 'D'] 
#> '1'->'2' [arrowhead = 'normal'] 
#> '1'->'3' [arrowhead = 'normal'] 
#> '3'->'4' [arrowhead = 'normal'] 
#> '3'->'2' [arrowhead = 'normal'] 
#> }

I also tried adding the attribute using set_edge_attrs() and it is still not working.

graph <- graph %>% 
  set_edge_attrs(edge_attr = "weight", 
                 values = "5", 
                 from = 1, 
                 to = 2
                 )
# digraph {
#   
#   graph ...
#  {trimmed output}
#
#   '1'->'2' [arrowhead = 'normal'] 
#   '1'->'3' [arrowhead = 'normal'] 
#   '3'->'4' [arrowhead = 'normal'] 
#   '3'->'2' [arrowhead = 'normal'] 
# }

In the end, I had to modify the dot code by hand (see below), but It is not ideal given that I have to produce several diagrams from info in data frames.

graph2 <- "digraph {

graph [layout = 'dot',
       outputorder = 'edgesfirst',
       bgcolor = 'white',
       overlap = 'false']

node [fontname = 'Helvetica',
      fontsize = '10',
      shape = 'circle',
      fixedsize = 'true',
      width = '0.5',
      style = 'filled',
      fillcolor = 'aliceblue',
      color = 'gray70',
      fontcolor = 'gray50']

edge [fontname = 'Helvetica',
     fontsize = '8',
     len = '1.5',
     color = 'gray80',
     arrowsize = '0.5']

  '1' [label = 'A'] 
  '2' [label = 'B'] 
  '3' [label = 'C'] 
  '4' [label = 'D'] 
'1'->'2' [arrowhead = 'normal', weight = 5] 
'1'->'3' [arrowhead = 'normal', weight = 0] 
'3'->'4' [arrowhead = 'normal', weight = 0] 
'3'->'2' [arrowhead = 'normal', weight = 0]  
}
"
grViz(graph2)

I highly appreciate any help.

Thanks. Best,


Solution

  • I'm not sure if it is a bug or if "weight" is being ignored deliberately, but anyway the culprit is a helper function in DiagrammeR that generates a vector of allowed attributes and generate_dot() only allows attributes from that vector.

    # DiagrammeR/R/utils.R
    gv_edge_attributes <- function() {
    
      c("style", "penwidth", "color", "arrowsize",
        "arrowhead", "arrowtail",
        "fontname", "fontsize", "fontcolor",
        "len", "tooltip", "URL",
        "label", "labelfontname", "labelfontsize",
        "labelfontcolor", "labeltooltip", "labelURL",
        "edgetooltip", "edgeURL",
        "headtooltip", "headURL",
        "headclip", "headlabel", "headport",
        "tailtooltip", "tailURL",
        "tailclip",  "taillabel", "tailport",
        "dir", "decorate")
    }
    

    If you add "weight" to that list – or modify the generate_dot to allow all attributes without checks –, your example works just fine.