Skip to content

Instantly share code, notes, and snippets.

@tanykim
Last active October 28, 2015 19:12
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tanykim/62462c396b37874ebd87 to your computer and use it in GitHub Desktop.
Save tanykim/62462c396b37874ebd87 to your computer and use it in GitHub Desktop.
Smart Axis Ticks

When you make bar charts or line graphs, or any other visualizations that need axes, you need to set the range of numbers on an axis.

You can simply set the range of axis values exactly same as the data range. However, often you want to set the highest value larger than the maximum number in the dataset.

Also, for the easier understanding for the data range, it is often recommended to set the distance between the nearby two ticks "a rounded number" such as 1, 2, 5, 10, 20, 50, 100 (i.e., 2 * 10^n, 5 * 10^n, 10^n).

This smart tick function (getSmartTicks) generates a desirable range of data, cut by the most appopriate rounded step depending on the maximum value of the given dataset.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font-size: 12px;
font-family: helvetica, arial, san-serif;
}
.axis line {
fill: none;
stroke: #999;
stroke-width: 1px;
shape-rendering: crispEdges;
}
.axis path {
fill: none;
stroke: none;
}
</style>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.2/underscore-min.js"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
//draw svg
d3.select('body').append('svg')
.attr('width', 960)
.attr('height', 500)
//each chart dimension
var margin = { top: 20, right: 20, bottom: 30, left: 40 };
var dim = {
w: 960/2 - margin.left - margin.right,
h: 500/2 - margin.top - margin.bottom
};
//example data for four charts
var maxVals = [9, 40, 228, 1019];
var data = _.map(maxVals, function (d) {
return [d/4, d/2, d, d/2, d/2];
});
function getSmartTicks(val) {
//base step between nearby two ticks
var step = Math.pow(10, val.toString().length - 1);
//modify steps either: 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000...
if (val / step < 2) {
step = step / 5;
} else if (val / step < 5) {
step = step / 2;
}
//add one more step if the last tick value is the same as the max value
//if you don't want to add, remove "+1"
var slicesCount = Math.ceil((val + 1) / step);
return {
endPoint: slicesCount * step,
count: Math.min(10, slicesCount) //show max 10 ticks
};
}
_.each(_.range(4), function (i) {
var svg = d3.select('svg').append('g')
.attr('transform', 'translate(' +
((dim.w + margin.left + margin.right) * Math.floor(i/2) + margin.left) + ',' +
((dim.h + margin.top + margin.bottom) * (i%2) + margin.top) + ')');
var x = d3.scale.ordinal().rangeBands([0, dim.w]).domain([0, 1, 2, 3, 4]);
//get the end value of Y axis and the number of ticks
var yTicks = getSmartTicks(maxVals[i], dim.h);
var y = d3.scale.linear().range([0, dim.h]).domain([yTicks.endPoint, 0]);
var xAxis = d3.svg.axis().scale(x).orient('bottom').outerTickSize(0);
var yAxis = d3.svg.axis().scale(y).orient('left')
.ticks(yTicks.count)
.tickSize(-dim.w)
.tickPadding(9);
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0, ' + dim.h + ')')
.call(xAxis);
svg.append('g')
.attr('class', 'y axis')
.call(yAxis);
svg.selectAll('.rect')
.data(data[i])
.enter()
.append('rect')
.attr('x', function (d, j) { return x(j) + dim.w / data[i].length / 4; })
.attr('y', function (d) { return y(d); })
.attr('width', dim.w / data[i].length / 2)
.attr('height', function (d) { return dim.h - y(d); })
.attr('class', 'rect');
svg.append('text')
.attr('x', dim.w / 2)
.attr('y', y(maxVals[i]))
.attr('dy', -9)
.text(maxVals[i])
.style('text-anchor', 'middle')
.style('font-size', '14px');
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment