Engee 文档
Notebook

医疗医疗报告分析

在本演示中,将使用分析工具分析医疗数据。

在分析数据集期间,为医生工作的一致性和准确性计算了几个指标。

必须安装库

您必须手动指定项目所在的路径才能转到工作目录,以及安装必要的依赖项。

In [ ]:
!cd /user/Demo_public/biomedical/analis_medical_result
In [ ]:
 !pip install -r /user/Demo_public/biomedical/analis_medical_result/requirements.txt
In [ ]:
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行中包含的内容。

In [ ]:
focus = pd.read_csv('/user/Demo_public/biomedical/analis_medical_result/Дата.csv')
In [ ]:
focus.head()
Out[0]:
ID Файла Врач№1 Врач№2 Врач№3 Врач№4 Врач№5 Врач№6 Врач№7 Врач№8 Врач№9 Врач№10 Врач№11 Врач№12 Врач№13 Врач№14 Врач№15
0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0
1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
2 2 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
4 4 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1
In [ ]:
focus = focus.drop('ID Файла', axis=1)

这些文件包含图像的标记。 15名医生中的每一位都根据476张照片在没有或存在病理学的情况下做出结论。

您可以估计每张图片对病理学有信心的医生数量,并预先评估哪里真的有病理学,哪里没有病理学。 你也可以在医生的结果中寻找强烈的差异。 有可能确定医生在病理学的定义中有多少相互同意。

我们将评估每位病理医生的标记质量。

In [ ]:
focus_corr = focus.corr(method='spearman')
filtered = focus_corr.where(focus_corr > 0.5).abs()

从相关表中可以看出,关于妇科的决定至少有50%重合的医生并不多。 存在弱相关性。

然而,有两对医生(3和13)(12和10)具有平均相关强度。 这清楚地表明,这两对往往使大多数图片相同的标记。 这可能表明它们之间关于图像中是否存在病理的高度一致性。

In [ ]:
focus_corr_ = focus.corr(method='spearman')
filtered_ = focus_corr_.where(focus_corr < 0.1)

也有一对医生给出完全相反的答案,这意味着他们的意见几乎是100%不同。

In [ ]:
focus_corr_mean = focus_corr.mean(axis=1).sort_values(ascending=False)
focus_corr_mean
Out[0]:
Врач№1     0.425707
Врач№3     0.394424
Врач№12    0.392694
Врач№10    0.384297
Врач№9     0.384255
Врач№13    0.376615
Врач№14    0.336404
Врач№5     0.328782
Врач№4     0.322743
Врач№11    0.317238
Врач№15    0.316465
Врач№6     0.312953
Врач№2     0.307039
Врач№7     0.297679
Врач№8     0.143105
dtype: float64

上表显示了每个医生与其他医生的平均相关性,这些信息清楚地表明每个医生的意见与其他人的认同程度。

In [ ]:
plt.figure(figsize=(8,8))
sns.heatmap(focus_corr.round(2), annot=True, center=0, cmap='inferno')
plt.title('Корреляция между разметками врачей')
Out[0]:
Text(0.5, 1.0, 'Корреляция между разметками врачей')
In [ ]:
plt.figure(figsize=(8,8))
sns.heatmap(filtered.round(2), annot=True, center=0, cmap='inferno')
plt.title('Корреляция между разметками врачей с средней по силе корреляции')
Out[0]:
Text(0.5, 1.0, 'Корреляция между разметками врачей с средней по силе корреляции')

让我们看看有多少医生对每张图片的病理学百分比有信心。

In [ ]:
confidence = focus.sum(axis=1).sort_values(ascending=False) / focus.shape[1] * 100
confidence.head(20)
Out[0]:
433    100.000000
394     86.666667
28      80.000000
346     80.000000
352     80.000000
359     73.333333
182     73.333333
134     73.333333
113     73.333333
383     73.333333
398     73.333333
153     73.333333
295     66.666667
120     66.666667
342     60.000000
103     60.000000
341     53.333333
387     53.333333
339     53.333333
192     53.333333
dtype: float64

因此,在上表中,您可以观察到相应ID的图片中病理的百分比概率,同时考虑到医生的决定。

In [ ]:
average_marking = focus.mean(axis=1)
In [ ]:
average_doctor_deviation = focus.sub(average_marking, axis=0).abs().mean().sort_values(ascending=False)
average_doctor_deviation
Out[0]:
Врач№15    0.245702
Врач№2     0.211880
Врач№6     0.168553
Врач№7     0.153878
Врач№8     0.145073
Врач№4     0.144095
Врач№12    0.140741
Врач№14    0.124948
Врач№11    0.122991
Врач№10    0.121454
Врач№1     0.117540
Врач№5     0.116841
Врач№9     0.116143
Врач№13    0.110552
Врач№3     0.106918
dtype: float64

15号医生的平均偏差最高(0.2457),这意味着他的标记与所有医生的平均标记显着不同。 这可能表明标记中可能存在的错误。

3号医生的平均偏差最低(0.1069),这表明他的标记与所有医生的平均标记的高度一致性。 这位医生最常以与其他大多数医生相同的方式标记照片。

因此,在这里获得了与所有医生的平均标记的平均偏差。

接下来,我们计算医生评估的协方差矩阵

In [ ]:
covariance_matrix = focus.cov()
covariance_matrix
Out[0]:
Врач№1 Врач№2 Врач№3 Врач№4 Врач№5 Врач№6 Врач№7 Врач№8 Врач№9 Врач№10 Врач№11 Врач№12 Врач№13 Врач№14 Врач№15
Врач№1 0.094031 0.046544 0.032090 0.029244 0.031869 0.054032 0.030905 0.004118 0.033649 0.046152 0.026346 0.047610 0.033750 0.041832 0.048782
Врач№2 0.046544 0.181157 0.022775 0.038361 0.020176 0.038414 0.016357 0.008676 0.025206 0.047540 0.023492 0.061801 0.028082 0.033111 0.060079
Врач№3 0.032090 0.022775 0.051645 0.022770 0.020017 0.022466 0.026743 0.007183 0.029262 0.021814 0.021431 0.027699 0.030407 0.027047 0.027972
Врач№4 0.029244 0.038361 0.022770 0.107033 0.018313 0.038643 0.020387 0.011500 0.030209 0.040260 0.016780 0.049346 0.020158 0.015248 0.039612
Врач№5 0.031869 0.020176 0.020017 0.018313 0.053512 0.017917 0.020176 0.004955 0.026994 0.023704 0.012883 0.021088 0.019780 0.020572 0.016846
Врач№6 0.054032 0.038414 0.022466 0.038643 0.017917 0.138479 0.016939 -0.003788 0.030896 0.040022 0.020031 0.047073 0.023871 0.026346 0.055983
Врач№7 0.030905 0.016357 0.026743 0.020387 0.020176 0.016939 0.110195 0.015446 0.025673 0.023030 0.033296 0.034023 0.017811 0.023307 0.034168
Врач№8 0.004118 0.008676 0.007183 0.011500 0.004955 -0.003788 0.015446 0.057220 0.003550 0.000172 0.008390 0.009967 0.004827 0.001321 0.007201
Врач№9 0.033649 0.025206 0.029262 0.030209 0.026994 0.030896 0.025673 0.003550 0.073472 0.036084 0.021788 0.038705 0.026826 0.027086 0.037326
Врач№10 0.046152 0.047540 0.021814 0.040260 0.023704 0.040022 0.023030 0.000172 0.036084 0.090693 0.016133 0.058731 0.023492 0.025369 0.054225
Врач№11 0.026346 0.023492 0.021431 0.016780 0.012883 0.020031 0.033296 0.008390 0.021788 0.016133 0.064531 0.025540 0.023241 0.019542 0.021524
Врач№12 0.047610 0.061801 0.027699 0.049346 0.021088 0.047073 0.034023 0.009967 0.038705 0.058731 0.025540 0.125478 0.029183 0.023691 0.067874
Врач№13 0.033750 0.028082 0.030407 0.020158 0.019780 0.023871 0.017811 0.004827 0.026826 0.023492 0.023241 0.029183 0.055371 0.024602 0.026730
Врач№14 0.041832 0.033111 0.027047 0.015248 0.020572 0.026346 0.023307 0.001321 0.027086 0.025369 0.019542 0.023691 0.024602 0.075234 0.028302
Врач№15 0.048782 0.060079 0.027972 0.039612 0.016846 0.055983 0.034168 0.007201 0.037326 0.054225 0.021524 0.067874 0.026730 0.028302 0.208657
In [ ]:
covariance_matrix_mean = covariance_matrix.mean(axis=1).sort_values(ascending=False)
covariance_matrix_mean
Out[0]:
Врач№15    0.049019
Врач№12    0.044520
Врач№2     0.043451
Врач№1     0.040064
Врач№6     0.037822
Врач№10    0.036495
Врач№4     0.033191
Врач№9     0.031115
Врач№7     0.029897
Врач№14    0.027507
Врач№3     0.026088
Врач№13    0.025875
Врач№11    0.023663
Врач№5     0.021920
Врач№8     0.009383
dtype: float64

