block by jalapic 4a0febfc13b6a529b8e465e8ad9f2364

Bump Plot

Full Screen

Bump plot for 80 popular female names. Data from baby-name-scraper collected from Social Security Administration by Eric Socolofsky.

Also see Popular Baby Names by Nadieh Bremer for another application of bump charts to this dataset.

forked from syntagmatic‘s block: Bump Plot

index.html

 <!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
  background: #fff;
  font: 12px sans-serif;
}
svg,
canvas {
  position: absolute;
}
.axis path,
.axis line {
  fill: none;
  stroke: #d0d0d0;
}
.x.axis .tick text {
  font: 10px sans-serif;
  fill: #555;
}
</style>
<body>
<canvas></canvas>
<svg></svg>
<script src="//d3js.org/d3.v4.0.0-alpha.40.js"></script>
<script>
var num = 80;

var margin = {top: 30, right: 60, bottom: 30, left: 60};
var width = 960,
    height = 800;

var devicePixelRatio = window.devicePixelRatio || 1;

var canvas = d3.select("canvas")
    .attr("width", width * devicePixelRatio)
    .attr("height", height * devicePixelRatio)
    .style("width", width + "px")
    .style("height", height + "px");

var svg = d3.select("svg")
    .style("width", width + "px")
    .style("height", height + "px");

var color = d3.scaleOrdinal()
  .range(["#DB7F85", "#50AB84", "#4C6C86", "#C47DCB", "#B59248", "#DD6CA7", "#E15E5A", "#5DA5B3", "#725D82", "#54AF52", "#954D56", "#8C92E8", "#D8597D", "#AB9C27", "#D67D4B", "#D58323", "#BA89AD", "#357468", "#8F86C2", "#7D9E33", "#517C3F", "#9D5130", "#5E9ACF", "#776327", "#944F7E"]);

var xscale = d3.scaleLinear()
  .domain([1880,2015])
  .range([margin.left,width-margin.right]);

var xaxis = d3.axisBottom()
  .scale(xscale)
  .ticks(20)
  .tickFormat(d3.format(""));

var xaxis2 = d3.axisTop()
  .scale(xscale)
  .ticks(20)
  .tickFormat(d3.format(""));

svg.append("g")
  .attr("class", "x axis")
  .attr("transform", "translate(0," + (height-margin.bottom) + ")")
  .call(xaxis);

svg.append("g")
  .attr("class", "x axis")
  .attr("transform", "translate(0," + (margin.top-10) + ")")
  .call(xaxis2);

var yscale = d3.scaleLinear()
  .domain([0,num])
  .range([margin.top, height-margin.bottom]);

var radius = d3.scaleSqrt()
  .domain([0,0.1])
  .range([0,4]);

