Seite 1 von 2

Werte aus einer Liste zusammenfassen

Verfasst: Freitag 26. April 2013, 06:56
von mit
Hi,
Wie ist es möglich die Position in der Liste pos zusammen zufassen und als output 2579, 3033 zubekommen?

Code: Alles auswählen

class Pos():
    def __init__(self, start, end):
        self.start = start
        self.end = end

pos = [Pos(2579, 2712), Pos(2813, 2945), Pos(3013, 3033)]
Vielen Dank im Vorraus

Re: Werste aus einer Liste zusammenfassen

Verfasst: Freitag 26. April 2013, 07:04
von Sirius3
Hi mit,

ich gehe mal davon aus, Du willst das Minimum aller Startwerte und das Maximum aller Endwerte.
Was hast Du schon ausprobiert?

Wie Du eine Liste durchgehst weißt Du?
Wie Du aus den Startwerten eine neue Liste erzeugst, weißt Du auch?
Wie man das Minimum einer Liste findet, weißt Du ebenso?
Das selbe für das Maximum?
Falls eine der Fragen nein ist, schau in einem beliebigen Anfängertutorium nach.

Wenn Du irgendwo stecken bleibst, kannst Du gerne nochmal nachfragen.

Grüße
Sirius

Re: Werste aus einer Liste zusammenfassen

Verfasst: Freitag 26. April 2013, 07:14
von snafu
@mit:
Falls die Angaben bereits vorsortiert sind (wie in deinem Beispiel), dann so:

Code: Alles auswählen

class Pos():
    def __init__(self, start, end):
        self.start = start
        self.end = end

positions = [Pos(2579, 2712), Pos(2813, 2945), Pos(3013, 3033)]

total = Pos(positions[0].start, positions[-1].end)

Re: Werste aus einer Liste zusammenfassen

Verfasst: Freitag 26. April 2013, 08:03
von BlackJack
Falls die Sortierung nicht gegeben ist wollte ich noch kurz auf die eingebauten Funktionen `min()` und `max()` hinweisen.

Re: Werste aus einer Liste zusammenfassen

Verfasst: Samstag 27. April 2013, 01:46
von mit
Danke fuer die Loesungen und es war ein gut zu sortieren:

Code: Alles auswählen

import operator

class Pos():
    def __init__(self, start, end):
        self.start = start
        self.end = end

    def __str__(self):
        print self.start, self.end

pos = [Pos(2813, 2945), Pos(2579, 2712), Pos(3013, 3033)]

pos.sort(key=operator.attrgetter('start'))

print pos[0].start, pos[-1].end

Re: Werste aus einer Liste zusammenfassen

Verfasst: Samstag 27. April 2013, 07:57
von Sirius3

Code: Alles auswählen

total = Pos(min(p.start for p in pos), max(p.end for p in pos))

Re: Werste aus einer Liste zusammenfassen

Verfasst: Samstag 27. April 2013, 09:31
von xeike
Ohne in neue Datenstrukturen zu überführen und ohne weitere Module, lediglich Pos um ein passendes get() erweitert:

Code: Alles auswählen

min_x, max_y = pos[0].get()
for p in pos[1:]:
    x, y = p.get()
    min_x, max_y = min(x, min_x), max(y, max_y)
Alternativ könnte man min und max auch schon beim Einsammeln aufnehmen:

Code: Alles auswählen

class PosListe:

    sammlung = []

    def __init__(self, posObj ):
        self.minx, self.maxy = posObj.get()
        self.sammlung.append(posObj)

    def hinzu(self, posObj):
        x, y = posObj.get()
        self.minx, self.maxy = min(x, self.minx), max(y, self.maxy)
        self.sammlung.append(posObj)

    def print_sammlung(self):
        for p in self.sammlung:
            print(p.get())
        print(self.minx, self.maxy)
(oder direkt von list ableiten?)

Xe

Re: Werste aus einer Liste zusammenfassen

Verfasst: Samstag 27. April 2013, 10:17
von BlackJack
@xeike: Beides IMHO keine schönen Lösungen. Das `get()` ist nicht wirklich notwendig.

In der ersten Lösung wird eine Kopie von fast der gesamten Liste angelegt.

Bei der zweiten Lösung ist `sammlung` ein *Klassenattribut*, das ist ziemlich sicher ein Fehler. Und im Gegensatz zu einer Liste muss man zwingend ein Objekt hinein tun und alle anderen dann mit einer Methode hinzufügen. Wenn man eine Menge an gleichberechtigen Positionen hat, fände ich das komisch eine anders behandeln zu müssen.

