laden von Werten aus binären Dateien schneller machen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
feldmaus
User
Beiträge: 284
Registriert: Donnerstag 12. Oktober 2006, 16:48

CM hat geschrieben:Mögliche Lösungen hat BlackJack bereits beschrieben.
Die Lösung von BlackJack bringt ja diese Fehlermeldung.
Zuletzt geändert von feldmaus am Donnerstag 28. Mai 2009, 11:34, insgesamt 1-mal geändert.
feldmaus
User
Beiträge: 284
Registriert: Donnerstag 12. Oktober 2006, 16:48

BlackJack hat geschrieben:Aaah, man kann die Überprüfung verhindern:

Code: Alles auswählen

In [156]: a.resize((5,), refcheck=False)
Ansonsten sind die "list comprehension"s, die Du da jetzt verwendest natürlich völlig fehl am Platze. ;-)
Hmm ja, heißt das jetzt ... ?
Also entweder eine Schleife oder refcheck=False setzen damit
ich auch mit Referenzen von Referenzen arbeiten kann ?
Ich habe mal eine Schleife genommen.
BlackJack hat geschrieben:.... Zusammen mit der Indexerei um jeweils nur auf die Hälfte der offenen Dateien zuzugreifen ist das jetzt schwerer verständlich als vorher.
Also Du meinst der Quellcode ist weniger verständlicher durch das
Problem mit den Referenzen, also wäre eine Schleife an der richtigen Stelle besser ?
BlackJack hat geschrieben:Mit ``is`` darf man nur auf Objektidentität testen, ..., denn `data_shall_be_saved` *ist* ja schon ein Wahrheitswert, den braucht man nicht noch einmal mit einem Wahrheitswert vergleichen, ....
geändert
BlackJack hat geschrieben:Ich hätte fast bemängelt, dass die Datendateien nicht wieder geschlossen werden, als ich ziemlich weit unten die Schleife entdeckte. Warum erst dort? Und warum über einen Index auf die Elemente zugreifen?
Die Anzahl der Dateien variiert ja, abhängig von der Anzahl der Bänder, daher ein Index. Du hattest ja auch mal vorgeschlagen die Daten anders
zu kapseln. Das kann ich in Gnuradio aber nicht mal eben so, dazu müßte
ich schwer einsteigen in die Gnuradio Entwicklung. :(
BlackJack hat geschrieben:Der Name des Objekttyps sollte übrigens nicht im Namen auftauchen, an den das Objekt gebunden wird.
Stimmt, allerdings habe ich sonst viele gleich klingende Namen.

@BlackJack
Du hattest ja gefragt, ob Gnuradio noch auf die Dateien zugreift, wenn
mein Analyse Programm läuft. Nein tut es nicht. Leider. Dann wäre mein
Programm wahrscheinlich komplizierter, denn ich müsste es in Gnuradio
einbinden, allerdings hätte ich dann ein Echtzeit Analyse. Steht zur Zeit
aber außer Frage für mich.

Mein Code sieht jetzt so aus,
http://pastebin.com/m68261d5b

und ich bekomme die Fehlermeldung für Zeile 38 bis 40,
File "./histogramm.py", line 103, in extrahierePeaks
list_arraypeaks[criterion],
TypeError: only integer arrays with one element can be converted to an index
Irgendwie geht das mit dem Criterion nicht über eine Liste mit mehreren
arrays. :P
Hat einer von Euch beiden eine gute Idee ? :)
feldmaus
User
Beiträge: 284
Registriert: Donnerstag 12. Oktober 2006, 16:48

Mein Programm in der jetzigen Form findet 222134 peaks während meine
alte Version nur 27271 findet.

Ic bin mir jetzt nicht mehr sicher was nun richtig ist.
Wie kann ich die 1 zählen in <list_arraypeaks[0]> ?

Zur zeit sind ja immer 3 arrays in dieser Liste unter gebracht.

Die Bänder scheinen in diesem Fall gleich zu sein, da ich sie künstlich
erzeugt habe. Ich habe getestet, ob die arrays list_arraypeaks[0],
list_arraypeaks[1] und list_arraypeaks[2], gleich sind mit:

Code: Alles auswählen

        if np.equal(np.all(list_arraypeaks[0]),np.all(list_arraypeaks[1])):
            print "Bänder sind gleich"
Ist das so in Ordnung ?

Nun müsste ich dann nur noch die 1 zählen, aber wie mache ich das ?
BlackJack

@feldmann_markus: Die Kritik an den LCs hatte nichts mehr mit Referenzen bei den Arrays zu tun. Da geht's einfach nur um Lesbarkeit und Verständlichkeit und ein ganz kleines bisschen um Effizienz.

