I've been banging my head against a wall trying to figure this one out. My normally reliable Google-Fu has failed me. Nothing on I've found on SO has pointed me in the right direction. Would love some help on this.
I'm building a grouped bar chart. For some reason, y(number) will only give me NaN even though I am passing it a number.
Code below:
const data = [
{
'provider': 'twitter',
'likes': 2362,
'comments': 107,
'shares': 1129
},
{
'provider': 'facebook',
'likes': 2783,
'comments': 148,
'shares': 1069
},
{
'provider': 'instagram',
'likes': 1878,
'comments': 101,
'shares': 1032
},
{
'provider': 'tumblr',
'likes': 2965,
'comments': 147,
'shares': 1393
}
]
const margin = {top: 10, right: 10, bottom: 10, left: 10},
width = 628 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom
const x0 = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.1)
const x1 = d3.scaleBand()
.padding(0.1)
const y = d3.scaleLinear()
.rangeRound([height, 0])
const keys = d3.keys(data[0]).filter((key) => (key !== 'provider'))
console.log('keys ', keys) // keys ["likes","comments","shares"]
data.forEach((d) => {
d.values = keys.map((name) => ({name: name, value: +d[name]}))
})
x0.domain(data.map(d => d.provider))
x1.domain(keys).range([0, x0.bandwidth()])
y.domain(0, d3.max(data, (d) => (d3.max(d.values, (d) => (d.value)))))
console.log('max', d3.max(data, (d) => (d3.max(d.values, (d) => (d.value))))) // max 2965
const svg = d3.select('body').append('svg')
.attr('width', 628)
.attr('height', 300)
const provider = svg.selectAll('.provider')
.data(data)
.enter().append('g')
.attr('class', d => 'provider-' + d.provider)
.attr('transform', d => `translate(${x0(d.provider)}, 0)`)
provider.selectAll('rect')
.data(d => d.values)
.enter().append('rect')
.attr('width', x1.bandwidth())
.attr('x', d => x1(d.name))
.attr('y', d => {
console.log('d.value ', d.value) // d.value 2362
console.log('y(d.value) ', y(d.value)) // NaN
})
The domainmethod expects an array:
y.domain([0,
d3.max(data, (d) => (d3.max(d.values, (d) => (d.value))))
])
Related
I am a working on multiline chart using d3 version7 , I have data like
data1: sample1[] = [
{
symbol: 'a',
x1: '2022-01-01',
y1: 150
},
{
symbol: 'c',
x1: '2022-01-01',
y1: 300
},
{
symbol: 'a',
x1: '2022-01-02',
y1: 200
},
{
symbol: 'c',
x1: '2022-01-02',
y1: 700
},
{
symbol: 'a',
x1: '2022-01-03',
y1: 750
},
{
symbol: 'c',
x1: '2022-01-03',
y1: 100
},
];
In X-axis I want to display x1 as string and group with a symbol.
I am able to plot x-axis and y-axis from below code
const x = d3.scaleBand()
.domain(this.data1.map((d: any) => { return d.x1 }))
.range([0, this.width]);
this.svg.append("g")
.attr("transform", `translate(0, ${this.height})`)
.call(d3.axisBottom(x).ticks(5));
// Add Y axis
const y = d3.scaleLinear()
.domain([0, d3.max(this.data1, ((d: any) => {
return d.y1;[![enter image description here][1]][1]
}))])
.range([this.height, 0]);
this.svg.append("g")
.call(
d3.axisLeft(y)
);
And It looks like this screenshot of axis
Now When draw a multiple line using this code
const dataNest = Array.from(
d3.group(this.data1, d => d.symbol), ([key, value]) => ({ key, value })
);
var legendSpace = this.width / dataNest.length; // spacing for the legend
dataNest.forEach((d1: any, i: any) => {
const xScale = d3
.scaleBand()
.domain(d1.value.map((d: any) => { return d.x1 }))
// .domain([0, d3.max(this.data1.map((d: any) => d.x1))])
.range([0, this.width])
// y-scale
const yScale = d3
.scaleLinear()
.domain([0, d3.max(this.data1.map((d: any) => d.y1))])
.range([this.height, 0]);
// Data line
const line = d3
.line()
.x((d: any) => d.x1)
.y((d: any) => yScale(d.y1));
this.svg.append("path")
.data(dataNest)
.join("path")
.attr("fill", "none")
.attr("class", "line")
.style("stroke", this.colors[i])
.attr("d", line(d1.value));
// Add the Legend
this.svg.append("text")
.attr("x", (legendSpace / 2) + i * legendSpace) // space legend
.attr("y", this.height + (this.margin.bottom / 2) + 15)
.attr("class", "legend") // style the legend
.style("fill", this.colors[i])
.text(d1.key);
});
I am not able to plot any line and I am getting an error saying,
Error: attribute d: Expected number, "MNaN,160LNaN,146.…".
So anyone have solution for displaying the multiline chart.
In your line generator function, you don't use the xScale to convert values to pixels.
const line = d3
.line()
.x((d: any) => d.x1)
.y((d: any) => yScale(d.y1));
should be
const line = d3
.line()
.x((d: any) => xScale(d.x1))
.y((d: any) => yScale(d.y1));
A few additional notes:
In every loop of the forEach you define a new xScale and yScale that are identical to the existing x and y used for rendering the axes. Just use x and y.
The same goes for the line generator, which should be the same for all lines.
Why do you use a forEach on the dataNest array? This is what the d3 data join is for as worked out in the snippet below.
Use an appropriate scale for the data type. scaleBand is for categorical data and something like bar charts. You have dates and should use scaleTime. If you really don't want to use a time scale, stick to scalePoint for line charts as the bandwidth is fixed to zero.
const data1 = [
{symbol: 'a', x1: '2022-01-01', y1: 150},
{symbol: 'c', x1: '2022-01-01', y1: 300},
{symbol: 'a', x1: '2022-01-02', y1: 200},
{symbol: 'c', x1: '2022-01-02', y1: 700},
{symbol: 'a', x1: '2022-01-03', y1: 750},
{symbol: 'c', x1: '2022-01-03', y1: 100},
];
const width = 500,
height = 180;
const svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
const x = d3.scalePoint()
.domain(data1.map(d => d.x1))
.range([30, width-30]);
svg.append("g")
.attr("transform", `translate(0, ${height-30})`)
.call(d3.axisBottom(x).ticks(5));
const y = d3.scaleLinear()
.domain([0, d3.max(data1, d => d.y1)])
.range([height-30, 0]);
svg.append("g")
.attr("transform", `translate(30, 0)`)
.call(d3.axisLeft(y));
const dataNest = Array.from(
d3.group(data1, d => d.symbol), ([key, value]) => ({ key, value })
);
const line = d3.line()
.x(d => x(d.x1))
.y(d => y(d.y1));
svg.selectAll("path.line")
.data(dataNest)
.join("path")
.attr("class", "line")
.attr("fill", "none")
.style("stroke", d => d.key === 'a' ? 'blue' : 'red')
.attr("d", d => line(d.value));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.3.0/d3.min.js"></script>
I am new in d3.js.I made the multiline plot
Using this code file
import {
select,
csv,
scaleLinear,
scaleTime,
scaleOrdinal,
extent,
axisLeft,
scaleBand ,
axisBottom,
line,
curveBasis,
nest,
schemeCategory10,
timeFormat,
descending
} from 'd3';
import { colorLegend } from './colorLegend';
const svg = select('svg');
const width = +svg.attr('width');
const height = +svg.attr('height');
const render = data => {
const title='Profit Comparision by Segment by Region'
// Region,Sub_Category,Profit
const xValue = d => d.Sub_Category;
const xAxisLabel="Sub-Category"
const yValue = d => d.Profit;
const circleRadius = 6;
const yAxisLabel="Profit"
var barPadding = 0.2;
const colorValue = d => d.Region;
const margin = { top: 60, right: 160, bottom: 128, left: 105 };
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
/*const xScale = scaleLinear()
.domain(extent(data, xValue))
.range([0, innerWidth])
.nice();*/
var xScale = d3.scalePoint().domain(data.map(xValue))
.range([0, innerWidth]);
/* var xScale = scaleOrdinal().domain(extent(data, xValue))
.range([0, innerWidth]);*/
const yScale = scaleLinear()
.domain(extent(data, yValue))
.range([innerHeight, 0])
.nice();
const colorScale = scaleOrdinal(schemeCategory10);
const g = svg.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
const xAxis = axisBottom(xScale)
.tickSize(-innerHeight)
.tickPadding(15);
const yAxis = axisLeft(yScale)
.tickSize(-innerWidth)
.tickPadding(10);
const yAxisG = g.append('g').call(yAxis);
yAxisG.selectAll('.domain').remove();
yAxisG.append('text')
.attr('class', 'axis-label')
.attr('y', -60)
.attr('x', -innerHeight / 2)
.attr('fill', 'black')
.attr('transform', `rotate(-90)`)
.attr('text-anchor', 'middle')
.text(yAxisLabel);
const xAxisG = g.append('g').call(xAxis)
.attr('transform', `translate(0,${innerHeight})`);
xAxisG
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d) {
return "rotate(-65)"
});
xAxisG.select('.domain').remove();
xAxisG.append('text')
.attr('class', 'axis-label')
.attr('y', 110)
.attr('x', innerWidth / 2)
.attr('fill', 'black')
.text(xAxisLabel);
const lineGenerator = line()
.x(d => xScale(xValue(d)))
.y(d => yScale(yValue(d)))
.curve(curveBasis);
const lastYValue = d =>
yValue(d.values[d.values.length - 1]);
const nested = nest()
.key(colorValue)
.entries(data)
.sort((a, b) =>
descending(lastYValue(a), lastYValue(b))
);
console.log(nested);
colorScale.domain(nested.map(d => d.key));
g.selectAll('.line-path').data(nested)
.enter().append('path')
.attr('class', 'line-path')
.attr('d', d => lineGenerator(d.values))
.attr('stroke', d => colorScale(d.key));
g.append('text')
.attr('class', 'title')
.attr('y', -10)
.text(title);
svg.append('g')
.attr('transform', `translate(820,121)`)
.call(colorLegend, {
colorScale,
circleRadius: 10,
spacing: 38,
textOffset: 20
});
};
csv('data4.csv')
.then(data => {
data.forEach(d => {
// Region,Sub_Category,Profit
d.Profit = +d.Profit;
});
render(data);
});
This multiline plot does not match with the plot actual result on the same data. Here this picture in the actual result on the same data
How I can make this my multiline plot just like the result. What changes in the code is needed. Here is the link to vizhub code
Update:
I filtered and sorted them in alphabetical order and profit sorted in a descending order. But I got a different graph not the required result
How I can get the required result?
Begin, the x-axis labels are in the middle of ticks. However, the x-axis labels show some problem after I rotate the x-axis labels.
Now, I want the x-axis labels to be in the middle of ticks. How can I do?
If transform is used, how to get the middle point of x-axis?
The result looks like https://drive.google.com/open?id=1Fen0to5Ih86alOXu6UXeJzeicX1E1JFJ
const data = [
{
'group': 'G1',
'sample': 's1',
'Actinomyces': 12.55802794990189,
'Alloprevotella': 0.3671446023182472,
'Atopobium': 0.15760660109181326,
'Anaerococcus': 0
},
{
'group': 'G1',
'sample': 's2',
'Actinomyces': 9.55802794990189,
'Alloprevotella': 0.3671446023182472,
'Atopobium': 0.12760660109181326,
'Anaerococcus': 10.0
},
{
'group': 'G2',
'sample': 's3',
'Actinomyces': 11.55802794990189,
'Alloprevotella': 0.3671446023182472,
'Atopobium': 0.9760660109181326,
'Anaerococcus': 5.0
},
{
'group': 'G2',
'sample': 's4',
'Actinomyces': 19.55802794990189,
'Alloprevotella': 1.3671446023182472,
'Atopobium': 2.15760660109181326,
'Anaerococcus': 4.0
}
]
const w = 800
const h = 400
const margin = { top: 50, right: 50, bottom: 50, left: 150 }
const keys = Object.keys(data[0]).filter(function (val) {
return val !== 'sample' && val !== 'group'
})
// create a stack generator
let stack = d3.stack()
.keys(keys)
const xScale = d3.scaleBand()
.domain(d3.range(data.length))
.range([margin.left, w - margin.right])
.paddingOuter(0.02)
const yScale = d3.scaleLinear()
.domain([0,
d3.max(data, function (d) {
return d3.sum(keys.map(val => d[val]))
})
])
.range([h - margin.bottom, margin.top])
const colorScale = d3.scaleLinear()
.domain([0, keys.length - 1])
.range([0, 1])
// create svg
const svg = d3.select('#app')
.append('svg')
.attr('width', w)
.attr('height', h)
const groups = svg.selectAll('g')
.data(stack(data))
.enter()
.append('g')
.style('fill', function (d, i) {
return d3.interpolateSpectral(colorScale(i))
})
groups.selectAll('rect')
.data(function (d) {
return d
})
.enter()
.append('rect')
.attr('x', (d, i) => xScale(i))
.attr('y', d => yScale(d[1]))
.attr('height', d => yScale(d[0]) - yScale(d[1]))
.attr('width', xScale.bandwidth())
// add axis
const xAxis = d3.axisBottom(xScale)
.tickFormat(d => keys[d])
svg.append('g')
.attr('class', 'xAxis')
.attr('transform', 'translate(0, ' + yScale(0) + ')')
.call(xAxis)
.selectAll('text')
.attr('text-anchor', 'start')
.attr('dx', '10px')
.attr('transform', 'rotate(90)')
const yAxis = d3.axisLeft(yScale)
svg.append('g')
.attr('class', 'yAxis')
.attr('transform', 'translate(' + xScale(0) + ', 0)')
.call(yAxis)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="app">
</div>
My English is poor, so I build a transform process picture and it can be find here https://drive.google.com/open?id=19zmGFwivdjPqVabVGIgdNVXKRGdR8v2s
The part of code has been changed locate between
/** change **/
...
/***********/
const data = [
{
'group': 'G1',
'sample': 's1',
'Actinomyces': 12.55802794990189,
'Alloprevotella': 0.3671446023182472,
'Atopobium': 0.15760660109181326,
'Anaerococcus': 0
},
{
'group': 'G1',
'sample': 's2',
'Actinomyces': 9.55802794990189,
'Alloprevotella': 0.3671446023182472,
'Atopobium': 0.12760660109181326,
'Anaerococcus': 10.0
},
{
'group': 'G2',
'sample': 's3',
'Actinomyces': 11.55802794990189,
'Alloprevotella': 0.3671446023182472,
'Atopobium': 0.9760660109181326,
'Anaerococcus': 5.0
},
{
'group': 'G2',
'sample': 's4',
'Actinomyces': 19.55802794990189,
'Alloprevotella': 1.3671446023182472,
'Atopobium': 2.15760660109181326,
'Anaerococcus': 4.0
}
]
const w = 800
const h = 400
const margin = { top: 50, right: 50, bottom: 50, left: 150 }
const keys = Object.keys(data[0]).filter(function (val) {
return val !== 'sample' && val !== 'group'
})
// create a stack generator
let stack = d3.stack()
.keys(keys)
const xScale = d3.scaleBand()
.domain(d3.range(data.length))
.range([margin.left, w - margin.right])
.paddingOuter(0.02)
const yScale = d3.scaleLinear()
.domain([0,
d3.max(data, function (d) {
return d3.sum(keys.map(val => d[val]))
})
])
.range([h - margin.bottom, margin.top])
const colorScale = d3.scaleLinear()
.domain([0, keys.length - 1])
.range([0, 1])
// create svg
const svg = d3.select('#app')
.append('svg')
.attr('width', w)
.attr('height', h)
const groups = svg.selectAll('g')
.data(stack(data))
.enter()
.append('g')
.style('fill', function (d, i) {
return d3.interpolateSpectral(colorScale(i))
})
groups.selectAll('rect')
.data(function (d) {
return d
})
.enter()
.append('rect')
.attr('x', (d, i) => xScale(i))
.attr('y', d => yScale(d[1]))
.attr('height', d => yScale(d[0]) - yScale(d[1]))
.attr('width', xScale.bandwidth())
// add axis
const xAxis = d3.axisBottom(xScale)
.tickFormat(d => keys[d])
svg.append('g')
.attr('class', 'xAxis')
.attr('transform', 'translate(0, ' + yScale(0) + ')')
.call(xAxis)
/** change **/
.selectAll('text')
.attr('text-anchor', 'start')
.attr('y', 0)
.attr('transform', 'rotate(90)')
.attr('dx', 9) // let the text move a little to the bottom
.attr('dy', 2) // at the beginning, the top of text is parallel with the tick. So we need move a little to the right
/***********/
const yAxis = d3.axisLeft(yScale)
svg.append('g')
.attr('class', 'yAxis')
.attr('transform', 'translate(' + xScale(0) + ', 0)')
.call(yAxis)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="app">
</div>
I have a codepen here - https://codepen.io/anon/pen/GyeEpJ?editors=0010#0
Its a stacked bar chart with a line chart on top
The line chart points appear at the left side of the bar below.
How can I position the points in the line so they appear above the ticks in the x axis.
let dataline = d3.line()
.x((d) => {
return x(d.date);
})
.y((d) =>{
return y(d.total);
});
let layersLineArea = chart.append('g')
.attr('class', 'layers-lines');
let layersLine = layersLineArea.append('path')
.data([totalData])
.attr("class", "line")
.attr('d', dataline);
You're using a band scale, which is not suitable for a line chart.
The simplest solution is adding half the bandwidth in the line generator:
let dataline = d3.line()
.x((d) => {
return x(d.date) + x.bandwidth()/2;
})
.y((d) =>{
return y(d.total);
});
Here is your code with that change:
let keys = [];
let maxVal = [];
let dataToStack = [];
let totalData = [];
let legendKeys = ['usedInf', 'newInf'];
let w = 800;
let h = 450;
let margin = {
top: 60,
bottom: 40,
left: 50,
right: 20,
};
let width = w - margin.left - margin.right;
let height = h - margin.top - margin.bottom;
let colors = ['#FFC400', '#FF4436', '#FFEBB6', '#FFC400', '#B4EDA0'];
let data = [{
"one": 10,
"two": 12,
"three": 18,
"four": 22,
"five": 30,
"six": 44,
"seven": 125,
"date": "2015-05-31T00:00:00"
}, {
"one": 30,
"two": 42,
"three": 38,
"four": 62,
"five": 90,
"six": 144,
"seven": 295,
"date": "2015-06-30T00:00:00"
}, {
"one": 30,
"two": 92,
"three": 18,
"four": 100,
"five": 120,
"six": 10,
"seven": 310,
"date": "2015-07-31T00:00:00"
}, ];
for (let i = 0; i < data.length; i++) {
dataToStack.push({
date: data[i]['date'].toString(),
usedInf: data[i]['one'] + data[i]['two'] + data[i]['three'],
newInf: data[i]['four'] + data[i]['five'] + data[i]['six']
});
totalData.push({
date: data[i]['date'].toString(),
total: data[i]['seven']
});
}
//------------------------- Stack ------------------------//
let stack = d3.stack()
.keys(legendKeys);
let stackedSeries = stack(dataToStack);
//------------------------- Stack ------------------------//
let x = d3.scaleBand()
.domain(dataToStack.map(function(d) {
//let date = new Date(d.date);
return d.date;
}))
.rangeRound([0, width])
.padding(0.05);
let y = d3.scaleLinear()
.domain([0, d3.max(stackedSeries, function(d) {
return d3.max(d, (d) => {
return d[1];
})
})])
.range([height, 0]);
let svg = d3.select('.chart').append('svg')
.attr('class', 'chart')
.attr('width', w)
.attr('height', h);
let chart = svg.append('g')
.classed('graph', true)
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
//------------------------- Bar Chart ------------------------//
let layersBarArea = chart.append('g')
.attr('class', 'layers-bars');
let layersBar = layersBarArea.selectAll('.layer-bar').data(stackedSeries)
.enter()
.append('g')
.attr('class', 'layer-bar')
.style('fill', (d, i) => {
return colors[i];
});
layersBar.selectAll('rect')
.data((d) => {
return d
})
.enter()
.append('rect')
.attr('height', (d, i) => {
return y(d[0]) - y(d[1]);
})
.attr('y', (d) => {
return y(d[1]);
})
.attr('x', (d, i) => {
return x(d.data.date)
})
.attr('width', x.bandwidth());
//------------------------- Bar Chart ------------------------//
//------------------------- Line Chart ------------------------//
let dataline = d3.line()
.x((d) => {
return x(d.date) + x.bandwidth() / 2;
})
.y((d) => {
return y(d.total);
});
let layersLineArea = chart.append('g')
.attr('class', 'layers-lines');
let layersLine = layersLineArea.append('path')
.data([totalData])
.attr("class", "line")
.attr('d', dataline);
//------------------------- Line Chart ------------------------//
chart.append('g')
.classed('x axis', true)
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
chart.append('g')
.classed('y axis', true)
.call(d3.axisLeft(y)
.ticks(10));
.line {
fill: none;
stroke: #00D7D2;
stroke-width: 5px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div class="chart"></div>
I have a plunker here https://plnkr.co/edit/hBWoIIyzcHELGyewOyZE?p=preview
I'm trying to create a simple stacked bar chart.
The bars go above the top of the chart which I think is a problem with the domain
I also need a scale on the y axis which I think is to do with the y domain.
Is it the y domain that controls the height of the bars and scales shown on the y axis
y.domain([0, d3.max(data, (d)=>{
return d
})])
This is a list of the issues so far:
First, your y domain is not correctly set. It should use the stacked data:
y.domain([0, d3.max(stackedSeries, function(d) {
return d3.max(d, function(d) {
return d[0] + d[1];
});
})])
Second, the math for the y and height of the rectangles is wrong. It should be:
.attr('height', (d) => {
return y(d[0]) - y(d[0] + d[1]);
})
.attr('y', (d) => {
return y(d[0] + d[1]);
})
Finally, use the x scale for the x position:
.attr('x', (d, i) => {
return x(d.data.day)
})
Here is the code with those changes:
var margin = {
top: 40,
right: 20,
bottom: 40,
left: 40
}
var width = 400 - margin.left - margin.right
var height = 500 - margin.top - margin.bottom
var data = [{
day: 'Mon',
apricots: 120,
blueberries: 180,
cherries: 100
},
{
day: 'Tue',
apricots: 60,
blueberries: 185,
cherries: 105
},
{
day: 'Wed',
apricots: 100,
blueberries: 215,
cherries: 110
},
{
day: 'Thu',
apricots: 150,
blueberries: 330,
cherries: 105
},
{
day: 'Fri',
apricots: 120,
blueberries: 240,
cherries: 105
}
];
var svg = d3.select('body')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
var colors = ['#FBB65B', '#513551', '#de3163'];
var stack = d3.stack()
.keys(['apricots', 'blueberries', 'cherries']);
var stackedSeries = stack(data);
// Create a g element for each series
var g = d3.select('g')
.selectAll('g.series')
.data(stackedSeries)
.enter()
.append('g')
.classed('series', true)
.style('fill', (d, i) => {
return colors[i];
});
var x = d3.scaleBand()
.rangeRound([0, width])
.padding(0.1)
var y = d3.scaleLinear()
.range([height, 0])
x.domain(data.map((d) => {
return d.day
}))
y.domain([0, d3.max(stackedSeries, function(d) {
return d3.max(d, function(d) {
return d[0] + d[1];
});
})])
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0, ' + height + ')')
.call(d3.axisBottom(x))
svg.append('g')
.attr('class', 'y axis')
.call(d3.axisLeft(y))
// For each series create a rect element for each day
g.selectAll('rect')
.data((d) => {
return d;
})
.enter()
.append('rect')
.attr('height', (d) => {
return y(d[0]) - y(d[0] + d[1]);
})
.attr('y', (d) => {
return y(d[0] + d[1]);
})
.attr('x', (d, i) => {
return x(d.data.day)
})
.attr('width', x.bandwidth())
.style("stroke", "#ccc");
<script src="https://d3js.org/d3.v4.min.js"></script>