d3.csv("girl-names.csv", function(error, data) {
  data.forEach(function(d) {
    d.fraction = +d.fraction;
    d.year = +d.year;
  });

  // only girls
  data = data.filter(function(d) {
    return d.sex == "f";
  });

  // nest by name and rank by total popularity
  var nested = d3.nest()
    .key(function(d) { return d.name; })
    .rollup(function(leaves) {
      return {
        data: leaves,
        sum: d3.sum(leaves, function(d) { return d.fraction; })
      };
    })
    .sortValues(function(a,b) { return a.sum - b.sum;  })
    .entries(data);

  var topnames = nested.slice(0,num).map(function(d) { return d.key; });
  data = data.filter(function(d) {
    return topnames.indexOf(d.name) > -1;
  });

  // nest by name and rank by total popularity
  window.byYear = {}
  d3.nest()
    .key(function(d) { return d.year; })
    .key(function(d) { return d.name; })
    .sortValues(function(a,b) { return a.fraction - b.fraction;  })
    .rollup(function(leaves,i) {
      return leaves[0].fraction;
    })
    .entries(data)
    .forEach(function(year) {
      byYear[year.key] = {};
      year.values.forEach(function(name,i) {
        byYear[year.key][name.key] = i;
      });
    });

  var ctx = canvas.node().getContext("2d");
  ctx.scale(devicePixelRatio, devicePixelRatio);

  ctx.fillStyle = "#1a1a1a";
  ctx.strokeStyle = "#1a1a1a";
  ctx.textAlign = "right";
  ctx.textBaseline = "middle";

  nested.slice(0,num).reverse().forEach(function(name,i) {
    var yearspopular = name.values.data;

    if (i > num-11) {
      ctx.globalAlpha = 0.85;
      ctx.strokeStyle = color(name.key);
      ctx.lineWidth = 2.5;
    } else {
      ctx.globalAlpha = 0.55;
      ctx.strokeStyle = "#888";
      ctx.lineWidth = 1;
    }

    // bump line
    ctx.globalCompositeOperation = "darken";
    ctx.beginPath();
    ctx.lineCap = "round";
    yearspopular.forEach(function(d,j) {
      if (j == 0 || ((d.year - yearspopular[j-1].year) > 1)) {
        ctx.moveTo(xscale(d.year), yscale(byYear[d.year][name.key]))
      } else {
        ctx.lineTo(xscale(d.year), yscale(byYear[d.year][name.key]));
      }
    });
    ctx.stroke();
    ctx.closePath();
  });

  ctx.font = "10px sans-serif";
  nested.slice(0,num).reverse().forEach(function(name,i) {
    var yearspopular = name.values.data;
    if (i > num-11) {
      ctx.fillStyle = color(name.key);
    } else {
      ctx.fillStyle = "#555";
    }

    // start names
    ctx.globalCompositeOperation = "source-over";
    ctx.globalAlpha = 0.9;
    ctx.textAlign = "end";
    var start = yearspopular[0].year;
    ctx.fillText(name.key, xscale(start)-4, yscale(byYear[start][name.key]));

    // end names
    ctx.textAlign = "start";
    var end= yearspopular[yearspopular.length-1].year;
    ctx.fillText(name.key, xscale(end)+4, yscale(byYear[end][name.key]));
    
  });
});
</script>

all-boys.html

 <!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
  background: #fff;
  font: 12px sans-serif;
}
svg,
canvas {
  position: absolute;
}
.axis path,
.axis line {
  fill: none;
  stroke: #d0d0d0;
}
.x.axis .tick text {
  font: 10px sans-serif;
  fill: #555;
}
</style>
<body>
<canvas></canvas>
<svg></svg>
<script src="//d3js.org/d3.v4.0.0-alpha.40.js"></script>
<script>
var num = 1000;

var margin = {top: 30, right: 60, bottom: 30, left: 60};
var width = 960,
    height = 7600;

var devicePixelRatio = window.devicePixelRatio || 1;

var canvas = d3.select("canvas")
    .attr("width", width * devicePixelRatio)
    .attr("height", height * devicePixelRatio)
    .style("width", width + "px")
    .style("height", height + "px");

var svg = d3.select("svg")
    .style("width", width + "px")
    .style("height", height + "px");

var color = d3.scaleOrdinal()
  .range(["#DB7F85", "#50AB84", "#4C6C86", "#C47DCB", "#B59248", "#DD6CA7", "#E15E5A", "#5DA5B3", "#725D82", "#54AF52", "#954D56", "#8C92E8", "#D8597D", "#AB9C27", "#D67D4B", "#D58323", "#BA89AD", "#357468", "#8F86C2", "#7D9E33", "#517C3F", "#9D5130", "#5E9ACF", "#776327", "#944F7E"]);

var xscale = d3.scaleLinear()
  .domain([1880,2015])
  .range([margin.left,width-margin.right]);

var xaxis = d3.axisBottom()
  .scale(xscale)
  .ticks(20)
  .tickFormat(d3.format(""));

var xaxis2 = d3.axisTop()
  .scale(xscale)
  .ticks(20)
  .tickFormat(d3.format(""));

svg.append("g")
  .attr("class", "x axis")
  .attr("transform", "translate(0," + (height-margin.bottom) + ")")
  .call(xaxis);

svg.append("g")
  .attr("class", "x axis")
  .attr("transform", "translate(0," + (margin.top-10) + ")")
  .call(xaxis2);

var yscale = d3.scaleLinear()
  .domain([0,num])
  .range([margin.top, height-margin.bottom]);

var radius = d3.scaleSqrt()
  .domain([0,0.1])
  .range([0,4]);

