Excel? Nein - AWK und Gnuplot!

vorhergehende Artikel in: Linux
17.12.2020

Nachdem ich neulich ein Video zum Thema AWK gefunden hatte habe ich gedacht - da geht noch mehr

@youtube.com

Es ging dabei darum, dass man LibreOffice Calc oder ähnliche Programme nicht benötigt wenn man ein Linux-Terminal hat. Ausgehend von Garys Beispielen habe ich mir eine schnelle Liste von Anforderungen aufgestellt, die ich hier mal mit Syntaxbeispielen zu Lösung darstellen möchte.

Beispiel-Setup

Ich bin von einer ähnlichen Datenquelle wie Gary ausgegangen - ein einfaches Dateisystem-Listing mittels ls -l sorgte für Input. Ich musste die erste Zeile noch abschneiden damit ich sauber mit tabellarischen Daten arbeiten konnte - dafür sorgte tail -n +2.

Als erstes wird das Feld mit der Dateigröße in jeder Zeile der Ausgabe ausgegeben:

ls -l /usr/bin |tail -n +2| awk '{print $5}'

Die Anzahl der Zeilen kann man mittels wc ermitteln:

ls -l /usr/bin |tail -n +2| wc -l

Das funktioniert aber auch mit AWK:

ls -l /usr/bin |tail -n +2| awk 'END{print NR}'

Man kann die extrahierten Daten jeder Zeile durch eine vorangestellte Zeilennummer ergänzen:

ls -l /usr/bin |tail -n +2| awk '{print NR":"$5}'

Statistik

Nun ein wenig Statistik: Die Berechnung des Durchschnitts (Mittelwerts) der Daten in Spalte 5 erfolgt so:

ls -l /usr/bin |tail -n +2| awk 'BEGIN{sum=0.0}{sum+=$5}END{printf("Average: %f\n",sum/NR)}'

Variablen werden von AWK immer auf 0 initialisiert - daher kann man im vorigen Statement die BEGIN Klausel auch weglassen. Es ist auch möglich, Variablen auf der Kommandozeile zu vereinbaren und dem Skript zu übergeben:

ls -l /usr/bin |tail -n +2| awk -v N=5 '{sum+=$N}END{printf("Average: %f\n",sum/NR)}'

Wir berechnen zusätzlich die Standardabweichung:

ls -l /usr/bin |tail -n +2| awk -v N=5 \
'{sum+=$N;sumsq+=$N^2}END{printf("Average: %f Std deviation: %f\n",sum/NR,sqrt((sumsq-sum^2/NR)/NR))}'

Dieses Mal wird der Median bestimmt:

ls -l /usr/bin |tail -n +2| awk -v N=5 '{print $N}'|sort -n| awk \
'{a[i++]=$1}END{x=int((i+1)/2);if (x < (i+1)/2) y=(a[x-1]+a[x])/2; else y=a[x-1]; printf("Median: %f\n",y)}'

Möchte man erfahren, ab welchem Wert ein bestimmter Prozentsatz der Eingaben jeweils unterhalb dieses Wertes liegen (Percentile), kann man das ebenfalls mittels AWK herausfinden:

ls -l /usr/bin |tail -n +2| awk -v N=5 \
'{print $N}'|sort -n| awk '{s[NR-1]=$N} END{for(i=0.1;i<=1.0;i+=0.1){printf("%d %f\n",i*100,s[int(NR*i-0.5)])}}'

Man kann mittels AWK auch Histogramme erzeugen:

ls -l /usr/bin |tail -n +2| awk -v N=5 '{print $N}'|sort -n| awk -v DELTA=150000 \
'BEGIN{delta = (DELTA == "" ? 10000 : DELTA)} \
{bucketNr = int(($0+delta) / delta);cnt[bucketNr]++;numBuckets = (numBuckets > bucketNr ? numBuckets : bucketNr)} \
END{for (bucketNr=1; bucketNr<=numBuckets; bucketNr++) {end = beg + delta;printf("%0.1f %0.1f %d\n"), beg, end, cnt[bucketNr];beg = end;}}'>hist.dat

Gnuplotting

In diesem Fall sollten wir uns auf den interessanten Teil des Histogrammes beschränken - das zeigt auch gleich, dass man mehrere Variablen auf der Kommandozeile vereinbaren kann:

