Over the years, data analytics have become increasingly influential across industries as it has become key to corporate decision-making. Data analytics techniques can reveal trends, patterns and indicators, providing insight and optimization. That’s why it’s important for developers to know how to build programs that make data visualization easy.

In this article, we will use Flask and D3.js to create a simple, interactive data dashboard to understand some of the factors associated with the assumed customer churn rate.

Flask is a Python networking framework that provides tools, libraries, and techniques for building web applications. D3.js is a JavaScript library that uses data manipulation of DOM elements to render visual components.

Project Settings and environment

It is important to create an isolated local environment that specifies the various packages and versions of installations that are only applicable to this project to prevent global installations and package collisions.

We’ll start by creating a virtual Python environment. Install the Virtualenv package like this using PIP.

pip install virtualenv

Copy the code

Navigate to the project root and create the virtual environment.

virtualenv flask

Copy the code

The virtual environment must be activated before we can install the package. In the project root directory, execute.

source flask/bin/activate

Copy the code

Next, we install the packages required for this project. This can be done by installing all packages through PIP, or by using the requirements.txt file found in the project’s GitHub repository.

pip install -r requirements.txt

Copy the code

With the required Python packages successfully installed, we move on to setting up the project’s file structure and the required files.

. ├ ─ ─ the README. Md ├ ─ ─ app. Py ├ ─ ─ flask ├ ─ ─ the static │ ├ ─ ─ Review. GIF │ ├ ─ ─ CSS │ ├ ─ ─ data │ ├ ─ ─ js │ └ ─ ─ logo. The jpeg └ ─ ─ Templates └ ─ ─ index. HTMLCopy the code

Overview of project workflow

The churn data is fed to the Flask application, where data processing operations are performed in Python. The Flask application will provide the data dashboard and D3.js will render the corresponding charts in JavaScript.

Analyze Flask’s network application

The app.py Python script is a Flask instance that contains entries, routes, and ends. Python’s Pandas and NumPy libraries are used for data-processing operations. The preprocessed data is serialized into JSON format to provide to index.html, and the analysis includes contract and tenure characteristics.

The contract characteristics describe the terms of the contract between the customer and the instance company in three levels: monthly, one year, and two years. Tenure is a continuous feature that describes the number of months a customer has stayed with the company.

from flask import Flask, jsonify, render_template
import pandas as pd
import numpy as np

app = Flask(__name__)

#Reading data
data_df = pd.read_csv("static/data/Churn_data.csv")
churn_df = data_df[(data_df['Churn']=="Yes").notnull()]

@app.route('/')
def index():
   return render_template('index.html')

def calculate_percentage(val, total):
   """Calculates the percentage of a value over a total"""
   percent = np.round((np.divide(val, total) * 100), 2)
   return percent

def data_creation(data, percent, class_labels, group=None):
   for index, item in enumerate(percent):
       data_instance = {}
       data_instance['category'] = class_labels[index]
       data_instance['value'] = item
       data_instance['group'] = group
       data.append(data_instance)

@app.route('/get_piechart_data')
def get_piechart_data():
   contract_labels = ['Month-to-month', 'One year', 'Two year']
   _ = churn_df.groupby('Contract').size().values
   class_percent = calculate_percentage(_, np.sum(_)) #Getting the value counts and total

   piechart_data= []
   data_creation(piechart_data, class_percent, contract_labels)
   return jsonify(piechart_data)

@app.route('/get_barchart_data')
def get_barchart_data():
   tenure_labels = ['0-9', '10-19', '20-29', '30-39', '40-49', '50-59', '60-69', '70-79']
   churn_df['tenure_group'] = pd.cut(churn_df.tenure, range(0, 81, 10), labels=tenure_labels)
   select_df = churn_df[['tenure_group','Contract']]
   contract_month = select_df[select_df['Contract']=='Month-to-month']
   contract_one = select_df[select_df['Contract']=='One year']
   contract_two =  select_df[select_df['Contract']=='Two year']
   _ = contract_month.groupby('tenure_group').size().values
   mon_percent = calculate_percentage(_, np.sum(_))
   _ = contract_one.groupby('tenure_group').size().values
   one_percent = calculate_percentage(_, np.sum(_))
   _ = contract_two.groupby('tenure_group').size().values
   two_percent = calculate_percentage(_, np.sum(_))
   _ = select_df.groupby('tenure_group').size().values
   all_percent = calculate_percentage(_, np.sum(_))

   barchart_data = []
   data_creation(barchart_data,all_percent, tenure_labels, "All")
   data_creation(barchart_data,mon_percent, tenure_labels, "Month-to-month")
   data_creation(barchart_data,one_percent, tenure_labels, "One year")
   data_creation(barchart_data,two_percent, tenure_labels, "Two year")
   return jsonify(barchart_data)