d3.csv("boy-names.csv", function(error, data) {
  data.forEach(function(d) {
    d.fraction = +d.fraction;
    d.year = +d.year;
  });

  // only girls
  data = data.filter(function(d) {
    return d.sex == "m";
  });

  // nest by name and rank by total popularity
  var nested = d3.nest()
    .key(function(d) { return d.name; })
    .rollup(function(leaves) {
      return {
        data: leaves,
        length: leaves.length,
        sum: d3.sum(leaves, function(d) { return d.fraction; })
      };
    })
    .sortValues(function(a,b) { return a.length - b.length;  })
    .entries(data);

  var topnames = nested.map(function(d) { return d.key; });
  data = data.filter(function(d) {
    return topnames.indexOf(d.name) > -1;
  });

  // nest by name and rank by total popularity
  window.byYear = {}
  d3.nest()
    .key(function(d) { return d.year; })
    .key(function(d) { return d.name; })
    .sortValues(function(a,b) { return a.fraction - b.fraction;  })
    .rollup(function(leaves,i) {
      return leaves[0].fraction;
    })
    .entries(data)
    .forEach(function(year) {
      byYear[year.key] = {};
      year.values.forEach(function(name,i) {
        byYear[year.key][name.key] = i;
      });
    });

  var ctx = canvas.node().getContext("2d");
  ctx.scale(devicePixelRatio, devicePixelRatio);

  ctx.fillStyle = "#1a1a1a";
  ctx.strokeStyle = "#1a1a1a";
  ctx.textAlign = "right";
  ctx.textBaseline = "middle";

  // color fiddling
  d3.range(8).map(color);

  nested.reverse().forEach(function(name,i) {
    var yearspopular = name.values.data;

    ctx.globalCompositeOperation = "darken";
    if (i > nested.length-11) {
      ctx.strokeStyle = color(name.key);
      ctx.lineWidth = 2;
      ctx.globalAlpha = 0.75;
    } else {
      ctx.strokeStyle = "#777";
      ctx.globalAlpha = 0.15;
      ctx.lineWidth = 1;
    }

    // bump line
    ctx.beginPath();
    yearspopular.forEach(function(d,j) {
      if (j == 0 || ((d.year - yearspopular[j-1].year) > 1)) {
        ctx.moveTo(xscale(d.year), yscale(byYear[d.year][name.key]))
      } else {
        ctx.lineTo(xscale(d.year), yscale(byYear[d.year][name.key]));
      }
    });
    ctx.stroke();
    ctx.closePath();
  });

  nested.reverse().forEach(function(name,i) {
    var yearspopular = name.values.data;

    ctx.globalCompositeOperation = "source-over";
    if (i > nested.length-11) {
      ctx.fillStyle = color(name.key);
    } else {
      ctx.fillStyle = "#111";
    }

    ctx.font = "bold 9px sans-serif";
    // start names
    ctx.globalAlpha = 1;
    ctx.textAlign = "end";
    var startyear = yearspopular[0].year;
    ctx.fillText(name.key, xscale(startyear)-4, yscale(byYear[startyear][name.key]));

    ctx.font = "9px sans-serif";
    // end names
    ctx.textAlign = "start";
    var endyear = yearspopular[yearspopular.length-1].year;
    ctx.fillText(name.key, xscale(endyear)+4, yscale(byYear[endyear][name.key]));
    
  });
});
</script>

all.html

 <!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
  background: #fff;
  font: 12px sans-serif;
}
svg,
canvas {
  position: absolute;
}
.axis path,
.axis line {
  fill: none;
  stroke: #d0d0d0;
}
.x.axis .tick text {
  font: 10px sans-serif;
  fill: #555;
}
</style>
<body>
<canvas></canvas>
<svg></svg>
<script src="//d3js.org/d3.v4.0.0-alpha.40.js"></script>
<script>
var num = 1000;

var margin = {top: 30, right: 60, bottom: 30, left: 60};
var width = 960,
    height = 7600;

var devicePixelRatio = window.devicePixelRatio || 1;

var canvas = d3.select("canvas")
    .attr("width", width * devicePixelRatio)
    .attr("height", height * devicePixelRatio)
    .style("width", width + "px")
    .style("height", height + "px");