15号医生的平均协方差最高(0.0525)。 这表明15号医生的标记平均与其他医生的标记更一致。 这位医生最常以与其他医生相同的方式注意到病理的存在或不存在。

8号医生的平均协方差最低(0.0101)。 这表明8号医生的标记平均与其他医生的标记不太一致。 这位医生最常评估病理的存在或不存在与其他医生不同。

可以看出,15号医生的平均协方差最高,平均偏差最高。 这可能是由于医生对图片中病理的看法与大多数意见一致,但是,他可能会比其他人更经常地注意到隐藏的特征,并且稍微高估了它们。

In [ ]:
doctor_13 = focus['Врач№13']
doctor_3 = focus['Врач№3']
doctor_12 = focus['Врач№12']
doctor_10 = focus['Врач№10']
In [ ]:
cohen_kappa_score(doctor_13, doctor_3), cohen_kappa_score(doctor_12, doctor_10)
Out[0]:
(np.float64(0.5681836885853017), np.float64(0.5380704515191865))

这确认了根据图像的估计的最高度一致的对的相关表。

In [ ]:
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()
In [ ]:
fleiss_kappa(counts)
Out[0]:
np.float64(0.2591132582884047)

该测试显示所有医生的注释的一致性。 从结果可以看出,一致性很低,医生对病理学的存在存在存在分歧。

In [ ]:
kappa_scores = pd.DataFrame(focus.columns, focus.columns)
In [ ]:
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)

由于这个标志,你可以找出哪些医生彼此更同意。

In [ ]:
kappa_scores_mean = kappa_scores.mean(1).sort_values(ascending=False)
kappa_scores_mean
Out[0]:
Врач№1     0.411056
Врач№12    0.374667
Врач№10    0.370726
Врач№9     0.370257
Врач№3     0.369324
Врач№13    0.355202
Врач№14    0.324713
Врач№4     0.311602
Врач№5     0.310791
Врач№11    0.304169
Врач№6     0.296735
Врач№7     0.286314
Врач№2     0.275085
Врач№15    0.270183
Врач№8     0.137958
dtype: float64

从这个板块可以看出,15号医生在医生中具有最低的平均网络一致性之一。

Kappa Cohen系数与相关表不同,调节随机巧合

这可能是由于这样的事实,医生经常同意与其他医生,但他做出更积极或消极的诊断。

让我们从医生的平均值计算医生结果的标准偏差

In [ ]:
standard_deviation_mean=focus.sub(focus.mean(axis=1), axis=0).pow(2).mean().sort_values(ascending=False)
standard_deviation_mean
Out[0]:
Врач№15    0.174125
Врач№2     0.140303
Врач№6     0.096976
Врач№7     0.082301
Врач№8     0.073496
Врач№4     0.072518
Врач№12    0.069164
Врач№14    0.053371
Врач№11    0.051414
Врач№10    0.049877
Врач№1     0.045963
Врач№5     0.045264
Врач№9     0.044566
Врач№13    0.038975
Врач№3     0.035341
dtype: float64

在这种情况下,来自医生平均值的MSE有助于了解哪些医生具有最高偏差,他们最常给出与其他医生不一致的特定快照的估计。

In [ ]:
std_mean=focus.std().sort_values(ascending=False)
std_mean
Out[0]:
Врач№15    0.456790
Врач№2     0.425625
Врач№6     0.372128
Врач№12    0.354229
Врач№7     0.331956
Врач№4     0.327159
Врач№1     0.306645
Врач№10    0.301153
Врач№14    0.274288
Врач№9     0.271057
Врач№11    0.254030
Врач№8     0.239208
Врач№13    0.235310
Врач№5     0.231327
Врач№3     0.227254
dtype: float64

COEX系统有助于看到变异最大的医生.

接下来,我们将为每张图片获得一个"参考分数",该分数将根据选票总和计算,也就是说,如果大多数医生赞成具有病理,那么我们设置1,否则-0。