Hier eine Lösung die mit jedem iterierbaren Objekt funktioniert, dass mindestens eine Position liefert und auch nur einmal über die Daten geht um Minimum und Maximum zu bestimmen:

Code: Alles auswählen

class Position(object):
    def __init__(self, start, end):
        self.start = start
        self.end = end


def main():
    positions = [
        Position(2579, 2712), Position(2813, 2945), Position(3013, 3033)
    ]
    result = reduce(
        lambda (a, b), (c, d): (min(a, c), max(b, d)),
        ((p.start, p.end) for p in positions)
    )
    print result


if __name__ == '__main__':
    main()

Re: Werste aus einer Liste zusammenfassen

Verfasst: Samstag 27. April 2013, 10:40
von Sirius3
@xeike: neben dem was BlackJack schon geschrieben hat, »x« und »y« sind nicht wirklich die richtigen Bezeichnet, da in der Klasse ja schon »start« und »end« verwendet werden. Was machst Du, wenn Du wieder ein Element aus der Liste entfernen willst?

Das pärchenweise »min« und »max« finde ich nicht wirklich schön:

Code: Alles auswählen

import collections
Position = collections.namedtuple('Position','start,end')

def min_max(a, b):
    return min(a), max(b)

def main():
    positions = [
        Position(2579, 2712), Position(2813, 2945), Position(3013, 3033)
    ]
    result = min_max(*zip(*((p.start, p.end) for p in positions)))
    # oder: result = min_max(*zip(*positions))
    print result

if __name__ == '__main__':
    main()

Re: Werste aus einer Liste zusammenfassen

Verfasst: Samstag 27. April 2013, 11:05
von snafu
Meine Erfahrung ist ja, dass in Python ausformulierte Funktionalität oftmals ein besseres Laufzeitverhalten zeigt im Vergleich zu Funktionsaufrufen innerhalb von Schleifen. Daher mein Lösungsvorschlag (falls Laufzeitverhalten eine Rolle spielt):

Code: Alles auswählen

from collections import namedtuple
from itertools import islice

Pos = namedtuple('Pos', 'start, end')

def get_total(positions):
    if not positions:
        raise ValueError('need at least one position')
    min_start, max_end = positions[0].start, positions[0].end
    for pos in islice(positions, 1, len(positions)):
        if pos.start < min_start:
            min_start = pos.start
        if pos.end > max_end:
            max_end = pos.end
    return Pos(min_start, max_end)

def main():
    positions = [Pos(2813, 2945), Pos(2579, 2712), Pos(3013, 3033)]
    print get_total(positions)

if __name__ == '__main__':
    main()

Re: Werste aus einer Liste zusammenfassen

Verfasst: Samstag 27. April 2013, 11:11
von xeike
BlackJack hat geschrieben:@xeike: Beides IMHO keine schönen Lösungen. Das `get()` ist nicht wirklich notwendig.

In der ersten Lösung wird eine Kopie von fast der gesamten Liste angelegt.
Ach verflixt stimmt. :oops: Ich wollte die Liste einfach nur durchlaufen. Merke schon, ich programmiere im Moment zu viel in einer anderen Sprache.

Re: Werste aus einer Liste zusammenfassen

Verfasst: Samstag 27. April 2013, 11:21
von Sirius3
@snafu: Deine Lösung funktioniert aber auch nur mit Containerobjekten die Indizierung erlauben.

Code: Alles auswählen

def get_total(positions):
    positions = iter(positions)
    first = next(positions)
    min_start, max_end = first.start, first.end
    for pos in positions:
        if pos.start < min_start:
            min_start = pos.start
        if pos.end > max_end:
            max_end = pos.end
    return Pos(min_start, max_end)

Re: Werste aus einer Liste zusammenfassen

Verfasst: Samstag 27. April 2013, 11:43
von snafu
@Sirius3: Jepp. Deins ist nochmal ein stückweit flexibler. Jetzt müsste man die mutmaßlich bessere Laufzeit nur noch durch Messungen belegen. Vielleicht bastel ich da gleich mal was...

Re: Werste aus einer Liste zusammenfassen

Verfasst: Samstag 27. April 2013, 11:58
von xeike
Sirius3 hat geschrieben:@xeike: Was machst Du, wenn Du wieder ein Element aus der Liste entfernen willst?
Vielleicht so etwas:

