Statisticien américain [Nate Silver](https://ja.wikipedia.org/wiki/%E3%83%8D%E3%82%A4%E3%83%88] célèbre pour la prédiction électorale de l'élection présidentielle américaine % E3% 83% BB% E3% 82% B7% E3% 83% AB% E3% 83% 90% E3% 83% BC) sur un site appelé FiveThirtyEight , "Le nombre de cas de coronavirus n'a pas de sens" Je vais.
L'allégation dans cet article est: ** «À moins que vous ne sachiez suffisamment comment les tests sont effectués, le nombre d'infections au COVID-19 signalé n'est pas un indicateur utile [^ 1]. On l'appelle *. Bien que je pense intuitivement «c'est vrai», l'infection virale est un phénomène exponentiel, donc même si le nombre de tests est réduit (peut-être ** arbitrairement **), le taux de reproduction Je vois et j'entends aussi des affirmations qui (expliquées plus tard) devraient être observables. D'un point de vue complètement différent, on prétend que les soins médicaux s'effondreront si les tests ne sont pas limités. L'article de Nate comprend une simulation simple que les lecteurs peuvent essayer sous forme de feuille de calcul Excel (https://fivethirtyeight.com/wp-content/uploads/2020/04/covidia_1.05_updated.xlsx) Pour que vous puissiez découvrir comment le nombre de personnes infectées est signalé et le nombre réel de personnes infectées est différent en fonction de la politique de test (comment sélectionner la cible de test et le nombre total de tests) et du contrôle de l'infection (distanciation sociale et verrouillage). Il est devenu. Vous pouvez également observer la différence entre le taux de reproduction réel et le taux de reproduction apparent observé.
Le modèle de feuille de calcul [^ 2] est pratique pour jouer facilement avec les paramètres et voir la différence dans les résultats, mais chaque cellule de la feuille de calcul indique $ \ text {BH32} $ "colonne" Il est représenté par une combinaison de "= alphabet" et "ligne = nombre", et leur relation est écrite dans une formule de type BASIC, donc elle n'est pas très appropriée pour comprendre ce qui est calculé et comment. ..
[^ 2]: Nate Silver n'a pas lu la feuille de calcul comme modèle. Il ne s'agit pas de prédire quoi que ce soit.
J'ai donc converti le modèle de feuille de calcul en un programme Python (manuellement) pour comprendre ce que je faisais. De plus, j'ai donné la situation actuelle au Japon (22 avril 2020) comme paramètre et l'ai essayé. Ensuite, bien que le nombre déclaré de personnes infectées vient de dépasser 10 000, le nombre réel de patients dépasse en fait ** 7,5 millions , ce qui signifie qu'environ 6% de la population totale est infectée. J? ai compris. Avec cela, vous devriez penser: " Si vous sortez, la personne suivante est une personne infectée **".
Il y a un notebook jupyter sur github.
Si vous considérez le phénomène d'infection comme un phénomène qui se produit en continu, cela devient une équation différentielle et il est difficile pour un amateur de le gérer, il est donc considéré comme un phénomène discret très grossier. En d'autres termes, considérez la chaîne d'infection comme une chaîne de la génération précédente [^ 3] à la génération suivante, et simplifiez encore le calcul en fixant la différence de temps entre les générations à une certaine constante (par exemple, 5 jours).
Le paramètre le plus important est le taux de reproduction ($ R $), qui spécifie combien d'individus infectés $ n + 1 $ dans la génération $ n $ transmettront la maladie. .. Cela dépend de l'infectivité de la maladie, mais aussi du comportement des personnes dans la société. Le taux de reproduction du bateau de croisière aurait été très élevé et le taux de reproduction de Wuhan aurait été faible depuis qu'il a été verrouillé et toutes les sorties ont été interdites. Si $ R $ est supérieur à 1, l'infection se propage et s'il est inférieur à 1, la maladie finira par disparaître.
[^ 3]: Bien sûr, cette génération n'est pas la génération du baby-boom ou la génération du millénaire, mais les premières personnes infectées sont appelées la 0ème génération, les personnes infectées à partir de là sont appelées la 1ère génération, et ainsi de suite.
Les paramètres liés à la propagation de la maladie sont exprimés par la classe «Disease_spread_params».
R_uncontrolled
: Taux de reproduction lorsqu'aucune mesure n'est prise.R_intermediate
: taux de reproduction à mi-parcours auxquels les gens font plus attention, tels que la distance sociale, les pratiques de lavage des mains et le port de masques.R_lockdown
: Taux de reproduction lorsque le verrouillage est appliqué.cluster
: paramètres à ajuster lorsque la proportion de personnes infectées dans la population augmente (0 = aucun ajustement, 0,5: légèrement, 1: modérément, 2: significativement)begin_internmediate
: numéro de génération qui entre dans l'étape intermédiaire après la fin de la période de non-mesurebegin_lockdown
: numéro de génération où le verrouillage commenceLes paramètres de population sont représentés par la classe «Population_params».
population
: population totale de ce paysfaux_severe
: Pourcentage de patients gravement malades avec des causes similaires au COVID-19 autres que le coronavirusfaux_mild
: Pourcentage de patients modérément malades avec des causes similaires au COVID-19 autres que le coronavirusdesire_severe
: Pourcentage de patients gravement malades recherchant un test PCRdesire_mild
: Pourcentage de patients modérément malades recherchant un test PCRdesire_asymptomatic
: Pourcentage de personnes asymptomatiques cherchant un test PCRLes paramètres liés aux tests PCR sont exprimés par la classe Testing_params
.
ramp_period
: numéro de génération pour commencer à augmenter le nombre d'inspectionstest_growth_rate
: Taux d'augmentation depuis le début de l'augmentation du nombre de teststest_max
: nombre maximum de testsrationed_test_ratio
: Sévère> Modéré> Pourcentage de tests garantis pour respecter les priorités asymptomatiquesfalse_negative
: pourcentage de faux négatifs dans les tests PCRfalse_positive
: pourcentage de faux positifs dans les tests PCRdelay
: délai entre l'inspection et le résultat (nombre de générations)Les valeurs de ces paramètres sont données par défaut en sélectionnant des valeurs plausibles dans divers articles.
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import time, re
import datetime
iround = lambda x: int((x * 2 + 1) // 2)
class Disease_spread_params:
def __init__(self,
R_uncontrolled = 2.7,
R_intermediate = 1.4,
R_lockdown = 0.7,
cluster = 0.5,
begin_intermediate = 11,
begin_lockdown = 15,
mild = 0.6,
asymptomatic = 0.3,
zero_date = '2020-01-01',
serial = 5):
self.R_uncontrolled = R_uncontrolled
self.R_intermediate = R_intermediate
self.R_lockdown = R_lockdown
self.cluster = cluster
self.begin_intermediate = begin_intermediate
self.begin_lockdown = begin_lockdown
self.severe = 1 - mild - asymptomatic
self.mild = mild
self.asymptomatic = asymptomatic
self.zero_date = zero_date
self.serial = serial
class Population_params:
def __init__(self,
population = 10_000_000,
initial_infection = 1,
faux_severe = 0.001,
faux_mild = 0.025,
desire_severe = 1.0,
desire_mild = 0.5,
desire_asymptomatic = 0.02):
self.population = population
self.initial_infection = initial_infection
self.faux_severe = faux_severe
self.faux_mild = faux_mild
self.desire_severe = desire_severe
self.desire_mild = desire_mild
self.desire_asymptomatic = desire_asymptomatic
class Testing_params:
def __init__(self,
initial_tests = 1_000,
ramp_period = 3,
test_growth_rate = 0.5,
test_max = 10_000_000,
rationed_test_ratio = 0.75,
false_negative = 0.20,
false_positive = 0.002,
delay = 2):
self.initial_tests = initial_tests
self.ramp_period = ramp_period
self.test_growth_rate = test_growth_rate
self.test_max = test_max
self.rationed_test_ratio = rationed_test_ratio
self.false_negative = false_negative
self.false_positive = false_positive
self.delay = delay
Ici, pour arrondir un nombre réel à un entier, j'utilise ma propre fonction appelée ʻiround () au lieu de la fonction intégrée de Python
round () . Ceci est pour la compatibilité avec la fonction Excel
ROUND` [^ 4].
[^ 4]: «round ()» de Python est arrondi à un nombre pair, tandis que «round ()» d'EXCEL est arrondi.
En examinant les dépendances cellule par cellule de la feuille de calcul dont je parlais, j'ai compris comment elle avait été calculée et réimplémentée. Pour être honnête, c'est sale (pas humble).
La classe Simulator
prend un paramètre, l'instancie, l'exécute en appelant la méthode run ()
et renvoie le résultat sous la forme pandas.DataFrame
.
class Simulator:
def __init__(self,
disease_spread_params,
population_params,
testing_params):
self.disease_spread_params = disease_spread_params
self.population_params = population_params
self.testing_params = testing_params
self.columns = [ 'date', 'actual.R', 'doubling time in days' ]
self.date_regex = re.compile('(\d+)-(\d+)-(\d+)')
def decode_date(self, date):
match = self.date_regex.fullmatch(date)
if match:
y, m, d = match.group(1, 2, 3)
timestamp = time.mktime((int(y), int(m), int(d), 0, 0, 0, -1, -1, -1))
return timestamp
return None
def encode_date(self, timestamp):
t = time.localtime(timestamp)
return '{0:04d}-{1:02d}-{2:02d}'.format(t[0], t[1], t[2])
def run(self, zero_day = '2020-01-01', generations = 40):
self.generations = generations
self.df = pd.DataFrame(index = range(0, self.generations))
t0 = self.decode_date(zero_day)
self.df['date'] = [ self.encode_date(t0 + d * self.disease_spread_params.serial * 24 * 60 * 60) for d in range(0, self.generations) ]
self.set_target_R()
self.compute_actual_infection()
self.compute_tests()
return self.df
def set_target_R(self):
begin_lockdown = self.disease_spread_params.begin_lockdown
begin_intermediate = self.disease_spread_params.begin_intermediate
self.df['target_R'] = np.NaN
for i in range(0, self.generations):
if begin_lockdown != None and i >= begin_lockdown:
self.df.at[i, 'target_R'] = self.disease_spread_params.R_lockdown
elif begin_intermediate != None and i >= begin_intermediate:
self.df.at[i, 'target_R'] = self.disease_spread_params.R_intermediate
else:
self.df.at[i, 'target_R'] = self.disease_spread_params.R_uncontrolled
def compute_actual_infection(self):
population = self.population_params.population
initial_infection = self.population_params.initial_infection
cluster = self.disease_spread_params.cluster
serial = self.disease_spread_params.serial
df = self.df
df['susceptible'] = np.NaN
df.at[0, 'susceptible'] = population - initial_infection
df['new_infection'] = np.NaN
df.at[0, 'new_infection'] = initial_infection
df['cumulative_infection'] = np.NaN
df.at[0, 'cumulative_infection'] = initial_infection
df['actual_R'] = np.NaN
df['actual_doubling_time'] = np.NaN
for i in range(1, self.generations):
df.at[i, 'new_infection'] = iround(df.at[i-1, 'susceptible']*(1-(1-((df.at[i-1, 'target_R']*(df.at[i-1, 'susceptible']/population)**cluster)/population))**df.at[i-1, 'new_infection']))
df.at[i, 'cumulative_infection'] = df.at[i-1, 'cumulative_infection'] + df.at[i, 'new_infection']
df.at[i, 'susceptible'] = population - df.at[i, 'cumulative_infection']
df.at[i-1, 'actual_R'] = df.at[i, 'new_infection'] / df.at[i-1, 'new_infection']
if df.at[i-1, 'cumulative_infection'] != 0:
df.at[i-1, 'actual_doubling_time'] = np.inf if df.at[i, 'new_infection'] == 0 else serial * np.log(2) / np.log(df.at[i, 'cumulative_infection']/df.at[i-1, 'cumulative_infection'])
def compute_tests(self):
population = self.population_params.population
ramp_period = self.testing_params.ramp_period
tests_max = self.testing_params.test_max
test_growth_rate = self.testing_params.test_growth_rate
rationed_test_ratio = self.testing_params.rationed_test_ratio
mild = self.disease_spread_params.mild
asymptomatic = self.disease_spread_params.asymptomatic
faux_severe = self.population_params.faux_severe
faux_mild = self.population_params.faux_mild
desire_severe = self.population_params.desire_severe
desire_mild = self.population_params.desire_mild
desire_asymptomatic = self.population_params.desire_asymptomatic
false_negative = self.testing_params.false_negative
false_positive = self.testing_params.false_positive
delay = self.testing_params.delay
serial = self.disease_spread_params.serial
cumulative_tests_conducted = 0
cumulative_detected_cases = 0
df = self.df
df['tests_available'] = 0
df['new_detected_cases'] = 0
df['cumulative_detected_cases'] = 0
for i in range(0, self.generations):
if i == 0:
df.at[i, 'tests_available'] = 0
elif i == 1:
df.at[i, 'tests_available'] = self.testing_params.initial_tests
elif i < ramp_period:
df.at[i, 'tests_available'] = df.at[i-1, 'tests_available']
else:
df.at[i, 'tests_available'] = iround(min(tests_max, df.at[i-1, 'tests_available'] * (1 + test_growth_rate)))
tests_available = df.at[i, 'tests_available']
rationed_tests = iround(tests_available * rationed_test_ratio)
on_demand_tests = tests_available - rationed_tests
new_infection_severe = iround(df.at[i, 'new_infection'] * (1 - mild - asymptomatic))
new_infection_mild = iround(df.at[i, 'new_infection'] * mild)
new_infection_asymptomatic = df.at[i, 'new_infection'] - new_infection_severe - new_infection_mild
population_severe = iround((population - df.at[i, 'new_infection']) * faux_severe) + new_infection_severe
population_mild = iround((population - df.at[i, 'new_infection']) * faux_mild) + new_infection_mild
population_asymptomatic = population - population_severe - population_mild
desiring_tests_severe = iround(population_severe * desire_severe * (1 - cumulative_tests_conducted/population))
desiring_tests_mild = iround(population_mild * desire_mild * (1 - cumulative_tests_conducted/population))
desiring_tests_asymptomatic = iround(population_asymptomatic * desire_asymptomatic * (1 - cumulative_tests_conducted/population))
alloc_rationed_tests_severe = min(rationed_tests, desiring_tests_severe)
alloc_rationed_tests_mild = min(desiring_tests_mild, rationed_tests-alloc_rationed_tests_severe)
alloc_rationed_tests_asymptomatic = min(desiring_tests_asymptomatic, rationed_tests-alloc_rationed_tests_severe-alloc_rationed_tests_mild)
unfilled_test_demand_severe = desiring_tests_severe - alloc_rationed_tests_severe
unfilled_test_demand_mild = desiring_tests_mild - alloc_rationed_tests_mild
unfilled_test_demand_asymptomatic = desiring_tests_asymptomatic - alloc_rationed_tests_asymptomatic
unfilled_test_demand = unfilled_test_demand_severe + unfilled_test_demand_mild + unfilled_test_demand_asymptomatic
alloc_on_demand_tests_severe = 0 if unfilled_test_demand == 0 else iround(on_demand_tests * unfilled_test_demand_severe / unfilled_test_demand)
alloc_on_demand_tests_mild = 0 if unfilled_test_demand == 0 else iround(on_demand_tests * unfilled_test_demand_mild / unfilled_test_demand)
alloc_on_demand_tests_asymptomatic = 0 if unfilled_test_demand == 0 else iround(on_demand_tests * unfilled_test_demand_asymptomatic / unfilled_test_demand)
tests_conducted_severe = alloc_rationed_tests_severe + alloc_on_demand_tests_severe
tests_conducted_mild = alloc_rationed_tests_mild + alloc_on_demand_tests_mild
tests_conducted_asymptomatic = alloc_rationed_tests_asymptomatic + alloc_on_demand_tests_asymptomatic
df.at[i, 'tests_conducted_severe'] = tests_conducted_severe
df.at[i, 'tests_conducted_mild'] = tests_conducted_mild
df.at[i, 'tests_conducted_asymptomatic'] = tests_conducted_asymptomatic
tests_conducted = tests_conducted_severe + tests_conducted_mild + tests_conducted_asymptomatic
cumulative_tests_conducted += tests_conducted
positive_tests_severe = iround(tests_conducted_severe * new_infection_severe / population_severe * (1 - false_negative)) + \
iround(tests_conducted_severe * (1 - new_infection_severe / population_severe) * false_positive)
positive_tests_mild = iround(tests_conducted_mild * new_infection_mild / population_mild * (1 - false_negative)) + \
iround(tests_conducted_mild * (1 - new_infection_mild / population_mild) * false_positive)
positive_tests_asymptomatic = iround(tests_conducted_asymptomatic * new_infection_asymptomatic / population_asymptomatic * (1 - false_negative)) + \
iround(tests_conducted_asymptomatic * (1 - new_infection_asymptomatic / population_asymptomatic) * false_positive)
if i+delay < self.generations:
df.at[i+delay, 'new_detected_cases'] = positive_tests_severe + positive_tests_mild + positive_tests_asymptomatic
cumulative_detected_cases += df.at[i, 'new_detected_cases']
df.at[i, 'cumulative_detected_cases'] = cumulative_detected_cases
if i > 0 and df.at[i-1, 'new_detected_cases'] > 0:
df.at[i-1, 'observed_R'] = df.at[i, 'new_detected_cases'] / df.at[i-1, 'new_detected_cases']
df.at[i-1, 'observed_doubling_time'] = np.inf if df.at[i, 'new_detected_cases'] == 0 else serial * np.log(2) / np.log(df.at[i, 'cumulative_detected_cases']/df.at[i-1, 'cumulative_detected_cases'])
L'article de Nate Silver présente une simulation du pays virtuel Covidia. Le graphique ci-dessous est une visualisation des résultats. Covidia compte 10 millions d'habitants et commencera à se propager avec l'arrivée de la première personne infectée le 1er janvier 2020. Nous supposons un taux de reproduction initial de 2,7 $, une étape intermédiaire de 1,4 $ et un verrouillage de 0,7 $.
La ligne bleue représente le nombre réel de personnes nouvellement infectées et la ligne jaune correspond à la transition du nombre de personnes nouvellement infectées signalées par l'inspection. Dans ce scénario, la capacité de test initiale est aussi petite que 1000 par génération, mais en augmentant indéfiniment le nombre de tests, il semble que le nombre de rapports augmente en fonction du nombre réel de personnes infectées, bien qu'il y ait un décalage dans le temps. ..
Cependant, si vous regardez de plus près, vous pouvez voir que dans la phase où le nombre de personnes infectées augmente, il est sous-estimé de 20 fois ou plus, et lorsqu'il a tendance à diminuer, il est surestimé.
Le Japon a été critiqué pour avoir maintenu le nombre de tests PCR à un faible niveau. Le ministère de la Santé, du Travail et du Bien-être rend compte quotidiennement [^ 5], mais on dit que le nombre d'inspections ne suit toujours pas la demande. Par conséquent, j'ai essayé ce qui se passerait si le nombre de tests PCR était bas pour la population japonaise de 126 millions. En passant, j'ai également tracé le nombre de personnes confirmées positives qui ont été effectivement signalées jusqu'à présent. Alors que le graphique ci-dessus montre le nombre de personnes nouvellement infectées, il s'agit du nombre cumulé de personnes infectées [^ 6].
[^ 5]: Par exemple, dans Dernier rapport, 7826 nouveaux tests PCR ont été effectués à partir du rapport de la veille. Tu peux voir ça. Contrairement à la figure précédente, il s'agit d'un graphique du nombre cumulé de personnes infectées. [^ 6]: Les données réelles sont données de manière cumulative, et je pensais que cela m'était plus familier.
![fig2.png] (https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223481/193b7bce-f37f-4b9e-727b-223c95b838f5.png)
Ici, la ligne verte représente le nombre réel de personnes infectées au Japon [^ 6].
[^ 6]: J'utilise les données publiées par l'Université John Hopkins sur github.
Je n'ai pas l'intention de prédire quoi que ce soit dans cette simulation, mais j'aimerais d'abord que vous voyiez l'ampleur de la différence entre la valeur réelle (ligne bleue) et le résultat de la simulation et la valeur rapportée (ligne verte). Ce qui suit peut être observé.
Mais que se passerait-il si nous avions augmenté notre capacité de test dès le début et avions des tests illimités? Le graphique suivant montre les résultats lorsque la valeur initiale de la capacité d'inspection est fixée à 10 000 et que la capacité d'inspection est augmentée progressivement mais de manière illimitée.
Même dans ce cas, il n'est toujours pas possible de capturer le nombre réel de personnes infectées, mais nous avons pu observer environ 10 fois le nombre actuel de journalistes. De plus, bien que l'augmentation du nombre de reporters (jaune) soit un peu lente, la pente est proche de la pente du nombre réel de personnes infectées, donc la différence entre les deux peut être considérée comme la différence dans le sens gauche-droite (délai), ce qui est correct. Il semble que le taux de production de $ R $ puisse être observé.
Je pense qu'il est vrai qu'il n'y a pas de différence en termes de traitement parce qu'il n'y a pas d'hôpitaux ou d'établissements d'isolement où la personne infectée est connue. Cependant, je pense que le comportement quotidien des gens peut changer un peu s'ils savent que le nombre de personnes véritablement infectées est 10 fois plus élevé que celui rapporté, et dans certains cas 100 fois plus élevé.
Je me demande si les experts de la télévision en sciences infectieuses élaborent une version beaucoup plus détaillée de ce modèle pour prédire l'avenir.
Je ne sais pas.
Recommended Posts