Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Description to Nodes for sankeyNetwork function #130

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

ivot
Copy link

@ivot ivot commented Jun 28, 2016

Hi Christopher,
I suggest to modify sankeyNetwork function by adding a new field (NodeDesc) into Nodes data frame, which contains description of the node, so that on mouse hover-over the node pop-up message shows not the Node name, but a description for the node. If NodeDesc field is not specified when calling sankeyNetwork function then it is defaulted to NodeID.

This adds flexibility to the information displayed about Network Nodes, while keeping backwards compatibility.

Thank you,
Ivan

ivot added 3 commits June 28, 2016 19:52
Adding Node Description as a parameter to sankeyNetwork function
fixing missing check
Updating Docs with NodeDesc details
@ivot ivot closed this Jun 28, 2016
@ivot ivot reopened this Jun 28, 2016
@christophergandrud
Copy link
Owner

Hi, can you provide a reproducible example for testing?

Adding JSON file with Node Description example
@ivot
Copy link
Author

ivot commented Jun 30, 2016

Hi,

I have uploaded energywDesc.json file.

In this example desc field shows % share of different sources in the target value.

You can test it like below:

Load energy projection data with Node Descriptions

URL <- 'https://raw.githubusercontent.com/ivot/networkD3/master/JSONdata/energywDesc.json'
energy <- jsonlite::fromJSON(URL)

Plot

sankeyNetwork(Links = energy$links, Nodes = energy_nodes_upd, Source = 'source',
Target = 'target', Value = 'value', NodeID = 'name', NodeDesc = 'desc',
units = 'TWh', fontSize = 12, nodeWidth = 30)

@christophergandrud
Copy link
Owner

Hi @ivot. Probably being daft, but where does energy_nodes_upd come from?

@ivot
Copy link
Author

ivot commented Jul 12, 2016

Hi @christophergandrud. I was lazy and used Excel to create node Description and then exported it as JSON.

But here is the R code which does the same using standard energy.json file:

#' Recreate Bostock Sankey diagram: http://bost.ocks.org/mike/sankey/
#' Load energy projection data
URL <- paste0('https://raw.githubusercontent.com/christophergandrud/networkD3/master/JSONdata/',
              'energy.json')
energy <- jsonlite::fromJSON(URL)

add_desc_to_nodes<-function(list_links_nodes){
  require(dplyr)
  #' Add Source share for each Target
  links_wSourceShare<-mutate(group_by(list_links_nodes$links,target), share=paste0(round(value/sum(value)*100, digits = 2),"%") )

  #' Get list of Nodes and add ID which starts with 0, so we can join nodes and links data frames
  nodes_wID<-list_links_nodes$nodes
  nodes_wID$ID<-0:(nrow(nodes_wID)-1)

  #' Add Source Names to Links data frame
  links_wSourceShare<-merge.data.frame(links_wSourceShare, nodes_wID, by.x = "source", by.y = "ID")

  #' Create a Comment as a concatenation of Source Name and Source Share in Target
  links_wSourceShare$comment<-paste0(links_wSourceShare$name," - ",links_wSourceShare$share)

  #' Order Links so that biggest value for each Target is on the top of the list. 
  #' This is needed so that concatenated text shows largest Source on the top of the Description field
  links_wSourceShare<-arrange(links_wSourceShare, target, desc(value))

  #' Combine all Comments into 1 string groupped by Target
  Targets_wComments<-aggregate(comment ~ target, data = links_wSourceShare, paste0, collapse = "\n")

  #' Add Combined Comment back to nodes data frame
  nodes_wComments<-merge.data.frame(nodes_wID, Targets_wComments, by.x = "ID", by.y = "target", all.x = T)

  #' Join Target Name and Combined Comment back to nodes data frame. This is to make Node Description in the format:
  #' <Target Name>:
  #' <Source1 Name> - <Source1 Share in Target>
  #' <Source2 Name> - <Source2 Share in Target>
  #' ...
  nodes_wComments$desc<-paste0(nodes_wComments$name, sapply(nodes_wComments $comment, function(x){if(is.na(x)){""} else {paste0(":\n",x)}}  ))
  #' Keep only Node Name and Node Description in the final nodes data frame
  list_links_nodes$nodes<-nodes_wComments[,c("name","desc")]
  return(list_links_nodes)
}

energy<- add_desc_to_nodes(energy)

#' Plot
sankeyNetwork(Links = energy$links, Nodes = energy$nodes, Source = 'source',
              Target = 'target', Value = 'value', NodeID = 'name', NodeDesc = 'desc',
              units = 'TWh', fontSize = 12, nodeWidth = 30)

