Bereits mit geringem Aufwand lassen sich auf der Shell Statistiken zur Ausbreitung des Corona-Virus auswerten und auf ansehnliche Weise darstellen.
Jeden Tag werden wir mit Nachrichten über das Corona-Virus und einer Unmenge von statistischen Zahlen bezüglich dessen Ausbreitung konfrontiert. Nicht immer lassen sich diese Zahlen nachvollziehen. Oft hat man das Gefühl, dass die Medien sie aus dem Zusammenhang reißen, um sie der Öffentlichkeit in “verständlicher” Form präsentieren zu können. Die Frage ist nur, ob die Zahlen auch Sinn machen – etwa, wenn man liest oder hört, dass es deutschlandweit etwa 30 000 Neuinfektionen gab, man aber nicht weiß, wie viele Tests gemacht wurden oder wie hoch beispielsweise die Rate an positiven Tests war. Auch viele andere Kennzahlen fallen unter den medialen Tisch, aus welchen Gründen auch immer.
Statistische Daten über die Ausbreitung des Corona-Virus werden in der Regel täglich erfasst und in sogenannten Timelines organisiert. Die Organisation Our World in Data (OWID) zeichnet seit Beginn der Krise diese Daten auf. Auf ihrer Webseite [1] stellt sie die Daten in verschiedenen Formaten zum Download zur Verfügung, unter anderem auch im für die Weiterverarbeitung gut geeigneten CSV-Format.
Ein paar Einzeiler
Am besten laden Sie diese CSV-Datei erst einmal manuell herunter, um zu sehen, was sie enthält. Betrachten Sie das recht umfangreiche File owid-covid-data.csv mit einem einfachen Less oder Cat, kann es sein, dass Sie eine leichte Augenentzündung davontragen, wenn Sie versuchen, genauere Daten einzelner Länder daraus herauszulesen. Selbst wenn Sie die Datei auch mit einem Editor oder auch OpenOffice Writer öffnen, ist es eher schwierig, eine Übersicht zu bekommen.
Grundsätzlich wäre es erst einmal interessant, zu wissen, welche Spalten die CSV-Datei enthält. Der Einzeiler aus der ersten Zeile von Listing 1 bringt etwas Licht ins Dunkel. Der Head-Befehl am Anfang der Pipe gibt die erste Zeile der Datei aus. Sed wandelt anschließend alle Kommas in Zeilenvorschübe um. cat -n nummeriert die Ausgabe durch, und der Pr-Befehl verteilt das Ganze in Spalten über den Bildschirm.
Listing 1
Praktische Einzeiler
$ head -n 1 owid-covid-data.csv | sed 's/,/\n/g' | cat -n | pr -T --columns=2 --width=90 $ cut -f 3 -d "," owid-covid-data.csv | sort | uniq | sed -r '/World|International/d' | cat -n | pr -T --columns=2 --width=120 $ grep -i germany owid-covid-data.csv | cut -f 4-8 -d "," | column -t -s ","
Auf diese Weise erhalten Sie eine Übersicht, mit der man auch etwas anfangen kann (Abbildung 1). Sie können davon ausgehen, dass die ersten vier Spalten (iso_code, continent, location, date) im Lauf der Zeit weder verschwinden, noch Plätze tauschen. Bei den anderen Spalten können Sie da nicht so sicher sein, da ständig neue Kennzahlen hinzukommen. Man versucht auf diesem Weg das Virus zu beobachten, und weiß dabei nicht, wie sich die Pandemie weiterentwickeln wird, und inwieweit Medikamente, Impfungen und Ähnliches dazu beitragen können, die weitere Ausbreitung zu verhindern.
Auf der Github-Seite von OWID können Sie nachsehen, was genau sich hinter den einzelnen Kennzahlen verbirgt [2]. Dort finden Sie auch Kennzahlen, mit denen das Robert-Koch-Institut arbeitet, oder besser gesagt: die es selbst berechnet und dann erst später anderen Organisationen, wie zum Beispiel dem European Centre for Disease Prevention and Control (ECDC), zur Verfügung stellt. Auch OWID bedient sich aus dieser Datenquelle.
Weiterhin ist interessant, welche Länder Daten zur Verfügung stellen. Das fördert der Einzeiler aus der zweiten Zeile von Listing 1 zutage. Je nach Terminalgröße können Sie hier den Parameter --columns oder --width anpassen. Es liegen Daten für knapp 200 Länder vor (Abbildung 2).

