Radar Chart

Dataviz logo representing a Spider chart.

A radar or spider or web chart is a two-dimensional chart type designed to plot one or more series of values over multiple quantitative variables. Each variable has its own axis, all axes are joined in the center of the figure.

This page is a step-by-step guide on how to build your own radar chart for the web, using React (for rendering) and D3.js (to compute the axis, and shape coordinates).

It starts by describing how the data should be organized and how to initialize the Radar component. It then explains how to compute axis dynamically, and plot the lines and axis. Once this is done, it shows how to deal with scaling and how to add an interactive legend. 🙇‍♂️.

Useful links

The Data

The dataset provides several numeric values for a set of data items.

The suggested data structure is an array of object, where each object is a data item. It can have as many numeric properties as needed. It also has a name property that identifies the data item.


Here is a minimal example of the data structure:

const data = [
  {var1: 5.1, var2: 3.5, ..., name: 'Mark'},
  {var1: 4.9, var2: 3.0, ..., name: 'Rosa'},
  ...
]

Note: this is the same data format as for a correlogram or for a parralel chart.

Component skeleton

The goal here is to create a Radar component that will be stored in a Radar.tsx file. This component requires 4 props to render: a width, a height, some data and an array providing the name of the variables to display.

The shape of the data is described above. The width and height will be used to render an svg element in the DOM, in which we will insert the radar chart.

To put it in a nutshell, that's the skeleton of our Radar component:

import * as d3 from "d3"; // we will need d3.js

type DataItem = {
  [variable: string]: number;
} & { name: string };


type RadarProps = {
  width: number;
  height: number;
  data: DataItem[];
  variables: string[]
};

export const Radar = ({ width, height, data, variables }: RadarProps) => {

  // read the data & get a list of groups
  // build X scale
  // build Y scales: 1 per variable
  // build color scales
  // loop through variables to add axes
  // loop through data items and through variables to draw lines

  return (
    <div>
      <svg width={width} height={height}>
        // render all the <lines>
      </svg>
    </div>
  );
};

It's fundamental to understand that with this code organization, d3.js will be used to prepare the SVG path, but it's React that will render them in the return() statement. We won't use d3 methods like append that you can find in usual d3.js examples.

Scales

Building a radar chart requires several scales and axes. Understanding how those scales work and how to draw the background grid using polar coordinates is probably the trickiest part or the radar chart creation.

D3.js comes with a handful set of predefined scales. scaleBand and scaleRadial are the ones we are going to use here.

→ X Scale

We need only 1 X scale. This scale is gonna allocate an angle for each variable name of the dataset. The first variable will be directed to the top of the figure, the second a few more radians clock-wise and so on.

This is how the scale is defined:

const allVariableNames = axisConfig.map((axis) => axis.name);

// The x scale provides an angle for each variable of the dataset
const xScale = d3
  .scaleBand()
  .domain(allVariableNames)
  .range([0, 2 * Math.PI]);

→ Y Scales

Several Y scales are required, one per variable in the dataset. The corresponding axes will be drawn from the center of the figure to the outer part, with an angle determined by the xScale.

The y scales are computed using the scaleRadial() function as follow. They are all stored in a yScales object.

// Compute the y scales: 1 scale per variable.
// Provides the distance to the center.
let yScales: { [name: string]: YScale } = {};

axisConfig.forEach((axis) => {
  yScales[axis.name] = d3
    .scaleRadial()
    .domain([0, axis.max])
    .range([INNER_RADIUS, outerRadius]);
});

Radar chart background grid

Once those scales are available, we need to draw the background grid of the radar chart.

A bunch of options exist for this. Here I suggest to loop through the axisConfig to draw the axes, and add some concentric circles to create a grid.

Since the code is a bit long to create this grid, I strongly advise to place it in a separate component (RadarGrid here).

speedaccelerationconsosafetystyleprice

Background grid of a radar chart built with react and d3.js. 6 Variables are represented using 6 axes with polar coordinates

Note that placing the labels requires to translate some polar coordinates to cartesian coordinates. This can be done using the following function:

export const polarToCartesian = (angle: number, distance: number) => {
  const x = distance * Math.cos(angle);
  const y = distance * Math.sin(angle);
  return { x, y };
};

Radar chart with 1 group

Finally! ✨

We can now map through the data array and draw a path per item thanks to the scales computed above.

What's tricky here is that we are dealing with polar coordinates. We have a set of points that are defined by an angle (x scale) and by a distance to the center of the figure (y scale).

Fortunately, the lineRadial() function of d3 is here to help. We can define a radial line generator using the following statement:

// Create a radial line generator
const lineGenerator = d3.lineRadial();

It works pretty much the same as the classic line() function of d3, but expects an angle and a distance instead of a x and a y position.

// Use the radial line generator
const path = lineGenerator([
  [0, 100], // first data point, 0 is its angle, 100 is its distance to the center
  [Math.PI / 2, 50], // second data point = second variable
  [Math.PI, 10],
]);

// Result is a path that you can pass to the d argument of a SVG <path>
// console.log(path)
// M0,-100 L50,-3.06 L1.2246,10

Note that in order to close the shape, we need to add the first data point again after reaching the last data point, to close the loop.

speedaccelerationconsosafetystyleprice

A first basic radar chart with only 1 group represented. Made with React and d3.js

Responsive Spider / Radar with react

The component above is not responsive. It expects 2 props called width and height and will render a Spider / Radar of those dimensions.

Making the Spider / Radar responsive requires adding a wrapper component that gets the dimension of the parent div, and listening to a potential dimension change. This is possible thanks to a hook called useDimensions that will do the job for us.

useDimensions: a hook to make your viz responsive
export const useDimensions = (targetRef: React.RefObject<HTMLDivElement>) => {

  const getDimensions = () => {
    return {
      width: targetRef.current ? targetRef.current.offsetWidth : 0,
      height: targetRef.current ? targetRef.current.offsetHeight : 0
    };
  };

  const [dimensions, setDimensions] = useState(getDimensions);

  const handleResize = () => {
    setDimensions(getDimensions());
  };

  useEffect(() => {
    window.addEventListener("resize", handleResize);
    return () => window.removeEventListener("resize", handleResize);
  }, []);

  useLayoutEffect(() => {
    handleResize();
  }, []);

  return dimensions;
}

I'm in the process of writing a complete blog post on the topic. Subscribe to the project to know when it's ready.




Spider / Radar inspiration

If you're looking for inspiration to create your next Spider / Radar, note that dataviz-inspiration.com showcases many examples. Definitely the best place to get ... inspiration!

dataviz-inspiration.com showcases hundreds of stunning dataviz projects. Have a look to get some ideas on how to make your Spider / Radar looks good!

visit

Radar chart with several groups

The process to get a radar chart with several groups is very similar to the previous example.

We just need to create a color scale and add a shape for each item of the dataset through a loop. Do not try to add too many groups on the same figure, it make it totally unreadable.

speedaccelerationconsosafetystyleprice

A radar chart with several groups displayed on the same figure. Made with React and d3.js

ToDospider chart with small multiple to make it more readable

Animation

It is common to have a radar chart that is updated when a button is clicked on the application. It is possible to implement a smooth, animated transition between states thanks to the react-spring library.

The following example illustrates this with a real world example. The radar chart is animated, together with a line chart and a lollipop.


GIF of a radar chart, a line chart and a lollipop that animate between dataset

Radar chart, line chart an lollipop with animated transition

Three charts connected to same buttons, with smooth, synchronized animation when a button is clicked

Ranking

Contact

👋 Hey, I'm Yan and I'm currently working on this project!

Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!