Der effizienteste Weg, stündlich eine halbe Million mathematische Operationen durchzuführen

Der effizienteste Weg, stündlich eine halbe Million mathematische Operationen durchzuführen

Aus persönlichen Gründen und um Erfahrungen zu sammeln, habe ich begonnen, Wetterdaten in einer Datenbank zu speichern. Ich verwende wgrib2, um die Daten zu analysieren und in MySQL zu importieren. Da die Daten in verschiedenen Einheiten formatiert sind – Windkomponenten „U“ und „V“, Kelvin usw. – muss ich sie in Windgeschwindigkeitsknoten, Windradiusgrade und Temperaturgrad Celsius usw. umrechnen.

Ich habe eine Bash-For-Schleife erstellt, die alle Datenwerte durchläuft, aber sie ist ziemlich ineffizient und ich bin sicher, dass es bessere Möglichkeiten gibt, dies zu tun. Sie stützt sich stark auf awk ... und benötigt 15 bis 17 Minuten, um die Daten für etwa 1150 Stationen zu analysieren. Jede Station hat eine Tabelle mit identischer Struktur und 160 Spalten in der MySQL-Datenbank.

Die Bash-Arrays, die ich für TK (Temperatur Kelvin), RH (Luftfeuchtigkeit) usw. eingerichtet habe, haben Werte von 1000, 975, 950, 925 usw. bis hinunter zu 100 Millibar.

for thKey in ${!TK[@]}
do
    thRH=${RH[$thKey]}
    thTK=${TK[$thKey]}
    thTC=$(echo -| awk -v tk="$thTK" '{printf "%.1f\n", tk-273.15}')
    thWU=${WU[$thKey]}
    thWV=${WV[$thKey]}
    thTD=$(echo -| awk -v tc="$thTC" -v rh="$thRH" '{printf "%.1f\n", tc-(100-rh)/5}')
    thWD=$(echo -| awk -v wu="$thWU" -v wv="$thWV" '{printf "%.0f\n", 57.29578*(atan2(wu, wv))+180}')
    thWS=$(echo -| awk -v wu="$thWU" -v wv="$thWV" '{printf "%.1f\n", sqrt(wu*wu+wv*wv)*1.944}')
    sed -i '/\/station_id/a <'"$thKey"'T>'"$thTC"'<\/'"$thKey"'T><'"$thKey"'D>'"$thTD"'<\/'"$thKey"'D><'"$thKey"'WD>'"$thWD"'<\/'"$thKey"'WD><'"$thKey"'WS>'"$thWS"'<\/'"$thKey"'WS>' $xmlOut
done

Wie Sie daran erkennen können, besteht das offensichtliche Problem darin, dass es etwa 1150 * 160 Aufrufe von awk durchführt. Daher wäre es wahrscheinlich effizienter, die Master-Arrays an awk zu übergeben und awk nur einmal pro Schleife zu erzeugen (1/160 von dem, was ich jetzt mache!). Aber ich scheine die awk-Syntax für diese Vorgehensweise nicht richtig hinzubekommen ...

awk --version

GNU Awk 4.1.3, API: 1.1 (GNU MPFR 3.1.4, GNU MP 6.1.0)

Hier ist ein Beispiel:

TK=(325,350,231,655)
echo -| awk -v tk="${TK[*]}" '{split(tk,tka,/ /)} { for (i=0; i<=NF; i++) { printf "%.1f\n", tka[i]-273.15 } } '

-273,1 51,9

^ das ist nicht richtig. Das Array hat 4 Werte, es sollte nicht nur 2 zurückgeben.

echo -| awk -v tk="${TK[*]}" '{split(tk,tka,/ /)} { for (i=0; i<=length(tka); i++) { printf "%.1f\n", tka[i]-273.15 } } '

^ dies erzeugt eine Endlosschleife.

Irgendwelche Ideen? Vielleicht etwas Perl lernen und das alles an ein Perl-Skript übergeben?

Antwort1

Persönlich würde ich ja alles in Perl machen. :-)

TK=(325,350,231,655)

Ups. Vorsicht. Sie haben ein Array mit einem einzigen Element erstellt, dessen Element ein durch Kommas getrennter String ist.

echo -| awk -v tk="${TK[*]}" '{split(tk,tka,/ /)} { for (i=0; i<=NF; i++) { printf "%.1f\n", tka[i]-273.15 } } '

awkArrays beginnen bei 1, nicht bei 0.

Da Sie die Variable zuweisen, verwenden Sie die STDIN-Daten eigentlich nur für den NF-Wert (aber Sie haben nur ein einziges Element übergeben). Anstatt NF zu verwenden, zählen wir das Ergebnis einfach splitexplizit ab. Vielleicht so etwas:

$ TK=(325 350 231 655)
$ echo - | awk -v tk="${TK[*]}" '{fields=split(tk,tka,/ /)} { for (i=1; i<=fields; i++) { printf "%.1f\n", tka[i]-273.15 } } '
51.9
76.9
-42.1
381.9

Wie dave_thompson_085 erwähnt, machen Sie sich mehr Arbeit, wenn Sie die Daten direkt einer Variablen zuweisen, anstatt sie einfach über STDIN zu senden. Üblicher wäre wahrscheinlich so etwas wie:

$ echo ${TK[*]} | awk '{for (i=1; i<=NF; i++) { printf "%.1f\n", $i-273.15 } } '
51.9
76.9
-42.1
381.9

Und falls Sie einen Lösungsansatz suchen perl:

$ echo ${TK[*]} | perl -lane 'for $item (@F) {print $item-273.15}'
51.85
76.85
-42.15
381.85

verwandte Informationen