Tag Archive for: CSV

Auswertung von CSV- und Log-Dateien auf der Command Line mit awk

Die Programmiersprache awk ist klein und unscheinbar, unter Data Science at the Command Line-Verfechtern allerdings ein häufiges Tool zur schnellen Analyse von CSV-Datein und vergleichbar strukturierten Daten (z. B. Logfiles) mit über Trennzeichen differenzierten Spalten. Auch in Shell-Skripten kommt awk meistens dann zum Einsatz, wenn es um den Zugriff, aber auch um die Manipulation von solchen Dateien geht.

Data Science at the Command Line: Facing the Future with Time-Tested Tools

awk wird als Skriptsprache mit nahezu jeder Linux-Distribution ausgeliefert und ist recht einfach eingehalten, kann jedoch auch schnell kryptisch werden. awk wird meistens ad-hoc auf der Kommandozeile ausgeführt, es können jedoch auch Skripte in awk-Dateien erstellt werden. Häufiger Grund für den Einsatz von awk ist die Anwendung von regulären Ausdrücken (Textmustersuche) auf Logdateien.

Nachfolgend ein kleines Tutorial für den Schnelleinstieg in diese interessante Analysetool auf Kommandozeile. Die CSV-Datei einfach hier downloaden: (einen Überblick über den Inhalt bietet auch eine Einführung in Python, die ebenfalls auf dieser CSV-Datei basiert)

wget https://www.data-science-blog.com/download/standorte.csv

CSV-Datei gedownloaded? Dann kann es losgehen im Terminal jeder beliebiger Linux-Distribution:

awk -F'|' '{print}' standorte.csv

Anweisungen, so auch die obige, beginnen stets mit “awk”. Da diese CSV-Datei nicht mit dem Standardchar (Komma), sondern einem vertikalen Strich (Pipe) getrennt ist, muss dies via “-F’|'” angegeben werden. Wäre das Trennzeichen ein Semikolon, wäre der Parameter “-F’;'” korrekt. Der Befehl gibt jede Zeile des CSV in der Kommandozeile aus, so dass wie nachfolgend den gesamten Dateiinhalt sehen:

ID|Standort|Funktion|Mitarbeiter|Umsatz|Kosten 
1|Muenchen|Verwaltung + Vertrieb|45|3500000|2300000 
2|Stuttgart|Nur Vertrieb|23|2800000|800000 
3|Hannover|Verwaltung + Vertrieb|45|1800000|1000000 
4|Leipzig|Nur Vertrieb|12|1000000|320000 
5|Dresden|Produktio + Vertrieb|65|450000|700000 
6|Frankfurt am Main|Nur Vertrieb|12|240000|20000 
7|Duesseldorf|Nur Vertrieb|43|45000|53000 
8|Kassel|Nur Vertrieb|23|250000|90000 
9|Hamburg|Verwaltung + Vertrieb|89|2800000|690000 
10|Koeln|Nur Vertrieb|21|110000|12000 
11|Potsdam|Nur Vertrieb|12|20000|67000 
12|Nuernberg|Nur Vertrieb|15|60000|30000 
13|Ingolstadt|Nur Vertrieb|8|80000|10000 
14|Wolfsburg|Nur Vertrieb|8|90000|23000 
15|Braunschweig|Nur Vertrieb|32|900000|750000 
16|Augsburg|Verwaltung + Vertrieb|45|700000|370000 
17|Chemnitz|Nur Vertrieb|4|95000|78000 
18|Bochum|Nur Vertrieb|9|32000|67000 
19|Dortmund|Produktio + Vertrieb|56|2100000|450000
20|Essen|Nur Vertrieb|10|190000|140000

Viele CSV- und Logdateien haben keinen Header, diese hier hat jedoch die erste Zeile als Header, die daher bei der Analyse nicht als Werte-Zeile fehlinterpretiert werden darf, daher wird nachfolgend von nun an die Anweisung “NR>1” mitgegeben:

awk -F'|' 'NR>1 {print}' standorte.csv

Spalten werden in awk über das Dollarzeichen angesprochen, folgende Anweisung zeigt uns alle Zeilen der zweiten Spalte:

awk -F'|' 'NR>1 {print $2}' standorte.csv

Diese Skriptsprache beherrscht assoziative Arrays. Es können demnach auch nicht-numerische Schlüssel für den Zugriff auf Datenfelder verwendet werden. Dies machen wir uns für das Anzeigen aller Standorte mit Angabe der jeweiligen Mitarbeiterzahl an dem Standort zu nutze. Die Variable a speichert alle Mitarbeiterzahlen in Spalte 4 über den Schlüssel des Standortnamens in Spalte 2, dann endet der Anweisungsblock und es folgt eine For-Schleife, die alle Schlüsselwerte ausgibt und den dazugehörigen Speicherwert (Mitarbeiterzahl) ausgibt.

