Tag Corona

  • Die Intensiv-Antwort

    Ich habe ja immer noch nicht so recht meinen Frieden gemacht mit dem Schluss von 66 ist das neue 50 vom letzten Freitag, dass nämlich die Impfkampagne bisher fast keinen Unterschied macht für unsere Fähigkeit, hohe Inzidenzen – die wir praktisch sicher bald haben werden, wenn wir nicht wieder viel weiter zumachen als derzeit – zu ertragen, ohne dass die Intensivstationen zu einem Schlachtfeld werden.

    Die Frage lässt sich zurückführen auf: „Wie viele Intensivbetten waren eigentlich relativ zur jeweiligen Inzidenz durch SARS-2-Erkrankte belegt?“ (bei allen berechtigten Zweifeln an der zuverlässigen Messung letzterer). Diese Antwort der Intensivbelegung auf das Infektionsgeschehen – ich taufe es mal int/inc, weil ich zu faul bin, nachzusehen, wie die Profis das nennen – kriege ich mit dem Besteck vom Coronafilm-Post und meiner DIVI-Auswertung relativ schnell ausgerechnet, und wenn ich dann das Verhältnis von DIVI-Belegung (gezogen jeweils aus den RKI-Berichten der Zeit) zur bundesweiten Inzidenz (auf der Basis der Referenz-Daten des RKI) plotte, sieht das so aus:

    Ein großes Maximum im Juli 2021

    Ich habe die y-Achse wieder logarithmisch eingeteilt, in dem Fall nicht, weil ich irgendwelche exponentiellen Verläufe erwarten würde, sondern weil mir im Wesentlichen der Verlauf im unteren Teil des Wertebereichs am Herzen liegt.

    Das dominierende Feature Anfang Juli 2021 nämlich ist schnell erklärt: Hier waren die Inzidenzen sehr niedrig, die langwierigen Fälle blieben aber auf den Intensivstationen. Und so ging es bis zu 120 IntensivpatientInnen pro Punkt Inzidenz. Ähnlich reflektiert das zweithöchste Feature rund um den Februar 2021 lediglich die fallende Inzidenz und also quasi vorauseilend kleine Nenner zwischen zweiter und dritter Welle.

    Wenn mensch sich diese großen Features mal rausdenkt, kommt als Basis etwas heraus zwischen 20 und 40 Intensivpatient_innen pro Inzidenzpunkt während der Wellen. Mitte August, als die Inzidenzen kräftig anzogen, haben wir vielleicht auch mal 10 touchiert, was zu erwarten wäre, wenn die Basisrate 30 wäre und um die zwei Drittel der Leute geimpft sind. Angesichts der dramatischen Altersabhängigkeit der Hospialisierung von SARS-2 sind solche Überlegungen aber ohne demographische Betrachtungen unsinnig.

    Der Anstieg ganz am rechten Rand des Graphen dürfte übrigens zumindest in diesem Ausmaß ein Artefakt sein, denn ich rechne hier wie gesagt mit dem, was das RKI als Referenzdatum angibt (wo das Infektionsdatum das Meldedatum ersetzt, wenn das rauszukriegen ist), und es kann so sein, dass aus den letzten zwei Wochen Fälle in die weitere Vergangenheit gewandert sind, die, die sie in Zukunft aus der Gegenwart bekommen werden, aber noch nicht da sind; das würde die Inzidenz unterschätzen und mein int/inc überschätzen.

    Nehmen wir trotzdem an, wir hätten mit der derzeitigen Virenpopulation, Infektionsdemographie und Impfquote im Gleichgewicht 20 Intensivpatient_innen pro Indizenzpunkt. Wenn wir dann unter 6000 Intensivpatient_innen bleiben wollen, ist eine 300er-Inzidenz das äußerste, was mit viel Glück zu stemmen wäre. Wenn also nicht mein int/inc drastisch runtergeht – als Faustregel dürfte taugen, dass jede Halbierung des Anteils der ungeimpften Erwachsenen auch eine Halbierung von int/inc nach sich ziehen würde –, wird es noch lang dauern, bis die (relativ) strikte Kontrolle von SARS 2 aufhören kann.

    Eine 300er-Inzidenz in der RKI-Rechnung heißt ja, dass jede Woche 300 von 100000 Menschen SARS-2 hinter sich bringen. Bis dann alle mal SARS-2 hatten (und alle werden es irgendwann gehabt haben, dauerhaft sterile Impfungen gibts bei Atemwegserkrankungen nicht), dauert es logischerweise 100000/300, also gut 300 Wochen. Oder grob sechs Jahre.

    Sorry, liebe Ungeimpfte: so lange will ich echt nicht warten, bis ich nicht mehr an jeder Ecke damit rechnen muss, Namen und Geburtsdatum (im Impfzertifkat) von mir nicht annähernd kontrollierbaren Rechnern überantworten zu müssen.

    Technics

    Der Code, der das erzeugt, ist nicht spannend; mit corona.py sieht die Inzidenzberechnung so aus:

    def get_incidences(tuples):
        """returns a mapping of dates to 7-day incidences from RKI tuples
        as yielded by corona.iter_counts.
        """
        tuples.sort(key=lambda r: r[0])
        incidences = {}
        queue = collections.deque([0], maxlen=7)
        cur_date = None
    
        for tup in tuples:
            if tup[0]!=cur_date:
                if len(queue)==7:
                    incidences[cur_date] = sum(queue)/830
                queue.append(0)
                cur_date = tup[0]
    
            queue[-1] += tup[2]
    
        incidences[cur_date] = sum(queue)/83e4
        return incidences
    
       with open(corona.DATA_DIR
              +"Aktuell_Deutschland_SarsCov2_Infektionen.csv") as f:
          all_rows = list(corona.iter_counts(f, True))
      incidences = get_incidences(all_rows)
    

    Das Zusammenfummeln mit den DIVI-Daten nimmt meine aus den RKI-Berichten gescreenscrapten Daten. Das ist an sich Quatsch, und ihr solltet die Daten einfach von der DIVI selbst ziehen, wenn ihr das reproduzieren wollt.

    Mit dem zusammengefummelten Daten (Spalten: Datum, int/inc, Intensivbelegung und referenzdatenbasierte Inzidenz) könnt ihr aber selbst Plots machen. Das Bild oben kommt aus diesem Gnuplot-Skript:

    set size ratio 0.375
    set key inside left top
    set xdata time
    set yrange [5:200]
    set ytics (10,22,47,100)
    set timefmt "%Y-%m-%d"
    set xrange ['2020-08-01':'2021-08-31']
    set logscale y
    set xtics format "%Y-%m-%d" time
    set term svg size 800,300
    set out "int-vs-inc.svg"
    plot "int-vs-inc.txt" using 1:2 with lines title "int/inc"
    

    ElektronikbastlerInnen werden die Ticks auf der y-Achse wiedererkennen. Logarithmen sind überall.

  • 66 ist das neue 50

    Ich sage es nicht gerne, aber: Entweder wir machen im Winter wieder einen Lockdown oder wir kriegen ein übles Gemetzel.

    Ich hatte ja schon im Januar erzählt, warum ich die Intensivbelegung für die aussagekräftigste Zahl halte, die in den RKI-Berichten vorkommt (natürlich mit dem Nachteil, dass sie für Echtzeitsteuerung nicht taugt, weil sie so weit nachläuft). Und die sieht seit ein paar Wochen übel aus, nämlich praktisch so wie im letzten Oktober:

    Im September 2021 siehts aus wie im Oktober 2020

    SARS 2-Intensivbelegung seit Mitte letzten Jahres in einem Log-Plot (also: exponentielle Entwicklung ist eine Gerade). Zur Verdeutlichung habe ich die Kurve seit diesem Juli nochmal weiß unterlegt neben die Gesamtkurve seit August 2020 gestellt: mensch sieht, wie sich die Verläufe ähneln, abgesehen von der höheren Ausgangsbasis in diesem Jahr. Die grüne Kurve ist übrigens die Zahl der invasiv Beatmeten. Sie hört mittendrin auf, weil der RKI-Tagesbericht die entsprechenden Daten nicht mehr berichtet hat.

    Die Verdopplungszeit ist derzeit so rund drei Wochen; wir dürften also, wenn nicht ein Wunder geschieht, in sechs Wochen, Mitte Oktober, deutlich über 4000 liegen und damit in der Gegend der Notbremsenbelastung rund um Neujahr 2021.

    In der Tat scheint es, als habe die Impfkampagne enttäuschend wenig Einfluss auf diese Zahlen. Heute ist die SARS 2-Intensivbelegung laut RKI-Bericht 1169 bei einer Inzidenz von 80. Eine vergleichbare Belegung war am 23.10.2020 mit 1121 bei einer Inzidenz von 60. Von einer Entkopplung der Intensivbelegung von der Inzidenz kann also keine Rede sein, und schon gar nicht vom „200 ist das neue 50“, das Gesundheitsminister Spahn vor ein paar Wochen wagte. Dazu kommt, dass die Dunkelziffer angesichts des immer noch relativ engen Testregimes in diesem Jahr vermutlich niedriger ist als letztes Jahr und mithin die damalige Inzidenz nach oben korrigiert werden müsste, um mit aktuellen Zahlen vergleichbar zu sein. Selbst ohne so eine Korrektur ist allenfalls 50 ⋅ (80/60) = 66 das neue 50.

    Warum kaum ein Impfeffekt sichtbar ist in diesen Zahlen: Tja, das ist eine spannende Frage, und ich bin neugierig auf Christian Drostens Kommentare dazu, wenn es heute abend wieder ein neues Coronavirus-Update gibt (endlich!). Mein persönlicher Verdacht ist etwas gruselig. Wahrscheinlich stimmen die anekdotischen Berichte, dass aus den Alten- und Pflegeheimen in den ersten drei Wellen kaum Menschen in Intensivstationen überwiesen wurden und sie in aller Regel einfach an Ort und Stelle starben. Die jüngeren Menschen hingegen, die es jetzt vermehrt erwischt (auch wenn der Altersmedian weniger schwankt als ich glaubte), erhalten vermutlich erheblich häufiger Intensivpflege. Ich bin nicht sicher, ob ich da ein „hoffe ich“ dazu schreiben möchte.

    Ohne einen „Delta-Effekt“ wird diese Erklärung jedoch nicht auskommen, denn von den rund 1000 relativ jungen Menschen, denen im letzten Juni bei Tönnies SARS-2 reingedrückt wurde, landeten, soweit ich weiß, keine auf Intensivstationen – und jedenfalls starben keine. Bei vor drei Wochen (als sich heute eingelieferte so plusminus angesteckt haben werden) rund 30000 RKI-aktiven SARS-2-Fällen und einer derzeit vermutlich mit der Tönnies-Belegschaft vergleichbaren Demographie dürften die Intensivstationen eigentlich nicht volllaufen, wenn das Virus nicht eine ganze Ecke fieser geworden wäre.

    Wie dem auch sei: Meine Hoffnung, dass mit jüngeren Kranken und vielen Geimpften höhere Inzidenzen ohne Gemetzel durchhaltbar sind, war klar unbegründet. Das aber macht erneute Notbremsen ab Oktober (und vielleicht schon früher) zu einer praktischen Gewissheit. Ich vermute stark, dass diese Geimpfte anfangs ausnehmen werden. Ich nehme keine Wetten an zur Frage, ob das ausreichen wird. So'n Mist.

  • Corona-Film, Teil 2

    Am Ende meines Posts zum Corona-Inzidenzfilm hatte ich zuversichtlich gesagt, der RKI-Datensatz gebe durchaus auch Plots von Altersmedianen her. Tja… da habe ich den Mund etwas zu voll genommen, denn was wirklich drinsteht, sind Altersgruppen, und die sind so grob, dass eine halbwegs seriöse Schätzung hinreichend dekorativer (also: mit mindestens 256 verschiedenen Werten) Mediane doch einige Überlegung erfordert.

    Die Altersgruppen reichen aber auch ohne Sorgfalt, um eine Art Score auszurechnen (ich verrate gleich, wie ich das gemacht habe), und damit ergibt sich zum Beispiel dieser Film:

    (sollte euer Browser das vermurksen: Download).

    Nachtrag (2021-11-03)

    Nur damit sich niemand wundert, wie Herbst-Zahlen in einen Post vom August kommen: Ich habe den Film bis November 2021 fortgesetzt und werde ihn künftig wohl noch ein paar Mal aktualisieren.

    Was ist da zu sehen? Die Farbskala gibt etwas, das ich Alters-Score genannt habe. Dabei habe ich die Altersgruppen aus den RKI-Daten so in Zahlen übersetzt:

    A00-A04 2
    A05-A14 10
    A15-A34 20
    A35-A59 47
    A60-A79 70
    A80+ 85
    unbekant ignoriert

    Das, was dabei rauskommt, mittele ich für alle berichteten Fälle innerhalb von 14 Tagen. Die robustere und ehrlichere Alternative wäre wahrscheinlich, da einen interpolierten Median auszurechnen, aber das habe ich schon deshalb gelassen, weil ich dann möglicherweise eine obere Grenze bei A80+ hätte annehmen müssen; so, wie es ist, ist es allenfalls ein Score, dessen Vergleichbarkeit zwischen Kreisen angesichts wahrscheinlich recht weit auseinanderliegender Altersverteilungen so la-la ist. Mehr Substanz als Uni- oder Mensarankings hat er aber auf jeden Fall (was kein starker Claim ist).

    Wirklich fummelig an dieser Visualisierung war, dass für weite Zeiträume in vielen Kreisen entweder gar keine Daten vorliegen, einfach weil es keine Infektionen gab, oder die Schätzung auf so wenigen Fällen beruht, dass sie recht wenig Bedeutung hat. Letzteres würde ein starkes Blubbersignal liefern, das in Wirklichkeit nur das Rauschen schlechter Schätzungen beziehungsweise schlecht definierter Verteilungen ist.

    Deshalb habe ich die Inzidenz in die Transparenz gesteckt; zwecks Abstand vom Hintergrund-Weiß fange ich dabei aber gleich bei 20% an, und weil mir 100 Fälle robust genug erscheinen, setze ich ab einer 14-Tage-Inzidenz von 100 auch 100% Deckung. Wo Daten ganz fehlen, male ich nur die Umrisse der Kreise.

    Was habe ich aus dem Film gelernt? Nun, offen gestanden erheblich weniger als aus dem Inzidenzfilm im letzten Post. Ich hatte eigentlich gehofft, dass (mit der derzeitigen Colourmap) ein dramatischer Umschwung von rötlich nach bläulich stattfindet, wenn Anfang 2021 die Impfungen in den großen Altenpflegeeinrichtungen anlaufen. Das ist aber allenfalls dann sichtbar, wenn mensch genau drauf aufpasst. Überhaupt hat mich überrascht, wie niedrig die Alters-Scores doch meist sind. Hätte ich vorher nachgedacht, hätten sowohl die Inzidenz-Heatmap des RKI wie auch einige Prosa zu den Altersverteilungen das allerdings schon stark nahegelegt, so etwa im letzten Wochenbericht des RKI:

    Von allen Todesfällen waren 79.101 (86%) Personen 70 Jahre und älter, der Altersmedian lag bei 84 Jahren. Im Unterschied dazu beträgt der Anteil der über 70-Jährigen an der Gesamtzahl der übermittelten COVID-19-Fälle etwa 13 %.

    – Corona war zwar von den Folgen her vor allem ein Problem ziemlich alter Menschen, getragen haben die Pandemie aber praktisch durchweg die jüngeren.

    Aufschlussreich ist vielleicht, dass die Kreise meist von Blau nach Rot gehen, Ausbrüche also bei relativ jungen Personen anfangen und sich zu älteren hinbewegen. Das ist schon beim Heinsberg-Ausbruch zu sehen, der mit einem Score von 36 anfängt (das hätte ich für einen Kappenabend nie vorhergesagt) und recht monoton immer weiter steigt. Bei etwa 55 habe ich ihn aus den Augen verloren. Diese, wenn mensch so will, Rotverschiebung ist ein recht häufig zu beobachtendes Phänomen in dem Film. Mein unheimlicher Verdacht ist ja, dass dabei die outgesourcten Putz- und Pflegekräfte, die im Namen der Kostenersparnis nicht selten als Kolonne durch mehrere Altenpflegeeinrichtungen hintereinander gescheucht werden, eine große Rolle gespielt haben.

    Recht erwartbar war, dass bei den „jungen“ Kreisen regelmäßig Unistädte auftauchen, Göttingen z.B. im ansonsten ruhigen Juni 2020, während gleichzeitig in Gütersloh die Tönnies-Wanderarbeiter deutlich höhere Alters-Scores haben – beeindruckend, dass diese die Schinderei in unseren Schlachthöfen in das bei diesem Ausbruch starke A35-A59-bin durchhalten.

    In dieser Ausprägung nicht erwartet hätte ich die grün-rot-Trennung zwischen West- und Ostdeutschland in der zweiten Welle, besonders deutlich im Januar 2021. Ein guter Teil davon wird sicher die Basisdemographie sein, denn arg viele junge Leute, die überhaupt krank werden könnten, gibt es in weiten Teilen Ostdeutschlands nicht mehr. Aber so viel anders dürfte das in vielen ländlichen Kreisen Westdeutschlands auch nicht sein. Hm. Ich brauche gelegentlich nach Alter und Kreis aufgelöste Demographiedaten für die BRD.

    Nehmen wir mal den Landkreis Hof, der im Juni 2021 in den fünf jüngsten Kreisen mitspielt: da würde ich eigentlich eine recht alte Bevölkerung erwarten. Der niedrige Score in der Zeit ist also be-stimmt Folge von, jaklar, den wilden Parties der Jugend, von denen wir schon im Sommer 2020 so viel gehört haben. Naughty kids.

    Mit anderen Worten: Ich habe leider keine sehr tiefen Erkenntnisse aus der Visualisierung gezogen. Wenn das, was da gezeigt ist, nicht ziemlich ernst wäre, könnte mensch sich immerhin an der lavalampenähnlichen Erscheinung freuen.

    Technics

    Nachtrag (2022-10-27)

    Der Code ist jetzt am Codeberg

    Das Umschreiben des Codes vom vorigen Post war eine interessante Übung, die insbesondere eine (vor dem Hintergrund der Empfehlung der Gang of Four, normalerweise eher über Komposition als über Vererbung nachzudenken) recht natürliche Anwendung von Vererbung mit sich brachte, nämlich in der Plotter-Klasse. Auch die Parametrisierung dessen, worüber iteriert wird (_iter_maps, iter_freqs, iter_age_scores) war, nun, interessant.

    Das Programm hat dabei eine (fast) ordentliche Kommandozeilenschnittstelle bekommen:

    $ python3 mkmovie.py --help
    usage: mkmovie.py [-h] [-d] [-i N] [-m ISODATE] {inc,age}
    
    Make a movie from RKI data
    
    positional arguments:
      {inc,age}             select what kind of movie should be made
    
    optional arguments:
      -h, --help            show this help message and exit
      -d, --design_mode     just render a single frame from a dict left in a
                            previous run.
      -i N, --interpolate N
                            interpolate N frames for one day
      -m ISODATE, --min-date ISODATE
                            discard all records earlier than ISODATE
    

    Damit entsteht der Film oben durch:

    $ python3 mkmovie.py --min-date=2020-02-20 -i 7 age
    

    Der aktuelle Code: mkmovie.py und corona.py.

  • Corona als Film

    Inzidenzen mögen nicht mehr das ideale Mittel sein, um die aktuelle Corona-Gefährdungslage zu beschreiben, auch wenn es zumindest so lange kaum ein schnelleres Signal geben wird, wie nicht flächendeckend PCR auf Abwässer läuft. Aber sie ist bei allen auch hier angemäkelten Defiziten und Ungenauigkeiten doch kaum zu schlagen, wenn es um ein nach Zeit und Ort aufgelöstes Bild davon geht, was das Virus – Verzeihung, die Viren – so getrieben haben.

    Und nachdem ich neulich angefangen hatte, mit dem großen Infektions-Datensatz des RKI zu spielen, ist mir aufgefallen, dass es ausgehend von dem dort diskutierten Code nicht schwierig sein sollte, einen Film zu basteln, der die Inzidenzverläufe auf Kreisebene visualisiert. Nun, das habe ich gemacht:

    (sollte euer Browser das vermurksen: Download; Kreispolygone: © GeoBasis-DE / BKG 2021). Im Unterschied zu den bekannten Bildern aus dem RKI-Bericht (die die Inzidenz nach dem Meldedatum rechnen) verwende ich hier übrigens das Referenzdatum, also wenn möglich das Datum der Infektion und nur andernfalls das Meldedatum.

    Nachtrag (2021-11-03)

    Nur damit sich niemand wundert, wie Herbst-Zahlen in einen Post vom August kommen: Ich habe den Film bis November 2021 fortgesetzt und werde ihn künftig wohl noch ein paar Mal aktualisieren.

    Ein paar Dinge, die ich daraus gelernt habe:

    • Es gab offenbar schon vor Heinsberg einiges an Corona-Rauschen in der Republik (am Anfang vom Video). Wie viel davon einfach Tippfehler sind, ist natürlich schwer zu sagen. Aber einiges wird schon real sein, und dann ist sehr bemerkenswert, dass es in keiner dieser Fälle zu einem klinisch auffallenden Ausbruch gekommen ist. Das wäre eine sehr deutliche Illustration der hohen Überdispersion zumindest des ursprünglichen Virus: fast alle Infizierten steck(t)en [1] niemanden an, so dass, solange die Ausbrüche klein sind, sie schnell wieder verschwinden.
    • Ich hatte ganz vergessen, wie schnell am Anfang der Pandemie die Folgen des Bierfests in Tirschenreuth die des Kappenabends in Heinsberg überrundet hatten (nämlich so um den 10.3. rum). Es lohnt sich, im März 2020 Ostbayern im Blick zu halten.
    • Die etwas brodelnde Erscheinung des Bildes speziell in ruhigeren Phasen – wie ein träge kochender Brei vielleicht, bei dem an mehr oder minder zufälligen Stellen immer mal wieder eine Blase hochkommt – zeigt wieder, dass sich Corona vor allem in Ausbrüchen ausbreitet. Das tut es bestimmt auch in weniger ruhigen Phasen, aber dann sind überall Blasen („sprudelnd kochend“), so dass das nicht mehr auffällt.
    • Die großen Ausbrüche des Sommers 2020 (vor allem Gütersloh und Dingolfing) waren erstaunlich allein stehende Ereignisse. Wenn mensch bedenkt, dass es ja schon einen Haufen Schlachthöfe und andere ähnlich furchtbare Betriebe in der BRD gibt, fragt sich schon, warum es nicht mehr Ausbrüche im Tönnies-Stil gab. Waren die anderen Läden alle vorsichtiger? Oder hatten die einfach Glück, weil niemand mit hoher Virusausscheidung in ihre Betriebshallen gekommen ist? Sind vielleicht Ausbrüche übersehen worden? Rein demographisch nämlich waren beispielsweise die Tönnies-Leute so jung und fit, dass nur wenige im Krankenhaus landeten und keineR gestorben ist.
    • Auch in den zweiten und dritten Wellen blieb erstaunlich viel Struktur im Infektionsgeschehen – mehr, als ich nach Betrachtung der statischen RKI-Plots und der relativ parallelen Bundesländer-Inzidenzen erwartet hätte, vor allem aber entgegen der Einlassung, das Geschehen sei „diffus“. Angesichts des weiter bestehenden „Brodelns“ würde mich eigentlich überraschen, wenn sich B.1.1.7 oder die B.1.617.x so viel anders ausbreiten würden als der ursprüngliche Wildtyp.
    • Insbesondere gibt es auch in den späteren Wellen Kreise, die kurz „hochbubbeln“ und dann auch wieder rasch unauffällig werden. Es wäre bestimmt aufschlussreich, wenn mensch wüsste, warum das mit dem Rasch-Unauffällig-Werden in den Kreisen in Südsachsen und -thüringen über lange Zeit nicht passiert ist.

    Technics

    Nachtrag (2022-10-27)

    Der Code ist jetzt am Codeberg.

    Es war übrigens doch nicht so ganz einfach, diesen Film zu machen, und zwar vor allem, weil ich eine Weile mit den Polygonen für die Kreise gerungen habe. Mein erster Plan war, die einfach aus der Openstreetmap zu ziehen. Ich wollte aber nicht an einem kompletten OSM-Dump herumoperieren, und so war ich sehr angetan von osm-boundaries. Mit diesem Dienst ließen sich eigentlich recht leicht die „administrative boundaries“ von Kreisen (das wäre dort Level 6) in geojson bekommen.

    Abgesehen davon aber, dass die interaktive Auswahl gerne mit „cannot download tree“ scheiterte, mein Webkit lange am Javascript kaute, und Cloudflare die Downloads regelmäßig zu früh abbrach (die MacherInnen von osm-boundaries verstehen das selbst nicht so ganz), sind die Label der Kreise doch sehr verschieden von dem, was das RKI hat: „Solingen (Klingenstadt)“ auf RKI-Seite war ebenso lästig wie fehlende Unterscheidung zwischen Stadt- und Landkreisen in den Bezeichnungen der Openstreetmap (was immerhin durch Betrachtung der Flächen zu umgehen war).

    Aber auch, als ich mir die Abbildung zwischen den verschiedenen Bezeichnern zusammengehackt hatte, blieben einige weiße Flecken, Kreise also, die ich in der Openstreetmap schlicht nicht finden konnte. An dem Punkt bin ich zur offiziellen Quelle gegangen, nämlich dem Bundesamt für Kartographie und Geodäsie, speziell zum VG2500-Datensatz, der zu meiner großen Erleichterung auch die Kreis-Identifier des RKI (1001 etwa für Flensburg-Stadt) enthält. Na ja, abgesehen von Berlin, das das RKI aufteilt, was wiederum etwas Gefummel zur Wiedervereinigung von Berlin im Code braucht.

    Aber leider: Der Kram kommt als Shape, wie das BKG sagt ein „De-facto-Industriestandard“, mit dem allerdings ich noch nie etwas zu tun hatte und der als über einige Dateien verteilte Binärsoße daherkommt. Immerhin: das Debian-paketierte Cartopy kann damit umgehen. Puh. Nur: frech add_geometries auf die Geometrien loslassen, die aus dem Reader herausfallen, führt zu einer leeren Karte.

    Im Folgenden bin ich etwas untergegangen in all den Referenzsystemen, mit denen sich die GeographInnen so rumschlagen müssen. Ach, haben wir es gut in der Astronomie. Ja, klar, auch wir haben einen Haufen verschiedene Äquatoren und Nullpunkte (z.B. zwei verschiedene Systeme für galaktische Koordinaten, und haufenweise historische äquatoriale Systeme, die zudem durch verschiedene Sternkataloge definiert waren): Aber letztlich sind das im Wesentlichen Drehungen mit winzigen Knitterungen, und schlimmstenfalls kommen die Dinge halt am falschen Platz raus, was für meine gegenwärtigen Zwecke völlig wurst gewesen wäre.

    Aber hier: Nichts auf der ganzen Karte. Es braucht beim Plotten zwingend das richtige Quell-Bezugssystem, hier (wie aus dem .prj des VG2500 hervorgeht) das EPSG-System Nummer 25832 (Fünfundzwanzigtausenachthundertzweiundreißig! Holy Cow!). Damit kann Cartopy sogar umgehen, aber es zieht dann bei jedem Programmlauf die Beschreibung des Systems erneut von einem Onlinedienst, und das geht in meiner Welt gar nicht. Deshalb habe ich mir geschwind eine Proj4Projection-Klasse gefummelt, die den String, der von dem Online-Dienst kommt, händisch in die zugrundeliegende Bibliothek packt. Warnung: Das ist ohne Sachkenntnis geschrieben; dass ich da die Gültigkeitsgrenzen völlig fake, ist vermutlich Gift außerhalb dieser spezifischen Anwendung.

    Der Rest des Codes ist harmloses Python, das die Eingabedaten hinmassiert. Weil die RKI-Daten leider nach Kreis und nicht nach Datum sortiert sind, muss ich den kompletten Datensatz ins RAM nehmen; auf nicht völlig antiker Hardware ist das aber kein Drama.

    Was für den optischen Eindruck noch ziemlich wichtig ist: Ich interpoliere linear zwischen den Tagen (die iter_interpolated-Funktion). Das ist nützlich, weil damit die Übergänge nicht so hart sind, und auch, damit der Film nicht nur 25 Sekunden (also rund 600 Frames) lang ist, sondern etwas wie zwei Minuten läuft.

    Wers nachbauen will oder z.B. Altersgruppen-spezifische Filme machen will oder welche mit dem Median des Alters – das würde der Datensatz durchaus hergeben – oder welche, die nicht alles über 350 saturiert darstellen oder so etwas, braucht mkmovie.py und corona.py. Die Quelldaten werden in einem in corona.py definierten externen Verzeichnis erwartet (DATA_DIR); welche, und woher die kommen, steht am Kopf von mkmovie.py.

    [1]Bei den späteren Virusvarianten mag es eingestandenermaßen nicht mehr ganz so einfach sein, weshalb ich hier so ein vorsichtiges Präteritum schreibe.
  • Falscher Instinkt

    Ausschnitt aus dem RKI-Bericht von heute

    Anlass meiner Paranoia: Im RKI-Bericht von heute drängeln sich verdächtig viele Kreise gerade unter der 50er-Inzidenz.

    Mein Abgesang auf die RKI-Berichte von neulich war wie erwartet etwas voreilig: Immer noch studiere ich werktäglich das Corona-Bulletin des RKI. Es passiert ja auch wieder viel in letzter Zeit. Recht schnell schossen die ersten Landkreise im Juli über die 50er-Schwelle, während die breite Mehrheit der Kreise noch weit von ihr entfernt war. Das ist, klar, auch so zu erwarten, wenn die „Überdispersion“ (find ich ja ein komisches Wort für „die Verteilung der Zahl der von einem_r Infizierten Angesteckten hat einen langen Schwanz nach oben hin“, aber na ja) noch irgendwie so ist wie vor einem Jahr, als, wie im inzwischen klassischen Science-Artikel von Laxminarayan et al (DOI 10.1126/science.abd7672) auf der Grundlage von Daten aus Indien berichtet wurde, 5% der Infizierten 80% der Ansteckungen verursachten (und umgekehrt 80% der Infizierten gar niemanden ansteckten): SARS-2 verbreitete sich zumindest in den Prä-Alpha- und -Delta-Zeiten in Ausbrüchen.

    Nachdem aber die ersten Landkreisen die 50 gerissen hatten, tat sich für eine ganze Weile im Bereich hoher Inzidenzen nicht viel; auch heute sind nur drei Landkreise über der 50er-Inzidenz, während sich knapp darunter doch ziemlich viele zu drängen scheinen.

    Und da hat sich ein Verdacht in mir gerührt: Was, wenn die Gesundheitsämter sich mit Händen und Füßen wehren würden, über die vielerorts immer noch „maßnahmenbewehrte“ 50er-Schwelle zu gehen und ihre Meldepraktiken dazu ein wenig… optimieren würden? Wäre das so, würde mensch in einem Histogramm der Inzidenzen (ein Häufigkeit-von-Frequenzen-Diagram; ich kann die nicht erwähnen ohne einen Hinweis auf Zipfs Gesetz) eine recht deutliche Stufe bei der 50 erwarten.

    Gibt es die? Nun, das war meine Gelegenheit, endlich mal mit den Meldedaten zu spielen, die das RKI bereitstellt – zwar leider auf github statt auf eigenen Servern, so dass ich mit meinen Daten statt mit meinen Steuern bezahle (letzteres wäre mir deutlich lieber), aber das ist Jammern auf hohem Niveau. Lasst euch übrigens nicht einfallen, das ganze Repo zu klonen: Das sind ausgecheckt wegen eines gigantischen Archivs krasse 24 GB, und was ihr tatsächlich braucht, sind nur die aktuellen Zahlen (Vorsicht: das sind auch schon rund 100 MB, weil das quasi die ganze deutsche Coronageschichte ist) und der Landkreisschlüssel (vgl. zu dem Update unten).

    Auch mit diesen Dateien muss mensch erstmal verstehen, wie aus deren Zeilen die Inzidenzen werden, denn es ist nicht etwa so, dass jede Zeile einer Erkrankung entspricht: Nein, manche berichten mehrere Fälle, es wird nach schon gemeldeten und ganz neuen Fällen unterschieden, und eventuell gibts auch noch Korrekturzeilen. Dazu findet ein in-band-signalling zu Gestorbenen und Genesenen statt. Lest das README aufmerksam, sonst verschwendet ihr nur (wie ich) eure Zeit: EpidemiologInnen denken ganz offenbar etwas anders als AstronomInnen.

    Das Ergebnis ist jedenfalls das hier:

    unverdächtig aussehendes Histogramm

    Ich muss also meinen Hut essen: Wenn da irgendwo Hemmschwellen sein sollten, dann eher knapp unter 40, und das ist, soweit ich weiß, in keiner Corona-Verordnung relevant. Na ja, und der scharfe Abfall knapp unter 25 könnte zu denken geben. Aber warum würde jemand bei der 25 das Datenfrisieren anfangen? Der Farbe im RKI-Bericht wegen? Nee, glaub ich erstmal nicht.

    Wenn ihr selbst mit den RKI-Daten spielen wollt, kann euch das Folgende vielleicht etwas Fummeln ersparen – hier ist nämlich mein Aggregationsprogramm. Ich werdet die Dateipfade anpassen müssen, aber dann könnt ihr damit eure eigenen Inzidenzen ausrechnen, ggf. auch nach Altersgruppen, Geschlechtern und was immer. In dem großen CSV des RKI liegt in der Tat auch die Heatmap, die jetzt immer im Donnerstagsbericht ist. Reizvoll fände ich auch, das gelegentlich zu verfilmen…

    Hier jedenfalls der Code (keine Abhängigkeiten außer einem nicht-antiken Python). So, wie das geschrieben ist, bekommt ihr eine Datei siebentage.csv mit Landkreisnamen vs. Inzidenzen; die entsprechen zwar nicht genau dem, was im RKI-Bericht steht, die Abweichungen sind aber konsistent mit dem, was mensch von lebenden Daten erwartet:

    # (RKI-Daten zu aktuellen 7-Tage-Meldeinzidenzen: Verteilt unter CC-0)
    import csv
    import datetime
    import sys
    
    LKR_SRC = "/media/incoming/2020-06-30_Deutschland_Landkreise_GeoDemo.csv"
    INF_SRC = "/media/incoming/Aktuell_Deutschland_SarsCov2_Infektionen.csv"
    LANDKREIS = 0
    MELDEDATUM = 3
    REFDATUM = 4
    NEUER_FALL = 6
    ANZAHL_FALL = 9
    
    
    def getcounts(f, n_days=7):
        counts = {}
        collect_start = (datetime.date.today()-datetime.timedelta(days=n_days)
            ).isoformat()
        sys.stderr.write(f"Collecting from {collect_start} on.\n")
    
        row_iter = csv.reader(f)
        # skip the header
        next(row_iter)
    
        for row in row_iter:
            if row[MELDEDATUM]>=collect_start:
                key = int(row[LANDKREIS])
                kind = row[NEUER_FALL]
                if kind!="-1":
                    counts[key] = counts.get(key, 0)+int(row[ANZAHL_FALL])
    
        return counts
    
    
    def get_lkr_meta():
        lkr_meta = {}
        with open(LKR_SRC, "r", encoding="utf-8") as f:
            for row in csv.DictReader(f):
                row["IdLandkreis"] = int(row["IdLandkreis"])
                row["EW_insgesamt"] = float(row["EW_insgesamt"])
                lkr_meta[row["IdLandkreis"]] = row
    
        return lkr_meta
    
    
    def main():
        lkr_meta = get_lkr_meta()
        with open(INF_SRC, "r", encoding="utf-8") as f:
            counts = getcounts(f)
    
        with open("siebentage.csv", "w", encoding="utf-8") as f:
            f.write("Lkr, Inzidenz\n")
            w = csv.writer(f)
            for lkr in lkr_meta.values():
                w.writerow([lkr["Gemeindename"],
                    1e5*counts.get(lkr["IdLandkreis"], 0)/lkr["EW_insgesamt"]])
    
    
    if __name__=="__main__":
        main()
    

    Nachtrag (2021-08-13)

    Eine Woche später ist der Damm definitiv gebrochen. Von drei Landkreisen über dem 50er-Limit sind wir laut aktuellem RKI-Bericht jetzt bei 39, wobei allein seit gestern 10 dazukamen. An die aktuelle Verteilung würde ich gerne mal eine Lognormalverteilung fitten:

    noch unverdächtiger aussehendes Histogramm

    Nicht, dass ich eine gute Interpretation hätte, wenn das lognormal wäre. Aber trotzdem.

    Nachtrag (2021-09-08)

    Das RKI hat die Landkreis-Daten (2020-06-30_Deutschland_Landkreise_GeoDemo.csv) aus ihrem Github-Repo entfernt (commit cc99981f, „da diese nicht mehr aktuell sind“; der letzte commit, der sie noch hat, ist 0bd2cc53). Die aus der History rausklauben würde verlangen, das ganze Riesending zu clonen, und das wollt ihr nicht. Deshalb verteile ich unter dem Link oben die Datei unter CC-BY 4.0 International, mit Namensnennung… nun, Destatis wahrscheinlich, oder halt RKI; die Lizenzerklärung auf dem Commit ist nicht ganz eindeutig. Als Quelle der Geodaten war vor der Löschung https://www.destatis.de/DE/Themen/Laender-Regionen/Regionales/Gemeindeverzeichnis/Administrativ/Archiv/ angegeben, aber da finde ich das nicht.

  • Adieu, RKI-Berichte

    Oh je! Im RKI-Bericht von heute heißt es:

    Ab Montag, 19.07.2021, wird die Berichterstattung umgestellt: Eine tägliche kürzere Berichterstattung wird ergänzt mit einer ausführlichen Berichterstattung donnerstags.

    Ich gestehe offen: ich finde das ein wenig bitter. Auch wenn die Berichte nie wirklich das sagten, was sich wohl alle gefragt haben („Wo stecken sich diese Leute denn nun an? Was für Ausbrüche gibts im Land?“), waren die Zahlen und Grafiken, die da jeden Tag kamen (na ja, seit ein paar Wochen schon nicht mehr am Wochenende) durchaus ein beruhigendes und strukturierendes Element der Coronazeit.

    Die RKI-Deutschlandkarte mit farbkodierten Inzidenzen

    Gerade, wo die Karte wieder bunter wird, strafft das RKI seinen täglichen Corona-Bericht. Hm.

    Die sich windenden Inzidenzlinien der Bundesländer, die bunt gefärbten Landkreise, bei denen Extreme oft gute und manchmal auch gar keine guten Erklärungen hatten, das Schwingen der R-Wert-Schätzungen und natürlich die nach Wochentag wechselnden Features vom Wochenvergleich am Dienstag bis zur Mortalitätsstatistik am Freitag: Mir wird das fehlen, jedenfalls, solange Corona rein ethisch nicht ignorierbar ist.

    Wann ich Corona ethisch ignorierbar finde, habe ich indes noch nicht ganz klar. Wahrscheinlich werde ich irgendwann im September finden, wer dann noch nicht geimpft ist, kann es entweder nicht (und dann hat Zuwarten auch keinen Wert) oder wollte es nicht (und kann dann von mir keine Rücksicht erwarten).

    Aber wie das wirklich aussieht, wenn die Inzidenzen irgendwo jenseits der 1000 liegen – und das würden sie, wenn die IgG-Spiegel der meisten Leute wieder halbwegs normal sind und Schulen, Büros, Kneipen und Discos offen – ach, wer weiß?

    Als Abschiedsbetrachtung zu den gewohnten RKI-Berichten noch etwas aus den Tabellen zu „Betreuung, Unterbringung und Tätigkeit in Einrichtungen“ (die ich eingestandenermaßen meist überblättert habe): Dort werden ja unter anderem nach §36 IfSG „Untergebrachte“ diskutiert, was Pflegeeinrichtungen, Obdachlosenunterkünfte, Lager für (oder gegen) Geflüchtete und auch Gefängnisse umfasst. Genau dabei überrascht mich die Zeile „Sonstige“, die nach Lage der Dinge insbesondere die Gefängnis-Zahlen enthalten müsste. Dort stehen heute 1512 Fälle (seit Pandemie-Anfang), von denen 210 älter waren als 60, 132 ins Krankenhaus mussten und 27 gestorben sind.

    Das kann, nach allem, was so aus den Gefängnissen zu hören ist, schlicht nicht sein; deutsche Haftanstalten haben wahrscheinlich keine Positivraten wie Townships in Südafrika, aber sehr weit dürften sie auch nicht davon entfernt sein. Das aber würde bedeuten, dass Fälle aus Gefängnissen fast durchweg unter die 138284-85924 = 52360 Fälle nach §36 ohne „differenzierte Angaben“ fallen müssen. Und das ist schon irgendwo auf dem Skandalspektrum: Wie schwierig kann es sein, Fälle aus Gefängnissen auch so zu labeln?

  • Impfpass ohne App, Apple und Google

    Morgen werden zwei Wochen seit meiner zweiten Corona-Impfung vergangen sein. Damit wird die Impfpassfrage für mich relevant: Mit so einem Ding könnte ich wieder in die Mensa gehen!

    Allerdings habe ich, soweit ich das sehe, keinen Zugang zur offiziellen Covpass-App, weil ich mich von Apples Appstore und Googles Playstore fernhalte und eigentlich auch die Toolchains der beiden nicht auf meinem Rechner haben will. Immerhin gibt es (es lebe der Datenschutz-Aktivismus, der für offene Entwicklung von dem Kram gesorgt hat) die Quellen der Apps (wenn auch leider auf github). Wie kompliziert kann es schon sein, das ohne den ganzen proprietären Zauber nachzubauen?

    Stellt sich raus: schon etwas, aber es ist ein wenig wie eine Kurzgeschichte von H.P. Lovecraft: Die Story entwickelt sich wie beim Schälen einer Zwiebel. Schale um Schale zeigt sich, dass die Wahrheit immer noch tiefer liegt.

    Bei Lovecraft sind nach dem Abpulen der letzten Schale meist alle ProtagonistInnen tot oder wahnsinnig. Im Fall der Covpass-App hingegen ist der Kram sogar dokumentiert: So finden sich die halbwegs lesbare Dokumentation des Datenstroms im QR-Code und das JSON-Schema – leider schon wieder auf github.

    Schale 1: QR-Code scannen

    Ich dachte mir, zum Nachbauen der Covpass-App sollte ich erstmal ihre Eingabe verstehen, also die Daten aus den beiden Impfzertifikaten lesbar darstellen. Der erste Schritt dazu ist etwas, das QR-Codes lesen kann. Ich hatte anderweitig schon mit zbar gespielt, für das es das Debian-Paket python3-zbar gibt. Damit (und mit der unverwüstlichen Python Imaging Library aus python3-pillow) geht das so, wenn das Foto mit dem Zertifikat in der Datei foto.jpeg liegt:

    def get_single_qr_payload(img):
      img = img.convert("L")
    
      scanner = zbar.ImageScanner()
      scanner.parse_config('enable')
      image = zbar.Image(img.size[0], img.size[1], 'Y800', data=img.tobytes())
      if not scanner.scan(image):
        raise ValueError("No QR code found")
    
      decoded = list(image)
      if len(decoded)>1:
        raise ValueError("Multiple QR codes found")
    
      return decoded[0].data
    
    encoded_cert = get_single_qr_payload(Image.open("foto.jpeg"))
    

    Im Groben wandele ich in der Funktion das Bild (das wahrscheinlich in Farbe sein wird) in Graustufen, denn nur damit kommt zbar zurecht. Dann baue ich eine Scanner-Instanz, also das Ding, das nachher in Bildern nach QR-Codes sucht. Die API hier ist nicht besonders pythonesk, und ich habe längst vergessen, was parse_config('enable') eigentlich tut – egal, das ist gut abgehangene Software mit einem C-Kern, da motze ich nicht, noch nicht mal über diesen fourcc-Unsinn, mit dem vor allem im Umfeld von MPEG allerlei Medienformate bezeichnet werden; bei der Konstruktion des zbar.Image heißt „8 bit-Graustufen“ drum "Y800". Na ja.

    Der Rest der Funktion ist dann nur etwas Robustheit und wirft ValueErrors, wenn im Foto nicht genau ein QR-Code gefunden wurde. Auch hier ist die zbar-API vielleicht nicht ganz preiswürdig schön, aber nach dem Scan kann mensch über zbar.Image iterieren, was die verschiedenen gefundenen Barcodes zurückgibt, zusammen mit (aus meiner Sicht eher knappen) Metadaten. Das .data-Attribut ist der gelesene Kram, hier als richtiger String (im Gegensatz zu bytes, was ich hier nach der python3-Migration eher erwartet hätte).

    Schale 2: base45-Kodierung

    Das Ergebnis sieht nach üblichem in ASCII übersetzten Binärkram aus. Bei mir fängt der etwa (ich habe etwas manipuliert, das dürfte so also nicht dekodieren) so an: HC1:6B-ORN*TS0BI$ZDFRH%. Insgesamt sind das fast 600 Zeichen.

    Als ich im Wikipedia-Artikel zum Digitalen Impfnachweis gelesen habe, das seien base45-kodierte Daten, habe ich erst an einen Tippfehler gedacht und es mit base85 versucht, das es in Pythons base64-Modul gibt. Aber nein, weit gefehlt, das wird nichts. War eigentlich klar: die Wahrscheinlichkeit, dass was halbwegs Zufälliges base85-kodiert keine Kleinbuchstaben enthält, ist echt überschaubar. Und base45 gibts wirklich, seit erstem Juli in einem neuen RFC-Entwurf, der sich explizit auf QR-Codes bezieht. Hintergrund ist, dass der QR-Standard eine Kodierungsform (0010, alphanumeric mode) vorsieht, die immer zwei Zeichen in 11 bit packt und dafür nur (lateinische) Großbuchstaben, Ziffern und ein paar Sonderzeichen kann. Extra dafür ist base45 erfunden worden. Fragt mich bloß nicht, warum die Leute nicht einfach binäre QR-Codes verwenden.

    Es gibt bereits ein Python-Modul für base45, aber das ist noch nicht in Debian bullseye, und so habe ich mir den Spaß gemacht, selbst einen Dekodierer zu schreiben. Technisch baue ich das als aufrufbares (also mit einer __call__-Methode) Objekt, weil ich die nötigen Tabellen aus dem globalen Namensraum des Skripts draußenhalten wollte. Das ist natürlich ein Problem, das verschwindet, wenn sowas korrekt in ein eigenes Modul geht.

    Aber so gehts eben auch:

    class _B45Decoder:
      chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"
      code_for_char = dict((c, i) for i, c in enumerate(chars))
      sig_map = [1, 45, 45*45]
    
      def __call__(self, str):
        raw_bytes = [self.code_for_char[s] for s in str]
        cooked_bytes = []
    
        for offset in range(0, len(raw_bytes), 3):
          next_raw = raw_bytes[offset:offset+3]
          next_bytes = sum(w*v for w,v in
            zip(self.sig_map, next_raw))
          if len(next_raw)>2:
            cooked_bytes.append(next_bytes//256)
          cooked_bytes.append(next_bytes%256)
    
        return bytes(cooked_bytes)
    
    b45decode = _B45Decoder()
    

    Die b45decode-Funktion ist, wenn mensch so will, ein Singleton-Objekt der _B45Decoder-Klasse (so hätten das jedenfalls die IBMlerInnen der CovPass-App beschrieben, vgl. unten). Ansonsten bildet das den Grundgedanken von base45 ziemlich direkt ab: Immer drei Bytes werden zur Basis 45 interpretiert, wobei die Wertigkeit der verschiedenen Zeichen im Dictionary code_for_char liegt. Die resultierende Zahl lässt sich in zwei dekodierte Bytes aufteilen. Nur am Ende vom Bytestrom muss mensch aufpassen: Wenn die dekodierte Länge ungerade ist, stehen kodiert nur zwei Byte.

    Und ja: vielleicht wärs hübscher mit dem grouper-Rezept aus der itertools-Doku, aber die next_raw-Zuweisung schien mir klar genug.

    Schale 3: zlib

    Ein b45decode(encoded_cert) gibt erwartungsgemäß wilden Binärkram aus, etwas wie b'x\x9c\xbb\xd4\xe2\xbc\x90Qm!… Das sind, ganz wie die Wikipedia versprochen hat, zlib-komprimierte Daten. Ein zlib.decompress wirkt und führt auf etwas, in dem zum ersten Mal irgendetwas zu erkennen ist; neben viel Binärkram findet sich etwa:

    ...2bdtj2021-07-01bistRobert Koch-InstitutbmamOR...
    

    Keine Frage: Ich mache Fortschritte.

    Schale 4: CBOR/COSE

    An dieser Stelle bin ich auf mir unbekanntes Terrain vorgestoßen: CBOR, die Concise Binary Object Representation (Nerd-Humor: erfunden hat das Carsten Bormann, weshalb ich auch sicher bin, dass das Akronym vor der ausgeschriebenen Form kam) alias RFC 7049. Gedacht ist das als so eine Art binäres JSON; wo es auf die Größe so ankommt wie bei QR-Codes, habe ich schon Verständnis für diese Sorte Sparsamkeit. Dennoch: Sind Protocol Buffers eigentlich tot?

    Praktischerweise gibt es in bullseye das Paket python3-cbor2. Munter habe ich cbor2.loads(raw_payload) geschrieben und war doch etwas enttäuscht, als ich etwas wie:

    CBORTag(18, [b'\xa1\x01&',
      {4: b'^EVf\xa5\x1exW'},
      b'\xa4\x01bDE...',
      b'\xf9\xe9\x9f...'])
    

    zurückbekommen habe. Das ist deutlich mehr viel Binärrauschen als ich erhofft hatte. Aber richtig, das Zeug sollte ja signiert sein, damit die Leute sich ihre Impf- und Testzertifikate nicht einfach selbst schreiben. Die hier genutzte Norm heißt COSE (nämlich CBOR Object Signing and Encryption, RFC 8152 aus dem Jahr 2017). Ich habe das nicht wirklich gelesen, aber Abschnitt 2 verrät gleich mal, dass, wer an einer Signaturprüfung nicht interessiert ist (und das bin ich nicht, solange ich nicht vermuten muss, dass meine Apotheke mich betrogen hat), einfach nur aufs dritte Arrayelement schauen muss. Befriedigenderweise ist das auch das längste Element.

    Ein wenig neugierig war ich aber schon, was da noch so drinsteht. Die 18 aus dem CBORTag heißt nach Abschnitt 4.2, dass das eine Nachricht mit nur einer Unterschrift ist, und der letzte Binärkram ist eben diese eine Unterschrift. Das erste Array-Element sind Header, die mit unterschrieben werden, wieder CBOR-kodiert. Dekodiert ist das {1: -7}, und Überfliegen der COSE-Spezifikation (Tabellen 2 und 5) schlägt vor, dass das heißt: der Kram ist per ECDSA mit einem SHA-256-Hash unterschrieben.

    Tabelle 2 von COSE erklärt auch das nächste Element, die Header, die nicht unterschrieben werden (und über die mensch also Kram in die Nachrichten einfummeln könnte). Das sieht auch erstmal binär aus, ist aber ein „entpacktes“ Dictionary: In meiner Nachricht steht da nur ein Header 4, was der „Key Identifier“ ist. Der Binärkram ist schlicht eine 64-bit-Zahl, die angibt, mit welchem Schlüssel die Unterschrift gemacht wurde (bei PGP wären das die letzten 8 byte des Fingerabdrucks, viel anders wird das bei COSE auch nicht sein).

    Schale 5: CBOR lesbar machen

    Zurück zu den Zwiebelschalen. Wenn also die Nutzdaten im dritten Element des Array von oben sind, sage ich:

    cbor_payload = cbor2.loads(cose_payload.value[2])
    

    Heraus kommt dabei etwas wie:

    {1: 'DE', 4: 1657705736, 6: 1626169736,
    -260: {1: {'v': [{'co': 'DE', 'dn': 2, 'dt': '2021-07-01',
    'is': 'Robert Koch-Institut', 'ma': 'ORG-100030215',
    'mp': 'EU/1/20/1528', 'sd': 2, 'tg': '840539006',...}]}}}
    

    – das ist ziemlich offensichtlich die Datenstruktur, die der Zauber liefern sollte. Nur sind die Schlüssel wirklich unklar. v könnte wohl „Vaccination“ sein, is der Issuer, also der Herausgeber des Impfpasses; die Werte von 4 und 6 sehen verdächtig nach Unix-Timestamps in der nächsten Zeit aus (ja, es sind schon sowas wie 1,6 Milliarden Sekunden vergangen seit dem 1.1.1970).

    Aber Raten ist doof, wenn es Doku gibt. Wie komme ich also zu …

  • Die Viren loben

    „Jetzt ist aber wirklich höchste Zeit, dass Corona endlich mal weggeht“ ist inzwischen ein universelles Sentiment, und auch mich lockt gelegentlich die autoritäre Versuchung, einen „harten Lockdown“ zu wünschen, damit das Ding weggeht (was es natürlich nicht täte). Der Hass auf SARS-2 steigt, und damit womöglich auf Viren allgemein.

    In der Tat scheinen Viren erstmal richtig doof, eklig und widerwärtig. Wie scheiße ist das eigentlich, sich von den Zellen vertrauensvoll aufnehmen zu lassen und dann den Laden zu übernehmen mit dem einzigen Ziel, neues Virus zu machen? Und dann die Ähnlichkeit von z.B. der T2-Bakteriophage mit Invasoren vom Mars...

    Andererseits bin ich überzeugt, dass eine gewisse Anfälligkeit gegen Viren wahrscheinlich ein evolutionärer Vorteil ist. Da gibts bestimmt jede Menge echte Wissenschaft dazu, aber ich denke, eine einfache Intiution geht auch ohne: Praktisch alle Viren nämlich wirken nur auf kurze Distanz in Zeit und Raum (verglichen etwa mit Pflanzenpollen, aber sogar mit vielen Bakterien), werden also im Wesentlichen bei Begegnungen übertragen. Da die Wahrscheinlichkeit von Begegnungen mit dem Quadrat der Bevölkerungsdichte geht, sollten Viren explodierende Populationen „weicher“ begrenzen als leergefressene Ressourcen und so wahrscheinlich katastrophalen Aussterbeereignissen vorbeugen.

    Vorneweg: Ja, das klingt alles erstmal wild nach Thomas Malthus. Dessen Rechtfertigung massenhaften Sterbenlassens ist natürlich unakzeptabel (ebenso allerdings wie das fortgesetzte Weggucken von den Meadows-Prognosen, die in der Regel auch katastrophale Zusammenbrüche erwarten lassen).

    Dies aber nicht, weil falsch wäre, dass in endlichen Systemen der Ressourcengebrauch nicht endlos steigen kann; das ist nahe an einer Tautologie. Nein, Malthus' Fehler ist der der Soziobiologie, nämlich menschliche Gesellschaft und menschliches Verhalten an Funktionsweisen der Natur auszurichten. Wer das will, wird recht notwendig zum Schlächter, während umgekehrt die Geschichte der letzten 100 Jahre überdeutlich zeigt, wie (sagen wir) Wachstumszwänge diverser Art durch mehr Bildung, mehr Gleichheit und vor allem durch reproduktive Selbstbestimmung von Frauen ganz ohne Blutbad und unter deutlicher Hebung der generellen Wohlfahrt zu beseitigen sind.

    Bei Kaninchen ist das aber, da muss ich mich leider etwas als Speziezist outen, anders. Und daher habe ich mir Modellkaninchen für ein weiteres meiner Computerexperimente herausgesucht, ganz analog zu den Schurken und Engeln.

    Die Fragestellung ist: Werden Ausschläge in Populationen wirklich weniger wild, wenn Viren (also irgendwas, das Individuen nach Begegnungen mit einer gewissen Wahrscheinlichkeit umbringt) im Spiel sind?

    Um das zu untersuchen, baue ich mir eine Spielwelt (wer mag, kann auch „modifiziertes Lotka-Volterra-Modell“ dazu sagen) wie folgt:

    • Es gibt 1000 Felder, auf denen Gras wachsen kann oder nicht.
    • In der Welt leben Kaninchen, die pro Periode ein Grasfeld leerfressen müssen, damit sie glücklich sind.
    • Haben Kaninchen mehr Gras als sie brauchen, vermehren sie sich, und zwar um so mehr, je mehr Extrafutter sie haben (so machen das zumindest Rehe).
    • Haben Kaninchen zu wenig Gras, sterben ein paar.
    • In jeder Periode verdoppelt sich die Zahl der Grasfelder (sagen wir: durch Aussaat), bis alle 1000 Felder voll sind.

    In Code sieht die Berechnung der Vermehrungsrate der Kaninchen so aus:

    def get_growth_factor(self):
      grass_per_rabbits = self.grass/self.rabbits
      if grass_per_rabbits<1:
        return grass_per_rabbits**2
      else:
        return 1+math.sqrt(grass_per_rabbits-1)
    

    Wer den Verlauf der Vermehrungsrate mit dem Gras/Kaninchenverhältnis γ auf der Abszisse sehen will:

    Um dieses Modell zu rechnen, habe ich ein kleines Python-Programm geschrieben, lv.py, das, mit anfänglichen Zahlen von Gras und Kaninchen aufgerufen, den Verlauf der jeweiligen Populationen im Modell ausgibt (nachdem es die Anfangsbedingungen etwas rausevolvieren hat lassen).

    Wie bei dieser Sorte von Modell zu erwarten, schwanken die Populationen ziemlich (außer im Fixpunkt Kaninchen=Gras). So sieht das z.B. für python3 lv.py 400 410 (also: anfänglich ziemlich nah am Gleichgewicht) aus:

    Das sieht nicht viel anders aus, wenn ich mit einem Kaninchen-Überschuss anfange (python3 lv.py 400 800):

    oder mit einem Gras-Paradies (python3 lv.py 800 150):

    Aus Modellsicht ist das schon mal fein: Recht unabhängig von den Anfangsbedingungen (solange sie im Rahmen bleiben) kommen zwar verschiedene, aber qualitativ doch recht ähnliche Dinge raus: die Kaninchenpopulationen liegen so zwischen 250 und 600 – im anfänglichen Gras-Paradies auch mal etwas weiter auseinander – und schwanken wild von Schritt zu Schritt.

    Jetzt baue ich einen Virus dazu. Im lv.py geht das durch Erben vom LV-Modell, was auf die LVWithVirus-Klasse führt. Diese hat einen zusätzlichen Parameter, deadliness, der grob sagt, wie wahrscheinlich es ist, dass ein Kaninchen nach einer Begegnung mit einem anderen Kaninchen stirbt. Die Mathematik in der propagate-Methode,

    def propagate(self):
      LV.propagate(self)
      self.rabbits = max(1, self.rabbits-self.rabbits**2*self.deadliness)
    

    würde etwa einem Bau entsprechen, in dem sich alle Kaninchen ein Mal pro Periode sehen. Das ist jetzt sicher kein gutes Modell für irgendwas, aber es würde mich sehr überraschen, wenn die Details der Krankheitsmodellierung viel an den qualitativen Ergebnissen ändern würden. Wichtig dürfte nur sein, dass die Todesrate irgendwie überlinear mit der Population geht.

    lv.py lässt das Modell mit Virus laufen, wenn es ein drittes Argument, also die Tödlichkeit, bekommt. Allzu tödliche Viren löschen die Population aus (python3 lv.py 800 150 0.05):

    Zu harmlose Viren ändern das Verhalten nicht nennenswert (python3 lv.py 800 150 1e-6):

    Interessant wird es dazwischen, zum Beispiel python3 lv.py 800 150 2.1e-4 (also: rund jede fünftausendste Begegnung bringt ein Kaninchen um):

    – wer an die Beschriftung der Ordinate schaut, wird feststellen, dass die Schwankungen tatsächlich (relativ) kleiner geworden sind. Das Virus wirkt offenbar wirklich regularisierend.

    Wir befinden uns aber im Traditionsgebiet der Chaostheorie, und so überrascht nicht, dass es Bereiche der Tödlichkeit gibt, in denen plötzlich eine starke Abhängigkeit von den Anfangsbedingungen entsteht und sich die Verhältnisse weit in die Entwicklung rein nochmal grundsätzlich ändern können („nicht-ergodisch“). So etwa python3 lv.py 802 300 0.0012:

    gegen python3 lv.py 803 300 0.0012:

    Ein Kaninchen weniger am Anfang macht hundert Schritte später plötzlich so ein gedrängeltes, langsames Wachstum.

    Warum ich gerade bei 0.0012 geschaut habe? Nun ich wollte einen Überblick über das Verhalten bei verschiedenen Tödlichkeiten und habe darum stability_by_deadliness.py geschrieben, das einfach ein paar interessante Tödlichkeiten durchprobiert und dann die relative Schwankung (in Wirklichkeit: Standardabweichung durch Mittelwert) und den Mittelwert der Population über der Virustödlichkeit aufträgt:

    – das sieht sehr gut aus für meine These: Mit wachsender Tödlichkeit des Virus nimmt die relative Streuung der Population ab, irgendwann allerdings auch die Population selbst. Ganz links im Graphen gehts durcheinander, weil sich dort chaotisches Systemverhalten mit kleinen Zahlen tifft, und dazwischen scheint es noch ein, zwei Phasenübergänge zu geben.

    Leider ist dieses Bild nicht wirklich robust. Wenn ich z.B. die Anfangsbedingungen auf 600 Gras und 250 Kaninchen ändere, kommt sowas raus:

    – die meisten der Effekte, die mich gefreut haben, sind schwach oder gar ganz weg – wohlgemerkt, ohne Modelländerung, nur, weil ich zwei (letztlich) Zufallszahlen geändert habe.

    Mit etwas Buddeln findet mensch auch das Umgekehrte: wer immer mit 170 Gras und 760 Kaninchen anfängt, bekommt einen Bereich, in dem die Populationen mit Virus größer sind als ohne, während gleichzeitig die relative Schwankung nur noch halb so groß ist wie ohne Virus. Dazwischen liegt ein 1a Phasenübergang:

    Mensch ahnt: da steckt viel Rauschen drin, und auf der anderen Seite höchst stabiles Verhalten, wo mensch es vielleicht nicht erwarten würde (bei den hohen Tödlichkeiten und mithin kleinen Zahlen). Wissenschaft wäre jetzt, das systematisch anzusehen (a.k.a. „Arbeit“). Das aber ist für sehr ähnliche Modelle garantiert schon etliche Male gemacht worden, so dass der wirklich erste Schritt im Jahr 51 nach PDP-11 erstmal Literatur-Recherche wäre.

    Dazu bin ich natürlich zu faul – das hier ist ja nicht mein Job.

    Aber das kleine Spiel hat meine Ahnung – Viren stabilisieren Ökosysteme – weit genug gestützt, dass ich weiter dran glauben kann. Und doch wäre ich ein wenig neugierig, was die Dynamische-Systeme-Leute an harter Wissenschaft zum Thema zu bieten haben. Einsendungen?

  • Michel Foucault vs. Corona

    Weil ich es neulich von der GEW hatte: ein weiterer Grund, warum ich 20 Jahre, nachdem es hip war, einen Blog angefangen habe, war eine Telecon im April letzten Jahres und die GEW.

    Na gut, es war nicht direkt die Telecon und eigentlich auch gar nicht die GEW.

    Tatsächlich hatte ich damals aber die erste Lehrsituation im engeren Sinne via Telecon, und kurz danach ich eine Epiphanie dazu, warum sich Lehre über Videokonferenzen so scheiße anfühlt. Dazu habe ich dann einen Artikel geschrieben, den ich, ermutigt von GEW-KollegInnen, gerne in der B&W (das ist die monatlich an alle Mitglieder in Baden-Württemberg verschickte Zeitschrift) untergebracht hätte – so brilliant fand ich ihn. Ahem.

    Nun, was soll ich sagen, die Redaktion war skeptisch, um das mal vorsichtig zu sagen. Ich habe da auch einiges Verständnis dafür, denn im letzten Juni gings bestimmt hoch her in Sachen computervermitteltem Unterricht, und da wären Einwürfe, die Videokonferenzen mit wüsten Folterszenen in Verbindung brachten, bestimmt nicht hilfreich gewesen.

    Aber schade fand ich es doch. Ich hatte aber nicht wirklich einen Platz, um sowas geeignet unterzubringen.

    Jetzt habe ich einen. Und damit: „Wider das Panopticon – Michel Foucault und der Unterricht via Videokonferenz“.

  • Vielleicht doch ein wertvolles Experiment

    Noch vor einem Jahr hatte sich kaum jemand vorstellen können, wie schnell die Staaten die Grenzen im März 2020 geschlossen haben – aber, das lässt sich hier leider wirklich nicht wegdiskutieren, im Prinzip können Bewegungseinschränkungen bei so einer Pandemie je nach Verteilung und Entwicklung schon mal nicht einfach nur atavistische Reflexe sein, und so will ich einmal nicht allzu sehr die Zähne fletschen.

    Das nun „je nach Verteilung und Entwicklung“ hat das RKI im Epidemiologischen Bulletin 8/2021 (DOI 10.25646/7955) für die Folgen der Sommerferien etwas genauer betrachtet.

    Die Ergebnisse in der zentralen Frage – letztlich: Wärs besser gewesen, wir wären alle daheim geblieten? – sind wenig überraschend, wie auch das Fazit zur Frage der Massentests für Heimkehrer_innen:

    Ein längeres Angebot zur freiwilligen, kostenlosen Testung für Reiserückkehrer hätte vielleicht die Eintragungen vor und während der Herbstferien besser erfasst, die zweite Infektionswelle aber nicht verhindert.

    Richtig bemerkenswert fand ich hingegen folgende Abbildung in dem Artikel:

    Verlauf der Inzidenzen über relative Ferientage

    Sie entstand, indem die RKI-Leute erstmal als Zeiteinheit „Tage vor oder nach dem Beginn der Sommerferien im jeweiligen Bundesland“ gewählt haben. An der Ordinate stehen die üblichen Wocheninzidenzen pro 100000 Einwohner_innen, und zwar für Fälle, für die eine Exposition im Ausland bekannt ist. Insofern ist es kein Wunder, dass die Zahlen im Laufe der Zeit hochgehen. Das muss schon allein aufgrund der gestiegenen Reisetätigkeit so sein.

    Wertvoll wird die Abbildung aber als Mahnung, bei allen Metriken immer zu bedenken, was wie gemessen wurde. Denn richtig auffallend verhalten sich hier Bayern und Baden-Württemberg scheinbar anders als alle anderen: Ihre Kurven steigen erhebnlich früher und steiler als die der anderen Bundesländer.

    Es wäre jedoch unvernünftig, anzunehmen, die Dinge hätten sich in den anderen Bundesländern in der Realität wesentlich anders verhalten (jedenfalls, soweit es die westlichen Bundesländer betrifft). Und in der Tat liefert schon das RKI die Erklärung für den Unterschied: Die Südländer hatten einfach so spät Ferien, dass ihre Reiserückkehrenden in die allgemeine Testpflicht fielen sind und mithin die Erfassung Infizierter früher in deren Krankheitsverlauf und darüber hinaus bereits bei den Indexfällen passierte.

    Ob das jetzt eine weise Verwendung von Ressourcen war oder nicht, muss ich glücklicherweise nicht entscheiden. Zumindest für die nächsten Jahre aber – solange sich die Menschen noch an die Diskussion um die Massentests im Sommer 2020 erinnern – ist diese Grafik aber, glaube ich, eine wunderbare Art, den Einfluss von Messung (und in diesem Fall von Politik) auf scheinbar unumstößliche Grafiken und Metriken zu illustrieren.

    Ich werde das beim nächsten Mensen-Ranking auspacken. Oder, wenn wieder mal das Bruttoinlandsprodukt verkündet wird.

  • Es waren die Läden

    Na gut, und/oder die Schulen. Meine Vorhersage vom 16.1. jedenfalls, nach der sich der damas fast zwei Wochen alte Abwärtstrend bei der Intensivbelegung (als hierzulande einzige halbwegs zuverlässige Maßzahl fürs Infektionsgeschehen) in etwa in der Folgewoche nach oben wenden würde, war falsch. Die fallende Intensivbelegung setzt sich fort, plusminus exponentiell mit einer Halbierungszeit von gut sechs Wochen:

    Plot: Gerade in Log/log

    (die Achsen wären ähnlich wie am 16.1., aber darauf kommts mir hier nicht an).

    Nachdem vor drei oder vier Wochen zumindest anekdotisch und von hier aus gesehen nicht viel mehr Heimarbeit lief als vor Weihnachten, bleibt dann wohl nur der Schluss, dass meine Überzeugung, Ansteckungen fänden vor allem in den Betriebe und beim Berufspendeln statt, falsch war – während sich die Schätzung von einer Verzögerung von rund drei Wochen zwischen Ansteckungen und Intensivzahlen wohl als recht robust erweist.

    Denn dann reflektiert die Wende von wachsender zu fallender Intensivbelegung vom 4.1. ziemlich klar die weitgehende Schließung der Läden und Schulen rund um den 16.12. Schade, dass beides wieder so parallel lief, denn so bleibt es schwierig, rauszufinden, was dann was ausgemacht hat.

    Und: Dann war der große Ausbruch Anfang Dezember wirklich das Weihnachtsshopping? Tödlicher Konsum my ass.

  • Klar: Corona

    Ich kann nicht lügen: Einer der Gründe, weshalb ich gerade jetzt mit diesem Blog daherkomme ist, dass ich mir seit Dezember ganz besonders auf die Schulter klopfe wegen der Präzision meiner Corona-Vorhersagen: Wie befriedigend wäre es gewesen, wenn ich auf was Öffentliches hinweisen könnte, das meine Vorhersagen Anfang November dokumentieren würde. Als der „weiche Lockdown“ losging, habe ich nämlich etwas verkniffen rumerzählt: Klar werden die Zahlen nicht runtergehen, solange die Betriebe [1] nicht massiv runterfahren, und weil sich das niemand traut, werden in der Folge immer bizarrere Maßnahmen getroffen werden.

    So ist es nun gekommen, bis hin zu den Windmühlenkämpfen gegen die Rodler_innen im Sauerland und die nächtlichen Ausgangssperren hier in Baden-Württemberg (die allerdings, soweit ich das erkennen kann, genau niemand durchsetzt).

    Nun, jetzt habe ich die nächste Gelegenheit. Zu den relativ wenig beachteten Phänomenen gerade gehört nämlich, dass die Zahl der Corona-Intensivpatient_innen seit dem 4.1. konsistent fällt. Ich bin ziemlich überzeugt, dass das im Wesentlichen das Runterfahren von eigentlich praktisch allem (Betriebe, Geschäfte, Schulen) in der Woche vor Weihnachten spiegelt; und das würde auch darauf hinweisen, dass die Intensivbelegung dem Infektionsgeschehen etwas weniger als die generell angenommenen drei Wochen hinterherläuft.

    Tatsächlich lasse ich seit September jeden Tag ein ad-hoc-Skript laufen, das die aktuellen DIVI-Zahlen aus dem RKI-Bericht des Tages extrahiert und dann logarithmisch (also: exponentielle Entwicklung ist eine Gerade) plottet. Das sieht dann etwa so aus:

    Plot: Intensivbelegung 9/2020-1/2021

    Das angebrachte Lineal ist ein kleiner Python-Hack, den ich extra dafür gemacht habe (da schreibe ich bestimmt demnächst auch mal was zu), und er zeigt: Wir haben seit fast zwei Wochen einen exponentiellen Rückgang der Intensivbelegung – auch bei den Beatmeten, was die untere Linie zeigt; deren paralleler Verlauf lässt übrigens ziemlich zuverlässig darauf schließen, dass wohl keine im Hinblick auf den Verlauf aggressivere Mutante in großer Zahl unterwegs ist.

    Die schlechte Nachricht: Wenn mensch die Steigung anschaut, kommt eine Halbierungszeit von was wie sechs Wochen raus. Das wird nicht reichen, zumal, und hier kommt jetzt meine Prognose, diese Entwicklung wohl bald gebrochen wird, denn zumindest in meiner Umgebung war die Weihnachtsruhe spätestens am 11.1. vorbei, in Bundesländern ohne Feiertag am 6. wahrscheinlich schon früher. Unter der Annahme von zweieinhalb Wochen zwischen Infektionsgeschehen und Intensivreaktion dürfte es dann also etwa Mitte nächster Woche so oder so vorbei sein mit dem Traum zurückgehender Infektionen.

    Und wenn ich schon über Coronazahlen rede: Diesen Belegungsplot mache ich, weil ich ziemlich sicher bin, dass von all den Zahlen, die das RKI derzeit verbreitet, nur die DIVI-Zahlen überhaupt ziemlich nah an dem sind, was sie zu sagen vorgeben, auch wenn Peter Antes, auf dessen Urteil ich viel gebe, da neulich auch Zweifel geäußert hat, die ich erstmal nicht ganz verstehe: die zwei „komischen“ Schnackler, die ich sehe, sind jetzt mal wirklich harmlos.

    Dass die Infektionszahlen problematisch sind, ist inzwischen ein Gemeinplatz; zwar wäre sicher, könnte mensch wirklich den Zeitpunkt der Übertragung in nennenswerter Zahl feststellen, ein sichtbarer Effekt vom Wochenende zu sehen (denn die Übertragung in der Breite dürfte derzeit stark von Arbeit und Arbeitsweg dominiert sein), aber nicht mal der würde die wilden Zacken verursachen, an die wir uns in den letzten Monaten gewöhnt haben.

    Aber ok – dass in Daten dieser Art das Wochenende sichtbar ist, hätte ich auch bei 24/7-Gesundheitsämtern jederzeit vorhergesagt. Beim besten Willen nicht vorhergesagt hätte ich allerdings die Zackigkeit dieser Kurve:

    Plot: Corona-Tote über Tag von JHU

    Zu sehen sind hier die Todesmeldungen pro Tag (jetzt nicht vom RKI, sondern von Johns Hopkins, aber beim RKI sieht das nicht anders aus). Sowohl nach Film-Klischee („Zeitpunkt des Todes: Dreizehnter Erster, Zwölf Uhr Dreiunddreissig“) als auch nach meiner eigenen Erfahrung als Zivi auf einer Intensivstation hätte ich mir gedacht, dass Sterbedaten im Regelfall zuverlässig sind. Und so sehr klar ist, dass während Volksfesten mehr Leute sterben und bei den Motorradtoten ein deutliches Wochenend-Signal zu sehen sein sollte: Corona kennt ganz sicher kein Wochenende.

    Also: DIVI rules.

    Und ich muss demnächst wirklich mal gegen dark mode ranten.

    [1]Als bekennender Autofeind muss ich ja zugeben, dass der größte Wow-Effekt der ganzen Corona-Geschichte war, als im letzten März VW die Produktion eingestellt hat. Dass ich das noch erleben durfte... Der zweitgrößte Wow-Effekt war übrigens, dass die doch ziemlich spürbare Reduktion im Autoverkehr im März und April sich nicht rasch in den Sterblichkeitsziffern reflektiert hat.

« Seite 2 / 2

Letzte Ergänzungen