Das `refcheck`-Argument kannst Du benutzen um den Index bei den Schleifen mit dem `resize()` loszuwerden. Damit kann man die Ausnahme verhindern, die Du vorher bekommen hast. Das kann potentiell gefährlich sein, wenn man an anderer Stelle im Programm schon "Views" auf die Daten gelegt hat, die nach dem `resize()` auf den falschen Speicher zugreifen, aber da können wir uns hier ja ziemlich sicher sein, dass das nicht passiert, da die Daten frisch eingelesen wurden und wir genau wissen, was damit bisher gemacht wurde.

Deine Argumentation für den Index verstehe ich nicht. ``for item in sequence:`` ist auch unabhängig davon wieviele Elemente in `sequence` sind, benötigt aber keinen zusätzlichen Index.

Wenn Du zuviele ähnlich klingende Namen hast, solltest Du die Namen grundsätzlich überdenken und/oder deren Anzahl innerhalb des Gültigksitsbereichs. Man kann das ja auch auf mehrere Funktionen aufteilen, wenn's zu unübersichtlich wird.

Mein Vorschlag die Daten in *Deinem* Programm in einer Klasse zu kapseln, hat nichts mit Gnuradio zu tun.

Das ``list_arraypeaks[criterion]`` nicht funtkioniert, sollte eigentlich klar sein. Selbst wenn `list` die Indexarten von `numpy.array` verstehen würde, wäre das Objekt der falsche Adressat für die Operation. Du willst ja nicht aus `list_arraypeaks` nach dem `criterion` Arrays auswählen, sondern aus jedem der enthaltenen Arrays die Werte. Das wäre zum Beispiel so möglich: ``[a[criterion] for a in list_arraypeaks]``.

Einsen in Arrays zählen, die nur 0en und 1en enthalten geht einfach durch aufsummieren:

Code: Alles auswählen

In [206]: a
Out[206]: array([1, 1, 0, 1, 0, 1, 1, 1, 1, 1])

In [207]: b
Out[207]: array([0, 1, 0, 0, 1, 1, 1, 0, 0, 1])

In [208]: a.sum(), b.sum()
Out[208]: (8, 5)
Gleichheit kann man so wie Du das da machst nicht testen. So etwas kann man aber auch ganz schnell interaktiv in einer Python-Shell ausprobieren. Mit `a` und `b` von eben, die ganz offensichtlich nicht gleich sind:

Code: Alles auswählen

In [218]: np.equal(np.all(a), np.all(b))
Out[218]: True
Weil `all()` testet ob alle Elemente Wahr sind, und das sind sie in beiden Arrays natürlich nicht, aber `False` und `False` sind natürlich gleich:

Code: Alles auswählen

In [219]: np.all(a), np.all(b)
Out[219]: (False, False)

In [220]: np.all?
Type:           function
Base Class:     <type 'function'>
String Form:    <function all at 0x83a9c6c>
Namespace:      Interactive
File:           /usr/lib/python2.5/site-packages/numpy/core/fromnumeric.py
Definition:     np.all(x, axis=None, out=None)
Docstring:
    Return true if all elements of x are true:
Du kannst die Arrays vergleichen -- da kommt dann ein Array heraus, dass das Ergebnis der elementweisen Vergleiche enthält, und *darauf* dann `all()` anwenden:

Code: Alles auswählen

In [221]: (a == b).all()
Out[221]: False

In [222]: (a == a).all()
Out[222]: True
feldmaus
User
Beiträge: 284
Registriert: Donnerstag 12. Oktober 2006, 16:48

@BlackJack
Danke für deine Antwort.

Komischerweise hatten meine arrays im alten Programm zu wenig Peaks
gefunden. Zumindest wenn ich dem glaube darf was ich ausprobiert habe:

Code: Alles auswählen

        anzahlpeaks = 0
        j=0
        for i in xrange(len(list_arraypeaks[0])):
            j += 1
            if (list_arraypeaks[0][i] == 1) | (list_arraypeaks[1][i] == 1) | (list_arraypeaks[2][i] == 1):
                anzahlpeaks += 1
                print "Index :",i,"Band1:",list_arraypeaks[0][i],\
                    "Band2:",list_arraypeaks[1][i],\
                    "Band3:",list_arraypeaks[2][i],\
                    "Übereinstimmede Peaks : ",anzahlpeaks
            if j > 1000:
                raw_input("PAUSE -- mit Enter weiter !")
                j = 0