var svg = d3.select("svg")
    .style("width", width + "px")
    .style("height", height + "px");

var color = d3.scaleOrdinal()
  .range(["#DB7F85", "#50AB84", "#4C6C86", "#C47DCB", "#B59248", "#DD6CA7", "#E15E5A", "#5DA5B3", "#725D82", "#54AF52", "#954D56", "#8C92E8", "#D8597D", "#AB9C27", "#D67D4B", "#D58323", "#BA89AD", "#357468", "#8F86C2", "#7D9E33", "#517C3F", "#9D5130", "#5E9ACF", "#776327", "#944F7E"]);

var xscale = d3.scaleLinear()
  .domain([1880,2015])
  .range([margin.left,width-margin.right]);

var xaxis = d3.axisBottom()
  .scale(xscale)
  .ticks(20)
  .tickFormat(d3.format(""));

var xaxis2 = d3.axisTop()
  .scale(xscale)
  .ticks(20)
  .tickFormat(d3.format(""));

svg.append("g")
  .attr("class", "x axis")
  .attr("transform", "translate(0," + (height-margin.bottom) + ")")
  .call(xaxis);

svg.append("g")
  .attr("class", "x axis")
  .attr("transform", "translate(0," + (margin.top-10) + ")")
  .call(xaxis2);

var yscale = d3.scaleLinear()
  .domain([0,num])
  .range([margin.top, height-margin.bottom]);

var radius = d3.scaleSqrt()
  .domain([0,0.1])
  .range([0,4]);

d3.csv("girl-names.csv", function(error, data) {
  data.forEach(function(d) {
    d.fraction = +d.fraction;
    d.year = +d.year;
  });

  // only girls
  data = data.filter(function(d) {
    return d.sex == "f";
  });

  // nest by name and rank by total popularity
  var nested = d3.nest()
    .key(function(d) { return d.name; })
    .rollup(function(leaves) {
      return {
        data: leaves,
        length: leaves.length,
        sum: d3.sum(leaves, function(d) { return d.fraction; })
      };
    })
    .sortValues(function(a,b) { return a.length - b.length;  })
    .entries(data);

  var topnames = nested.map(function(d) { return d.key; });
  data = data.filter(function(d) {
    return topnames.indexOf(d.name) > -1;
  });

  // nest by name and rank by total popularity
  window.byYear = {}
  d3.nest()
    .key(function(d) { return d.year; })
    .key(function(d) { return d.name; })
    .sortValues(function(a,b) { return a.fraction - b.fraction;  })
    .rollup(function(leaves,i) {
      return leaves[0].fraction;
    })
    .entries(data)
    .forEach(function(year) {
      byYear[year.key] = {};
      year.values.forEach(function(name,i) {
        byYear[year.key][name.key] = i;
      });
    });

  var ctx = canvas.node().getContext("2d");
  ctx.scale(devicePixelRatio, devicePixelRatio);

  ctx.fillStyle = "#1a1a1a";
  ctx.strokeStyle = "#1a1a1a";
  ctx.textAlign = "right";
  ctx.textBaseline = "middle";

  // color fiddling
  d3.range(8).map(color);

  nested.reverse().forEach(function(name,i) {
    var yearspopular = name.values.data;

    if (i > nested.length-11) {
      ctx.strokeStyle = color(name.key);
      ctx.lineWidth = 2;
      ctx.globalAlpha = 0.75;
    } else {
      ctx.strokeStyle = "#777";
      ctx.globalAlpha = 0.15;
      ctx.lineWidth = 1;
    }

    // bump line
    ctx.beginPath();
    yearspopular.forEach(function(d,j) {
      if (j == 0 || ((d.year - yearspopular[j-1].year) > 1)) {
        ctx.moveTo(xscale(d.year), yscale(byYear[d.year][name.key]))
      } else {
        ctx.lineTo(xscale(d.year), yscale(byYear[d.year][name.key]));
      }
    });
    ctx.stroke();
    ctx.closePath();
  });

  nested.reverse().forEach(function(name,i) {
    var yearspopular = name.values.data;

    if (i > nested.length-11) {
      ctx.fillStyle = color(name.key);
    } else {
      ctx.fillStyle = "#111";
    }

    ctx.font = "bold 9px sans-serif";
    // start names
    ctx.globalAlpha = 1;
    ctx.textAlign = "end";
    var startyear = yearspopular[0].year;
    ctx.fillText(name.key, xscale(startyear)-4, yscale(byYear[startyear][name.key]));

    ctx.font = "9px sans-serif";
    // end names
    ctx.textAlign = "start";
    var endyear = yearspopular[yearspopular.length-1].year;
    ctx.fillText(name.key, xscale(endyear)+4, yscale(byYear[endyear][name.key]));
    
  });
});
</script>

