【初心者向け】基礎&実践プログラミング

初心者がつまづきやすいところ、最短で実力が身につく方法をお伝えします。

【Python】ROC曲線の作成・AUC算出・最適なカットオフ値の決定・分類正確度 / 感度 / 特異度の算出まで

f:id:AIProgrammer:20210226170005p:plain

動かしながら学ぶ PyTorchプログラミング入門

動かしながら学ぶ PyTorchプログラミング入門

  • 作者:斎藤勇哉
  • 発売日: 2020/11/30
  • メディア: Kindle版

目次

目的

  • Pythonを使ってROC曲線を作成
  • Youden indexを用いて、最適なcutoff値を決定
  • AUCおよび分類正確度・感度・特異度を算出

重要な関数

  • ROC曲線の作成:roc_curve
  • AUCの計算:roc_auc_score
  • 正確度の計算:accuracy_score

以上の関数は、scikit-learnのライブラリである。インストールしていない場合は、下記のコマンドインストールする。

pip3 install scikit-learn

準備

次のような、データ(data.csv)があったとする。

Labelは、1:Control、2:Disease type A、3:Disease type Bである。

FA, MD, FD, LogFC, FDCは、拡散MRIから取得できる定量値である。これらの列は、任意に変更してもかまわない。

ID Label FA MD FD LogFC FDC
Subj001 1 0.344805 0.001213 0.5885 0.0523 0.6236
Subj002 2 0.3454 0.001139 0.6277 0.1368 0.723
Subj003 3 0.360035 0.001143 0.5992 0.1562 0.7022
Subj067 3 0.351871 0.00109 0.5949 0.0527 0.6223

コード

以上のファイルが用意出来たら、以下のコマンドを実行する。

コード作成は、内田先生にご協力いただいた。

手順は次の通り。

  1. CSVファイルの読み込み
  2. Disease type A(Label=2)とDisease type B(Label=3)の2分類精度をみるために、Control(Label=1)を除いたDisease type AとDisease type Bのデータを抽出
  3. ROC曲線の作成
  4. AUCの算出
  5. Youden Indexを用いて最適なカットオフ値を決定
  6. 最適なカットオフ値における、分類正確度・感度・特異度を算出
  7. 混合行列(Confusion matrix)で図示
import os
import glob
import numpy as np
import seaborn as sns
import pandas as pd
from sklearn.metrics import roc_curve
from sklearn.metrics import auc
from sklearn.metrics import roc_auc_score
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix
from sklearn.metrics import accuracy_score


def each_roc_plot(fpr, tpr, map):
    # Plot
    if map == 'FA':
        plt.plot(fpr, tpr, linewidth='3', label=(map))
    elif map == 'MD':
        plt.plot(fpr, tpr, linestyle='-', linewidth='3', label=(map))
    elif map == 'FD':
        plt.plot(fpr, tpr, linestyle='--', linewidth='3', label=(map))
    elif map == 'LogFC':
        plt.plot(fpr, tpr, linestyle='dashdot', linewidth='3', label=(map))
    else:
        plt.plot(fpr, tpr, linestyle='dotted', linewidth='3', label=(map))


def all_roc_plot():
    plt.xlabel('FPR: False positive rate')
    plt.ylabel('TPR: True positive rate')
    plt.xlim([-0.02, 1.0])
    plt.ylim([0.0, 1.02])
    plt.legend()
    plt.grid()
    plt.savefig('result/ROC_Curve.png', dpi=300)
    plt.close()


def confmat_plot(true_label_list, pred_label_list, accuracy_list, tpr_list, tnr_list, map_list):
    for i, map in enumerate(map_list):
        conf_mat = confusion_matrix(true_label_list[i], pred_label_list[i])
        sns.heatmap(conf_mat, annot=True, cmap='Blues', cbar=False)
        plt.title("Acc:{:.3f} (Se:{:.3f}, Sp:{:.3f})".format(
            accuracy_list[i], tpr_list[i], tnr_list[i]))
        plt.xlabel('Predicted_label')
        plt.ylabel('Actual_label')
        plt.savefig("result/Confusion_matrix_{}.png".format(map), dpi=300)
        plt.close()


