JavaScriptのArrayクラスのメソッドで要素の標準偏差を求める

JavaScriptの`Array`クラスにはECMAScript 5.1以降で、配列要素を操作する新しいメソッドが備わっています。その中から[`Array.reduce()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)と[`Array.map()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/map)の使い方として、配列要素の標準偏差を求めてみましょう。配列要素を`for`文で取り出して計算するより、すっきりと導けます。

#標準偏差とは
「[標準偏差](https://ja.wikipedia.org/wiki/%E6%A8%99%E6%BA%96%E5%81%8F%E5%B7%AE)」は、データのばらつきを表す指標です。まず平均を計算したうえで、各データと平均値の差を2乗して要素数で割った「分散」を求めます(「[【統計学】初めての「標準偏差」（統計学に挫折しないために）](http://qiita.com/kenmatsu4/items/e6c6acb289c02609e619)」参照)。

```math
 分散 = \frac{(データ1 - 平均)^2 + (データ2 - 平均)^2+ … +(データn - 平均)^2}{n}
```
そうして得た分散の正の平方根が「標準偏差」で、記号${\sigma}$(シグマ)を使って表します。データの分布の基本的なモデルとされる「[正規分布](https://ja.wikipedia.org/wiki/%E6%AD%A3%E8%A6%8F%E5%88%86%E5%B8%83)」では、平均値±${\sigma}$の値の範囲にデータの7割弱が含まれます(図001)。

```math
標準偏差 = \sqrt{分散}
```

####図001■正規分布と標準偏差
![Standard deviation diagram.svg](https://upload.wikimedia.org/wikipedia/commons/8/8c/Standard_deviation_diagram.svg)
[\>\> Widipediaより引用](https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:Standard_deviation_diagram.svg)

この標準偏差を使ったのが、テストの「[偏差値](https://ja.wikipedia.org/wiki/%E5%81%8F%E5%B7%AE%E5%80%A4)」です。平均を50とし、そこからの差は標準偏差$\sigma$を10として測ります。すると、平均値にかかわらず、データ全体の中の位置づけが得られるのです。偏差値60以上は15%ほど、70を超えるのは2%ちょっとということになります。

#配列要素の平均を求める
では、JavaScriptに戻りましょう。Arrayクラスのメソッドで要素の平均を求めます。用いるのは[`Array.reduce()`メソッド](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)です。引数に与えた関数で要素を順番に処理し、結果の戻り値はつぎの要素に渡します。

前の要素に今の要素を順に加えていけば、合計が得られます。つぎのように、それを要素数で割れば平均です。引数には[アロー関数式](https://developer.mozilla.org/docs/Web/JavaScript/Reference/arrow_functions)を用いました。

```js
const array = [6, 4, 6, 6, 6, 3, 7, 2, 2, 8];
const average = array.reduce((previous, current) =>
    previous + current  // 前の要素につぎの要素を足す
) / array.length;  // 要素数で割る
console.log(average); // 5
```

#各要素について平均値との差の2乗を求める
平均が得られたら、もとの配列のそれぞれの要素について、平均値との差の2乗を求めます。このとき使うのは、[`Array.map()`メソッド](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/map)です。`Array.reduce()`と同じく、渡した関数で配列要素を順に処理します。違うのは、戻り値を要素とする新たな配列が返されることです。

するとつぎの式で、平均値との差の2乗を要素とする配列が得られます。なお、[べき乗演算子**](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#べき乗_(**))はECMAScript 2016で採り入れられました。

```js
const squaredDifference = array.map((current) => {
    const difference = current - average;  // 平均値との差を求める
    return difference ** 2;  // 差を2乘する
});
console.log(squaredDifference);  // [1, 1, 1, 1, 1, 4, 4, 9, 9, 9]
```

#配列要素の分散を求める
要素の平均値との差の2乗を足し合わせて、平均すれば分散です。つぎのように、前に平均を求めたときと同じ式になります。

```js
const variance = squaredDifference.reduce((previous, current) =>
    previous + current  // 前の要素につぎの要素を足す
) / array.length;  // 要素数で割る
console.log(variance);  // 4
```

分散と聞くと想い起こすのは、ある科学者が述べたといわれる宝くじを買う理由です。曰く「期待値でなく分散を買うのだ」と。宝くじは割りが最悪なギャンブルで、期待値は半額以下です(「[統計と確率の見方・考え方](http://www.fumiononaka.com/Sample/Creators_MeetUp/Creators_MeetUp_160625.html)」参照)。けれど、分散は振れ幅が大きいので、一攫千金の夢があるというわけです。ものは言いようといえます。

#分散の平方根が標準偏差
分散が導ければ、あとはその正の平方根を求めるだけです。`Array.map()`メソッドは配列を返しますので、ドット(`.`)をつなげればさらにメソッドが呼び出せます。

コードをつぎにまとめましょう。引数に関数を使った記述は少し長いものの、`for`ループで求めるより、計算の手順がわかりやすいのではないでしょうか。

```js
const array = [6, 4, 6, 6, 6, 3, 7, 2, 2, 8];
const average = array.reduce((previous, current) =>
    previous + current  // 要素をすべて足す
) / array.length;  // 平均を求める
const standardDeviation = Math.sqrt(  // 分散の平方根を求める
    array.map((current) => {  // 各要素について
        let difference = current - average;  // 平均値との差を求める
        return difference ** 2;  // 差を2乘する
    })
    .reduce((previous, current) =>
        previous + current  // 差の2乗をすべて足す
    ) / array.length  // 差の2乗の平均が分散
);
console.log(standardDeviation);  // 標準偏差: 2.0
```