My First Chart

Let’s make a chart! I’m going to try to replicate a chart from Golden State of Mind where we were voting on players for a NBA draft board (fear not, you won’t have to know what the heck that means to follow along here). Here’s a snapshot of it:

Snapshot of poll results that we’re trying to replicate.

The Result

Here’s my version followed by the code to implement it:

The Method

First, we define our dataset, which is an array of objects:


var players = [
                {"First": "Harrison", "Last": "Barnes", "Votes": 61},
                {"First":"Andre", "Last":"Drummond", "Votes":27},
                {"First":"Bradley", "Last":"Beal", "Votes":"58"},
                {"First":"Meyers", "Last":"Leonard", "Votes":8},
                {"First":"Jared", "Last":"Sullinger", "Votes":8},
                {"First":"Damian", "Last":"Lillard", "Votes":5},
                {"First":"Terrence", "Last":"Jones", "Votes":2},
                {"First":"Austin", "Last":"Rivers", "Votes":2},
                {"First":"Perry", "Last":"Jones", "Votes":2},
                {"First":"Others","Last":"","Votes":6}
                ],

There are surely many different ways to read in the data, but that one made sense to me. Now we’ll define some other variables that will help us make the chart later:


    maxVotes = Math.max.apply(Math,players.map(function(o){return o.Votes;})),
    minVotes = Math.min.apply(Math,players.map(function(o){return o.Votes;})),
    numVotes = players.reduce(function(a,b) {return a+parseInt(b.Votes);},0),
    xOffset = 50,
    numPlayers = players.length,
    svgHeight = numPlayers*55,
    svgWidth = maxVotes + 200,
    yOffset = svgHeight/(numPlayers+1);

We’ll sort the array by votes in descending order (actually this is an improvement over the above chart):


    function sorter(a,b) {
         return b.Votes - a.Votes;
         }
    players.sort(sorter);

Next we insert the svg and draw a border around it:


    d3.select("body").append("svg")
        .attr("width",svgWidth)
        .attr("height",svgHeight);
    d3.select("svg").append("rect")
        .attr("x",0)
        .attr("y",0)
        .attr("width",svgWidth)
        .attr("height",svgHeight)
        .attr("style","fill-opacity:0; stroke:black;stroke-width:6px");

Then we draw the bars:


    d3.select("svg")
        .append("g").attr("class","bars")
        .selectAll("rect")
        .data(players)
        .enter()
            .append("rect")
                .attr("x",xOffset)
                .attr("style","fill:gold; stroke:black")
                .attr("y", function(d,i) {
                    return yOffset*i+50;
                    })
                .attr("width", function(d,i) {
                    return  d.Votes*2;
                    })
                .attr("height", 20);

Followed by the labels above each bar containing player names:


    d3.select("svg")
        .append("g").attr("class","names")
        .selectAll("text")
        .data(players)
        .enter()
            .append("text")
                .attr("x", xOffset)
                .attr("y", function(d,i) {
                    return yOffset*i+45;
                    })
                .attr("style","font-family: sans-serif; font-size: 12pt")
                .text(function(d) {return d.First+" "+d.Last;});

The number of votes each player received is placed to the right of the bars (I think an improvement in readability over the original):


    d3.select("svg")
        .append("g").attr("class","votes")
        .selectAll("text")
        .data(players)
        .enter()
            .append("text")
                .attr("x", function(d,i) {
                    return xOffset+d.Votes*2+5;
                    })
                .attr("y", function(d,i) {
                    return yOffset*i+45+20;
                    })
                .attr("style","font-family: sans-serif; font-size: 10pt")
                .text(function(d) {return parseInt(d.Votes) + " votes";});

And the % of total votes each player received is placed to the left of the bars:


    d3.select("svg")
        .append("g").attr("class","percent")
        .selectAll("text")
        .data(players)
        .enter()
            .append("text")
                .attr("x", 10)
                .attr("y", function(d,i) {
                    return yOffset*i+45+20;
                    })
                .attr("style","font-family: sans-serif; font-size: 10pt")
                .text(function(d) {
                    return d3.format(".2p")(d.Votes/numVotes);
                    });

Finally, I put the total number of votes below the chart:


    d3.select("body").append("div")
        .text(numVotes+" votes");