In [ ]:
diagnosis_major = pd.Series([0 if x > y else 1 for x, y in zip(counts[:, 0], counts[:, 1])])
In [ ]:
accuracy_df = focus.apply(lambda x: accuracy_score(diagnosis_major, x)).sort_values(ascending=False)
accuracy_df
Out[0]:
Врач№3     0.955975
Врач№5     0.949686
Врач№13    0.947589
Врач№9     0.943396
Врач№1     0.943396
Врач№14    0.932914
Врач№11    0.928721
Врач№10    0.926625
Врач№12    0.893082
Врач№4     0.888889
Врач№7     0.884696
Врач№8     0.882600
Врач№6     0.870021
Врач№2     0.807128
Врач№15    0.761006
dtype: float64

上表显示了医生图像的评估与基于多数概念获得的参考估计进行比较的准确性,即医生对特定图像的诊断的确认越多,它确实是病理的可能性就越高。

In [ ]:
recall_df = focus.apply(lambda x: recall_score(diagnosis_major, x)).sort_values(ascending=False)
recall_df
Out[0]:
Врач№15    0.965517
Врач№1     0.896552
Врач№2     0.862069
Врач№12    0.827586
Врач№6     0.793103
Врач№10    0.724138
Врач№9     0.689655
Врач№14    0.620690
Врач№7     0.586207
Врач№4     0.586207
Врач№3     0.586207
Врач№5     0.551724
Врач№13    0.551724
Врач№11    0.482759
Врач№8     0.034483
dtype: float64

召回可用于评估医生正确识别图像中病理存在的频率。

In [ ]:
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()
In [ ]:
errors_df = pd.DataFrame(list(confusion_matrices.items()), columns=['Врач', 'Количество ошибок']).sort_values(ascending=False, by='Количество ошибок')
errors_df
Out[0]:
Врач Количество ошибок
14 Врач№15 114
1 Врач№2 92
5 Врач№6 62
7 Врач№8 56
6 Врач№7 55
3 Врач№4 53
11 Врач№12 51
9 Врач№10 35
10 Врач№11 34
13 Врач№14 32
0 Врач№1 27
8 Врач№9 27
12 Врач№13 25
4 Врач№5 24
2 Врач№3 21

通过计算FP+FN,我们可以看到哪些医生的错误最多。

In [ ]:
counts[:, 0].sum(), counts[:, 1].sum()
Out[0]:
(np.int64(6316), np.int64(839))

(6316,839)-医生诊断的总数。 6316-医生对病理缺席的投票数量,839-存在。 正如你所看到的,有一个轻微的类不平衡,让我们尝试从sklearn应用accuracy_balanced。

In [ ]:
balanced_df = focus.apply(lambda x: balanced_accuracy_score(diagnosis_major, x)).sort_values(ascending=False)
balanced_df
Out[0]:
Врач№1     0.921490
Врач№12    0.862454
Врач№15    0.856643
Врач№6     0.834052
Врач№2     0.832820
Врач№10    0.831935
Врач№9     0.824738
Врач№14    0.786907
Врач№3     0.783059
Врач№5     0.763585
Врач№13    0.762469
Врач№4     0.747345
Врач№7     0.745112
Врач№11    0.720174
Врач№8     0.485991
dtype: float64

让我们构建图表,直观地显示关于哪个医生最好地应对诊断的信息。 总共计算了10个指标,我们将构建10个图表。

In [ ]:
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在数字上明确了医生平均估计的变化,即粗略地说,医生的意见与平

计算每个图像的多数评级,即根据多数意见评估病理学。 我们会认为这是一个基准。 准确性显示了医生如何正确地做出诊断(病理的缺失和存在),但完整性显示了医生在真正存在的情况下发现病理的程度。 平衡准确度是在没有病理时医生预测的病例数倍于存在时的情况下计算的,即轻微的不平衡。

In [ ]:
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()
In [ ]:
plt.figure(figsize=(8, 6))
sns.boxplot(data=confidence, orient='v', color='lightblue')
plt.ylabel('Вероятность наличия патологии (%)')
plt.title('Распределение вероятностей наличия патологии')
plt.tight_layout()
plt.show()

正如你可以从上面的图表中看到的,大多数图像没有100percent保证那里有病理。 在一张照片中,所有的医生都说有一个病理。

让我们尝试创建一个综合上述所有指标的单一指标

让我们为每种疾病选择最糟糕的医生。

