0

I've been working on getting a Coefficient of Variation equation ported from PHP to Javascript, but can't seem to get it working.

Original PHP script:

// define variables, strip spaces
$weights = $_POST['weights'];
// define coefficient of variation function
function cv($array){
    $n = 0;
    $mean = 0;
    $M2 = 0;
    foreach($array as $x){
        if ($x != NULL AND $x != '') {
            $n++;
            $delta = $x - $mean;
            $mean = $mean + $delta/$n;
            $M2 = $M2 + $delta*($x - $mean);
            $total = $total + $x;
        }
    }
    return (((sqrt($M2/($n - 1))) / ($total/$n))*100);
}
$cv = (cv($weights));

This basically takes an array, and figures out the coefficient of variation for it. Now as I try to convert it to Javascript via some Jquery function:

var fields = $('#cvform').serializeArray();
        var count = 0;
        var num = 0;
        var mean = 0;
        var m2 = 0;
        var total = 0;
        var delta = 0;
        jQuery.each(fields, function(i, field){
        if (field.value > 0) {
            num++;
            delta=(field.value-mean);
            mean=(mean+delta/num);
            m2=(m2+delta*(field.value-mean));
            total=(total+field.value);
        };
        });
        var cov=(((Math.sqrt(m2/(num-1)))/(total/num))*100);
        $("<span>Coefficient of Variation: " + cov +  "</span>").appendTo('#cvdisplay');

While the javascript function outputs an answer, it is not correct. If I enter the values "3,3,2,3,3,4" the PHP script gives an output of 21.08, which is correct. The javascript function gives me the value of 0.0011418432035849642.

Can anyone point me to where the equations are differing?

4
  • 2
    One thing to try: explicitly convert "field.value" into a number before messing with it. var v = 1 * field.value; for example. Commented Aug 4, 2011 at 21:53
  • @Pointy is right: jsfiddle.net/qvj3U (If you change the fields variable to var fields = ["3","3","2","3","3","4"]; you get the wrong answer.) Commented Aug 4, 2011 at 21:59
  • As far as I know, serializeArray() produces string values, so a float/integer conversion is necessary Commented Aug 4, 2011 at 22:26
  • I definitely just overlooked this. No idea how I missed it, even though I skimmed over the code plenty of times. Thanks! Commented Aug 5, 2011 at 5:17

5 Answers 5

1

You need to convert your array values to floats via parseFloat() (or integers, parseInt(), whatever suits you):

var fields = $('#cvform').serializeArray();
var count = 0;
var num = 0;
var mean = 0;
var m2 = 0;
var total = 0;
var delta = 0;
$.each(fields, function(i, field) {
    alert(field.value);
    if (parseFloat(field.value) > 0) {
        num++;
        delta = (parseFloat(field.value) - mean);
        mean = (mean + delta / num);
        m2 = (m2 + delta * (parseFloat(field.value) - mean));
        total = (total + parseFloat(field.value));
    }
});
var cov = (((Math.sqrt(m2 / (num - 1))) / (total / num)) * 100);
$("<span>Coefficient of Variation: " + cov +  "</span>").appendTo('#cvdisplay');
Sign up to request clarification or add additional context in comments.

Comments

1

The issue is the javascript line total=(total+field.value); which results in 0332334 instead of 18 as expected. String concatenation is being applied instead of numeric addition.

You can fix this by parsing the integer value: total += parseInt(field.value);

Comments

0
function stDeviation(array){
    var L= array.length,
    mean= array.reduce(function(a, b){
        return a+b;
    })/L,
    dev= array.map(function(itm){
        var tem= itm-mean;
        return tem*tem;
    });
    return Math.sqrt(dev.reduce(function(a, b){
        return a+b;
    })/L);
}

Math.mean= function(array){
    return array.reduce(function(a, b){ return a+b; })/array.length;
}
Math.stDeviation=function(array){
    var mean= Math.mean(array);
    dev= array.map(function(itm){return (itm-mean)*(itm-mean); });
    return Math.sqrt(dev.reduce(function(a, b){ return a+b; })/array.length);
}


var A2= [6.2, 5, 4.5, 6, 6, 6.9, 6.4, 7.5];
alert ('mean: '+Math.mean(A2)+'; deviation: '+Math.stDeviation(A2))

Comments

0

Here, I changed the code around a bit and got it to work. I basically isolated the segments a bit more. Made it more direct.

<?php
$weights = Array(3,3,2,3,3,4);

// define coefficient of variation function
function cv($array) {
  $n = 0;
  $mean = 0;
  $M2 = 0;
  $total = 0;

  foreach($array as $x) {
    if ( !empty($x) ) {
      $n++;
      $delta = $x - $mean;
      $mean = $mean + $delta / $n;
      $M2 = $M2 + $delta * ($x - $mean);
      $total = $total + $x;
    }
  }

  $sqrt = sqrt( $M2 / ($n - 1) );
  $tn = $total / $n;
  echo "Sqrt is $sqrt tn is $tn";

  return ( $sqrt / $tn ) * 100;
}

$cv = cv($weights);
?>
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
  var fields = Array(3,3,2,3,3,4);
  var count = 0;
  var n = 0;
  var mean = 0;
  var m2 = 0;
  var total = 0;
  var delta = 0;

  jQuery.each(fields, function(i, field) {
    //var x = field.value;
    var x = 1 * field;

    if (x > 0) {
      n++;
      delta = (x - mean);
      mean = (mean + (delta / n));
      m2 = (m2 + delta * (x - mean));
      total = (total + x);
    };
  });

  var sqrt = Math.sqrt(m2 / (n - 1));
  var tn = total / n;
  var cov = ((sqrt / tn) * 100);
  console.log("Total is: "+ total);
  console.log("Sqrt is " + sqrt + " tn is " + tn + " cov is " + cov);

  $('#js').text("JS Output is: " + cov);
});
</script>
</head>
<body>
  <div>
    <div>PHP Output: <?=$cv;?></div>
    <div id="js"></div>
  </div>
</body>
</html>

Comments

-1

Here's a direct translation of the function you provided. You have to pass in a javascript array of numbers, and it produces the result you're looking for (at least according to unit tests written in Node.js). You should to the type conversion (string-to-array) in another function to separate your concerns and make the code easier to reason about;

var CoV = function(ary) {
    var mean = 0,
        n = 0,
        m2 = 0,
        total = 0,
        delta;

    for(var i = 0, l = ary.length; i < l; i += 1) {
        n += 1;

        delta = ary[i] - mean;
        mean = mean + delta / n;
        m2 = m2 + delta * (ary[i] - mean)
        total = total + ary[i]
    }

    console.log(mean);
    console.log(m2);
    console.log(total);

    return ((Math.sqrt(m2/(i - 1))) / (total / i)) * 100;
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.