awk -F'|' 'NR>1{a[$2] = $4;} END {for (i in a) print "Standort: " i " - Mitarbeiterzahl: " a[i];}' standorte.csv
Standort: Essen - Mitarbeiterzahl: 10
Standort: Bochum - Mitarbeiterzahl: 9
Standort: Hannover - Mitarbeiterzahl: 45
Standort: Frankfurt am Main - Mitarbeiterzahl: 12
Standort: Dresden - Mitarbeiterzahl: 65
Standort: Wolfsburg - Mitarbeiterzahl: 8
Standort: Dortmund - Mitarbeiterzahl: 56
Standort: Braunschweig - Mitarbeiterzahl: 32
Standort: Chemnitz - Mitarbeiterzahl: 4
Standort: Augsburg - Mitarbeiterzahl: 45
Standort: Leipzig - Mitarbeiterzahl: 12
Standort: Duesseldorf - Mitarbeiterzahl: 43
Standort: Nuernberg - Mitarbeiterzahl: 15
Standort: Hamburg - Mitarbeiterzahl: 89
Standort: Muenchen - Mitarbeiterzahl: 45
Standort: Potsdam - Mitarbeiterzahl: 12
Standort: Kassel - Mitarbeiterzahl: 23
Standort: Koeln - Mitarbeiterzahl: 21
Standort: Ingolstadt - Mitarbeiterzahl: 8
Standort: Stuttgart - Mitarbeiterzahl: 23

Auch If-Anweisungen sind einfach machbar. Folgendes Beispiel unterscheidet die Zeilennummern (Spalte1) nach geraden und ungeraden Zahlen und gibt den dazugehörigen Standortnamen (Spalte 2) aus.

awk -F'|' 'NR>1 {if ($1 % 2 == 0) print "Gerade: " $1"->"$2; else print "Ungerade: " $1"->"$2} ' standorte.csv
Ungerade: 1->Muenchen
Gerade: 2->Stuttgart
Ungerade: 3->Hannover
Gerade: 4->Leipzig
Ungerade: 5->Dresden
Gerade: 6->Frankfurt am Main
Ungerade: 7->Duesseldorf
Gerade: 8->Kassel
Ungerade: 9->Hamburg
Gerade: 10->Koeln
Ungerade: 11->Potsdam
Gerade: 12->Nuernberg
Ungerade: 13->Ingolstadt
Gerade: 14->Wolfsburg
Ungerade: 15->Braunschweig
Gerade: 16->Augsburg
Ungerade: 17->Chemnitz
Gerade: 18->Bochum
Ungerade: 19->Dortmund
Gerade: 20->Essen

Folgendes Beispiel klassifiziert alle Standorte mit weniger als 10 Mitarbeitern, allerdings nicht über “if…else…”, sondern über die Kurzabfrage nach dem Schema a>b?”True”:”False”.

awk -F'|' 'NR>1 {a[$2]=$4>=10?$2"->"$4:$2" hat weniger als 10 Mitarbeiter"; print a[$2]}' standorte.csv
Muenchen->45
Stuttgart->23
Hannover->45
Leipzig->12
Dresden->65
Frankfurt am Main->12
Duesseldorf->43
Kassel->23
Hamburg->89
Koeln->21
Potsdam->12
Nuernberg->15
Ingolstadt hat weniger als 10 Mitarbeiter
Wolfsburg hat weniger als 10 Mitarbeiter
Braunschweig->32
Augsburg->45
Chemnitz hat weniger als 10 Mitarbeiter
Bochum hat weniger als 10 Mitarbeiter
Dortmund->56
Essen hat weniger als 10 Mitarbeiter

Folgendes Code-Beispiel zeigt die Zählung der Vorkommnisse (Entsprechung: GROUP BY Spalte3, Count(*)).

awk -F'|' 'NR>1 {a[$3]++;} END {for (i in a) print i, a[i];}' standorte.csv
Produktio + Vertrieb 2
Verwaltung + Vertrieb 4
Nur Vertrieb 14

Etwas umformuliert, können wir auch die Werte pro Gruppe aufsummieren, nachfolgend beispielhaft der Gewinn (Einnahmen aus Spalte 5 – Kosten aus Spalte 6) und die Mitarbeiterzahl über die jeweilige Gruppe.

awk -F'|' 'NR>1{a[$3]+=$5-$6; b[$3]+= $4}END{for (i in a) print i "; Gewinn: " a[i] "; Mitarbeter: " b[i];}' standorte.csv
Produktio + Vertrieb; Gewinn: 1400000; Mitarbeter: 121
Verwaltung + Vertrieb; Gewinn: 4440000; Mitarbeter: 224
Nur Vertrieb; Gewinn: 3452000; Mitarbeter: 232

Das Zusammenführen von Zeichenketten erfolgt simpel durch Aneinandereihung:

c = a b     # Verknüpfung ohne Seperator
c = a";"b   # Verknüpfung mit Semikolon als Seperator
awk -F'|' 'NR>1 {if (a[$3] < $6) a[$3] = $2"->"$6;} END {for (i in a) {print i "->" a[i];}}' standorte.csv
Produktio + Vertrieb->Dresden->700000
Verwaltung + Vertrieb->Muenchen->2300000
Nur Vertrieb->Stuttgart->800000

Ein letztes Beispiel möchte keine einzelnen Zeilen des Datensatzes auflisten und auch keine Gruppierung unterscheiden, sondern die Zusammenfassung über die Angabe der gesamten Mitarbeiteranzahl und der Gewinn-Summe über alle Standorte angeben.

awk -F'|' 'NR>1{x+=$4;y+=$5-$6} END {print "Summe Mitarbeiter: " x " Summe Gewinn: " y}' standorte.csv
Summe Mitarbeiter: 577 Summe Gewinn: 9292000

Fazit

Als Programmiersprache ist awk sicherlich nur ein nice-to-have, aber wenn man das Prinzip dieser Sprache erstmal verstanden hat, kann sie ein interessantes Tool darstellen, um schon auf Kommandozeilenebene sich schnell einen Überblick über Datenbestände zu beschaffen und auch um Datenqualitätstests durchzuführen.