@christophergandrud
Copy link
Owner

Would it be possible to condense this so that it can be added as an easily understandable example and test?

@ivot
Copy link
Author

ivot commented Jul 14, 2016

Hi @christophergandrud

I removed function, so it is easy to run step-by-step.
I also condensed the code a bit. Can you please have a look?

#' Recreate Bostock Sankey diagram: http://bost.ocks.org/mike/sankey/
#' Load energy projection data
URL <- paste0('https://raw.githubusercontent.com/christophergandrud/networkD3/master/JSONdata/',
              'energy.json')
energy <- jsonlite::fromJSON(URL)

require(dplyr)
require(networkD3)
#' Get the list of Nodes and add ID which starts with 0, so we can join nodes and links data frames 
#' (our data frames should both be 0-indexed)
nodes_wID<-as.data.frame(list(name=energy$nodes$name,ID=0:(nrow(energy$nodes)-1)))

#' Add % Source Share for each Target as a % of Source Value in Total Value for each Target
#' Add Source Names to Links data frame joining Source field in Links with ID field in Nodes
#' And sort Links so that biggest value for each Target is at the top of the list. 
#' This is needed so that concatenated text shows largest Source on the top of the Description field
links_wSourceShare<-arrange(merge.data.frame(mutate(group_by(energy$links,target)
                                                    , share=paste0(round(value/sum(value)*100, digits = 2),"%") )
                                             , nodes_wID, by.x = "source", by.y = "ID")
                            , target, desc(value))

#' Create a Comment as a concatenation of Source Name and %Source Share in Target
#' This is done by collapsing all Comments into 1 Combined Comment string grouping them by Target. 
#' Now each Target has a description which mentions all Sources it depends on and % Source Share in the Target

Targets_wComments<-aggregate( paste0(name," - ",share) ~ target
                              , data = links_wSourceShare
                              , paste0, sep = " - ", collapse = "\n")
names(Targets_wComments)<-c("target","comment")

#' Add Combined Comment back to Nodes data frame joining by ID in Nodes and Target in Targets_wComments data frame
nodes_wComments<-merge.data.frame(nodes_wID, Targets_wComments, by.x = "ID", by.y = "target", all.x = T)

#' Combine Node Name with Node description.
#' #' This is to make Node Description in the format:
#' <Target Node Name>:
#' <Source1 Name> - <Source1 Share in Target Node>
#' <Source2 Name> - <Source2 Share in Target Node>
#' ...
nodes_wComments$desc<-paste0(nodes_wComments$name, sapply(nodes_wComments$comment
                                                          , function(x){if(is.na(x)){""} else {paste0(":\n",x)}}  ))

#' Keep only Node Name and Node Description in the final nodes data frame
energy$nodes<- nodes_wComments[,c("name","desc")]

#' Plot
sankeyNetwork(Links = energy$links, Nodes = energy$nodes, Source = 'source',
              Target = 'target', Value = 'value', NodeID = 'name', NodeDesc = 'desc',
              units = 'TWh', fontSize = 12, nodeWidth = 30)

@christophergandrud
Copy link
Owner

Hm, I ran the example and nothing happens on mouseover. Any thoughts?

@ivot
Copy link
Author

ivot commented Jul 14, 2016

I assume you did install my version of networkD3

devtools::install_github(repo = "ivot/networkD3")

@christophergandrud
Copy link
Owner

Same thing with your fork (I'm using Chrome to view the plot).

@ivot
Copy link
Author

ivot commented Jul 15, 2016

Hmm, I think this might be due to the fact that in sankeyNetwork.js I have replaced also "
" with "\n" in the following part of the code, because on Mac it did not add new line, but rather shown "
" as a part of one-liner pop-up message.

node.append("rect")
            .attr("height", function(d) { return d.dy; })
            .attr("width", sankey.nodeWidth())
            .style("fill", function(d) {
                return d.color = color_node(d); })
            .style("stroke", function(d) { return d3.rgb(d.color).darker(2); })
            .style("opacity", 0.9)
            .style("cursor", "move")
            .append("title")
            .text(function(d) { return d.desc + "\n" + format(d.value) + 
                " " + options.units; });

Obviously, for Windows "\n" is not enough. To fix this I created a branch ("ChangeBackToBR") for my project, where I changed it now to "\r\n". It works on Mac. Can you please check as well?

devtools::install_github(repo = "ivot/networkD3", ref = "ChangeBackToBR")

@christophergandrud
Copy link
Owner

Hm, still nothing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants