block by curran 1fb2ae1dfc1471972904

Reusable Scatter Plot

Full Screen

A reusable D3 scatter plot, constructed using Towards Reusable Charts and Standalone Scatter Plot as inspiration.

In the original reusable charts example, the comments state

// Convert data to standard representation greedily;
// this is needed for nondeterministic accessors.

In this adaptation, a simplifying assumption was made that no nondeterministic accessors will be given. Going one step further, an assumption was made that accessors will only want to access specific “columns” of data, meaning properties on each row object.

web counter

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Reusable Scatterplot Example</title>
    <script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
    <link href="//fonts.googleapis.com/css?family=Poiret+One" rel="stylesheet" type="text/css">
    <style>

      /* Make the container flush against the upper left corner. */
      body {
        margin: 0px;
      }

      /* Put a border around SVG elements so we can see how big they are. */
      svg {
        border-style: solid;
        border-width: 1px;
      }
    
      /* Style the X and Y axes. */
      .axis text {
        font-family: "Poiret One", cursive;
        font-size: 16pt;
      }
      .axis .label {
        font-size: 32pt;
      }
      .axis path, .axis line {
        fill: none;
        stroke: #000;
        shape-rendering: crispEdges;
      }

    </style>
  </head>
  <body>
    <div id="chart"></div>
    <script src="scatterPlot.js"></script>
    <script src="main.js"></script>

    <script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-66108038-1', 'auto'); ga('send', 'pageview'); </script>
  </body>
</html>

iris.csv

