Seite 1 von 2

Optimierung

Verfasst: Dienstag 6. Juli 2010, 16:07
von Peak_me
huhu!

Mein Programm braucht zu lange, um durchzulaufen.
Da die Optimierung meines Codes doch vielverspechend ist, bin ich ihn jetzt mal durchgegangen und habe die ersten Fragen aufgeschrieben:

##########################################################
Ich habe eine Textdatei, deren Inhalt ich in ein set laden will.
In jeder Zeile der Textdatei steht ein Wort; jedes Element des sets soll ein Wort enthalten.

Dazu muss ich die Enter-Zeilen entfernen.
Dies habe ich bis jetzt so gemacht:

Code: Alles auswählen

datei=open(pfad)
a=datei.readlines()

for i in range(len(a)):
    a[i] = a[i].replace('\n','')
a=set(a)
Wie kann ich die Enter-Zeilen direkt aus dem Set entfernen und kann mir den Umweg über die Liste sparen?

##########################################################nächste Frage
Ich habe eine Bilddatei und will in einem 10-mal-10-Pixel-Rechteck dieser Datei für jeden Pixel eine Fallunterscheidung durchführen.
Dies mache ich bis jetzt so:

Code: Alles auswählen

img=Image.open(pfad)
a=list()
y=0
for h in range(10):
    x=0
    y+=1
    for g in range(10):
        x+=1
        pix=img.getpixel((x,y))
        if pix==(0,0,0):
            a.append(1)
        else:
            a.append(0)

BlackJack deutete dann aber eine Möglichkeit an, dies mit der crop()-Methode schneller hinzubekommen.
Weiter als das bin ich aber nicht gekommen:

Code: Alles auswählen

box = (0,0, 10, 10)
b = img.crop(box)
Wie komme ich jetzt an die einzelnen Pixel ran und kann mit ihnen eine Fallunterscheidung durchführen?

##########################################################nächste Frage
Ich möchte eine rückwärtszählende Schleife erstellen.

Code: Alles auswählen

for i in range(5):
liefert ja die Zahlen 0,1,2,3,4.

Nun will ich aber die Zahlen 4,3,2,1,0 haben.
Dies habe ich bis jetzt damit gelöst:

Code: Alles auswählen

for i in [4,3,2,1,0]:
Wie geht das, ohne alle Elemente einzeln aufzuzählen?

Code: Alles auswählen

for i in range(4,0)
geht genauso wenig wie

Code: Alles auswählen

for i in range(0,4,-1).
######

Ich verwende sehr oft Schleifen, die dann aber zu kleinen Ungetümen ausarten:

Code: Alles auswählen

a=[[[1,2,9,4],[5,9,7,8],[9,10,11,12]],[[9,10,11,12],[5,6,9,8],[1,2,9,4]]]

b=list()
b1=list()

for i in range(len(a)):
    a1=a[i]
    for h in range(len(a1)):
        a2=a1[h]
        for g in range(len(a2)):
            if a2[g]==9:
                b1.append(g)
                break
    b.append(b1)
    b1=list()
Diese Schleife soll die Position einer "9" herausfinden, aber die Orgranisationsstruktur der Ursprungsliste beibehalten.
Gibt es irgendwelche Tipps, um solche Verschachtelungen besser zu gestalten?

Mein Hauptproblem ist, dass ich nicht das machen kann:

Code: Alles auswählen

for i in range(len(a)):
    for g in range(len(a[i])):
        print a[i][g]
sonder immer eine zusätzliche Variable einführen muss:

Code: Alles auswählen

for i in range(len(a)):
    a1=a[i]
    for g in range(len(a1)):
        print a1[g]
##########################################################

Ich würde mich freuen, wenn ihr mir auch dabei weiterhelfen könntet.
Mit EyDu's Hinweis, dass in einigen Fällen Sets besser als Listen sind, konnte ich mein Programm ja schon extrem verschnellern :mrgreen:


Gruß
Paul

Re: Optimierung

Verfasst: Dienstag 6. Juli 2010, 16:41
von b.esser-wisser

Code: Alles auswählen

the_whole_set = set()
with open(FILENAME) as fd:
    for line in fd:
        the_whole_set.add(line.strip())
       #oder line.rstrip(), oder line[:-1]
