D3

Bar graphs are one of the foundations of the graphing world. We'll walk through how to take data, transform it using `scaleBand`

and `scaleLinear`

to create a dynamic bar chart to fit into any width/height.

To get setup we import some data, specify a width/height to work with and render our `svg`

so we have a place to render our bars.

```
import React, { Component } from "react";
import { data } from "./data";
const width = 500;
const height = 300;
class App extends Component {
render() {
return (
<div>
<svg width={width} height={height} />
</div>
);
}
}
```

These are our JavaScript data selectors. When given a data point from the array of data they will select the appropriate key.
So if your data had a value like `price`

instead of `y`

, you'd have `d.price`

being returned for our `ySelector`

.

```
const xSelector = d => d.x;
const ySelector = d => d.y;
```

Our data structure example

```
[
{
x: 0,
y: 1, // Just so it shows up :)
},
{
x: 1,
y: 50,
},
{
x: 2,
y: 100,
},
{
x: 3,
y: 150,
},
];
//...
```

We need to start by importing our 2 scales from `d3-scale`

.

`import { scaleBand, scaleLinear } from "d3-scale";`

`scaleBand`

is used to create an output range is continuous and numeric.

Basically the concept of `scaleBand`

is take an output area (some width), take our data and amount of data points, split it into bands.
A band in our case can be considered each bar. This is why we need to know how many pieces of data we have. In our example data we have `10`

bars. With the current width, as well as known data points it will figure out how wide each bar is.

Not only will it divide our content into each specific bar by width but we can apply padding. This will allow us to create an even spacing of each band aka each bar.

Read more about `scaleBand`

in the D3 documentation d3-scale#band-scales.

```
const xScale = scaleBand()
.range([0, width])
.domain(data.map(d => xSelector(d)))
.padding(0.3);
```

So here we have our range of rendering which will be constrained to `[0, width]`

, so from `[0, 500]`

. Then we will pass in our domain.
Our domain will be an array of our data points so it can calculate how many bars we need to place into `500`

pixels of space. Finally we toss some padding on. This will set our `innerPadding`

and `outerPadding`

to the same value.

`innerPadding`

being between each band aka each bar. The `outerPadding`

being on the left and right sides after all bands are rendered. The padding must be inbetween 0 and 1. We set it to `.3`

so that it's roughly a 1/3 of the width of a bar as spacing. If we set it to `.5`

the padding and the bars would all be equal widths.

Our `scaleLinear`

function will map our domain of `y`

value data which sits between `0`

and `500`

as the max value. Which will map it to the `height`

of our svg we're rendering into.

```
const yScale = scaleLinear()
.range([height, 0])
.domain([0, 500]);
```

The reason why the `height`

is mapped to the `0`

of our data is that the top left corner of an SVG is `0,0`

. When graphing `0`

is generally on the bottom. So in order to map our `0`

value to render at the bottom we map it to the height of our svg.

We need to map over each of our data pieces and we'll calculate the data needed to render using our scales that we created. Each bar will eventually be rendered separately.

```
{
data.map((d, i) => {
const yValue = yScale(ySelector(d));
const barHeight = height - yValue;
});
}
```

Our `yScale(ySelector(d))`

is calculating the `y`

value. You may think that we are rendering from the bottom up because that's how you're viewing it but what we're doing is actually rendering from the top down. Remember everything originates from `0,0`

in the corner.

So for a value of `0`

we've mapped it to the height of `300`

. So in our case a value that's 0 would be rendered at `y=300`

. Then based upon the value of which we are rendering at we need to render it down to the bottom of the svg which is at `300`

.

In the middle of our values it will be at `y=150`

. So we'll start our rendering in the middle, and render a bar that is `150px`

as well. Then for our value that is at the very top our `y`

is going to be `0`

so it will start it's rendering at the top, and thus our bar height will be `300px`

.

To render a bar we just use the `rect`

.

First off we use `width={xScale.bandwidth()}`

to get the bar width which is automatically calculated for us based upon the `range`

and `domain`

that we set. This will make sure that our bars fit in the space we've set.

Then we have our `height={barHeight}`

, it's calculated from the `y`

point minus the `height`

. As mentioned before the `y`

value is going to be the top of the bar, and then we're rendering down towards the bottom of the SVG.

The `x={xScale(xSelector(d))}`

will take our value, select the `x`

off of it and then pass it through to our `xScale`

which is our `scaleBand`

. This will take into account the number of bars we have, as well as the `padding`

we've set and give us our `x`

value to render.

The `y`

will just be our `yValue`

that we've calculated with our `scaleLinear`

.

```
return (
<div>
<svg width={width} height={height}>
{data.map((d, i) => {
const yValue = yScale(ySelector(d));
const barHeight = height - yValue;
return (
<rect
key={i}
width={xScale.bandwidth()}
height={barHeight}
x={xScale(xSelector(d))}
y={yValue}
stroke="#ff6347"
strokeWidth={3}
fill="#f5f5f5"
/>
);
})}
</svg>
</div>
);
```

Now if we wanted to render our data in an upside down fashion all we need to do is make one small tweak. Rather than render at our `yValue`

we just render at `0`

. The `barHeight`

will be correctly calculated from our scales, but we're just changing the point at which it starts rendering.

So to accomplish this pop in `y={0}`

.

```
<rect
key={i}
width={xScale.bandwidth()}
height={barHeight}
x={xScale(xSelector(d))}
y={0}
stroke="#ff6347"
strokeWidth={3}
fill="#f5f5f5"
/>
```

There we have it! We took some data and using 2 different scale methods were able to turn it into a bar graph that can be rendered into dynamic width/height containers.

Liked this content?

Get notified more about D3!

No Spam! We Promise!