sepal_length,sepal_width,petal_length,petal_width,species
5.1,3.5,1.4,0.2,setosa
4.9,3.0,1.4,0.2,setosa
4.7,3.2,1.3,0.2,setosa
4.6,3.1,1.5,0.2,setosa
5.0,3.6,1.4,0.2,setosa
5.4,3.9,1.7,0.4,setosa
4.6,3.4,1.4,0.3,setosa
5.0,3.4,1.5,0.2,setosa
4.4,2.9,1.4,0.2,setosa
4.9,3.1,1.5,0.1,setosa
5.4,3.7,1.5,0.2,setosa
4.8,3.4,1.6,0.2,setosa
4.8,3.0,1.4,0.1,setosa
4.3,3.0,1.1,0.1,setosa
5.8,4.0,1.2,0.2,setosa
5.7,4.4,1.5,0.4,setosa
5.4,3.9,1.3,0.4,setosa
5.1,3.5,1.4,0.3,setosa
5.7,3.8,1.7,0.3,setosa
5.1,3.8,1.5,0.3,setosa
5.4,3.4,1.7,0.2,setosa
5.1,3.7,1.5,0.4,setosa
4.6,3.6,1.0,0.2,setosa
5.1,3.3,1.7,0.5,setosa
4.8,3.4,1.9,0.2,setosa
5.0,3.0,1.6,0.2,setosa
5.0,3.4,1.6,0.4,setosa
5.2,3.5,1.5,0.2,setosa
5.2,3.4,1.4,0.2,setosa
4.7,3.2,1.6,0.2,setosa
4.8,3.1,1.6,0.2,setosa
5.4,3.4,1.5,0.4,setosa
5.2,4.1,1.5,0.1,setosa
5.5,4.2,1.4,0.2,setosa
4.9,3.1,1.5,0.1,setosa
5.0,3.2,1.2,0.2,setosa
5.5,3.5,1.3,0.2,setosa
4.9,3.1,1.5,0.1,setosa
4.4,3.0,1.3,0.2,setosa
5.1,3.4,1.5,0.2,setosa
5.0,3.5,1.3,0.3,setosa
4.5,2.3,1.3,0.3,setosa
4.4,3.2,1.3,0.2,setosa
5.0,3.5,1.6,0.6,setosa
5.1,3.8,1.9,0.4,setosa
4.8,3.0,1.4,0.3,setosa
5.1,3.8,1.6,0.2,setosa
4.6,3.2,1.4,0.2,setosa
5.3,3.7,1.5,0.2,setosa
5.0,3.3,1.4,0.2,setosa
7.0,3.2,4.7,1.4,versicolor
6.4,3.2,4.5,1.5,versicolor
6.9,3.1,4.9,1.5,versicolor
5.5,2.3,4.0,1.3,versicolor
6.5,2.8,4.6,1.5,versicolor
5.7,2.8,4.5,1.3,versicolor
6.3,3.3,4.7,1.6,versicolor
4.9,2.4,3.3,1.0,versicolor
6.6,2.9,4.6,1.3,versicolor
5.2,2.7,3.9,1.4,versicolor
5.0,2.0,3.5,1.0,versicolor
5.9,3.0,4.2,1.5,versicolor
6.0,2.2,4.0,1.0,versicolor
6.1,2.9,4.7,1.4,versicolor
5.6,2.9,3.6,1.3,versicolor
6.7,3.1,4.4,1.4,versicolor
5.6,3.0,4.5,1.5,versicolor
5.8,2.7,4.1,1.0,versicolor
6.2,2.2,4.5,1.5,versicolor
5.6,2.5,3.9,1.1,versicolor
5.9,3.2,4.8,1.8,versicolor
6.1,2.8,4.0,1.3,versicolor
6.3,2.5,4.9,1.5,versicolor
6.1,2.8,4.7,1.2,versicolor
6.4,2.9,4.3,1.3,versicolor
6.6,3.0,4.4,1.4,versicolor
6.8,2.8,4.8,1.4,versicolor
6.7,3.0,5.0,1.7,versicolor
6.0,2.9,4.5,1.5,versicolor
5.7,2.6,3.5,1.0,versicolor
5.5,2.4,3.8,1.1,versicolor
5.5,2.4,3.7,1.0,versicolor
5.8,2.7,3.9,1.2,versicolor
6.0,2.7,5.1,1.6,versicolor
5.4,3.0,4.5,1.5,versicolor
6.0,3.4,4.5,1.6,versicolor
6.7,3.1,4.7,1.5,versicolor
6.3,2.3,4.4,1.3,versicolor
5.6,3.0,4.1,1.3,versicolor
5.5,2.5,4.0,1.3,versicolor
5.5,2.6,4.4,1.2,versicolor
6.1,3.0,4.6,1.4,versicolor
5.8,2.6,4.0,1.2,versicolor
5.0,2.3,3.3,1.0,versicolor
5.6,2.7,4.2,1.3,versicolor
5.7,3.0,4.2,1.2,versicolor
5.7,2.9,4.2,1.3,versicolor
6.2,2.9,4.3,1.3,versicolor
5.1,2.5,3.0,1.1,versicolor
5.7,2.8,4.1,1.3,versicolor
6.3,3.3,6.0,2.5,virginica
5.8,2.7,5.1,1.9,virginica
7.1,3.0,5.9,2.1,virginica
6.3,2.9,5.6,1.8,virginica
6.5,3.0,5.8,2.2,virginica
7.6,3.0,6.6,2.1,virginica
4.9,2.5,4.5,1.7,virginica
7.3,2.9,6.3,1.8,virginica
6.7,2.5,5.8,1.8,virginica
7.2,3.6,6.1,2.5,virginica
6.5,3.2,5.1,2.0,virginica
6.4,2.7,5.3,1.9,virginica
6.8,3.0,5.5,2.1,virginica
5.7,2.5,5.0,2.0,virginica
5.8,2.8,5.1,2.4,virginica
6.4,3.2,5.3,2.3,virginica
6.5,3.0,5.5,1.8,virginica
7.7,3.8,6.7,2.2,virginica
7.7,2.6,6.9,2.3,virginica
6.0,2.2,5.0,1.5,virginica
6.9,3.2,5.7,2.3,virginica
5.6,2.8,4.9,2.0,virginica
7.7,2.8,6.7,2.0,virginica
6.3,2.7,4.9,1.8,virginica
6.7,3.3,5.7,2.1,virginica
7.2,3.2,6.0,1.8,virginica
6.2,2.8,4.8,1.8,virginica
6.1,3.0,4.9,1.8,virginica
6.4,2.8,5.6,2.1,virginica
7.2,3.0,5.8,1.6,virginica
7.4,2.8,6.1,1.9,virginica
7.9,3.8,6.4,2.0,virginica
6.4,2.8,5.6,2.2,virginica
6.3,2.8,5.1,1.5,virginica
6.1,2.6,5.6,1.4,virginica
7.7,3.0,6.1,2.3,virginica
6.3,3.4,5.6,2.4,virginica
6.4,3.1,5.5,1.8,virginica
6.0,3.0,4.8,1.8,virginica
6.9,3.1,5.4,2.1,virginica
6.7,3.1,5.6,2.4,virginica
6.9,3.1,5.1,2.3,virginica
5.8,2.7,5.1,1.9,virginica
6.8,3.2,5.9,2.3,virginica
6.7,3.3,5.7,2.5,virginica
6.7,3.0,5.2,2.3,virginica
6.3,2.5,5.0,1.9,virginica
6.5,3.0,5.2,2.0,virginica
6.2,3.4,5.4,2.3,virginica
5.9,3.0,5.1,1.8,virginica

main.js

scatterPlot.js