Sowas?

hth, Jörg

Re: Optimierung

Verfasst: Dienstag 6. Juli 2010, 16:42
von Defnull
Peak_me hat geschrieben:

Code: Alles auswählen

datei=open(pfad)
a=datei.readlines()

for i in range(len(a)):
    a[i] = a[i].replace('\n','')
a=set(a)

Code: Alles auswählen

with open(pfad) as fp:
    a = set(line[:-1] for line in fp)
Peak_me hat geschrieben: Ich möchte eine rückwärtszählende Schleife erstellen.

Code: Alles auswählen

>>> range(4,0,-1)
[4, 3, 2, 1]

Re: Optimierung

Verfasst: Dienstag 6. Juli 2010, 17:12
von BlackJack
@Peak_me: Eigene Themen für verschiedene Fragen wären nicht schlecht gewesen, oder zumindest an den "Trennlinien" (###…) durchnummerieren, damit man die Antworten einfacher zuordnen kann.

Du solltest Dich mal mit dem Thema "iterables" und "list comprehension" beziehungsweise Generatorausdrücke beschäftigen. `set()` will etwas iterierbares, Dateiobjekte sind iterierbar und mit einem Generatorausdruck kann man aus dem einen "iterable" ein weiteres "iterable" machen das die Zeilenendezeichen entfernt. Wenn "whitespace" am Ende der Zeilen grundsätzlich weg kann, würde ich die `rstrip()`-Methode verwenden:

Code: Alles auswählen

a = set(s.rstrip() for s in datei)
An die "flachen" Pixeldaten eines PIL-Images kommt man mit der `getdata()`-Methode:

Code: Alles auswählen

a = [int(p == (0, 0, 0)) for p in img.crop((0, 0, 10, 10)).getdata()]
Sollen in der Liste wirklich Zahlen enthalten sein, oder reichen Wahrheitswerte? Dann könnte man den `int()`-Aufruf nämlich weglassen.

Rückwärtszählende Schleifen erstelle ich immer mit `reversed()`. Geht auch in dem man die Argumente von `range()` oder `xrange()` passend wählt, aber ich komme da auch immer durcheinander und finde `reversed()` lesbarer.

Bei dem "Schleifenungetüm" ist mir als erstes das erstellen von `b1` aufgefallen. Diese Zeile sollte nur einmal im Programm auftauchen müssen und nicht zweimal. Überlege mal wo man das hinschreiben müsste.

Grunsätzlich solltest Du bei Schleifen über `range(len(obj))` innehalten und es anders schreiben. Es gibt nur ganz wenige Fälle in denen man nur den Index braucht. Wenn man ihn eigentlich gar nicht braucht, kann man direkt über die Elemente iterieren, ansonsten bekommt man mit `enumerate()` Indizes *und* Elemente.

Wenn garantiert ist, dass die 9 auch tatsächlich in jeder inneren Liste enthalten ist, dann ist das ein Einzeiler:

Code: Alles auswählen

b = [[x.index(9) for x in xs] for xs in a]
Ansonsten müsste man aber sowieso spezifizieren was an der Stelle in der Liste stehen soll wenn keine 9 gefunden wird.

Nochmal: `range(len(irgendwas))` ist fast immer "unpythonisch"!

Re: Optimierung

Verfasst: Dienstag 6. Juli 2010, 19:09
von hendrikS
Peak_me hat geschrieben: In jeder Zeile der Textdatei steht ein Wort; jedes Element des sets soll ein Wort enthalten.
Dazu muss ich die Enter-Zeilen entfernen.
Im Prinzip ist das nicht 100% kausal. Auch mit New Line wären die Elemente in der Menge immer eindeutig. Also, wenn's nicht drauf ankommt könntest Du Dir das sogar sparen. Aber ich würde die Einträge vermutlich auch vorher bereinigen.

Re: Optimierung

Verfasst: Dienstag 6. Juli 2010, 19:54
von BlackJack
@hendrikS: Die Worte wären zwar auch mit Newline eindeutig, aber das `set()` wird ja nicht nur so zum Spass erstellt, sondern um andere Worte dagegen zu testen. Und sollte man an die dann jedesmal ein Newline anhängen bevor man testet ob sie enthalten sind!? Klingt nicht besonders sinnvoll.

Re: Optimierung

Verfasst: Dienstag 6. Juli 2010, 19:54
von noisefloor
Hallo,

Kurzform der Schachtelschleife:

Code: Alles auswählen

...
for i in a:
    for j in i:
        if 9 in j:
            b1.append(j)
            ...
Wie BlackJack schon sagte: Über viele Dinge kann man in Python direkt iterieren. :-)

