R - state of the art performance of visNetwork vs networkd3 in a Shiny app (2017)
Asked Answered
M

1

5

I want to color vertices on a "large" graph (less than 500 nodes) according to their centrality, and allow the user to delete an edge or vertex on click events. When a vertex or edge is deleted, a new centrality score is recalculated on the graph and the colors of the edges and vertexes are rendered again.

I've been looking at the visNetwork and networkD3 R packages. This 2015 blog post mentions that visNetwork is better featured that networkD3, but I've read a couple of SO questions mentioning how networkD3 has changed and expanded its functionality.

In which of these two packages would it be simpler to satisfy my requirements?

Mettah answered 27/5, 2017 at 0:43 Comment(5)
How large is large?Sukkoth
networkD3 does not directly support that level of customizationUnlettered
@MikeWise actually, less than 500 nodes.Mettah
Note there are two D3 packages, d3network and networkD3 package, both ports of D3 to R for networks. Confusing.Sukkoth
Mike, as I noticed here, christophergandrud.github.io/d3Network "Attention: Development has moved to networkD3. d3Network is no longer supported."Coolidge
S
17

You know, I need to know this myself for a project I am starting, and the only way to really know is to try it out.

So I wrote an app.

library(shiny)
library(ggplot2)
library(visNetwork)
library(networkD3)
library(RColorBrewer)
set.seed(123)

u <- shinyUI(fluidPage(
  titlePanel("Network Library Comparison"),

  sidebarLayout(position = "left",
    sidebarPanel( h2("Parameters"),
      selectInput("mode","Network:",c("MisNodes","Random","Circular"),"Random"),
      numericInput("nnodes","Nodes:",10,1,10000,1),
      sliderInput("edgefak","Edge Factor:",0,10,2,step=0.1),
      numericInput("ngroups","Groups:",5,1,11,1),
      actionButton("gennet","Generate"),
      textOutput("networkstat")
    ),
  mainPanel(h2("Network Plots"),
    tabsetPanel(
      tabPanel("networkD3", forceNetworkOutput("networkD3",,height="500px"),
                                           style = "background-color: #eeeeee;"),
      tabPanel("visNetwork",visNetworkOutput("visnetwork",height="500px"),
                                            style = "background-color: #eeeeee;")
    )
   )
  )
))

#MisLinks and MisNodes are a standard example from networkD3
data(MisLinks)
data(MisNodes)
fmtarrstr <- function(arr){
  # first add ' surrounding every element
  qarr <- sprintf("'%s'",as.character(arr))
  # now concactinate them together seperated with ,
  paste(qarr,collapse=",")
}
clrpal <- brewer.pal(n=11,name="Spectral")
clrscale <- sprintf('d3.scaleOrdinal() .domain([%s]) .range([%s]);',
                                       fmtarrstr(1:11),fmtarrstr(clrpal))


s <- shinyServer(function(input, output){


  net <- reactiveValues(nodes=NULL,edges=NULL,groups=NULL)

  observeEvent(input$gennet,{
    print("regenerating network")
    mode <- input$mode
    nn <- input$nnodes
    ng <- input$ngroups
    edgefak <- input$edgefak
    if(mode=="MisNodes"){
      nodes <- data.frame(id = 0:(nrow(MisNodes)-1),
                          label=MisNodes$name,
                          title=MisNodes$name,
                          group=MisNodes$group,
                          color=clrpal[MisNodes$group+1],
                          size=MisNodes$size)
      edges <- data.frame(from = MisLinks$source, to = MisLinks$target)
      net$groups <- data.frame(id=1:11,colors<-clrpal[1:11])
    } else if(mode=="Random"){
      nodes <- data.frame(id = 0:(nn-1),
                          label=0:(nn-1),
                          title=0:(nn-1),
                          group=sample(0:(ng-1),nn,replace=T),
                          size=10)
      nodes$color <- clrpal[nodes$group+1]
      nedge <- trunc(nn*edgefak)
      frvek <- sample(0:(nn-1),nedge,replace=T)
      tovek <- sample(0:(nn-1),nedge,replace=T)
      edges <- data.frame(from = frvek, to = tovek)
      net$groups <- data.frame(id=1:ng,colors<-clrpal[1:ng])
    } else if(mode=="Circular"){

      nodes <- data.frame(id = 0:(nn-1),
                          label=0:(nn-1),
                          title=0:(nn-1),
                          group=sample(0:(ng-1),nn,replace=T),
                         size=10)
      nodes$color <- clrpal[nodes$group+1]
      nedge <- nn
      frvek <- 0:(nn-1)
      tovek <- c(1:(nn-1),0)
      edges <- data.frame(from = frvek, to = tovek)
      net$groups <- data.frame(id=1:ng,colors<-clrpal[1:ng])
    }
    net$nodes <- nodes
    net$edges <- edges
    net$groups <- data.frame(id=1:ng,colors<-clrpal[1:ng])
  })
  output$visnetwork <- renderVisNetwork({ 
    req(net$edges)
    netout <- visNetwork(net$nodes,net$edges) 
    netout
  })
  output$networkD3 <- renderForceNetwork({ 
    req(net$edges)
    netout <- forceNetwork(
      Links  = net$edges, Nodes   = net$nodes,
      Source = "from", Target  = "to",
      NodeID  = "label", Nodesize="size",
      Group="group",opacity=1.0, zoom=T, fontSize = 12,
      colourScale = JS(clrscale)) 
    netout
  })
  output$networkstat <- renderText({
    sprintf("\nNodes:%d  Edges:%d Groups:%d",
              nrow(net$nodes),nrow(net$edges),nrow(net$groups))
  })
})
shinyApp(u,s)

Looks like this:

enter image description here

In conclusion I would say they both have pluses and minuses, overall I think visNetwork is easier to use, and looks cooler with those spline curves, but networkD3 is definitely a lot faster at initialization for bigger networks. visNetwork gets painful during initialization at 200 nodes already, although it is fine once it gets drawn.

Notes:

  • the networkD3 mouse zoom only works in a real browser (I was using Chrome). I couldn't get it to work in the R-Studio browser thing. Drove me crazy.
  • for that matter, everything was noticeably faster in Chrome than in the R-Studio browser. Do your benchmarking and real work in a real browser.
Sukkoth answered 28/5, 2017 at 12:28 Comment(3)
It's true than vis.js coordinates algorithm is not the fastest. But you have some way of improvment : use igraph to compute coordinates before rendering visIgraphLayout(), see network during stabilization visPhysics(stabilization = FALSE), or/and disable smooth edge visEdges(smooth = FALSE) and finally play with physics ?visPhysicsMove
It would be great if you also add other frameworks such as cytoscape and threejs.Tasman
I might sometime. I put a fair amount of work into it but judging from the upvotes and views it is not that hot a topic.Sukkoth

© 2022 - 2024 — McMap. All rights reserved.