Public
Edited
Mar 11, 2023
1 fork
Insert cell
Insert cell
chart = doublebardraw(data,dataavailable,"#twinbartopn")
Insert cell
dataavailable = true
Insert cell
data = FileAttachment("dummydata.json").json();
Insert cell
function doublebardraw(data,dataavailable,id){

var w = 500,
h = 300;

// margin.middle is distance from center line to each y-axis
var margin = {
top: 50,
right: 320,
bottom: 140,
left: 50,
middle: 50, //28
error: 2
};

// the width of each side of the chart
var regionWidth = w/2 - margin.middle;

// x-coordinates of the y-axes
var pointA = regionWidth,
pointB = w - regionWidth;


const svg = d3.create("svg")
.attr("id",id)
.attr('width', margin.left + w + margin.right)
.attr('height', margin.top + h + margin.bottom)
.attr('padding',50)
.attr('transform', translation(margin.left, margin.top));

const Glycopeptide = svg.append("text")
.attr("fill", "black")
.attr("x", w/4+margin.left)
.attr("y", 15)
.attr("text-anchor", "middle")
.text("Glycopeptide");
const Proteome = svg.append("text")
.attr("fill", "black")
.attr("x", 3*(w/4)+margin.left)
.attr("y", 15)
.attr("text-anchor", "middle")
.text("Proteome");



// find the maximum data value on either side
// since this will be shared by both of the x-axes
var maxValue = Math.max(
d3.max(data, function(d) { return d.value_g; }),
d3.max(data, function(d) { return d.value_p; })
);

// SET UP SCALES

// the xScale goes from 0 to the width of a region
// it will be reversed for the left x-axis
var xScale = d3.scaleLinear()
.domain([0, maxValue])
.range([0, regionWidth])
.nice();

var xScaleLeft = d3.scaleLinear()
.domain([0, maxValue])
.range([regionWidth, 0]);

var xScaleRight = d3.scaleLinear()
.domain([0, maxValue])
.range([0, regionWidth]);


var yScaleLeft = d3.scaleBand()
.domain(data.map(function(d) { return d.name_g; }))
.rangeRound([0,h])
.padding(0.1);

// var yScaleLeftest = d3.scaleBand()
// .domain(data.map(function(d) { return d.id_g; }))
// .rangeRound([0,h])
// .padding(0.1);

var yScaleRight = d3.scaleBand()
.domain(data.map(function(d) { return d.name_p; }))
.rangeRound([0,h])
.padding(0.1);


// SET UP AXES
var yAxisLeft = d3.axisRight(yScaleLeft)
.tickSize(4,0)
.tickPadding(3); //margin.middle-4

// var yAxisLeftest = d3.axisLeft(yScaleLeftest)
// .tickSize(4,0)
// .tickPadding(3);

var yAxisRight = d3.axisLeft(yScaleRight)
.tickSize(4,0)
.tickPadding(3);

var xAxisRight = d3.axisBottom(xScale)
.ticks(5);

var xAxisLeft = d3.axisBottom(xScale.copy().range([pointA, 0]))
// REVERSE THE X-AXIS SCALE ON THE LEFT SIDE BY REVERSING THE RANGE
.ticks(5);

// MAKE GROUPS FOR EACH SIDE OF CHART
// scale(-1,1) is used to reverse the left side so the bars grow left instead of right
var leftBarGroup = svg.append('g')
.attr('transform', translation(pointA, 0) + 'scale(-1,1)')
.attr("id","lefti");
var rightBarGroup = svg.append('g')
.attr('transform', translation(pointB, 0));

// DRAW AXES
svg.append('g')
.attr('class', 'axis y left')
.attr('transform', translation(pointA+margin.left, margin.top))
.call(yAxisLeft)
.selectAll('text')
.style('text-anchor', 'left');

// svg.append('g')
// .attr('class', 'axis y left')
// .attr('transform', translation(margin.left, margin.top))
// .call(yAxisLeftest)
// .selectAll('text')
// .style('text-anchor', 'right');

svg.append('g')
.attr('class', 'axis y right')
.attr('transform', translation(pointB+margin.left, margin.top))
.call(yAxisRight);

svg.append('g')
.attr('class', 'axis x left')
.attr('transform', translation(margin.left, h+margin.top))
.call(xAxisLeft);

svg.append('g')
.attr('class', 'axis x right')
.attr('transform', translation(pointB+margin.left, h+margin.top))
.call(xAxisRight);
if(dataavailable){
// DRAW BARS
leftBarGroup.selectAll('.bar.left')
.data(data)
.enter().append('rect')
.attr('class', 'bar left')
.attr('x', -margin.left)
.attr('y', function(d) { return yScaleLeft(d.name_g)+margin.top; })
.attr('width', function(d) { return xScale(d.value_g); })
.attr('height', yScaleLeft.bandwidth())
.append("title").text(function(d) { return ` Identifier: ${d.id_g}\n Gene: ${d.name_g}\n Value: ${parseFloat(d.value_g).toFixed(2)}`});
rightBarGroup.selectAll('.bar.right')
.data(data)
.enter().append('rect')
.attr('class', 'bar right')
.attr('x', margin.left)
.attr('y', function(d) { return yScaleRight(d.name_p)+margin.top; })
.attr('width', function(d) { return xScale(d.value_p); })
.attr('height', yScaleRight.bandwidth())
.append("title").text(function(d) { return ` Gene: ${d.name_p}\n Value: ${parseFloat(d.value_p).toFixed(2)}`});
// Error Bars
let gl = leftBarGroup.append('g');
var lines = gl.selectAll('.line.error')
.data(data);
lines.enter()
.append('line')
.attr('class', 'error')
.attr('x1', function(d) { return xScale(parseFloat(d.value_g)+parseFloat(d.moe_g))-margin.left; })
.attr('x2', function(d) { return xScale(parseFloat(d.value_g)-parseFloat(d.moe_g))-margin.left; })
.attr('y1', function(d) { return yScaleLeft(d.name_g)+(yScaleLeft.bandwidth()/2)+margin.top; })
.attr('y2', function(d) { return yScaleLeft(d.name_g)+(yScaleLeft.bandwidth()/2)+margin.top; });
let glTl = leftBarGroup.append('g');
var lines = glTl.selectAll('.line.errorT')
.data(data);
lines.enter()
.append('line')
.attr('class', 'errorT')
.attr('x1', function(d) { return xScale(parseFloat(d.value_g)+parseFloat(d.moe_g))-margin.left; })
.attr('x2', function(d) { return xScale(parseFloat(d.value_g)+parseFloat(d.moe_g))-margin.left; })
.attr('y1', function(d) { return yScaleLeft(d.name_g)+(yScaleLeft.bandwidth()/2)+margin.top+margin.error; })
.attr('y2', function(d) { return yScaleLeft(d.name_g)+(yScaleLeft.bandwidth()/2)+margin.top-margin.error; });
let glTr = leftBarGroup.append('g');
var lines = glTr.selectAll('.line.errorT')
.data(data);
lines.enter()
.append('line')
.attr('class', 'errorT')
.attr('x1', function(d) { return xScale(parseFloat(d.value_g)-parseFloat(d.moe_g))-margin.left; })
.attr('x2', function(d) { return xScale(parseFloat(d.value_g)-parseFloat(d.moe_g))-margin.left; })
.attr('y1', function(d) { return yScaleLeft(d.name_g)+(yScaleLeft.bandwidth()/2)+margin.top+margin.error; })
.attr('y2', function(d) { return yScaleLeft(d.name_g)+(yScaleLeft.bandwidth()/2)+margin.top-margin.error; });
let gr = rightBarGroup.append('g');
var lines = gr.selectAll('.line.error')
.data(data);
lines.enter()
.append('line')
.attr('class', 'error')
.attr('x1', function(d) { return xScale(parseFloat(d.value_p)+parseFloat(d.moe_p))+margin.left; })
.attr('x2', function(d) { return xScale(parseFloat(d.value_p)-parseFloat(d.moe_p))+margin.left; })
.attr('y1', function(d) { return yScaleRight(d.name_p)+(yScaleRight.bandwidth()/2)+margin.top; })
.attr('y2', function(d) { return yScaleRight(d.name_p)+(yScaleRight.bandwidth()/2)+margin.top; });
let grTl = rightBarGroup.append('g');
var lines = grTl.selectAll('.line.errorT')
.data(data);
lines.enter()
.append('line')
.attr('class', 'errorT')
.attr('x1', function(d) { return xScale(parseFloat(d.value_p)+parseFloat(d.moe_p))+margin.left; })
.attr('x2', function(d) { return xScale(parseFloat(d.value_p)+parseFloat(d.moe_p))+margin.left; })
.attr('y1', function(d) { return yScaleRight(d.name_p)+(yScaleRight.bandwidth()/2)+margin.top+margin.error; })
.attr('y2', function(d) { return yScaleRight(d.name_p)+(yScaleRight.bandwidth()/2)+margin.top-margin.error; });
let grTr = rightBarGroup.append('g');
var lines = grTr.selectAll('.line.errorT')
.data(data);
lines.enter()
.append('line')
.attr('class', 'errorT')
.attr('x1', function(d) { return xScale(parseFloat(d.value_p)-parseFloat(d.moe_p))+margin.left; })
.attr('x2', function(d) { return xScale(parseFloat(d.value_p)-parseFloat(d.moe_p))+margin.left; })
.attr('y1', function(d) { return yScaleRight(d.name_p)+(yScaleRight.bandwidth()/2)+margin.top+margin.error; })
.attr('y2', function(d) { return yScaleRight(d.name_p)+(yScaleRight.bandwidth()/2)+margin.top-margin.error; });
}
// string concatenation for translations
function translation(x,y) {
return 'translate(' + x + ',' + y + ')';
}
return svg.node()
}
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more