ある感染症の感染者と非感染者を識別する問題を考えます。ある検査法において陽性・陰性という結果が出た時、検査結果は次の4種類に分類できます。
- TP (True Positive) :検査結果が陽性と出た感染者
- FN (False Negative) :検査結果が陰性と出た感染者
- FP (False Positive):検査結果が陽性と出た非感染者
- TN (True Negative) :検査結果が陰性と出た非感染者
このとき、正答率(正解率)は Accuracy = (TP + TN) / (TP + FN + FP + TN) と計算できます。
このとき、次の問題について考えてみましょう。
【問題】「正答率9割」を謳っている検査法がある。この検査法で正答率を計算した時点において、検査を受けた人の50%が感染者の場合、10%が感染者の場合、1%が感染者の場合という3つのケースに対して、陽性者が感染者である確率を推定しなさい。
この問題に答えるPythonコードを書いてみます。
日本語版 matplotlib をインストール
!pip install japanize-matplotlib
Collecting japanize-matplotlib
[?25l Downloading https://files.pythonhosted.org/packages/aa/85/08a4b7fe8987582d99d9bb7ad0ff1ec75439359a7f9690a0dbf2dbf98b15/japanize-matplotlib-1.1.3.tar.gz (4.1MB)
[K |████████████████████████████████| 4.1MB 4.0MB/s
[?25hRequirement already satisfied: matplotlib in /usr/local/lib/python3.6/dist-packages (from japanize-matplotlib) (3.2.2)
Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.6/dist-packages (from matplotlib->japanize-matplotlib) (2.4.7)
Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.6/dist-packages (from matplotlib->japanize-matplotlib) (1.3.1)
Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.6/dist-packages (from matplotlib->japanize-matplotlib) (0.10.0)
Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.6/dist-packages (from matplotlib->japanize-matplotlib) (2.8.1)
Requirement already satisfied: numpy>=1.11 in /usr/local/lib/python3.6/dist-packages (from matplotlib->japanize-matplotlib) (1.19.5)
Requirement already satisfied: six in /usr/local/lib/python3.6/dist-packages (from cycler>=0.10->matplotlib->japanize-matplotlib) (1.15.0)
Building wheels for collected packages: japanize-matplotlib
Building wheel for japanize-matplotlib (setup.py) ... [?25l[?25hdone
Created wheel for japanize-matplotlib: filename=japanize_matplotlib-1.1.3-cp36-none-any.whl size=4120276 sha256=286795079465880c2c9d5d742e7259a51cb2f2923b5bf644de4121990dcccd1b
Stored in directory: /root/.cache/pip/wheels/b7/d9/a2/f907d50b32a2d2008ce5d691d30fb6569c2c93eefcfde55202
Successfully built japanize-matplotlib
Installing collected packages: japanize-matplotlib
Successfully installed japanize-matplotlib-1.1.3
「検査法の正答率9割」を全列挙する
どのような場合に「検査法の正答率9割」になるのか、全列挙するコードはこちらになります。
import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib
percentage_infected = 0.1 # 検査を受けた人のうち感染者の割合
population_size = 100 # 検査を受けた人数
target_accuracy = 0.9 # 正答率の値
def enumerate_cases(percentage_infected, population_size, target_accuracy):
infected = population_size * percentage_infected # 感染者の数
notinfected = population_size * (1 - percentage_infected) # 非感染者の数
case_id = 0
for TP in range(int(infected) + 1): # 全感染者のうちのTPの数
FN = int(infected - TP) # 全感染者のうちTPを除いた数がFN
for TN in range(int(notinfected) + 1): # 全非感染者のうちのTNの数
FP = int(notinfected - TN) # 全非感染者のうちTNを除いた数がFP
accuracy = (TP+TN)/(TP+FN+FP+TN) # 正答率の計算
if accuracy == target_accuracy: # 目的の正答率と一致したら表示
case_id += 1
print("Case {}".format(case_id))
fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(12, 4))
axes[0].set_title("検査人数: {}名".format(population_size))
axes[0].pie([TP, FN, FP, TN], counterclock=False, autopct="%.1f%%", startangle=90,
labels=["TP(感染・陽性)", "FN(感染・陰性)", "FP(非感染・陽性)", "TN(非感染・陰性)"],
colors=["red", "orange", "pink", "skyblue"])
axes[1].set_title("検査結果が陽性: {}名".format(TP+FP))
axes[1].pie([TP, FP], counterclock=False, autopct="%.1f%%", startangle=90,
labels=["TP(感染・陽性)", "FP(非感染・陽性)"], colors=["red", "pink"])
axes[2].set_title("検査結果が陰性: {}名".format(FN+TN))
axes[2].pie([FN, TN], counterclock=False, autopct="%.1f%%", startangle=90,
labels=["FN(感染・陰性)", "TN(非感染・陰性)"], colors=["orange", "skyblue"])
plt.show()
50%が感染者(検査人数100人)の場合
percentage_infected = 0.5 # 検査を受けた人のうち感染者の割合
population_size = 100 # 検査を受けた人数
target_accuracy = 0.9 # 正答率の値
enumerate_cases(percentage_infected, population_size, target_accuracy)
Case 1
Case 2
Case 3
Case 4
Case 5
Case 6
Case 7
Case 8
Case 9
Case 10
Case 11
以上の11ケースが得られました。感染者・非感染者の数が拮抗している場合は、「正答率9割」はけっこう信頼できそうな気がします。
10%が感染者(検査人数100人)の場合
percentage_infected = 0.1 # 検査を受けた人のうち感染者の割合
population_size = 100 # 検査を受けた人数
target_accuracy = 0.9 # 正答率の値
enumerate_cases(percentage_infected, population_size, target_accuracy)
Case 1
Case 2
Case 3
Case 4
Case 5
Case 6
Case 7
Case 8
Case 9
Case 10
Case 11
こちらも11ケースが得られましたが、感染者の割合が10%まで少なくなると、「正答率9割」と言えども、「検査結果が陽性」の人の中の「感染者」の割合は50%に過ぎないということが分かります。
1%が感染者(検査人数1000人)の場合
percentage_infected = 0.01 # 検査を受けた人のうち感染者の割合
population_size = 1000 # 検査を受けた人数
target_accuracy = 0.9 # 正答率の値
enumerate_cases(percentage_infected, population_size, target_accuracy)
Case 1
Case 2
Case 3
Case 4
Case 5
Case 6
Case 7
Case 8
Case 9
Case 10
Case 11
検査人数1000人の場合、1%が感染したケースは11とおり考えられますが、いずれも「陽性の人が感染者である確率」はけっこう低いことが分かります。
教訓
「正答率9割」という、たったひとつの数字だけでは正しい情報は得られないので、情報提供する側にはもっとデータを開示していただきたいし、情報を受け取る側も、たったひとつの数字だけで判断しないように気をつけていただきたいと思います。