ls -l /usr/bin |tail -n +2| awk -v N=5 '{print $N}'|sort -n| awk -v DELTA=1000 -v MAX=15001  \
'BEGIN{delta = (DELTA == "" ? 10000 : DELTA)} \
{if($0<15001){bucketNr = int(($0+delta) / delta);cnt[bucketNr]++;numBuckets = (numBuckets > bucketNr ? numBuckets : bucketNr)}} \
END{for (bucketNr=1; bucketNr<=numBuckets; bucketNr++) {end = beg + delta;printf "%0.1f %0.1f %d\n", beg, end, cnt[bucketNr];beg = end;}}'>hist.dat

Man kann dieses Histogramm direkt mit Gnuplot im Terminal darstellen - die Größe des Plots passt sich automatisch der Größe des Terminals an...

ls -l /usr/bin |tail -n +2| awk -v N=5 '{print $N}'|sort -n| awk -v DELTA=1000 -v MAX=15001  \
'BEGIN{delta = (DELTA == "" ? 10000 : DELTA)} \
{if($0<MAX){bucketNr = int(($0+delta) / delta);cnt[bucketNr]++;numBuckets = (numBuckets > bucketNr ? numBuckets : bucketNr)}} \
END{for (bucketNr=1; bucketNr<=numBuckets; bucketNr++) {end = beg + delta;printf "%0.1f %0.1f %d\n", beg, end, cnt[bucketNr];beg = end;}}' \
|gnuplot -p -e "set terminal dumb size $(tput cols), $(tput lines) enhanced; set autoscale;set style data histogram;set style fill solid;plot '-' using 3:xtic(1)"

Bedingungen

Es ist möglich, nur bestimmte Zellen in der Berechnung zu benutzen - Man kann zum Beispiel anhand regulärer Ausdrücke bestimmte Zeilen ausschließen:

ls -l /usr/bin |tail -n +2| awk -v N=5 \
'{if($9 ~ /^m/){sum+=$N;sumsq+=$N^2;count++}} \
END{printf("Average: %f Std deviation: %f Count: %d\n",sum/count,sqrt((sumsq-sum^2/count)/count),count)}'

Es ist dabei auch möglich, die Groß- und Kleinschreibung zu ignorieren:

ls -l /usr/bin |tail -n +2| awk -v N=5 \
'{IGNORECASE = 1;if($9 ~ /^m/){sum+=$N;sumsq+=$N^2;count++}} \
END{printf("Average: %f Std deviation: %f Count: %d\n",sum/count,sqrt((sumsq-sum^2/count)/count),count)}'

Eine alternative Schreibweise dafür:

ls -l /usr/bin |tail -n +2| awk -v N=5 \
'IGNORECASE = 1 && $9 ~ /^m/{sum+=$N;sumsq+=$N^2;count++} \
END{printf("Average: %f Std deviation: %f Count: %d\n",sum/count,sqrt((sumsq-sum^2/count)/count),count)}'

Es ist natürlich auch möglich, die in der Berechnung zu berücksichtigenden Zeilen einfach über ihre Zeilennummern zu bestimmen:

ls -l /usr/bin |tail -n +2| awk -v N=5 \
'{if(NR >9 && NR<21){sum+=$N;sumsq+=$N^2;count++}} \
END{printf("Average: %f Std deviation: %f Count: %d\n",sum/count,sqrt((sumsq-sum^2/count)/count),count)}'

Eine alternative Schreibweise dafür:

ls -l /usr/bin |tail -n +2| awk -v N=5 \
'(NR >9 && NR<21){sum+=$N;sumsq+=$N^2;count++} \
END{printf("Average: %f Std deviation: %f Count: %d\n",sum/count,sqrt((sumsq-sum^2/count)/count),count)}'

Es ist sogar möglich, Werte über den Vergleich von Daten (Zeitpunkten) zu filtern:

ls -lt --time-style=long-iso /usr/bin|awk -v date=2020-06-09 'date<$6{print $8}'

Funktionen

Es gibt auch Möglichkeiten, Funktionen zu definieren - während int(n) einfach nur die Nachkommastellen abschneidet, kann man in AWK eine korrekte Rundungsfunktion wie folgt definieren:

func round(n)
{
  return int(n+0.5)
}

Eine Funktion für die nächstgrößere ganze Zahl ist ebenfalls sehr einfach zu schaffen:

function ceil(n)
{
  return n%1 ? int(n)+1 : n
}

Cheat-Sheet

Wer möchte, kann sich das hier angehängte Cheat-Sheet, das ich aus einem Template erstellt habe auch ausdrucken...

Lizenz

TeX-Sourcecode

Cheat-Sheet

Aktualisierung vom 17. Dezember 2020

