JavaScript
数学

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

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

標準偏差とは

標準偏差」は、データのばらつきを表す指標です。まず平均を計算したうえで、各データと平均値の差を2乗して要素数で割った「分散」を求めます(「【統計学】初めての「標準偏差」(統計学に挫折しないために)」参照)。

 分散 = \frac{(データ1 - 平均)^2 + (データ2 - 平均)^2+ … +(データn - 平均)^2}{n}

そうして得た分散の正の平方根が「標準偏差」で、記号${\sigma}$(シグマ)を使って表します。データの分布の基本的なモデルとされる「正規分布」では、平均値±${\sigma}$の値の範囲にデータの7割弱が含まれます(図001)。

標準偏差 = \sqrt{分散}

図001■正規分布と標準偏差

Standard deviation diagram.svg
>> Widipediaより引用

この標準偏差を使ったのが、テストの「偏差値」です。平均を50とし、そこからの差は標準偏差$\sigma$を10として測ります。すると、平均値にかかわらず、データ全体の中の位置づけが得られるのです。偏差値60以上は15%ほど、70を超えるのは2%ちょっとということになります。

配列要素の平均を求める

では、JavaScriptに戻りましょう。Arrayクラスのメソッドで要素の平均を求めます。用いるのはArray.reduce()メソッドです。引数に与えた関数で要素を順番に処理し、結果の戻り値はつぎの要素に渡します。

前の要素に今の要素を順に加えていけば、合計が得られます。つぎのように、それを要素数で割れば平均です。引数にはアロー関数式を用いました。

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()メソッドです。Array.reduce()と同じく、渡した関数で配列要素を順に処理します。違うのは、戻り値を要素とする新たな配列が返されることです。

するとつぎの式で、平均値との差の2乗を要素とする配列が得られます。なお、べき乗演算子**はECMAScript 2016で採り入れられました。

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乗を足し合わせて、平均すれば分散です。つぎのように、前に平均を求めたときと同じ式になります。

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

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

分散の平方根が標準偏差

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

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

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