Gruß, noisefloor

Re: Optimierung

Verfasst: Dienstag 6. Juli 2010, 20:40
von BlackJack
@noisefloor: Das ist nicht die Kurzform der "Schachtelschleife". Da wirst Du eine Ausnahme bekommen, dass der Typ `int` nicht "iterable" ist. Ausserdem soll in der Liste `b1` der Index vermerkt werden, nicht das Element selbst.

Re: Optimierung

Verfasst: Mittwoch 7. Juli 2010, 06:16
von Dav1d
Ausserdem soll in der Liste `b1` der Index vermerkt werden, nicht das Element selbst.
Dafür könnte man auch `enumerate` benutzen

Re: Optimierung

Verfasst: Mittwoch 7. Juli 2010, 07:02
von noisefloor
Hallo,

@BlackJack: ???

Wenn ich die "Kurzform" von mir auf

Code: Alles auswählen

a=[[[1,2,9,4],[5,9,7,8],[9,10,11,12]],[[9,10,11,12],[5,6,9,8],[1,2,9,4]]]
los lassen, dass läuft das ohne Fehler durch...

Gruß, noisefloor

Re: Optimierung

Verfasst: Mittwoch 7. Juli 2010, 08:55
von BlackJack
@noisefloor: Ups, mein Fehler. Aber das Ergebnis ist falsch.

Re: Optimierung

Verfasst: Mittwoch 7. Juli 2010, 13:21
von Peak_me
vielen Dank für eure Hilfe!
Ich habe viele Abschnitte durch neue ersetzt.

####################Datei öffnen und in set packen ##Problem 1

Das funktioniert perfekt:

Code: Alles auswählen

datei=open(pfad)
a = set(s.rstrip() for s in datei)
Doch brauche ich das gleiche nochmal als Liste aber mit den "/n"s.
Mit dieser Liste führe ich keine aufwendigen Vergleiche durch, sondern muss nur auf ganz wenige, aber spezifische, Elemente zugreifen.
Doch die Erstellung dieser Liste geht nicht mehr

Code: Alles auswählen

datei=open(pfad)
b=datei.readlines()
a= set(s.rstrip() for s in datei)
Ich habe festgestellt, dass er nicht aus "datei" lesen kann, wenn er es vorher schon mal gemacht hat.
Deswegen habe ich es so geschrieben:

Code: Alles auswählen

datei=open(pfad)
b=datei.readlines()
datei.close()
datei=open(pfad)
a= set(s.rstrip() for s in datei)
Muss ich die Datei zweimal öffnen oder geht es auch direkt hintereinander?

####################Pixelrechteck auslesen ##Problem 2

a=[int(p ==(0,0,0)) for p in img.crop((x+1, y, x+10,y+10)).getdata()]
funktioniert bestens!

####################Soweit alle Probleme gelöst ##Danke

Re: Optimierung

Verfasst: Mittwoch 7. Juli 2010, 13:24
von /me
Peak_me hat geschrieben:

Code: Alles auswählen

datei=open(pfad)
b=datei.readlines()
datei.close()
datei=open(pfad)
a= set(s.rstrip() for s in datei)
Muss ich die Datei zweimal öffnen oder geht es auch direkt hintereinander?
Mit seek() solltest du wieder an den Dateianfang kommen.

Re: Optimierung

Verfasst: Mittwoch 7. Juli 2010, 13:42
von BlackJack
@Peak_me: Ad 1) Vergiss das mit `seek()`. Das funktioniert zwar aber warum die Datei zweimal einlesen? Du hast sie doch gerade als Liste eingelesen.

Re: Optimierung

Verfasst: Mittwoch 7. Juli 2010, 14:09
von Peak_me
@ /me:
Dass der Cursor ja durch das erste Einlesen nicht mehr am Anfang ist, hatte ich vergessen.
Mit datei.seek(0) klaptt es jetzt!