Code

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102

                        
<!DOCTYPE html>

                        
<html>

                        
<head>

                        
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.v2.js"></script>

                        
<style type="text/css">

                        
</style>

                        
</head>

                        
<body>

                        
<script>

                        
var players = [

                        
{"First": "Harrison", "Last": "Barnes", "Votes": 61},

                        
{"First":"Andre", "Last":"Drummond", "Votes":27},

                        
{"First":"Bradley", "Last":"Beal", "Votes":"58"},

                        
{"First":"Meyers", "Last":"Leonard", "Votes":8},

                        
{"First":"Jared", "Last":"Sullinger", "Votes":8},

                        
{"First":"Damian", "Last":"Lillard", "Votes":5},

                        
{"First":"Terrence", "Last":"Jones", "Votes":2},

                        
{"First":"Austin", "Last":"Rivers", "Votes":2},

                        
{"First":"Perry", "Last":"Jones", "Votes":2},

                        
{"First":"Others","Last":"","Votes":6}

                        
],

                        
maxVotes = Math.max.apply(Math,players.map(function(o){return o.Votes;})),

                        
minVotes = Math.min.apply(Math,players.map(function(o){return o.Votes;})),

                        
numVotes = players.reduce(function(a,b) {return a+parseInt(b.Votes);},0),

                        
xOffset = 50,

                        
numPlayers = players.length,

                        
svgHeight = numPlayers*55,

                        
svgWidth = maxVotes + 200,

                        
yOffset = svgHeight/(numPlayers+1);

                        
function sorter(a,b) {

                        
return b.Votes - a.Votes;

                        
}

                        
players.sort(sorter);

                        
d3.select("body").append("svg")

                        
.attr("width",svgWidth)

                        
.attr("height",svgHeight);

                        
d3.select("svg").append("rect")

                        
.attr("x",0)

                        
.attr("y",0)

                        
.attr("width",svgWidth)

                        
.attr("height",svgHeight)

                        
.attr("style","fill-opacity:0; stroke:black;stroke-width:6px");

                        
d3.select("svg")

                        
.append("g").attr("class","bars")

                        
.selectAll("rect")

                        
.data(players)

                        
.enter()

                        
.append("rect")

                        
.attr("x",xOffset)

                        
.attr("style","fill:gold; stroke:black")

                        
.attr("y", function(d,i) {

                        
return yOffset*i+50;

                        
})

                        
.attr("width", function(d,i) {

                        
return d.Votes*2;

                        
})

                        
.attr("height", 20);

                        
d3.select("svg")

                        
.append("g").attr("class","names")

                        
.selectAll("text")

                        
.data(players)

                        
.enter()

                        
.append("text")

                        
.attr("x", xOffset)

                        
.attr("y", function(d,i) {

                        
return yOffset*i+45;

                        
})

                        
.attr("style","font-family: sans-serif; font-size: 12pt")

                        
.text(function(d) {return d.First+" "+d.Last;});

                        
d3.select("svg")

                        
.append("g").attr("class","votes")

                        
.selectAll("text")

                        
.data(players)

                        
.enter()

                        
.append("text")

                        
.attr("x", function(d,i) {

                        
return xOffset+d.Votes*2+5;

                        
})

                        
.attr("y", function(d,i) {

                        
return yOffset*i+45+20;

                        
})

                        
.attr("style","font-family: sans-serif; font-size: 10pt")

                        
.text(function(d) {return parseInt(d.Votes) + " votes";});

                        
d3.select("svg")

                        
.append("g").attr("class","percent")

                        
.selectAll("text")

                        
.data(players)

                        
.enter()

                        
.append("text")

                        
.attr("x", 10)

                        
.attr("y", function(d,i) {

                        
return yOffset*i+45+20;

                        
})

                        
.attr("style","font-family: sans-serif; font-size: 10pt")

                        
.text(function(d) {

                        
return d3.format(".2p")(d.Votes/numVotes);

                        
});

                        
d3.select("body").append("div")

                        
.text(numVotes+" votes");

                        
</script>

                        
</body>

                        
</html>

                      
hosted with ❤ by
Posted on by
Tagged :

Categories :bar chartsdata joins

Post comment as twitter logo facebook logo
Sort: Newest | Oldest