Tutorials auf die einfachste Weise

Los gehts: Tutorials, die nicht in die Kurztipp-Serie passen, weil sie zu groß, zu lang, zu wichtig sind oder ein besonderes Feature enthalten haben nun ebenfalls eine eigene Kategorie-Grafik. Diesmal geht es um das Einlesen beliebiger, durch ein Komma getrennter Daten aus sogenannten csv-Dateien (comma-seperated-values) in Python mit Hilfe der regulären Ausdrücke (regular expressions).

Idee und Intention

Wie aus der Seitenleiste des Blogs entnommen werden kann, lese ich regelmäßig die Artikel des walkingrandomly.com Blogs von Mike Croucher. Am 21. Mai 2010 schrieb Mike ein Matlab Tutorial, dessen Inhalt es war, wie csv-Dateien in Matlab eingelesen werden können. Im Rahmen dieses Artikels ging er auf verschiedene Möglichkeiten ein, eine csv-Datei zu importieren.

Begonnen hat er mit einer einfachen zu importierenden Datei, worin die Daten ohne Ausnahmen durch eine Komma getrennt waren. Richtig interessant wurde es beim Import von Daten aus einer unordentlich definierten csv-Datei. Das Beispiel stammte von einem Leser seines Blogs und soll hier ebenfalls verwendet werden.

Der Datensatz

Es lässt sich unendlich lange darüber unterhalten, wie sinnvoll oder sinnlos es ist, innerhalb einer Datei mit csv-Daten weitere Daten zu integrieren, welche nicht durch Kommas getrennt werden und eventuell andere Datentypen sind. Abgesehen davon ist das Beispiel, welches auch Mike verwendet folgendes:

header 1;
header 2;
1031,-948,-76, ,"12"
507,635,-1148, ,"34"
-1031,948,750, ,"45"
-507,-635,114, ,"67"

Da das Ziel beim Import der Daten ist, mit ihnen weiter zu rechnen oder zu plotten, ergeben sich für mich zwei Herausforderungen, bei dem gegebenen Datensatz:

  1. Eine straight-forward Lösung bricht sich bereits an der ersten Zeile das Bein, an der ersten Kopfzeile. Also müssen eine vorgegebene Anzahl an Zeilen beim Importieren ignoriert werden können.
  2. Da die letzte Spalte nicht nur durch ein Komma getrennt ist, sondern in Anführungszeichen steht, soll angenommen werden, dass es reiner Text ist und soll beim Import ignoriert werden.

Damit wären die Stolpersteine lokalisiert und können aus dem Weg geräumt werden.

Reguläre Ausdrücke (regular expressions, kurz: regex)

Es gibt mehrere Möglichkeiten das beschriebene Problem anzugehen und die regulären Ausdrücke stellen in meinen Augen nicht die schlechteste dar. Zum einen wird aus den zwei Herausforderungen nur noch eine und zum anderen ist die Lösung sehr schnell auf dem Tisch und sieht elegant aus. Sollte sich die Struktur der csv-Daten noch ändern, stellt es für die Lösung mit regulären Ausdrücken kein großes Problem dar. Es muss lediglich eine Zeile angepasst werden.

Reguläre Ausrücke habe ich persönlich bisher sehr sporadisch gebraucht, würde aber dennoch folgendes Vorgehen vorschlagen:

  1. Klären der Struktur der Daten
  2. Erkennen eines Musters
  3. Definition des Musters in regulären Ausdrücken
  4. Implementierung des Rahmenprogramms: öffnen einer Datei, ausgeben der Ergebnisse …

Die Struktur des Datensatzes haben wir schon geklärt: es ist ein Datensatz, dessen Elemente durch Kommata getrennt sind. Die zwei Kopfdateien interessieren uns hierbei nicht, die sollen nicht importiert werden.

Das Muster ergibt sich, indem logische Bereiche von einer Zeile betrachtet werden:
-1031,948,750, ,"45"
Zum Einen ist zu bemerken, dass alle Zahlen ganze Zahlen sind. Zudem ist ein Minus auch erlaubt. Also ergibt sich aus der Überlegung folgendes Muster: drei aufeinander folgenden Integer-Zahlen, die durch jeweils ein Komma getrennt sind und evt. ein Minus vor der Integer-Zahl.

Daraus lässt sich nun der folgende einfache reguläre Ausdruck ableiten:
(-?[0-9]*),(-?[0-9]*),(-?[0-9]*),.*
Es sei angemerkt, dass auch an dieser Stelle viele Wege nach Rom führen. Drei Programmierern fallen bestimmt spontan neun verschiedene reguläre Ausdrücke ein, die alle die korrekte Lösung liefern.

Reguläre Ausdrücke in Python

Der glückliche und aktive Linux Benutzer kann reguläre Ausdrücke in sehr vielen Programmen und Tools anwenden. Als Beispiele seien nur die Tools grep und awk genannt.

Ich möchte nicht behaupten, dass die meisten Bücher, jedoch sehr viele erklären reguläre Ausdrücke in Kombination mit der Skriptsprache Perl. In Python lassen sich die regulären Ausdrücke doch ebenso leicht verwenden. Dazu muss lediglich das Modul re importiert werden:
import re Damit sind wir mit einer mächtigen Waffe ausgestattet.

