Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can d3.js draw two scatterplots on the same graph using data from the same source?

All of the d3 tutorials I've found use data arranged in arrays of objects from which they graph one point for each object in the array. Given data in the following structure:

data = [
     {id: 1, x: 4, y: 10, type: 1},
     {id: 2, x: 5, y: 20, type: 2}
     ...
]

The x and y values are used to make a scatterplot. The type parameter is used to change the color of each point. See this jsfiddle for an example: http://jsfiddle.net/uxbHv/

Unfortuately, I have a different data structure and I can't figure out how to create the same graph by drawing two data points for each object. Here is some example data:

dataSet = [
     {xVar: 5, yVar1: 90, yVar2: 22},
     {xVar: 25, yVar1: 30, yVar2: 25},
     {xVar: 45, yVar1: 50, yVar2: 80},
     {xVar: 65, yVar1: 55, yVar2: 9},
     {xVar: 85, yVar1: 25, yVar2: 95}
]

I can graph xVar individually against yVar1 or yVar2, but I can not figure out how to get both on the same graph: http://jsfiddle.net/634QG/

like image 842
Andrew Staroscik Avatar asked Jul 26 '12 15:07

Andrew Staroscik


People also ask

How do I plot multiple Scatterplots in R?

You can create a scatter plot in R with multiple variables, known as pairwise scatter plot or scatterplot matrix, with the pairs function. In addition, in case your dataset contains a factor variable, you can specify the variable in the col argument as follows to plot the groups with different color.

Can scatter plots be connected?

A connected scatterplot displays the evolution of a numeric variable. Data points are represented by a dot and connected by straight line segments. It often shows a trend in data over intervals of time: a time series.

What is the use of plot to D3?

Plot represents a chart in its own terms. D3 transforms the native DOM; it describes incremental changes to standard HTML or SVG elements and their attributes. It has no notion of a “bar”, let alone a bar chart, but you can use it to append and transform an SVG <rect> , which serves the role of a bar.


1 Answers

The general rule when using a data-join is that you want a one-to-one mapping from data to elements. So, if you have two series in your scatterplot, you’ll want two container elements (such as G elements) to represent the series. Since you currently have only one data array, you’ll also want to use array.map to convert the data representation into two parallel arrays with the same representation. This way, you don’t have to duplicate code for each series.

Say your data was represented in a CSV file with one column for the x-values, and multiple other columns for the y-values of each series:

x,y1,y2
5,90,22
25,30,25
45,50,80
65,55,9
85,25,95

If you want the code to be completely generic, you first need to compute the series’ names, such as ["y1", "y2"]. (If you added a third column to the CSV file, it might be ["y1", "y2", "y3"].) You can compute the names using d3.keys, which extracts the named properties from an object. For example, d3.keys({foo: 1, bar: 2}) returns ["foo", "bar"].

// Compute the series names ("y1", "y2", etc.) from the loaded CSV.
var seriesNames = d3.keys(data[0])
    .filter(function(d) { return d !== "x"; })
    .sort();

Now that you have the series names, you can create an array of arrays of points. The outer array represents the series (of which there are two) and the inner arrays store the data points. You can simultaneously convert the points to a consistent representation (objects with x and y properties), allowing you to reuse code across series.

// Map the data to an array of arrays of {x, y} tuples.
var series = seriesNames.map(function(series) {
  return data.map(function(d) {
    return {x: +d.x, y: +d[series]};
  });
});

Note this code uses the + operator to coerce the CSV values to numbers. (CSV files are untyped, so they are initially strings.)

Now that you’ve mapped your data to a regular format, you can create G elements for each series, and then circle elements within for each point. The resulting SVG structure will look like this:

<g class="series">
  <circle class="point" r="4.5" cx="1" cy="2"/>
  <circle class="point" r="4.5" cx="3" cy="2"/>
  …
</g>
<g class="series">
  <circle class="point" r="4.5" cx="5" cy="4"/>
  <circle class="point" r="4.5" cx="7" cy="6"/>
  …
</g>

And the corresponding D3 code:

// Add the points!
svg.selectAll(".series")
    .data(series)
  .enter().append("g")
    .attr("class", "series")
    .style("fill", function(d, i) { return z(i); })
  .selectAll(".point")
    .data(function(d) { return d; })
  .enter().append("circle")
    .attr("class", "point")
    .attr("r", 4.5)
    .attr("cx", function(d) { return x(d.x); })
    .attr("cy", function(d) { return y(d.y); });

I’ve also added a bit of code to assign each series a unique color by adding a fill style to the containing G element. There are lots of different ways to do this, of course. (You might want to be more specific about the color for each series, for example.) I’ve also left out the code that computes the domains of your x and y scales (as well as rendering the axes), but if you want to see the entire working example:

  • http://bl.ocks.org/3183403
like image 69
mbostock Avatar answered Oct 21 '22 09:10

mbostock