40

I am trying to get the standard deviation of a user input string. I have as follows, but it returns the wrong value for SD. The calculation should go as follows: Sum values/number values = mean Square (sum each value-mean) Sum squares/number values.

Assistance appreciated (and explanation if possible):

function sum() {
  var val = document.getElementById('userInput').value;
  var temp = val.split(" ");
  var total = 0;
  var v;
  var mean = total / temp.length;
  var total1 = 0;
  var v1;
  var temp23;
  var square;

  for (var i = 0; i < temp.length; i++) {
    v = parseFloat(temp[i]);
    total += v;
  }

  mean = total / temp.length;

  for (var i = 0; i < temp.length; i++) {
    v1 = parseFloat(Math.pow(temp[i] - mean), 2);
    total1 += v1;
  }


  temp23 = total1 / temp.length;
  square = Math.sqrt(temp23);

  document.write(total + '<br />');
  document.write(mean + '<br />');
  document.write(square);
}
<html>

<head>
</head>

<body>
  <form id="input">
    <textarea id="userInput" rows=20 cols=20></textarea>
    <input id="Run" type=Button value="run" onClick="sum()" />
  </form>
</body>

</html>
fodma1
  • 3,485
  • 1
  • 29
  • 49
tc03
  • 455
  • 2
  • 5
  • 8
  • 2
    Strings don't have a standard deviation. Do you mean the standard deviation of a series of numbers contained in a string? – nnnnnn Sep 08 '11 at 06:29

10 Answers10

69

Shorthand method for getting standard deviation from an array if you don't like lots of code:

function getStandardDeviation (array) {
  const n = array.length
  const mean = array.reduce((a, b) => a + b) / n
  return Math.sqrt(array.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n)
}
zingi
  • 1,141
  • 13
  • 23
Foxcode
  • 1,279
  • 11
  • 10
  • 6
    `if (!array || array.length === 0) {return 0;}` to avoid that divide by 0 – Josh Hibschman Jun 03 '21 at 17:33
  • 1
    Thoughts after 2 years of posting this: If you only need to calculate a few SDs at a time, go ahead and use this (actually use "(x - mean) ** 2" instead of Math.pow to make the code even shorter :P), you're welcome. However, beware, performance-wise, as it stands, using for loops is much faster in all browsers. So if you have a huge data-set (like >100k arrays) and need to calculate the SDs of them all at once, or if you really just want to save those milliseconds anyway, I'd recommend using for loops instead of the .reduce and .map methods. – Foxcode Jun 19 '22 at 19:30
9

For anyone looking for a more generic solution, here's a standard deviation function added to the Array#. The function expects to be called on an array of numbers.

Array.prototype.stanDeviate = function(){
   var i,j,total = 0, mean = 0, diffSqredArr = [];
   for(i=0;i<this.length;i+=1){
       total+=this[i];
   }
   mean = total/this.length;
   for(j=0;j<this.length;j+=1){
       diffSqredArr.push(Math.pow((this[j]-mean),2));
   }
   return (Math.sqrt(diffSqredArr.reduce(function(firstEl, nextEl){
            return firstEl + nextEl;
          })/this.length));
};
cssimsek
  • 1,255
  • 14
  • 20
  • Even though this answer is quite old already, I still think that it is worth noting that extending standard prototypes is considered a bad practice in js (or anywhere else). Try to avoid this approach and use a function that expects an array as a parameter instead – Phelizer May 09 '23 at 14:17
8

This ES6 implementation matches Excel's built in STDEV.P and STDEV.S (.S is the default when STDEV is called). Passing in the true flag for usePopulation here will match Excel's STDEV.P

const standardDeviation = (arr, usePopulation = false) => {
  const mean = arr.reduce((acc, val) => acc + val, 0) / arr.length;
  return Math.sqrt(
    arr.reduce((acc, val) => acc.concat((val - mean) ** 2), []).reduce((acc, val) => acc + val, 0) /
      (arr.length - (usePopulation ? 0 : 1))
  );
};

console.log('STDEV.S =>',
  standardDeviation([
    10, 2, 38, 23, 38, 23, 21
  ])
);

console.log('STDEV.P =>',
  standardDeviation([
    10, 2, 38, 23, 38, 23, 21
  ], true)
);

Original Source

Vinnie James
  • 5,763
  • 6
  • 43
  • 52
6

I think the (main) problem is on this line:

v1 = parseFloat(Math.pow(temp[i]-mean),2);

Should be:

v1 = Math.pow(parseFloat(temp[i])-mean,2);

Your code is trying to use the string in temp[i] as a number and subtract mean from it, and then square it, and then parse the resulting value. Need to parseFloat before using it in a calculation. Also you've got the ,2 outside the closing parenenthesis for the Math.pow call so the squaring won't work either.

Would be helpful to use more meaningful variable names too, I mean, e.g., you have a variable called "square" that holds the result of a square-root operation.

