1/** 2 * 3 * @returns D3 Sankey SVG 4 */ 5 d3.sankey = function() { 6 var sankey = {}, 7 nodeWidth = 24, 8 nodePadding = 8, 9 size = [1, 1], 10 nodes = [], 11 links = []; 12 13 sankey.nodeWidth = function(_) { 14 if (!arguments.length) return nodeWidth; 15 nodeWidth = +_; 16 return sankey; 17 }; 18 19 sankey.nodePadding = function(_) { 20 if (!arguments.length) return nodePadding; 21 nodePadding = +_; 22 return sankey; 23 }; 24 25 sankey.nodes = function(_) { 26 if (!arguments.length) return nodes; 27 nodes = _; 28 return sankey; 29 }; 30 31 sankey.links = function(_) { 32 if (!arguments.length) return links; 33 links = _; 34 return sankey; 35 }; 36 37 sankey.size = function(_) { 38 if (!arguments.length) return size; 39 size = _; 40 return sankey; 41 }; 42 43 sankey.layout = function(iterations) { 44 computeNodeLinks(); 45 computeNodeValues(); 46 computeNodeBreadths(); 47 computeNodeDepths(iterations); 48 computeLinkDepths(); 49 return sankey; 50 }; 51 52 sankey.relayout = function() { 53 computeLinkDepths(); 54 return sankey; 55 }; 56 57 sankey.link = function() { 58 var curvature = .5; 59 60 function link(d) { 61 var x0 = d.source.x + d.source.dx, 62 x1 = d.target.x, 63 xi = d3.interpolateNumber(x0, x1), 64 x2 = xi(curvature), 65 x3 = xi(1 - curvature), 66 y0 = d.source.y + d.sy + d.dy / 2, 67 y1 = d.target.y + d.ty + d.dy / 2; 68 return "M" + x0 + "," + y0 69 + "C" + x2 + "," + y0 70 + " " + x3 + "," + y1 71 + " " + x1 + "," + y1; 72 } 73 74 link.curvature = function(_) { 75 if (!arguments.length) return curvature; 76 curvature = +_; 77 return link; 78 }; 79 80 return link; 81 }; 82 83 // Populate the sourceLinks and targetLinks for each node. 84 // Also, if the source and target are not objects, assume they are indices. 85 function computeNodeLinks() { 86 nodes.forEach(function(node) { 87 node.sourceLinks = []; 88 node.targetLinks = []; 89 }); 90 links.forEach(function(link) { 91 var source = link.source, 92 target = link.target; 93 if (typeof source === "number") source = link.source = nodes[link.source]; 94 if (typeof target === "number") target = link.target = nodes[link.target]; 95 source.sourceLinks.push(link); 96 target.targetLinks.push(link); 97 }); 98 } 99 100 // Compute the value (size) of each node by summing the associated links. 101 function computeNodeValues() { 102 nodes.forEach(function(node) { 103 node.value = Math.max( 104 d3.sum(node.sourceLinks, value), 105 d3.sum(node.targetLinks, value) 106 ); 107 }); 108 } 109 110 // Iteratively assign the breadth (x-position) for each node. 111 // Nodes are assigned the maximum breadth of incoming neighbors plus one; 112 // nodes with no incoming links are assigned breadth zero, while 113 // nodes with no outgoing links are assigned the maximum breadth. 114 function computeNodeBreadths() { 115 var remainingNodes = nodes, 116 nextNodes, 117 x = 0; 118 119 while (remainingNodes.length) { 120 nextNodes = []; 121 remainingNodes.forEach(function(node) { 122 node.x = x; 123 node.dx = nodeWidth; 124 node.sourceLinks.forEach(function(link) { 125 if (nextNodes.indexOf(link.target) < 0) { 126 nextNodes.push(link.target); 127 } 128 }); 129 }); 130 remainingNodes = nextNodes; 131 ++x; 132 } 133 134 // 135 moveSinksRight(x); 136 scaleNodeBreadths((size[0] - nodeWidth) / (x - 1)); 137 } 138 139 function moveSourcesRight() { 140 nodes.forEach(function(node) { 141 if (!node.targetLinks.length) { 142 node.x = d3.min(node.sourceLinks, function(d) { return d.target.x; }) - 1; 143 } 144 }); 145 } 146 147 function moveSinksRight(x) { 148 nodes.forEach(function(node) { 149 if (!node.sourceLinks.length) { 150 node.x = x - 1; 151 } 152 }); 153 } 154 155 function scaleNodeBreadths(kx) {
156 nodes.forEach(function(node) { 157 node.x *= kx; 158 }); 159 } 160 161 function computeNodeDepths(iterations) { 162 var nodesByBreadth = d3.nest() 163 .key(function(d) { return d.x; }) 164 .sortKeys(d3.ascending) 165 .entries(nodes) 166 .map(function(d) { return d.values; }); 167 168 // 169 initializeNodeDepth(); 170 resolveCollisions(); 171 for (var alpha = 1; iterations > 0; --iterations) { 172 relaxRightToLeft(alpha *= .99); 173 resolveCollisions(); 174 relaxLeftToRight(alpha); 175 resolveCollisions(); 176 } 177 178 function initializeNodeDepth() { 179 var ky = d3.min(nodesByBreadth, function(nodes) { 180 return (size[1] - (nodes.length - 1) * nodePadding) / d3.sum(nodes, value); 181 }); 182 183 nodesByBreadth.forEach(function(nodes) { 184 nodes.forEach(function(node, i) { 185 node.y = i; 186 node.dy = node.value * ky; 187 }); 188 }); 189 190 links.forEach(function(link) { 191 link.dy = link.value * ky; 192 }); 193 } 194 195 function relaxLeftToRight(alpha) { 196 nodesByBreadth.forEach(function(nodes, breadth) { 197 nodes.forEach(function(node) { 198 if (node.targetLinks.length) { 199 var y = d3.sum(node.targetLinks, weightedSource) / d3.sum(node.targetLinks, value); 200 node.y += (y - center(node)) * alpha; 201 } 202 }); 203 }); 204 205 function weightedSource(link) { 206 return center(link.source) * link.value; 207 } 208 } 209 210 function relaxRightToLeft(alpha) { 211 nodesByBreadth.slice().reverse().forEach(function(nodes) { 212 nodes.forEach(function(node) { 213 if (node.sourceLinks.length) { 214 var y = d3.sum(node.sourceLinks, weightedTarget) / d3.sum(node.sourceLinks, value); 215 node.y += (y - center(node)) * alpha; 216 } 217 }); 218 }); 219 220 function weightedTarget(link) { 221 return center(link.target) * link.value; 222 } 223 } 224 225 function resolveCollisions() { 226 nodesByBreadth.forEach(function(nodes) { 227 var node, 228 dy, 229 y0 = 0, 230 n = nodes.length, 231 i; 232 233 // Push any overlapping nodes down. 234 nodes.sort(ascendingDepth); 235 for (i = 0; i < n; ++i) { 236 node = nodes[i]; 237 dy = y0 - node.y; 238 if (dy > 0) node.y += dy; 239 y0 = node.y + node.dy + nodePadding; 240 } 241 242 // If the bottommost node goes outside the bounds, push it back up. 243 dy = y0 - nodePadding - size[1]; 244 if (dy > 0) { 245 y0 = node.y -= dy; 246 247 // Push any overlapping nodes back up. 248 for (i = n - 2; i >= 0; --i) { 249 node = nodes[i]; 250 dy = node.y + node.dy + nodePadding - y0; 251 if (dy > 0) node.y -= dy; 252 y0 = node.y; 253 } 254 } 255 }); 256 } 257 258 function ascendingDepth(a, b) { 259 return a.y - b.y; 260 } 261 } 262 263 function computeLinkDepths() { 264 nodes.forEach(function(node) { 265 node.sourceLinks.sort(ascendingTargetDepth); 266 node.targetLinks.sort(ascendingSourceDepth); 267 }); 268 nodes.forEach(function(node) { 269 var sy = 0, ty = 0; 270 node.sourceLinks.forEach(function(link) { 271 link.sy = sy; 272 sy += link.dy; 273 }); 274 node.targetLinks.forEach(function(link) { 275 link.ty = ty; 276 ty += link.dy; 277 }); 278 }); 279 280 function ascendingSourceDepth(a, b) { 281 return a.source.y - b.source.y; 282 } 283 284 function ascendingTargetDepth(a, b) { 285 return a.target.y - b.target.y; 286 } 287 } 288 289 function center(node) { 290 return node.y + node.dy / 2; 291 } 292 293 function value(link) { 294 return link.value; 295 } 296 297 return sankey; 298 }; 299 300/** 301 * Generate charts based on github open data 302 * @param {string} mean_senti_path 303 * @param {string} eco 304 * @param {string} emo_freq_path 305 * @param {string} nounphrase_path 306 * @param {string} rnag_path 307 */ 308 309function chart(mean_senti_path, eco, emo_freq_path, nounphrase_path,rnag_path) { 310 311 312 /** 313 * 314 * Ecological status csv 315 * 316 */ 317 var tabulate = function (data, columns) { 318 var table = d3.select('#csv').append('table') 319 var thead = table.append('thead') 320 var tbody = table.append('tbody') 321 322 thead.append('tr') 323 .selectAll('th') 324 .data(columns) 325 .enter() 326 .append('th') 327 .text(function (d) { 328 return d 329 }) 330 331 var rows = tbody.selectAll('tr') 332 .data(data) 333 .enter() 334 .append('tr') 335 336 var cells = rows.selectAll('td') 337 .data(function (row) { 338 return columns.map(function (column) { 339 return { 340 column: column, 341 value: row[column] 342 } 343 }) 344 }) 345 .enter() 346 .append('td') 347 .text(function (d) { 348 return d.value 349 }) 350 .style('background-color', function (d) { 351 if (d.value == "Poor") { 352 return 'orange'; 353 } else if (d.value == "Moderate") { 354 return 'yellow'; 355 } else if (d.value == "Good") { 356 return 'green'; 357 } else { 358 return 'red'; 359 } 360 }); 361 362 return table; 363 } 364 365 d3.csv(eco, function (data) { 366 var columns = ['2013', '2014', '2015', '2016', '2019'] 367 tabulate(data, columns) 368 }) 369 370 /** 371 * 372 * Sentiment Polarity Score 373 * 374 */ 375 376 // set the dimensions and margins of the graph 377 var margin0 = { 378 top: 10, 379 right: 30, 380 bottom: 30, 381 left: 60 382 }, 383 width0 = 1200 - margin0.left - margin0.right, 384 height0 = 400 - margin0.top - margin0.bottom; 385 386 // append the svg object to the body of the page 387 var svg0 = d3.select("#my_dataviz") 388 .append("svg") 389 .attr("width", width0 + margin0.left + margin0.right) 390 .attr("height", height0 + margin0.top + margin0.bottom) 391 .append("g") 392 .attr("transform", 393 "translate(" + margin0.left + "," + margin0.top + ")"); 394 395 //Read the data for graph 396 d3.csv(mean_senti_path, 397 // When reading the csv, I must format variables: 398 function (d) { 399 return { 400 date: d3.timeParse("%Y-%m-%d")(d.Date), 401 value: d.mean_senti 402 } 403 }, 404 405 function (data) { 406 407 // Add X axis --> it is a date format 408 var x = d3.scaleTime() 409 .domain(d3.extent(data, function (d) { 410 return d.date; 411 })) 412 .range([0, width0]); 413 xAxis = svg0.append("g") 414 .style("stroke", "black") 415 .style("font-size", "16px") 416 .style("font-family", "'M PLUS Rounded 1c', sans-serif") 417 .attr("transform", "translate(0," + height0 + ")") 418 .call(d3.axisBottom(x)); 419 420 // Add Y axis 421 var y = d3.scaleLinear() 422 .domain([-5, 5]) 423 .range([height0, 0]); 424 yAxis = svg0.append("g") 425 .style("stroke", "black") 426 .style("font-family", "'M PLUS Rounded 1c', sans-serif") 427 .style("font-size", "16px") 428 .call(d3.axisLeft(y)); 429 430 // Add a clipPath: everything out of this area won't be drawn. 431 var clip = svg0.append("defs").append("svg:clipPath") 432 .attr("id", "clip") 433 .append("svg:rect") 434 .attr("width", width0) 435 .attr("height", height0) 436 .attr("x", 0) 437 .attr("y", 0); 438 439 // Add brushing 440 var brush = d3.brushX() // Add the brush feature using the d3.brush function 441 .extent([ 442 [0, 0], 443 [width0, height0] 444 ]) // initialise the brush area: start at 0,0 and finishes at width,height: it means I select the whole graph area 445 .on("end",
446 updateChart) // Each time the brush selection changes, trigger the 'updateChart' function 447 448 // Create the line variable: where both the line and the brush take place 449 var line = svg0.append('g') 450 .attr("clip-path", "url(#clip)") 451 452 // Add the line 453 line.append("path") 454 .datum(data) 455 .attr("class", "line") // I add the class line to be able to modify this line later on. 456 .attr("fill", "none") 457 .attr("stroke", "black") 458 .attr("stroke-width", 1.5) 459 .attr("d", d3.line() 460 .x(function (d) { 461 return x(d.date) 462 }) 463 .y(function (d) { 464 return y(d.value) 465 }) 466 ) 467 468 // Add the brushing 469 line 470 .append("g") 471 .attr("class", "brush") 472 .call(brush); 473 474 // A function that set idleTimeOut to null 475 var idleTimeout 476 477 function idled() { 478 idleTimeout = null; 479 } 480 481 // A function that update the chart for given boundaries 482 function updateChart() { 483 484 // What are the selected boundaries? 485 extent = d3.event.selection 486 487 // If no selection, back to initial coordinate. Otherwise, update X axis domain 488 if (!extent) { 489 if (!idleTimeout) return idleTimeout = setTimeout(idled, 490 350); // This allows to wait a little bit 491 x.domain([4, 8]) 492 } else { 493 x.domain([x.invert(extent[0]), x.invert(extent[1])]) 494 line.select(".brush").call(brush.move, 495 null) // This remove the grey brush area as soon as the selection has been done 496 } 497 498 // Update axis and line position 499 xAxis.transition().duration(1000).call(d3.axisBottom(x)) 500 line 501 .select('.line') 502 .transition() 503 .duration(1000) 504 .attr("d", d3.line() 505 .x(function (d) { 506 return x(d.date) 507 }) 508 .y(function (d) { 509 return y(d.value) 510 }) 511 ) 512 } 513 514 // If user double click, reinitialize the chart 515 svg0.on("dblclick", function () { 516 x.domain(d3.extent(data, function (d) { 517 return d.date; 518 })) 519 xAxis.transition().call(d3.axisBottom(x)) 520 line 521 .select('.line') 522 .transition() 523 .attr("d", d3.line() 524 .x(function (d) { 525 return x(d.date) 526 }) 527 .y(function (d) { 528 return y(d.value) 529 }) 530 ) 531 }); 532 533 /** 534 * 535 * Reasons for not achieving good 536 * 537 */ 538 var units = "Counts"; 539 540 // set the dimensions and margins of the graph 541 var margin = {top: 10, right: 10, bottom: 10, left: 10}, 542 width = 700 - margin.left - margin.right, 543 height = 300 - margin.top - margin.bottom; 544 545 // format variables 546 var formatNumber = d3.format(",.0f"), // zero decimal places 547 format = function(d) { return formatNumber(d) + " " + units; }, 548 color = d3.scaleOrdinal(d3.schemeCategory10); 549 550 // append the svg object to the body of the page 551 var svg = d3.select("#chart").append("svg") 552 .attr("width", width + margin.left + margin.right) 553 .attr("height", height + margin.top + margin.bottom) 554 .append("g") 555 .attr("transform", 556 "translate(" + margin.left + "," + margin.top + ")"); 557 558 // Set the sankey diagram properties 559 var sankey = d3.sankey() 560 .nodeWidth(36) 561 .nodePadding(40) 562 .size([width, height]); 563 564 var path = sankey.link(); 565 566 // load the data 567 d3.csv(rnag_path, function(error, data) { 568 569 //set up graph in same style as original example but empty 570 graph = {"nodes" : [], "links" : []}; 571 572 data.forEach(function (d) {
573 graph.nodes.push({ "name": d.source }); 574 graph.nodes.push({ "name": d.target }); 575 graph.links.push({ "source": d.source, 576 "target": d.target, 577 "value": +d.value }); 578 }); 579 580 // return only the distinct / unique nodes 581 graph.nodes = d3.keys(d3.nest() 582 .key(function (d) { return d.name; }) 583 .object(graph.nodes)); 584 585 // loop through each link replacing the text with its index from node 586 graph.links.forEach(function (d, i) { 587 graph.links[i].source = graph.nodes.indexOf(graph.links[i].source); 588 graph.links[i].target = graph.nodes.indexOf(graph.links[i].target); 589 }); 590 591 // now loop through each nodes to make nodes an array of objects 592 // rather than an array of strings 593 graph.nodes.forEach(function (d, i) { 594 graph.nodes[i] = { "name": d }; 595 }); 596 597 sankey 598 .nodes(graph.nodes) 599 .links(graph.links) 600 .layout(32); 601 602 // add in the links 603 var link = svg.append("g").selectAll(".link") 604 .data(graph.links) 605 .enter().append("path") 606 .attr("class", "link") 607 .attr("d", path) 608 .style("stroke-width", function(d) { return Math.max(1, d.dy); }) 609 .sort(function(a, b) { return b.dy - a.dy; }); 610 611 // add the link titles 612 link.append("title") 613 .text(function(d) { 614 return d.source.name + " â " + 615 d.target.name + "\n" + format(d.value); }); 616 617 // add in the nodes 618 var node = svg.append("g").selectAll(".node") 619 .data(graph.nodes) 620 .enter().append("g") 621 .attr("class", "node") 622 .attr("transform", function(d) { 623 return "translate(" + d.x + "," + d.y + ")"; }) 624 .call(d3.drag() 625 .subject(function(d) { 626 return d; 627 }) 628 .on("start", function() { 629 this.parentNode.appendChild(this); 630 }) 631 .on("drag", dragmove)); 632 633 // add the rectangles for the nodes 634 node.append("rect") 635 .attr("height", function(d) { return d.dy; }) 636 .attr("width", sankey.nodeWidth()) 637 .style("fill", function(d) { 638 return d.color = color(d.name.replace(/ .*/, "")); }) 639 .style("stroke", function(d) { 640 return d3.rgb(d.color).darker(2); }) 641 .append("title") 642 .text(function(d) { 643 return d.name + "\n" + format(d.value); }); 644 645 // add in the title for the nodes 646 node.append("text") 647 .attr("x", -6) 648 .attr("y", function(d) { return d.dy / 2; }) 649 .attr("dy", ".35em") 650 .attr("text-anchor", "end") 651 .attr("transform", null) 652 .text(function(d) { return d.name; }) 653 .filter(function(d) { return d.x < width / 2; }) 654 .attr("x", 6 + sankey.nodeWidth()) 655 .attr("text-anchor", "start"); 656 657 // the function for moving the nodes 658 function dragmove(d) { 659 d3.select(this) 660 .attr("transform", 661 "translate(" 662 + d.x + "," 663 + (d.y = Math.max( 664 0, Math.min(height - d.dy, d3.event.y)) 665 ) + ")"); 666 sankey.relayout(); 667 link.attr("d", path); 668 } 669 }); 670 671 /** 672 * 673 * Emmotional Classifcation Frequency 674 * 675 */ 676 // set the dimensions and margins of the graph 677 var margin1 = { 678 top: 20, 679 right: 20, 680 bottom: 40, 681 left: 185 682 }, 683 width1 = 500 - margin.left - margin.right, 684 height2 = 400 - margin.top - margin.bottom; 685 686 // append the svg object to the body of the page 687 var svg1 = d3.select("#my_dataviz1") 688 .append("svg") 689 .attr("width", width1 + margin1.left + margin1.right) 690 .attr("height", height2 + margin1.top + margin1.bottom) 691 .append("g") 692 .attr("transform", 693 "translate(" + margin1.left + "," + margin1.top + ")"); 694 695 // Parse the Data 696 d3.csv(emo_freq_path, function (data) { 697 698 // Add X axis 699 var x = d3.scaleLinear() 700 .domain([0, 50]) 701 .range([0, width]); 702 svg1.append("g") 703 .attr("transform", "translate(0," + height + ")") 704 .style("stroke", "black") 705 .style("font-family", "'M PLUS Rounded 1c', sans-serif") 706 .style("font-size", "16px") 707 .call(d3.axisBottom(x)) 708 .selectAll("text") 709 .attr("transform", "translate(-10,0)rotate(-45)") 710 .style("text-anchor", "end"); 711 712 // Y axis 713 var y = d3.scaleBand() 714 .range([0, height]) 715 .domain(data.map(function (d) { 716 return d.sentiment; 717 })) 718 .padding(.1); 719 svg1.append("g") 720 .style("stroke", "black") 721 .style("font-size", "16px") 722 .style("font-family", "'M PLUS Rounded 1c', sans-serif") 723 .call(d3.axisLeft(y)) 724 725 //Bars 726 svg1.selectAll("myRect") 727 .data(data) 728 .enter() 729 .append("rect") 730 .attr("x", x(0)) 731 .attr("y", function (d) { 732 return y(d.sentiment); 733 }) 734 .attr("width", function (d) { 735 return x(d.percent); 736 }) 737 .attr("height", y.bandwidth()) 738 .attr("fill", "black") 739 740 }) 741 742 /** 743 * 744 * Common nounphrases word cloud 745 * 746 */ 747 748 // set the dimensions and margins of the graph 749 var margin5 = { 750 top: 20, 751 right: 45, 752 bottom: 20, 753 left: 20 754 }, 755 width3 = 500 - margin.left - margin.right, 756 height3 = 400 - margin.top - margin.bottom; 757 758 // append the svg object to the body of the page 759 var svg4 = d3.select("#my_dataviz4").append("svg") 760 .attr("width", width3 + margin5.left + margin5.right) 761 .attr("height", height3 + margin5.top + margin5.bottom) 762 .append("g"); 763 764 d3.csv(nounphrase_path, function (data) { 765 766 767 // Constructs a new cloud layout instance. It run an algorithm to find the position of words that suits your requirements 768 // Wordcloud features that are different from one word to the other must be here 769 var layout = d3.layout.cloud() 770 .size([width3, height3]) 771 .words(data.map(function(d) { return {text: d.text, size:d.n}; })) 772 .padding(5) //space between words 773 .rotate(function() { return ~~(Math.random() * 2) * 90; }) 774 .fontSize(function(d) { return d.size*1.2; }) // font size of words 775 .on("end", draw); 776 layout.start(); 777 778 // This function takes the output of 'layout' above and draw the words 779 // Wordcloud features that are THE SAME from one word to the other can be here 780 function draw(words) { 781 svg4 782 .append("g") 783 .attr("transform", "translate(" + layout.size()[0] / 2 + "," + layout.size()[1] / 2 + ")") 784 .selectAll("text") 785 .data(words) 786 .enter().append("text") 787 .style("fill", "black") 788 .attr("text-anchor", "middle") 789 .style("font-family", "Impact") 790 .style("font-size", function(d) {return d.size + "px"; }) 791 .attr("transform", function(d) { 792 return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")"; 793 }) 794 .text(function(d) { return d.text; }); 795 } 796 797 }); 798 799 }) 800}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.