if __name__ == '__main__':
   app.run(debug=True)

Copy the code

This entry point has an index.html template file that consists of a data dashboard layout. The index.html template consists of two containers: the authoring part and the visualization part.

The template file contains the access point to the script file and a CDN that links d3.js to the project along with the CSS styles.css. Scripts including piechart.js, barchart.js, updateBarchart.js, and index.js do the following.

  • Render pie charts and default bar charts
  • Update the bar chart based on the pie chart selection
  • Includes a main script that runs the charting function for rendering on the dashboard.

The index.html template also gets JSON response data via the route URL, which has two variables: pieChartDataUrl and barChartDataUrl.

> <! DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content="Data Dashboard"> <meta name="author" content="Aboze Brain"> <meta http-equiv="content-type" content="text/html; charset=utf-8"/> <title>Data Dashboard</title> <link href="{{ url_for('static', filename='css/styles.css') }}" rel="stylesheet"> </head> <body> <div class="about"> <h1>Data Dashboard</h1> <h2>Project:  Interactive charts for frontend data visualization using flask and D3js</h2> <h2>Author: Aboze Brain John</h2> <p>Bio: Aboze Brain John is a Technology Business Analyst. He has experience in Data Science and Analytics, Software Engineering, Product Research, and Technical Writing.</p> <p>Project Overview: The project is focused on the analysis of churned customers. This analysis is achieved using Python's Flask library to serve the data and Javascript D3.js library to visualize the analysis. The use case is the Telco Customer Churn found on  Kaggle <a href="https://www.kaggle.com/blastchar/telco-customer-churn">here</a> </p> <h4>The code can be found on Github <a href="https://github.com/codebrain001/Interactive-charts-for-frontend-data-visualization-using-flask-and-D3js">here</a>< /h4> <h4>The article can be found on Logrocket blog <a href="#">here</a></h4> <img src="{{ url_for('static', filename='logo.jpeg')}}" alt="Logrocket logo"> </div> <div class="visualization"> <div id="pieChart"></div> <div id="barChart"></div> </div> <script src="https://d3js.org/d3.v5.min.js"></script> <script> const pieChartDataUrl = "{{ url_for('get_piechart_data') }}"; const barChartDataUrl = "{{ url_for('get_barchart_data') }}"; </script> <script src="{{ url_for('static', filename='js/pieChart.js') }}"></script> <script src="{{ url_for('static', filename='js/barChart.js') }}"></script> <script src="{{ url_for('static', filename='js/updateBarChart.js') }}"></script> <script src="{{ url_for('static', filename='js/index.js') }}"></script> </body> </html>Copy the code

JavaScript scripts take advantage of the functional programming paradigm, using various functions to create components that execute in index.js. The index.js file uses promises to handle asynchronous operations and represents final completion (or failure) and the resulting value.

const urls = [pieChartDataUrl, barChartDataUrl];

Promise.all(urls.map(url => d3.json(url))).then(run);

function run(dataset) {
   d3PieChart(dataset[0], dataset[1]);
   d3BarChart(dataset[1]);
};

Copy the code

Function to create pie and bar charts

Next, we have two functions that create d3PieChart and d3BarChart in the piechart.js and barchart.js static files respectively. We’ll take advantage of SVG elements because they offer different shapes and provide more flexibility and power.

The d3PieChart function takes two parameters: pie chart data and data set to update the bar graph when a slice of the pie is selected. The piechart.js file contains the following contents.