In [ ]:
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']
In [ ]:
metrics_df
Out[0]:
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
Врач№1 0.425707 0.117540 0.040064 0.411056 0.045963 0.306645 0.943396 0.896552 0.921490 27
Врач№3 0.394424 0.106918 0.026088 0.369324 0.035341 0.227254 0.955975 0.586207 0.783059 21
Врач№12 0.392694 0.140741 0.044520 0.374667 0.069164 0.354229 0.893082 0.827586 0.862454 51
Врач№10 0.384297 0.121454 0.036495 0.370726 0.049877 0.301153 0.926625 0.724138 0.831935 35
Врач№9 0.384255 0.116143 0.031115 0.370257 0.044566 0.271057 0.943396 0.689655 0.824738 27
Врач№13 0.376615 0.110552 0.025875 0.355202 0.038975 0.235310 0.947589 0.551724 0.762469 25
Врач№14 0.336404 0.124948 0.027507 0.324713 0.053371 0.274288 0.932914 0.620690 0.786907 32
Врач№5 0.328782 0.116841 0.021920 0.310791 0.045264 0.231327 0.949686 0.551724 0.763585 24
Врач№4 0.322743 0.144095 0.033191 0.311602 0.072518 0.327159 0.888889 0.586207 0.747345 53
Врач№11 0.317238 0.122991 0.023663 0.304169 0.051414 0.254030 0.928721 0.482759 0.720174 34
Врач№15 0.316465 0.245702 0.049019 0.270183 0.174125 0.456790 0.761006 0.965517 0.856643 114
Врач№6 0.312953 0.168553 0.037822 0.296735 0.096976 0.372128 0.870021 0.793103 0.834052 62
Врач№2 0.307039 0.211880 0.043451 0.275085 0.140303 0.425625 0.807128 0.862069 0.832820 92
Врач№7 0.297679 0.153878 0.029897 0.286314 0.082301 0.331956 0.884696 0.586207 0.745112 55
Врач№8 0.143105 0.145073 0.009383 0.137958 0.073496 0.239208 0.882600 0.034483 0.485991 56

让我们总结一下指标

In [ ]:
scaler = MinMaxScaler()
normalized_df = pd.DataFrame(scaler.fit_transform(metrics_df), index=metrics_df.index, columns=metrics_df.columns)
In [ ]:
normalized_df
Out[0]:
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
Врач№1 1.000000 0.076536 0.774068 1.000000 0.076536 0.345876 0.935484 0.925926 1.000000 0.064516
Врач№3 0.889305 0.000000 0.421469 0.847191 0.000000 0.000000 1.000000 0.592593 0.682131 0.000000
Врач№12 0.883183 0.243706 0.886512 0.866755 0.243706 0.553179 0.677419 0.851852 0.864440 0.322581
Врач№10 0.853470 0.104733 0.684026 0.852324 0.104733 0.321947 0.849462 0.740741 0.794362 0.150538
Врач№9 0.853321 0.066465 0.548299 0.850607 0.066465 0.190834 0.935484 0.703704 0.777837 0.064516
Врач№13 0.826287 0.026183 0.416106 0.795481 0.026183 0.035093 0.956989 0.555556 0.634853 0.043011
Врач№14 0.683997 0.129909 0.457279 0.683842 0.129909 0.204907 0.881720 0.629630 0.690969 0.118280
Врач№5 0.657025 0.071501 0.316315 0.632860 0.071501 0.017741 0.967742 0.555556 0.637416 0.032258
Врач№4 0.635656 0.267875 0.600673 0.635830 0.267875 0.435245 0.655914 0.592593 0.600124 0.344086
Врач№11 0.616178 0.115811 0.360295 0.608615 0.115811 0.116653 0.860215 0.481481 0.537734 0.139785
Врач№15 0.613442 1.000000 1.000000 0.484167 1.000000 1.000000 0.000000 1.000000 0.851096 1.000000
Врач№6 0.601015 0.444109 0.717502 0.581393 0.444109 0.631160 0.559140 0.814815 0.799222 0.440860
Врач№2 0.580087 0.756294 0.859540 0.502117 0.756294 0.864227 0.236559 0.888889 0.796394 0.763441
Врач№7 0.546967 0.338369 0.517571 0.543235 0.338369 0.456147 0.634409 0.592593 0.594998 0.365591
Врач№8 0.000000 0.274924 0.000000 0.000000 0.274924 0.052077 0.623656 0.000000 0.000000 0.376344

让我们为每个指标设置一个加权因子

In [ ]:
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
}

让我们将组合度量计算为将加权系数乘以归一化度量,并将它们求和。

