JSFiddle - React, Tailwind, and code Playground

by Gordon Woodhull

HTML

<script src="https://unpkg.com/d3@5/dist/d3.js"></script>
<script src="https://unpkg.com/crossfilter2@1.4/crossfilter.js"></script>
<link rel="stylesheet" href="https://unpkg.com/dc@3/dc.css">
<script src="https://unpkg.com/dc@3/dc.js"></script>
<script src="https://rawgit.com/crossfilter/reductio/master/reductio.js"></script>
<script src="https://npmcdn.com/universe@latest/universe.js"></script>
<div id="test"></div>
<!-- here's a way to load data from a jsfiddle, to avoid CORS
  problems - see http://stackoverflow.com/a/22896088/676195
  -->
<pre id="data">
Prec,Recall,System
0.5,0.5,Test
0.7,0.3,Foo
0.95,0.87,Bar
0.1,0.1,Bad
0.2,0.9,Good
</pre>

CSS

pre#data {
  display: none;
}

JavaScript

function safe_division(numerator, denominator){
  if (!denominator) {  // Matches +0, -0, NaN
    return 0.0;
  }
  return numerator/denominator;
}
function generateFmeasureContours(width, height, beta){
  //x axis is recall, y axis is precision
  var betasquared = beta * beta;
  var values = new Array(height * width);
  for (var prec = height, k = 0; prec > 0; --prec) {
    for (var rec = 0; rec < width; ++rec, ++k) {
      var newPrec = prec/height;
      var newRec = rec/width;
      values[k] = safe_division((1 + betasquared) * (newPrec * newRec), (betasquared * newPrec) + newRec);
    }
  }
  var thresholds = d3.range(0.0, 1.0, 0.05);
  var color = d3.scaleLinear()
  .domain(d3.extent(thresholds))
  .interpolate(d => d3.interpolateRdYlGn)
  var contours = d3.contours()
  .size([width, height])
  .thresholds(thresholds)
  (values);
  return [contours, color];
}

d3.selection.prototype.moveToFront = function() {
  return this.each(function(){
    this.parentNode.appendChild(this);
  });
};

var chart = dc.scatterPlot("#test");
var experiments = d3.csvParse(d3.select('pre#data').text());
var ndx                 = crossfilter(experiments),
    scatterDim          = ndx.dimension(function(d) {return [d.Prec, d.Recall];}),
    scatterGroup        = scatterDim.group();

chart
  .width(808)
  .height(768)
  .dimension(scatterDim)
  .group(scatterGroup)
  .xAxisLabel("Recall")
  .x(d3.scaleLinear().domain([0,1]))
  .yAxisLabel("Precision")
  .y(d3.scaleLinear().domain([0,1]))
  .on('pretransition', function (chart) {
    // add contour layer to back (beginning of svg) only if it doesn't exist
    var contourLayer = chart.g().selectAll('g.contour-layer').data([0]);
    contourLayer = contourLayer
        .enter().insert('g', ':first-child')
        .attr('class', 'contour-layer')
        .attr('transform', 'translate(' + [chart.margins().left,chart.margins().top].join(',') + ')')
        .merge(contourLayer);

    var width = chart.effectiveWidth(), height =...