block by curran 3cc1a2a289dddbd64688

Fundamental Visualizations

Full Screen

Fundamental visualization techniques as Chiasm plugins. Inspired by:

Goals of this example:

For a more advanced example that adds brushing interaction to a scatter plot, check out Focus + Context Scatter Plots.

Data includes:

web counter

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Fundamental Visualizations</title>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>

    <!-- The date parsing for the line chart data uses Moment.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.6/moment.min.js"></script>

    <!-- The Choropleth component uses TopoJSON. -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.19/topojson.min.js"></script>
    
    <!-- A functional reactive model library. github.com/curran/model -->
    <script src="//curran.github.io/model/cdn/model-v0.2.4.js"></script>

    <!-- Chiasm core and plugins. github.com/chiasm-project -->
    <script src="//chiasm-project.github.io/chiasm/chiasm-v0.2.0.js"></script>
    <script src="//chiasm-project.github.io/chiasm-component/chiasm-component-v0.2.1.js"></script>
    <script src="//chiasm-project.github.io/chiasm-layout/chiasm-layout-v0.2.2.js"></script>
    <script src="//chiasm-project.github.io/chiasm-links/chiasm-links-v0.2.0.js"></script>

    <!-- Custom Chiasm plugins for this example. -->
    <script src="dataLoader.js"></script>

    <script src="pieChart.js"></script>
    <script src="scatterPlot.js"></script>
    <script src="barChart.js"></script>
    <script src="lineChart.js"></script>
    <script src="choropleth.js"></script>

    <style>

      body {
        background-color: black;
      }

      /* Make the chart container fill the page using CSS. */
      #chiasm-container {
        background-color: white;
        position: fixed;
        left: 20px;
        right: 20px;
        top: 20px;
        bottom: 20px;
      }
    </style>

  </head>
  <body>
    <div id="chiasm-container"></div>

    <script>

      var chiasm = Chiasm();

      chiasm.plugins.layout = ChiasmLayout;
      chiasm.plugins.links = ChiasmLinks;
      chiasm.plugins.dataLoader = DataLoader;
      chiasm.plugins.pieChart = PieChart;
      chiasm.plugins.scatterPlot = ScatterPlot;
      chiasm.plugins.barChart = BarChart;
      chiasm.plugins.lineChart = LineChart;
      chiasm.plugins.choropleth = Choropleth;

      // Use D3 to fetch the chiasm configuration file and
      // Set the Chaism application configuration.
      d3.json("chiasmConfig.json", function (config){
        chiasm.setConfig(config);
      });

      // Set up the line chart component with data from William Playfair.
      chiasm.getComponent("lineChart").then(function (lineChart){

        // Fetch the JSON version of the data for the line chart.
        d3.json("lineChartData.json", function (lineChartData){

          // Do a custom data transform to make it the table structure that the
          // line chart component understands.
          var data = lineChartData.year.map(function (yearStr, i){
            return {
              year: moment(yearStr).toDate(),
              imports_minus_exports: lineChartData.imports_minus_exports[i]
            };
          });

          // Configure the line chart with the data.
          lineChart.set({
            data: data,
            xColumn: "year",
            yColumn: "imports_minus_exports"
          });
        });
      });


      // Set up the choropleth component with data from the US Census.
      chiasm.getComponent("choropleth").then(function (choropleth){

        // Fetch the JSON version of the data for the line chart.
        d3.json("choroplethData.json", function (topoData){

          var geometries = topoData.objects.State_2010Census_DP1.geometries;

          var data = geometries.map(function (d){
            return {
              geoId: d.properties.GEOID10,
              population: d.properties.DP0010001
            };
          });

          // Configure the line chart with the data.
          choropleth.set({
            topoData: topoData,
            data: data
          });
        });
      });
    
    </script>
  </body>
</html>

barChart.js

