Optimierung

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.
Peak_me
User
Beiträge: 92
Registriert: Sonntag 27. Januar 2008, 03:09

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
Benutzeravatar
b.esser-wisser
User
Beiträge: 272
Registriert: Freitag 20. Februar 2009, 14:21
Wohnort: Bundeshauptstadt B.

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
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

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]
Bottle: Micro Web Framework + Development Blog
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"!
Benutzeravatar
hendrikS
User
Beiträge: 420
Registriert: Mittwoch 24. Dezember 2008, 22:44
Wohnort: Leipzig

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.
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.
Benutzeravatar
noisefloor
User
Beiträge: 3843
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

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
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.
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Ausserdem soll in der Liste `b1` der Index vermerkt werden, nicht das Element selbst.
Dafür könnte man auch `enumerate` benutzen
the more they change the more they stay the same
Benutzeravatar
noisefloor
User
Beiträge: 3843
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

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
BlackJack

@noisefloor: Ups, mein Fehler. Aber das Ergebnis ist falsch.
Peak_me
User
Beiträge: 92
Registriert: Sonntag 27. Januar 2008, 03:09

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
Benutzeravatar
/me
User
Beiträge: 3554
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

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.
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.
Peak_me
User
Beiträge: 92
Registriert: Sonntag 27. Januar 2008, 03:09

@ /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
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.
albertus
User
Beiträge: 52
Registriert: Mittwoch 7. Juli 2010, 14:48

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
Mit freundlichen Grüßen

Albertus
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.
albertus
User
Beiträge: 52
Registriert: Mittwoch 7. Juli 2010, 14:48

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
Mit freundlichen Grüßen

Albertus
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.
Antworten