機械学習モデル精度の評価

二値分類におけるMetricsの紹介

公開日: 2022-03-02
更新日: 2022-04-11

Table of Contents

2標本問題のノンパラメトリック検定

2つのグループ(T, C)があるとします. それぞれのグループについて$(n, m)$のサンプルを観測し、互いに独立に確率密度関数Tグループは$f(x)$, Cグループは$g(x)$に従うものとします.

\[\begin{align*} \text{Group T} &= \{X_1, \cdots, X_n\}\\ \text{Group C} &= \{Y_1, \cdots, Y_m\} \end{align*}\]

このとき、2つの分布が等しいという仮説

\[H_0: f(x) = g(x)\]

を検定することを考えます.

ウィルコクソンの順位和検定(Wilcoxon rank sum test)

厳密手法

  1. $K = n + m$を計算
  2. 比較したい2標本の観測値に全体順位(Rank, 昇順)をつけ、その値を$R_i$とする(観測値にタイ(同一順位)がある場合は、順位の平均値)
  3. グループごとに順位の合計: $w_j = \sum_{i \in j}R_i $ $\ \ ,j \in \{T, C\}$を計算
  4. $n, m$を比較し、観測数が少ない方の$w_j$をウィルコクソンの順位和統計量$W$として計算
  5. $P(W_j \leq W)$を計算し、p-valueを求める

背景

\[H_0: f(x) = g(x)\]

が正しい場合、$X_1, \cdots, X_n, Y_1, \cdots, Y_m$はすべて同一の分布に従うので$R_1, \cdots, R_n$が $\{1, \cdots, K\}$の中の任意の$n$個の数$r_1, \cdots, r_n$を取る確率はすべて等しく

\[P(R_1 = r_1, \cdots, R_n = r_n) = \frac{1}{K(K-1)\cdots(K-n+1)}\]

となります. この帰無仮説の下で、順位和は分布の形に無関係で計算できるので、ノンパラメトリック検定と分類されます.

Pythonでの実装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
def ranksums(x, y):
    """Compute the Wilcoxon rank-sum statistic for two samples.
    
    Parameters
    ----------
    x,y : array_like
        The data from the two samples.
    
    Returns
    -------
    w : float
        The rank sum statistic 

    pvalue : float
        The p-value of the test.


    distribution : array
        The rank sum statistic distribution

    Remarks
    -------
    len(x), len(y) should be lower than 20
    """
    x, y = map(np.asarray, (x, y))
    n1 = len(x)
    n2 = len(y)
    alldata = np.concatenate((x, y))
    sorter = np.argsort(alldata) ## get the sorted index

    inv = np.empty(sorter.size, dtype=np.intp)
    inv[sorter] = np.arange(sorter.size, dtype=np.intp) ## rank the element

    alldata = alldata[sorter]
    obs = np.r_[True, alldata[1:] != alldata[:-1]] #先頭はTrue
    dense = obs.cumsum()[inv]

    # cumulative counts of each unique value
    count = np.r_[np.nonzero(obs)[0], len(obs)] #extract the nonzero index and add the length at the last
    ranked = .5 * (count[dense] + count[dense - 1] + 1)

    x, y = ranked[:n1], ranked[n1:]
    idx = np.argmin([n1, n2])
    w = [np.sum(x, axis=0), np.sum(y, axis=0)][idx]
    n_length = [n1, n2][idx]

    simulated_array = np.sum(list(combinations(np.arange(1, n1+n2+1), n_length)), axis = 1)
    p_value = 1 - abs(np.sum(simulated_array <= w)/len(simulated_array) -.5) * 2

    simulated_array = np.sort(simulated_array)
    simulated_obs = np.r_[True, simulated_array[1:] != simulated_array[:-1]]
    i = np.append(np.where(simulated_array[1:] != simulated_array[:-1]), len(simulated_array) - 1) ## return the index
    z = np.diff(np.append(-1, i))

    distribution = [simulated_array[simulated_obs], np.cumsum(z)/len(simulated_array)]
    
    return w, p_value, distribution


## test
sample1 = [30, 20, 52]
sample2 = [40, 50, 35, 60]

ranksums(sample1, sample2)
>>> 9.0
>>> 0.2
>>>  [array([ 6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18]),
      array([0.02857143, 0.05714286, 0.11428571, 0.2       , 0.31428571,
             0.42857143, 0.57142857, 0.68571429, 0.8       , 0.88571429,
             0.94285714, 0.97142857, 1.        ])])

ブログ関連記事

References



Share Buttons
Share on:

Feature Tags
Leave a Comment
(注意:GitHub Accountが必要となります)