web-dev-qa-db-ja.com

曲線下の部分面積(AUC)の計算方法

Scikit学習では、バイナリ分類器の曲線の下の面積を計算できます

roc_auc_score( Y, clf.predict_proba(X)[:,1] )

偽陽性率が0.1未満の曲線の部分にのみ関心があります。

このようなしきい値の誤検出率が与えられた場合、曲線のしきい値を超える部分についてのみAUCをどのように計算できますか?

例として、いくつかのROC曲線の例を次に示します。

Illustration of ROC-curves plot for several types of a classifier.

Scikit学習ドキュメントはroc_curveの使用方法を示しています

>>> import numpy as np
>>> from sklearn import metrics
>>> y = np.array([1, 1, 2, 2])
>>> scores = np.array([0.1, 0.4, 0.35, 0.8])
>>> fpr, tpr, thresholds = metrics.roc_curve(y, scores, pos_label=2)
>>> fpr
array([ 0. ,  0.5,  0.5,  1. ])
>>> tpr
array([ 0.5,  0.5,  1. ,  1. ])
>>> thresholds
array([ 0.8 ,  0.4 ,  0.35,  0.1 ]

これから部分的なAUCに移動する簡単な方法はありますか?


Roc_curveが必ずしもそれを与えるとは限らないので、唯一の問題はfpr = 0.1でのtpr値の計算方法です。

10
eleanora

で始まるとしましょう

import numpy as np
from sklearn import  metrics

次に、真のyと予測されたscoresを設定します。

y = np.array([0, 0, 1, 1])

scores = np.array([0.1, 0.4, 0.35, 0.8])

yが問題から1だけシフトしていることに注意してください。これは重要ではありません。1、2、または0、1を予測しても、まったく同じ結果(fpr、tpr、しきい値など)が得られますが、 sklearn.metrics関数は、0、1を使用しない場合のドラッグです。)

ここでAUCを見てみましょう:

>>> metrics.roc_auc_score(y, scores)
0.75

あなたの例のように:

fpr, tpr, thresholds = metrics.roc_curve(y, scores)
>>> fpr, tpr
(array([ 0. ,  0.5,  0.5,  1. ]), array([ 0.5,  0.5,  1. ,  1. ]))

これにより、次のプロットが得られます。

plot([0, 0.5], [0.5, 0.5], [0.5, 0.5], [0.5, 1], [0.5, 1], [1, 1]);

enter image description here

構造上、有限長yのROCは長方形で構成されます。

  • 十分に低いしきい値の場合、すべてが負として分類されます。

  • しきい値が連続的に増加するにつれて、離散点で、いくつかの負の分類が正に変更されます。

したがって、有限yの場合、ROCは常に(0、0)から(1、1)に。

AUCはこれらの長方形の合計です。ここでは、上に示したように、長方形の面積が0.5 * 0.5 + 0.5 * 1 = 0.75であるため、AUCは0.75です。

場合によっては、線形補間によってAUCを計算することを選択します。 yの長さが、FPRとTPRに対して計算された実際のポイント数よりもはるかに大きいとします。次に、この場合、線形補間は、mightの間のポイントが何であったかの近似です。いくつかのケースでは、人々は予測にも従います。yが十分に大きければ、その間のポイントは線形に補間されます。 sklearn.metricsはこの推測を使用せず、sklearn.metricsと一致する結果を得るには、台形ではなく長方形の合計を使用する必要があります。

fprtprから直接AUCを計算する独自の関数を書いてみましょう:

import itertools
import operator

def auc_from_fpr_tpr(fpr, tpr, trapezoid=False):
    inds = [i for (i, (s, e)) in enumerate(Zip(fpr[: -1], fpr[1: ])) if s != e] + [len(fpr) - 1]
    fpr, tpr = fpr[inds], tpr[inds]
    area = 0
    ft = Zip(fpr, tpr)
    for p0, p1 in Zip(ft[: -1], ft[1: ]):
        area += (p1[0] - p0[0]) * ((p1[1] + p0[1]) / 2 if trapezoid else p0[1])
    return area

この関数は、FPRとTPR、および台形加算を使用するかどうかを示すオプションのパラメーターを受け取ります。実行すると、次のようになります。

>>> auc_from_fpr_tpr(fpr, tpr), auc_from_fpr_tpr(fpr, tpr, True)
(0.75, 0.875)

四角形の合計の場合はsklearn.metricsと同じ結果が得られ、台形の合計の場合は異なる、より高い結果が得られます。

したがって、FPR 0.1で終了した場合にFPR/TPRポイントがどうなるかを確認する必要があります。 bisect module でこれを行うことができます

import bisect

def get_fpr_tpr_for_thresh(fpr, tpr, thresh):
    p = bisect.bisect_left(fpr, thresh)
    fpr = fpr.copy()
    fpr[p] = thresh
    return fpr[: p + 1], tpr[: p + 1]

これはどのように作動しますか? thresh内のfprの挿入ポイントがどこにあるかを確認するだけです。 FPRのプロパティ(0から開始する必要があります)を指定すると、挿入ポイントは水平線上にある必要があります。したがって、この長方形より前のすべての長方形は影響を受けず、この長方形より後のすべての長方形は削除され、この長方形はおそらく短くなるはずです。

それを適用しましょう:

fpr_thresh, tpr_thresh = get_fpr_tpr_for_thresh(fpr, tpr, 0.1)
>>> fpr_thresh, tpr_thresh
(array([ 0. ,  0.1]), array([ 0.5,  0.5]))

最後に、更新されたバージョンからAUCを計算するだけです。

