Seite 1 von 2
Datenauswertung mittels Python
Verfasst: Dienstag 25. Februar 2014, 11:11
von Larusso
Hallo liebe Pythone,
ich stehe for folgendem Problem. Aus einer Datenbank lese ich mittels eines Skripts gefilterte Daten als .csv aus, welche ich nun mittels Python weiter aufbereiten möchte. Leider bin ich kein versierter Programmierer, daher mein Post.
Die .csv besteht aus ca 500000 Zeilen, mit einer Headerzeile für die Spaltenbezeichnungen (zwischen 5 bis 12 Spalten) und den dann folgenden Werten. Nun möchte ich allerdings die meisten Zeilen löschen, welche an irgendeiner Stelle innerhalb jeder Zeile den Wert "nan" stehen hat.
Die übrig bleibenden Zeilen sollen dann nach 2 Merkmalen (also 2 der Spalten) eine Matrix aufspannen und die Häufigkeiten über diese 2 Merkmale zählen (eine Art Zählschleife für jede mögliche Kombination der 2 Merkmale)
Bisher bin leider nur soweit gekommen, dass ich die Datei eingelesen habe mit:
Code: Alles auswählen
f = open("datei.csv")
zeilen = f.readlines()
f.close()
Ich scheitere bereits daran eine passende Schleife zu bauen um die entsprechenden Zeilen in Abhängigkeit ihrer Werte zu löschen.
Ich vermute es müsste irgendwie so aussehen:
Code: Alles auswählen
for zeile in zeilen:
for index in zeile:
if index == "nan":
del zeilen[zeile]
else:
continue
Für eure Hilfe bin ich euch sehr dankbar!
Viele Grüße
Larusso
Re: Datenauswertung mittels Python
Verfasst: Dienstag 25. Februar 2014, 11:37
von snafu
Nutze hierzu besser das in Python enthaltene `csv`-Modul. Details zur Nutzung kannst du in dessen
Doku nachlesen. Das Pattern für den einfachen Fall lautet:
Code: Alles auswählen
import csv
with open('datei.csv') as csv_file:
reader = csv.reader(csv_file)
for row in reader:
do_something(row)
`row` ist hier eine Liste mit Elementen für die jeweilige Zeile.
Zum Testen bietet sich übrigens
islice() an, weil man damit prima nur ein paar Datensätze zum Angucken rausfischen kann. Das oben genannte Pattern sähe gekürzt dann zum Beispiel so aus:
Code: Alles auswählen
from itertools import islice
[...]
for row in islice(reader, 5):
print(row) # zeigt nur die ersten 5 Zeilen an
Re: Datenauswertung mittels Python
Verfasst: Dienstag 25. Februar 2014, 11:51
von BlackJack
@Larusso: Schau Dir doch mal an was `zeile` und `index` in Deinen Schleifen für Werte haben. Das sind nicht die, die Du wahrscheinlich denkst, denn der Name `index` passt so gar nicht zu den Werten die daran gebunden werden.
Ein ``continue`` am Ende des Kontrollflusses in einer Schleife hat keinen Effekt — der nächste Schleifendurchlauf würde ja sowieso der nächste Schritt sein. Der ``else``-Zweig ist also überflüssig.
In der Standardbibliothek gibt es ein Modul zum lesen und schreiben von CSV-Dateien: das `csv`-Modul.
Zum generellen Vorgehen: Um mit ``del some_list[index]`` ein Element aus einer Liste zu löschen muss `index` eine Zahl sein. Innerhalb einer Schleife über eine Liste Elemente aus genau dieser Liste zu löschen geht aber sowieso nicht, weil sich durch das löschen der Index aller folgenden Elemente verschiebt, wovon die ``for``-Schleife aber nichts mitbekommt und dadurch Elemente übersprungen werden.
Deshalb erstellt man üblicherweise eine neue Liste wo man nur die Elemente aufnimmt, die man haben möchte. Bei der Gelegenheit kann man dann auch gleich nur die beiden Spalten selektieren die für das Endergebnis wichtig sind. Und man kann das gleich beim Einlesen machen, damit man nicht die Werte von allen 500.000 Zeilen auf einmal im Speicher haben muss, wenn man sowieso einen Grossteil davon gar nicht haben will.
Das zählen der Kombinationen könnte man dann mit einem `collections.Counter` sehr einfach machen.
Und statt mit Listen zu operieren könnte man auch Generatorfunktionen und -Ausdrücke verwenden, beziehungsweise Funktionen aus dem `itertools`-Modul. Dann kann man einlesen und filtern mit konstantem Speicherverbrauch schreiben und nur das erstellen der Kombinationszähler belegt dann Speicher der abhängig von der Anzahl der Kombinationen in den Daten ist.
Re: Datenauswertung mittels Python
Verfasst: Dienstag 25. Februar 2014, 11:51
von snafu
Larusso hat geschrieben:Ich scheitere bereits daran eine passende Schleife zu bauen um die entsprechenden Zeilen in Abhängigkeit ihrer Werte zu löschen.
Ich vermute es müsste irgendwie so aussehen:
Code: Alles auswählen
for zeile in zeilen:
for index in zeile:
if index == "nan":
del zeilen[zeile]
else:
continue
Vergiss am Besten sofort, dass man Listenelemente löschen kann. Das hat aus technischen Gründen meistens eine sauschlechte Performance und führt zu unerwartetem Verhalten, wenn man zudem noch die Liste, aus der man löscht, gerade durchläuft. Der übliche Weg ist, eine neue Liste zu erstellen, die dann nur noch diejenigen Elemente enthält, die man behalten wollte.
Das `continue` ist hier übrigens überflüssig. Der Programmfluss würde an dieser Stelle so oder so mit dem nächsten Iterationsschritt beginnen. `continue` macht nur dann Sinn, wenn man mitten in einem Iterationsschritt die Abarbeitung für das aktuelle Elemente beenden und sofort mit dem nächsten Element weitermachen möchte. Es steht sinngemäß also für ein "brich aktuellen Iterationsschritt ab" und wird relativ selten verwendet.
Re: Datenauswertung mittels Python
Verfasst: Dienstag 25. Februar 2014, 12:14
von Larusso
Danke für eure schnellen Antworten!
Ich werde mich direkt einmal damit beschäftigen und überdenken. Insbesondere danke für den Hinweis, dass der del-Befehl in Kombination mit einer For-Schleife keinen Sinn macht!
MfG
Larusso
Re: Datenauswertung mittels Python
Verfasst: Donnerstag 27. Februar 2014, 15:26
von Larusso
Moin Moin,
bin gerade dabei einige eurer Tipps umzusetzen, sieht wie folgt aus:
Code: Alles auswählen
import csv
rohdatenliste=[]
Ausgabeliste=[]
datei ="complete.csv"
with open(datei) as csv_file:
reader = csv.reader(csv_file)
for row in reader:
rohdatenliste.append(row)
for a in range(len(rohdatenliste)):
if (rohdatenliste[a][5] != "nan") and (rohdatenliste[a][5] < 0.5):
Ausgabeliste.append(rohdatenliste[a])
Mir haut der Index a scheinbar ab, da ich die Fehlermeldung line 13. index out of range erhalte, kann mir aber nicht erklären warum dies so ist, Anregungen?
Danke!
Re: Datenauswertung mittels Python
Verfasst: Donnerstag 27. Februar 2014, 15:41
von BlackJack
@Larusso: `a` kann gar nicht das Problem sein, denn dafür werden ja nur die Werte 0 bis len(rohdatenliste) - 1 verwendet. Dann muss es die 5 sein. Es gibt offenbar Zeilen in der Datei die weniger als 6 Spalten haben.
Die `rohdatenliste` scheint mir überflüssig zu sein. Wäre auch einfacher zu erstellen. In Zeile 8 einfach ``rohdatenliste = list(csv.reader(csv_file))``. Und wenn man die Daten erst einmal komplett einliest, braucht der Rest der Verarbeitung nicht mehr im ``with``-Block stehen.
``for i in range(len(sequence)):`` ist in Python ein „anti pattern”. Man braucht keinen Index um über die Elemente von einem Sequenztyp zu iterieren weil man *direkt* über diese Elemente gehen kann. Also ``for item in sequence:``.
Der konkrete Typ der Datenstruktur sollte nicht im Namen stehen. Wenn man den Typ dann mal ändert, muss man entweder überall den Namen ändern, oder man hat falsche, verwirrende Namen im Programm stehen.
Du vergleichst den gleichen Wert einmal mit einer Zeichenkette und einmal mit einer Zahl, da der Wert eine Zeichenkette ist, macht das vergleichen mit einer Zahl keinen Sinn. Ich würde die Daten in `float`\s wandeln und den ersten Test mit `math.isnan()` machen.
Wie sehen die Daten denn aus? Sind in den Zeilen nur Gleitkommazahlen, oder auch andere Daten?
Edit: Ungetestet:
Code: Alles auswählen
#!/usr/bin/env python
import csv
from math import isnan
def main():
filename = 'complete.csv'
result = list()
with open(filename) as csv_file:
reader = csv.reader(csv_file)
for row in reader:
value = float(row[5])
if not isnan(value) and value < 0.5:
result.append(row)
if __name__ == '__main__':
main()
Re: Datenauswertung mittels Python
Verfasst: Donnerstag 27. Februar 2014, 15:59
von Larusso
Hi Blackjack!
Die Daten sehen folgendermaßen aus:
Entweder: 2010-10-22;23:21:00;nan;nan;nan;nan;nan;nan;nan;nan;nan
Oder: 2010-10-22;23:22:00;11;4.09836;3.85604;0.384319;0.361596;32.6727;3.99455;7.24545;1026.41
Jede Zeile verfügt über die gleiche Anzahl an Spalten (meistens 11), lediglich deren Werte sind unterschiedlich, "nan", Datum, Uhrzeit oder Fließkommazahl.
Das ich 2 Vergleiche auf auf unterschiedliche Datentypen vornehme, war mir auch schon in den Kopf gekommen, danke nochmal für die Klarstellung.
Du vergleichst den gleichen Wert einmal mit einer Zeichenkette und einmal mit einer Zahl, da der Wert eine Zeichenkette ist, macht das vergleichen mit einer Zahl keinen Sinn. Ich würde die Daten in `float`\s wandeln und den ersten Test mit `math.isnan()` machen.
Das mache ich dann am besten so:
?
Was genau meinst du mit den ersten test: math.isnan() ?
Danke im Voraus
Re: Datenauswertung mittels Python
Verfasst: Donnerstag 27. Februar 2014, 16:00
von Larusso
Ok, map geht schonmal nicht. Liegt denke ich an den ersten Einträgen, die Uhrzeit bzw. Datum darstellen. Auf Zeit und Datum würde ich eigentlich nur ungern verzichten, da sie schon ne gewisse Bedeutung für die spätere Auswertung haben
Re: Datenauswertung mittels Python
Verfasst: Donnerstag 27. Februar 2014, 16:03
von Sirius3
@Larusso: map geht nicht, weil Du Listen nicht in Floats umwandeln kannst. Und math.isnan tut genau das was der Name sagt, prüfen ob der Wert "nan" ist.
Re: Datenauswertung mittels Python
Verfasst: Donnerstag 27. Februar 2014, 16:09
von Larusso
Mir is klar, dass es dies abprüfen wird.
Ich bin mir nur nicht ganz sicher an welcher Stelle ich dies tun soll, vorm casten der Liste oder danach. Ich probiere mal weiter rum.
Re: Datenauswertung mittels Python
Verfasst: Donnerstag 27. Februar 2014, 16:10
von BlackJack
@Larusso: `math.isnan()` ist eine Funktion die testet ob eine Gleitkommazahl den Wert „not a number” hat. Genau so wenig wie es Sinn macht zu testen ob eine Zeichenkette kleiner als 0.5 ist, macht es umgekehrt keinen Sinn zu testen ob eine Zahl ungleich der Zeichenkette 'nan' ist, denn das ist sie immer, selbst wenn der Wert der Zahl „not a number” ist.
Das `map()` nicht geht ist ja klar weil sich eine Zeichenkette mit einem Darum nicht mit `float()` in eine Zahl umwandeln lässt. Man könnte aus jeder Zeilenliste eine neue erstellen aus den ersten beiden Elementen und den restlichen Elementen in Zahlen umgewandelt. Je nach dem was man später mit dem Datum und der Uhrzeit anfangen will, würde ich die auch umwandeln und zwar in *ein* `datetime`-Objekt aus dem `datetime`-Modul.
Re: Datenauswertung mittels Python
Verfasst: Donnerstag 27. Februar 2014, 17:02
von Larusso
Ok,
also zum Vorgang konkret:
Ich lese meine Rohdatendatei als Liste ein.
Nun erstelle ich eine weitere Liste die, welche die ersten beiden Spalteneinträge jeder Zeile aufnimmt (in datetime umwandeln) und eine weitere welche die restlichen Zahlenwerte aufnimmt (in float umwandeln).
Fr DTListe:
Code: Alles auswählen
with open(datei) as csv_file:
rohdatenliste = list(csv.reader(csv_file))
for a in rohdatenliste:
for b in range(2):
DTliste.append(rohdatenliste[a][b])
Fehler
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "csvnanfilter.py", line 5, in <module>
DTliste.append(rohdatenliste[a]
)
IndexError: list index out of range
Ärgerlich! Es hakt bei meinen Indizes
Re: Datenauswertung mittels Python
Verfasst: Donnerstag 27. Februar 2014, 17:15
von Larusso
Liegt es evtl. daran, dass jede Zeile als eine Zeichenkette gespeichert wurde und somit keine Index b existiert? Also keine Unterlisten pro Listeneintrag in der Hauptliste
Re: Datenauswertung mittels Python
Verfasst: Donnerstag 27. Februar 2014, 17:17
von Sirius3
Bei "for a in rohdatenliste" enthält "a" schon eine Zeile und nicht den Index auf eine Zeile. Warum machst Du zwei Listen, wenn die Listeneinträge der beiden Listen doch zusammengehören?
Re: Datenauswertung mittels Python
Verfasst: Donnerstag 27. Februar 2014, 17:21
von Larusso
Weil die Einträge in den jeweiligen Zeilenspalten verschiedene Datentypen beeinhalten und ich diese separieren will.
Ich denke meine Überlegung war richtig. Meine "rohdatenliste" enthält als Einträge keine weitere Liste sondern eine Zeichenkette. Dies muss ich ändern um überhaupt Unterindizes zu bekommen,
daher auch mein Index out of range fehler!
Re: Datenauswertung mittels Python
Verfasst: Donnerstag 27. Februar 2014, 17:21
von EyDu
Dann lass dir doch mal "a" (was besser "row") heißen sollte, ausgeben. Dann siehst du schon, warum es nicht funktioniert. Wenn du mittls ``for i in range(...)`` auf Listenelemente zugreifst, dann ist das übrigens sehr unpythonisch. Du kannst direkt über Listen iterieren und solltest nicht den Umweg über Indizes gehen.
Du muss hier auch keinen Umweg über eine unnütze Liste gehen:
Code: Alles auswählen
with open(dateiname) as fp:
for row in csv.reader(fp):
for element in row:
...
Statt "DTliste" solltest du dir auch noch einen besseren Namen einfallen lassen. Zum einen sollten Namen von Variablen mit Kleinbuchstaben anfangen (siehe PEP

, beginnen sie mit einem Großbuchstaben, dann deutet das auf eine Klasse hin. Dann solltest du auch keine Datentypen in Namen einbauen. Unter Umständen ändert sich mal der Typ und dann müsstest du alle Namen ändern. Wähle also einfach den Plural als Namen. Statt "tier_liste" also einfach "tiere". (Sellbiges gilt für "rohdatenliste"). Der einzig aussagekräftige Teil von "DTliste" ist das DT. Leider weiß niemand, nicht einmal du in vier Woche, was "DT" eigentlich bedeutet. Überlege dir also etwas vernünftiges und nicht selbstgemachte Abkürzungen.
Re: Datenauswertung mittels Python
Verfasst: Donnerstag 27. Februar 2014, 17:41
von Balmung
Wie Sirius bereits sagte, das "a" referenziert das Element der Liste, nicht den Index. d.h. a
würde auf das jeweilige subelement zugreifen. (eine bessere Benennung der Variablen wäre angebracht)
Anmerken sollte man vielleicht noch, dass das csv modul standardmäßig das Komma als Trenner nutzt (das C in CSV steht für "Comma" *schock*), du aber das Semikolon.
Um das zu berücksichtigen, füge einfach den delimiter Parameter hinzu:
Re: Datenauswertung mittels Python
Verfasst: Montag 3. März 2014, 12:53
von Larusso
So hallo und schoene neue Woche!
Mein Miniprogramm sieht nun folgendermaßen aus:
Code: Alles auswählen
# -*- coding: utf-8 -*-
import csv
datei = open("rda002_complete.csv", "rb")
Ausgabe = []
rohdaten = csv.reader(datei, delimiter = ";")
for row in rohdaten:
if row[5] != "nan":
Ausgabe.append(row)
datei.close()
neuedatei = open("_rda002_gefiltert.csv","wb")
writer = csv.writer(neuedatei)
writer.writerows(Ausgabe)
Funktioniert soweit, vielen Dank für eure Hilfe! Der eher anspruchsvolle Teil steht mir allerdings noch bevor (kommt noch ^^)
Einige Fragen hätte ich allerdings noch:
-In euren Vorschlägen arbeitet ihr mit with .. as zum öffnen, welchen Grund hat das genau?
- Ist datei.close() bei csv-reader überhaupt erforderlich?
Die Idee mit math.isnan in meiner Bedingung werde ich noch einbauen, hatte nur das Problem, dass die erste Zeile (meine Headerzeile) nicht floatbar ist, da Spaltenname. Funktioniert derzeit auch so wie oben abgebildet. Das wichtigste ist erstmal, dass nur die gültigen Zeilen mit werten ausgegeben werden. Sehr gut!
Re: Datenauswertung mittels Python
Verfasst: Montag 3. März 2014, 12:59
von BlackJack
@Larusso: ``with`` sorgt bei Dateiobjekten dafür das Datei die in jedem Fall geschlossen wird sobald der Kontrollfluss den ``with``-Block verlässt. Und zwar egal auf welche weise das passiert, ob wegen einer Ausnahme, einem ``break`` oder ``continue`` falls der ``with``-Block in einer Schleife steht, oder durch eine ``return``-Anweisung.
Schliessen von Dateien ist wichtig, weil Dateideskriptoren keine unendliche Ressource sind. Die werden vom Betriebssystem üblicherweise begrenzt, darum sollte man sie durch Schliessen der Dateiobjekte wieder freigeben. Und beim Schreiben ist es ausserdem noch wichtig, weil erst nach dem im Schliessen impliziten `flush()`-Aufruf die Daten auch garantiert alle geschrieben werden und nicht noch etwas im Dateipuffer liegen bleibt.