Disclaimer: I'm an R newbie, so I may be overlooking something really obvious here...
I am currently working on a sankeyNetwork diagram using R, and I am facing a problem that almost seems to be a bug, but I'm completely clueless...
I've googled extensively, and haven't been able to find anybody else reporting the same...
The problem is that in my code I currently have 7 nodes, and 5 links. When I plot the diagram, everything works fine:
Plot 1, everything working fine
This is the code for Plot 1:
library(networkD3)
# List of nodes (portfolios & targets)
nodes = data.frame("trialnodes" =
c("portfolio1", # 0
"portfolio2", # 1
"portfolio3", # 2
"portfolio4", # 3
"target1", # 4
"target2", # 5
"target3" # 6
))
# List of links
links = as.data.frame(matrix(c(
0,4,2,
1,6,1,
2,3,1,
2,6,1,
3,5,1),
byrow = TRUE, ncol = 3))
# Column names of data frame
names(links) = c("source", "target", "value")
# check
links
# Sankey Diagram
# Colour scale
colourScale = JS("d3.scaleOrdinal(d3.schemeCategory20);")
# Diagram
sankeyNetwork(Links = links, Nodes = nodes,
Source = "source", Target = "target", Value = "value", NodeID = "trialnodes",
fontSize = 14, nodeWidth = 10, nodePadding = 140, iterations = 0,
colourScale = colourScale)
however, as soon as I add one more node, and 1 more link, the plot's format becomes completely broken, showing the links between nodes as simple gray thin lines (not representing the Value). The nodeWidth and nodePadding specifications also get ignored :(
Plot 2, links as thin gray lines
This is the code for Plot 2:
library(networkD3)
# List of nodes (portfolios & targets)
nodes = data.frame("trialnodes" =
c("portfolio1", # 0
"portfolio2", # 1
"portfolio3", # 2
"portfolio4", # 3
"target1", # 4
"target2", # 5
"target3", # 6
"target4" # 7
))
# List of links
links = as.data.frame(matrix(c(
0,4,2,
0,7,1,
1,6,1,
2,3,1,
2,6,1,
3,5,1),
byrow = TRUE, ncol = 3))
# Column names of data frame
names(links) = c("source", "target", "value")
# check
links
# Sankey Diagram
# Colour scale
colourScale = JS("d3.scaleOrdinal(d3.schemeCategory20);")
# Diagram
sankeyNetwork(Links = links, Nodes = nodes,
Source = "source", Target = "target", Value = "value", NodeID = "trialnodes",
fontSize = 14, nodeWidth = 10, nodePadding = 140, iterations = 0,
colourScale = colourScale)
Can anybody spot what's going on? I hope someone can help... I'm desperate D: Thank you very much in advance! :)
Either reduce your nodePadding value to something reasonable, or make the viewer/browser-window size large enough to show the max number of nodes you have in a column * 140 pixels (plus some for the actual node) and then refresh (in your second example that comes out to ~600px).
library(networkD3)
# List of nodes (portfolios & targets)
nodes = data.frame("trialnodes" =
c("portfolio1", # 0
"portfolio2", # 1
"portfolio3", # 2
"portfolio4", # 3
"target1", # 4
"target2", # 5
"target3", # 6
"target4" # 7
))
# List of links
links = as.data.frame(matrix(c(
0,4,2,
0,7,1,
1,6,1,
2,3,1,
2,6,1,
3,5,1),
byrow = TRUE, ncol = 3))
# Column names of data frame
names(links) = c("source", "target", "value")
# check
links
# Sankey Diagram
# Colour scale
colourScale = JS("d3.scaleOrdinal(d3.schemeCategory20);")
# Diagram
sankeyNetwork(Links = links, Nodes = nodes,
Source = "source", Target = "target", Value = "value", NodeID = "trialnodes",
fontSize = 14, nodeWidth = 10, nodePadding = 14, iterations = 0,
colourScale = colourScale)
Related
Due to a mysterious problem discussed on this thread, I currently can't plot a Sankey Diagram using R.
Since some reported being unable to replicate my problem on their R environment, and at this point I'm willing to do anything for my diagram, it occurred to me, maybe somebody could help me plotting the actual code I need, and sending me the image...
So, this is the code:
library(networkD3)
#List of nodes (portfolios & targets)
nodes = data.frame("finalnodes" =
c("portfolio1", #0
"portfolio2", #1
"portfolio3", #2
"portfolio4", #3
"target1", #4
"target2", #5
"target3", #6
"target4", #7
"target5", #8
"target6", #9
"target7", #10
"target8" #11
))
#List of links
links = as.data.frame(matrix(c(
0,4,1,
2,10,1,
2,6,1,
3,4,1,
3,5,1,
3,7,1,
3,8,1,
3,9,1,
3,11,1),
byrow = TRUE, ncol = 3))
#Column names of data frame
names(links) = c("source", "target", "value")
#check
links
#Sankey Diagram
#Colour scale
colourScale = JS("d3.scaleOrdinal(d3.schemeCategory20);")
#Diagram
sankeyNetwork(Links = links, Nodes = nodes,
Source = "source", Target = "target", Value = "value", NodeID = "finalnodes",
fontSize = 14, nodeWidth = 10, nodePadding = 140, iterations = 0,
colourScale = colourScale)
Needless to say, when I plot it, the format is all messed up.
The plot I get:
I have been using the sankeyD3 package to create SankeyNetworks and the 'NodePosX' feature isn't working for me yet. The 'NodePosX' feature is not in the 'networkD3' package but it is in the 'sankeyD3' package.
To help illustrate the problem that I am having, I have edited the example from akraemer007 that was posted here to include the X positions of the nodes (see below) but it's still not working in the way that he had originally wanted, with manual control over the x-position of the 'Opted-Out' node.
We're aiming for something like this, but without the small line from 'Opted-Out' to 'Activated':
library(devtools)
devtools::install_github("fbreitwieser/sankeyD3")
library(sankeyD3)
name <- c('Enrolled', 'Opted-Out', 'Invited', 'Activated')
xpos <- c(0, 1, 1, 2)
nodes <- data.frame(name, xpos)
source <- c(0, 0, 2, 1)
target <- c(1, 2, 3, 3)
value <- c(20, 80, 60, 0)
links <- data.frame(source, target, value)
sankeyNetwork(Links = links, Nodes = nodes, Source = "source",
Target = "target", Value = "value", NodeID = "name",NodePosX = "xpos",
units = "TWh", fontSize = 12, nodeWidth = 30)
Assuming the last row in your links data frame is only there to force the plot to look the way you want and not part of the actual data you want to plot, you can achieve this with networkd3 using the sinksRight = FALSE parameter.
library(networkD3)
name <- c('Enrolled', 'Opted-Out', 'Invited', 'Activated')
xpos <- c(0, 1, 1, 2)
nodes <- data.frame(name, xpos)
source <- c(0, 0, 2)
target <- c(1, 2, 3)
value <- c(20, 80, 60)
links <- data.frame(source, target, value)
sankeyNetwork(Links = links, Nodes = nodes, Source = "source",
Target = "target", Value = "value", NodeID = "name",
units = "TWh", fontSize = 12, nodeWidth = 30, sinksRight = FALSE)
I wish to implement onClick on this sankey diagram such that by clicking on a link, I should see the details of the link between the two nodes. It's like the plotly_click function
library(networkD3)
nodes = data.frame("name" =
c("r1", # Node 0
"r2", # Node 1
"r3", # Node 2
"r4", # Node 3
"r5", # Node 4
"r6", # Node 5
"r7", # Node 6
"Blood Test", # Node 7
"Check Out", # Node 8
"Discuss Results", # Node 9
"MRI Scan", # Node 10
"Registration", # Node 11
"Triage and Assessment", # Node 12
"X-ray"))# Node 13
links = as.data.frame(matrix(c(
0, 11, 500, # Each row represents a link. The first number
1, 12, 500, # represents the node being conntected from.
2, 7, 237, # the second number represents the node connected to.
3, 10, 236,
4, 13, 261,
5, 9, 495,
6, 8, 492),# The third number is the value of the node
byrow = TRUE, ncol = 3))
names(links) = c("source", "target", "value")
sankeyNetwork(Links = links, Nodes = nodes,
Source = "source", Target = "target",
Value = "value", NodeID = "name",
fontSize= 12, nodeWidth = 30)
You can add click events using htmlwidgets::onRender function. It's not clear what details you want to see, but this, for example, will show a link's value in an alert box when you click it...
library(htmlwidgets)
sn <- sankeyNetwork(Links = links, Nodes = nodes,
Source = "source", Target = "target",
Value = "value", NodeID = "name",
fontSize = 12, nodeWidth = 30)
clickJS <- 'd3.selectAll(".link").on("click", function(d){ alert(d.value); })'
htmlwidgets::onRender(sn, clickJS)
Here is an interesting solution based on the parset package:
devtools::install_github("timelyportfolio/parsetR")
library(parsetR)
links$source <- as.character(factor(links$source, labels=nodes[1:7,1]))
links$target <- as.character(factor(links$target, labels=nodes[8:14,1]))
parset(links, dimensions = c('source', 'target'),
value = htmlwidgets::JS("function(d) {return d.value}"),
tension = 0.5)
Is there a way to use a node as a link to an external website using the function forceNetwork() in the networkD3 package in r? I was thinking maybe modifying the clickAction?
Example data:
library(networkD3)
data(MisLinks)
data(MisNodes)
# Create a random URL in the nodes dataset
MisNodes$URL <- paste0("http://www.RANDOMLINK_", sample(1:100, NROW(MisNodes)), ".com")
head(MisNodes)
MyClickScript <- 'alert(d.index)'
forceNetwork(Links = MisLinks, Nodes = MisNodes,
Source = "source", Target = "target",
Value = "value", NodeID = "name",
Group = "group", opacity = 0.8,
clickAction = MyClickScript)
Desired outcome: When a user clicks on a node, a new tab will open (e.g. window.open) pointing to the associated URL for the node - How can I get clickAction to point to MisNodes$URL[d.index]?
networkD3 design does not make this easy. Here is one way to answer. I'll try to comment inline to explain what we are doing in each step.
library(networkD3)
# example from ?forceNetwork
data(MisLinks)
data(MisNodes)
# Create graph
fn <- forceNetwork(
Links = MisLinks, Nodes = MisNodes, Source = "source",
Target = "target", Value = "value", NodeID = "name",
Group = "group", opacity = 0.4, zoom = TRUE
)
# let's look at our forceNetwork
# nodes are provided to JavaScript
# in a nodes data.frame
str(fn$x$nodes)
# make up some links to demonstrate
# how we can add them to our nodes df
fn$x$nodes$hyperlink <- paste0(
'http://en.wikipedia.org/wiki/Special:Search?search=',
MisNodes$name
)
# then with our hyperlinks in our data
# we can define a click action to open
# the hyperlink for each node in a new window
fn$x$options$clickAction = 'window.open(d.hyperlink)'
fn
I am using the sankeyNetwork function in the networkD3 package in R using as an example the code found here. However, all I get is a blank screen. The diagram is supposed to show the flow of infections between age groups (by gender). My code is as below:
library(RCurl)
library(networkD3)
edges <- read.csv(curl("https://raw.githubusercontent.com/kilimba/data/master/infection_flows.csv"),stringsAsFactors = FALSE )
nodes = data.frame(ID = unique(c(edges$Source, edges$Target)))
nodes$indx =0
for (i in 1:nrow(nodes)){
nodes[i,]["indx"] = i - 1
}
edges2 <- merge(edges,nodes,by.x = "Source",by.y = "ID")
edges2$Source <-NULL
names(edges2) <- c("target","value","source")
edges2 <- merge(edges2,nodes,by.x = "target",by.y = "ID")
edges2$target <- NULL
names(edges2) <- c("value","source","target")
nodes$indx <- NULL
# Plot
sankeyNetwork(Links = edges2, Nodes = nodes,
Source = "source", Target = "target",
Value = "value", NodeID = "ID",
width = 700, fontsize = 12, nodeWidth = 30)
Are you sure there are no errors printed in your R console?
This works for me with two small modifications:
Load the curl package as well at the beginning
library("curl")
The fontsize parameter apparently does not work and should be removed.
# Plot
sankeyNetwork(Links = edges2, Nodes = nodes,
Source = "source", Target = "target",
Value = "value", NodeID = "ID",
width = 700, #fontsize = 12,
nodeWidth = 30)
Adjusting fontsize does work, but your argument is missing a capitalization: fontSize
sankeyNetwork(Links = edges2, Nodes = nodes,
Source = "source", Target = "target",
Value = "value", NodeID = "ID",
width = 700, fontSize = 12,
nodeWidth = 30)
you do not need RCurl, read.csv is able to read directly from a URL
it's probably safer to use the stringsAsFactors = FALSE option when creating the nodes data.frame
as others have pointed out, you must make sure that the source and target variables in the links data are numeric, and that they are zero-indexed
as others have pointed out, the font size parameter is properly named fontSize
I have provided a more direct way of creating the links data with numeric indexes of the nodes in the nodes data.frame
library(networkD3)
edges <- read.csv("https://raw.githubusercontent.com/kilimba/data/master/infection_flows.csv",stringsAsFactors = FALSE)
nodes = data.frame(ID = unique(c(edges$Source, edges$Target)), stringsAsFactors = FALSE)
edges$Source <- match(edges$Source, nodes$ID) - 1
edges$Target <- match(edges$Target, nodes$ID) - 1
sankeyNetwork(Links = edges, Nodes = nodes,
Source = "Source", Target = "Target",
Value = "Value", NodeID = "ID",
width = 700, fontSize = 12, nodeWidth = 30)
I solved it for me by making sure that source, target and values were all numeric.
For example:
Energy$links$value <- as.numeric(Energy$links$value)