Is there a way to visualize a partially directed graph using R forceNetwork() function?

Viewed 105

I am currently working with the R forceNetwork function of the networkD3 package and I have properly validated the correctness of the Nodes and Links data frames for my graph.

My Nodes data frame (node_df) is like this:

   node_id  node_type node_size
0  T054717 irrelevant        10
1  T095006 irrelevant        10
2  T088658 irrelevant        10
3  T069179 irrelevant        10
4  T009515 irrelevant        10
5  T152167 irrelevant        10
6  T100447 irrelevant        10
7  T150659 irrelevant        10
...

and My Links dataframe (links_df) is like this:

    tid1 tid2 edge_dir
0     37   36       10
1     37    0       10
2     37    1       10
3     37    3       10
...
147   34   35        5
148    7   47        5
149   34   47        5
150   35   47        5
151   36   48        5
152    1   48        5

I set the forceNetwork function like this:

network <- forceNetwork (Links = links_df,
                           Nodes = node_df,
                           Source = "tid1",
                           Target = "tid2",
                           Value = "edge_dir",
                           NodeID = "node_id",
                           Nodesize = "node_size",
                           Group = "node_type",
                           colourScale = JS("d3.scaleOrdinal(d3.schemeCategory20);"),
                           fontSize = 10,
                           linkDistance = 100,
                           radiusCalculation = JS(" Math.sqrt(d.nodesize)+6"), 
                           charge = -30,
                           linkColour = ifelse(links_df$edge_dir == 10, "black","red"),
                           opacity = 1,
                           zoom = TRUE,
                           arrows = ifelse(links_df$edge_dir == 10, TRUE, FALSE),
                           opacityNoHover = TRUE,
                           clickAction = NULL)

I am struggling with the arrows parameter of the function. In fact I would like to specify if there should be a directed edge (arrows = TRUE) or an undirected edge (arrows = FALSE) for each link, by checking the Value parameter.

In my case Value refers to a column of the Links data frame named edge_dir, which specifies if the edge should be directed (edge_dir = 10) or not (edge_dir = 5).

After looking at this stackoverflow link, specify-colors-for-each-link-in-a-force-directed-network-networkd3

I've tried to set the parameter like this: arrows = ifelse(links_df$edge_dir == 10, TRUE, FALSE) but the output graph has arrows where there just should be undirected lines.

Using the same structure in the linkColour parameter: linkColour = ifelse(links_df$edge_dir == 10, "black","red") it works fine, directed edges are black and the ones that should be undirected, but they aren't, are red, as shown in this graph output image

Is it possible to display a graph which has directed and undirected edges by modifying the arrows parameter? Thank you!

1 Answers

This is an imperfect solution, but it's easier than making substantial modifications to the underlying JavaScript yourself. You can use htmlwidgets::onRender() to inject some JavaScript to run just after the plot is generated. Here's an example of how you could turn off the arrows for only those links that have a value equal to 5...

library(tibble)
library(networkD3)
library(htmlwidgets)

node_df <- tibble::tribble(
  ~node_id,  ~node_type,   ~node_size, ~directed,
  "T054717", "irrelevant", 10,       TRUE,
  "T095006", "irrelevant", 10,       FALSE,
  "T088658", "irrelevant", 10,       TRUE,
  "T069179", "irrelevant", 10,       FALSE,
  "T009515", "irrelevant", 10,       TRUE,
  "T152167", "irrelevant", 10,       FALSE,
  "T100447", "irrelevant", 10,       TRUE,
  "T150659", "irrelevant", 10,       FALSE
  )

links_df <- tibble::tribble(
  ~tid1, ~tid2, ~edge_dir,
  0,     1,     10,
  0,     2,     10,
  0,     3,     10,
  1,     3,     10,
  2,     4,     5,
  2,     5,     5,
  3,     6,     5,
  4,     5,     5,
  5,     6,     5,
  5,     6,     5
  )

network <- forceNetwork (Links = links_df,
                         Nodes = node_df,
                         Source = "tid1",
                         Target = "tid2",
                         Value = "edge_dir",
                         NodeID = "node_id",
                         Nodesize = "node_size",
                         Group = "node_type",
                         colourScale = JS("d3.scaleOrdinal(d3.schemeCategory20);"),
                         fontSize = 10,
                         linkDistance = 100,
                         radiusCalculation = JS(" Math.sqrt(d.nodesize)+6"),
                         charge = -30,
                         linkColour = ifelse(links_df$edge_dir == 10, "black","red"),
                         opacity = 1,
                         zoom = TRUE,
                         arrows = ifelse(links_df$edge_dir == 10, TRUE, FALSE),
                         opacityNoHover = TRUE,
                         clickAction = NULL)

htmlwidgets::onRender(network, '
  function(el) {
    d3.select("svg")
      .selectAll(".link")
      .filter(function(d) { return d.value == 5; })
      .style("marker-end", null);
  }')

enter image description here

Related