christophergandrud / networkD3

D3 JavaScript Network Graphs from R
http://christophergandrud.github.io/networkD3
649 stars 270 forks source link

Make it possible to show decimals in links and nodes #292

Closed Ferdinandinio closed 2 years ago

Ferdinandinio commented 2 years ago

Hello, please make it possible to show decimals in links and nodes. Especially when wanting to display percentages, this is an issue. Values are only rounded to the next full digit.

Best regards

cjyetman commented 2 years ago

{networkD3} has a hardcoded formatting option set inside of its JavaScript. You can reset it to be whatever you want using the example answer you linked to... critically setting the format option in the .format(",.2f") bit, where in this case the 2 means print two digits after the decimal point.

library(networkD3)
library(htmlwidgets)

nodes <- data.frame(name = c('a','b'))
links <- data.frame(source = 0, target = 1, value = 0.34)

p <- sankeyNetwork(
  Links = links,
  Source = "source",
  Target = "target",
  Value = "value",
  Nodes = nodes,
  NodeID = "name",
  fontSize = 12,
  nodeWidth = 30,
  iterations = 0
)

customJS <- '
function(el,x) { 
    var link = d3.selectAll(".link");

    var format = d3.formatLocale({"decimal": ",", "thousands": ".", "grouping": [3], "currency": ["", "\u00a0€"]}).format(",.2f");

    link.select("title").select("body")
        .html(function(d) { return "<pre>" + d.source.name + " \u2192 " + d.target.name +
            "\\n" + format(d.value) + "<pre>"; });
}
'
onRender(p, customJS)
Screen Shot 2022-05-10 at 17 54 17
Ferdinandinio commented 2 years ago

Hello and thank you for your quick reply @cjyetman I believe I found a similar approach to yours somewhere on stackoverflow already. The thing is that this approach does only change the format of the links, while it also removes the unit sign. E.g. node before: 12 %, node after: 12 %; link before: 12 %, link after: 11,52. What I need: Link/Node after 11.52 %

cjyetman commented 2 years ago

If you provide a working reproducible example, then someone might be able to give you exactly what you want.... otherwise we're just making guesses about what you precisely want.

Ferdinandinio commented 2 years ago

Here is the reproducible example containing the issues as explained above:


library(networkD3)
library(htmlwidgets)

nodes <- data.frame(name = c('a','b','c','a','b','c'))
links <- data.frame(source = c(0,1,2,0,1,2), target = c(3,4,5,4,5,3), value = c(0.36,0.49,0.61,0.12,0.76,0.15))

p <- sankeyNetwork(
  Links = links,
  Source = "source",
  Target = "target",
  Value = "value",
  Nodes = nodes,
  NodeID = "name",
  fontSize = 12,
  nodeWidth = 30,
  iterations = 0,
  units = "%"
)

customJS <- '
function(el,x) { 
    var link = d3.selectAll(".link");

    var format = d3.formatLocale({"decimal": ",", "thousands": ".", "grouping": [3], "currency": ["", "\u00a0€"]}).format(",.2f");

    link.select("title").select("body")
        .html(function(d) { return "<pre>" + d.source.name + " \u2192 " + d.target.name +
            "\\n" + format(d.value) + "<pre>"; });
}
'
onRender(p, customJS)
cjyetman commented 2 years ago

The formats possible with D3 are specified here https://github.com/d3/d3-format/tree/v3.1.0#locale_format

If you want to format a value as a percentage, use d3.format("%")... that will format a value of 0.36 as "36.000000%"

If you want to restrict the number of digits beyond the decimal point, use something like d3.format(".2%")... that will format a value of 0.36 as "36.00%", or 0.1152 as "11.52%"

If you want to format the title text for the nodes, then you'll have to apply the formatting to the nodes also...

library(networkD3)
library(htmlwidgets)

nodes <- data.frame(name = c('a','b','c','a','b','c'))
links <- data.frame(source = c(0,1,2,0,1,2), target = c(3,4,5,4,5,3), value = c(0.36,0.49,0.61,0.12,0.76,0.15))

p <- sankeyNetwork(
  Links = links,
  Source = "source",
  Target = "target",
  Value = "value",
  Nodes = nodes,
  NodeID = "name",
  fontSize = 12,
  nodeWidth = 30,
  iterations = 0,
  units = "%"
)

customJS <- '
function(el,x) { 
  var format = d3.format(".2%");

  d3.select(el)
    .selectAll(".link")
    .select("title")
    .select("body")
    .html(d => format(d.value));

  d3.select(el)
    .selectAll(".node")
    .select("title")
    .select("body")
    .html(d => format(d.value));
}
'
onRender(p, customJS)
Screen Shot 2022-05-11 at 13 16 25 Screen Shot 2022-05-11 at 13 16 18
Ferdinandinio commented 2 years ago

Thank you so much! To include the Link and Node names (as default) when hovering and also to add a space between the number and the % sign I changed the customJS code accordingly:


customJS <- '
function(el,x) { 
  var format = d3.format(".2f");

  d3.select(el)
    .selectAll(".link")
    .select("title")
    .select("body")
            .html(function(d) { return "<pre>" + d.source.name + " \u2192 " + d.target.name +
            "\\n" + format(d.value) + " %" + "<pre>"; });

  d3.select(el)
    .selectAll(".node")
    .select("title")
    .select("body")
            .html(function(d) { return "<pre>" + d.name +
            "\\n" + format(d.value) + " %" + "<pre>"; });

}
'