How to increase the axis? - javascript

I make vertical histogram. The center of the axes is located in the left bottom corner.
JSFIDDLE
But last bar went beyond the x-axis limit (point [700, 400]). I need increase x-axis. Please help me.
My svg element:
const svg = d3.select('#svg');
My axis:
xScale = d3.scaleLinear()
.domain([
d3.min(points, d => d[0]),
d3.max(points, d => d[0])
])
.range([paddings.left, width - paddings.right]);
yScale = d3.scaleLinear()
.domain([
d3.min(points, d => d[1]),
d3.max(points, d => d[1])
])
.range([height - paddings.bottom, 0 + paddings.top]);
const xAxis = d3.axisBottom(xScale);
const yAxis = d3.axisLeft(yScale);
svg.append('g')
.attr('class', 'x-axis-group')
.attr('transform', 'translate(0,' + (height - paddings.bottom) + ')')
.call(xAxis);
svg.append('g')
.attr('class', 'y-axis-group')
.attr('transform', 'translate(' + paddings.left + ',0)')
.call(yAxis);
My bars:
svg.selectAll(null)
.data(points)
.enter('')
.append('rect')
.attr('x', d => xScale(d[0]))
.attr('y', d => yScale(d[1]))
.attr('width', width / points.length)
.attr('height', d => height - yScale(d[1]) - paddings.bottom)

If the bars on your chart are 100 units wide, the maximum x value should be d3.max(points => d[0]) + 100. If you plug that into your scale, you will find your chart now covers the correct range.
xScale = d3.scaleLinear()
.domain([
d3.min(points, d => d[0]),
d3.max(points, d => d[0]) + 100
])
.range([paddings.left, width - paddings.right]);
You will now find that you have made a mistake in calculating the width of the bars:
svg.selectAll(null)
.data(points)
.enter('')
.append('rect')
.attr('x', d => xScale(d[0]))
.attr('y', d => yScale(d[1]))
.attr('width', width / points.length)
.attr('height', d => height - yScale(d[1]) - paddings.bottom)
Can you work out why they are too wide?

Related

Why is it showing only one rect for the last value of each year in my d3js bar chart?

Trying to build a bar chart and, I don't know why, it's only showing the last value for each year and not all the values in both arrays, and that's what I thought that it was supposed to happen. How can I fix that?
let url = "https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/GDP-data.json";
const padding = 50;
const height = 460;
const width = 940;
var svg = d3.select('body')
.append('svg')
.attr('width', width)
.attr('height', height);
var arr = [];
var years = [];
d3.json(url, function(data) {
for (let i = 0; i < data.data.length; i++) {
arr[i] = data.data[i];
years[i] = parseInt(data.data[i][0].slice(0,4));
}
const yScale = d3.scaleLinear()
.domain([0, d3.max(arr, (d) => d[1])])
.range([height - padding, padding]);
const xScale = d3.scaleLinear()
.domain([d3.min(years, d => d), d3.max(years, (d) => d)])
.range([padding, width - padding]);
let bandScale = d3.scaleBand().domain(years, d => d).range([padding, width - padding]);
const xAxis = d3.axisBottom(bandScale)
const yAxis = d3.axisLeft(yScale);
svg.append("g")
.attr("transform", "translate(0," + (height - padding) + ")")
.call(xAxis);
svg.append('g')
.attr('transform', 'translate(' + padding + ', 0)')
.call(yAxis)
svg.selectAll('rect')
.data(arr)
.enter()
.append('rect')
.attr('fill', 'blue')
.attr('height', d => height - padding - yScale(d[1]))
.attr('width', d => bandScale.bandwidth())
.attr('x', (d, i) => (bandScale(years[i])))
.attr('y', d => yScale(d[1]))
.append('title')
.text((d, i) => years[i] + ': ' + d[1])
});
<script src="https://d3js.org/d3.v4.min.js"></script>

How can I make a bar chart starting from the 0 point of the y axis and not from the bottom of the svg?