Damit erhalte ich dann für mein altes Programm folgende Einträge die bis
Index 685780 gehen:
http://pastebin.com/m192a5388

Und mein neues Programm macht da aber weiter:
http://pastebin.com/mad06b6c

Meine Peakdateien haben ja eine Größe von 5486850 Bytes, also
kann der Index auch bis über 5 Mil. gehen. Nur warum stimmte das in
meinem alten Programm nicht ?

Verdammt ! Ich habe den Bug mit dem Teiler durch 8 in meinem alten
programm vergessen. Arghhhh :cry:

Grüße Markus
Zuletzt geändert von feldmaus am Donnerstag 28. Mai 2009, 18:35, insgesamt 1-mal geändert.
feldmaus
User
Beiträge: 284
Registriert: Donnerstag 12. Oktober 2006, 16:48

Mein Programm sieht jetzt so aus,
http://pastebin.com/m3a93fd00

Die extrahieren Methode funktioniert jetzt auch. Auf zur nächsten. :-)
Zuletzt geändert von feldmaus am Donnerstag 28. Mai 2009, 18:31, insgesamt 1-mal geändert.
BlackJack

Wo Du gerade das Wort "Methode" verwendest: Das ist eigentlich gar keine, oder habe ich irgendwo eine Verwendung von `self` übersehen!? Das sieht nach einer Funktion aus. Warum ist die an eine Klasse gebunden?
feldmaus
User
Beiträge: 284
Registriert: Donnerstag 12. Oktober 2006, 16:48

BlackJack hat geschrieben:Wo Du gerade das Wort "Methode" verwendest: Das ist eigentlich gar keine, oder habe ich irgendwo eine Verwendung von `self` übersehen!? Das sieht nach einer Funktion aus. Warum ist die an eine Klasse gebunden?
Mr. Zufall war am Werk. :-)

also man könnte mal drüber nachdenken die Funktion auszulagern, ist
aber erstmal für die Performance nicht so wichtig.

Und schon kommt meine nächste Methode,
http://pastebin.com/m712a67ef

Mein Rechner geht dabei gut bis 100%. Diese Methode soll die alle Daten
als Gnuplot Tabelle speichern.
Sieht einer von Euch da ne Chance ein wenig mehr Performance
raußzuholen?

self.daten ist ein numpy array der wie folgt aufgebaut ist:

Code: Alles auswählen

[[np.array mit Zeitstempel],[3 np.arrays mit den Peaks ],
[3 np.arrays mit den Peak Werten]]
Grüße Markus
BlackJack

@feldmann_markus: Naja, so besonders toll ist die vorherige Funkion aber immer noch nicht. Der Name ist "Denglisch" und nicht PEP8-Konform. Es gibt eine handvoll Zeilen, die länger als 80 Zeichen sind. Er könnte eine Tüte Leerzeichen nach Kommata vertragen um lesbarer zu werden. Zeichenketten werden als Kommentare missbraucht.

Es wird zuviel in dieser einen Funktion getan. Laden, verarbeiten, speichern -- alles in einer Funktion. Nicht voneinander abgekoppelt, so dass man zum Beispiel nicht einfach Tests für die einzelnen Aspekte schreiben kann. Zuviele Namen, wie wir ja schon gestgestellt haben. Wenn man nicht mehr sieht, dass nicht alle Argumente verwendet werden, dann ist das ein Warnzeichen, dass die Funktion zu unübersichtlich geworden ist. `starttime` und `endtime` werden nirgends benutzt.

----

Zur nächsten Funktion: OMG. Du solltest Dir echt mehr Gedanken darüber machen was jeder einzelne Schritt tut und welche Zwischenergebnisse da rauskommen. Und endlich mit dieser ekligen Indexerei aufhören. Lauter unnötige Namen und Verschachtelte Zugriffe bei denen man nicht mehr durchsteigt.

Nochmal: Zeichenketten sind keine Kommentare, ausser wenn sie das erste in einem Modul, nach einer ``def``- oder ``class``-Zeile sind. An anderen Stellen sind sie nur toter Ballast im Code.

Diese Zeichenketten-Additionen sind "teuer" weil bei jeder Addition eine neue Zeichenkette entsteht, die in die die Bytes der beiden alten kopiert werden. Es werden also Unmengen an temporären Zeichenketten erzeugt und wieder weggeworfen.

Ausserdem werden für jede Zeile drei unnötige Arrays mit den Indexen erzeugt.

Und das `np.real()` ist auch unglaublich. Hast Du mal ausprobiert was das aus Skalaren macht? Das erzeugt ein `np.array` der Länge 1 mit dem Wert darin. Warum machst Du das!?