In [ ]:
normalized_df['final_score'] = (normalized_df * pd.Series(weights)).sum(axis=1)
In [ ]:
normalized_df['final_score'].sort_values(ascending=False)
Out[0]:
Врач№3     0.392190
Врач№13    0.338965
Врач№1     0.303614
Врач№9     0.294062
Врач№5     0.256713
Врач№10    0.217204
Врач№14    0.184836
Врач№11    0.178581
Врач№12    0.070730
Врач№4     0.001668
Врач№7    -0.063771
Врач№6    -0.157113
Врач№8    -0.179304
Врач№2    -0.423276
Врач№15   -0.601389
Name: final_score, dtype: float64

因此,基于现有的指标,我们编写了自己的指标,计算了它,并获得了医生的"评级"。

In [ ]:
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()
/tmp/ipykernel_201/2568690680.py:2: FutureWarning: 

Passing `palette` without assigning `hue` is deprecated and will be removed in v0.14.0. Assign the `y` variable to `hue` and set `legend=False` for the same effect.

  sns.barplot(x=normalized_df['final_score'].sort_values(ascending=False).values, y=normalized_df['final_score'].sort_values(ascending=False).index, palette='coolwarm')

正如你可以从上面的图表中看到,最糟糕的医生是- 15, 2, 8, 6, 7

让我们形成最终的标记并计算医生的信心。

你可以考虑到医生的整体评级,也就是说,如果15个医生中有14个放了1个,那么就有病理学等等。 但是,可以考虑医生的评级。

让我们计算最终的诊断

In [ ]:
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)
In [ ]:
Diagnosis.sum()
Out[0]:
np.int64(25)

让我们计算医生的信心

In [ ]:
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)
Out[0]:
433    100.0
394     92.0
28      88.0
346     88.0
352     88.0
       ...  
472      0.0
3        0.0
474      0.0
475      0.0
1        0.0
Length: 477, dtype: float64

我们来计算置信区间

In [ ]:
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}]")
Доверительный интервал: [0.07206422989620075, 0.1021915353029607]

让我们分析接收到的数据。

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

In [ ]:
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
Out[0]:
Diagnosis Probability, %
0 0 92.0
1 0 100.0
2 0 96.0
3 0 100.0
4 0 88.0
... ... ...
472 0 100.0
473 0 84.0
474 0 100.0
475 0 100.0
476 0 96.0

477 rows × 2 columns

上面的dataframe包含最终诊断和诊断确实正确的百分比概率。 对于每张图片,记录诊断和诊断正确的概率。

让我们评估每种病理的标记的最终质量。

In [ ]:
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}")
Средняя вероятность: 0.08712788259958072
Стандартное отклонение вероятностей: 0.16725534527288208
Максимальная вероятность: 1.0
Минимальная вероятность: 0.0

正如你所看到的,大多数诊断都是阴性的,也就是说,没有病理。

In [ ]:
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}")
Точность: 0.9622641509433962
Полнота: 0.6206896551724138
Сбалансированная Точность: 0.8025323275862069
F1: 0.6666666666666666
Fbeta: 0.6976744186046512
roc-auc: 0.8025323275862069

高准确度(96.2%)表明大多数预测是正确的。

完整性(62.1%)表明缺少一些阳性病例。

平衡精度(80.3%)和ROC-AUC(80.3%)-良好的阶级辨别能力。

F1评分(66.7%)和Fbeta评分(69.8%)表明最终诊断质量平衡。

In [ ]:
confusion_matrix_ = confusion_matrix(diagnosis_major, Diagnosis)
disp = ConfusionMatrixDisplay(confusion_matrix_)
disp.plot(cmap=plt.cm.Blues)
plt.title('Confusion Matrix')
Out[0]:
Text(0.5, 1.0, 'Confusion Matrix')

误差矩阵清楚地表明,没有很多误诊。

In [ ]:
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")
Out[0]:
<matplotlib.legend.Legend at 0x7f8fa9817790>

从上面的图表中可以看出,准确性比完整性更重要,但是,从图表中可以看出,精度略低于准确性,这表明不准确的阳性诊断。

从上面的标记质量评估可以看出,标记执行得相当好,存在很小的差距。 然而,值得考虑的是,"参考标记",可以这么说,目标,由医生计算为平均分数,可能无法准确反映现实。 这是因为一些医生的评分很低,甚至平均值也可能不准确。 然而,值得考虑的是,医生虽然很低,但具有一致性,这可能表明最终标记一致性的正确性。

结论

在这个例子中,使用数据分析方法对医生的结论进行了分析。