Reputation: 237
I'm trying to recreate this figure using D3.js, but I'm having some trouble.
I'm very new to D3 and I haven't been able to find examples of scatterplots with ordinal values. I'm also having trouble including both positive and negative values on the y-axis. I got pretty close by using a bar chart example but the points aren't centered over the axis ticks and negative values are missing from the figure altogether.
I'm using made up data for this but the dots are supposed to be at the "value" and the line is supposed to go from min to max for each phenotype. Clearly there are several issues with my approach.
var plot_data = [
[{Phenotype: 'TG', value: 0.01, min: -0.1, max: 0.4},
{Phenotype: 'LDL', value: -0.29, min: -0.5, max: -0.1},
{Phenotype: 'HDL', value: 0.41, min: 0.0, max: 0.5},
{Phenotype: 'TC', value: 0.07, min: -0.2, max: 0.4}]];
If anyone can point me to an example that does something similar it would be greatly appreciated.
<script>
var margin = {top: 20, right: 20, bottom: 70, left: 40},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom ;
var x = d3.scale.ordinal().rangeRoundBands([0, width], .05);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("middle");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(4);
var svg = d3.select("#cluster_".concat("{{ n }}")).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 plot_data = [
[{Phenotype: 'TG', value: 0.01, min: -0.1, max: 0.4},
{Phenotype: 'LDL', value: -0.29, min: -0.5, max: -0.1},
{Phenotype: 'HDL', value: 0.41, min: 0.0, max: 0.5},
{Phenotype: 'TC', value: 0.07, min: -0.2, max: 0.4}]];
plot_data.forEach(function(data){
x.domain(data.map(function(d) { return d.Phenotype; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)" );
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
svg.selectAll("scatter-dots")
.data(data)
.enter().append("svg:circle")
.style("fill", "steelblue")
.attr("cx", function(d) { return x(d.Phenotype); })
.attr("cy", function(d) { return y(d.value); })
.attr("r", 8);
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.style("fill", "steelblue")
.attr("x", function(d) { return x(d.Phenotype); })
.attr("width", 1)
.attr("y1", function(d) { return y(d.min); })
.attr("y2", function(d) { return y(d.max); })
.attr("height", function(d) { return Math.abs(y(d.max) - y(d.min));});
</script>
Similar issues have been posted before but don't seem to be resolved.
Upvotes: 0
Views: 320
Reputation: 33
You have a few things that need to be fixed, but you are on the right track. I suggest you have a look at some of the good resources online. In particular, https://leanpub.com/D3-Tips-and-Tricks.
Your plot_data
array should be an array of objects rather than an array with an array.
This loop is unnecessary - one of the points of d3 is that it has "joins" that in many cases takes the place of loops:
plot_data.forEach(function(data){...}
Have a look at this fiddle - jsfiddle is a great place to experiment with code and slowly learn and build up the project.
https://jsfiddle.net/sanandak/8h7hzyw5/1/
Hmm.. Stackoverflow has it's own "fiddle"-like capability now! Here is the same code...
var svg = d3.select('body').append('svg')
svg.attr('width', 300).attr('height', 300)
svg.append('g').attr('class', 'axis x-axis')
svg.append('g').attr('class', 'axis y-axis')
var plot_data = [
{Phenotype: 'TG', value: 0.01, min: -0.1, max: 0.4},
{Phenotype: 'LDL', value: -0.29, min: -0.5, max: -0.1},
{Phenotype: 'HDL', value: 0.41, min: 0.0, max: 0.5},
{Phenotype: 'TC', value: 0.07, min: -0.2, max: 0.4}];
var xScale = d3.scalePoint()
.domain(['TG', 'LDL', 'HDL', 'TC'])
.range([30,290])
var yScale = d3.scaleLinear()
.domain([-1, 1])
.range([290, 30])
var xAxis = d3.axisBottom(xScale);
var yAxis = d3.axisRight(yScale);
svg.select('.x-axis').call(xAxis);
svg.select('.y-axis').call(yAxis);
var circles = svg.append('g')
.selectAll('.circle')
.data(plot_data)
.enter()
.append('circle')
.attr('class', 'circle')
.attr('cx', function(d) {return xScale(d.Phenotype);})
.attr('cy', function(d) {return yScale(d.value);})
.attr('r', 5)
.attr('fill', 'red')
var eWidth = 2;
var errbars = svg.append('g')
.selectAll('.errbars')
.data(plot_data)
.enter()
.append('rect')
.attr('class', 'errbars')
.attr('x', function(d) {return xScale(d.Phenotype) - eWidth/2;})
.attr('y', function(d) {return yScale(d.max);})
.attr('width', eWidth)
.attr('height', function(d) {return yScale(d.min) - yScale(d.max);})
<script src="//d3js.org/d3.v4.min.js"></script>
Upvotes: 1