for-loop auf lösen für speed up

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.
Antworten
schneitzmaster
User
Beiträge: 94
Registriert: Freitag 26. Oktober 2012, 15:35
Wohnort: Hamburg

Hallo liebes Forum,

ich habe eine for-Schleife die über eine sehr große anzahl an Elementen geht, so dass es ungemein lange dauert bis die operation fertig ist. Dabei soll nichts berechent werden sondern lediglich eine art sortierung vorgenommen werden.
Meine Frage ist es nun ob das ganze in Python auch ohne for-schleife möglich wäre (da die Zuordnung gerade zu trivial ist)

Code: Alles auswählen

pc_elem_seq = [0]*len(PC_SET.elements)
i = 0
for element in PC_SET.elements:      
    pc_elem_seq[i]  = PC_ABS.elements[element.label-1:element.label]
    i                      = i+1

Gruß
BlackJack

@schneitzmaster: Das Vorgehen an sich sieht schon ziemlich „unpythonisch” aus. Eine Liste mit Dummy-Werten erzeugen und die dann alle der Reihe nach über einen Index mit den echten Werten zu ersetzen ist kein idiomatisches Python. Man würde eine Liste durch anhängen von Elementen an eine anfangs leere Liste erstellen. In diesem Fall würde sich auch eine „list comprehension” geradezu anbieten.

Code: Alles auswählen

    pc_elements = [
        PC_ABS.elements[element.label - 1:element.label]
        for element in PC_SET.elements
    ]
Edit: Eigentlich sollte das hier reichen falls `PC_ABS.elements` eine Liste ist:

Code: Alles auswählen

    pc_elements = [
        [PC_ABS.elements[element.label - 1]] for element in PC_SET.elements
    ]
Es sei denn Du erwartest, dass `label`-Indizes ausserhalb von der Sequenz liegen, auf die dort zugegriffen wird. Wobei die Struktur auf den ersten Blick schon etwas komisch aussieht, die da erzeugt wird.
schneitzmaster
User
Beiträge: 94
Registriert: Freitag 26. Oktober 2012, 15:35
Wohnort: Hamburg

hi BlackJack,

leider funktioniert dein Vorschlag nicht. Das Problem ist, dass ich die Elemente von

Code: Alles auswählen

pc_elem_seq
in einer komischen Art und Weise angegeben werden müssen (ich benutze Python als skript-Sprache für ABAQUS CAE).
Hier ein kurzes Beispiel zur Illustration

Code: Alles auswählen

>>> PC_SET.elements[0]
mdb.models['3D-Zelle'].parts['PC_ABS'].elements[2164]
>>> PC_SET.elements[1]
mdb.models['3D-Zelle'].parts['PC_ABS'].elements[2165]
>>> PC_SET.elements[0].label
2165
>>> PC_SET.elements[1].label
2166
>>> pc_elem_seq[0]
mdb.models['3D-Zelle'].rootAssembly.instances['PC_ABS'].elements[2164:2165]
Ich benötige also eine Sequenz in der die elemente mit dem : angegeben sind. Ich weiß nicht genau warum und was der Doppelpunkt in der letzten Zeile zu sagen hat, aber ich denke das ist eine Vorgabe von ABAQUS.
Weiterhin habe ich die Variante mit dem .append schon probiert (das sollte dann etwas mehr "pythonisch" sein).

Code: Alles auswählen

pc_elem_seq = []
for element in PC_SET.elements:      
    pc_elem_seq.append( PC_ABS.elements[element.label-1:element.label] )

Ich hatte nur vermutet, dass man die Geschwindigkeit der for-schleife noch erhöhen kann wenn man den Speicherplatz vorher allokiert ( wie das in MATLAB gängige Praxis ist).

Aber alle deine Vorschläge sind ja auch mit einer for-schleife versehen. Vielleicht gibt es ja keine andere Möglichkeit. Ich hatte nur gedacht das ich diese zu Ordnung jedes einzelnen Elements ohne for-Schleife durchführen kann. Diese benötigt nämlich die meiste Zeit.
Zuletzt geändert von schneitzmaster am Mittwoch 14. August 2013, 15:02, insgesamt 1-mal geändert.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