boys.html

 <!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
  background: #fff;
  font: 12px sans-serif;
}
svg,
canvas {
  position: absolute;
}
.axis path,
.axis line {
  fill: none;
  stroke: #d0d0d0;
}
.x.axis .tick text {
  font: 10px sans-serif;
  fill: #555;
}
</style>
<body>
<canvas></canvas>
<svg></svg>
<script src="//d3js.org/d3.v4.0.0-alpha.40.js"></script>
<script>
var num = 80;

var margin = {top: 30, right: 60, bottom: 30, left: 60};
var width = 960,
    height = 700;

var devicePixelRatio = window.devicePixelRatio || 1;

var canvas = d3.select("canvas")
    .attr("width", width * devicePixelRatio)
    .attr("height", height * devicePixelRatio)
    .style("width", width + "px")
    .style("height", height + "px");

var svg = d3.select("svg")
    .style("width", width + "px")
    .style("height", height + "px");

var color = d3.scaleOrdinal()
  .range(["#DB7F85", "#50AB84", "#4C6C86", "#C47DCB", "#B59248", "#DD6CA7", "#E15E5A", "#5DA5B3", "#725D82", "#54AF52", "#954D56", "#8C92E8", "#D8597D", "#AB9C27", "#D67D4B", "#D58323", "#BA89AD", "#357468", "#8F86C2", "#7D9E33", "#517C3F", "#9D5130", "#5E9ACF", "#776327", "#944F7E"]);

var xscale = d3.scaleLinear()
  .domain([1880,2015])
  .range([margin.left,width-margin.right]);

var xaxis = d3.axisBottom()
  .scale(xscale)
  .ticks(20)
  .tickFormat(d3.format(""));

var xaxis2 = d3.axisTop()
  .scale(xscale)
  .ticks(20)
  .tickFormat(d3.format(""));

svg.append("g")
  .attr("class", "x axis")
  .attr("transform", "translate(0," + (height-margin.bottom) + ")")
  .call(xaxis);

svg.append("g")
  .attr("class", "x axis")
  .attr("transform", "translate(0," + (margin.top-10) + ")")
  .call(xaxis2);

var yscale = d3.scaleLinear()
  .domain([0,num])
  .range([margin.top, height-margin.bottom]);

var radius = d3.scaleSqrt()
  .domain([0,0.1])
  .range([0,4]);

