Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how do you draw linear line in scatter plot with d3.js

Tags:

d3.js

I am looking to implement ggplot2 type of graphs using d3.js library for interactivey purpose. I love ggplot2 but users are interested in interactive graphs. I've been exploring d3.js library and there seems to be lots of different graph capability but I really did not see any statistical graphs like linear line, forecast etc. Given a scatter plot, is it possible to also add linear line to the graph.

I have this sample script that draws scatter plot. How would I add linear line to this graph in d3.js?

// data that you want to plot, I've used separate arrays for x and y values
var xdata = [5, 10, 15, 20],
    ydata = [3, 17, 4, 6];

// size and margins for the chart
var margin = {top: 20, right: 15, bottom: 60, left: 60}
  , width = 960 - margin.left - margin.right
  , height = 500 - margin.top - margin.bottom;

// x and y scales, I've used linear here but there are other options
// the scales translate data values to pixel values for you
var x = d3.scale.linear()
          .domain([0, d3.max(xdata)])  // the range of the values to plot
          .range([ 0, width ]);        // the pixel range of the x-axis

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

// the chart object, includes all margins
var chart = d3.select('body')
.append('svg:svg')
.attr('width', width + margin.right + margin.left)
.attr('height', height + margin.top + margin.bottom)
.attr('class', 'chart')

// the main object where the chart and axis will be drawn
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);

// draw the graph object
var g = main.append("svg:g"); 

g.selectAll("scatter-dots")
  .data(ydata)  // using the values in the ydata array
  .enter().append("svg:circle")  // create a new circle for each value
      .attr("cy", function (d) { return y(d); } ) // translate y value to a pixel
      .attr("cx", function (d,i) { return x(xdata[i]); } ) // translate x value
      .attr("r", 10) // radius of circle
      .style("opacity", 0.6); // opacity of circle
like image 263
user1471980 Avatar asked Jul 01 '13 14:07

user1471980


People also ask

Can scatter plots have lines?

Scatter plots are similar to line graphs in that they start with mapping quantitative data points. The difference is that with a scatter plot, the decision is made that the individual points should not be connected directly together with a line but, instead express a trend.

How do I add a line to a scatter plot in R?

A scatter plot can be created using the function plot(x, y). The function lm() will be used to fit linear models between y and x. A regression line will be added on the plot using the function abline(), which takes the output of lm() as an argument. You can also add a smoothing line using the function loess().

When making a line from this scatter plot What is this line called?

A line of best fit is a straight line drawn through the maximum number of points on a scatter plot balancing about an equal number of points above and below the line. It is used to study the nature of relation between two variables.


2 Answers

To add a line to your plot, all that you need to do is to append some line SVGs to your main SVG (chart) or to the group that contains your SVG elements (main).

Your code would look something like the following:

chart.append('line')
    .attr('x1',x(10))
    .attr('x2',x(20))
    .attr('y1',y(5))
    .attr('y2',y(10))

This would draw a line from (10,5) to (20,10). You could similarly create a data set for your lines and append a whole bunch of them.

One thing you might be interested in is the SVG path element. This is more common for lines than drawing one straight segment at a time. The documentation is here.

On another note you may find it easier to work with data in d3 if you create it all as one object. For example, if your data was in the following form:

data = [{x: 5, y:3}, {x: 10, y:17}, {x: 15, y:4}, {x: 20, y:6}]

You could use:

g.selectAll("scatter-dots")
  .data(ydata)  // using the values in the ydata array
  .enter().append("svg:circle")  // create a new circle for each value
      .attr("cy", function (d) { return y(d.y); } ) //set y
      .attr("cx", function (d,i) { return x(d.x); } ) //set x

This would eliminate potentially messy indexing if your data gets more complex.

like image 142
ckersch Avatar answered Sep 30 '22 02:09

ckersch


UPDATE: Here is the relevant block: https://bl.ocks.org/HarryStevens/be559bed98d662f69e68fc8a7e0ad097

I wrote this function to calculate a linear regression from data, formatted as JSON.

It takes 5 parameters: 1) Your data 2) The column name of the data plotted on your x-axis 3) The column name of the data plotted on your y-axis 4) The minimum value of your x-axis 5) The minimum value of your y-axis

I got the formula for calculating a linear regression from http://classroom.synonym.com/calculate-trendline-2709.html

function calcLinear(data, x, y, minX, minY){
  /////////
  //SLOPE//
  /////////

  // Let n = the number of data points
  var n = data.length;

  var pts = [];
  data.forEach(function(d,i){
    var obj = {};
    obj.x = d[x];
    obj.y = d[y];
    obj.mult = obj.x*obj.y;
    pts.push(obj);
  });

  // Let a equal n times the summation of all x-values multiplied by their corresponding y-values
  // Let b equal the sum of all x-values times the sum of all y-values
  // Let c equal n times the sum of all squared x-values
  // Let d equal the squared sum of all x-values
  var sum = 0;
  var xSum = 0;
  var ySum = 0;
  var sumSq = 0;
  pts.forEach(function(pt){
    sum = sum + pt.mult;
    xSum = xSum + pt.x;
    ySum = ySum + pt.y;
    sumSq = sumSq + (pt.x * pt.x);
  });
  var a = sum * n;
  var b = xSum * ySum;
  var c = sumSq * n;
  var d = xSum * xSum;

  // Plug the values that you calculated for a, b, c, and d into the following equation to calculate the slope
  //  m = (a - b) / (c - d)
  var m = (a - b) / (c - d);

  /////////////
  //INTERCEPT//
  /////////////

  // Let e equal the sum of all y-values
  var e = ySum;

  // Let f equal the slope times the sum of all x-values
  var f = m * xSum;

  // Plug the values you have calculated for e and f into the following equation for the y-intercept
  // y-intercept = b = (e - f) / n = (14.5 - 10.5) / 3 = 1.3
  var b = (e - f) / n;

  // return an object of two points
  // each point is an object with an x and y coordinate
  return {
    ptA : {
      x: minX,
      y: m * minX + b
    },
    ptB : {
      y: minY,
      x: (minY - b) / m
    }
  }

}
like image 27
Harry Stevens Avatar answered Sep 30 '22 04:09

Harry Stevens