倭算数理研究所

科学・数学・学習関連の記事を、「倭マン日記」とは別に書いていくのだ!

commons-math3 解読 (1-2) : 統計量を計算するユーティリティ・クラス StatUtils : math3.stat パッケージ

今回は org.apache.commons.math3.stat パッケージに定義されている StatUtils クラスを見ていきます。 このクラスは統計量を計算するためのユーティリティ・クラスで、static メソッドのみが定義されています。

参考

StatUtils クラスに定義されているメソッド

org.apache.commons.math3.stat.StatUtils クラスに定義されている static メソッドを、適当に分類して書き出すとこんな感じ:

package org.apache.commons.math3.stat;

public final class StatUtils {
    // ***** 総量 *****
    // 総和
    public static double sum(double[] values)
    public static double sum(double[] values, int begin, int length)
    public static double sumDifference(double[] sample1, double[] sample2)

    // 平方和
    public static double sumSq(double[] values)
    public static double sumSq(double[] values, int begin, int length)

    //  対数和
    public static double sumLog(double[] values)
    public static double sumLog(double[] values, int begin, int length)

    // 総積
    public static double product(double[] values)
    public static double product(double[] values, int begin, int length)


    // ***** 平均 *****
    // 平均(算術平均・相加平均)
    public static double mean(double[] values)
    public static double mean(double[] values, int begin, int length)
    public static double meanDifference(double[] sample1, double[] sample2)

    // 幾何平均(相乗平均)
    public static double geometricMean(double[] values)
    public static double geometricMean(double[] values, int begin, int length)


    // ***** 分位数 *****
    // 最小値
    public static double min(double[] values)
    public static double min(double[] values, int begin, int length)

    // 最大値
    public static double max(double[] values)
    public static double max(double[] values, int begin, int length)

    // 分位数
    public static double percentile(double[] values, double p)
    public static double percentile(double[] values, int begin, int length, double p)

    // ***** 分散 *****
    // 母分散
    public static double populationVariance(double[] values)
    public static double populationVariance(double[] values, double mean)
    public static double populationVariance(double[] values, int begin, int length)
    public static double populationVariance(double[] values, double mean, int begin, int length)

    // 標本分散
    public static double variance(double[] values)
    public static double variance(double[] values, double mean)
    public static double variance(double[] values, int begin, int length)
    public static double variance(double[] values, double mean, int begin, int length)
    public static double varianceDifference(double[] sample1, double[] sample2, double meanDifference)

    // 正規化
    public static double[] normalize(double[] sample)
}

どのメソッドにも、データとなるdouble の配列以外に int 値を2つとるメソッドオーバーロードされてますが、これは引数の配列のどの部分を使用するか指定するのに使います。 この記事ではこれ以上扱いません。 また double の配列を2つとる xxxxDifference() メソッドが幾つか(sumDifference(), meanDifference(), varianceDifference())定義されてますが、これらは引数の2つの配列の差を使って対応する統計量を計算するメソッドです。 これらのメソッドも扱いません。

統計量

以下で StatUtils クラスで計算できる統計量を簡単に見ていきましょう。 以降では、データ { \textbf{x} }{ x_i \; (0 \le i \le n-1) } の要素(サンプル)を持つとします。 データの要素数{ n } です。

総量
総量というのは正式名称ではありませんが、まぁ言わんとしていることは分かって貰えるかと。 総量には次の4つがあります:

  • 総和 sum
  • 平方和 sumSq
  • 対数和 sumLog
  • 総積 product

各量を式で表すと以下のようになります:

  { \displaystyle
\begin{align*}
    {\rm sum}({\bf x}) &= \sum_{i=0}^{n-1}x_i \\
    {\rm sumSq}({\bf x}) &= \sum_{i=0}^{n-1}x_i^2 \\
    {\rm sumLog}({\bf x}) &= \sum_{i=0}^{n-1}\log x_i = \log \prod_{i=0}^{n-1}x_i \\
    {\rm product}({\bf x}) &= \prod_{i=0}^{n-1}x_i
\end{align*}
}