Mal ungetestet beide Methoden als Funktionen: http://paste.pocoo.org/show/119616/
feldmaus
User
Beiträge: 284
Registriert: Donnerstag 12. Oktober 2006, 16:48

BlackJack hat geschrieben:@feldmann_markus: Naja, so besonders toll ist die vorherige Funkion aber immer noch nicht.
Stimmt, aber mir war halt die performance wichtig. Die Übersichtlichkeit
kommt mit der Zeit. Nur habe ich für dieses Projekt nicht unendlich viel
Zeit.
Ich schreibe noch an einem anderen Python Programm(privat), da
kann ich mich dann richtig austoben. :-)
BlackJack hat geschrieben:`starttime` und `endtime` werden nirgends benutzt.
Geändert.


BlackJack hat geschrieben:Nochmal: Zeichenketten sind keine Kommentare, ausser wenn sie das erste in einem Modul, nach einer ``def``- oder ``class``-Zeile sind. An anderen Stellen sind sie nur toter Ballast im Code.
Ich werde mir das logging Modul noch mal angucken, aber nicht mehr
für dieses Programm.
BlackJack hat geschrieben:Diese Zeichenketten-Additionen sind "teuer" weil bei jeder Addition eine neue Zeichenkette entsteht, die in die die Bytes der beiden alten kopiert werden. Es werden also Unmengen an temporären Zeichenketten erzeugt und wieder weggeworfen.
Ich brauche für Gnuplot Leerzeichen zwischen den Daten.
BlackJack hat geschrieben:Ausserdem werden für jede Zeile drei unnötige Arrays mit den Indexen erzeugt.
Ich werde das wie Du mit der map() Funktion machen.
BlackJack hat geschrieben:Und das `np.real()` ist auch unglaublich. Hast Du mal ausprobiert was das aus Skalaren macht? Das erzeugt ein `np.array` der Länge 1 mit dem Wert darin. Warum machst Du das!?
Weil die Ausgabe sonst komisch aus sah, hier mal eine Ausgabe mit real()
und imag():
8.45053534704e-07 1 1 1 -1.0 -4.79459260937e-17j 0.5 -0.866025388241j 0.5 0.866025388241j
3.38021413882e-06 1 1 1 -3.00149301233e-17 -1.0j 0.866025388241 0.5j -0.866025388241 0.5j
0.00013436350855 1 1 1 -1.0 3.65565637658e-25j 0.5 -0.866025388241j 0.5 0.866025388241j
Ich gucke mir mal dein Programm an, sieht ja schon mal scharf aus. :-)
Die with Funktion kennt mein System, mein Buch und die Python Doku
2.6.2 nicht.
BlackJack hat geschrieben:Mal ungetestet beide Methoden als Funktionen: http://paste.pocoo.org/show/119616/
Danke das Du Dir die Mühe gemacht hast. :-)
Wie gesagt ich habe keine Zeit mehr, normalerweise müsste ich das
Programm Heute Abend fertig haben. :-)
Ich werde morgen und Heute noch ein ein paar Sachen ändern ergänzen.
BlackJack

@feldmann_markus: Für Leerzeichen zwischen Zeichenketten braucht man nicht zwingend ``+``. Wenn man die als Liste oder allgemein als "iterable" vorliegen hat, ist die `str.join()`-Methode in der Regel wesentlich effizienter.

Was heisst die Ausgabe sah "komisch" aus? Das macht bei `np.float32`- und `np.byte`-Objekten bei der Umwandlung in Zeichenketten keinen sichtbaren Unterschied. Es erzeugt bloss unnötige Objekte. Selbst bei `np.complex64`-Objekten ist es nicht die erste Wahl, weil auch da ein *Array* mit der jeweiligen Komponente als einzigem Element erzeugt wird. Wenn Du den Real- und Imaginärteil *einer* komplexen Zahl benötigst, dann gibt's dafür Attribute auf dem Objekt selbst.

Code: Alles auswählen

In [310]: c = np.complex64(4.5+2.5j)

In [311]: c
Out[311]: (4.5+2.5j)

In [312]: np.real(c)
Out[312]: array(4.5, dtype=float32)

In [313]: np.imag(c)
Out[313]: array(2.5, dtype=float32)

In [314]: c.real
Out[314]: 4.5

In [315]: c.imag
Out[315]: 2.5
Die ``with``-Anweisung gibt es seit Python 2.5 und die ist auch in der Dokumentation vorhanden. Im Tutorial am Ende des Abschnitts über Methoden auf Datei-Objekten und in der Sprachreferenz unter dem Titel The ``with`` statement.
Antworten