Abbildung 2: Die Ausgabe der Länder passt wahrscheinlich nicht ganz ins Terminal. Sie können jedoch einige Parameter des Befehls anpassen, um die Ausgabe an die Bildschirmgröße anzupassen.
Ein Datenblock für ein einzelnes Land lässt sich mit einer simplen Grep-Cut-Kombination herausfiltern. Die letzte Zeile von Listing 1 erledigt das für Deutschland. Allerdings enthält diese Art der Ansicht keine Überschriften. Es lohnt sich also in jedem Fall, ein Skript zu erstellen. Das sollte dann aber auch berücksichtigen, dass sich die Spaltenabfolge ab der fünften Spalte verändern und verschieben kann. Das Skript muss also herauslesen können, welche und wie viele Spalten die Daten enthalten.
Aufgedröselt
Ein solches Skript zeigt Listing 2. Wie jedes Skript beginnt es mit der Angabe des zu verwendenden Interpreters. In den Zeilen 2 bis 6 sehen Sie grundlegende Deklarationen, die in Variablen gespeichert werden, um später leichter darauf zugreifen zu können. Im Prinzip handelt es sich um recht einfache Dinge wie die URL, den Dateinamen, zwei im späteren Verlauf benötigte Spalten sowie die erste Kennzahl: die Weltbevölkerung.
Listing 2
Datenauswertung (abfrage.sh)
#!/usr/bin/bash
URL=https://covid.ourworldindata.org/data/owid-covid-data.csv
DATEI=owid-covid-data.csv
spalte_bevoelkerung=`sed 1q $DATEI | sed 's/,/\n/g' | cat -n | grep "population$" | cut -f 1`
spalte_bevoelkerungdichte=`sed 1q $DATEI | sed 's/,/\n/g' | cat -n | grep "population_density$" | cut -f 1`
welt_bevoelkerung=`grep -i world $DATEI | tail -n 1 | cut -f $spalte_bevoelkerung -d ","`
download(){
test -e $DATEI || wget -O $DATEI $URL
test `ls --full-time $DATEI | gawk '{print $6}'` = `date +%Y-%m-%d` ||
wget -O $DATEI $URL
}
download
laender_auswahl_menu(){
laender=`cut -f 1,3 -d "," $DATEI | sed 1d | sort | uniq | sed '/International/d;/World/d' | sed 's/^/(/;s/,/) /'`
pr --columns=3 -T <<<$laender
echo "Iso-Codes zeilenweise eingeben und mit Strg-D die Eingabe beenden:"
readarray -t laender_auswahl
}
spalten_auswahl_menu(){
sp=`sed 1q $DATEI | sed 's/,/\n/g' | cat -n`
pr -T --columns=2 <<<$sp
echo "Spalten zeilenweise eingeben und mit Strg-D die Eingabe beenden:"
readarray -t spalten_auswahl
}
ausgabe(){
suchausdruck=`tr [:lower:] [:upper:] <<<$1`
local land=`grep "("$suchausdruck")" <<<$laender`
ausgabeblock=`cat <(sed 1q $DATEI) <(grep -E "^"$suchausdruck $DATEI)`
bevoelkerung_land=`tail -n 1 <<<$ausgabeblock | cut -f $spalte_bevoelkerung -d ","`
bevoelkerungsdichte_land=`tail -n 1 <<<$ausgabeblock | cut -f $spalte_bevoelkerungdichte -d ","`
spaltenformat=`for sp in 4 ${spalten_auswahl[*]};do cut -f $sp -d "," <<<$ausgabeblock | wc -L | sed 's/^/ %/g;s/$/s/g' ;done`
echo -e "\n"
echo `tr [:lower:] [:upper:] <<<"$land"`
gawk -F "," -v bev=$bevoelkerung_land -v welt_bev=$welt_bevoelkerung -v dichte=$bevoelkerungsdichte_land <<<$ausgabeblock '
BEGIN{
ub1="Bevölkerung: "bev", Anteil an der Weltbevölkerung: "bev*100/welt_bev" %"
ub2="Einwohner pro km<+>2<+>: "dichte
sterne = gensub(/./, "*", "g", ub1)
print sterne
print ub1
print ub2
print sterne
}
FNR == 1{
printf "'"`echo ${spaltenformat[*]}`"'\n", '"`echo 4 ${spalten_auswahl[*]} | sed 's/^/$/;s/ /,$/g'`"'
}
FNR > 1{
printf "'"`echo ${spaltenformat[*]}`"'\n", '"`echo 4 ${spalten_auswahl[*]} | sed 's/^/$/;s/ /,$/g'`"'
datensatzzaehler++
}
END{
print "\nDatensätze insgesamt: "datensatzzaehler
}
'
}
laender_auswahl_menu
spalten_auswahl_menu
test -e Auswertungen && : || mkdir Auswertungen
for land in "${laender_auswahl[@]}"
do
ausgabe "$land"
done | tee "Auswertungen/""`echo ${laender_auswahl[*]} | sed 's/ /_/g;s/$/_/'`""`date +%a_%d_%b_%Y`"".txt" | less
read -p "weitere Abfragen? (J/N)" weiter
test $weiter = "j" || test $weiter = "J" && $0 || exit 1
Die Zeilen 8 bis 12 enthalten die Funktion für das Herunterladen der aktuellen CSV-Datei. Der Code prüft zunächst, ob die Datei bereits heruntergeladen wurde und ob sie dem aktuellen Datum entspricht. Trifft eines der beiden Kriterien nicht zu, erfolgt der Download, der eine bereits ältere bestehende Datei überschreibt.
In den Zeilen 16 bis 28 befinden sich zwei Funktionen, die ähnlich funktionieren. Die erste bietet alle Länder zur Auswahl an (Abbildung 3), die zweite alle Spalten. Anschließend werden Ihre Eingaben in die jeweils dafür vorgesehenen Arrays eingelesen. Im ersten Fall geben Sie die gewünschten ISO-Codes zeilenweise ein, im zweiten die gewünschten Spalten. Sie beenden die Eingabe jeweils mit [Strg]+[D]. Um Groß- oder Kleinschreibung brauchen Sie sich bei der Eingabe der ISO-Codes nicht zu kümmern. Bei der Eingabe der Spalten müssen Sie die Spalte date nicht extra auswählen: Sie liest das Skript grundsätzlich mit ein, da eine Auflistung der anderen Daten ohne diese wichtige Spalte nicht viel Sinn ergibt.
![Abbildung 3: Bei der Auswahl der Länder geben Sie die ISO-Codes zeilenweise ein und bestätigen die Auswahl dann mit <span class="key-combo">[Strg]+[D]</span>. Die Spaltenauswahl funktioniert genauso, nur geben Sie dort die Ziffern für die gewünschten Spalten ein.](/wp-content/uploads/2021/03/b03_abfrage-laender-288x300.jpg)
Abbildung 3: Bei der Auswahl der Länder geben Sie die ISO-Codes zeilenweise ein und bestätigen die Auswahl dann mit [Strg]+[D]. Die Spaltenauswahl funktioniert genauso, nur geben Sie dort die Ziffern für die gewünschten Spalten ein.
Die Funktion, die sich um die Ausgabe der Daten kümmert, finden Sie in den Zeilen 30 bis 60. Sie enthält ab Zeile 39 ein Gawk-Skript mit darin eingebetteten Bash-Befehlen. Dazu schreibt man den Code für Gawk in einfache Hochkommas und setzt die Bash-Befehle in Anführungszeichen dazwischen:
'Gawk-Code'"Bash-Code"'Gawk-Code'
Diese Funktion bekommt als Parameter einen ISO-Code übergeben, der in Zeile 31 in Großbuchstaben umgewandelt wird und in einer Variablen landet. Mit deren Hilfe fischt Grep dann aus der Liste der Länder den entsprechenden Namen und legt ihn ebenfalls in einer Variablen ab (Zeile 32). Zeile 33 legt die erste Zeile der Datei (die Spaltennamen) und wieder mittels eines Grep den Datenblock für das jeweilige Land in einer Variablen ab, sodass die Bash oder auch Gawk damit arbeiten können. Die Zeilen 34 und 35 filtern die aktuelle Bevölkerungszahl und -dichte des Landes aus dem Ausgabeblock heraus.
Zeile 36 erstellt einen Format-String für einen späteren Printf-Befehl für Gawk. Dazu ermittelt der Code für jede Spalte, die Sie auswählen, im Ausgabeblock den längsten Eintrag, sodass eine Zahlenreihe entsteht, in der jede Zahl am Anfang mit einem Prozentzeichen belegt wird und am Ende ein “s” angefügt wird. So enthält die Variable dann einen String der Form %10s %20s ....
In Zeile 37 beginnt schon die eigentliche Ausgabe, die an dieser Stelle noch die Bash übernimmt. In Zeile 39 übernimmt das Gawk-Skript. Als Parameter erhält es die vorbelegten Variablen sowie den eingelesenen Ausgabeblock.
Der BEGIN-Block von Zeile 40 bis 48 arbeitet mit den Variablen. In ub1 und ub2 werden Strings zusammengesetzt oder auch buchstäblich zusammengeschoben, indem der Code sie einfach aneinanderkettet. In Zeile 43 erfolgt eine sogenannte General Substitution, die einfach jedes Zeichen in ub1 in einen Stern umwandelt und das Resultat in der Variablen sterne abspeichert. Die Zeilen 44 bis 47 geben die Variableninhalte dann aus.
Zwei weitere Blöcke kümmern sich um die Ausgabe der passenden Überschriften und Datenspalten (Zeilen 49 bis 55). Durch die Abfrage der speziellen Variablen FNR weiß Gawk, welche Zeile gerade an der Reihe ist, und führt den entsprechenden Block dann aus. Die Variable datensatzzaehler in Zeile 54 erfasst, wie viele Datensätze es insgesamt gibt; das Resultat wird dann im End-Block mit dem passenden Text ausgegeben. Dabei fällt immer wieder auf, dass nicht alle Spalten jedes Satzes komplett befüllt sind: Bei der Unmenge von Daten bleiben Datenpannen nicht aus, und manche Länder beobachten nicht alle oder nicht dieselben Kennzahlen wie andere.
Zu guter Letzt müssen alle bisher definierten Funktionen auch aufgerufen werden. Die Abfrage der anzuzeigenden Länder und Spalten übernehmen die Zeilen 62 und 63. Zeile 65 prüft, ob es das Verzeichnis ./Auswertungen/ schon gibt, und legt es gegebenenfalls an, um dort später Daten ablegen zu können. Die Aufrufe in den Zeilen 67 bis 70 arbeiten anschließend alle ausgewählten Länder ab und übergeben die Resultate an die Funktion ausgabe.
Am Ende der For-Schleife befindet sich noch eine Tee-Anweisung, die die Daten mit passendem Datum und Angabe der Länder versehen in das Verzeichnis ./Auswertungen/ schreibt und dann auf die Standardausgabe kopiert, wo ein Less Ihnen ermöglicht, die Daten im Terminal zu betrachten (Abbildung 4). Die Routine in den Zeilen 72 und 73 erfragt zu guter Letzt, ob Sie noch weitere Abfragen starten möchten. Ist das der Fall, startet das Skript von vorn, anderenfalls verabschiedet es sich mit einem Exit-Befehl.

Abbildung 4: Mit dem Skript verfolgen Sie nach, wie sich die Pandemie im Ursprungsland China und anderswo entwickelt hat. Dabei wählen Sie selbst aus, welche Zahlen Sie sehen wollen.
Mit dem Skript sehen Sie alle Kennzahlen beliebig ein und erkennen dadurch mehr, als Ihnen die Medien präsentieren. Insbesondere lassen sich so die Daten einzelner Länder besser miteinander vergleichen, als das mit fertig aufbereiteten, oft sehr selektiven Daten aus Presse, Radio und Fernsehen möglich wäre. Zudem darf man getrost davon ausgehen, dass fast niemand genau weiß, wie ein R-Wert, eine Inzidenz oder andere magische Zahlen berechnet werden, obwohl sehr viele Leute gern und ausgiebig über das Thema diskutieren und auch Politiker den Anschein erwecken, als wüssten sie genau darüber Bescheid.
Sie haben zudem die Möglichkeit, auch selbst Berechnungen mit diesen Zahlen anzustellen, um noch tiefergehende Einblicke zu bekommen oder auch Zahlen herauszufiltern, die üblicherweise nicht in den Medien auftauchen. Möglicherweise haben Sie sich schon einmal gefragt, was Covid-Tests denn kosten und wie viel Geld die Pharmaindustrie damit weltweit verdient?
Das herauszufinden, ist mit ein paar Kniffen kein Problem. In der Spalte total_tests weisen die meisten Länder die Gesamtanzahl der Tests aus. Im Prinzip muss man nur die Testzahlen aller Länder zusammenrechnen, um zu erfahren, wie viele Tests weltweit bis zum aktuellen Datum gemacht wurden. Dann multipliziert man das Ergebnis mit einem aktuellen Preis und weiß dann, wie viel Umsatz damit gemacht wird.
Zahlen selbst berechnen
Das Skript dazu zeigt Listing 3. Es bedient sich in Zeile 2 der Download-Routine aus Listing 2. Deswegen ist es wichtig, dass das Skript im selben Verzeichnis liegt wie das erste Skript. Zeile 3 speichert den Dateinamen in einer Variablen. Das kann man aber auch weglassen, da die Source-Anweisung aus Zeile 2 das auch schon aus dem ersten Skript übernimmt.
Listing 3
Umsatzberechnung (umsatz.sh)
#!/usr/bin/bash
source <(sed -n '2,/download$/p' abfrage.sh)
DATEI=owid-covid-data.csv
total_test_spalte=`sed 's/,/\n/g;1q' $DATEI | grep -n total_tests$ | cut -f 1 -d ":"`
readarray -t alle_laender <<<`cut -f 3 -d "," $DATEI | sort | uniq | sed -r '/World|International/d'`
for land in "${alle_laender[@]}"
do
wert_land=`grep -F "$land" $DATEI | cut -f $total_test_spalte -d "," | grep -E "[0-9]" | tail -n 1`
grep -q "[0-9]" <<<$wert_land && echo "$land,$wert_land,`(sed -r 's/\.[0-9]*$//' <<<$wert_land) | numfmt --grouping`"
done | sort -t "," -k 2 -n | gawk -F "," '
BEGIN{
printf "%-25s %20s\n","Land","Tests"
preis_pro_test=180
}
{
printf "%-25s %20s\n",$1,$3
tests+=$2
}
END{
"numfmt --grouping "tests |& getline tests_grupiert
printf "\n%-20s %25s\n","gemachte Tests: ",tests_grupiert
printf "%-20s %25s\n","Preis pro Test: ",preis_pro_test" Euro"
"numfmt --grouping "tests*preis_pro_test |& getline megaumsatz
printf "%-20s %25s\n","erzielter Umsatz:",megaumsatz" Euro"
}
'
Zeile 4 ermittelt die richtige Spalte für die Testanzahl, Zeile 6 liest alle Länder in ein Array ein. Die Schleife in den Zeilen 8 bis 12 geht alle Länder einzeln durch und erzeugt drei durch Kommas getrennte Spalten: der Name des Lands, die Gesamtzahl der Tests sowie eine mit Tausender-Dezimalpunkten formatierte Gesamtzahl. Danach sortiert ein Sort-Befehl diese Daten nach der zweiten Spalte.
Nun übernimmt ein Gawk-Skript die formatierte Ausgabe. Der BEGIN-Block erzeugt eine passende Überschrift und legt den aktuellen Preis für Tests fest. Der Hauptblock gibt den Namen des Landes und die formatierten Zahlen aus, wobei er gleichzeitig die Zahlen aus der zweiten Spalte aufaddiert. Der END-Block zeigt dann das Gesamtergebnis (Abbildung 5) – eine Zahl, die die Pharmabranche vermutlich nicht so gern präsentiert.

