X and Y axis are not together - d3.js

Here is an image of what my axis looks like
As you can see, they are widely separated and I don't know why. Here is the code I've used to create them:
margin = ({top: 40, right: 20, bottom: 40, left: 50});
height = 500 - margin.top - margin.bottom;
width = 600 - margin.left - margin.right;
// Crear el svg
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
// Cálculo de ejes
x = d3.scaleTime()
.domain([2015, 2021]).nice()
.range([margin.left, width - margin.right]);
y = d3.scaleLinear()
.domain([0,
13]).nice()
.range([height - margin.bottom, margin.top]);
svg.append("g")
.attr("transform", `translate( ${margin.left}, ${height -
margin.bottom})`)
.call(d3.axisBottom(x).tickFormat(d3.format("d")));
svg.append("g")
.attr("transform", `translate(${margin.left}, 0)`)
.call(d3.axisLeft(y))
My goal is to stick 0 and 2015 but I don't know how to do it

The reason of this shift is the horizontal translation added by you in below code line, here the first parameter specifying the horizontal translation of x-axis due to which both axes are not intersecting with each other.
.attr("transform", `translate( ${margin.left}, ${height - margin.bottom})`)
Just change it to zero and it will resolve the issue. I have added the snippet for this below
margin = ({
top: 40,
right: 20,
bottom: 40,
left: 50
});
height = 500 - margin.top - margin.bottom;
width = 600 - margin.left - margin.right;
// Crear el svg
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
// Cálculo de ejes
x = d3.scaleTime()
.domain([2015, 2021]).nice()
.range([margin.left, width - margin.right]);
y = d3.scaleLinear()
.domain([0,
13
]).nice()
.range([height - margin.bottom, margin.top]);
svg.append("g")
.attr("transform", `translate( 0, ${height -
margin.bottom})`)
.call(d3.axisBottom(x).tickFormat(d3.format("d")));
svg.append("g")
.attr("transform", `translate(${margin.left}, 0)`)
.call(d3.axisLeft(y))
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

Related

Align gridline with axis y in Bar Chart D3 v5

I have a problem with a bar chart. I insert the gridline, but the effect I get is not as desired as can be seen from the image.
image
The gridline is not align with y axis.
The code:
var svg = d3.select("#chart");
var margin = {top: 10, bottom: 10, left: 40, right: 0};
var width = +svg.attr("width") - margin.left - margin.right;
var height = +svg.attr("height") - margin.top - margin.bottom;
var x = d3.scaleBand()
.range([margin.left, width - margin.right])
.padding(0.1)
//.paddingOuter(0.2);
var y = d3.scaleLinear()
.rangeRound([height - margin.bottom, margin.top]);
var xAxis = function(g) {
g
.attr("transform", "translate(0," + (height - margin.bottom) + ")")
.call(d3.axisBottom(x).tickSizeOuter(0));
return g;
};
var yAxis = function(g) {
g
.attr("transform", "translate(" + margin.left + ", " + (margin.bottom - margin.top) + ")")
.call(d3.axisLeft(y));
return g;
};
function gridlines() {
return d3.axisLeft(y).ticks();
}
svg.append("g")
.attr("class", "grid")
.attr("transform", "translate(" + margin.left + "," + (margin.bottom - margin.top) + ")")
.call(gridlines()
.tickSize(-width)
.tickFormat("")
);
svg.append("g").attr("class", "x-axis");
svg.append("g").attr("class", "y-axis");
Thank.

How to show the end scale value in the axis?

Using d3.js v4, how to show the special scale value which can't be divide exactly?
For example: set the domain([0,24]), set the ticks(3). The value 24 don't show in the axis.
Please tell me how to show the value.
var margin = {top: 10, right: 30, bottom: 30, left: 30},
width = 600 - margin.left - margin.right,
height = 450 - margin.top - margin.bottom;
var x = d3.scaleBand()
.rangeRound([0, width])
.domain(['A', 'B', 'C'])
.padding(0);
var y = d3.scaleLinear()
.domain([0, 24])
.range([height, 0]);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y).ticks(3);
var svg = d3.select('#chart')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.style('background-color', '#ecf0f1')
.append('g')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var g = svg.append('g');
g.append('g')
.attr('class', 'axis axis--x')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
g.append('g')
.attr('class', 'axis axis--y')
.call(yAxis);
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg id="chart"></svg>
Use tickValues, defining how many ticks you want with scale.ticks() and pushing the first and last domain values by concatenation with scale.domain():
yAxis = d3.axisLeft(y)
.tickValues(y.ticks(3).concat(y.domain()));
Check the updated snippet:
var margin = {top: 10, right: 30, bottom: 30, left: 30},
width = 600 - margin.left - margin.right,
height = 450 - margin.top - margin.bottom;
var x = d3.scaleBand()
.rangeRound([0, width])
.domain(['A', 'B', 'C'])
.padding(0);
var y = d3.scaleLinear()
.domain([0, 24])
.range([height, 0]);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y)
.tickValues(y.ticks(3).concat(y.domain()));
var svg = d3.select('#chart')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.style('background-color', '#ecf0f1')
.append('g')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var g = svg.append('g');
g.append('g')
.attr('class', 'axis axis--x')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
g.append('g')
.attr('class', 'axis axis--y')
.call(yAxis);
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg id="chart"></svg>

D3 x axis value stacked on the left