Import der csv-Daten und Ausgabe der Listen (Arrays)

Gehen wir es an und importieren den Datensatz mit den unordentlich formatierten Daten. Dazu schlage ich folgendes kleines Python-Skript vor:

import re

# define a pattern for matching the needed data

fileO = open('walking_example.dat','r')
search_data = fileO.readlines()

fileO.seek(0,0)

print("Following data will be parsed:\n")
print(fileO.read())

fileO.close()

pattern = '(-?[0-9]*),(-?[0-9]*),(-?[0-9]*),.*'
r = []; s = []; t = [];

for line in search_data:
    found = re.search(pattern, line)
    if found:
        r.append(float(found.group(1)))
        s.append(float(found.group(2)))
        t.append(float(found.group(3)))

print("\nThe parsed lists:\n")
print(r,s,t)

Schrittweise erklärt:

1.Zeile: Zu Beginn wird das Modul zur Verwendung regulärer Ausdrücke importiert.

5.Zeile: Es wird ein file-Objekt angelegt, welches die Datei mit dem Datensatz enthält. Die Funktion open erhält im ersten Übergabeparameter die Datei inklusive dem Pfad und im zweiten die Option ‘r’, was bedeutet, dass nur gelesen werden soll. Da in meinem Fall die csv-Datei sich im selben Ordner befindet, wie das Python-Skript, muss lediglich der Dateiname angegeben werden.

6.Zeile: Um die Daten aus dem file-Objekt zu verwenden, werden sie zeilenweise eingelesen und in der Variablen search_data gespeichert.

7-11.Zeile: Dieser Abschnitt ist optional. Zunächst wird die Position im file-Objekt wieder auf den Ausgangszustand zurückgesetzt, damit der Datensatz erneut mittels read() gelesen und dadurch einfach mit print() ausgegeben werden kann. Dies alles dient nur der leichten Kontrolle des regulären Ausdrucks.

13.Zeile: Das file-Objekt wird geschlossen.

15.Zeile: Der bereits vorgestellte reguläre Ausdruck wird in der Variable pattern gespeichert.

16.Zeile: Initialisierung leerer Listen für die drei Spalten aus den Datensatz.

19.Zeile: Anwendung des regulären Ausdrucks für jede Zeile aus search_data, was mit Hilfe der for-Schleife aus Zeile 18 bewerkstelligt wird. Hierzu wird die Funktion search aus dem re-Modul verwendet. search erhält als erstes Argument das Muster und als zweites die zu untersuchende Zeile des Datensatzes.

21-23.Zeile: Wenn re.search etwas gefunden hat, dann werden die Teile, welche im regulären Ausdruck in runden Klammern stehen in die drei Listen r,s,t geschrieben. Vor dem eigentlichen Anhängen an die Liste mittels append wird der Inhalt in float-Zahlen umgewandelt.

26.Zeile: Zu guter Letzt werden die Listen zur Kontrolle ausgegeben.

Ergebnis und Kontrolle

Die Arbeit ist getan, jetzt gilt es nur noch die Lorbeeren zu pflücken.

Dazu schauen wir uns an, was das Skript in der Shell ausgibt:
Following data will be parsed:
header 1;
header 2;
1031,-948,-76, ,"12"
507,635,-1148, ,"34"
-1031,948,750, ,"45"
-507,-635,114, ,"67"
The parsed lists:
([1031.0, 507.0, -1031.0, -507.0], [-948.0, 635.0, 948.0, -635.0], [-76.0, -1148.0, 750.0, 114.0])

Es ist ganz leicht zu erkennen, dass das kleine Programm für diesen Fall richtig funktioniert hat.

Schlusswort

Reguläre Ausdrücke sind ein mächtiges Instrument, welches jedoch aufgrund der etwas undurchsichtigen Syntax oft als zu umständlich abgelehnt werden. Aus meiner Sicht lohnt sich eine kurze Einarbeitung in die Syntax, wenn öfter mal bestimmte Bereiche eines Datensatzes extrahiert werden müssen, was bei mir der Fall ist.

An dieser Stelle ein Dankeschön an Mike vom walkingrandomly.com-Blog für die Steilvorlage.

Weiterhin würde mich interessieren, wie meine Leser an die Sache herangegangen wären und welche Erfahrungen sie gemacht haben in Bezug auf reguläre Ausdrücke.

Scribtee - Designer T-Shirts

Artikel aus der selben Kategorie:

Bisher nur ein Kommentar zu “Python Tutorial: Lesen von csv-Daten”

  1. 1. muvik-multigrid » Python Tutorial: Lesen von csv-Daten 2 » Von Muvik » Tutorial, Python, InputOutput, Programmierung, Mathematik, Pyhton

    Pingback vom 8 Juni 2010 um 10:12

    [...] gehts: Sehr schnell nach der Veröffentlichen des ersten Tutorials zum Lesen von csv-Daten mit Python, kamen von zwei Identi.ca-Teilnehmern der Hinweis auf das Python csv-Modul. Darauf möchte ich [...]


Meinungsfreiheit für alle!