d3.csv("boy-names.csv", function(error, data) {
  data.forEach(function(d) {
    d.fraction = +d.fraction;
    d.year = +d.year;
  });

  // only girls
  data = data.filter(function(d) {
    return d.sex == "m";
  });

  // nest by name and rank by total popularity
  var nested = d3.nest()
    .key(function(d) { return d.name; })
    .rollup(function(leaves) {
      return {
        data: leaves,
        sum: d3.sum(leaves, function(d) { return d.fraction; })
      };
    })
    .sortValues(function(a,b) { return a.sum - b.sum;  })
    .entries(data);

  var topnames = nested.slice(0,num).map(function(d) { return d.key; });
  data = data.filter(function(d) {
    return topnames.indexOf(d.name) > -1;
  });

  // nest by name and rank by total popularity
  window.byYear = {}
  d3.nest()
    .key(function(d) { return d.year; })
    .key(function(d) { return d.name; })
    .sortValues(function(a,b) { return a.fraction - b.fraction;  })
    .rollup(function(leaves,i) {
      return leaves[0].fraction;
    })
    .entries(data)
    .forEach(function(year) {
      byYear[year.key] = {};
      year.values.forEach(function(name,i) {
        byYear[year.key][name.key] = i;
      });
    });

  var ctx = canvas.node().getContext("2d");
  ctx.scale(devicePixelRatio, devicePixelRatio);

  ctx.fillStyle = "#1a1a1a";
  ctx.strokeStyle = "#1a1a1a";
  ctx.font = "9px sans-serif";
  ctx.textAlign = "right";
  ctx.textBaseline = "middle";

  // color fiddling
  d3.range(8).map(color);

  nested.slice(0,num).reverse().forEach(function(name,i) {
    var yearspopular = name.values.data;

    if (i > num-11) {
      ctx.strokeStyle = color(name.key);
      ctx.lineWidth = 2;
    } else {
      ctx.strokeStyle = "#bbb";
      ctx.lineWidth = 1;
    }

    // bump line
    ctx.globalAlpha = 0.75;
    ctx.beginPath();
    yearspopular.forEach(function(d,j) {
      if (j == 0 || ((d.year - yearspopular[j-1].year) > 1)) {
        ctx.moveTo(xscale(d.year), yscale(byYear[d.year][name.key]))
      } else {
        ctx.lineTo(xscale(d.year), yscale(byYear[d.year][name.key]));
      }
    });
    ctx.stroke();
    ctx.closePath();
  });

  nested.slice(0,num).reverse().forEach(function(name,i) {
    var yearspopular = name.values.data;
    if (i > num-11) {
      ctx.fillStyle = color(name.key);
    } else {
      ctx.fillStyle = "#555";
    }

    // start names
    ctx.globalAlpha = 0.9;
    ctx.textAlign = "end";
    var start = yearspopular[0].year;
    ctx.fillText(name.key, xscale(start)-4, yscale(byYear[start][name.key]));

    // end names
    ctx.textAlign = "start";
    var end= yearspopular[yearspopular.length-1].year;
    ctx.fillText(name.key, xscale(end)+4, yscale(byYear[end][name.key]));
    
  });
});
</script>

matrix.html

 <!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
  background: #fff;
}
</style>
<body>
<canvas width="960" height="700"></canvas>
<script src="//d3js.org/d3.v4.0.0-alpha.40.js"></script>
<script>
var width = 960,
    height = 700,
    margin = {top: 10, right: 10, bottom: 10, left: 70};

var devicePixelRatio = window.devicePixelRatio || 1;

var canvas = d3.select("canvas")
    .attr("width", width * devicePixelRatio)
    .attr("height", height * devicePixelRatio)
    .style("width", width + "px")
    .style("height", height + "px");

var ctx = canvas.node().getContext("2d");
ctx.scale(devicePixelRatio, devicePixelRatio);

var xscale = d3.scaleLinear()
  .domain([1880,2015])
  .range([margin.left,width-margin.right]);

var yscale = d3.scalePoint()
  .domain(d3.range(70))
  .range([margin.top, height-margin.bottom]);

var radius = d3.scaleSqrt()
  .domain([0,0.1])
  .range([0,4]);

d3.csv("girl-names.csv", function(error, data) {
  data.forEach(function(d) {
    d.fraction = +d.fraction;
    d.year = +d.year;
  });

  // only girls
  data = data.filter(function(d) {
    return d.sex == "f";
  });

  // nest by name and rank by total popularity
  var nested = d3.nest()
    .key(function(d) { return d.name; })
    .rollup(function(leaves) {
      return {
        data: leaves,
        sum: d3.sum(leaves, function(d) { return d.fraction; })
      };
    })
    .sortValues(function(a,b) { return a.sum - b.sum;  })
    .entries(data);

console.log(nested);
  ctx.fillStyle = "#1a1a1a";
  ctx.font = "11px sans-serif";
  ctx.textAlign = "right";
  ctx.textBaseline = "middle";
  nested.forEach(function(name,i) {
    ctx.fillText(name.key, xscale.range()[0]-6, yscale(i));
    name.values.data.forEach(function(d) {
      ctx.beginPath()
      ctx.arc(xscale(d.year), yscale(i), radius(d.fraction), 0, 2*Math.PI);
      ctx.fill();
    });
  });
});
</script>

