D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
kent37
Full window
Github gist
Expense ratio vs yield
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Expense Ratio vs Yield</title> <script type="text/javascript" src="https://d3js.org/d3.v3.js"></script> <style type="text/css"> body { background-color: white; font-family: Helvetica, Arial, sans-serif; } h1 { font-size: 24px; margin: 0; } p { font-size: 14px; margin: 10px 0 0 0; } button { background-color: orange; } svg { background-color: white; } circle:hover { fill: orange; } .axis path, .axis line { fill: none; stroke: black; shape-rendering: crispEdges; } .axis text { font-family: sans-serif; font-size: 11px; } </style> </head> <body> <h1>Expense Ratio vs Yield</h1> <p>Expense Ratio vs Yield for a selection of mutual funds and ETFs.</p> <p id='legend'>Showing one-year yield.</p> <script type="text/javascript"> var w = 700; var h = 600; var padding = [ 20, 10, 50, 50 ]; //Top, right, bottom, left var xScale = d3.scale.linear() .range([ padding[3], w - padding[1] - padding[3] ]); var yScale = d3.scale.linear() .range([ padding[0], h - padding[2] ]); var colorScale = d3.scale.category10(); var xAxis = d3.svg.axis() .scale(xScale) .orient("bottom"); var yAxis = d3.svg.axis() .scale(yScale) .orient("left"); var svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); d3.csv("ExpenseVsYield.csv", function(data) { xScale.domain([ d3.min(data, function(d) { return +d.ExpenseRatio; }), d3.max(data, function(d) { return +d.ExpenseRatio; }) ]); // The domain of yScale has to be the max of all the yields yScale.domain([ d3.max(data, function(d) { return d3.max([+d.Y1, +d.Y3, +d.Y5, +d.Y10]); }), d3.min(data, function(d) { return d3.min([+d.Y1, +d.Y3, +d.Y5, +d.Y10]); }) ]); colorScale.domain([0, data.length-1]); // Make a group for each stock var groups = svg.selectAll("g") .data(data) .enter() .append("g"); // A function to create a translation string for a stock var translation = function(d, year) { return "translate(" + xScale(d.ExpenseRatio) + "," + yScale(d[year]) + ")"; } // A function to format title text var titleText = function(d, year, text) { return d.Name + "'s expense ratio is " + d.ExpenseRatio + ", and " + text + " yield is " + d[year] + ""; } // Locate the group groups.attr("transform", function(d) { return translation(d, 'Y1');}) .append("title") .text(function(d) { return titleText(d, 'Y1', 'one-year'); }); // Add a circle and a text legend for each group groups.append('circle') .attr("cx", 0) .attr("cy", 0) .attr("r", 5) .attr("fill", function(d, i) { return colorScale(i%10); }); groups.append('text') .attr({ x: 8, y: 4, class: 'label', 'font-size': '10px', fill: function(d, i) { return colorScale(i%10); } }) .text(function (d) { return d.Symbol; }); // Add the axes svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + (h - padding[2] + 10) + ")") .call(xAxis); svg.append("g") .attr("class", "y axis") .attr("transform", "translate(" + (padding[3] - 10) + ",0)") .call(yAxis); // Add event handlers for the controls // This is the function that will be called yield = function(year, text) { // Reset text label positions svg.selectAll('.label').attr({x: 8, y: 4}); groups.select('title') .text(function(d) { return titleText(d, year, text); }); groups.transition(1500) .attr("transform", function(d) { return translation(d, year);}) .attr('visibility', function(d) { return d[year]=='NA' ? 'hidden' : 'visible';}) .each('end', arrangeLabels(year)); d3.select('p#legend') .transition(750).style('color', 'white') .transition(750).style('color', 'black') .text('Showing ' + text + ' yield.'); } d3.select('button#y1').on('click', function () { yield('Y1', 'one-year'); }); d3.select('button#y3').on('click', function () { yield('Y3', 'three-year'); }); d3.select('button#y5').on('click', function () { yield('Y5', 'five-year'); }); d3.select('button#y10').on('click', function () { yield('Y10', 'ten-year'); }); // Set the initial positions arrangeLabels('Y1'); }); // Test for intersection of two rectangles var intersectRect = function(r1, r2) { return !(r2.left > r1.right || r2.right < r1.left || r2.top > r1.bottom || r2.bottom < r1.top); }; // Arrange the labels so they don't overlap // From https://bl.ocks.org/larskotthoff/11406992 // and https://blog.safaribooksonline.com/2014/03/11/solving-d3-label-placement-constraint-relaxing/ function arrangeLabels(year) { var alpha = 0.5; // First reset them all var textLabels = svg.selectAll('.label'); //textLabels.attr({x: 8, y: 4}); var again = false; textLabels.each(function() { if (this.__data__[year] == 'NaN') return; var that = this, thatRect = this.getBoundingClientRect(); textLabels.each(function() { if (this == that || this.__data__[year]=='NaN') return; var thisRect = this.getBoundingClientRect(); if (!intersectRect(thisRect, thatRect)) return; var deltaY = thatRect.top - thisRect.top; // If the labels collide, we'll push each // of the two labels up and down a little bit. again = true; var sign = deltaY > 0 ? 1 : -1; var adjust = sign * alpha; var dthat = d3.select(that); dthat.attr("y", +dthat.attr('y') + adjust); var dthis = d3.select(this); dthis.attr("y", +dthis.attr('y') - adjust); }); }); if(again) { setTimeout(function() { arrangeLabels(year); },20); } } </script> <p>Click the buttons to change the displayed yield.</p> <button class='click' id='y1'>1-Year</button> <button class='click' id='y3'>3-Year</button> <button class='click' id='y5'>5-Year</button> <button class='click' id='y10'>10-Year</button> </body> </html>
Modified
http://d3js.org/d3.v3.js
to a secure url
https://d3js.org/d3.v3.js