Eine weitere Idee ist das Einfärben einzelner Abschnitte (Zellen) in einer Datei - wieder am Beispiel eines Dateisystemlistings mittels ANSI-Escape-Codes. Hier wird die Spalte mit der Dateigröße in Bytes farblich hervorgehoben, wenn der jeweilige Wert größer als eine festgelegte Schwelle ist. Das Beispiel funktioniert nur in Terminals, die Farben darstellen und ANSI-Escape-Sequenzen interpretieren können - beispielsweise xterm.

ls -l /usr/bin |tail -n +2| \
awk '{if($5>999999)print NR"\t"$1"\t"$2"\t"$3"\t"$4"\t\033[1;31m"$5"\033[0m"; \
else print NR"\t"$1"\t"$2"\t"$3"\t"$4"\t"$5;}'

Das Cheat-Sheet wurde natürlich entsprechend erweitert...

Artikel, die hierher verlinken

GPS-(NMEA-)Tracks mit Gnuplot zu Animationen verarbeiten

02.11.2021

Durch einen Post auf Mastodon wurde ich auf eine Idee gebracht...

AWK Linksammlung

29.10.2021

Ich habe bereits darüber berichtet, dass man in vielen Fällen AWK den Vorzug vor Excel und ähnlichem geben kann (und sollte!) - hier nun einige weitere Inspirationen dahingehend

Gnuplot und Statistik

26.05.2021

Ich bin neulich bei Mastodon über eine Anfrage gestolpert, wie man online schnell eine graphische Darstellung statistischer Daten mit Mittelwert usw. anfertigen könnte. Ich gab dazu einige Suchparameter ergänzt um den Schlüsselbegriff "Gnuplot" ein und fand sofort einen Artikel, der erklärte, wie man das angefragte Szenario mittels Gnuplot umsetzen könnte.

Renderer für generierte Dungeons mit Ansi-Steuercodes

15.01.2021

Nachdem ich neulich mein AWK-Kochbuch um ein neues Rezept erweitert habe mit dem es möglich ist, einzelne Felder einer Datei regelbasiert mittels Ansi-Escape-Sequenzen unterschiedlich einzufärben, hat mich der Ansi-Steuercode-Virus wieder voll erwischt...

Alle Artikel rss Wochenübersicht Monatsübersicht Github Repositories Gitlab Repositories Mastodon Über mich home xmpp


Vor 5 Jahren hier im Blog

  • Certstream, InfluxDB, Grafana und Netflix

    16.04.2019

    Nachdem ich vor kurzem über mein erstes Spielen mit dem certstream berichtete, habe ich weitere Experimente gemacht und die Daten zur besseren Auswertung in eine InfluxDB gepackt, um sie mit Grafana untersuchen zu können.

    Weiterlesen...

Neueste Artikel

  • Die sQLshell ist nun cloudnative!

    Die sQLshell hat eine weitere Integration erfahren - obwohl ich eigentlich selber nicht viel dazu tun musste: Es existiert ein Projekt/Produkt namens steampipe, dessen Slogan ist select * from cloud; - Im Prinzip eine Wrapperschicht um diverse (laut Eigenwerbung mehr als 140) (cloud) data sources.

    Weiterlesen...
  • LinkCollections 2024 III

    Nach der letzten losen Zusammenstellung (für mich) interessanter Links aus den Tiefen des Internet von 2024 folgt hier gleich die nächste:

    Weiterlesen...
  • Funktionen mit mehreren Rückgabewerten in Java

    Da ich seit nunmehr einem Jahr bei meinem neeun Arbeitgeber beschäftigt und damit seit ungefähr derselben Zeit für Geld mit Python arbeite, haben sich gewisse Antipathien gegenüber Python vertieft (ich kann mit typlosen Sprachen einfach nicht umgehen) - aber auch einige meiner Gründe, Python zu lieben sind ebenso stärker geworden. Einer davon ist der Fakt, dass eine Methode in Python mehr als einen Wert zurückgeben kann.

    Weiterlesen...

Manche nennen es Blog, manche Web-Seite - ich schreibe hier hin und wieder über meine Erlebnisse, Rückschläge und Erleuchtungen bei meinen Hobbies.

Wer daran teilhaben und eventuell sogar davon profitieren möchte, muß damit leben, daß ich hin und wieder kleine Ausflüge in Bereiche mache, die nichts mit IT, Administration oder Softwareentwicklung zu tun haben.

Ich wünsche allen Lesern viel Spaß und hin und wieder einen kleinen AHA!-Effekt...

PS: Meine öffentlichen GitHub-Repositories findet man hier - meine öffentlichen GitLab-Repositories finden sich dagegen hier.