Highcharts – deeper practice with jquery

Highcharts – deeper practice with jquery

8 104730
Highcharts - deeper practice for real statistics

Highcharts – deeper practice for real statistics

Everyone of us can face with a situation, when we need to handle with some statistics. Me too, I was going to make a short review on how to build active charts (diagrams), and in the long run I built quite actual statistical graphs, which initially are presented as CSV files. I found this statistics here, where I chose a couple of interesting tables: Marital Status and Educational Attainment. I developed the script that is able to select data from one or even multiple tables (of a CSV file), drawing various graphs for analyzing. In today’s article I will show you an interesting method of parsing CSV files, and displays the results in graphs using Highcharts.

Live Demo
Live Demo 2

[sociallocker]

download in package

[/sociallocker]


Ok, let’s download the source files and start coding !


Step 1. HTML

In the most beginning we have to include all necessary styles scripts in the header section:

<!-- add styles -->
<link href="css/main.css" rel="stylesheet" type="text/css" />
<!-- add scripts -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.js"></script>
<script src="js/highcharts.js"></script>
<script src="js/skies.js"></script>
<script src="main.js"></script>

We included jQuery and highcharts libraries, one small CSS file to stilize our page, and one main.js file with our own custom code. Rest of the code can looks like this:

<!-- Various actions -->
<div class="actions">
    <button class="switcher" id="2">Get stat #2 (Both)</button>
    <button class="switcher" id="19">Get stat #19 (Males)</button>
    <button class="switcher" id="36">Get stat #36 (Females)</button>
    <button class="switcher" id="multiple">Range</button>
</div>
<!-- Chart container object -->
<div id="container" class="chart"></div>

There are just several buttons (to evoke different events) and the container for chart object.

Step 2. CSS

css/main.css

All we need is to add few styles for our buttons:

.actions, .chart {
    margin: 15px auto;
    width: 1000px;
}
button {
    background: none repeat scroll 0 0 #E3E3E3;
    border: 1px solid #BBBBBB;
    border-radius: 3px 3px 3px 3px;
    box-shadow: 0 0 1px 1px #F6F6F6 inset;
    color: #333333;
    font: bold 12px;
    margin: 0 5px;
    padding: 8px 0 9px;
    text-align: center;
    text-shadow: 0 1px 0 #FFFFFF;
    width: 150px;
}
button:hover {
    background: none repeat scroll 0 0 #D9D9D9;
    box-shadow: 0 0 1px 1px #EAEAEA inset;
    color: #222222;
    cursor: pointer;
}
button:active {
    background: none repeat scroll 0 0 #D0D0D0;
    box-shadow: 0 0 1px 1px #E3E3E3 inset;
    color: #000000;
}

Step 3. JS

Now, we can overview the final JS code and I will explain how it works:

main.js

// on document ready
$(document).ready(function() {
   // prepare an empty Highcharts object
   var chart = new Highcharts.Chart({
        chart: {
            renderTo: 'container',
            showAxes: true,
            height: 700
        },
        title: {
            text: 'Marital Status (United States: 2011)',
        },
        credits: {
            text: 'By Script Tutorials'
        },
        xAxis: {
            title: {
               text: 'Categories'
            }
        },
        yAxis: {
            title: {
               text: 'Amount'
            }
        }
     });
    $('.switcher').click(function () {
        var id = $(this).attr('id');
        // display Loading message
        chart.showLoading('Getting stat data ....');
        if (id != 'multiple') {
            // get stat data (json)
            $.getJSON('data.php?id=' + id, function(aData) {
                // remove all existing series
                while (chart.series.length) {
                    chart.series[0].remove();
                }
                // re-set categories for X axe
                var categories = [];
                $.each(aData.categories, function(idx, res) {
                    categories.push(res);
                });
                chart.xAxis[0].setCategories(categories);
                chart.yAxis[0].axisTitle.attr({
                    text: 'Amount of ' + aData.name + 's (thousands)'
                });
                // gather data (values) and prepare a new Series array
                var seriesValData = [];
                $.each(aData.values, function(idx, res) {
                    seriesValData.push([res.name, parseFloat(res.val)]);
                });
                var seriesValues = {
                    name: aData.name,
                    data: seriesValData,
                    type: 'column'
                }
                // gather data (percentages) and prepare a new Series array
                var seriesPerData = [];
                $.each(aData.percentages, function(idx, res) {
                    seriesPerData.push([res.name, parseFloat(res.val)]);
                });
                var seriesPercentages = {
                    name: aData.name + ' (%)',
                    data: seriesPerData,
                    type: 'pie',
                    size: '40%',
                    center: ['60%', '30%'],
                    showInLegend: true
                }
                // hide Loading, add both series and redraw our chart
                chart.hideLoading();
                chart.addSeries(seriesValues, false);
                chart.addSeries(seriesPercentages, false);
                chart.redraw();
            });
        } else {
            // get stat data (json)
            $.getJSON('data2.php', function(aData) {
                // remove all existing series
                while (chart.series.length) {
                    chart.series[0].remove();
                }
                // re-set categories for X axe
                var categories = [];
                $.each(aData.categories, function(idx, res) {
                    categories.push(res);
                });
                chart.xAxis[0].setCategories(categories);
                chart.yAxis[0].axisTitle.attr({
                    text: 'Percentage'
                });
                // hide Loading
                chart.hideLoading();
                $.each(aData.series, function(idx, ser) {
                    // gather data (percentages) and prepare a new Series array
                    var seriesValData = [];
                    $.each(ser.values, function(idx, res) {
                        seriesValData.push([res.name, parseFloat(res.val)]);
                    });
                    var seriesValues = {
                        name: ser.name,
                        data: seriesValData,
                        type: 'column',
                    }
                    // and add the series into chart
                    chart.addSeries(seriesValues, false);
                });
                // add both series and redraw our chart
                chart.redraw();
            });
        }
    });
});

