timbit
timbit

Reputation: 353

Dots of Scatterplot in d3 are not rendering

I have zero experience using d3 and I use Javascript and jQuery only sporadically.

I am trying to create a simple scatterplot with a slider in d3 (and jQuery). The purpose of the slider is to select the dataset to plot. I have a JSON object all_data, a list of maps. For each map in the list, I use the "SI" and "F1" lists as x and y values, respectively.

When the slider is moved, I call the plotData() function with as argument an integer number that represents the index of the dataset in all_data (So plotData(5) would plot the 6th dataset in all_data).

Now, for some reason, the plot axes construct nicely, but the data points do not plot. I have tried to debug the code by placing console.log(data) and function (d,i) { console.log(d); return x(d['SI']) at relevant sections in the code (see the comment lines). The data object contains data at all points in the function. The problem seems to arise after g.selectAll("scatter-dots") part. I suspect the final call to the function plotting the 'dots' is not called, but I cannot figure out why.

My question is: how can I fix my code such that the data points are added to the plot? Any other tips on coding, design or performance improvements are more than welcome - here to learn.

EDIT: I use a custom d3.slider extension from GitHub

var all_data = [{"REGRESSION_Y": [ list_of_floats ], "F1": [ list_of_floats ], "SI": [ list_of_floats ], "f+": some_float_val }

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>D3 Test</title>
        <link rel="stylesheet" href="d3/d3-slider-master/d3.slider.css" />  
        <link rel="stylesheet" href="plot.css" />  

        <script type="text/javascript" src="jquery/jquery-3.1.1.min.js"></script>
        <script type="text/javascript" src="d3/d3.v3.min.js"></script>
        <script type="text/javascript" src="json_data.js"></script>
        <script type="text/javascript" src="d3/d3-slider-master/d3.slider.js"></script>
    </head>
    <body>

        <h2><span id="slider3text">Data points selected: 5</span></h2>

        <div id='container' style='width: 75%; height: 100%' class='centered'>

            <div id="plotfield" style='width: 100%; height: 90%'></div>
            <div id="slider3" style='height: auto'></div>

        </div>

    </body>
    <script type="text/javascript">


    d3.select('#slider3').call(d3.slider().axis(true).min( 5 ).max( all_data.length-1 ).step(1).on("slide", function(evt, value) {

                    d3.select('#slider3text').text("Data points selected: "  + value);
                    plotData(value)

              }));


        </script>


<script type="text/javascript">

function plotData(i) {

    var data = all_data[i]

    $("#plotfield").empty()

    var margin = {top: 50, right: 5, bottom: 80, left: 5}
      , width = $("#container").width() - margin.left - margin.right
      , height = $("#container").height() - margin.top - margin.bottom;

    var x = d3.scale.linear()
              .domain([0, d3.max(data, function(d) { return d['SI']; })])
              .range([ 0, width ]);

    var y = d3.scale.linear()
            .domain([0, d3.max(data, function(d) { return d['F1']; })])
            .range([ height, 0 ]);

    var chart = d3.select('#plotfield')
  .append('svg:svg')
  .attr('width', width + margin.right + margin.left)
  .attr('height', height + margin.top + margin.bottom)
  .attr('class', 'chart')

    var main = chart.append('g')
  .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
  .attr('width', width)
  .attr('height', height)
  .attr('class', 'main')   

    // draw the x axis
    var xAxis = d3.svg.axis()
  .scale(x)
  .orient('bottom');

    main.append('g')
  .attr('transform', 'translate(0,' + height + ')')
  .attr('class', 'main axis date')
  .call(xAxis);

    // draw the y axis
    var yAxis = d3.svg.axis()
  .scale(y)
  .orient('left');

    main.append('g')
  .attr('transform', 'translate(0,0)')
  .attr('class', 'main axis date')
  .call(yAxis);

   var g = main.append("svg:g"); 

    //console.log(data), this worked here

    g.selectAll("scatter-dots")
      .data(data)
      .enter().append("svg:circle")

          // I have tried function (d,i) { console.log(d); return x(d['SI']) 
          //in the below anonymous functions, but that doesn't yield an output, 
          //causing me to suspect that the functions are not executed.

          .attr("cx", function (d,i) { return x(d['SI']); } )
          .attr("cy", function (d) { return y(d['F1']); } )
          .attr("r", 3);

}

and the CSS file:

.centered {
  position: fixed;
  top: 50%;
  left: 50%;
  /* bring your own prefixes */
  transform: translate(-50%, -50%);
}

body {
  font: 11px sans-serif;
}

.tooltip {
  position: absolute;
  width: 200px;
  height: 28px;
  pointer-events: none;
}

.axis line, .axis path {
    shape-rendering: crispEdges;
    stroke: black;
    fill: none;
}

circle {
    fill: steelblue;
}

Upvotes: 1

Views: 807

Answers (2)

Jaap Nieland
Jaap Nieland

Reputation: 26

From what I see the data[i] returned an object. This is of course not a problem, but the d3 method enter() only supports arrays.

This means that you cannot have things like d['f1'] when using enter() to add data. Converting them into arrays before using them fixes this and you can use d (the array entry) or i the array entry index.

Upvotes: 1

timbit
timbit

Reputation: 353

The problem turned out to be the fact dat the var data = all_data[i] returned an object, instead of an array. A colleague of mine provided the following edit to make it work. (Also some other code was changed to add a trend line to the script).

<script type="text/javascript">

    function plotData(i) {
        // extracting data
        var data = all_data[i];
        var REGRESSION_Y = data.REGRESSION_Y;
        var F1 = data.F1;
        var SI = data.SI;

        // removing previous plot
        $("#plotfield").empty()

        // margins and spacings (slightly larger, y-axis was invinsible)
        var margin = {top: 50, right: 15, bottom: 80, left: 15}
        , width = $("#container").width() - margin.left - margin.right
        , height = $("#container").height() - margin.top - margin.bottom;

        var x = d3.scale.linear()
            .domain([0, d3.max(SI)])
            .range([ 0, width ]);

        var y = d3.scale.linear()
            .domain([0, d3.max(F1)])
            .range([ height, 0]);

        var chart = d3.select('#plotfield')
            .append('svg:svg')
            .attr('width', width + margin.right + margin.left)
            .attr('height', height + margin.top + margin.bottom)
            .attr('class', 'chart')

        var main = chart.append('g')
            .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
            .attr('width', width)
            .attr('height', height)
            .attr('class', 'main')   

        // draw the x axis
        var xAxis = d3.svg.axis()
            .scale(x)
            .orient('bottom');

        main.append('g')
            .attr('transform', 'translate(0,' + height + ')')
            .attr('class', 'main axis date')
            .call(xAxis);

        // draw the y axis
        var yAxis = d3.svg.axis()
            .scale(y)
            .orient('left');

        main.append('g')
            .attr('transform', 'translate(0,0)')
            .attr('class', 'main axis date')
            .call(yAxis);

        // append extra g part in svg and append to it
        var g = main.append("svg:g")

        // append the line
        g.append('line')
            .attr('x1', x(d3.min(SI)))//function(d, i) { return x(d[]) + i; })
            .attr('x2', x(d3.max(SI)))//(2)//function(d, i) { return y(d['REGRESSION_Y']); });
            .attr('y1', y(d3.min(REGRESSION_Y)))
            .attr('y2', y(d3.max(REGRESSION_Y)))
            .style('stroke', '#1B3277')
            .style('stroke-width','1')

        // append the dots
        g.selectAll("scatter-dots")
            .data(SI)
            .enter().append("svg:circle")
            .attr("cx", function (d,i) { return x(SI[i]); } )
            .attr("cy", function (d,i) { return y(F1[i]); } )
            .attr("r", 3);

    }


</script>

Upvotes: 0

Related Questions