schneitzmaster hat geschrieben:leider funktioniert dein Vorschlag nicht.
Was heißt den "funktioniert nicht"? Gibt es eine Fehlermeldung?
schneitzmaster hat geschrieben:Ich benötige also eine Sequenz in der die elemente mit dem : angegeben sind. Ich weiß nicht genau warum und was der Doppelpunkt in der letzten Zeile zu sagen hat, aber ich denke das ist eine Vorgabe von ABAQUS.
Das nennt sich Slicing und hat mit ABQAUS erstmal nichts zu tun. Hast du schon das offizielle Python-Tutorial durchgearbeitet?
schneitzmaster hat geschrieben:Weiterhin habe ich die Variante mit dem .append schon probiert[/code]
Warum initialisierst du mit [0]?
schneitzmaster hat geschrieben:Aber alle deine Vorschläge sind ja auch mit einer for-schleife versehen. Vielleicht gibt es ja keine andere Möglichkeit. Ich hatte nur gedacht das ich diese zu Ordnung jedes einzelnen Elements ohne for-Schleife durchführen kann. Diese benötigt nämlich die meiste Zeit.
Irgendwo brauchst du auf jeden Fall so etwas wie eine for-Schleife. Egal ob nun explizit oder verdeckt durch ``map``.
Das Leben ist wie ein Tennisball.
schneitzmaster
User
Beiträge: 94
Registriert: Freitag 26. Oktober 2012, 15:35
Wohnort: Hamburg

Okay gut danke. Das ist eine wertvolle Information. Mit dem Slicing das wusste ich noch nicht.
Nein das Tutorial habe ich noch nicht gemacht.
Und ja es gibt eine Fehler Meldung, da ABAQUS die Elemente in dieser "geslicten" Form haben möchte. Ansonsten wird das ABAQUS interne SET, dass ich erzeugen möchte nicht erzeugt wird.

Ich hatte vermuttet das es einen schnellen weg ohne for-schleife gibt, da ich ja schon alle elemente in dem Object PC_SET.elements drin habe und nur noch in die geslicte Form bringen muss.
Na ja aber wenn du meinst eine for-schleife lässt sich nicht umgehen, denke ich auch dass wohl keinen merklichen Geschwindigkeitszuwachs geben wird oder?
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@schneitzmaster: Wenn du mit allen Listenelementen irgendwas machen willst, dann müssen eben auch alle Listenelemente abgelaufen werden. Das wird am Ende immer auf eine Schleife hinauslaufen.

Wie EyDu schon angedeutet hatte, mag es - je nach Situation - irgendwelche Python-Funktionen geben, die keine explizite `for`-Schleife in Python-Code erfordern, aber unter der Haube ist letzten Endes trotzdem eine Schleife nötig.
schneitzmaster
User
Beiträge: 94
Registriert: Freitag 26. Oktober 2012, 15:35
Wohnort: Hamburg

@snafu: Schon klar das am ende immer irgendwie eine Schleife durchläuft. Jedoch gibt es ja zum Beispiel beim bilden eines Skalarproduktes auch mehrere möglichkeiten. Und wenn man da numpy.dot(...) verwendet ist das ja um Größenordnungen schneller als Schleifen zu benutzen. Von daher dachte ich gibt es vielleicht auch fortgeschrittene module oder vorgehensweisen die listen / sequenzen Operationen beschleunigen.

Aber gut ich werd mich mit der Schleife abfinden und in der zwischen Zeit einen Tee trinken :)
BlackJack

@schneitzmaster: `numpy.dot()` ist schlecht mit so etwas vergleichbar. Da werden intern Operationen auf Werten von „Prozessor-Datentypen” in reinem C-Code (oder manchmal auch Fortran-Code) und je nach Prozessor vielleicht auch noch parallelisiert, ausgeführt.

Wenn das mit einer ``for``-Schleife und `append()` funktioniert, dann kannst Du auch eine äquivalente „list comprehension” (LC) schreiben. Mein erstes Beispiel sollte da eigentlich funktionieren. Da wird ja noch „slicing” verwendet. Eine LC ist auch ein ganz kleines bisschen schneller als eine ``for``-Schleife. Ohne unleserlich zu werden, dürfte wohl folgendes in reinem Python das Limit darstellen:

Code: Alles auswählen

    elements = PC_ABS.elements
    pc_elements = [
        elements[element.label - 1:element.label] for element in PC_SET.elements
    ]
