簡體   English   中英

使用D3 JS在Sankey圖中顯示百分比

[英]Displaying Percentages in a Sankey Diagram using D3 JS

我是D3 js的新手,並嘗試按節點在sankey圖上顯示百分比。 我可以在sankey中顯示百分比,但是它會按總值計算百分比。 任何幫助表示贊賞。 謝謝!!

 d3.sankey = function() { var sankey = {}, nodeWidth = 24, nodePadding = 8, size = [1, 1], nodes = [], links = []; sankey.nodeWidth = function(_) { if (!arguments.length) return nodeWidth; nodeWidth = +_; return sankey; }; sankey.nodePadding = function(_) { if (!arguments.length) return nodePadding; nodePadding = +_; return sankey; }; sankey.nodes = function(_) { if (!arguments.length) return nodes; nodes = _; return sankey; }; sankey.links = function(_) { if (!arguments.length) return links; links = _; return sankey; }; sankey.size = function(_) { if (!arguments.length) return size; size = _; return sankey; }; sankey.layout = function(iterations) { computeNodeLinks(); computeNodeValues(); computeNodeBreadths(); computeNodeDepths(iterations); computeLinkDepths(); return sankey; }; sankey.relayout = function() { computeLinkDepths(); return sankey; }; sankey.link = function() { var curvature = .5; function link(d) { var x0 = d.source.x + d.source.dx, x1 = d.target.x, xi = d3.interpolateNumber(x0, x1), x2 = xi(curvature), x3 = xi(1 - curvature), y0 = d.source.y + d.sy + d.dy / 2, y1 = d.target.y + d.ty + d.dy / 2; return "M" + x0 + "," + y0 + "C" + x2 + "," + y0 + " " + x3 + "," + y1 + " " + x1 + "," + y1; } link.curvature = function(_) { if (!arguments.length) return curvature; curvature = +_; return link; }; return link; }; // Populate the sourceLinks and targetLinks for each node. // Also, if the source and target are not objects, assume they are indices. function computeNodeLinks() { nodes.forEach(function(node) { node.sourceLinks = []; node.targetLinks = []; }); links.forEach(function(link) { var source = link.source, target = link.target; if (typeof source === "number") source = link.source = nodes[link.source]; if (typeof target === "number") target = link.target = nodes[link.target]; source.sourceLinks.push(link); target.targetLinks.push(link); }); } // Compute the value (size) of each node by summing the associated links. function computeNodeValues() { nodes.forEach(function(node) { node.value = Math.max( d3.sum(node.sourceLinks, value), d3.sum(node.targetLinks, value) ); }); } // Iteratively assign the breadth (x-position) for each node. // Nodes are assigned the maximum breadth of incoming neighbors plus one; // nodes with no incoming links are assigned breadth zero, while // nodes with no outgoing links are assigned the maximum breadth. function computeNodeBreadths() { var remainingNodes = nodes, nextNodes, x = 0; while (remainingNodes.length) { nextNodes = []; remainingNodes.forEach(function(node) { node.x = x; node.dx = nodeWidth; node.sourceLinks.forEach(function(link) { nextNodes.push(link.target); }); }); remainingNodes = nextNodes; ++x; } // moveSinksRight(x); scaleNodeBreadths((width - nodeWidth) / (x - 1)); } function moveSourcesRight() { nodes.forEach(function(node) { if (!node.targetLinks.length) { node.x = d3.min(node.sourceLinks, function(d) { return d.target.x; }) - 1; } }); } function moveSinksRight(x) { nodes.forEach(function(node) { if (!node.sourceLinks.length) { node.x = x - 1; } }); } function scaleNodeBreadths(kx) { nodes.forEach(function(node) { node.x *= kx; }); } function computeNodeDepths(iterations) { var nodesByBreadth = d3.nest() .key(function(d) { return dx; }) .sortKeys(d3.ascending) .entries(nodes) .map(function(d) { return d.values; }); // initializeNodeDepth(); resolveCollisions(); for (var alpha = 1; iterations > 0; --iterations) { relaxRightToLeft(alpha *= .99); resolveCollisions(); relaxLeftToRight(alpha); resolveCollisions(); } function initializeNodeDepth() { var ky = d3.min(nodesByBreadth, function(nodes) { return (size[1] - (nodes.length - 1) * nodePadding) / d3.sum(nodes, value); }); nodesByBreadth.forEach(function(nodes) { nodes.forEach(function(node, i) { node.y = i; node.dy = node.value * ky; }); }); links.forEach(function(link) { link.dy = link.value * ky; }); } function relaxLeftToRight(alpha) { nodesByBreadth.forEach(function(nodes, breadth) { nodes.forEach(function(node) { if (node.targetLinks.length) { var y = d3.sum(node.targetLinks, weightedSource) / d3.sum(node.targetLinks, value); node.y += (y - center(node)) * alpha; } }); }); function weightedSource(link) { return center(link.source) * link.value; } } function relaxRightToLeft(alpha) { nodesByBreadth.slice().reverse().forEach(function(nodes) { nodes.forEach(function(node) { if (node.sourceLinks.length) { var y = d3.sum(node.sourceLinks, weightedTarget) / d3.sum(node.sourceLinks, value); node.y += (y - center(node)) * alpha; } }); }); function weightedTarget(link) { return center(link.target) * link.value; } } function resolveCollisions() { nodesByBreadth.forEach(function(nodes) { var node, dy, y0 = 0, n = nodes.length, i; // Push any overlapping nodes down. nodes.sort(ascendingDepth); for (i = 0; i < n; ++i) { node = nodes[i]; dy = y0 - node.y; if (dy > 0) node.y += dy; y0 = node.y + node.dy + nodePadding; } // If the bottommost node goes outside the bounds, push it back up. dy = y0 - nodePadding - size[1]; if (dy > 0) { y0 = node.y -= dy; // Push any overlapping nodes back up. for (i = n - 2; i >= 0; --i) { node = nodes[i]; dy = node.y + node.dy + nodePadding - y0; if (dy > 0) node.y -= dy; y0 = node.y; } } }); } function ascendingDepth(a, b) { return ay - by; } } function computeLinkDepths() { nodes.forEach(function(node) { node.sourceLinks.sort(ascendingTargetDepth); node.targetLinks.sort(ascendingSourceDepth); }); nodes.forEach(function(node) { var sy = 0, ty = 0; node.sourceLinks.forEach(function(link) { link.sy = sy; sy += link.dy; }); node.targetLinks.forEach(function(link) { link.ty = ty; ty += link.dy; }); }); function ascendingSourceDepth(a, b) { return a.source.y - b.source.y; } function ascendingTargetDepth(a, b) { return a.target.y - b.target.y; } } function center(node) { return node.y + node.dy / 2; } function value(link) { return link.value; } return sankey; }; 
 <!DOCTYPE html> <meta charset="utf-8"> <title>SANKEY Experiment</title> <style> .node rect { cursor: move; fill-opacity: .9; shape-rendering: crispEdges; } .node text { pointer-events: none; text-shadow: 0 1px 0 #fff; } .link { fill: none; stroke: #000; stroke-opacity: .2; } .link:hover { stroke-opacity: .5; } </style> <Head> <link rel="stylesheet" type="text/css" href="sankey.css"> <h1> <div class="container"> <div>Equipment</div><div>Resolution</div><div>Repeat_call_Flag</div> </div> </h1> </Head> <body> <p id="chart"> <script src="http://d3js.org/d3.v3.min.js"></script> <script src="sankey.js"></script> <script> var units = "Widgets"; var margin = {top: 10, right: 10, bottom: 10, left: 10}, width = 1800 - margin.left - margin.right, height = 1300 - margin.top - margin.bottom; var formatNumber = d3.format(",.0f"), // zero decimal places format = function(d) { return formatNumber(d) + " " + units; }, color = d3.scale.category20(); // append the svg canvas to the page var svg = d3.select("#chart").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // Set the sankey diagram properties var sankey = d3.sankey() .nodeWidth(20) .nodePadding(30) .size([width, height]); var path = sankey.link(); // load the data (using the timelyportfolio csv method) d3.csv("sankey.csv", function(error, data) { //set up graph in same style as original example but empty graph = {"nodes" : [], "links" : []}; data.forEach(function (d) { graph.nodes.push({ "name": d.source }); graph.nodes.push({ "name": d.target }); // changed the name here graph.links.push({ "source": d.source, "target": d.target, "value": +d.value }); }); // return only the distinct / unique nodes graph.nodes = d3.keys(d3.nest() .key(function (d) { return d.name; }) .map(graph.nodes)); // loop through each link replacing the text with its index from node graph.links.forEach(function (d, i) { graph.links[i].source = graph.nodes.indexOf(graph.links[i].source); graph.links[i].target = graph.nodes.indexOf(graph.links[i].target); }); //now loop through each nodes to make nodes an array of objects // rather than an array of strings graph.nodes.forEach(function (d, i) { graph.nodes[i] = { "name": d }; }); sankey .nodes(graph.nodes) .links(graph.links) .layout(50); // add in the links var link = svg.append("g").selectAll(".link") .data(graph.links) .enter() .append("path") .attr("class", "link") .attr("id",function(d,i) { return "linkLabel" + i; }) .attr("d", path) .style("stroke-width", function(d) { return Math.max(1, d.dy); }) .sort(function(a, b) { return b.dy - a.dy; }); // % calculation var total = d3.sum(data, function(d) {return d.value}); // % data label experiment var toPercent = d3.format("0.1%") //show travel points /*var labelText = svg.selectAll(".labelText") .data(graph.links) .enter() .append("text") .attr("class","labelText") .attr("dx",130) .attr("dy",0) .append("textPath") .attr("xlink:href",function(d,i) { return "#linkLabel" + i;}) .text(function(d,i) { return d.source.name + " → " + d.target.name + " : " + d.value;})*/ /* // add the link titles link.append("title") .text(function(d) { return d.source.name + " → " + d.target.name + "\\n" + format(d.value); }); */ // add in the nodes var node = svg.append("g").selectAll(".node") .data(graph.nodes) .enter().append("g") .attr("class", "node") .attr("transform", function(d) { return "translate(" + dx + "," + dy + ")"; }) .call(d3.behavior.drag() .origin(function(d) { return d; }) .on("dragstart", function() { this.parentNode.appendChild(this); }) .on("drag", dragmove)); // add the rectangles for the nodes node.append("rect") .attr("height", function(d) { return d.dy; }) .attr("width", sankey.nodeWidth()) .style("fill", function(d) { return d.color = color(d.name.replace(/ .*/, "")); }) .style("stroke", function(d) { return d3.rgb(d.color).darker(2); }) .append("title") .text(function(d) { return d.name + "\\n" + format(d.value); }); // add in the title for the nodes node.append("text") .attr("x", -6) .attr("y", function(d) { return d.dy / 2; }) .attr("dy", ".35em") .attr("text-anchor", "end") .attr("transform", null) .text(function(d) { return d.name + " : " + d.value + " : " + toPercent(d.value / total); }) //added d.value and % .filter(function(d) { return dx < width / 2; }) .attr("x", 6 + sankey.nodeWidth()) .attr("text-anchor", "start"); // the function for moving the nodes function dragmove(d) { d3.select(this).attr("transform", "translate(" + dx + "," + ( dy = Math.max(0, Math.min(height - d.dy, d3.event.y)) ) + ")"); sankey.relayout(); link.attr("d", path); } }); </script> </body> </html> 

如果我理解正確,則需要計算任何節點值相對於total的比率,以使每一列的總和為100%。

您只需要考慮源節點(不是任何鏈接目標的源節點)來計算total ,即可得出所需的總數。 用以下公式替換當前的total公式:

var total = d3.sum(graph.nodes, function(d) {
     if (d.targetLinks.length>0) 
        return 0; //node is not source, exclude it from the total
     else 
        return d.value; //node is a source: add its value to the sum
     });    

如果沒有一個運行中的示例或您想要的明確想法,很難說更多。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM