Hantieren mit großen Listen

Du hast eine Idee für ein Projekt?
xPae
User
Beiträge: 12
Registriert: Freitag 23. März 2012, 10:56

Liebes Forum,

ich erstelle hiermit mein erstes Thema und wollte mich und meine Unwissenheit kurz vorstellen. Ich komme aus der Biotechnologie und arbeite mit Massenspektrometern. Dabei werden riesige txt Files erzeugt. Ich lerne seit kurzem Python, um diese Listen zu bearbeiten. Die Liste besteht aus ca 20 durch einen Tab getrennte Spalten und 300.000 Zeilen. Das Skript soll zunächst diese Liste nach einer Spalte sortieren bzw dem Wert darin sortieren und dann ab einem bestimmten Cutoff diese Löschen.

Dies bezügliche Suche ich nach Tutorials die explizit auf das bearbeiten mit großen Listen eingeht. Oder vllt ist hier jmd der etwas ähnliches schon einmal gemacht hat. Hauptproblem ist für mich, dass ich das Einlesen der Datei nach Spalten und zum Beispiel das Löschen ganzer Spalten bisher nicht bewerkstelligen kann.

Für eine Hilfe wäre ich Euch sehr dankbar.

Viele Grüße

xPae
deets

Dazu gibt es sicher verschiedene Ansaetze, insbesondere Abhaengig davon, wie gross die tatsaechlichen Daten sind (also, welche Typen die Spalten haben).

Wenn es sich um uniforme, numerische Werte handelt, ist sicherlich Numpy fuer dich interessant - damit liest du grosse Matrizen ein, und arbeitest mit denen.

Aber auch wenn du in reinem Python arbeitest, sollten die Daten ein paar 100MB bis 1-2 GB eigentlich nicht ueberschreiten - und damit auf einem hinreichend grossen system auch in-memory verarbeitbar.

Ausserdem ist die Frage, ob du immer wieder auf denselben Daten arbeitest, und diese dann nach unterschiedlichen Kriterien filterst. Dann waere uU eine Datenbank noch von Interesse.

Und last but not least: wenn du von "nicht bewerkstelligen" redest - hast du konkret etwas versucht, und was genau geht nicht - skalierst du nicht, oder geht schon die grundlegene Operation nicht?
xPae
User
Beiträge: 12
Registriert: Freitag 23. März 2012, 10:56

Danke für die schnelle Antwort!
Das sind txt Datein, eine Größe von 2 GB wird nicht überschritten. Es handelt sich um Messdaten und Berechnung von einem Programm. Um das besser zu beschrieben will ich das kurz Skizzieren:
Nr ... Code ... Score + viele weitere Spalten (Die gewünschten Spalten liegenNICHT nebeneinander)
1 NNZTI 50
2 UVHF 120
3 HUZ 20

Was das Skript zunächst tun sollte ist folgendes:
Alle Spalten rauslöschen bis auf diese drei und nach dem Score sortieren, dann ab einem gewissen Cutoff, den man festlegen kann, die anderen Werte löschen und dann diese (nach weitere bearbeiten, die jedoch erst später folgt) in einer neuen txt Datei abspeichern.
Biologisch geht es darum Sequenzen von Peptiden mit entsprechendem Score herauszufiltern. und diese bearbeiten (da hab ich sicher später auch noch eine Frage : ) )

Mit bewerkstelligen meinte ich, dass ich zur Zeit viele Tutorial + Buch (Bioinformatics Programming in Pythn Rüdiger marcus Flaig) lese , jedoch mit dem bisherigen Wissen das nicht kann! Bitte entschuldige die unglückliche Formulierung.

Viele Grüße
deets

na dann wuerde ich vorschlagen, dass du erstmal eine testdatei erzeugst, mit nur einigen wenigen zeilen. und dich daran dann abarbeitest. was du probierst, kannst du hier vorstellen, und dann schauen wir uns das an.

ist fuer den cutoff denn die sortierung notwendig, oder kann die spaeter erfolgen? denn das sollte ja die datenmenge deutlich reduzieren, wenn man schon beim ersten einlesen alles wegwerfen kann, was man nicht mehr betrachten braucht.
BlackJack

@xPae: Es kann sein, dass es bessere Methoden/Bibliotheken speziell für bioinformatische Problemstellungen gibt, aber rein mit den Grundoperationen und was die Standardbibliothek an allgemeinen Werkzeugen zum manipulieren der Grunddatentypen und -strukturen bietet, sollte man die beschriebene Aufgabe schon lösen können. Zumindest solange es nicht den Arbeitsspeicher sprengt. Also müsste auch das allgemeine Tutorial aus der Python-Dokumentation schon als Einstieg verwendbar sein.

Wenn es Probleme mit dem Arbeitsspeicher gibt, und die in der Regel kompaktere interne Repräsentation von `numpy`-Arrays nicht ausreicht, dann sollte man über eine Datenbank nachdenken.

Wenn Du vom löschen von Spalten redest, solltest Du das nicht wirklich so umsetzen, dass Du *alles* in eine verschachtelte Liste einliest und *dann* Spalteninhalte löschst, sondern gleich beim Einlesen schon aus jeder Zeile nur die gewünschten Werte auswählst.

Ein paar Stichpunkte: Aufteilen einer Zeile in die verschiedenen Spaltenwerte kann man mit Zeichenkettenoperationen selber machen, oder man kann das `csv`-Modul verwenden. Bei der Auswahl der Spaltenwerte könnte eventuell `itemgetter()` aus dem `operator`-Modul nützlich sein. Für das Sortieren ist es wichtig, dass man die Daten in den Spalten in den entsprechenden Typen umwandelt. Um nach einer Spalte zu sortieren ist ganz sicher die `itemgetter()`-Funktion nützlich/wichtig. Und der „Cutoff“ klingt nach „slicing” — also eine Grundoperation auf Listen.
xPae
User
Beiträge: 12
Registriert: Freitag 23. März 2012, 10:56

Gute Abend,

ich habe mich jetzt nochmal dran gesetzt und das versucht umzusetzen, was ich heute in Tuts. oder books gelesen habe.
Meine Ausgangsdatei trägt den namen MSMS.txt, ich habe eine 10x5 matrix zur Überprüfung des Skripts verwendet und dann Spalte 1 und 3 extrahiert.
Mit dieser datenbank funktioniert dieses Skript bisher. Jedochhabe ich die befürchtung durch nr_lines=len(dat) für zu große Datein zuviel Zeit zu verschwenden. Außerdem habe ich hier nicht die Auswahl getroffen beim Einlesen, ob der Score größer eines Cutoffs ist. Ich wollte euch zunächst befragen, ob es sich lohnt damit weiter zu arbeiten.
itemgetter operator habe ich bisher nur überflogen =/ da ich die antwort gerade erst gesehen habe. Ist das csv Modul denn überhaupt für txt Datein anwendbar ? ( sry für hoffnetlich nicht alzu bescheuerte Fragen : ) 2 tage python bisher :D )

Code: Alles auswählen

import string
def read_file(filename):
    separator=" "
    file_tab=file(filename,"r")
    line=file_tab.readline()
    line=file_tab.readline()
    data=[]
    while line!="": 
            row=[]
            for v in string.split(line[:-1],separator):
                if string.strip(v)!="":
                    row.append(string.strip(v))
            data.append(row) 
            line=file_tab.readline()
    file_tab.close()
    return data
dat=read_file("c:/Python/MSMS.txt")
nr_lines=len(dat)
for i in range(nr_lines):
    dat[i][1]=float(dat[i][1])
    dat[i][3]=float(dat[i][3])
dat.sort(lambda x,y: cmp(x[1],y[1]))
file_out=file("c:/Python/DNSequenz.txt","w")
file_out.write("%-10s %-10s\n" % ("Sequenz","DN Score"))
for i in range(nr_lines):
    file_out.write("%10.3e %10.3e\n" % (dat[i][1],dat[i][3]))
file_out.close()
Die darauffolgenden Schritte wären die Analyse von Sequezen, da es zum Beispiel dazukommt, dass eine Ausgabe folgt: NWRG{I/L}U , daraus muss ich anschließend:
1. NWRGLU
und
2. NWRGIU

heraussuchen. Das habe ich gerade mit s.replace("{I/L},"I") gemacht, allerdings weiss ich nicht genau wie man die zwite implementiert, muss man dann den string erst kopieren? Weiterhin muss zu jeder Sequenz dann der vorherige Score zugeordnet werden! Es wird sicher eine elegantere Lösung geben!


Herzlichen Dank

xPae
BlackJack

@xPae: Der Quelltext könnte ein paar Leerzeilen vertragen um die Lesbarkeit zu erhöhen.

Funktionen aus dem `string`-Modul, die es auch als Methoden auf Zeichenketten gibt, sollte man nicht mehr verwenden. Die sind in Python 3.x dann auch endlich rausgeflogen.

Statt `readline()` würde ich `next()` verwenden, das ist universeller weil es mit allen Iteratoren funktioniert und nicht nur mit Dateien. Des weiteren sind Dateiobjekte iterierbar, man muss da also keine ``while``-Schleife mit `readline()`-Aufrufen verwenden, sondern kann eine einfachere ``for``-Schleife direkt über die Zeilen schreiben.

Dateien sollte man mit `open()` öffnen und das am besten im Zusammenhang mit der ``with``-Anweisung, damit man das `close()` nicht schreiben muss und die Datei auch in jedem Fall wieder geschlossen wird.

Du sprichst von Tab-separierten Daten, verwendest aber Leerzeichen zum Splitten!? Und Du solltest vielleicht mal ausprobieren was mit Deinen Daten passiert wenn Du den Separator beim `split()`-Aufruf weg lässt. Dann wird Dein Programm einfacher.

Du solltest das Auswählen und Umwandeln schon beim Einlesen machen. Das spart Speicherplatz und Du brauchst nicht so eine „unpythonische” Index-Schleife. Die solltest Du wenn überhaupt mit `enumerate()` oder zumindest `xrange()` schreiben.

Das `cmp`-Argument von `sort()` ist ineffizienter als das `key`-Argument. Das `cmp`-Argument ist in Python 3.x nicht mehr vorhanden.

In der Schleife zu schreiben brauchst Du das `i` wirklich überhaupt nicht. Da kann man problemlos direkt über die Elemente von `dat` iterieren.
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@xPae: Ich bin mir nicht ganz sicher, ob ich deinen Code verstanden habe, aber falls doch ist hier die pythonische Variante (ungetestet):

Code: Alles auswählen

import csv

source = 'c:/Python/MSMS.txt'
target = 'c:/Python/DNSequenz.txt'

with file(source,'r') as file_in:
    next(file_in) # Header-Zeile loswerden
    data_in = csv.reader(file_in, delimiter=' ', skipinitialspace=True)
    data_out = ((float(fields[1]), float(fields[3])) for fields in data_in)
    with file(target,'w') as file_out:
        file_out.write('%-10s %-10s\n' % ('Sequenz','DN Score')) # Header-Zeile
        file_out.writelines('%10.3e %10.3e\n' % each for each in sorted(data_out))
Take a step back, and above all, stop writing so much code.

Übrigens: wenn in den Tutorials, auf die du dich beziehst, steht, dass man über xrange(len(blah)) iterieren und mittels blah auf Listen-Elemente zugreifen soll, dann verbrenn diese Tutorials bitte umgehend. Gegebenenfalls musst du sie vorher noch ausdrucken. Da du den cmp-Parametert von list.sort() verwendest, vermute ich, dass das nicht so einfach sein wird, denn zu der Zeit, als der noch verwendet wurde, gab es noch kein Papier, sondern es wurde alles in Steinplatten geritzt. Heutzutage verwendet man den key-Parameter. Oder man bereitet seine Daten vorher schon so auf, dass sie gleich ganz ohne key-Parameter sortiert werden können, wie ich das im Code oben getan habe.
Zuletzt geändert von pillmuncher am Samstag 24. März 2012, 13:44, insgesamt 2-mal geändert.
In specifications, Murphy's Law supersedes Ohm's.
xPae
User
Beiträge: 12
Registriert: Freitag 23. März 2012, 10:56

Hi,

ich habe Dein Skrip (vielen dank dafür) mal ausprobiert. ich musste statt file open schreiben und bei der letzten Zeile gbit er als Fehler IndexError: list index out of range. aus. Die header Zele gibt er jedoch korrekt aus. Wird das eher ein Fehler beim Lesen der Datei sein, so dass er dann nicht ausschreiben kann? Eine Frage hätte ich noch, die ich mir bisher net selbst beantworten konnte. wenn ich den csv.reader verwende, wie wäre dann das trennzeichen für einen tab? :)

