Bell Curve / Normal Disribution Curve On A NVD3 Discrete Bar Chart


I am trying to figure out a way to get a distribution curve / bell curve onto a NVD3 chart. I have searched the net a lot and found literally nothing to work with what I've got. Maybe it isn't actually possible, but thought It was worth asking and also good to know for anyone else who would looks for something like this.

This is an example of what I need my graph to look like (Found on google images)

enter image description here

As you can see from the example, the line doesn't need a second axis, so there would be no need for a 'bar and line chart combo'. I know you can draw directly onto the canvas with D3 but I'm a little under experienced with that.

And Here is my code and jsfiddle If anyone would like to look

var json = [{ "values": [{"label":"450-456", "value":0, "color":"#D62728"},{"label":"456-462", "value":0, "color":"#D62728"},{"label":"462-468", "value":0, "color":"#D62728"},{"label":"468-474", "value":0, "color":"#D62728"},{"label":"474-480", "value":0, "color":"#D62728"},{"label":"480-486", "value":1, "color":"#D62728"},{"label":"486-492", "value":5, "color":"#D62728"},{"label":"492-498", "value":3, "color":"#D62728"},{"label":"498-504", "value":5, "color":"#D62728"},{"label":"504-510", "value":6, "color":"#D62728"},{"label":"510-516", "value":9, "color":"#D62728"},{"label":"516-522", "value":6, "color":"#D62728"},{"label":"522-528", "value":1, "color":"#D62728"},{"label":"528-534", "value":0, "color":"#D62728"},{"label":"534-540", "value":0, "color":"#D62728"},{"label":"540-546", "value":0, "color":"#D62728"},{"label":"546-552", "value":0, "color":"#D62728"},{"label":"552-558", "value":0, "color":"#D62728"},{"label":"558-564", "value":0, "color":"#D62728"},{"label":"564-570", "value":0, "color":"#D62728"}]}];




                nv.addGraph(function() {
                var chart = nv.models.discreteBarChart()
                .x(function(d) {
                return d.label
                })
                .y(function(d) {
                return d.value
                })  
                .staggerLabels(true)

                .tooltips(true)
                .showValues(true)
                .transitionDuration(250)
                ;

                chart.yAxis
                .tickFormat(d3.format('.0f'))
                chart.valueFormat(d3.format('d'));
               // REMOVE DECIMAL PLACES FROM Y AXIS
                chart.forceY([0,10]);

                d3.select('#chartDisribution svg')
                .datum(json)
                .call(chart);

                d3.select('#chartDisribution svg')
                .append("text")
                .attr("x", '50%')             
                .attr("y", 10)
                .attr("text-anchor", "middle")
                .style("font-size", "14px") 
                .style("text-decoration", "underline")
                .style("font-weight", "bold")
                .style("height", "20px")     
                .text("DISRIBUTION");

                nv.utils.windowResize(chart.update);
                return chart;
                });    

Original data before sorting for disribution -

[518, 514, 512, 514, 518, 498, 510, 516, 520, 508, 504, 504, 517, 494, 492, 491, 515, 507, 492, 527, 509, 500, 491, 506, 517, 516, 518, 505, 514, 486, 516, 504, 503, 490, 515, 498]

If any more info is needed please ask.

Thank you


Answers:


As @Altocumulus stated, you can use the LinePlusBarChart.

There is a lot of info out there on how to do this so I will explain only how to generate the data for the normal distribution.

The following formula is used to create the data: Wikipedia Source

enter image description here

So what we need is the Standard Deviation and Average from the total data set and the bins you are using for the histogram.

I won't go into detail on how to calculate the Average and Standard Deviation from your data set but the code to do this is included in the fiddle below.

We first declare the data set and the bins:

var values = [518, 514, 512, 514, 518, 498, 510, 516, 520, 508, 504, 504, 517, 494, 492, 491, 515, 507, 492, 527, 509, 500, 491, 506, 517, 516, 518, 505, 514, 486, 516, 504, 503, 490, 515, 498];

var bins=[450, 456, 462, 468, 474, 480, 486, 492, 498, 504, 510, 516, 522, 528, 534, 540, 546, 552, 558, 564]

the mean and the std for this data set are as follows:

var average = 507.1944444
var std = 10.43022927

With this data we can calculate the first part of the formula:

var ni1 = 1 / (std * Math.sqrt(2 * Math.PI));

The second part of the formula uses the bins so we then loop through each bin to calculate the values for the normal curve.

To scale the normal values to the chart, we need the size between each bin and we need the total number of values.

(I have also added a variable called norm but we only use this to output the results to the screen)

var norm ="norm data"
var length = bins.length;
var bin_distance = 6;
var num_records = 36;

for(var i=0;i<length;i++){
    // This is the second part of the formula 
    var ni2 = Math.exp(-1*((bins[i]-average)*(bins[i]-average))/(2* (std*std))) 

    // this is the final calculation for the norm values. I also rounded the value but thats up to you if you want, you can remove this for unrounded values.   
    var normdata = Math.round(ni1*ni2*bin_distance*num_records);

    //append value to norm to output to screen
    norm = norm +"<p>"+ bins[i]+" - "+normdata+"</p>"
}

//output results to screen: 
document.getElementById('chartDisribution').innerHTML = norm;   

Here is a working fiddle.

For your chart, you would want to push the normdata value into the object you have declared for your line.