Creating a Text Labeled x-Axis with an Ordinal Scale in D3.js

24,557

You can set the tick values of an ordinal axis explicitly using d3.svg.axis().tickValues(*array*).

But this is an odd way to do it because it dangerously separates your keys and values, meaning you have to take care to manually align the scales and make sure that your data corresponds correctly. It helps to group the keys and values in a single object and then use the format:

       axis.domain(array.map(function (d) { return d.value; }))

to map your axis domains.

I have reworked your data and fiddle to do it in what I see as the more d3 way. (Also note that I made some other changes just for fun, namely improved the margins and cleaned up the axis alignment, etc.)

Share:
24,557
arete
Author by

arete

Updated on July 09, 2022

Comments

  • arete
    arete almost 2 years

    I'm building a bar chart in d3.js with an ordinal x-axis whose ticks should label the chart with text. Could anyone explain how the ordinal scale "maps" x ticks to the corresponding bar positions? Specifically, if I want to designate the x tick labels with an array of text values to the corresponding bars in a bar chart.

    Currently I'm setting the domain as the following:

        var labels = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t"];
        var xScale = d3.scale.ordinal()
                    .domain(labels)
    

    However, values of 1-19 are showing after the text labels.

    As seen in this fiddle:

    http://jsfiddle.net/chartguy/FbqjD/

    Associated Fiddle Source Code:

    //Width and height
                var margin = {top: 20, right: 20, bottom: 30, left: 40};           
                var width = 600 - margin.left - margin.right;
                var height= 500-margin.top -margin.bottom;
                var w = width;
                var h = height;
    
    
                var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
                                11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ];
    
                var labels = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t"];
                var xScale = d3.scale.ordinal()
                .domain(labels)
                                .rangeRoundBands([margin.left, width], 0.05);
                var xAxis = d3.svg.axis().scale(xScale).orient("bottom");
    
    
                var yScale = d3.scale.linear()
                                .domain([0, d3.max(dataset)])
                                .range([h,0]);
    
    
                //Create SVG element
                var svg = d3.select("body")
                            .append("svg")
                            .attr("width", w)
                            .attr("height", h);
    
                //Create bars
                svg.selectAll("rect")
                   .data(dataset)
                   .enter()
                   .append("rect")
                   .attr("x", function(d, i) {
                        return xScale(i);
                   })
                   .attr("y", function(d) {
                        return yScale(d);
                   })
                   .attr("width", xScale.rangeBand())
                   .attr("height", function(d) {
                        return h - yScale(d);
                   })
                   .attr("fill", function(d) {
                        return "rgb(0, 0, 0)";
                   });
    
                svg.append("g")
          .attr("class", "x axis")
          .attr("transform", "translate(0," + 0 + ")")
          .call(xAxis);
    
                //Create labels
                svg.selectAll("text")
                   .data(dataset)
                   .enter()
                   .append("text")
                   .text(function(d) {
                        return d;
                   })
                   .attr("x", function(d, i) {
                        return xScale(i) + xScale.rangeBand() / 2;
                   })
                   .attr("y", function(d) {
                        return h - yScale(d) + 14;
                   });
    
  • arete
    arete almost 11 years
    So what is it about incorporating the value-label pairs into a single object in d3 instead of linking it labels to an object and values to an object. I appreciate your response, but I want to go the extra mile and know exactly what's going on behind the scenes so I'm not just programming by coincidence.
  • nsonnad
    nsonnad almost 11 years
    It's because you are defining the domain explicitly with one set of data then passing another set to it when you do .attr("x", function(d, i) { return xScale(i); }). The relevant line from the API reference says: "If the domain is set explicitly, values passed to the scale that were not explicitly part of the domain will be added." To this end, you might notice that in your original code calling the x axis before the rects will result in only your explicit array being called.