P.S. You need to add some error checking in case the user enters non-numeric data. Check that the result of parseFloat() is not NaN. I'd be inclined to do an initial loop through the array parsing and checking for valid numbers, storing the parsed numbers in a second array (or writing them back to the first array), and if any are invalid give the user an error message at that point and stop. Then in your actual calculations you don't have to worry about parsing as you go (or, in your case, parsing again in the second loop).

nnnnnn
  • 147,572
  • 30
  • 200
  • 241
  • ```v1 = Math.pow(parseFloat(temp[i])-mean,2);``` Note this is corrected version of the above (there's an extra bracket in the accepted answer). Edit queue is full, so posting as a comment. – Pog Le Pog Oct 21 '22 at 16:53
  • @PogLePog - Thanks. Fixed. Although looking at it now, I don't think `parseFloat()` is needed at all, the `-` operator converts both its operands to numbers. (But either way, as mentioned in my answer, user input should be validated before use in calculations.) – nnnnnn Oct 23 '22 at 07:58
5

function getStandardDeviation(numbersArr) {
    // CALCULATE AVERAGE
    var total = 0;
    for(var key in numbersArr) 
       total += numbersArr[key];
    var meanVal = total / numbersArr.length;
    // CALCULATE AVERAGE
  
    // CALCULATE STANDARD DEVIATION
    var SDprep = 0;
    for(var key in numbersArr) 
       SDprep += Math.pow((parseFloat(numbersArr[key]) - meanVal),2);
    var SDresult = Math.sqrt(SDprep/(numbersArr.length-1));
    // CALCULATE STANDARD DEVIATION

    return SDresult;       
}

var numbersArr = [10, 11, 12, 13, 14];
alert(getStandardDeviation(numbersArr));
Scott
  • 3,736
  • 2
  • 26
  • 44
Berk Kanburlar
  • 260
  • 2
  • 11
  • 2
    Correction: This code is for the uncorrected standard deviationl; for the correct standard deviation use "var SDresult = Math.sqrt(SDprep/(numbersArr.length-1));" instead of "var SDresult = Math.sqrt(SDprep/numbersArr.length);" – user1097111 Mar 20 '18 at 19:49
1

Standard Deviation is quite simple.

  1. Calcuate the mean
  2. Square the subtraction of each number against the mean. Average these new numbers again to get the variance.
  3. Finally square root the variance to get the standard deviation.
function standardDeviation(numArray) {
  const mean = numArray.reduce((s, n) => s + n) / numArray.length;
  const variance = numArray.reduce((s, n) => s + (n - mean) ** 2, 0) / (numArray.length - 1);
  return Math.sqrt(variance);
}

if you have a array of strings, remember to convert array of floats first array.map(s => parseFloat(s))

frogcoder
  • 963
  • 1
  • 7
  • 17
  • 1
    It does not produce same result as mathjs. To do that, you have to fix the `numArray.length` by `numArray.length -1` in the variance part. – throrin19 Aug 17 '21 at 08:02
  • @throrin19 Thanks for the correction. I will fix it by subtracting one from length. – frogcoder Aug 17 '21 at 10:07
1

You can use mathjs library

math.std(array)
tal weissler
  • 177
  • 1
  • 9
0

Quick implementation of the standard deviation function:

const sd = numbers => {
  const mean = numbers.reduce((acc, n) => acc + n) / numbers.length;
  return Math.sqrt(
    numbers.reduce((acc, n) => (n - mean) ** 2) / numbers.length
  );
};

Corrected SD version:

const correctedSd = numbers => {
  const mean = numbers.reduce((acc, n) => acc + n) / numbers.length;
  return Math.sqrt(
    numbers.reduce((acc, n) => (n - mean) ** 2) / (numbers.length - 1)
  );
};

Ibraheem
  • 2,168
  • 20
  • 27
  • This does not produce a correct Standard Deviation. I just tested the output of this function vs. what LibreOffice Calc's `stddev()` function produces, and this answer did not match. Whereas @Foxcode's answer above _does_ produce a matching result: https://stackoverflow.com/a/53577159/1024832. Math.js `std()` function disagrees w/the result of this implementation too. – Marc Jun 21 '20 at 04:52
  • Then remove the length - 1 part and use just length. That would give you the uncorrected SD. – Ibraheem Jun 21 '20 at 08:44
0

I think you almost got it right. It is always good to group your computations with brackets.

This is your code:

v1 = parseFloat(Math.pow(temp[i]-mean),2);

Correction:

v1 = parseFloat(Math.pow((temp[i]-mean),2)); // (temp[i] - mean) all squared
Nodrok
  • 31
  • 3
-1

This function works and produces the same result as numjs

const st = (numbers) => {
  const mean = numbers.reduce((acc, item) => acc + item) / numbers.length;
  return Math.sqrt(numbers.reduce((acc, item) => acc + Math.pow((parseFloat(item) -mean), 2)))
}
  • I did test this function and it does not give you the standard deviation. [The answer](https://stackoverflow.com/a/53577159/9135945) from @Foxcode works. – zingi Jul 22 '20 at 11:26