医疗医疗报告分析
在本演示中,将使用分析工具分析医疗数据。
在分析数据集期间,为医生工作的一致性和准确性计算了几个指标。
必须安装库
您必须手动指定项目所在的路径才能转到工作目录,以及安装必要的依赖项。
!cd /user/Demo_public/biomedical/analis_medical_result
!pip install -r /user/Demo_public/biomedical/analis_medical_result/requirements.txt
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.metrics import cohen_kappa_score
from statsmodels.stats.inter_rater import fleiss_kappa
import numpy as np
from sklearn.metrics import accuracy_score, recall_score, confusion_matrix, balanced_accuracy_score, mean_squared_error
from sklearn.metrics import precision_recall_curve, average_precision_score, f1_score, fbeta_score, roc_auc_score,ConfusionMatrixDisplay, roc_curve, auc
from sklearn.preprocessing import MinMaxScaler
from scipy.stats import sem, t
让我们读取数据并查看dataframe的前5行中包含的内容。
focus = pd.read_csv('/user/Demo_public/biomedical/analis_medical_result/Дата.csv')
focus.head()
focus = focus.drop('ID Файла', axis=1)
这些文件包含图像的标记。 15名医生中的每一位都根据476张照片在没有或存在病理学的情况下做出结论。
您可以估计每张图片对病理学有信心的医生数量,并预先评估哪里真的有病理学,哪里没有病理学。 你也可以在医生的结果中寻找强烈的差异。 有可能确定医生在病理学的定义中有多少相互同意。
我们将评估每位病理医生的标记质量。
focus_corr = focus.corr(method='spearman')
filtered = focus_corr.where(focus_corr > 0.5).abs()
从相关表中可以看出,关于妇科的决定至少有50%重合的医生并不多。 存在弱相关性。
然而,有两对医生(3和13)(12和10)具有平均相关强度。 这清楚地表明,这两对往往使大多数图片相同的标记。 这可能表明它们之间关于图像中是否存在病理的高度一致性。
focus_corr_ = focus.corr(method='spearman')
filtered_ = focus_corr_.where(focus_corr < 0.1)
也有一对医生给出完全相反的答案,这意味着他们的意见几乎是100%不同。
focus_corr_mean = focus_corr.mean(axis=1).sort_values(ascending=False)
focus_corr_mean
上表显示了每个医生与其他医生的平均相关性,这些信息清楚地表明每个医生的意见与其他人的认同程度。
plt.figure(figsize=(8,8))
sns.heatmap(focus_corr.round(2), annot=True, center=0, cmap='inferno')
plt.title('Корреляция между разметками врачей')
plt.figure(figsize=(8,8))
sns.heatmap(filtered.round(2), annot=True, center=0, cmap='inferno')
plt.title('Корреляция между разметками врачей с средней по силе корреляции')
让我们看看有多少医生对每张图片的病理学百分比有信心。
confidence = focus.sum(axis=1).sort_values(ascending=False) / focus.shape[1] * 100
confidence.head(20)
因此,在上表中,您可以观察到相应ID的图片中病理的百分比概率,同时考虑到医生的决定。
average_marking = focus.mean(axis=1)
average_doctor_deviation = focus.sub(average_marking, axis=0).abs().mean().sort_values(ascending=False)
average_doctor_deviation
15号医生的平均偏差最高(0.2457),这意味着他的标记与所有医生的平均标记显着不同。 这可能表明标记中可能存在的错误。
3号医生的平均偏差最低(0.1069),这表明他的标记与所有医生的平均标记的高度一致性。 这位医生最常以与其他大多数医生相同的方式标记照片。
因此,在这里获得了与所有医生的平均标记的平均偏差。
接下来,我们计算医生评估的协方差矩阵
covariance_matrix = focus.cov()
covariance_matrix
covariance_matrix_mean = covariance_matrix.mean(axis=1).sort_values(ascending=False)
covariance_matrix_mean
15号医生的平均协方差最高(0.0525)。 这表明15号医生的标记平均与其他医生的标记更一致。 这位医生最常以与其他医生相同的方式注意到病理的存在或不存在。
8号医生的平均协方差最低(0.0101)。 这表明8号医生的标记平均与其他医生的标记不太一致。 这位医生最常评估病理的存在或不存在与其他医生不同。
可以看出,15号医生的平均协方差最高,平均偏差最高。 这可能是由于医生对图片中病理的看法与大多数意见一致,但是,他可能会比其他人更经常地注意到隐藏的特征,并且稍微高估了它们。
doctor_13 = focus['Врач№13']
doctor_3 = focus['Врач№3']
doctor_12 = focus['Врач№12']
doctor_10 = focus['Врач№10']
cohen_kappa_score(doctor_13, doctor_3), cohen_kappa_score(doctor_12, doctor_10)
这确认了根据图像的估计的最高度一致的对的相关表。
counts = np.zeros((focus.shape[0], 2), dtype=int)
for idx, row in focus.iterrows():
counts[idx, 0] = (row == 0).sum()
counts[idx, 1] = (row == 1).sum()
fleiss_kappa(counts)
该测试显示所有医生的注释的一致性。 从结果可以看出,一致性很低,医生对病理学的存在存在存在分歧。
kappa_scores = pd.DataFrame(focus.columns, focus.columns)
for col in focus.columns:
for col_ in focus.columns:
kappa_scores.loc[col, col_] = cohen_kappa_score(focus[col], focus[col_])
kappa_scores = kappa_scores.drop([0], axis=1)
由于这个标志,你可以找出哪些医生彼此更同意。
kappa_scores_mean = kappa_scores.mean(1).sort_values(ascending=False)
kappa_scores_mean
从这个板块可以看出,15号医生在医生中具有最低的平均网络一致性之一。
Kappa Cohen系数与相关表不同,调节随机巧合
这可能是由于这样的事实,医生经常同意与其他医生,但他做出更积极或消极的诊断。
让我们从医生的平均值计算医生结果的标准偏差
standard_deviation_mean=focus.sub(focus.mean(axis=1), axis=0).pow(2).mean().sort_values(ascending=False)
standard_deviation_mean
在这种情况下,来自医生平均值的MSE有助于了解哪些医生具有最高偏差,他们最常给出与其他医生不一致的特定快照的估计。
std_mean=focus.std().sort_values(ascending=False)
std_mean
COEX系统有助于看到变异最大的医生.
接下来,我们将为每张图片获得一个"参考分数",该分数将根据选票总和计算,也就是说,如果大多数医生赞成具有病理,那么我们设置1,否则-0。
diagnosis_major = pd.Series([0 if x > y else 1 for x, y in zip(counts[:, 0], counts[:, 1])])
accuracy_df = focus.apply(lambda x: accuracy_score(diagnosis_major, x)).sort_values(ascending=False)
accuracy_df
上表显示了医生图像的评估与基于多数概念获得的参考估计进行比较的准确性,即医生对特定图像的诊断的确认越多,它确实是病理的可能性就越高。
recall_df = focus.apply(lambda x: recall_score(diagnosis_major, x)).sort_values(ascending=False)
recall_df
召回可用于评估医生正确识别图像中病理存在的频率。
columns = focus.columns
confusion_matrices = {}
for column in columns:
confusion_matrix_ = confusion_matrix(diagnosis_major, focus[column])
confusion_matrices[column] = confusion_matrix_.sum() - np.diag(confusion_matrix_).sum()
errors_df = pd.DataFrame(list(confusion_matrices.items()), columns=['Врач', 'Количество ошибок']).sort_values(ascending=False, by='Количество ошибок')
errors_df
通过计算FP+FN,我们可以看到哪些医生的错误最多。
counts[:, 0].sum(), counts[:, 1].sum()
(6316,839)-医生诊断的总数。 6316-医生对病理缺席的投票数量,839-存在。 正如你所看到的,有一个轻微的类不平衡,让我们尝试从sklearn应用accuracy_balanced。
balanced_df = focus.apply(lambda x: balanced_accuracy_score(diagnosis_major, x)).sort_values(ascending=False)
balanced_df
让我们构建图表,直观地显示关于哪个医生最好地应对诊断的信息。 总共计算了10个指标,我们将构建10个图表。
colors = ['blue', 'green', 'red', 'purple', 'orange', 'yellow', 'brown', 'pink', 'gray', 'cyan', 'magenta', 'lime', 'teal', 'navy']
fig, axes = plt.subplots(nrows=5, ncols=2,figsize=(13, 24))
axes = axes.flatten()
axes[0].bar(focus_corr_mean.index.tolist(), focus_corr_mean.values.tolist(), color=colors[0])
axes[0].set_title('Средняя корреляция по врачам')
axes[0].set_xlabel('Врач')
axes[0].set_ylabel('Средняя корреляция')
axes[0].tick_params(axis='x', rotation=45)
axes[1].bar(average_doctor_deviation.index.tolist(), average_doctor_deviation.values.tolist(), color=colors[1])
axes[1].set_title('Среднее отклонение по врачам')
axes[1].set_xlabel('Врач')
axes[1].set_ylabel('Среднее отклонение')
axes[1].tick_params(axis='x', rotation=45)
axes[2].bar(covariance_matrix_mean.index.tolist(), covariance_matrix_mean.values.tolist(), color=colors[2])
axes[2].set_title('Средняя ковариация по врачам')
axes[2].set_xlabel('Врач')
axes[2].set_ylabel('Средняя ковариация')
axes[2].tick_params(axis='x', rotation=45)
axes[3].bar(kappa_scores_mean.index.tolist(), kappa_scores_mean.values.tolist(), color=colors[3])
axes[3].set_title('Средняя оценка по Капо-Коэно по врачам')
axes[3].set_xlabel('Врач')
axes[3].set_ylabel('Средняя оценка по Капо-Коэно')
axes[3].tick_params(axis='x', rotation=45)
axes[4].bar(standard_deviation_mean.index.tolist(), standard_deviation_mean.values.tolist(), color=colors[4])
axes[4].set_title('Среднеквадратическое отклонение по врачам')
axes[4].set_xlabel('Врач')
axes[4].set_ylabel('Среднеквадратическое отклонение')
axes[4].tick_params(axis='x', rotation=45)
axes[5].bar(std_mean.index.tolist(), std_mean.values.tolist(), color=colors[5])
axes[5].set_title('Среднее СКО по врачам')
axes[5].set_xlabel('Врач')
axes[5].set_ylabel('Среднее СКО')
axes[5].tick_params(axis='x', rotation=45)
axes[6].bar(accuracy_df.index.tolist(), accuracy_df.values.tolist(), color=colors[6])
axes[6].set_title('Мажоритарная точность по врачам')
axes[6].set_xlabel('Врач')
axes[6].set_ylabel('Мажоритарная точность')
axes[6].tick_params(axis='x', rotation=45)
axes[7].bar(recall_df.index.tolist(), recall_df.values.tolist(), color=colors[7])
axes[7].set_title('Мажоритарная полнота по врачам')
axes[7].set_xlabel('Врач')
axes[7].set_ylabel('Мажоритарная полнота')
axes[7].tick_params(axis='x', rotation=45)
axes[8].bar(errors_df['Врач'], errors_df['Количество ошибок'], color=colors[8])
axes[8].set_title('Ошибки по врачам')
axes[8].set_xlabel('Врач')
axes[8].set_ylabel('ошибки по полнота')
axes[8].tick_params(axis='x', rotation=45)
axes[9].bar(balanced_df.index.tolist(), balanced_df.values.tolist(), color=colors[9])
axes[9].set_title('Мажоритарная сбалансированная точность по врачам')
axes[9].set_xlabel('Врач')
axes[9].set_ylabel('Мажоритарная сбалансированная точность')
axes[9].tick_params(axis='x', rotation=45)
plt.subplots_adjust(hspace=0.6)
plt.tight_layout()
让我们分析这些图表:医生之间平均相关性较高的医生通常与其他医生对图像的看法相同。 高协方差负责变异性,即该参数越高,特定医生的结果与图像上所有医生的意见差异越大。 Copa-Cohen评分有助于确定医生与其他医生的决定是否一致。 这个分数与平均相关性不同,考虑了医疗决策的随机性。
平均偏差,标准偏差,有助于查看特定医生的意见与医生的平均意见有多大偏差,但COE在数字上明确了医生平均估计的变化,即粗略地说,医生的意见与平
计算每个图像的多数评级,即根据多数意见评估病理学。 我们会认为这是一个基准。 准确性显示了医生如何正确地做出诊断(病理的缺失和存在),但完整性显示了医生在真正存在的情况下发现病理的程度。 平衡准确度是在没有病理时医生预测的病例数倍于存在时的情况下计算的,即轻微的不平衡。
fig, axes = plt.subplots(figsize=(7, 7))
axes.bar(accuracy_df.index.tolist(), accuracy_df.values.tolist(), color=colors[6], alpha=1, label='Мажоритарная точность')
axes.set_title('Мажоритарная точность и сбалансированная точность по врачам')
axes.set_xlabel('Врач')
axes.set_ylabel('Мажоритарная точность, сбалансированная точность')
axes.tick_params(axis='x', rotation=45)
axes.bar(balanced_df.index.tolist(), balanced_df.values.tolist(), color=colors[9], alpha=0.5, label='Мажоритарная сбалансированная точность')
axes.legend()
axes.set_ylim(0, 1.25)
plt.tight_layout()
plt.figure(figsize=(8, 6))
sns.boxplot(data=confidence, orient='v', color='lightblue')
plt.ylabel('Вероятность наличия патологии (%)')
plt.title('Распределение вероятностей наличия патологии')
plt.tight_layout()
plt.show()
正如你可以从上面的图表中看到的,大多数图像没有100percent保证那里有病理。 在一张照片中,所有的医生都说有一个病理。
让我们尝试创建一个综合上述所有指标的单一指标
让我们为每种疾病选择最糟糕的医生。
metrics_df = pd.concat([focus_corr_mean, average_doctor_deviation, covariance_matrix_mean, kappa_scores_mean, standard_deviation_mean, std_mean, accuracy_df, recall_df, balanced_df, errors_df.set_index('Врач')['Количество ошибок']], axis=1)
metrics_df.columns=['focus_corr_mean', 'average_doctor_deviation', 'covariance_matrix_mean', 'kappa_scores_mean', 'standard_deviation_mean', 'std_mean', 'accuracy_df', 'recall_df', 'balanced_df', 'errors_df']
metrics_df
让我们总结一下指标
scaler = MinMaxScaler()
normalized_df = pd.DataFrame(scaler.fit_transform(metrics_df), index=metrics_df.index, columns=metrics_df.columns)
normalized_df
让我们为每个指标设置一个加权因子
weights = {
'kappa_scores_mean': 0.3,
'covariance_matrix_mean': -0.2,
'average_doctor_deviation': -0.25,
'focus_corr_mean': 0.25,
'balanced_accuracy': 0.25,
'recall': 0.25,
'accuracy': 0.25,
'std_mean': -0.15,
'standard_deviation_mean': -0.10,
'errors_df': -0.2
}
让我们将组合度量计算为将加权系数乘以归一化度量,并将它们求和。
normalized_df['final_score'] = (normalized_df * pd.Series(weights)).sum(axis=1)
normalized_df['final_score'].sort_values(ascending=False)
因此,基于现有的指标,我们编写了自己的指标,计算了它,并获得了医生的"评级"。
plt.figure(figsize=(10, 6))
sns.barplot(x=normalized_df['final_score'].sort_values(ascending=False).values, y=normalized_df['final_score'].sort_values(ascending=False).index, palette='coolwarm')
plt.title('Рейтинг врачей')
plt.xlabel('Рейтинг')
plt.ylabel('Врач')
plt.show()
正如你可以从上面的图表中看到,最糟糕的医生是- 15, 2, 8, 6, 7
让我们形成最终的标记并计算医生的信心。
你可以考虑到医生的整体评级,也就是说,如果15个医生中有14个放了1个,那么就有病理学等等。 但是,可以考虑医生的评级。
让我们计算最终的诊断
votes = focus.sum(axis=1)
f = focus * normalized_df['final_score']
final_votes = (f.sum(1) > 0.4).astype(int)
confidence__ = focus.sum(axis=1)
weights_ = {
'doctor' : 0.4,
'confidence': 0.6
}
Diagnosis_weights = ((confidence__ / focus.shape[1]) * weights_['confidence'] + (f.sum(1) > 0.4).astype(int) * weights_['doctor'])
Diagnosis = (Diagnosis_weights >= 0.36).astype(int)
Diagnosis.sum()
让我们计算医生的信心
normalized_diagnosis_weights = (Diagnosis_weights - np.min(Diagnosis_weights)) / (np.max(Diagnosis_weights) - np.min(Diagnosis_weights))
confidence = normalized_diagnosis_weights * 100
confidence.sort_values(ascending=False)
我们来计算置信区间
mean = np.mean(Diagnosis_weights)
std_err = sem(Diagnosis_weights)
confidence_level = 0.95
n = len(Diagnosis_weights)
h = std_err * t.ppf((1 + confidence_level) / 2, n - 1)
lower_bound = mean - h
upper_bound = mean + h
# Вывод доверительного интервала
print(f"Доверительный интервал: [{lower_bound}, {upper_bound}]")
让我们分析接收到的数据。
Pd。系列诊断包含关于特定图像的确切诊断的摘要标签。 诊断是根据医生的评分计算的,并且还考虑了病理的概率。 将特定图像的这种概率计算为图像上医生标记的平均值。
图像中是否存在病理的最终概率,基于上述数据,被计算为上述分量的加权和
以0.4的权重计算医生的评级,基于平均值的概率权重为0.6。 选择这些权重是因为,如果15名医生中有14名医生确认没有病理,并且评分最高的1名医生(例如0.4)表示有病理,那么加权总和将很高,结果会出现假阳性。 归一化_diagnosis_weights-加权和。
normalized_diagnosis_weights-图像中是否存在病理的确定性。 她得到了回报。 基于该置信度,选择图像的最终标签。
我取了0.4的阈值,即如果诊断的权重超过0.4,则存在病理学。 结果:揭示了25个具有病理学的图像。
还计算了诊断确定性的置信区间,其中大多数概率为95%[0.07206422989620075, 0.1021915353029607], 也就是说,大多数图像的最终诊断为0
df = pd.concat([Diagnosis, confidence], axis=1)
df.columns = ['Diagnosis', 'Probability, %']
df['Probability, %'] = df.apply(lambda row: 100 - row['Probability, %'] if row['Diagnosis'] == 0 else row['Probability, %'], axis=1)
df
上面的dataframe包含最终诊断和诊断确实正确的百分比概率。 对于每张图片,记录诊断和诊断正确的概率。
让我们评估每种病理的标记的最终质量。
mean_probability = np.mean(Diagnosis_weights)
print(f"Средняя вероятность: {mean_probability}")
# Стандартное отклонение вероятностей
std_deviation = np.std(Diagnosis_weights)
print(f"Стандартное отклонение вероятностей: {std_deviation}")
# Максимальная и минимальная вероятность
max_probability = np.max(Diagnosis_weights)
min_probability = np.min(Diagnosis_weights)
print(f"Максимальная вероятность: {max_probability}")
print(f"Минимальная вероятность: {min_probability}")
正如你所看到的,大多数诊断都是阴性的,也就是说,没有病理。
accuracy_d = accuracy_score(diagnosis_major, Diagnosis)
recall_d = recall_score(diagnosis_major, Diagnosis)
balanced_accuracy_d = balanced_accuracy_score(diagnosis_major, Diagnosis)
F1_d = f1_score(diagnosis_major, Diagnosis)
fbeta_d = fbeta_score(diagnosis_major, Diagnosis, beta=0.5)
roc_auc_d = roc_auc_score(diagnosis_major, Diagnosis)
print(f"Точность: {accuracy_d}")
print(f"Полнота: {recall_d}")
print(f"Сбалансированная Точность: {balanced_accuracy_d}")
print(f"F1: {F1_d}")
print(f"Fbeta: {fbeta_d}")
print(f"roc-auc: {roc_auc_d}")
高准确度(96.2%)表明大多数预测是正确的。
完整性(62.1%)表明缺少一些阳性病例。
平衡精度(80.3%)和ROC-AUC(80.3%)-良好的阶级辨别能力。
F1评分(66.7%)和Fbeta评分(69.8%)表明最终诊断质量平衡。
confusion_matrix_ = confusion_matrix(diagnosis_major, Diagnosis)
disp = ConfusionMatrixDisplay(confusion_matrix_)
disp.plot(cmap=plt.cm.Blues)
plt.title('Confusion Matrix')
误差矩阵清楚地表明,没有很多误诊。
precision, recall, _ = precision_recall_curve(diagnosis_major, Diagnosis)
average_precision = average_precision_score(diagnosis_major, Diagnosis)
plt.figure()
plt.step(recall, precision, where='post', color='b', alpha=0.2, linestyle='-', linewidth=2, label='Precision-Recall curve')
plt.fill_between(recall, precision, step='post', alpha=0.2, color='b')
plt.xlabel('Recall')
plt.ylabel('Precision')
plt.title('Precision-Recall curve: AP={0:0.2f}'.format(average_precision))
plt.legend(loc="lower left")
从上面的图表中可以看出,准确性比完整性更重要,但是,从图表中可以看出,精度略低于准确性,这表明不准确的阳性诊断。
从上面的标记质量评估可以看出,标记执行得相当好,存在很小的差距。 然而,值得考虑的是,"参考标记",可以这么说,目标,由医生计算为平均分数,可能无法准确反映现实。 这是因为一些医生的评分很低,甚至平均值也可能不准确。 然而,值得考虑的是,医生虽然很低,但具有一致性,这可能表明最终标记一致性的正确性。
结论
在这个例子中,使用数据分析方法对医生的结论进行了分析。