>>> auc_from_fpr_tpr(fpr, tpr), auc_from_fpr_tpr(fpr, tpr, True)
0.050000000000000003, 0.050000000000000003)

この場合、長方形の合計と台形の合計は同じ結果になります。一般的にはそうではないことに注意してください。 sklearn.metricsとの一貫性を保つために、最初のものを使用する必要があります。

9
Ami Tavory

Python sklearn roc_auc_score()max_fprを設定できるようになりました。あなたの場合、max_fpr=0.1を設定できます。関数はAUCを計算します。 https://scikit-learn.org/stable/modules/generated/sklearn.metrics.roc_auc_score.html

3
Cherry Wu

Fprとtp​​rの値は、[0.0、0.1]の範囲でのみ計算してください。

次に、 numpy.trapz を使用して、次のように部分的なAUC(pAUC)を評価できます。

pAUC = numpy.trapz(tpr_array, fpr_array)

この関数は、複合台形ルールを使用して、曲線の下の面積を評価します。

現在のベストアンサーを実装しましたが、すべての状況で正しい結果が得られませんでした。以下の実装を再実装してテストしました。また、組み込みの台形AUC関数を活用した場合と、ゼロから再作成した場合の比較も行いました。

def line(x_coords, y_coords):
    """
    Given a pair of coordinates (x1,y2), (x2,y2), define the line equation. Note that this is the entire line vs. t
    the line segment.

    Parameters
    ----------
    x_coords: Numpy array of 2 points corresponding to x1,x2
    x_coords: Numpy array of 2 points corresponding to y1,y2

    Returns
    -------
    (Gradient, intercept) Tuple pair
    """    
    if (x_coords.shape[0] < 2) or (y_coords.shape[0] < 2):
        raise ValueError('At least 2 points are needed to compute'
                         ' area under curve, but x.shape = %s' % p1.shape)
    if ((x_coords[0]-x_coords[1]) == 0):
        raise ValueError("gradient is infinity")
    gradient = (y_coords[0]-y_coords[1])/(x_coords[0]-x_coords[1])
    intercept = y_coords[0] - gradient*1.0*x_coords[0]
    return (gradient, intercept)

def x_val_line_intercept(gradient, intercept, x_val):
    """
    Given a x=X_val vertical line, what is the intersection point of that line with the 
    line defined by the gradient and intercept. Note: This can be further improved by using line
    segments.

    Parameters
    ----------
    gradient
    intercept

    Returns
    -------
    (x_val, y) corresponding to the intercepted point. Note that this will always return a result.
    There is no check for whether the x_val is within the bounds of the line segment.
    """    
    y = gradient*x_val + intercept
    return (x_val, y)

def get_fpr_tpr_for_thresh(fpr, tpr, thresh):
    """
    Derive the partial ROC curve to the point based on the fpr threshold.

    Parameters
    ----------
    fpr: Numpy array of the sorted FPR points that represent the entirety of the ROC.
    tpr: Numpy array of the sorted TPR points that represent the entirety of the ROC.
    thresh: The threshold based on the FPR to extract the partial ROC based to that value of the threshold.

    Returns
    -------
    thresh_fpr: The FPR points that represent the partial ROC to the point of the fpr threshold.
    thresh_tpr: The TPR points that represent the partial ROC to the point of the fpr threshold
    """    
    p = bisect.bisect_left(fpr, thresh)
    thresh_fpr = fpr[:p+1].copy()
    thresh_tpr = tpr[:p+1].copy()
    g, i = line(fpr[p-1:p+1], tpr[p-1:p+1])
    new_point = x_val_line_intercept(g, i, thresh)
    thresh_fpr[p] = new_point[0]
    thresh_tpr[p] = new_point[1]
    return thresh_fpr, thresh_tpr

def partial_auc_scorer(y_actual, y_pred, decile=1):
    """
    Derive the AUC based of the partial ROC curve from FPR=0 to FPR=decile threshold.

    Parameters
    ----------
    y_actual: numpy array of the actual labels.
    y_pred: Numpy array of The predicted probability scores.
    decile: The threshold based on the FPR to extract the partial ROC based to that value of the threshold.

    Returns
    -------
    AUC of the partial ROC. A value that ranges from 0 to 1.
    """        
    y_pred = list(map(lambda x: x[-1], y_pred))
    fpr, tpr, _ = roc_curve(y_actual, y_pred, pos_label=1)
    fpr_thresh, tpr_thresh = get_fpr_tpr_for_thresh(fpr, tpr, decile)
    return auc(fpr_thresh, tpr_thresh)
2
SriK

これは、FPRがx-axisまたはy-axis(独立変数または従属変数)であるかどうかによって異なります。

xの場合、計算は簡単です。[0.0、0.1]の範囲でのみ計算します。

yの場合、最初にy = 0.1の曲線を解く必要があります。これは、x軸を計算する必要がある領域と、高さが0.1の単純な長方形の領域に分割します。

説明のために、[x1、x2]と[x3、x4]の2つの範囲で0.1を超える関数を見つけたと仮定します。範囲全体の曲線の下の面積を計算します

[0, x1]
[x2, x3]
[x4, ...]

これに、見つけた2つの間隔のy = 0.1の下に長方形を追加します。

area += (x2-x1 + x4-x3) * 0.1

それはあなたがあなたを動かすのに必要なものですか?

1
Prune

@eleanora sklearnの一般的なmetrics.aucメソッドを使用する衝動を考えてください(それが私がやったことです)。 tprとfprのポイントセットを取得すると、簡単になるはずです(また、scipyの補間方法を使用して、どちらかのシリーズの正確なポイントを概算できます)。

0
plmedici