function ScatterPlot(){

  var outerWidth = 100;
  var outerHeight = 100;

  var margin = { left: 60, top: 5, right: 10, bottom: 60 };

  var rMin = 2; // "r" stands for radius
  var rMax = 300;
  var xColumn = "No X column configured";
  var yColumn = "No Y column configured";
  var rColumn = "No radius column configured";
  var colorColumn = "No color column configured";
  var colorRange = d3.scale.category10().range();

  var xAxisLabel = "No X axis label configured";
  var xAxisLabelOffset = 40;

  var yAxisLabel = "No Y axis label configured";
  var yAxisLabelOffset = 30;

  var xScale = d3.scale.linear();
  var yScale = d3.scale.linear();
  var rScale = d3.scale.linear();
  var colorScale = d3.scale.ordinal();

  var xAxis = d3.svg.axis().scale(xScale).orient("bottom")
    .tickFormat(d3.format("s"))
    .outerTickSize(0);
  var xTicks = 5;

  var yAxis = d3.svg.axis().scale(yScale).orient("left")
    .tickFormat(d3.format("s"))
    .outerTickSize(0);
  var yTicks = 5;

  function chart(selection){

    var innerWidth  = outerWidth  - margin.left - margin.right;
    var innerHeight = outerHeight - margin.top  - margin.bottom;

    colorScale.range(colorRange);
    xScale.range([0, innerWidth]);
    yScale.range([innerHeight, 0]);
    rScale.range([rMin, rMax]);

    xAxis.ticks(xTicks);
    yAxis.ticks(yTicks);

    selection.each(function (data) {

      var svg = d3.select(this).selectAll("svg").data([data]);
      var gEnter = svg.enter().append("svg").append("g");
      var g = svg.select("g");
      var circles = g.selectAll("circle").data(data);

      gEnter
        .append("g")
          .attr("class", "x axis")
        .append("text")
          .attr("class", "label")
          .style("text-anchor", "middle");

      gEnter
        .append("g")
          .attr("class", "y axis")
        .append("text")
          .attr("class", "label")
          .style("text-anchor", "middle");

      xScale.domain(d3.extent(data, function (d){ return d[xColumn]; }));
      yScale.domain(d3.extent(data, function (d){ return d[yColumn]; }));
      rScale.domain(d3.extent(data, function (d){ return d[rColumn]; }));

      svg 
        .attr("width", outerWidth)
        .attr("height", outerHeight);

      g.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

      g
        .select(".x.axis")
          .attr("transform", "translate(0," + innerHeight + ")")
          .call(xAxis)
        .select("text")
          .attr("x", innerWidth / 2)
          .attr("y", xAxisLabelOffset)
          .text(xAxisLabel);

      g
        .select(".y.axis")
          .call(yAxis)
        .select("text")
          .attr("transform", "translate(-" + yAxisLabelOffset + "," + (innerHeight / 2) + ") rotate(-90)")
          .text(yAxisLabel);

      circles.enter().append("circle");
      circles
        .attr("cx",      function (d){ return       xScale(d[xColumn]);     })
        .attr("cy",      function (d){ return       yScale(d[yColumn]);     })
        .attr("r",       function (d){ return       rScale(d[rColumn]);     })
        .attr("fill",    function (d){ return   colorScale(d[colorColumn]); });

      circles.exit().remove();

    });
  }

  chart.outerWidth = function(_) {
    if (!arguments.length) return outerWidth;
    outerWidth = _;
    return chart;
  };

  chart.outerHeight = function(_) {
    if (!arguments.length) return outerHeight;
    outerHeight = _;
    return chart;
  };

  chart.xColumn = function(_) {
    if (!arguments.length) return xColumn;
    xColumn = _;
    return chart;
  };

  chart.yColumn = function(_) {
    if (!arguments.length) return yColumn;
    yColumn = _;
    return chart;
  };

  chart.rColumn = function(_) {
    if (!arguments.length) return rColumn;
    rColumn = _;
    return chart;
  };

  chart.rMin = function(_) {
    if (!arguments.length) return rMin;
    rMin = _;
    return chart;
  };

  chart.rMax = function(_) {
    if (!arguments.length) return rMax;
    rMax = _;
    return chart;
  };

  chart.colorRange = function(_) {
    if (!arguments.length) return colorRange;
    colorRange = _;
    return chart;
  };

  chart.colorColumn = function(_) {
    if (!arguments.length) return colorColumn;
    colorColumn = _;
    return chart;
  };

  chart.xAxisLabel = function(_) {
    if (!arguments.length) return xAxisLabel;
    xAxisLabel = _;
    return chart;
  };

  chart.yAxisLabel = function(_) {
    if (!arguments.length) return yAxisLabel;
    yAxisLabel = _;
    return chart;
  };

  chart.xAxisLabelOffset = function(_) {
    if (!arguments.length) return xAxisLabelOffset;
    xAxisLabelOffset = _;
    return chart;
  };

  chart.yAxisLabelOffset = function(_) {
    if (!arguments.length) return yAxisLabelOffset;
    yAxisLabelOffset = _;
    return chart;
  };

  chart.xTicks = function(_) {
    if (!arguments.length) return xTicks;
    xTicks = _;
    return chart;
  };

  chart.yTicks = function(_) {
    if (!arguments.length) return yTicks;
    yTicks = _;
    return chart;
  };

  chart.margin = function(_) {
    if (!arguments.length) return margin;
    margin = _;
    return chart;
  };

  return chart;
}