Nächster Schritt wäre dann Cython, um den Mehraufwand des Bytcode-Interpretierens zu vermeiden. Wobei das nicht allzu viel bringen dürfte.

Das ganze steckt hoffentlich in einer Funktion‽ Davon abgesehen, dass das sauberer ist, werden lokale Namen auch schneller aufgelöst als (modul)globale Namen.

Dein Beispiel zur Illustration hilft übrigens nicht viel weiter weil alles was mit `PC_SET` zu tun hat, nicht relevant ist. Einzig die letzten beiden Zeilen zeigen das gewünschte Ergebnis. Du möchtest in der Liste also keine Elemente aus `PC_ABS.elements` haben, sondern ein „slice” von diesem Objekt welches immer genau ein Element enthält.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Wo genau soll denn jetzt eigentlich die eingangs genannte Sortierung sein? Da werden doch im Grunde nur Objekt-Referenzen rumkopiert oder übersehe ich etwas entscheidendes?
BlackJack

@snafu: Die Reihenfolge in `PC_SET.elements` beziehungweise die `label`-Attribute dort bestimmen die Reihenfolge des Ergebnisses. Die Ergebniselemente sind ja nicht aus `PC_SET.elements` sondern aus `PC_ABS.elements`.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ich hab keine Ahnung ob's schneller ist und ist ungetestet, da ich die angesprochene Umgebung nicht installiert habe. Rein theoretisch müsste es aber funktionieren:

Code: Alles auswählen

from itertools import imap
from operator import attrgetter

import numpy as np

labels = imap(attrgetter('label'), PC_SET.elements)
choices = np.fromiter(labels, int, len(PC_SET.elements))
print np.choose(choices, PC_ABS.elements)
BlackJack

@snafu: Ich glaube es macht wenig Sinn `numpy` für so etwas zu missbrauchen. Es geht hier um Objekte und nicht um Arrays von Zahlen. Ausserdem dürfte `numpy.choose()` wohl kaum „slicen”, also ist das keine funktionierende Lösung.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

BlackJack hat geschrieben:Ausserdem dürfte `numpy.choose()` wohl kaum „slicen”, also ist das keine funktionierende Lösung.
Wenn denn Slicing wirklich gewollt ist...

Naja, PyPy wäre natürlich noch eine naheliegende Alternative. Fragt sich nur, ob der Threadersteller den benutzten Interpreter in seiner Anwendung auch entsprechend auf PyPy umstellen könnte.
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@schneitzmaster: Aus deinen Beispielen geht nicht hervor, ob am Ende alle Ergebnisse tatsächlich als Sequenz vorliegen müssen, weil du zB. darüber iterieren und alle weiterverarbeiten möchtest, oder ob es genügt, einen Zugriff auf einzelne Ergebnisse zu haben. In diesem Fall könntest du dir das vorherige Erzeugen aller Ergebnisse sparen:

Code: Alles auswählen

class LazyElements(dict):

    def __init__(self, pc_abs_elements, pc_set_elements):
        slf.pc_abs_elements = pc_abs_elements
        slf.pc_set_elements = pc_set_elements

    def __missing__(self, key):
        index = self.pc_set_elements[key].label
        value = self[key] = self.pc_abs_elements[index - 1 : index]
        return value

pc_elements = LazyElements(PC_ABS.elements, PC_SET.elements)

print pc_elements[0]
Hierbei werden immer nur die Werte erzeugt, die du auch tatsächlich verwendest, und das Erzeugen geschieht jeweils nur einmal, da alle erzeugten Werte memoized werden. Informationen zu dicts gibt es hier. Iterieren wie über eine fertige Sequenz ist damit natürlich nicht mehr möglich.
Zuletzt geändert von pillmuncher am Dienstag 15. Oktober 2013, 00:27, insgesamt 1-mal geändert.
In specifications, Murphy's Law supersedes Ohm's.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

ich denke auch nicht, dass die for-Schleife an sich die Sache so langsam macht (Listen mit einigen Millionen Elementen hin und herzuiterieren sollte in wenigen Sekunden machbar sein), sondern das Erzeugen dieser ominösen ABACUS-Sequence-Objekte.
BlackJack

@snafu: Ja, slicing ist tatsächlich gewollt, weil da ein anderer Typ zurück gegeben wird als bei einem einfachen Indexzugriff, und offenbar wird genau der Typ im weiteren Verlauf benötigt.
Antworten