I've tried to make a simple line chart.
The x axis will show 10,100,1000 etc' values
For some reason I get all the values stacked on the left side of the x axis instead of spreading them equally on the axis.
var data = [
{views:10, odds: 56},
{views:100, odds: 64},
{views:1000, odds: 81},
{views:10000, odds: 95},
{views:100000, odds: 99},
{views:1000000, odds: 99},
{views:10000000, odds: 100},
];
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
// Set the ranges
var x = d3.scale.linear()
.domain([
d3.min(data, function(d) { return d.views; }),
d3.max(data, function(d) { return d.views; })
])
.range([0, width]);
var y = d3.scale.linear()
.domain([
d3.min(data, function(d) { return d.odds; }),
d3.max(data, function(d) { return d.odds; })
])
.range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(7)
.tickValues(data.map((d)=>{ return d.views; }));
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(7);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(d.views); })
.y(function(d) { return y(d.odds); });
// Adds the svg canvas
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 + ")");
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
https://jsfiddle.net/guy_l/77agq0hz/
This is the expected behaviour. They are not "stacked" on the left side, it's just a math problem: each value of x is just 10% of the next value! Keep in mind that your domain goes from 10 to 10 million, so the points would never be equally spread: 90% of your domain is just the space between the 6th and the 7th point.
You can change the scale for an ordinal one or, if you want to keep it quantitative, you need a logarithmic scale here:
d3.scale.log();
Check your updated fiddle: https://jsfiddle.net/gerardofurtado/v17cpqdk/

trouble with scales and hebin in d3js

I am trying to use the hexbin layout with data that is normally distributed around 0 - all the examples use data centered around the center of the screen, so the scales are the same as the screen scales (except for y inversion)
I've tried to modify the scale functions to account for possible negative values. It works for the y-scale, but the x-scale gives NaNs, and the hexagons are plotted off the screen upper left. That is not the only problem - I would like to programmatically determine the bin size for the hexbin function - in my data series, all of the values are 'binned' into only one to three hexagons, and I need them spread out over the available domain.. here is my code
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/d3.hexbin.v0.min.js?5c6e4f0"></script>
<script>
minMultArray =function(arr,index){
var min = arr[0][index];
for (i=0;i<arr.length;i++) {
min = (min>arr[i][index]?arr[i][index]:min);
}
return min;
};
maxMultArray =function(arr,index){
var max = arr[0][index];
for (i=0;i<arr.length;i++) {
max = (max< arr[i][index]?arr[i][index]:max);
}
return max;
};
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var randomX = d3.random.normal(0, 5),
randomY = d3.random.normal(0, 6),
points = d3.range(2000).map(function() { return [randomX(), randomY()]; });
var minX = minMultArray(points,0);
var minY = minMultArray(points,1);
//var minZ = minMultArray(points,2);
var maxX = maxMultArray(points,0);
var maxY = maxMultArray(points,1);
//var maxZ = maxMultArray(points,2);
var color = d3.scale.linear()
.domain([0, 20])
.range(["white", "steelblue"])
.interpolate(d3.interpolateLab);
var hexbin = d3.hexbin()
.size([width, height])
.radius(20);
alert('minX='+minX +' maxX='+maxX);
var x = d3.scale.linear()
.domain([minX, maxX])
.range(0,width);
alert('xScale(3)='+x(3));
var y = d3.scale.linear()
.domain([minY, maxY])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickSize(6, -height);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickSize(6, -width);
console.log('hex = ' +hexbin(points));
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 + ")");
svg.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("class", "mesh")
.attr("width", width)
.attr("height", height);
svg.append("g")
.attr("clip-path", "url(#clip)")
.selectAll(".hexagon")
.data(hexbin(points))
.enter().append("path")
.attr("class", "hexagon")
.attr("d", hexbin.hexagon())
.attr("transform", function(d) { return "translate(" + (d.x) + "," + (d.y) + ")"; })
.style("fill", function(d) { return color(d.length); });
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
</script>
After more debugging the hexbin functions, they are not compatible with negative and/or fractional domains- so I solved this by mapping my original data by linear scales up to the height and width of the hexagon plots. Then bin size is controlled by radius. I also modified the hexbin binning function to handle three element arrays, and can compute stats on the third element, using color or size to show mean/median/stddev/max/min. If interested, I can post the code on github...

d3 bar chart axis alignment

I am trying to use the margin conventions described in http://bl.ocks.org/mbostock/3019563
when plotting bar charts. However, the bars do not align with the x-axis as you can see
in this basic example: http://bl.ocks.org/kyrre/bbd29f225173825797e3. What am I doing wrong?
var data = [
{x: "Differential Geometry", y: 10},
{x: "Statistical Physics", y: 5},
{x: "Music", y: 3}
]
var margin = {top: 20, right: 10, bottom: 20, left: 50};
var width = 500 - margin.left - margin.right;
var height = 320 - margin.top - margin.bottom;
var y = d3.scale.linear()
.domain([0, d3.max(data, function(x) {
return x.y;
})])
.range([0, height]);
var x = d3.scale.ordinal()
.domain(_.map(data, function(d) { return d.x;}))
.rangeRoundBands([0, width], 0.10);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
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 rect = svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) {
return x(d.x);
})
.attr("y", 0)
.attr("height", function(d) {
return y(d.y);
})
.attr("fill", function(d) { return "blue";})
.attr("width", 20);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0,0)")
.call(yAxis);
The y coordinates are counted from the top (i.e. 0 is at the top of the image). It should work if you set y to the total minus height.
.attr("y", function(d) { return (height - y(d.y)); })

Resources