needswork.html

 <!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
  background: #fff;
}
svg,
canvas {
  position: absolute;
}
</style>
<body>
<canvas></canvas>
<svg></svg>
<script src="//d3js.org/d3.v4.0.0-alpha.40.js"></script>
<script>
var num = 50;

var margin = {top: 10, right: 10, bottom: 30, left: 60};
var width = 960,
    height = 500;

var devicePixelRatio = window.devicePixelRatio || 1;

var canvas = d3.select("canvas")
    .attr("width", width * devicePixelRatio)
    .attr("height", height * devicePixelRatio)
    .style("width", width + "px")
    .style("height", height + "px");

var svg = d3.select("svg")
    .style("width", width + "px")
    .style("height", height + "px");

var color = d3.scaleOrdinal()
  .range(["#DB7F85", "#50AB84", "#4C6C86", "#C47DCB", "#B59248", "#DD6CA7", "#E15E5A", "#5DA5B3", "#725D82", "#54AF52", "#954D56", "#8C92E8", "#D8597D", "#AB9C27", "#D67D4B", "#D58323", "#BA89AD", "#357468", "#8F86C2", "#7D9E33", "#517C3F", "#9D5130", "#5E9ACF", "#776327", "#944F7E"]);

var xscale = d3.scaleLinear()
  .domain([1880,2015])
  .range([margin.left,width-margin.right]);

var xaxis = d3.axisBottom()
  .scale(xscale);

svg.append("g")
  .attr("transform", "translate(0," + (height-margin.bottom) + ")")
  .call(xaxis);

var yscale = d3.scaleLinear()
  .domain([0,num])
  .range([margin.top, height-margin.bottom]);

var radius = d3.scaleSqrt()
  .domain([0,0.1])
  .range([0,4]);

d3.csv("girl-names.csv", function(error, data) {
  data.forEach(function(d) {
    d.fraction = +d.fraction;
    d.year = +d.year;
  });

  // only girls
  data = data.filter(function(d) {
    return d.sex == "f";
  });

  // nest by name and rank by total popularity
  var nested = d3.nest()
    .key(function(d) { return d.name; })
    .rollup(function(leaves) {
      return {
        data: leaves,
        sum: d3.sum(leaves, function(d) { return d.fraction; })
      };
    })
    .sortValues(function(a,b) { return a.sum - b.sum;  })
    .entries(data);

  var topnames = nested.slice(0,num).map(function(d) { return d.key; });
  data = data.filter(function(d) {
    return topnames.indexOf(d.name) > -1;
  });

  // nest by name and rank by total popularity
  window.byYear = {}
  d3.nest()
    .key(function(d) { return d.year; })
    .key(function(d) { return d.name; })
    .sortValues(function(a,b) { return a.fraction - b.fraction;  })
    .rollup(function(leaves,i) {
      return leaves[0].fraction;
    })
    .entries(data)
    .forEach(function(year) {
      byYear[year.key] = {};
      year.values.forEach(function(name,i) {
        byYear[year.key][name.key] = i;
      });
    });

  var ctx = canvas.node().getContext("2d");
  ctx.scale(devicePixelRatio, devicePixelRatio);

  ctx.fillStyle = "#1a1a1a";
  ctx.strokeStyle = "#1a1a1a";
  ctx.font = "10px sans-serif";
  ctx.textAlign = "right";
  ctx.textBaseline = "middle";

  nested.slice(0,num).reverse().forEach(function(name,i) {
    if (i > num-11) {
      ctx.fillStyle = color(name.key);
      ctx.strokeStyle = color(name.key);
      ctx.lineWidth = 2;
    } else {
      ctx.fillStyle = "#999";
      ctx.strokeStyle = "#bbb";
      ctx.lineWidth = 1;
    }

    if (!(i % 0)) {
      ctx.fillText(name.key, xscale.range()[0]-6, yscale(byYear[1880][name.key]));
    }

    ctx.beginPath();
    name.values.data.forEach(function(d,j) {
      if (j == 0 || ((d.year - name.values.data[j-1].year) > 1)) {
        ctx.moveTo(xscale(d.year), yscale(byYear[d.year][name.key]))
      } else {
        ctx.lineTo(xscale(d.year), yscale(byYear[d.year][name.key]));
      }
    });
    ctx.stroke();
    ctx.closePath();

  });
});
</script>