Reputation: 1821
I have a graph drawn with d3 with the x and y scales defined as follows:
x = d3.scaleLinear().domain([30, 150]).range([0, width])
y = d3.scaleLinear().domain([0, 100]).range([height, 0])
I need to draw a vertical line after 25% of data points. So I have my code like this:
svg.append('line')
.attr('x1', lowerLimit)
.attr('y1', 0)
.attr('x2', lowerLimit)
.attr('y2', height)
.style('stroke', 'red')
My problem is I am not sure how to set the lowerLimit
x value to be 25% of all the x-values in the scale. Can someone help please? Thanks in advance!
Upvotes: 0
Views: 866
Reputation: 102194
Well, your question is not clear. When you say:
I need to draw a vertical line after 25% of data points
You're talking about the first quartile, which is impossible to calculate without the data, and not only that, but which changes for every different data set.
If you are indeed talking about the first quartile, this is what you have to do:
Given an data array called data
, you can use d3.quantile:
var firstQuartile = d3.quantile(data, 0.25);
In the following demo, I'm plotting 100 dots, and calculating the 25 percentile (first quartile) regarding the property x
:
var lowerLimit = d3.quantile(data, 0.25, function(d) {
return d.x
});
The console shows the value. Check the demo:
var data = d3.range(100).map(() => ({
x: Math.random() * 120 + 30,
y: Math.random() * 100
}));
var w = 500,
h = 160;
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
x = d3.scaleLinear().domain([30, 150]).range([20, w - 20]);
y = d3.scaleLinear().domain([0, 100]).range([h - 20, 20]);
var xAxis = d3.axisBottom(x);
var yAxis = d3.axisLeft(y);
var circles = svg.selectAll("circles")
.data(data)
.enter()
.append("circle")
.attr("r", 2)
.attr("fill", "teal")
.attr("cx", d => x(d.x))
.attr("cy", d => y(d.y));
var data = data.sort((a, b) => d3.ascending(a.x, b.x))
var lowerLimit = d3.quantile(data, 0.25, function(d) {
return d.x
});
console.log(lowerLimit);
svg.append('line')
.attr('x1', x(lowerLimit))
.attr('y1', 20)
.attr('x2', x(lowerLimit))
.attr('y2', h - 20)
.style('stroke', 'red')
svg.append("g")
.attr("transform", "translate(0," + (h - 20) + ")")
.call(xAxis);
svg.append("g")
.attr("transform", "translate(20,0)")
.call(yAxis);
<script src="https://d3js.org/d3.v4.min.js"></script>
However, if you're talking about "getting the value that is 25% of the domain", the answer is easy. You can, for instance, create a function:
function findLimit(percentage) {
return x(x.domain()[0] + (x.domain()[1] - x.domain()[0]) * percentage / 100);
};
And pass the value of that function to lowerLimit
:
var lowerLimit = findLimit(25);
Check this demo, drawing a line at 25% of the x axis:
var data = d3.range(100).map(() => ({
x: Math.random() * 120 + 30,
y: Math.random() * 100
}));
var w = 500,
h = 200;
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
x = d3.scaleLinear().domain([30, 150]).range([20, w - 20]);
y = d3.scaleLinear().domain([0, 100]).range([h - 20, 20]);
var xAxis = d3.axisBottom(x);
var yAxis = d3.axisLeft(y);
var circles = svg.selectAll("circles")
.data(data)
.enter()
.append("circle")
.attr("r", 2)
.attr("fill", "teal")
.attr("cx", d => x(d.x))
.attr("cy", d => y(d.y));
var data = data.sort((a, b) => d3.ascending(a.x, b.x))
var lowerLimit = d3.quantile(data, 0.25, function(d) {
return d.x
});
console.log(lowerLimit);
svg.append('line')
.attr('x1', x(lowerLimit))
.attr('y1', 20)
.attr('x2', x(lowerLimit))
.attr('y2', h - 20)
.style('stroke', 'red')
svg.append("g")
.attr("transform", "translate(0," + (h - 20) + ")")
.call(xAxis);
svg.append("g")
.attr("transform", "translate(20,0)")
.call(yAxis);
<script src="https://d3js.org/d3.v4.min.js"></script>
Upvotes: 2