I'm trying to make a bar chart but I can't figure out a way to make the bar start from the 0 point of y axis and not from the very bottom of the svg. How can I fix that?
let url = "https://raw.githubusercontent.com/freeCodeCamp/ProjectReferenceData/master/GDP-data.json";
const padding = 50;
const height = 460;
const width = 900;
var svg = d3.select('body')
.append('svg')
.attr('width', width)
.attr('height', height);
var arr = [];
d3.json(url, function(data) {
for (let i = 0; i < data.data.length; i++) arr[i] = data.data[i];
const yScale = d3.scaleLinear()
.domain([0, d3.max(arr, (d) => d[1])])
.range([height - padding, padding]);
const yAxis = d3.axisLeft(yScale);
svg.append('g')
.attr('transform', 'translate(' + padding + ', 0)')
.call(yAxis)
svg.selectAll('rect')
.data(arr)
.enter()
.append('rect')
.attr('fill', 'blue')
.attr('height', d => d[1] + padding)
.attr('width', 2.909090909090909)
.attr('x', (d, i) => padding + (3.2 * i))
.attr('y', d => yScale(d[1]))
.append('title')
.text(d => d[1])
});
<script src="https://d3js.org/d3.v4.min.js"></script>
You are incorrectly calculating the height of the rectangle, and not using your scale. It's also trickier since your use of padding is not the typical D3 convention.
svg.selectAll('rect')
.data(arr)
.enter()
.append('rect')
.attr('fill', 'blue')
.attr('height', d => height - padding - yScale(d[1]))

Group line plotting in D3 with d3.group

