Search code examples
dated3.jsaxis-labels

Use words as tick labels with unequal intervals in D3.js


I am creating a D3.js plot of multiple years of data. I would like to use the month names as the the axis labels. I am currently using the number of days into the year corresponding with the first day of that month as the labels as shown in the screenshot of my x-axis:

enter image description here

I believe all the relevant code is shown below:

var margins = {'top' : 20, 'right' : 20, 'bottom' : 50, 'left' : 70};
var height = 500;
var width = 960;
var plotHeight = height - margins.top - margins.bottom;
var plotWidth = width - margins.right - margins.left;

var x = d3.scaleLinear()
    .domain([0,366])
    .range([margins.left, plotWidth]);

// I am not concerned with leap years
var monthDays = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
var monthAbr = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

var svg = d3.select('.svg').attr('width',width).attr('height',height);

svg.append('g').attr('transform', 'translate(0,' + plotHeight + ')').call(d3.axisBottom(x).tickValues(monthDays));

Is there some way to map the position on the x-axis to the correct month (i.e.: 31 to Feb.)? I have tried using d3.timeMonth/d3.timeMonths and ordinal scales but haven't found something suitable yet.


Solution

  • You can use tickFormat:

    .tickFormat(function(d,i){ return monthAbr[i]})
    

    Here is a working demo based on your code:

    var margins = {'top' : 20, 'right' : 20, 'bottom' : 50, 'left' : 20};
    var height = 100;
    var width = 600;
    var plotHeight = height - margins.top - margins.bottom;
    var plotWidth = width - margins.right - margins.left;
    
    var x = d3.scaleLinear()
        .domain([0,366])
        .range([margins.left, plotWidth]);
    
    // I am not concerned with leap years
    var monthDays = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
    var monthAbr = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
    
    var svg = d3.select('body').append("svg").attr('width',width).attr('height',height);
    
    svg.append('g').attr('transform', 'translate(0,' + plotHeight + ')').call(d3.axisBottom(x).tickValues(monthDays).tickFormat(function(d,i){ return monthAbr[i]}));
    <script src="https://d3js.org/d3.v4.min.js"></script>