Code: Alles auswählen

class PosListe(list):
    def __init__(self):
        list.__init__(self)
        self.min = None
        self.max = None

    def append(self, elem):
        if self.min is not None:
            self.min = min(self.min, elem.start)
            # dann ist auch max definier
            self.max = max(self.max, elem.end)
        else:
            self.min = elem.start
            self.max = elem.end
        super().append(elem)

    def remove(self, elem):
        if elem in self:
            if self.min == elem.start or self.max == elem.end:
                super().remove(elem)
                self.min = min(punkt.start for punkt in self)
                self.max = max(punkt.end for punkt in self)
            else:
                super().remove(elem)
Ja, remove() ist teuer.

Xe

Re: Werste aus einer Liste zusammenfassen

Verfasst: Samstag 27. April 2013, 12:09
von xeike
Sirius3 hat geschrieben:

Code: Alles auswählen

if pos.start < min_start:
    min_start = pos.start
Das ist doch genau:

Code: Alles auswählen

min_start = min(pos_start, min_start)
Und eben dieses finde ich leichter zu lesen.


Xe

Re: Werte aus einer Liste zusammenfassen

Verfasst: Samstag 27. April 2013, 12:16
von Sirius3
@Xeike: es ging snafu darum, den schnellsten Code zu finden, und ein Vergleich ist nunmal schneller als ein Funktionsaufruf.

Re: Werste aus einer Liste zusammenfassen

Verfasst: Samstag 27. April 2013, 12:19
von snafu
@xeike: Abgesehen von der schlechten Laufzeit deiner `.remove()`-Methode: Was passiert wohl, wenn jemand die Listenelemente über Indexzugriff via `del` löscht (`del posliste[42]`)? Oder wenn man Elemente mit `.extend()` anfügen möchte? Ingesamt wird das in einer total komplexen Implementierung enden, wenn man alles, was `list` kann, in einer eigenen Klasse umsetzen möchte. Wieder mal ein schönes Beispiel, warum es meistens keine so gute Idee ist, von "fremden" Klassen zu erben - Mixins, abstrakte Klassen bzw generell welche, wo Vererbung explizit erlaubt / vorgesehen ist, mal ausgenommen.

Re: Werte aus einer Liste zusammenfassen

Verfasst: Samstag 27. April 2013, 12:35
von snafu
Bezüglich `min(x, y)` vs. `if x < y:`: Ja, es ist lesbarer, leichter zu schreiben und im Grunde auch weniger fehleranfällig, wenn man eine fertige Funktion nimmt. Ich habe das nur so gemacht, weil 1 Million Vergleiche nunmal schneller sind, als 1 Million Funktionsaufrufe, wo der Vergleich dann auch nochmal gemacht werden muss. Gerade CPython glänzt nicht gerade dabei, wenn Funktionen *sehr häufig* aufgerufen werden müssen, da diesbezüglich keine Optimierung (z.B. durch Inlining) stattfindet und die zusätzliche Arbeit, die intern hinter Funktionsaufrufen steckt, in der Summe (also bei vielen Wiederholungen) eben spürbar ins Gewicht fallen kann. Wohlgemerkt, das bezieht sich jetzt nur auf Funktionsaufrufe in Schleifen, wo potenziell mit vielen Aufrufen gerechnet werden muss. Und den Fall haben wir hier IMHO.

Re: Werte aus einer Liste zusammenfassen

Verfasst: Samstag 27. April 2013, 12:46
von BlackJack
@snafu: Echt? Das war eine Liste mit drei `Pos`-Objekten. Lass es ein paar mehr in der tatsächlichen Anwendung sein. Solange kein messbares Problem vorliegt, würde ich erst einmal mit einer einfacheren, kürzeren Variante arbeiten.

Re: Werte aus einer Liste zusammenfassen

Verfasst: Samstag 27. April 2013, 12:59
von snafu
@BlackJack: Gut, man kann jetzt mit dem Argument kommen, dass es sich um verfrühte Optimierungen handelt. Falls man den Code nur für den internen Gebrauch schreibt und sicher ist, dass man es nur mit überschaubaren Mengen an Positionsobjekten zu tun bekommen wird, dann ist man mit `min()` / `max()` sicherlich besser beraten. Es kommt auf den konkreten Anwendungsfall an. Als allgemein zugängliche Bibliotheksfunktion würde ich persönlich das jedenfalls nicht so schreiben.