I am working with a tidy-long data structure with three columns: date, ID, num_orders.
date ID num_orders
"2018-08-22" 1 3
"2018-08-23" 7 1
"2018-08-23" 10 1
"2018-08-23" 17 1
"2018-08-23" 19 1
.
.
.
I would like to plot a line for each ID with date and num_orders as the x- and y-axis respectively, using D3.js. I am using this as a model for what I am doing, but in that code, the author is using the nest() function, which is no longer used in v6 of D3; the method used now is group(). So my code now is:
const margin = { top: 10, right: 30, bottom: 30, left: 60 };
const width = 1000 - margin.left - margin.right;
const height = 600 - margin.top - margin.bottom;
const svg = d3.select('#my_dataviz')
.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})`);
d3.json("./data.json")
.then( function(data) {
const grouped_data = d3.group(data, d => d.ID);
parseDate = d3.timeParse('%Y-%m-%d')
const xScale = d3.scaleTime()
.domain(d3.extent(data, d => parseDate(d.date)))
.range([0, width]);
svg.append('g')
.attr('transform', `translate(0, ${height})`)
.call(d3.axisBottom(xScale));
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.num_orders)])
.range([height, 0]);
svg.append('g')
.call(d3.axisLeft(yScale));
const myColor = d3.scaleOrdinal()
.domain(grouped_data.keys())
.range(d3.schemeSet3);
svg.selectAll('.line')
.data(grouped_data)
.enter()
.append('path')
.attr('fill', 'none')
.attr('stroke', d => myColor(d.keys))
.attr('stroke-width', 1.5)
.attr('d', function(d){
return d3.line()
.x(d => xScale(d.date))
.y(d => yScale(d.num_orders))
(d.values);
});
} )
So far I can get the axis with tick-marks to show up, but none of the lines are showing up, which makes me think the problem is in the final svg.selectAll('.line') statement. I'm pretty new to D3, so any guidance on this problem is appreciated. (And if you have any overall suggestions for my code, I also welcome this.)
d3.line() is accepting arrays only, while d.values() is an iterator.
By converting it into an array the problem is solved.
Notice that, on the snippet I removed the parseDate because I am generating data as Dates.
You most likely will need to keep the parseDate
const margin = { top: 10, right: 30, bottom: 30, left: 60 };
const width = 1000 - margin.left - margin.right;
const height = 600 - margin.top - margin.bottom;
var x = d3.timeDays(new Date(2020, 06, 01), new Date(2020, 10, 30));
var y = Array.from({length: x.length}, Math.random).map(n => Math.floor(n * 10) + 5);
var data = x.map((v, i) => {
return {
"date": v,
"id": Math.floor(Math.random() * (10 + 1)),
"num_orders": y[i]
};
});
const svg = d3.select('#my_dataviz')
.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})`);
const grouped_data = d3.group(data, d => d.id);
parseDate = d3.timeParse('%Y-%m-%d');
const xScale = d3.scaleTime()
.domain(d3.extent(data, d => d.date))
.range([0, width]);
svg.append('g')
.attr('transform', `translate(0, ${height})`)
.call(d3.axisBottom(xScale));
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.num_orders)])
.range([height, 0]);
svg.append('g')
.call(d3.axisLeft(yScale));
const myColor = d3.scaleOrdinal()
.domain(grouped_data.keys())
.range(d3.schemeSet3);
const line = d3.line()
.x(d => { return xScale(d.date); })
.y(d => yScale(d.num_orders));
svg.selectAll('.line')
.data(grouped_data)
.enter()
.append('path')
.attr('fill', 'none')
.attr('stroke', d => myColor(d[0]))
.attr('stroke-width', 1.5)
.attr('d', (d) => line(Array.from(d.values())[1]));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.5.0/d3.min.js"></script>
<div id="my_dataviz"></div>
Try to parse the data on the line generator
const margin = { top: 10, right: 30, bottom: 30, left: 60 };
const width = 1000 - margin.left - margin.right;
const height = 600 - margin.top - margin.bottom;
const svg = d3.select('#my_dataviz')
.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})`);
d3.json("./data.json")
.then( function(data) {
const grouped_data = d3.group(data, d => d.ID);
parseDate = d3.timeParse('%Y-%m-%d')
const xScale = d3.scaleTime()
.domain(d3.extent(data, d => parseDate(d.date)))
.range([0, width]);
svg.append('g')
.attr('transform', `translate(0, ${height})`)
.call(d3.axisBottom(xScale));
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.num_orders)])
.range([height, 0]);
svg.append('g')
.call(d3.axisLeft(yScale));
const myColor = d3.scaleOrdinal()
.domain(grouped_data.keys())
.range(d3.schemeSet3);
svg.selectAll('.line')
.data(grouped_data)
.enter()
.append('path')
.attr('fill', 'none')
.attr('stroke', d => myColor(d.keys))
.attr('stroke-width', 1.5)
.attr('d', function(d){
return d3.line()
.x(d => xScale(parseData(d.date)))
.y(d => yScale(d.num_orders))
(d.values);
});
} )

D3 Graph - changing y-axis to integer

I'm trying to create integer bands in the y axes.
Have tried changing .scaleband to .scalelinear and .ticks "arbitraryMetric" is stored as an integer.
Code:
const categories = vizData.map(d => d.arbitraryMetric);
const yScale = d3
.scaleBand()
.domain(categories)
.range([0, vizHeight - timelineMargin.bottom]);
const labels = vizCanvas
.selectAll('text')
.data(categories)
.enter()
.append('text')
.style('font-family', style.fontFamily)
.style('fill', '#3C4043')
.attr('x', timelineMargin.left - 10)
.attr('y', d => yScale(d) + yScale.bandwidth() / 2)
.attr('text-anchor', 'end')
.text(d => d);
This following solution will do the trick - credit to this answer
const yAxisTicks = yScale.ticks()
.filter(tick => Number.isInteger(tick));
const yAxis = d3.axisLeft(yScale)
.tickValues(yAxisTicks)
.tickFormat(d3.format('d'));

Calculate height of bar in pixels from a Y value in D3 graph

I have a quick question. I am trying to determine the height in pixels of a chart bar. This is for a D3 implementation, and my chart has a logarithmic y-axis.
I know the Y value for the bar I am trying to plot.
I also know the height of the axis in pixels (600px).
I know the min and the max of the Y-axis
I have tried various computations but cannot seem to calculate the height of the bar so that it connects the Y value with the x-Axis.
The picture below should provide a visual illustration of what I am trying to do. Right now I can't seem to get it right ... I think this is essentially a problem in maths, not so much D3. Thank you!
*** EDIT ****
This is the y axis scale that I am using:
var y = d3.scale.log()
.range([height, 0])
.domain([d3.min(sampleData, function(d) {
return d.y;
}),
d3.max(sampleData, function(d) {
return d.y;
})
]);
I'm still not sure about your problem, because the actual height of the bar is being calculated by the very scale you use to append the rectangles. And, if you're in fact appending the rectangles, you're already setting the height attribute!
Let's see an example. This is a bar chart using your log scale (I'm using D3 v4 here, but the principle is the same) and this fake data:
var data = [2000, 5000, 3000, 8000, 1500];
As you can see, there is a minimum and a maximum. I put a padding of 20px in the scale:
var yScale = d3.scaleLog()
.range([height - padding, padding])
.domain([d3.min(data), d3.max(data)]);
So, our first value in the range is h - padding and our last value is just padding. Here is the chart:
var width = 300,
height = 400,
padding = 20;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var data = [2000, 5000, 3000, 8000, 1500];
var xScale = d3.scaleBand()
.range([50, width])
.domain(d3.range(data.length))
.padding(0.2);
var yScale = d3.scaleLog()
.range([height - padding, padding])
.domain([d3.min(data), d3.max(data)]);
var bars = svg.selectAll("foo")
.data(data)
.enter()
.append("rect")
.attr("x", (d, i) => xScale(i))
.attr("width", xScale.bandwidth())
.attr("height", d => height - padding - yScale(d))
.attr("y", d => yScale(d))
.attr("fill", "teal");
var gX = svg.append("g")
.attr("transform", "translate(0," + (height - padding) + ")")
.call(d3.axisBottom(xScale));
var gY = svg.append("g")
.attr("transform", "translate(50,0)")
.call(d3.axisLeft(yScale));
<script src="//d3js.org/d3.v4.min.js"></script>
Suppose you want to calculate the height of the first bar. We can see, by inspecting the DOM, that its height is 61.867984771728516 pixels:
var width = 300,
height = 400,
padding = 20;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var data = [2000, 5000, 3000, 8000, 1500];
var xScale = d3.scaleBand()
.range([50, width])
.domain(d3.range(data.length))
.padding(0.2);
var yScale = d3.scaleLog()
.range([height - padding, padding])
.domain([d3.min(data), d3.max(data)]);
var bars = svg.selectAll("foo")
.data(data)
.enter()
.append("rect")
.attr("x", (d, i) => xScale(i))
.attr("width", xScale.bandwidth())
.attr("height", d => height - padding - yScale(d))
.attr("y", d => yScale(d))
.attr("fill", "teal");
var gX = svg.append("g")
.attr("transform", "translate(0," + (height - padding) + ")")
.call(d3.axisBottom(xScale));
var gY = svg.append("g")
.attr("transform", "translate(50,0)")
.call(d3.axisLeft(yScale));
console.log(d3.select("rect").node().height.animVal.value)
<script src="//d3js.org/d3.v4.min.js"></script>
But this is simply the first value in the range (height - padding) minus yScale(2000):
var width = 300,
height = 400,
padding = 20;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var data = [2000, 5000, 3000, 8000, 1500];
var xScale = d3.scaleBand()
.range([50, width])
.domain(d3.range(data.length))
.padding(0.2);
var yScale = d3.scaleLog()
.range([height - padding, padding])
.domain([d3.min(data), d3.max(data)]);
var bars = svg.selectAll("foo")
.data(data)
.enter()
.append("rect")
.attr("x", (d, i) => xScale(i))
.attr("width", xScale.bandwidth())
.attr("height", d => height - padding - yScale(d))
.attr("y", d => yScale(d))
.attr("fill", "teal");
var gX = svg.append("g")
.attr("transform", "translate(0," + (height - padding) + ")")
.call(d3.axisBottom(xScale));
var gY = svg.append("g")
.attr("transform", "translate(50,0)")
.call(d3.axisLeft(yScale));
console.log(height - padding - yScale(2000))
<script src="//d3js.org/d3.v4.min.js"></script>
Which, by the way, is the value used to set the height of the bars:
.attr("height", d => height - padding - yScale(d))

Categories

Resources