D3.js drawing a scatter plot step by step - scaleLinear(), domain(), range(), axisBottom()

Learn how to draw a scatter plot step by step.

How to create a scatter plot

As a simple graph sample, draw a scatter plot.

Scatter, or dispersion, is a value that represents how spread out a variable is.

Plotting coordinate points

This time, use the following data.

var dataset = [ [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
                [410, 12], [475, 44], [25, 67], [85, 21], [220, 88] ];

[ ] is an array, and this is two-dimensional array data in the form [[x coordinate 1, y coordinate 1], [x coordinate 2, y coordinate 2], ...].

First, display the coordinates using circle, which draws circles.

svg.selectAll("circle") 
  .data(dataset) 
  .enter()
  .append("circle")
  .attr("cx", function(d) { return d[0]; })
  .attr("cy", function(d) { return d[1]; })
  .attr("r", 4);

The full code is as follows:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>D3 Test</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
  </head>
  <body>
    <script>
      var svg = d3.select("body").append("svg").attr("width", 700).attr("height", 100);
      var dataset = [ [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
                      [410, 12], [475, 44], [25, 67], [85, 21], [220, 88] ];

      svg.selectAll("circle") 
        .data(dataset) 
        .enter()
        .append("circle")
        .attr("cx", function(d) { return d[0]; })
        .attr("cy", function(d) { return d[1]; })
        .attr("r", 4);
    </script>
  </body>
</html>

Because the data assigned to circle is a two-dimensional array, each circle is assigned data in the form [x coordinate, y coordinate]. For example, the first circle is assigned [5, 20]. This data is set to the cx and cy attributes respectively.

Displaying coordinate point text

Next, display each coordinate position as text in x coordinate, y coordinate format.

svg.selectAll("text") 
  .data(dataset) 
  .enter()
  .append("text")
  .attr("x", function(d) { return d[0]; })
  .attr("y", function(d) { return d[1]; })
  .attr("fill", "red")
  .text(function(d) {
    return d[0] + "," + d[1];
  });

The full code is as follows:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>D3 Test</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
  </head>
  <body>
    <script>
      var svg = d3.select("body").append("svg").attr("width", 700).attr("height", 100);
      var dataset = [ [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
                      [410, 12], [475, 44], [25, 67], [85, 21], [220, 88] ];

      svg.selectAll("circle") 
        .data(dataset) 
        .enter()
        .append("circle")
        .attr("cx", function(d) { return d[0]; })
        .attr("cy", function(d) { return d[1]; })
        .attr("r", 4);

      svg.selectAll("text") 
        .data(dataset) 
        .enter()
        .append("text")
        .attr("x", function(d) { return d[0]; })
        .attr("y", function(d) { return d[1]; })
        .attr("fill", "red")
        .text(function(d) {
          return d[0] + "," + d[1];
        });
    </script>
  </body>
</html>

Note that the x coordinate increases toward the right, while the y coordinate increases downward.

Scale conversion

Use the following D3 function for scale conversion.

var scale = d3.scaleLinear()
    .domain([0, 500])
    .range([0, 100]);

This function assigns to the variable scale a function that converts the range specified by domain into the range specified by range.

For example, you can call it as follows, and the call returns 20.

scale(100);

This is the result of converting the [0,500] range into the [0,100] range, in other words reducing the coordinate to one-fifth. 100 / 5 = 20.

Now apply this to the graph.

Set the graph width and height as follows:

var width = 400;
var height = 300;

The ratio is set to 4:3.

var xScale = d3.scaleLinear()
  .domain([0, d3.max(dataset, function(d) { return d[0]; })])
  .range([0, width]);

var yScale = d3.scaleLinear()
  .domain([0, d3.max(dataset, function(d) { return d[1]; })])
  .range([height, 0]);

Here, d3.max(..) is used to find the maximum value in the data. d3.max takes an array as the first argument and a return function as the second argument.
Because the y coordinate should be reversed, the larger value is placed first in range.

Redraw using this function.

  svg.selectAll("circle") 
     .data(dataset) 
     .enter()
     .append("circle")
     .attr("cx", function(d) { return xScale(d[0]); })
     .attr("cy", function(d) { return yScale(d[1]); })
     .attr("r", 4);

The full code is as follows:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>D3 Test</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
  </head>
  <body>
    <script>
      // Write code here.
      var dataset = [ [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
                [410, 12], [475, 44], [25, 67], [85, 21], [220, 88] ];

      var width = 400;
      var height = 300;

      var svg = d3.select("body").append("svg").attr("width", width).attr("height", height);

      var xScale = d3.scaleLinear()
        .domain([0, d3.max(dataset, function(d) { return d[0]; })])
        .range([0, width]);

      var yScale = d3.scaleLinear()
        .domain([0, d3.max(dataset, function(d) { return d[1]; })])
        .range([height, 0]);

      svg.selectAll("circle") 
        .data(dataset) 
        .enter()
        .append("circle")
        .attr("cx", function(d) { return xScale(d[0]); })
        .attr("cy", function(d) { return yScale(d[1]); })
        .attr("r", 4);

      svg.selectAll("text") 
        .data(dataset) 
        .enter()
        .append("text")
        .attr("x", function(d) { return xScale(d[0]); })
        .attr("y", function(d) { return yScale(d[1]); })
        .attr("fill", "red")
        .text(function(d) {
          return d[0] + "," + d[1];
        });
    </script>
  </body>
</html>

The y coordinate now gets smaller toward the bottom, and the aspect ratio is also 4:3.

Drawing an axis

Next, draw an axis. D3 provides functions for this.

var axisX = d3.axisBottom(xScale);
svg.append("g")
  .call(axisX);

Use this together with the scale from earlier. In addition to d3.axisBottom, which represents a bottom axis, there are d3.axisTop, d3.axisRight, and d3.axisLeft. Calling it with the call function draws an axis as shown below.

The full code with the code above applied is as follows. Because there is an axis, the text display has been removed.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>D3 Test</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
  </head>
  <body>
    <script>
      var dataset = [ [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
                [410, 12], [475, 44], [25, 67], [85, 21], [220, 88] ];

      var width = 400;
      var height = 300;

      var svg = d3.select("body").append("svg").attr("width", width).attr("height", height);

      var xScale = d3.scaleLinear()
        .domain([0, d3.max(dataset, function(d) { return d[0]; })])
        .range([0, width]);

      var yScale = d3.scaleLinear()
        .domain([0, d3.max(dataset, function(d) { return d[1]; })])
        .range([height, 0]);

      svg.selectAll("circle") 
        .data(dataset) 
        .enter()
        .append("circle")
        .attr("cx", function(d) { return xScale(d[0]); })
        .attr("cy", function(d) { return yScale(d[1]); })
        .attr("r", 4);

      var axisX = d3.axisBottom(xScale);
      svg.append("g")
        .call(axisX);
    </script>
  </body>
</html>

Adjusting coordinate point and axis positions

The previous version does not account for the positions and the width needed to display the axes. Move the position as follows.

var axisX = d3.axisBottom(xScale);
var padding = 30;
svg.append("g")
    .attr("transform", "translate(" + 0 + "," + (height-padding) + ")")
    .call(axisX);

Taking this into account, the graph drawing code becomes as follows.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>D3 Test</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
  </head>
  <body>
    <script>
     var dataset = [ [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
                      [410, 12], [475, 44], [25, 67], [85, 21], [220, 88] ];

      var width = 400;
      var height = 300;
      var svg = d3.select("body").append("svg").attr("width", width).attr("height", height);

      var padding = 30;
      var xScale = d3.scaleLinear()
        .domain([0, d3.max(dataset, function(d) { return d[0]; })])
        .range([padding, width - padding]);

      var yScale = d3.scaleLinear()
        .domain([0, d3.max(dataset, function(d) { return d[1]; })])
        .range([height - padding, padding]);

      var axisx = d3.axisBottom(xScale);
      var axisy = d3.axisLeft(yScale);
      svg.append("g")
        .attr("transform", "translate(" + 0 + "," + (height - padding) + ")")
        .call(axisx);

      svg.append("g")
        .attr("transform", "translate(" + padding + "," + 0 + ")")
        .call(axisy);

      svg.selectAll("circle") 
        .data(dataset) 
        .enter()
        .append("circle")
        .attr("cx", function(d) { return xScale(d[0]); })
        .attr("cy", function(d) { return yScale(d[1]); })
        .attr("fill", "SkyBlue")
        .attr("r", 4);
    </script>
  </body>
</html>

The scatter plot is now complete. The circle color has also been changed to sky blue.