In the beginning (on document ready) our script prepares an empty Highcharts object (where we can put initial params like various titles and height. After, I added onclick event handler for our buttons. First three buttons are to generate sumple charts (using only one table from CSV file). The last one button is to obtain range (which is predefined by me). This range is a range between tables 3 and 17. I will explain the structure of tables soon (in next step). Well, when we click to the one of buttons, we send a request ($.getJSON) to data.php file, this file parses CSV file and returns a JSON response to us. And now we can: remove all existing series of our Chart object (series – means some data here), then we parse all categories from server response and add own custom categories for X axis, then we parse all stat data in order to prepare a new object (of series) for Chart. In case if we request for a single table, our script (data.php) returns categories, values (amounts of people) and percentages. For values, I use ‘column’ chart, for percentages – ‘pie’. In case when we request for a whole range, we age going to use only percentages (for every used table).


Now, I think that I have to explain a bit about structure of provided CSV files. I downloaded two files (2011gender_table10.csv and 2011gender_table11.csv) from census.gov website. I’m pretty sure that this is official statistics of US. Every file, consists of some comments (I noticed, that first 4 rows as usual – comments, which explain content of this file) and various ‘tables’ with necessary content. Few weeks ago I located the very good PHP parser for CSV files (http://code.google.com/p/parsecsv-for-php/). So, I decided to use it for today’s demos. And, the result surpassed all expectations! This library was able to skip all these comments in the beginning of CSV file (using ‘offset’ param). In the result, I got a set of various tables (after it parses CSV files). In case of selected CSV files, I noticed, that few two tables contain set of ‘categories’ (like Married, Widowed, Divorced, Separated and Never married) and types of information for next tables. All other tables contain actual statistics.

As example, in ‘Marital status’ table (2011gender_table10.csv) you can find information about Marital Status of the Population 15 Years and Over by Sex and Age (US, 2011).

In ‘Educational Attainment’ table (2011gender_table11.csv) you can find information about Educational Attainment of the Population 15 Years and Over by Sex and Age (US, 2011).

Step 4. PHP

Now, the final step – where I will tell you how to make a server-side script to prepare data for our JS code:

data.php

function splitByPairs($array) {
    $odd = array();
    $even = array();
    $both = array(&$even, &$odd);
    array_walk($array, function($v, $k) use ($both) { $both[$k % 2][] = $v; });
    return array($odd, $even);
}
require_once('parsecsv.lib.php');
// select filename
$sFilename = (isset($_GET['extra'])) ? '2011gender_table11.csv' : '2011gender_table10.csv';
// parse CSV
$csv = new parseCSV($sFilename, 4); // 4 is offset (of rows with comments)
// get all categories from the first table
$aCats = array();
foreach ($csv->data[0] as $s) {
    if ($s = trim($s)) {
        $aCats[] = $s;
    }
}
// get stat number (ID)
$i = (int)$_GET['id'];
// Get exact Stat info by ID
$sTitle = $csv->data[$i]['Sex and age'];
$aDataSlc = array_slice($csv->data[$i], 3); // we can remove (slice) first three fields (they contain title and total information)
$aData = array();
foreach ($aDataSlc as $s) {
    $aData[] = $s;
}
// separate $aData array to odd and even pairs
list($aPerc, $aVals) = splitByPairs($aData);
// prepare separated arrays of amounts and percentages with category names
$i = 0;
$aValRows = $aPercRows = array();
foreach ($aCats as $s) {
    $fValue = str_replace(',', '.', $aVals[$i]);
    $fValue = ((float)$fValue) ? (float)$fValue : 0;
    $aValRows[] = array('name' => $s, 'val' => $fValue);
    $fPercent = str_replace(',', '.', $aPerc[$i]);
    $fPercent = ((float)$fPercent) ? (float)$fPercent : 0;
    $aPercRows[] = array('name' => $s, 'val' => $fPercent);
    $i++;
}
// echo JSON data
$aJson = array();
$aJson['name'] = trim($sTitle);
$aJson['categories'] = $aCats;
$aJson['values'] = $aValRows;
$aJson['percentages'] = $aPercRows;
echo json_encode($aJson);

In the beginning of this script, you can see that we use ‘parsecsv.lib.php’ library to parse the one of CSV files. Then we walk (foreach) through first table in order to obtain categories (of statistics). After, we extract a requested table (by ID) and gather all it’s fields (except for the first three fields, They contain title of table, total amount and total percentage). Then, we have to split result array with data to odd and even values (all because there are mix of Valies and Percentages). I made the special function to separate arrays: splitByPairs. Finally, we can prepare final separated arrays with values (amounts of people) and percentages for our JS script.

Now please pay attention that I use another one ‘data2.php’ file to prepare data for range of tables, here it is:

data2.php

function splitByPairs($array) {
    $odd = array();
    $even = array();
    $both = array(&$even, &$odd);
    array_walk($array, function($v, $k) use ($both) { $both[$k % 2][] = $v; });
    return array($odd, $even);
}
require_once('parsecsv.lib.php');
// select filename
$sFilename = (isset($_GET['extra'])) ? '2011gender_table11.csv' : '2011gender_table10.csv';
// parse CSV
$csv = new parseCSV($sFilename, 4); // 4 is offset (of rows with comments)
// get all categories from the first table
$aCats = array();
foreach ($csv->data[0] as $s) {
    if ($s = trim($s)) {
        $aCats[] = $s;
    }
}
// prepare array of series (range of tables)
$iStart = 3;
$iEnd = 17;
$aSeries = array();
for ($x = $iStart; $x <= $iEnd; $x++) {
    // Get exact Stat info by ID
    $sTitle = $csv->data[$x]['Sex and age'];
    $aDataSlc = array_slice($csv->data[$x], 3); // we can remove (slice) first three fields (they contain title and total information)
    $aData = array();
    foreach ($aDataSlc as $s) {
        $aData[] = $s;
    }
    // separate $aData array to odd and even pairs
    list($aPerc, $aVals) = splitByPairs($aData);
    // prepare separated array of percentages with category names
    $i = 0;
    $aPercRows = array();
    foreach ($aCats as $s) {
        $fPercent = str_replace(',', '.', $aPerc[$i]);
        $fPercent = ((float)$fPercent) ? (float)$fPercent : 0;
        $aPercRows[] = array('name' => $s, 'val' => $fPercent);
        $i++;
    }
    $aSeries[] = array(
        'name' => trim($sTitle),
        'values' => $aPercRows
    );
}
// echo JSON data
$aJson = array();
$aJson['categories'] = $aCats;
$aJson['series'] = $aSeries;
echo json_encode($aJson);

This is similar script as our first ‘data.php’. But, we don’t extract data from only one table, but we use a range of tables (from 3 to 17 tables). And, we don’t use values (amounts), but only Percentages. I think that it is enough for our demo.


Live Demo
Live Demo 2

Conclusion

That is all for today. We have just created the really interesting and actual charts (with Highcharts) of real marital and educational statistics of 2011 (for US). I’m sure that this material will be very useful for you. Good luck and welcome back

There are two great places where you can read about Highcharts library: Official website and ‘Learning Highcharts’ book

SIMILAR ARTICLES

Understanding Closures

0 23115

8 COMMENTS

  1. Good tutorial exelent help me very much

    can you make this but with a database conection please

    • Hello Frank,
      Thank you for your comment, I will try to prepare another new article about it, because in your case we will need to rewrite our ‘data.php’ completely.

      • Hey There,

        Firstly,,thanks for the great share of knowledge, you have really helped me with many aspect of my current project.

        I was tasked to develop a web “Data Visualization” server that will take consistent data via ftp push and convert that data into visual charts.

        I looked and looked, and D3 and Highcharts came up top. But, by looking at the needs,of my client (perhaps only 2 charts on a page, in 2D), I opted for highcharts.

        Now, I’m not really much of a JavaScript fundi, and although I have extensive PHP and MySQL experience, I’m just battling to bring them together!

        What I need to do is populate a MySQL database automatically from all the csv files coming into the server. It does get a bit more detailed than that, but if you are up for the challenge and wanna help a js noobie out… Please give me a “holler”!

  2. Hi Andreas,
    The most logical what comes to my mind is to create CSV parser with PHP (in order to populate the mySQL database), and then you can use the HighCharts library to display the data on the client side.

  3. For my project I wanted to use a button to insert csv files into my highchart, and i see that you found a way. I have no knowlegde of php, so I hope that you can help me. I have about 6 small csv files. I tried to to read the code but you lost me at the php part. I hope that you can help me a bit. I only need the column charts and I dont need the percentages. Does that meen i can remove the data2?

    • Hi Jo,
      Yes, this project can import CSV files and display a certain result using highcharts. However, I think that it would be difficult to use if without any knowledges of php and javascript. Because you also need to know the exact structure of your csv files.

  4. HI.
    I would like to do with that the graph enters in a page with other information for me posted, not to be alone in the page in that it was built.

    thanks.

Leave a Reply