Wenn ich mit den eingelesenen daten aus der source datei etwas machen möchte, zum Beipsiel, wie oben beschrieben, die Sequenz ändern, kann ich das dann einfach vor
"with file(targedt,"w") schreiben oder auch bei writelines implementieren?

Danke!!!!
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

xPae hat geschrieben:Eine Frage hätte ich noch, die ich mir bisher net selbst beantworten konnte. wenn ich den csv.reader verwende, wie wäre dann das trennzeichen für einen tab? :)
'\t' im Sourcecode wird intern in das passende Zeichen umgewandelt.
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@xPae: Ja, du hast Recht, es muss natürlich open() statt file() heißen:

Code: Alles auswählen

import csv

source = 'c:/Python/MSMS.txt'
target = 'c:/Python/DNSequenz.txt'

with open(source,'r') as file_in:
    next(file_in) # Header-Zeile loswerden
    data_in = csv.reader(file_in, delimiter='\t')  #  <-- jetzt sollten die Felder tab-getrennt sein
    data_out = ((float(fields[1]), float(fields[3])) for fields in data_in)
    with open(target,'w') as file_out:
        file_out.write('%-10s %-10s\n' % ('Sequenz','DN Score')) # Header-Zeile
        file_out.writelines('%10.3e %10.3e\n' % each for each in sorted(data_out))
Deine Frage bzgl. Tabs sollte damit auch beantwortet sein und der IndexError nicht mehr vorkommen.

Deine Frage andere Frage verstehe ich nicht ganz. Meinst du mit Sequenz die Reihenfolge?

Bei 300.000 Zeilen sollte man vernünftigerweise versuchen, nicht mehrere Kopien davon gleichzeitig im Speicher zu halten. Deshalb habe ich durchgehend Generator Expressions verwendet, erst sort() erzeugt eine Liste. Vorher ist immer nur ein Datensatz / eine Zeile gleichzeitig im Speicher, und sobald diese verarbeitet ist, kann der Garbage Collector sie wegschmeißen und den Speicher weder freigeben. csv.reader() arbeitet genauso. Je nachdem, was du unter "mit den eingelesenen daten aus der source datei etwas machen" verstehst, wirst du aber zwischendurch evtl. eine Liste erzeugen müssen. Am besten wäre es natürlich, wenn du es ebenfalls in einer Generator Expression unterbringen könntest. Das ginge, sofern es sich bloß um 1:1 Transformationen der Datensätze / Zeilen handelt. An welcher Stelle du das im Code unterbringst ist letztlich egal. Vermutlich würde ich es aber vor das with open(target, 'w'): stlellen, weil die Zeile mit file_out.writelines(...) eh schon zu lang ist, und es nicht übersichtlicher wird, wenn man alles in eine einzige Zeile quetscht.
In specifications, Murphy's Law supersedes Ohm's.
xPae
User
Beiträge: 12
Registriert: Freitag 23. März 2012, 10:56

Hallo,

tut mir leid, dass ich mich jetzt erst wieder melde, aber ich war ein bisschen im Urlaub! ;)

Das Einlesen der Datei funktioniert nun. Vielen Dank.

Ich werde nun meine Frage etwas genauer erklären. In den eingelesenen Sequenzen kommet sehr oft {I/L} vor. Zum Beispiel VNG{I/L}HG. Daraus müssen nun zwei Sequenzen gebildet werden, die den gleichen Score bekommen (zweite Spalte). Bsp: VNG{I/L}HG - 114 dann sollen daraaus: VNGIHG - 114 und VNGLHG - 114 entstehen (Wenn geschweifte Klammern angegeben sind, sind in diesen immer I/L enthalten). Weiterhin kommen in diesesn Sequenzen auch eckige Klammern vor zum Beispiel VNG[UG]HG - 115. (Hier können unterschiedliche Buchstaben stehen (Insgesamt zwanzig unterschiedliche)) Daraus müsste VNGUGHG - 115 und VNGGUHG - 115 erfolgen. (Deutlich: die reihenfolge ist nicht eindeutig)

P.S. Wie kann ich denn am besten einen cutoff für fields[3] in den code von pillmuncher einbauen?

Gruß
BlackJack

@xPae: Das mit dem Vervielfältigen würde ich gleich beim Einlesen erledigen. Dazu solltest Du Dir mal das Konzept von Iteratoren/Generatoren anschauen und das Einlesen nicht gleich in eine Liste erledigen, sondern „lazy” die einzelnen Elemente als Datenstrom generieren. Darauf kann man dann verschiedene Filter, oder eben auch Generatoren, die aus einem Element mehrere machen, aufsetzen.

Wie ist denn der Cutoff definiert? Kann man da schon vor dem sortieren Elemente anhand des Wertes in Spalte 4 rauswerfen, oder muss man dafür alle kennen?
xPae
User
Beiträge: 12
Registriert: Freitag 23. März 2012, 10:56

alles klar, das werde ich die Woche mal versuchen nebenbei zu erledigen.

Der Cutoff sollte vom user eingegeben werden und kann aus dem wert aus spalte 4 entnommen werden (bsp. 100 oder ähnliches)
Lieben Gruß
BlackJack

@xPae: Um das mit dem Cutoff noch einmal zu präzisieren: Der Benutzer gibt Beispielsweise 100 ein und dann werden alle Zeilen in denen in Spalte vier ein Wert grösser oder gleich 100 steht, nicht in das Ergebnis geschrieben? *Dann* sollte man diese Filterung relativ früh in der Verarbeitungskette machen, denn diese Zeilen braucht man dann auch nicht unnötig im Speicher halten und sortieren.
xPae
User
Beiträge: 12
Registriert: Freitag 23. März 2012, 10:56

Moin Moin,

endlich habe ich neben anderen Sachen mal wieder Zeit für Python gefunden. Ich erhalten beim Ausführen des Programms von pillmuncher leider immer noch Fehlermeldungen:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:/Users/Hendrik/Desktop/DN", line 12, in <module>
    file_out.writelines('10.3e % 10.3e\n' % each for each in sorted(data_out))
  File "C:/Users/Hendrik/Desktop/DN", line 9, in <genexpr>
    data_out=((float(fields[1]), float(fields[3])) for fields in data_in)
IndexError: list index out of range
Ich habe es mit einer kleinen Textdatei, getrennt durch Tab mit 3 Zeilen einfachen Zaheln ausprobiert. Wenn ich die Zeitel

Code: Alles auswählen

file_out.writelines('10.3e % 10.3e\n' % each for each in sorted(data_out))
lösche, geht das Prog und er schreibt mir die Überschriften in die Ausgabedatei.
Mache ich hier einen Fehler oder ist an dem Code noch etwas falsch? (keine Kritik an pillmuncheer natürlich, danke dir dafür nochmal)
Allerdings müsste doch der Befehl sorted() noch für die jeweile Spalte spezifiziert werden, oder nicht?
Für das weitere Vorgehen:



@BlackJack: Ja das mit dem Cutoff hast du richtig beschrieben. Alle Scores unter diesem Wert können wegfallen.
Entsprechend müsste ich jetzt Generatoren/Iteratoren schreiben für:

a) überspringen, falls in Spalte x ein Wert kleiner zB 100 steht (score filterung)
b) Falls in der anderen Spalte {I/L} vorkommt, die Sequenz und Score dublizieren und jeweils eine Sequenz mit I und eine L mit gleichem Score schreiben.

Ich hoffe das bekomme ich hin, werde bis Ende der Woche hoffentlich meine Vorschläge fertig haben.. gibts iwelche Beispielprogramme/Tuts die dafür zu empfehlen sind?

Beste Grüße

xpae
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

xPae hat geschrieben:Ich erhalten beim Ausführen des Programms von pillmuncher leider immer noch Fehlermeldungen:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:/Users/Hendrik/Desktop/DN", line 12, in <module>
    file_out.writelines('10.3e % 10.3e\n' % each for each in sorted(data_out))
  File "C:/Users/Hendrik/Desktop/DN", line 9, in <genexpr>
    data_out=((float(fields[1]), float(fields[3])) for fields in data_in)
IndexError: list index out of range
Ich habe es mit einer kleinen Textdatei, getrennt durch Tab mit 3 Zeilen einfachen Zaheln ausprobiert.
Dieser Fehler deutet darauf hin, dass deine Ausgangsdatei nicht so aufgebaut ist, wie du glaubst. Vielleicht stehen in deiner Datei wider Erwarten doch Spaces statt Tabs, oder es gibt in irgendeiner (oder allen) Zeilen weniger als vier Spalten, oder es gibt Leerzeilen, oder sonst was in dieser Richtung. Man könnte das Programm diesbezgl. etwas fehlertoleranter machen, zB. indem man statt des csv-Moduls das re-Modul verwendet. Vielleicht genügt es auch schon, Leerzeilen herauszufiltern. Besser wäre es aber IMO, wenn du dafür sorgen könntest, dass deine Ausgangsdatei in Ordnung gebracht wird.
In specifications, Murphy's Law supersedes Ohm's.
xPae
User
Beiträge: 12
Registriert: Freitag 23. März 2012, 10:56

Das dachte ich auch, jetzt habe ich eine Beispiel datei mit Excel erstellt und dann " save as txt.data (tab delimited) " verwendet. Demnach müsste das mit dieser aufejdenfall funktionieren, allerdings wird dieser Fehler ausgegeben (not all arguments converted during string formatting) :/ :


Code: Alles auswählen

Traceback (most recent call last):
  File "C:/Users/Hendrik/Desktop/DN", line 12, in <module>
    file_out.writelines('10.3e % 10.3e\n' % each for each in sorted(data_out))
  File "C:/Users/Hendrik/Desktop/DN", line 12, in <genexpr>
    file_out.writelines('10.3e % 10.3e\n' % each for each in sorted(data_out))
TypeError: not all arguments converted during string formatting
Data:

Code: Alles auswählen

asdg	asds	asda	asdgg	asdhh	asdhh
1	8	67	56	65	67
23	7	90	7	76	76
45	6	56	6	67	67
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Noch genauer könnte die Fehlermeldung wohl kaum sein. Dort steht doch ganz genau was nicht funktioniert und im Traceback wird dir sogar noch die Zeile angezeigt. Probiere doch einfach mal per Hand eine einzelne Zeile zu formatieren.
Das Leben ist wie ein Tennisball.
xPae
User
Beiträge: 12
Registriert: Freitag 23. März 2012, 10:56

Kaum erstelle ich die Frage fiel es mir natürlich auch auf ('%-10s %-10s\n'), entschuldigt die "blöde" Frage.

Wunderbar! Eine Frage hätteich jedoch noch bevor die richtige Arbeit losgeht (nach der schon allzuschweren geburt - nochmals sry für anfänger fehler)

Wenn ich jetzt zB ein "NaN" in einer Zeile der gewünschte Spalte hätte würde das Programm ja abschmieren, kann man die floats() Funktion durch etwas generelleres Ersetzten? Oder zB nur für Buchstaben? : /
Antworten