@BlackJack:
Ich brauche die Datei zweimal:
einmal als set, um damit aufwendige Vergleiche durchzuführen
und einmal, um auf einige wenige Wörter zuzugreifen, zB auf Wort 42.1378

Re: Optimierung

Verfasst: Mittwoch 7. Juli 2010, 14:22
von BlackJack
@Peak_me: Du brauchst die *Daten* zweimal aber nicht die Datei. Du hast doch in dem Beispiel da oben alle Zeilen in einer Liste an `b` gebunden. Warum musst Du die Datei nochmal ein lesen wenn die Daten doch schon alle im Speicher stehen. Noch dazu in einer iterierbaren Datenstruktur.

Re: Optimierung

Verfasst: Mittwoch 7. Juli 2010, 15:08
von albertus
Hallo Paul,
Peak_me hat geschrieben:huhu!

##########################################################
Ich habe eine Textdatei, deren Inhalt ich in ein set laden will.
In jeder Zeile der Textdatei steht ein Wort; jedes Element des sets soll ein Wort enthalten.

Dazu muss ich die Enter-Zeilen entfernen.
Dies habe ich bis jetzt so gemacht:

Code: Alles auswählen

datei=open(pfad)
a=datei.readlines()

for i in range(len(a)):
    a[i] = a[i].replace('\n','')
a=set(a)
Wie kann ich die Enter-Zeilen direkt aus dem Set entfernen und kann mir den Umweg über die Liste sparen?
Mal angenommen du brauchst die Liste nicht dann würde ich die Datei so in ein Set umwandeln:

myset = set(open(pfad).read().rstrip("\n").split("\n"))

brauchst du die Liste so würde ich es so machen:

mylist = open(pfad).read().rstrip("\n").split("\n")
myset = set(mylist)

Mit freundlichen Grüßen

Albert

Re: Optimierung

Verfasst: Mittwoch 7. Juli 2010, 18:04
von BlackJack
@albertus: Und warum würdest Du das so umständlich und nicht besonders speicherschonend machen? Dateiobjekte sind "iterable" und zwar sind die Elemente die Zeilen. Es besteht also kein Grund erst die ganze Datei in den Speicher zu laden, so dass jeweils beim `rstrip('\n')` und `split('\n')` Speicherplatz für die doppelte Menge vom Text belegt werden muss.

Ausserdem gäb's da noch die `splitlines()`-Methode.

Aber wie gesagt, alles viel zu umständlich.

Re: Optimierung

Verfasst: Donnerstag 8. Juli 2010, 08:07
von albertus
BlackJack hat geschrieben:@albertus: Und warum würdest Du das so umständlich und nicht besonders speicherschonend machen? Dateiobjekte sind "iterable" und zwar sind die Elemente die Zeilen. Es besteht also kein Grund erst die ganze Datei in den Speicher zu laden, so dass jeweils beim `rstrip('\n')` und `split('\n')` Speicherplatz für die doppelte Menge vom Text belegt werden muss.

Ausserdem gäb's da noch die `splitlines()`-Methode.

Aber wie gesagt, alles viel zu umständlich.
Guten Morgen BlackJack,

Was ist schneller das Zeilenweise auslesen einer Datei mit nachfolgenden rstrip(), oder das einlesen einer Datei in einem Rutsch mit nachfolgenden split("\n")? Denn gefragt wird hier nach der Geschwindigkeit und nicht nach der Speicher schonendsten Möglichkeit. Das rstrip() kann man sich auch sparen wenn ein "" Element im set nicht stört, ansonsten vielleicht noch ein myset.remove("")

Die `splitlines()`-Methode wehre aber eine gute Alternative zu split("\n").

Mit freundlichen Grüßen

Albertus

Re: Optimierung

Verfasst: Donnerstag 8. Juli 2010, 09:32
von BlackJack
@albertus: Kein Ahnung was schneller ist. Ich weiss aber dass Deine Methode deutlich umständlicher ausgedrückt ist als ein einfaches ``list(datei)`` oder ``datei.readlines()``. In wie weit dabei wirklich zeilenweise eingelesen wird, ist dann übrigens eine Implementierungsfrage. Ohne zu *wissen* das es *deutlich* schneller ist, sollte man das IMHO nicht so umständlich schreiben.