D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
samirrayani
Full window
Github gist
baseball
<!DOCTYPE html> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="spinner.css"> <style type="text/css"> body { background:#fff; filter: sepia(.95); -webkit-filter: sepia(.95); } h1 { text-align:center; font-family: monospace; margin-top:50px; text-transform: uppercase; fill: #000; font-size: 24px; } h2 { text-align: center; font-family: monospace; font-size: 12px; text-transform: uppercase; font-weight: 200; } text.division, text.league { font-family: monospace; text-transform: uppercase; font-size:15px; fill:#000; } div.container { width:960px; margin:20px auto; height:500px; } div.league { margin-top:35px; } div.division { display:inline-block; margin-right:5px; } svg { overflow: visible; } path.game-line{ fill:none; stroke-width:1px; stroke: #555; } path.game-line:hover{ stroke-width:2px; } path.game-line.positive { stroke-width:2px; } path.game-line.positive:hover { stroke-width:3px; } text.team-name { font-family: sans-serif; font-size:8px; text-transform: uppercase; fill: #333; } .axis text{ font-family: sans-serif; font-size:11px; text-transform: uppercase; fill: #666; font-weight: 200; } path.domain { fill:none; } .tick line{ fill: none; stroke: #999; shape-rendering: crispEdges; stroke-width: 1px; opacity:.75; } .game-circle { fill: #333; stroke: #fff; stroke-width:1px; } </style> <body> <h1>baseball: diagram showing "net" wins during the 2015 season</h1> <h2>Inspired by <a href="https://tinyletter.com/abovechart/letters/the-chicago-daily-tribune-oct-10-1909" target="_blank">The Chicago Daily Tribune</a>, <a href="https://www.twitter.com/kevinQ" target="_blank">Kevin Quealy</a>, & <a href="https://bl.ocks.org/pstuffa" target="_blank">Paul Buffa</a>. Created by <a href="https://www.twitter.com/samirrayani" target="_blank">Samir Rayani</a></h2> <div class="container"> <div id="cssload-pgloading"> <div class="cssload-loading"></div> </div> </div> <script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js" charset="utf-8"></script> <script> var divisionOrder = ["west", "central", "east"]; var format = d3.time.format("%Y-%m-%d"); var container = d3.select("div.container"); var margin = {top: 5, right: 50, bottom: 5, left: 5}; var width = container.node().getBoundingClientRect().width - margin.left - margin.right, height = container.node().getBoundingClientRect().height - margin.top - margin.bottom; var xScale = d3.time.scale() .range([0, width/3 - margin.right - margin.left]); var yScale = d3.scale.linear() .range([height/2,0]); var xAxis = d3.svg.axis() .scale(xScale) .innerTickSize(height/2) .outerTickSize(0) .tickFormat(d3.time.format("%b")) .orient("top"); var x2Axis = d3.svg.axis() .scale(xScale) .orient("top") .innerTickSize(height/2) .outerTickSize(0) .ticks(d3.time.days, 1) .tickFormat(""); var yAxis = d3.svg.axis() .scale(yScale) .innerTickSize((width-margin.left-margin.right-20)/3) .outerTickSize(0) .ticks(12) .orient("right"); drawChart = function(data) { d3.select("#cssload-pgloading").remove(); var league = container.selectAll(".league") .data(data, function(d) { return d.key; }) .enter() .append("div") .attr("class", "league" ); league.append("svg") .attr("width", 15) .attr("height", height/2) .append("text") .attr("class", "league") .text(function(d){ return d.key == "AL" ? "American League" : "National League"; }) .attr("transform", "translate(0,50) rotate(-90)") .style("text-anchor", "end"); var division = league.selectAll(".division") .data(function(d) { return d.values; }) .enter() .append("div") .attr("class", "division") .style("width", d3.round(width/3) + "px"); var divisionGroup = division.append("svg") .attr("width", (width - margin.left - margin.right)/3) .attr("height", (height + margin.top + margin.bottom)/2) .append("g") .attr("transform","translate(" + margin.left + "," + margin.top + ")"); divisionGroup.append("g") .attr("class", "x axis minor") .attr("transform","translate(0," + height/2 + ")") .call(x2Axis); divisionGroup.append("g") .attr("class", "x axis") .attr("transform","translate(0," + height/2 + ")") .call(xAxis) .selectAll("text") .attr("y", 4) .attr("x", -9) .attr("transform", "rotate(-90)") .style("text-anchor", "end"); divisionGroup.append("g") .attr("class", "y axis") .call(yAxis); divisionGroup.append("text") .text(function(d) { return d.key; }) .attr("class", "division") .attr("transform", "translate(40,"+(height-margin.bottom-margin.top-47)/2+")"); var lineFn = d3.svg.line() .x(function(d) { return xScale(d.formattedDate); }) .y(function(d) { return yScale(d.cumulative); }); var gameLine = divisionGroup.selectAll(".game-line") .data(function(d){ return d.values; }) .enter() .append("g"); gameLine.append("path") .attr("d", function(d) { return lineFn(d.values); }) .attr("class", "game-line") .classed("positive", function(d){ return d.positive; }); gameLine.selectAll(".game-circle") .data(function(d) { return [d.values[d.values.length-1]]; }) .enter() .append("circle") .attr("class", "game-circle") .attr("cx", function(d) { return xScale(d.formattedDate); }) .attr("cy", function(d) { return yScale(d.cumulative); }) .attr("r", function(d,i,c,k) { return (d.last) ? 3 : 0; }); gameLine.selectAll(".team-name") .data(function(d) { return [d.values[d.values.length-1]]; }) .enter() .append("text") .attr("class", "team-name") .text(function(d){ return d.team; }) .attr("transform", function(d){ var x = xScale(d.formattedDate)+5; var y = yScale(d.cumulative)+3+d.nudge; return "translate("+x+","+y+")"; }); }; clean = function(d) { d.formattedDate = format.parse(d.date_str2); d.pct = +d.pct; d.win_loss = (d.result == "W") ? 1 : -1; d.nudge = (d.team=="DET" || d.team=="TBR") ? -5 : 0; d.nudge = (d.team=="BOS" || d.team=="NYY" || d.team=="CHW") ? 3 : d.nudge; return d; }; ready = function(err, data) { var gameByLeagueByDivision = d3.nest() .key(function(d) { return d.league; }) .key(function(d) { return d.division; }) .key(function(d) { return d.team; }) .entries(data); gameByLeagueByDivision.forEach(function(league){ league.values.sort(function(a,b){ return divisionOrder.indexOf(a.key) - divisionOrder.indexOf(b.key); }); league.values.forEach(function(division){ division.values.forEach(function(team){ var cumulative = 0; team.values.forEach(function(game,i){ game.cumulative = cumulative + game.win_loss; cumulative = game.cumulative; game.last = (i==team.values.length-1); }); team.positive = cumulative > 0; }); }); }); xScale.domain(d3.extent(data, function(d){ return d.formattedDate; })); yScale.domain(d3.extent(data, function(d){ return d.cumulative; })); // yScale.domain([-50,50]); drawChart(gameByLeagueByDivision); }; // d3.tsv("lookup.tsv", clean, ready); d3.csv("games.csv", clean, ready); </script> </body>
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js