xxxxxxxxxx
<html>
<head>
<title>Dual Y Axes</title>
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<style type="text/css">
body{
font-family: sans-serif;
}
.domain{
fill:none;
stroke:#000;
shape-rendering:crispEdges;
}
.plot{
fill:none;
stroke-width:2;
}
.cost-scale .tick{
fill:#F00;
}
.sat-scale .tick{
fill:#00F;
}
.hidden{
display:none;
}
.notes{
max-width: 600px;
}
</style>
</head>
<body>
<h1 class="active">SAT scores remain sluggish despite funding increase <a href="#" data-satDomain="800,1200" data-costDomain="2800,4800">(OR...)</a></h1>
<h1 class="hidden">SAT scores soar despite sluggish funding <a href="#" data-satDomain="800,910" data-costDomain="0,20000">(OR...)</a></h1>
<div class="chart"></div>
<div class="notes">
<p>Why dual axis charts are a bad idea.</p>
<p>Click on the "OR" in the headline to see the same data drawn with different scales.</p>
<p>An example taken from <a href="https://www.sagepub.com/gray/Website%20material/Journals/er_wainer.pdf">Howard Wainer's Understanding Graphs and Tables</a> (figs 6 & 7) for more on the problems of dual y-axes see <a href="https://www.perceptualedge.com/articles/visual_business_intelligence/dual-scaled_axes.pdf">Steven Few's Dual-Scaled Axes in Graphs
Are They Ever the Best Solution?</a></p></div>
</body>
<script type="text/javascript">
var height=500, width=800,
margin={ top:50, left:150, bottom:70, right:100 },
axisGap = 5,
plotWidth = width-(margin.left+margin.right),
plotHeight = height-(margin.top+margin.bottom);
var satDomain = [0,1000];
var costDomain = [0,5000];
var data =[];
var container = d3.select('.chart')
.append('svg')
.attr({
width:width,
height:height
})
.append('g')
.attr({
'transform':'translate(' + margin.left + ',' + margin.top + ')'
});
container.append('g').attr({
'class':'sat-scale',
'transform':'translate('+ (plotWidth + axisGap) +',0)'
});
container.append('g').attr({
'class':'cost-scale',
'transform':'translate('+ (- axisGap) +',0)'
});
container.append('g').attr({
'class':'time-scale',
'transform':'translate(0,' + (plotHeight + axisGap) + ')'
});
var plot = container.append('g').attr({
'class':'plot'
});
plot.append('path').attr({
'class':'sat-line'
});
plot.append('path').attr({
'class':'cost-line'
});
var labels = container.append('g')
.attr('class','labels')
labels.append('text')
.attr({
fill:'#F00',
'transform':'translate(0,'+(-axisGap*4)+')',
'text-anchor':'end'
}).text('Expenditure/ pupil $');
labels.append('text')
.attr({
fill:'#00F',
'transform':'translate(' + plotWidth + ','+(-axisGap*4)+')'
}).text('SAT score');
labels.append('text')
.attr({
'transform':'translate(' + plotWidth/2 + ','+(plotHeight + axisGap*10)+')'
}).text('year');
function getDomains(){
var d = d3.select('h1.active a').node().dataset;
satDomain = d.satdomain.split(',').map(function(d){return Number(d); });
costDomain = d.costdomain.split(',').map(function(d){return Number(d); });
}
function drawChart(){
var costScale = d3.scale.linear()
.range([plotHeight, 0])
.domain(costDomain),
satScale = d3.scale.linear()
.range([plotHeight, 0])
.domain(satDomain),
timeScale = d3.scale.linear()
.range([0,plotWidth])
.domain(d3.extent(data, function(d){ return d.year; })),
leftAxis = d3.svg.axis()
.scale(costScale)
.orient('left'),
rightAxis = d3.svg.axis()
.scale(satScale)
.orient('right'),
timeAxis = d3.svg.axis()
.scale(timeScale)
.orient('bottom'),
costLine = d3.svg.line()
.x(function(d) { return timeScale(d.year); })
.y(function(d) { return costScale(d['expenditure per pupil']); });
satLine = d3.svg.line()
.x(function(d) { return timeScale(d.year); })
.y(function(d) { return satScale(d['sat scores']); });
d3.select( '.sat-scale' ).call( rightAxis );
d3.select( '.cost-scale' ).call( leftAxis );
d3.select( '.time-scale' ).call( timeAxis );
d3.select( '.cost-line' )
.datum(data)
.transition()
.attr({
'd': costLine,
'stroke':'#F00',
'stroke-width': 2
});
d3.select(' .sat-line ')
.datum(data)
.transition()
.attr({
'd': satLine,
'stroke':'#00F',
'stroke-width': 2
});
}
d3.selectAll('a').on('click',function(d){
console.log(this);
//toggle h1s
d3.selectAll('h1')
.attr({
'class':function(){
if (d3.select(this).classed('hidden')){
return 'active';
}
return 'hidden';
}
})
getDomains();
drawChart();
})
d3.csv('data.csv', function(d){
data = d;
getDomains();
drawChart();
})
</script>
</html>
Modified http://d3js.org/d3.v3.min.js to a secure url
https://d3js.org/d3.v3.min.js