def youden_index(fpr, tpr):
    # 特異度
    tnr = 1 - fpr
    # Youden indexを用いたカットオフ基準
    cutoff_criterion = tpr + tnr - 1
    return cutoff_criterion.argmax()


def plot_roc_curve(df):
    thr_list, fpr_list, tpr_list, tnr_list, auc_list, accuracy_list, true_label_list, pred_label_list, map_list = [
    ], [], [], [], [], [], [], [], []
    for map in df.drop(['ID', 'Label'], axis=1).columns:
        # Label modification
        if map in ['MD', 'AD', 'RD', 'ISO']:
            df_label = df['Label'].replace({2: 1, 3: 0})
        else:
            df_label = df['Label'].replace({2: 0, 3: 1})

        # Calc ROC and AUC
        fpr, tpr, thresholds = roc_curve(df_label.values, df[map].values)
        auc = roc_auc_score(df_label.values, df[map].values)
        # Plot each ROC
        each_roc_plot(fpr, tpr, map)
        # Search optimal cutoff using Youden Index
        index = youden_index(fpr, tpr)
        # Predict label and calc accuracy
        pred_df_label = (df[map] < thresholds[index]
                         ).replace({False: 1, True: 0})
        accuracy = accuracy_score(df_label.values, pred_df_label.values)
        # Add result
        thr_list.append(thresholds[index])
        fpr_list.append(fpr[index])
        tpr_list.append(tpr[index])
        tnr_list.append(1 - fpr[index])
        auc_list.append(auc)
        accuracy_list.append(accuracy)
        true_label_list.append(df_label.values)
        pred_label_list.append(pred_df_label.values)
        map_list.append(map)
    # Plot all AUC in a figure
    all_roc_plot()
    # Save result
    pd.DataFrame({'Map': df.drop(['ID', 'Label'], axis=1).columns,
                  'Cutoff': thr_list,
                  'FPR': fpr_list,
                  'TPR': tpr_list,
                  'TNR': tnr_list,
                  'AUC': auc_list,
                  'Accuracy': accuracy_list
                  }).to_csv('result/result.csv', index=False)
    return true_label_list, pred_label_list, accuracy_list, tpr_list, tnr_list, map_list


def main():
    # Define
    filename = 'data.csv'
    # Make directory
    if not os.path.isdir('result'):
        os.makedirs('result')
    # Read data
    df = pd.read_csv(filename)
    df_pat = df[(df['Label'] == 2) | (df['Label'] == 3)]  # 2: Disease type A, 3: Disease type B
    # Plot ROC curve
    true_label_list, pred_label_list, accuracy_list, tpr_list, tnr_list, map_list = plot_roc_curve(
        df_pat)
    # Plot confusion matrix
    confmat_plot(true_label_list, pred_label_list,
                 accuracy_list, tpr_list, tnr_list, map_list)


if __name__ == '__main__':
    main()

結果

ROC曲線

f:id:AIProgrammer:20210225153327p:plain

分類精度

Map Cutoff FPR TPR TNR AUC Accuracy
FA 0.337 0.125 0.708 0.875 0.835 0.792
MD 0.001 0.083 0.708 0.917 0.885 0.813
FD 0.576 0.125 0.667 0.875 0.748 0.771
LogFC -0.087 0.125 0.875 0.875 0.908 0.875
FDC 0.570 0.042 0.750 0.958 0.917 0.854

混合行列(Confusion matrix)

以下は、あるパラメータにおける混合行列である。

f:id:AIProgrammer:20210225153226p:plain


頑張れ!喝!!の代わりにB!ブックマークを押していただけるとただただうれしいです(^^)! ↓

動かしながら学ぶ PyTorchプログラミング入門

動かしながら学ぶ PyTorchプログラミング入門

  • 作者:斎藤勇哉
  • 発売日: 2020/11/30
  • メディア: Kindle版