[PYTHON] In Anbetracht der Situation in Japan durch den Statistiker Nate Silver: "Die Anzahl der mit Coronavirus infizierten Personen ist bedeutungslos."

Einführung

Der amerikanische Statistiker [Nate Silver](https://ja.wikipedia.org/wiki/%E3%83%8D%E3%82%A4%E3%83%88] ist berühmt für die Wahlvorhersage der US-Präsidentschaftswahlen % E3% 83% BB% E3% 82% B7% E3% 83% AB% E3% 83% 90% E3% 83% BC) auf einer Website namens FiveThirtyEight , "Coronavirus-Fallzahlen sind bedeutungslos" Ich werde.

Die Behauptung in diesem Artikel lautet ** "Wenn Sie nicht genug über die Durchführung der Tests wissen, ist die gemeldete Anzahl von COVID-19-Infektionen kein nützlicher Indikator [^ 1]." * Es wird genannt *. Während ich intuitiv denke, "das ist richtig", ist die Virusinfektion ein exponentielles Phänomen. Selbst wenn die Anzahl der Tests (möglicherweise ** willkürlich **) eingegrenzt wird, ist das Reproduktionsverhältnis Ich sehe und höre auch Behauptungen, dass (später erklärt) beobachtbar sein sollten. Aus einer völlig anderen Sicht gibt es Behauptungen, dass die medizinische Versorgung zusammenbrechen wird, wenn die Tests nicht eingegrenzt werden. Nates Artikel enthält eine einfache Simulation, die Leser als Excel-Tabelle ausprobieren können (https://fivethirtyeight.com/wp-content/uploads/2020/04/covidia_1.05_updated.xlsx). Damit Sie erfahren können, wie die Anzahl der infizierten Personen gemeldet wird und wie viele Personen tatsächlich infiziert sind, hängt von der Testrichtlinie (Auswahl des Testziels und der Gesamtzahl der Tests) und der Infektionskontrolle (soziale Distanzierung und Sperrung) ab. Es ist geworden. Sie können auch den Unterschied zwischen der tatsächlichen Wiedergaberate und der beobachteten scheinbaren Wiedergaberate beobachten.

Das Tabellenkalkulationsmodell [^ 2] ist praktisch, um einfach mit den Parametern zu spielen und den Unterschied in den Ergebnissen zu erkennen. In jeder Zelle der Tabelle steht jedoch $ \ text {BH32} $ "column". Es wird durch eine Kombination aus "= Alphabet" und "Zeile = Zahl" dargestellt, und ihre Beziehung ist in einer BASIC-ähnlichen Formel geschrieben, sodass es nicht sehr gut geeignet ist, zu verstehen, was wie berechnet wird. ..

[^ 2]: Nate Silver hat die Tabelle nicht als Modell gelesen. Es soll nichts vorhersagen.

Also habe ich das Tabellenkalkulationsmodell (manuell) in ein Python-Programm konvertiert, um zu verstehen, was ich tat. Außerdem habe ich die aktuelle Situation in Japan (22. April 2020) als Parameter angegeben und ausprobiert. Obwohl die gemeldete Anzahl infizierter Personen gerade 10.000 überschritten hat, übersteigt die tatsächliche Anzahl der Patienten tatsächlich 7,5 Millionen , was bedeutet, dass etwa 6% der Gesamtbevölkerung infiziert sind. Ich hab es geschafft. Dabei sollten Sie denken: " Wenn Sie nach draußen gehen, ist die nächste Person eine infizierte Person **".

Programm

Unter [github] befindet sich ein Jupyter-Notizbuch (https://github.com/survivor7777777/covid-19/blob/master/Infected%20vs%20Detected%20analysis.ipynb).

Wenn Sie sich das Infektionsphänomen als ein Phänomen vorstellen, das kontinuierlich auftritt, wird es zu einer Differentialgleichung, und es ist für einen Amateur schwierig, damit umzugehen. Daher wird es als ein sehr grobes diskretes Phänomen angesehen. Mit anderen Worten, betrachten Sie die Infektionskette als eine Kette von der vorherigen Generation [^ 3] zur nächsten Generation und vereinfachen Sie die Berechnung weiter, indem Sie den Zeitunterschied zwischen den Generationen auf eine bestimmte Konstante festlegen (z. B. 5 Tage).

Der wichtigste Parameter ist die Reproduktionsrate ($ R $), die angibt, wie viele $ n + 1 $ infizierte Personen in der $ n $ -Generation die Krankheit weitergeben. .. Dies hängt von der Infektiosität der Krankheit ab, aber auch vom Verhalten der Menschen in der Gesellschaft. Die Reproduktionsrate des Kreuzfahrtschiffes wäre sehr hoch gewesen, und die Reproduktionsrate von Wuhan wäre niedrig gewesen, da es gesperrt und alle Ausflüge verboten waren. Wenn $ R $ größer als 1 ist, breitet sich die Infektion aus, und wenn sie kleiner als 1 ist, verschwindet die Krankheit schließlich.

[^ 3]: Natürlich ist diese Generation nicht die Babyboomer-Generation oder die tausendjährige Generation, aber die ersten infizierten Menschen werden die 0. Generation genannt, die von dort infizierten Menschen werden die 1. Generation genannt und so weiter.

Parametereinstellungen

Die Parameter, die sich auf die Ausbreitung der Krankheit beziehen, werden durch die Klasse "Disease_spread_params" ausgedrückt.

Populationsparameter werden durch die Klasse "Population_params" dargestellt.

Parameter, die sich auf PCR-Tests beziehen, werden durch die Klasse "Testing_params" ausgedrückt.

Die Werte dieser Parameter werden als Standardwerte angegeben, indem plausible Werte aus verschiedenen Veröffentlichungen ausgewählt werden.

%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

Als Funktion zum Runden einer reellen Zahl auf eine Ganzzahl verwende ich hier meine eigene Funktion namens "iround ()" anstelle der in Python integrierten "round ()". Dies dient der Kompatibilität mit der Excel-Funktion ROUND [^ 4].

[^ 4]: Pythons round () wird auf eine gerade Zahl gerundet, während EXCELsround ()gerundet wird.

Simulator

Während ich mir die zellenweisen Abhängigkeiten der Tabelle ansah, auf die ich mich bezog, verstand ich, wie sie berechnet und neu implementiert wurde. Um ehrlich zu sein, ist es schmutzig (nicht bescheiden).

Die Simulator-Klasse nimmt einen Parameter, instanziiert ihn, führt ihn durch Aufrufen der run () -Methode aus und gibt das Ergebnis als pandas.DataFrame zurück.

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'])

Simulation

Covidia-Szenario 1

Nate Silvers Artikel enthält eine Simulation des virtuellen Landes Covidia. Die folgende Grafik ist eine Visualisierung der Ergebnisse. Covidia hat 10 Millionen Einwohner und wird sich mit der Ankunft der ersten infizierten Person am 1. Januar 2020 ausbreiten. Wir gehen von einer anfänglichen Reproduktionsrate von 2,7 $, einer Zwischenstufe von 1,4 $ und einer Sperrrate von 0,7 $ aus.

fig1.png

Die blaue Linie ist die wahre Anzahl neu infizierter Personen, und die gelbe Linie ist der Übergang der Anzahl neu infizierter Personen, die bei der Inspektion gemeldet wurden. In diesem Szenario ist die anfängliche Testkapazität mit 1.000 pro Generation gering. Wenn Sie jedoch die Anzahl der Tests auf unbestimmte Zeit erhöhen, scheint die Anzahl der Berichte entsprechend der tatsächlichen Anzahl infizierter Personen zu steigen, obwohl es eine Zeitverzögerung gibt. ..

Wenn Sie genau hinschauen, können Sie jedoch feststellen, dass in der Phase, in der die Anzahl der infizierten Personen zunimmt, diese um das 20-fache oder mehr unterschätzt wird und wenn sie tendenziell abnimmt, wird sie überschätzt.

Japanisches Szenario?

Japan wurde dafür kritisiert, die Anzahl der PCR-Tests niedrig zu halten. Das Ministerium für Gesundheit, Arbeit und Soziales berichtet täglich [^ 5], aber es wird gesagt, dass die Anzahl der Inspektionen immer noch nicht mit der Nachfrage Schritt hält. Daher habe ich versucht, was passieren würde, wenn die Anzahl der PCR-Tests für die japanische Bevölkerung von 126 Millionen niedrig eingestellt wäre. Übrigens habe ich auch die Anzahl der positiv bestätigten Personen aufgezeichnet, die bisher tatsächlich gemeldet wurden. Während die obige Grafik die Anzahl der neu infizierten Personen zeigt, ist dies die kumulierte Anzahl der infizierten Personen [^ 6].

[^ 5]: In Neuester Bericht wurden beispielsweise 7826 neue PCR-Tests aus dem Bericht des Vortages durchgeführt. Sie können sehen, dass. Im Gegensatz zur vorherigen Abbildung handelt es sich um eine grafische Darstellung der kumulierten Anzahl infizierter Personen. [^ 6]: Die tatsächlichen Daten werden kumulativ angegeben, und ich dachte, das wäre mir vertrauter.

![fig2.png] (https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223481/193b7bce-f37f-4b9e-727b-223c95b838f5.png)

Hier ist die grüne Linie die tatsächliche Anzahl infizierter Menschen in Japan [^ 6].

[^ 6]: Ich verwende die von der John Hopkins University auf [github] veröffentlichten Daten (https://github.com/CSSEGISandData/COVID-19/tree/master/csse_covid_19_data/csse_covid_19_time_series).

Ich habe nicht vor, in dieser Simulation etwas vorherzusagen, aber zuerst möchte ich, dass Sie die Größe der Differenz zwischen dem wahren Wert (blaue Linie) und dem Simulationsergebnis und dem gemeldeten Wert (grüne Linie) sehen. Folgendes kann beobachtet werden.

Aber was wäre, wenn wir unsere Testkapazität frühzeitig erhöht und unbegrenzt getestet hätten? Das folgende Diagramm zeigt die Ergebnisse, wenn der Anfangswert der Inspektionskapazität auf 10.000 eingestellt ist und die Inspektionskapazität schrittweise, aber unbegrenzt erhöht wird.

fig3.png

Selbst in diesem Fall ist es immer noch nicht möglich, die tatsächliche Anzahl infizierter Personen zu erfassen, aber wir konnten etwa das Zehnfache der aktuellen Anzahl von Reportern beobachten. Obwohl der Anstieg der Anzahl der Reporter (gelb) etwas langsam ist, liegt die Steigung nahe an der Steigung der tatsächlichen Anzahl infizierter Personen, sodass der Unterschied zwischen den beiden als der Unterschied in der Richtung von links nach rechts (Zeitverzögerung) angesehen werden kann, was korrekt ist. Es scheint, dass die Produktionsrate von $ R $ beobachtet werden kann.

Zusammenfassung

Ich denke, es ist wahr, dass es keinen Unterschied in Bezug auf die Behandlung gibt, da es keine Krankenhäuser oder Isolationseinrichtungen gibt, in denen die infizierte Person bekannt ist. Ich denke jedoch, dass sich das tägliche Verhalten der Menschen ein wenig ändern kann, wenn sie wissen, dass die Anzahl der tatsächlich infizierten Menschen 10-mal höher ist als angegeben und in einigen Fällen 100-mal höher.

Ich frage mich, ob die Experten für Infektionswissenschaft im Fernsehen eine viel detailliertere Version dieses Modells erstellen, um die Zukunft vorherzusagen.

Ich weiß es nicht.

Recommended Posts

In Anbetracht der Situation in Japan durch den Statistiker Nate Silver: "Die Anzahl der mit Coronavirus infizierten Personen ist bedeutungslos."
Ich habe versucht, die Anzahl der mit dem Coronavirus infizierten Menschen in Japan nach der Methode des neuesten Papiers in China vorherzusagen
Stellen wir uns die Anzahl der mit Matplotlib mit dem Coronavirus infizierten Personen vor
Sagen Sie die Anzahl der mit COVID-19 infizierten Personen mit Prophet voraus
Ich habe versucht, die Anzahl der mit dem Coronavirus infizierten Personen unter Berücksichtigung der Auswirkung des Verzichts auf das Ausgehen vorherzusagen
Erstellen Sie einen BOT, der die Anzahl der infizierten Personen in der neuen Corona anzeigt
Ein Server, der mit Flasche.py und OpenCV die Anzahl der Personen vor der Kamera zurückgibt
[Homologie] Zählen Sie mit Python die Anzahl der Löcher in den Daten
Ich habe versucht, die Anzahl der im Inland infizierten Menschen der neuen Korona mit einem mathematischen Modell vorherzusagen
Erstellen Sie einen Bot, der die Anzahl der Personen, die für das neue Corona-Virus in Tokio positiv sind, an Slack sendet
Generieren Sie eine Liste mit der Anzahl der Tage im aktuellen Monat.
Zeigen Sie den Status der COVID 19-Infektion in Japan mit Splunk an (GitHub-Version).
Berechnen wir den Übergang der Grundreproduktionszahl des neuen Koronavirus nach Präfektur