// This is an example Chaism plugin that uses D3 to make a bar chart. 
// Draws from this Bar Chart example http://bl.ocks.org/mbostock/3885304
function BarChart() {

  var my = ChiasmComponent({

    margin: {
      left:   30,
      top:    30,
      right:  30,
      bottom: 30
    },

    barSizeColumn: Model.None,

    // These properties adjust spacing between bars.
    // The names correspond to the arguments passed to
    // d3.scale.ordinal.rangeRoundBands(interval[, padding[, outerPadding]])
    // https://github.com/mbostock/d3/wiki/Ordinal-Scales#ordinal_rangeRoundBands
    barPadding: 0.1,
    barOuterPadding: 0.1,

    fill: "black",
    stroke: "none",
    strokeWidth: "1px",

    orientation: "vertical"

  });

  var barSizeScale = d3.scale.linear();
  var barIdentityScale = d3.scale.ordinal();

  my.el = document.createElement("div");
  var svg = d3.select(my.el).append("svg");
  var g = svg.append("g");

  // Respond to changes in size and margin.
  // Inspired by D3 margin convention from http://bl.ocks.org/mbostock/3019563
  my.when(["box", "margin"], function(box, margin){

    my.innerBox = {
      width: box.width - margin.left - margin.right,
      height: box.height - margin.top - margin.bottom
    };

    svg
      .attr("width", box.width)
      .attr("height", box.height);

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

  my.when(["data", "barSizeColumn", "innerBox", "barPadding", "barOuterPadding", "fill", "stroke", "strokeWidth", "orientation"],
      function (data, barSizeColumn, innerBox, barPadding, barOuterPadding, fill, stroke, strokeWidth, orientation){
    var x, y, width, height;

    barSizeScale
      .domain([0, d3.max(data, function (d){ return d[barSizeColumn]; })]);

    barIdentityScale
      .domain(d3.range(data.length));

    if(orientation === "vertical"){

      barSizeScale.range([innerBox.height, 0]);
      barIdentityScale.rangeRoundBands([0, innerBox.width], barPadding, barOuterPadding);

      x = function(d, i) { return barIdentityScale(i); };
      y = function(d) { return barSizeScale(d[barSizeColumn]); };
      width = barIdentityScale.rangeBand();
      height = function(d) { return innerBox.height - y(d); };

    } else if(orientation === "horizontal"){

      barSizeScale.range([0, innerBox.width]);
      barIdentityScale.rangeRoundBands([0, innerBox.height], barPadding, barOuterPadding);

      x = 0;
      y = function(d, i) { return barIdentityScale(i); };
      width = function(d) { return barSizeScale(d[barSizeColumn]); };
      height = barIdentityScale.rangeBand();

    }

    var bars = g.selectAll("rect").data(data);
    bars.enter().append("rect");
    bars
      .attr("x", x)
      .attr("width", width)
      .attr("y", y)
      .attr("height", height)
      .attr("fill", fill)
      .attr("stroke", stroke)
      .attr("stroke-width", strokeWidth);
    bars.exit().remove();
  });

  return my;
}

barChartData.csv

name,amount
A,18
B,20
C,15
D,35
E,12

barChartData.json

{
  "columns": [
    { "name": "name", "type": "string" },
    { "name": "amount", "type": "number" }
  ]
}

chiasmConfig.json


{
  "layout": {
    "plugin": "layout",
    "state": {
      "containerSelector": "#chiasm-container",
      "layout": {
        "orientation": "vertical",
        "children": [
          {
            "orientation": "horizontal",
            "children": [
              "scatterPlot",
              "pieChart",
              "barChartVertical",
              "barChartHorizontal"
            ]
          },
          {
            "orientation": "horizontal",
            "children": [
              "scatterPlotSize",
              "donutChart",
              "lineChart",
              "choropleth"
            ]
          }
        ]
      },
      "sizes": {
        "lineChart": {
          "size": 1
        }
      }
    }
  },
  "scatterPlotData": {
    "plugin": "dataLoader",
    "state": {
      "path": "scatterPlotData"
    }
  },
  "scatterPlot": {
    "plugin": "scatterPlot",
    "state": {
      "xColumn": "sepal_length",
      "yColumn": "petal_length"
    }
  },
  "scatterPlotSize": {
    "plugin": "scatterPlot",
    "state": {
      "xColumn": "sepal_length",
      "yColumn": "petal_length",
      "rColumn": "sepal_length",
      "rMax": 20,
      "fill": "none",
      "stroke": "black",
      "strokeWidth": "2px"
    }
  },
  "pieChartData": {
    "plugin": "dataLoader",
    "state": {
      "path": "pieChartData"
    }
  },
  "pieChart": {
    "plugin": "pieChart",
    "state": {
      "sliceColumn": "population",
      "fill": "none",
      "stroke": "black",
      "strokeWidth": "2px"
    }
  },
  "donutChart": {
    "plugin": "pieChart",
    "state": {
      "sliceColumn": "population",
      "donutHoleSize": 0.5,
      "fill": "none",
      "stroke": "black",
      "strokeWidth": "2px"
    }
  },
  "barChartData": {
    "plugin": "dataLoader",
    "state": {
      "path": "barChartData"
    }
  },
  "barChartVertical": {
    "plugin": "barChart",
    "state": {
      "barSizeColumn": "amount",
      "fill": "none",
      "stroke": "black",
      "strokeWidth": "2px",
      "barPadding": 0.2
    }
  },
  "barChartHorizontal": {
    "plugin": "barChart",
    "state": {
      "orientation": "horizontal",
      "barSizeColumn": "amount",
      "fill": "none",
      "stroke": "black",
      "strokeWidth": "2px",
      "barPadding": 0.2
    }
  },
  "lineChart": {
    "plugin": "lineChart",
    "state": {
      "yColumn": "amnt",
      "stroke": "black",
      "strokeWidth": "2px"
    }
  },
  "choropleth": {
    "plugin": "choropleth",
    "state": {
      "stroke": "black",
      "strokeWidth": "2px",
      "scale": 300,
      "idColumn": "geoId",
      "topoDataIdProperty": "GEOID10",
      "colorColumn": "population"
    }
  },
  "links": {
    "plugin": "links",
    "state": {
      "bindings": [
        "pieChartData.data -> pieChart.data",
        "pieChartData.data -> donutChart.data",
        "scatterPlotData.data -> scatterPlot.data",
        "scatterPlotData.data -> scatterPlotSize.data",
        "barChartData.data -> barChartVertical.data",
        "barChartData.data -> barChartHorizontal.data"
      ]
    }
  }
}

choropleth.js

// This is an example Chaism plugin that uses D3 to make a Choropleth Map. 
// Draws from this Choropleth example http://bl.ocks.org/mbostock/4060606
function Choropleth() {

  var my = ChiasmComponent({
    colorColumn: Model.None,
    idColumn: Model.None,
    topoDataIdProperty: Model.None,
    stroke: "gray",
    strokeWidth: "1px",
    scale: 1280
  });

  var quantize = d3.scale.quantize();
  var projection = d3.geo.albersUsa();
  var geoPath = d3.geo.path()
    .projection(projection);

  my.el = document.createElement("div");
  var svg = d3.select(my.el).append("svg");
  var g = svg.append("g");

  my.when("box", function (box) {
    svg
      .attr("width", box.width)
      .attr("height", box.height);
  });

  my.when(["data", "idColumn", "colorColumn", "topoDataIdProperty"],
      function (data, idColumn, colorColumn, topoDataIdProperty){

    if(colorColumn === Model.None) return;
    if(idColumn === Model.None) return;
    if(topoDataIdProperty === Model.None) return;

    quantize.domain(d3.extent(data, function (d) { return d[colorColumn]; }));

    // Grays from http://bl.ocks.org/mbostock/5577023
    // TODO move this to config.
    quantize.range(["#f7f7f7","#cccccc","#969696","#636363","#252525"]);

    var valueById = d3.map();
    data.forEach(function(d) {
      valueById.set(d[idColumn], d[colorColumn]);
    });

    my.fill = function(d) {
      var id = d.properties[topoDataIdProperty];
      return quantize(valueById.get(id));
    };
  });

  my.when(["topoData", "fill", "box", "scale", "stroke", "strokeWidth" ],
      function (topoData, fill, box, scale, stroke, strokeWidth){

    projection.scale(scale);
    projection.translate([box.width / 2, box.height / 2]);

    var topoDataObjectName = "State_2010Census_DP1";
    var topoDataObject = topoData.objects[topoDataObjectName];
    var features = topojson.feature(topoData, topoDataObject).features;

    var path = g.selectAll("path").data(features);

    path.enter().append("path");
    path.exit().remove();
    path
      .attr("d", geoPath)
      .attr("fill", fill)
      .attr("stroke", stroke)
      .attr("stroke-width", strokeWidth);
  });

  return my;
}

dataLoader.js

// A Chiasm plugin for loading DSV data sets.
function DataLoader (){

  var my = ChiasmComponent({
    path: Model.None
  });

  my.when("path", function (path){
    d3.json(path + ".json", function(error, schema) {

      var numericColumns = schema.columns.filter(function (column){
        return column.type === "number";
      });

      var type = function (d){
        numericColumns.forEach(function (column){
          d[column.name] = +d[column.name];
        });
        return d;
      }

      d3.csv(path + ".csv", type, function(error, data) {
        my.data = data;
      });
    });
  });

  return my;
}

lineChart.js

// This is an example Chaism plugin that uses D3 to make a line chart. 
// Draws from this Line Chart example http://bl.ocks.org/curran/60b40877ef898f19aeb8
function LineChart() {

  var my = ChiasmComponent({

    margin: {
      left:   30,
      top:    30,
      right:  30,
      bottom: 30
    },

    yColumn: Model.None,
    xColumn: Model.None,

    fill: "none",
    stroke: "gray",
    strokeWidth: "1px"

  });

  var xScale = d3.time.scale();
  var yScale = d3.scale.linear();
  
  my.el = document.createElement("div");
  var svg = d3.select(my.el).append("svg");
  var g = svg.append("g");
  var path = g.append("path");

  // Respond to changes in size and margin.
  // Inspired by D3 margin convention from http://bl.ocks.org/mbostock/3019563
  my.when(["box", "margin"], function(box, margin){

    my.innerBox = {
      width: box.width - margin.left - margin.right,
      height: box.height - margin.top - margin.bottom
    };

    svg
      .attr("width", box.width)
      .attr("height", box.height);

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

  var line = d3.svg.line();

  my.when(["data", "innerBox", "xColumn", "yColumn"],
      function (data, innerBox, xColumn, yColumn){

    if(xColumn !== Model.None){
      xScale.range([0, innerBox.width]);
      xScale.domain(d3.extent(data, function (d){ return d[xColumn]; }));
      my.x = function (d){ return xScale(d[xColumn]); };
    }

    if(yColumn !== Model.None){
      yScale.range([innerBox.height, 0]);
      yScale.domain(d3.extent(data, function (d){ return d[yColumn]; }));
      my.y = function (d){ return yScale(d[yColumn]); };
    }
  });

  my.when(["data", "x", "y", "fill", "stroke", "strokeWidth"],
      function (data, x, y, fill, stroke, strokeWidth){
    path
      .attr("d", line.x(x).y(y)(data))
      .attr("fill", fill)
      .attr("stroke", stroke)
      .attr("stroke-width", strokeWidth);
  });

  return my;
}

lineChartData.json

{
 "year": [ "1700-01-01", "1701-01-01", "1702-01-01", "1703-01-01", "1704-01-01", "1705-01-01", "1706-01-01", "1707-01-01", "1708-01-01", "1709-01-01", "1710-01-01", "1711-01-01", "1712-01-01", "1713-01-01", "1714-01-01", "1715-01-01", "1716-01-01", "1717-01-01", "1718-01-01", "1719-01-01", "1720-01-01", "1721-01-01", "1722-01-01", "1723-01-01", "1724-01-01", "1725-01-01", "1726-01-01", "1727-01-01", "1728-01-01", "1729-01-01", "1730-01-01", "1731-01-01", "1732-01-01", "1733-01-01", "1734-01-01", "1735-01-01", "1736-01-01", "1737-01-01", "1738-01-01", "1739-01-01", "1740-01-01", "1741-01-01", "1742-01-01", "1743-01-01", "1744-01-01", "1745-01-01", "1746-01-01", "1747-01-01", "1748-01-01", "1749-01-01", "1750-01-01", "1751-01-01", "1752-01-01", "1753-01-01", "1754-01-01", "1755-01-01", "1756-01-01", "1757-01-01", "1758-01-01", "1759-01-01", "1760-01-01", "1761-01-01", "1762-01-01", "1763-01-01", "1764-01-01", "1765-01-01", "1766-01-01", "1767-01-01", "1768-01-01", "1769-01-01", "1770-01-01", "1771-01-01", "1772-01-01", "1773-01-01", "1774-01-01", "1775-01-01", "1776-01-01", "1777-01-01", "1778-01-01", "1779-01-01", "1780-01-01"],
"imports_minus_exports": [       0.295918368,       0.308163266,       0.322448979,       0.342857142,        0.36122449,       0.385714285,       0.418367347,       0.442857143,       0.455102041,       0.475510204,               0.5, 0.5224489800000001,       0.571428571,       0.595918368,       0.612244898,        0.63877551,       0.665306123, 0.6918367350000001, 0.7183673469999999,        0.74489796,       0.751020408, 0.7755102039999999,       0.785714286,       0.797959183,        0.81632653,       0.826530612, 0.8244897959999999,       0.826530612,       0.836734694, 0.8469387749999999, 0.8469387749999999, 0.8469387749999999,       0.836734694,       0.826530612,       0.806122449, 0.7653061219999999,       0.732653061, 0.7081632660000001,       0.673469388,       0.640816326, 0.6224489799999999,       0.591836735,        0.56122449, 0.5306122449999999,       0.510204082,       0.489795919,       0.469387755, 0.4285714279999999,        0.37755102, 0.3469387749999999,       0.285714285,       0.230612245,       0.173469387, 0.1122448979999999, 0.06530612199999997, 0.03061224500000004, 0.03469387800000001, 0.04081632700000004,       0.071428571,       0.081632653,       0.132653061,       0.204081632,       0.397959183, 0.3959183669999999, 0.3653061230000001, 0.2142857150000002, 0.1326530610000001, 0.1326530609999999, 0.1387755099999999,       0.167346939,               0.2, 0.2346938779999999,       0.275510205,       0.336734694,       0.418367347,       0.581632654,       0.626530612, 0.6714285720000001, 0.6857142849999999, 0.7000000000000001, 0.7142857140000001,       0.295918368,       0.308163266,       0.322448979,       0.342857142,        0.36122449,       0.385714285,       0.418367347,       0.442857143,       0.455102041,       0.475510204,               0.5, 0.5224489800000001,       0.571428571,       0.595918368,       0.612244898,        0.63877551,       0.665306123, 0.6918367350000001, 0.7183673469999999,        0.74489796,       0.751020408, 0.7755102039999999,       0.785714286,       0.797959183,        0.81632653,       0.826530612, 0.8244897959999999,       0.826530612,       0.836734694, 0.8469387749999999, 0.8469387749999999, 0.8469387749999999,       0.836734694,       0.826530612,       0.806122449, 0.7653061219999999,       0.732653061, 0.7081632660000001,       0.673469388,       0.640816326, 0.6224489799999999,       0.591836735,        0.56122449, 0.5306122449999999,       0.510204082,       0.489795919,       0.469387755, 0.4285714279999999,        0.37755102, 0.3469387749999999,       0.285714285,       0.230612245,       0.173469387, 0.1122448979999999, 0.06530612199999997, 0.03061224500000004, 0.03469387800000001, 0.04081632700000004,       0.071428571,       0.081632653,       0.132653061,       0.204081632,       0.397959183, 0.3959183669999999, 0.3653061230000001, 0.2142857150000002, 0.1326530610000001, 0.1326530609999999, 0.1387755099999999,       0.167346939,               0.2, 0.2346938779999999,       0.275510205,       0.336734694,       0.418367347,       0.581632654,       0.626530612, 0.6714285720000001, 0.6857142849999999, 0.7000000000000001, 0.7142857140000001 ] 
}

pieChart.js

// This is an example Chaism plugin that uses D3 to make a pie chart. 
// Draws from this Pie Chart example http://bl.ocks.org/mbostock/3887235
function PieChart() {

  var my = ChiasmComponent({

    // This property specifies which column of the input data should be used to
    // determine the size of each slice of the pie chart.
    sliceColumn: Model.None,

    // This number varies between 0 and 1. Any value other than 0 converts the
    // visualization from a pie chart to a donut chart.
    //
    // 0 = a pie chart,
    // 0.5 = a thick donut chart
    // 0.9 = a thin donut chart
    donutHoleSize: 0,

    // The margin (in pixels) on all sides of the pie chart.
    margin: 10,

    fill: "none",
    stroke: "gray",
    strokeWidth: "1px"

  });

  var arc = d3.svg.arc();
  var pie = d3.layout.pie().sort(null);
  
  var g = d3.select(my.initSVG()).append("g");

  my.when(["data", "box", "sliceColumn", "donutHoleSize", "margin", "fill", "stroke", "strokeWidth" ],
      function (data, box, sliceColumn, donutHoleSize, margin, fill, stroke, strokeWidth){

    pie.value(function(d) { return d[sliceColumn]; });

    var arcPath = g.selectAll("path").data(pie(data));

    var radius = Math.min(box.width, box.height) / 2 - margin;
    arc.outerRadius(radius);
    arc.innerRadius(donutHoleSize * radius);

    arcPath.enter().append("path");
    arcPath.exit().remove();
    arcPath
      .attr("d", arc)
      .attr("fill", fill)
      .attr("stroke", stroke)
      .attr("stroke-width", strokeWidth);

  });

  my.when("box", function (box) {
    g.attr("transform", "translate(" + box.width / 2 + "," + box.height / 2 + ")");
  });

  return my;
}

pieChartData.csv

age,population
<5,2704659
5-13,4499890
14-17,2159981
18-24,3853788
25-44,14106543
45-64,8819342
≥65,612463

pieChartData.json

{
  "columns": [
    { "name": "age", "type": "string" },
    { "name": "population", "type": "number" }
  ]
}

scatterPlot.js

// This is an example Chaism plugin that uses D3 to make a scatter plot. 
// Draws from this Scatter Plot example http://bl.ocks.org/curran/134ed87c99257e3f2e31
function ScatterPlot() {

  var my = ChiasmComponent({

    margin: {
      left:   30,
      top:    30,
      right:  30,
      bottom: 30
    },
    
    xColumn: Model.None,
    yColumn: Model.None,

    // "r" stands for radius.
    rColumn: Model.None,

    // The circle radius used if rColumn is not specified.
    rDefault: 3,

    // The range of the radius scale if rColumn is specified.
    rMin: 0,
    rMax: 10,

    fill: "black",
    stroke: "none",
    strokeWidth: "1px"

  });

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

  var g = d3.select(my.initSVG()).append("g");

  // Respond to changes in size and margin.
  // Inspired by D3 margin convention from http://bl.ocks.org/mbostock/3019563
  my.when(["box", "margin"], function(box, margin){

    my.innerBox = {
      width: box.width - margin.left - margin.right,
      height: box.height - margin.top - margin.bottom
    };

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

  my.when(["data", "innerBox", "xColumn"], function (data, innerBox, xColumn){
    if(xColumn !== Model.None){
      xScale.range([0, innerBox.width]);
      xScale.domain(d3.extent(data, function (d){ return d[xColumn]; }));
      my.x = function (d){ return xScale(d[xColumn]); };
    }
  });

  my.when(["data", "innerBox", "yColumn"], function (data, innerBox, yColumn){
    if(yColumn !== Model.None){
      yScale.range([innerBox.height, 0]);
      yScale.domain(d3.extent(data, function (d){ return d[yColumn]; }));
      my.y = function (d){ return yScale(d[yColumn]); };
    }
  });

  // Generate a function or constant for circle radius,
  // depending on whether or not rColumn is defined.
  my.when(["data", "rColumn", "rDefault", "rMin", "rMax"],
      function (data, rColumn, rDefault, rMin, rMax){

    if(rColumn === Model.None){
      my.r = rDefault;
    } else {
      rScale
        .domain(d3.extent(data, function (d){ return d[rColumn]; }))
        .range([rMin, rMax]);
      my.r = function (d){ return rScale(d[rColumn]); };
    }
  });

  my.when([ "data", "x", "y", "r", "fill", "stroke", "strokeWidth" ],
      function (data, x, y, r, fill, stroke, strokeWidth){

    var circles = g.selectAll("circle").data(data);
    circles.enter().append("circle");
    circles.exit().remove();
    circles
      .attr("cx", x)
      .attr("cy", y)
      .attr("r", r)
      .attr("fill", fill)
      .attr("stroke", stroke)
      .attr("stroke-width", strokeWidth);

  });

  return my;
}

scatterPlotData.csv

sepal_length,sepal_width,petal_length,petal_width,class
5.0,3.4,1.5,0.2,setosa
4.8,3.4,1.6,0.2,setosa
5.1,3.5,1.4,0.3,setosa
5.4,3.4,1.7,0.2,setosa
5.0,3.4,1.6,0.4,setosa
4.4,3.0,1.3,0.2,setosa
5.0,3.5,1.3,0.3,setosa
4.6,3.2,1.4,0.2,setosa
6.9,3.1,4.9,1.5,versicolor
5.9,3.0,4.2,1.5,versicolor
5.8,2.7,4.1,1.0,versicolor
6.1,2.8,4.0,1.3,versicolor
5.7,2.6,3.5,1.0,versicolor
5.5,2.4,3.8,1.1,versicolor
5.6,3.0,4.1,1.3,versicolor
5.8,2.6,4.0,1.2,versicolor
5.1,2.5,3.0,1.1,versicolor
5.8,2.7,5.1,1.9,virginica
7.3,2.9,6.3,1.8,virginica
6.8,3.0,5.5,2.1,virginica
6.0,2.2,5.0,1.5,virginica
5.6,2.8,4.9,2.0,virginica
6.3,2.8,5.1,1.5,virginica
6.9,3.1,5.4,2.1,virginica
6.2,3.4,5.4,2.3,virginica

scatterPlotData.json

{
  "columns": [
    { "name": "sepal_length", "type": "number" },
    { "name": "sepal_width",  "type": "number" },
    { "name": "petal_length", "type": "number" },
    { "name": "petal_width",  "type": "number" },
    { "name": "class",        "type": "string" }
  ]
}