Abbildung 5: Die Zahlen für die weltweite Anzahl von CoViD-Tests und die Schätzung der damit verbundenen Umsätze lassen es eher unwahrscheinlich erscheinen, dass die Pharmabranche in den nächsten Jahren pleite geht.
Im END-Block des Gawk-Teils sehen Sie auch, dass Gawk mit Pipes umgehen kann. Dazu übergibt man einen Shell-Befehl an ein |& getline Variable (Zeile 22 und 25). Gawk fängt dann die Ausgabe des Shell-Befehls auf, speichert sie in der Variablen, und kann dann im weiteren Verlauf damit arbeiten. Auf diese Weise lassen sich selbst kompliziertere Subprogramme realisieren, allerdings auf Kosten einer komplexeren Handhabung. Vor allem muss man sie an der richtigen Stelle schließen, um eine passgerechte Ausgabe zu erzeugen. In diesem Fall handelt es sich aber um eine einfache Shell-Anweisung, die sich dann auch von selbst wieder beendet.
Fazit
Wie Sie sehen, gelingt es mit verhältnismäßig wenig Aufwand, Statistiken zur Ausbreitung des Corona-Virus auszuwerten und auf ansehnliche Weise darzustellen. So kann man dann im Endeffekt auch wirklich informiert bei diesem Thema mitreden.
Viele Zahlen, wie etwa Bevölkerungsdichte, Altersdurchschnitt der Bevölkerung, Lebenserwartung und so weiter, heben die Medien nur selten hervor. So hat etwa Deutschland eine zehn Mal höhere Bevölkerungsdichte als Schweden. Naturgemäß müssen Länder mit einer höheren Bevölkerungsdichte strengere Maßnahmen zur Eindämmung der Pandemie ergreifen als dünn besiedelte. Die Skandinavier können es sich eher leisten, lediglich eine Empfehlung für Masken auszusprechen, statt ihre Bürger dazu zu verdonnern, monatelang mit einer “zweiten Mütze” im Gesicht herumzulaufen. (jlu)
Infos
-
OWID-CoViD-Daten: https://ourworldindata.org/coronavirus-source-data
-
Bedeutung der Kennzahlen: https://github.com/owid/covid-19-data/blob/master/public/data/owid-covid-codebook.csv