function d3PieChart(dataset, datasetBarChart){ // Set up SVG dimensions and properties const margin = {top:20, right:20, bottom:20, left:20}; const width = 350 - margin.left - margin.right, height = 350 - margin.top - margin.bottom, outerRadius = Math.min(width, height) / 2, innerRadius = outerRadius * .5, color = d3.scaleOrdinal(d3.schemeAccent); //color scheme // Selecting the div with id pieChart on the index.html template file const visualization = d3.select('#pieChart') .append("svg") //Injecting an SVG element .data([dataset]) //Binding the pie chart data .attr("width", width) .attr("height", height) .append("g") //Grouping the various SVG components .attr("transform", "translate(" + outerRadius + "," + outerRadius + ")"); //Piechart tranformation and transition upon page loading const data = d3.pie() //Creating the data object that will develop the various segment of the pie chart. .sort(null) .value(function(d){return d.value; })(dataset); // Retrieve the pie chart data values from our Flask app, the pie chart where tied to a 'value' key of a JSON object. // Generate an arc generator that produces the circular chart (outer circle) const arc = d3.arc() .outerRadius(outerRadius) .innerRadius(0); // Generate an arc generator that produces the circular chart (inner circle) const innerArc = d3.arc() .innerRadius(innerRadius) .outerRadius(outerRadius); // Create pie chart slices based on the data object created const arcs = visualization.selectAll("g.slice") .data(data) .enter() // creates the initial join of data to elements .append("svg:g") .attr("class", "slice") .on("click", click); arcs.append("svg:path") // create path element .attr("fill", function(d, i) { return color(i); } ) //Add color to slice .attr("d", arc) // creates actual SVG path with associated data and the arc drawing function .append("svg:title") // Add title to each piechart slice .text(function(d) { return d.data.category + ": " + d.data.value+"%"; }); d3.selectAll("g.slice") // select slices in the group SVG element (pirchart) .selectAll("path") .transition() //Set piechart transition on loading .duration(200) .delay(5) .attr("d", innerArc); arcs.filter(function(d) { return d.endAngle - d.startAngle > .1; }) //Define slice labels at certain angles .append("svg:text") //Insert text area in SVG .attr("dy", "0.20em") // Shift along the Y-axis on the position of text content.attr ("text-anchor", "middle") //Position slice labels .attr("transform", function(d) { return "translate(" + innerArc.centroid(d) + ")"; }) //Positioning upon transition and transform .text(function(d) { return d.data.category; }); // Append category name on slices visualization.append("svg:text") //Append the title of chart in the middle of the pie chart .attr("dy", ".20em") .attr("text-anchor", "middle") .text("churned customers") .attr("class","title"); // Function to update barchart when a piechart slice is clicked function click(d, i) { updateBarChart(d.data.category, color(i), datasetBarChart); }}Copy the code

The d3BarChart function defines the default group, which is visualized when the page loads without selecting a specific contract class. The default group is the tenure distribution of lost customers.

D3BarChart accepts only one parameter: the bar chart data being served. Barchart.js contains the following.

//Set up SVG dimensions and properties const margin = {top: 20, right: 10, bottom: 20, left: 20}, width = 350 - margin.left - margin.right, height = 350 - margin.top - margin.bottom, barPadding = 5, graph_misc = {ylabel: 4, xlabelH : 5, title:9}; // Setting the default group const group = "All"; // Function to get the percentage values for a specific selected group from the whole dataset. function get_percentage(group, datasetBarChart){ const _ = []; for (instance in datasetBarChart){ if (datasetBarChart[instance].group==group){ _.push(datasetBarChart[instance]) } } return _; }; function d3BarChart(datasetBarChart){ defaultBarChart = get_percentage(group, datasetBarChart); const xScale = d3.scaleLinear() // Barchart X axis scale .domain([0, defaultBarChart.length]) // Scale range from 0 to the length of data object .range([0, width]); const yScale = d3.scaleLinear() // Barchart y axis scale .domain([0, d3.max(defaultBarChart, function(d) { return d.value;  })]) //Scale range from 0 to the maximum value of the default bar chart data .range([height, 0]); // // Selecting the div with id barChart on the index.html template file const bar = d3.select('#barChart') .append('svg') .attr('width', width + margin.left + margin.right) .attr('height', height + margin.top + margin.bottom) .attr('id', 'barChartPlot'); //Adding barchart title bar.append('text') .attr('x', (width + margin.left + margin.right)/2) .attr('y', graph_misc.title) .attr('class','title') .attr('text-anchor', 'middle') .text('Tenure group for churned customers'); const visualization = bar.append('g') .attr("transform", "translate(" + margin.left + "," + (margin.top + graph_misc.ylabel) + ")"); visualization.selectAll("rect") .data(defaultBarChart) .enter() .append("rect") .attr("x", function(d, i) { return xScale(i); }) .attr("width", width / defaultBarChart.length - barPadding) .attr("y", function(d) { return yScale(d.value); }) .attr("height", function(d) { return height-yScale(d.value); }) .attr("fill", "#757077"); //Adding barchart labels visualization.selectAll('text') .data(defaultBarChart) .enter() .append("text") .text(function(d) { return d.value+"%"; }) .attr("text-anchor", "middle") .attr("x", function(d, i) { return (i * (width / defaultBarChart.length)) + ((width / defaultBarChart.length - barPadding) / 2); }) .attr("y", function(d) { return (yScale(d.value) - graph_misc.ylabel); //Setting the Y axis to represent the value in the served JSON data }) .attr("class", "yAxis"); const xLabels = bar .append("g") .attr("transform", "translate(" + margin.left + "," + (margin.top + height + graph_misc.xlabelH) + ")"); xLabels.selectAll("text.xAxis") .data(defaultBarChart) .enter() .append("text") .text(function(d) { return d.category; }) .attr("text-anchor", "middle") .attr("x", function(d, i) { return (i * (width / defaultBarChart.length)) + ((width / defaultBarChart.length - barPadding) / 2); }) .attr("y", 15) .attr("class", "xAxis"); }Copy the code

Make diagrams interactive

We have successfully created the default bar chart, but to make our project interactive, we will update the bar chart with the new value when we select the pie image.

The updateBarchart.js script will enable this functionality. It takes three parameters: the group selected on the pie chart, the color of the pie picture, and the updated bar chart data.

function updateBarChart(group, color, datasetBarChart){ const currentBarChart = get_percentage(group, datasetBarChart); //Defining chart scale, same as the default bar chart const xScale = d3.scaleLinear() .domain([0, currentBarChart.length]) .range([0, width]); const yScale = d3.scaleLinear() .domain([0, d3.max(currentBarChart, function(d) { return d.value;  })]) .range([height,0]); const bar = d3.select('#barChart svg'); //Selecting the div containing bar chart ID and creating an SVG element // Add title to Barchart bar.selectAll("text.title") .attr("x", (width + margin.left + margin.right)/2) .attr("y", graph_misc.title) .attr("class","title") .attr("text-anchor", "middle") .text("Tenure group for churned customers "+group); const visualization = d3.select('barChartPlot') .datum(currentBarChart); //binding data to multiple SVG elements visualization.selectAll('rect') .data(currentBarChart) .transition() .duration(750) .attr('x', (width + margin.left + margin.right)/2) .attr('y', graph_misc.title) .attr('class', 'title') .attr('text-anchor', 'middle') .text('Tenure group for churned customers '+group); const plot = d3.select('#barChartPlot') .datum(currentBarChart); //binding data to multiple SVG elements plot.selectAll('rect') .data(currentBarChart) .transition() //Setting bar chart change transition .duration(800) .attr('x', function(d,i){ return xScale(i); }) .attr('width', width/currentBarChart.length - barPadding) .attr('y', function(d){ return yScale(d.value) }) .attr("height", function(d) { return height-yScale(d.value); }) .attr("fill", color); plot.selectAll("text.yAxis") .data(currentBarChart) .transition() .duration(750) .attr("text-anchor", "middle") .attr("x", function(d, i) { return (i * (width / currentBarChart.length)) + ((width / currentBarChart.length - barPadding) / 2); }) .attr("y", function(d) { return yScale(d.value) - graph_misc.ylabel; }) .text(function(d) { return d.value+'%'; }) .attr("class", "yAxis"); };Copy the code

Add the style

Finally, let’s add some styles to our HTML template. The stylesheet should be linked to the index.html file and include the following styles in the styles.css static file.

/* Reset default browser settings */ /* Box sizing rules */ *, *::before, *::after { box-sizing: border-box; } /* Remove default padding and margin */ * { padding: 0; margin: 0; } /* Set core body defaults */ body { position: fixed; display: flex; background: #fdfdfd; scroll-behavior: smooth; text-rendering: optimizeSpeed; font-family: "Roboto Mono", monospace; font-weight: bold; -webkit-font-smoothing: antialiased; overflow-x: hidden; } /* Make images easier to work with */ img { max-width: 100%; display: block; } .about { margin: 10% 2%; width: 40%; text-align: justify; } h1 { text-decoration: underline; Margin: 0.5 em 0 em; } p, h2, h6 {margin: 0.7em 0em; } a { text-decoration: none; } .visualization { display: flex; align-items: center; flex-direction: column; width:60%; } #pieChart { margin-top: 4em; font-size: 12px; } #barChart { font-size: 9px; margin-top: 4em; } #pieChart .title, #barChart .title{ font-weight: bold; } .slice { font-size: 8px; font-family: "Roboto Mono", monospace; fill: white; font-weight: bold; cursor: pointer; }Copy the code

The interactive data dashboard has been successfully set up and finally, we will run our Flask application on the terminal, as shown below.

python run.py

Copy the code

The web application will be hosted on our Localhost :5000 and can be accessed from any browser.

conclusion

In this article, we have shown how to use Python’s Flask services and pre-processed data to build an interactive chart dashboard. We manipulated DOM elements and rendered visualizations in Javascript d3.js on the web page. You can use this technique to render bar charts or pie charts and easily incorporate data visualization into your next project.

Building interactive charts with Flask and D3.js appears on the LogRocket blog.