import numpy as np
import pandas as pd
from sklearn.metrics import roc_curve, auc
import matplotlib.pyplot as plt
= pd.read_excel("aSAH.xlsx")
aSAH
= aSAH['outcome']
y_true_str = aSAH['s100b']
y_pred
# Create a dictionary to map string labels to binary labels
= {"Poor": 1, "Good": 0}
label_mapping
# Map true labels to binary labels
= [label_mapping[label] for label in y_true_str]
y_true
# Tính đường cong ROC và AUC
= roc_curve(y_true, y_pred)
fpr, tpr, thresholds = auc(fpr, tpr)
roc_auc
# Vẽ biểu đồ AUC
=(8, 6))
plt.figure(figsize='darkorange', lw=2, label='ROC curve (area = %0.2f)' % roc_auc)
plt.plot(fpr, tpr, color0, 1], [0, 1], color='navy', lw=2, linestyle='--')
plt.plot([0.0, 1.0])
plt.xlim([0.0, 1.05])
plt.ylim(['False Positive Rate')
plt.xlabel('True Positive Rate')
plt.ylabel('ROC Curve')
plt.title(="lower right")
plt.legend(loc plt.show()
Delong method for AUC interval and AUC test
AUC
AUC là viết tắt của “Area Under the ROC Curve” trong tiếng Anh, dịch sang tiếng Việt là “Diện tích dưới đường cong ROC”. Đây là một phép đo quan trọng trong đánh giá hiệu suất của mô hình phân loại (classifier) trong trường hợp binary classification (phân loại nhị phân).
Để hiểu AUC, hãy cùng nhau xem xét đến đường cong ROC (Receiver Operating Characteristic curve). ROC curve biểu diễn sự tương quan giữa tỷ lệ True Positive Rate (TPR) và tỷ lệ False Positive Rate (FPR) của mô hình phân loại, khi ngưỡng phân loại thay đổi từ 0 đến 1.
- True Positive Rate (TPR), còn gọi là Sensitivity hoặc Recall, đo lường tỷ lệ các trường hợp positive mà mô hình dự đoán chính xác.
- False Positive Rate (FPR) đo lường tỷ lệ các trường hợp negative bị phân loại sai (tức là dự đoán nhầm thành positive).
AUC là diện tích nằm dưới đường cong ROC và nó thể hiện khả năng của mô hình phân loại phân biệt giữa hai lớp positive và negative. AUC càng lớn thì mô hình càng có khả năng phân loại tốt hơn, với AUC = 1 thể hiện mô hình hoàn hảo, trong khi AUC = 0.5 chỉ ra mô hình không khác biệt so với một dự đoán ngẫu nhiên.
AUC là một phép đo định lượng và phổ biến trong đánh giá hiệu suất của các mô hình phân loại như Logistic Regression, Support Vector Machines (SVM), Random Forest, Neural Networks, và nhiều mô hình khác.
Liên hệ AUC và thống kê Mann-Whitney
Thống kê Mann-Whitney (U-test):
Giả sử bạn có hai nhóm dữ liệu độc lập, \(X\) và \(Y\), với kích thước lần lượt là \(n_x\) và \(n_y\). Để tính U-statistic, bạn cần xếp hạng dữ liệu trong mỗi nhóm và tính tổng các xếp hạng trong nhóm \(X\), ký hiệu là \(R_X\). Sau đó, U-statistic được tính bằng công thức:
\[ U = n_x \cdot n_y + \frac{n_x \cdot (n_x + 1)}{2} - R_X \]
Nếu \(U\) là giá trị U-statistic (thống kê Mann-Whitney), thì AUC được tính như sau:
\[ AUC = \frac{U - \frac{n_x \cdot (n_x + 1)}{2}}{n_x \cdot n_y} \]
DeLong’s test:
Giả sử bạn có hai mô hình phân loại và đã tính được các xác suất dự đoán của chúng trên tập dữ liệu kiểm tra. Để thực hiện kiểm định DeLong, bạn cần tính U-statistic và phương sai của U-statistic (Var(U)) như đã giải thích trước đó.
Tiếp theo, để tính Z-score, bạn cần có AUC của hai mô hình (\(AUC_1\) và \(AUC_2\)) và Var(U) như sau:
\[ Z = \frac{AUC_1 - AUC_2}{\sqrt{\text{Var}(U)}} \]
Sau đó, sử dụng Z-score để tính p-value, và nếu p-value nhỏ hơn mức ý nghĩa đã chọn (thường là 0.05), bạn có thể kết luận rằng có sự khác biệt đáng kể giữa AUC của hai mô hình.
Công thức tính khoảng tin cậy (confidence interval) cho AUC dựa trên phương pháp DeLong như sau:
Giả sử bạn đã tính được U-statistic (U) và Var(U) từ kiểm định DeLong, thì khoảng tin cậy 95% cho AUC sẽ được tính bằng công thức sau:
\[ \text{CI}_{\text{lower}} = AUC - z_{\alpha/2} \cdot \sqrt{\text{Var}(U)} \]
\[ \text{CI}_{\text{upper}} = AUC + z_{\alpha/2} \cdot \sqrt{\text{Var}(U)} \]
Trong đó:
- AUC là diện tích dưới đường cong ROC đã tính từ mô hình phân loại và tập dữ liệu.
- Var(U) là phương sai của U-statistic, tính từ kiểm định DeLong.
- \(z_{\alpha/2}\) là giá trị thống kê từ phân phối chuẩn tương ứng với mức đáng tin cậy 95%. Với mức đáng tin cậy 95%, \(\alpha = 0.05\), nên \(z_{\alpha/2}\) tương ứng với giá trị thống kê 1.96.
Thuật toán Sun và Xu
Thuật toán được đề xuất bởi Sun và Xu trong bài báo “Fast Implementation of DeLong’s Algorithm for Comparing the Areas Under Correlated Receiver Operating Characteristic Curves” cung cấp một cách tính toán hiệu suất và nhanh chóng để tính phương sai của sự khác biệt giữa diện tích dưới đường cong ROC (AUC) bằng phương pháp DeLong cho các đường cong ROC tương quan. Thuật toán của họ giảm đáng kể độ phức tạp tính toán so với phương pháp DeLong gốc, làm cho nó phù hợp cho các tập dữ liệu lớn.
Dưới đây là tổng quan về các bước của thuật toán:
AUC được tính theo công thức
\[AUC = \hat{\theta} = \frac{\sum_{i=1}^{m} R_i - \frac{m \cdot (m + 1)}{2}}{m \cdot n} = \frac{1}{mn} \sum_{i=1}^{m} \mathbb{T}_{Z}(X_{i}) - \frac{m+1}{2n}\]
Trong đó:
- m, n lần lượt là số lượng nhãn 1, 0
- \(R_i={T}_{Z}(X_{i})\) là giá trị score hoặc PD được sắp xếp theo thứ tự giảm dần (trường hợp bad = 1, good = 0). \({T}_{Z}(X_{i})\) được tính theo hàm Heaviside
- Hàm Heaviside \(\mathcal{H}(t)\):
\[ \mathcal{H}(t) = \begin{cases}1 & t > 0 \\ \frac{1}{2} & t = 0 \\ 0 & t < 0\end{cases} \]
- Mối liên hệ giữa Mid-Ranks và Hàm Heaviside:
\[ \mathbb{T}_{\mathcal{Z}}(\mathcal{Z}_{i}) = \sum_{j=1}^{M} \mathcal{H}(\mathcal{Z}_{i} - \mathcal{Z}_{j}) + \frac{1}{2} \]
Tính Z-score và p-value: Tính Z-score bằng cách sử dụng công thức:
\[ z \triangleq \frac{\hat{\theta}^{(1)}-\hat{\theta}^{(2)}}{\sqrt{\mathbb{V}\left[\hat{\theta}^{(1)}-\hat{\theta}^{(2)}\right]}}=\frac{\hat{\theta}^{(1)}-\hat{\theta}^{(2)}}{\sqrt{\mathbb{V}\left[\hat{\theta}^{(1)}\right]+\mathbb{V}\left[\hat{\theta}^{(2)}\right]-2 \mathbb{C}\left[\hat{\theta}^{(1)}, \hat{\theta}^{(2)}\right]}} \]
Trong đó:
\(\hat{\theta}^{(1)}\) và \(\hat{\theta}^{(2)}\): Đây là hai ước tính của tham số \(\theta\) hay
AUC
trong hai mẫu khác nhau mà đang so sánh.\(\mathbb{V}\left[\hat{\theta}^{(1)}\right]\) và \(\mathbb{V}\left[\hat{\theta}^{(2)}\right]\): Đây là phương sai của hai ước tính \(\hat{\theta}^{(1)}\) và \(\hat{\theta}^{(2)}\) tương ứng.
\(\mathbb{C}\left[\hat{\theta}^{(1)}, \hat{\theta}^{(2)}\right]\): Đây là hiệp phương sai giữa hai ước tính \(\hat{\theta}^{(1)}\) và \(\hat{\theta}^{(2)}\).
Sau đó, tính giá trị p-value từ giá trị phân phối chuẩn Z-score.
Kết quả: Dựa vào giá trị p-value tính toán được và một ngưỡng ý nghĩa đã chọn (ví dụ, 0.05), bạn có thể xác định xem sự khác biệt trong AUC giữa hai mô hình có ý nghĩa thống kê hay không.
def midrank(x):
= zip(*sorted(enumerate(x), key=lambda x:x[1]))
J, Z = list(J)
J = list(Z)
Z -1]+1)
Z.append(Z[= len(x)
N = np.zeros(N)
T
= 1
i while i <= N:
= i
a = a
j while Z[j-1] == Z[a-1]:
= j + 1
j = j - 1
b for k in range(a, b+1):
-1] = (a + b) / 2
T[k= b + 1
i = np.zeros(N)
T2 = T
T2[J]
return T2
def fastDeLong(samples):
# %FASTDELONGCOV
# %The fast version of DeLong's method for computing the covariance of
# %unadjusted AUC.
# %% Reference:
# % @article{sun2014fast,
# % title={Fast Implementation of DeLong's Algorithm for Comparing the Areas Under Correlated Receiver Operating Characteristic Curves},
# % author={Xu Sun and Weichao Xu},
# % journal={IEEE Signal Processing Letters},
# % volume={21},
# % number={11},
# % pages={1389--1393},
# % year={2014},
# % publisher={IEEE}
# % }
# %% [aucs, delongcov] = fastDeLong(samples)
# %%
# % Edited by Xu Sun.
# % Homepage: https://pamixsun.github.io
# % Version: 2014/12
# %%
if np.sum(samples.spsizes) != samples.ratings.shape[1] or len(samples.spsizes) != 2:
assert False, 'Argument mismatch error'
= samples.ratings
z = samples.spsizes
m, n = z[:, :m]
x = z[:, m:]
y = z.shape[0]
k
= np.zeros([k, m])
tx = np.zeros([k, n])
ty = np.zeros([k, m + n])
tz for r in range(k):
= midrank(x[r, :])
tx[r, :] = midrank(y[r, :])
ty[r, :] = midrank(z[r, :])
tz[r, :]
= np.sum(tz[:, :m], axis=1) / m / n - float(m + 1.0) / 2.0 / n
aucs = (tz[:, :m] - tx[:, :]) / n
v01 = 1.0 - (tz[:, m:] - ty[:, :]) / m
v10 = np.cov(v01)
sx = np.cov(v10)
sy = sx / m + sy / n
delongcov
return aucs, delongcov, v01, v10
class Samples:
def __init__(self, y_true, y_score1, y_score2):
= len(y_true)
n_samples = int(sum(y_true))
m = n_samples - m
n
# Zip the lists together
= list(zip(y_true, y_score1, y_score2))
zipped_data
# Sort the zipped data based on labels in decreasing order
= sorted(zipped_data, key=lambda x: x[0], reverse=True)
sorted_data
# Unzip the sorted data to get separate sorted values and labels
= zip(*sorted_data)
sorted_y_true, sorted_y_score1, sorted_y_score2
# Stack the sorted scores into a ratings matrix
= np.vstack((sorted_y_score1, sorted_y_score2))
ratings
# Create a tuple with the sample sizes
= (m, n)
spsizes
self.ratings = ratings
self.spsizes = spsizes
import numpy as np
import pandas as pd
= pd.read_excel("aSAH.xlsx")
aSAH
= aSAH['outcome']
y_true_str = aSAH['s100b']
y_pred1 = aSAH['wfns']
y_pred2
# Create a dictionary to map string labels to binary labels
= {"Poor": 1, "Good": 0}
label_mapping
# Map true labels to binary labels
= [label_mapping[label] for label in y_true_str]
y_true
= Samples(y_true, y_pred1, y_pred2)
samples
= fastDeLong(samples)
aucs, delongcov, v01, v10 # Print the results
print("AUCs:", aucs)
print("DeLong Covariance:", delongcov)
AUCs: [0.73136856 0.82367886]
DeLong Covariance: [[0.00266868 0.00119616]
[0.00119616 0.00146991]]
Kiểm định sự khác biệt AUC của 2 mô hình theo Delong
import scipy
def calpvalue(aucs, sigma):
= np.array([[1, -1]])
l = np.abs(np.diff(aucs)) / np.sqrt(np.dot(np.dot(l, sigma), l.T))
z = 2 * (1 - scipy.stats.norm.cdf(z, loc=0, scale=1))
pvalue return pvalue
= fastDeLong(samples)
AUC_DS, C, _, _ = calpvalue(AUC_DS, C)[0][0]
p_value
print(f"AUC Model 1: {AUC_DS[0]:.3f}")
print(f"AUC Model 2: {AUC_DS[1]:.3f}")
print("P-value for DeLong test:", p_value)
# So sánh AUC của hai mô hình
= 0.05
alpha if p_value < alpha:
print("Có sự khác biệt đáng kể giữa AUC của hai mô hình.")
else:
print("Không có sự khác biệt đáng kể giữa AUC của hai mô hình.")
AUC Model 1: 0.731
AUC Model 2: 0.824
P-value for DeLong test: 0.02717578222918804
Có sự khác biệt đáng kể giữa AUC của hai mô hình.
So sánh AUC của hai mô hình bằng bootstrap
import numpy as np
from sklearn.metrics import roc_auc_score
# Tạo dữ liệu mẫu với độ dài chuỗi y_true là 1000
# Tính AUC cho hai mô hình
= roc_auc_score(y_true, predictions_model1)
auc_model1 = roc_auc_score(y_true, predictions_model2)
auc_model2
# Thực hiện bootstrap để tính khoảng tin cậy 95% cho AUC của cả hai mô hình
= 1000 # Số lượng lần lấy mẫu bootstrap
n_iterations = len(y_true) # Số lượng phần tử trong chuỗi y_true
n_size
= []
auc_scores_model1 = []
auc_scores_model2
for _ in range(n_iterations):
# Lấy ngẫu nhiên mẫu từ dữ liệu với replacement
= np.random.randint(0, n_size, n_size)
indices = np.array(y_true)[indices]
y_true_bootstrap = predictions_model1[indices]
y_pred_bootstrap_model1 = predictions_model2[indices]
y_pred_bootstrap_model2
# Tính AUC cho mẫu lấy ra từ dữ liệu bootstrap cho hai mô hình
= roc_auc_score(y_true_bootstrap, y_pred_bootstrap_model1)
auc_score_model1 = roc_auc_score(y_true_bootstrap, y_pred_bootstrap_model2)
auc_score_model2
auc_scores_model1.append(auc_score_model1)
auc_scores_model2.append(auc_score_model2)
# Tính khoảng tin cậy 95% cho AUC cho cả hai mô hình
= np.percentile(auc_scores_model1, 2.5)
lower_bound_model1 = np.percentile(auc_scores_model1, 97.5)
upper_bound_model1
= np.percentile(auc_scores_model2, 2.5)
lower_bound_model2 = np.percentile(auc_scores_model2, 97.5)
upper_bound_model2
print(f"AUC Model 1: {auc_model1:.3f} (95% CI: [{lower_bound_model1:.3f}, {upper_bound_model1:.3f}])")
print(f"AUC Model 2: {auc_model2:.3f} (95% CI: [{lower_bound_model2:.3f}, {upper_bound_model2:.3f}])")
# So sánh khoảng tin cậy của AUC của hai mô hình
if lower_bound_model1 > upper_bound_model2 or lower_bound_model2 > upper_bound_model1:
print("Có sự khác biệt đáng kể giữa AUC của hai mô hình.")
else:
print("Không có sự khác biệt đáng kể giữa AUC của hai mô hình.")
AUC Model 1: 0.523 (95% CI: [0.403, 0.638])
AUC Model 2: 0.506 (95% CI: [0.383, 0.613])
Không có sự khác biệt đáng kể giữa AUC của hai mô hình.
Tính khoảng tin cậy AUC sử dụng phương pháp Delong
from scipy.stats import norm
def calculate_auc_ci(y_true, y_score, alpha = 0.05):
= Samples(y_true, y_score, y_score)
samples
= fastDeLong(samples)
auc, auc_cov,_,_ = auc[0]
auc = auc_cov[0,0]
var_U
= norm.ppf(1 - alpha / 2)
z_score = auc - z_score * np.sqrt(var_U)
lower_bound = auc + z_score * np.sqrt(var_U)
upper_bound
return auc, var_U, lower_bound, upper_bound
= calculate_auc_ci(y_true, y_pred)
auc_model, _, lower_bound, upper_bound print(f"AUC: {auc_model:.3f}")
print(f"95% Confidence Interval for AUC using bootstrap method: [{lower_bound:.3f}, {upper_bound:.3f}]")
AUC: 0.731
95% Confidence Interval for AUC using bootstrap method: [0.630, 0.833]
Tính khoảng tin cậy AUC sử dụng phương pháp Bootstrap
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import roc_auc_score, roc_curve
# Tính AUC cho mô hình ban đầu
= roc_auc_score(y_true, y_pred)
auc_model
# Số lượng lần lấy mẫu (bootstrap iterations)
= 1000
n_iterations
# Số lượng mẫu lấy ra từ dữ liệu ban đầu trong mỗi lần lấy mẫu
= len(y_true)
n_size
# Tạo mảng để lưu kết quả AUC từ các lần lấy mẫu
= []
auc_scores
for _ in range(n_iterations):
# Lấy ngẫu nhiên mẫu từ dữ liệu với replacement
= np.random.randint(0, n_size, n_size)
indices = np.array(y_true)[indices]
y_true_bootstrap = y_pred[indices]
y_pred_bootstrap
# Tính AUC cho mẫu lấy ra từ dữ liệu bootstrap
= roc_auc_score(y_true_bootstrap, y_pred_bootstrap)
auc_score
auc_scores.append(auc_score)
# Tính khoảng tin cậy 95% cho AUC
= np.percentile(auc_scores, 2.5)
lower_bound = np.percentile(auc_scores, 97.5)
upper_bound
print(f"AUC: {auc_model:.3f}")
print(f"95% Confidence Interval for AUC using bootstrap method: [{lower_bound:.3f}, {upper_bound:.3f}]")
AUC: 0.731
95% Confidence Interval for AUC using bootstrap method: [0.624, 0.824]