平均
平均には次の2つがあります:

  • 平均 mean (相加平均、算術平均)
  • 幾何平均 geometricMean (相乗平均)

それぞれの定義は以下の通り:

  { \displaystyle
\begin{align*}
    \textrm{mean}({\bf x}) &= \frac{1}{n}\sum_{i=0}^{n-1}x_i = \frac{\textrm{sum}(\textbf{x})}{n} \\
    \textrm{geometricMean}(\textbf{x}) &= \left(\prod_{i=0}^{n-1}x_i\right)^{\frac{1}{n}} = \sqrt[n]{\textrm{product({\bf x})}}
\end{align*}
}


分位数
分位数とは、データの要素を小さい順に並べたときに、指定した割合にある要素の値です(wikipedia:分位数 これは quantile の説明なので percentile とはちょっと違うかも)。 StatUtils で計算できる分位数は次の3つ:

  • 最小値 min
  • 最大値 max
  • 分位数 percentile (quantile)

percentile() メソッドは第2引数にどの割合の分位数を返すか指定する double 値 p (0 < p <= 100)をとります。 この値に 50 を指定すると中央値 (median) を計算できます。

分散
StatUtils が計算できる分散(と関連する正規化)は以下の3つ:

  • 母分散 population variance
  • 標本分散 (sample) variance
  • 正規化 normalization (standardization)

日本語は多分これであってると思うけど、文献によって違うかもしれない(らしい)。 定義は以下のようになります:

  { \displaystyle
\begin{align*}
    {\rm populationVariance}({\bf x}) &= \frac{1}{n}\sum_{i=0}^{n-1}(x_i - {\rm mean}({\bf x}))^2 \\
    {\rm variance}({\bf x}) &= \frac{1}{n-1}\sum_{i=0}^{n-1}(x_i - {\rm mean}({\bf x}))^2 \\
    \left[{\rm normalize}({\bf x})\right]_i &= \frac{x_i - {\rm mean}({\bf x})}{\sigma} &
        \left(\sigma = \sqrt{{\rm variance}({\bf x})}\right)
\end{align*}
}

平均値とかを関数として書きましたが、別に commons-math3 の実装とは関係ありません。

サンプル・コード

ではサンプル・コード。 StatUtils クラスの static メソッドは static import してます。 ちなみに、assert 文の左辺は StatUtils を使って計算、右辺は(なるべく) Groovy を駆使して同じ量を計算しています。 ちなみに統計量の計算結果は double 値になるので、そのまま == で等値評価して assert するとまずい場合があります。 なので、ここでは精度を 0.000001d として等値評価をしています。 normalize() で返される double の配列については MathArrays#equals() メソッドによって評価。

@Grab('org.apache.commons:commons-math3:3.0')

import static org.apache.commons.math3.stat.StatUtils.*
import org.apache.commons.math3.util.MathArrays

def data = (1..10)
int n = data.size()
def d = data as double[]

use(DoubleValueEquivalence){
    assert sum(d) == data.sum()
    assert sumSq(d) == data.sum{ it**2 }
    assert sumLog(d) == data.sum{ Math.log(it) }
    assert product(d) == data.inject(1){ x, y -> x * y }

    assert mean(d) == sum(d)/n
    assert geometricMean(d) == Math.pow(product(d), 1/n)
    
    assert min(d) == 1
    assert max(d) == 10
    assert percentile(d, 50) == 5.5d
    
    def m = mean(d)
    def v = sumSq(d)/n - m**2
    assert populationVariance(d) == v
    assert variance(d) == v*n/(n-1)
    
    def sigma = Math.sqrt(variance(d))    // 標準偏差
    assert MathArrays.equals(normalize(d), data.collect{ (it - m)/sigma } as double[])
}

class DoubleValueEquivalence{
    static boolean equals(Double a, Double b){
        return Math.abs(a - b) < 0.000001d    // 精度をつけて double の等値評価
    }
}

Javaによるアルゴリズム事典

Javaによるアルゴリズム事典

プログラミングGROOVY

プログラミングGROOVY