Flask + D3.js - Data Dashboard - Part 2

In previous part of this series, we created back-end python script to render our html page and serve JSON data. In this part, we will focus on creating Pie-chart representation of our data.

We will continue to work on files we created in part-1, so it's recommended you go through it, in case you haven't. You can get the whole project code at this github link.

5. Creating pie-chart Time to use D3 and add functionality to our application. Open piechcart.js in your editor: First we add a global variable for formatting values in integer form and create the piechart function which will be called from main.js.

    
        var formatAsInteger = d3.format(",");

        function d3PieChart(dataset, datasetBarChart){
        }
    
    

This function accepts 2 variables, dataset used for rendering pieChart and datasetBarChart for rendering barChart. Now inside the function we will add actual code for data handling and rendering.

Add following code inside d3PieChart() function we created above:

    
    var margin = {top: 30, right: 5, bottom: 20, left: 50};
    var width  = 400 - margin.left - margin.right ,
            height  = 400 - margin.top - margin.bottom,
            outerRadius  = Math.min(width, height) / 2,
            innerRadius  = outerRadius * .999,  
                // for animation
            innerRadiusFinal =  outerRadius * .5,
            innerRadiusFinal3 = outerRadius *  .45,
            color  = d3.scaleOrdinal(d3.schemeCategory10);
    
    

We start by defining some variables that will set our svg positions and dimensions. outerRadius and innerRadius relate to radius of pieChart. color variable will render colors based on ‘schemeCategory10’, which is an inbuilt D3 color scheme.

Next, we will select our pieChart div and add an SVG to it.

    
    var vis = d3.select("#pieChart")
            .append("svg:svg")           
            .data([dataset])  
            .attr("width", width) 
            .attr("height", height)
            .append("svg:g")    
            .attr("transform", "translate(" + outerRadius + "," + outerRadius + ")");
            
    
  • we added SVG to pieChart with few attributes like width, height.
  • svg:g will hold the piechart and other elements together.
  • .data is passed dataset we want to render
  • g is a D3 element which stands for “group” and used to hold multiple elements together, as a group.
  • transform will move the center of the pie chart from 0, 0 to radius, radius

We will add arc element to svg now. The arc generator produces a circular or angular sector, as in a pie or donut chart. Following code will do that:

    
    var arc = d3.arc()    
            .outerRadius(outerRadius).innerRadius(0);
    
        // for animation
    var arcFinal =  d3.arc().innerRadius(innerRadiusFinal).outerRadius(outerRadius);
    var arcFinal3 =  d3.arc().innerRadius(innerRadiusFinal3).outerRadius(outerRadius);  
    
    var pie = d3.pie()          
        .value(function(d) { return d.measure; });
                        
    
  • first line creates path element for us using arc data
  • arcFinal and arcFinal3 are used for animation effects. We want 2 different arcs with different radius, one for mouseover and another for default position.
  • d3.pie().value() will create arc data for us given a list of values. d.measure is the data we need to render one arc. d is D3 element which stands for data we passed initially in data(). measure is one of the keys in our key-value pair. It holds actual numeric value we passed from backend script in JSON format.

Next, we will create slices on our pie, based on numeric data values we passed from python backend script.

    
    var arcs = vis.selectAll("g.slice")    
        .data(pie)                         
        .enter()                           
        .append("svg:g")
        .attr("class", "slice")   
        .on("mouseover", mouseover)
        .on("mouseout", mouseout)
        .on("click", up);
    
    
  • First, we select the slice element inside g element. The slice element doesnt exist yet, but we are going to create it in a while
  • Next, we associate the generated data with element. It is an array of arcs which have startAngle, endAngle and value properties
  • .enter identifies any DOM elements that needs to be added when the joined array is longer than the selection. We use it to create g elements for every extra data element that should be associated with a selection
  • Then, we create a group g to hold all slices
  • Finally, we add , class, mouseover, mouseout and click events on our svg group

Now that we have created slice, its time to give some color to our slices based on group the data belongs to. For example Class I passengers will have different color than Class II and Class III.

    
    arcs.append("svg:path")
            .attr("fill", function(d, i) { return color(i); } )
            .attr("d", arc)
            .append("svg:title")
            .text(function(d) { return d.data.category + ": " + formatAsInteger(d.data.measure)+"%"; });
    
        d3.selectAll("g.slice").selectAll("path").transition()
            .duration(750)
            .delay(10)
            .attr("d", arcFinal );
    
    
  • First, we add fill attribute for each slice and set by returning color for each slice, chosen from the color function defined above.
  • .attr(‘d’, arc) creates actual SVG path using associated data with the arc drawing function
  • We add title element to each slice, which has category name and a percent value the specific slice represents. This title will be visible when user mouseovers the slice
  • Finally, we add animation on the data. arcFinal is the final radius of our pie at default position.

To wrap it up, we will add label to pie-chart slices, and main title to chart. Add following code:

    
    // Add a label to the larger arcs, translated to the arc centroid and rotated.
    arcs.filter(function(d) { return d.endAngle - d.startAngle  > .2; })
    .append("svg:text")
    .attr("dy", ".35em")
    .attr("text-anchor", "middle")
    .attr("transform", function(d) { return "translate(" +  arcFinal.centroid(d) + ")"; })
    .text(function(d) { return d.data.category; });

    // Pie chart title          
    vis.append("svg:text")
        .attr("dy", ".35em")
        .attr("text-anchor", "middle")
        .text("Survivors of Titanic")
        .attr("class","title");
    
    
  • As its a piechart, we need to have our slice labels at a certain angle. We define them through filters
  • We do some text transformation and for each slice add category name as label, using text()
  • Similarly, we append a svg:text to write main title of our pie-chart

Finally, we add mouseover, mouseout and up events to pie-chart. Basically we want to animate the pie-chart slice when mouseover or mouseout and when clicked, we want to render/update barchart.

    
    function mouseover() {
            d3.select(this).select("path").transition()
            .duration(750)
            .attr("d", arcFinal3);
        }

    function mouseout() {
            d3.select(this).select("path").transition()
            .duration(750)
            .attr("d", arcFinal);
        }

    function up(d, i) {
            updateBarChart(d.data.category, color(i), datasetBarChart);
        }
    
  • On mouseover, we select path and anmiate the slice to a new radius
  • On mouseout, we want to reset the radius of slice
  • When clicked, up() function is called. We want to update barChart when a slice is clicked.



Takeaways

We are done with pie-chart !! In part-2 of the series, we saw D3 in action and created a pie-chart based on JSON data.

In part-3, we will work on rendering bar-chart based on pie-chart slice clicked by user.


Note, References & Links: