Seite 1 von 2

Komplexeres sortieren

Verfasst: Montag 17. August 2009, 00:12
von Yogi
Hi, ich komme da gerade nicht weiter und bräuchte einen kleinen Push in die richtige Richtung.

Ich habe da folgende Listen:

nodeIDs, filenames, headlines, coordinates[coord{'x' : x, 'y' : y}], nodelabels, sources, comments

nodeIDs beinhaltet Strings von n0 bis n...
filenames können je nach nodeID-Zuweisung auch mehrfach vorhanden sein.

Ich brauche jetzt eine ganz bestimmte Sortierung, ohne, dass etwas durcheinandergerät ;)

1. Nach filenamen sortieren
2. Aufsteigend nach y-Koordinate sortieren (von - nach +)
3. Aufsteigend nach x-Koordinate sortieren ( von - bis +, also von links nach rechts)

Sollte ich das besser in eine Datenbank wie SQlite überführen?
Die Koordinaten sind in einem Dictionary innerhalb einer Liste. Jut so?

Wie gesagt, ich hänge da was fest und würde mich wirklich ungemein über qualifizierte Hilfen freuen :wink:

Verfasst: Montag 17. August 2009, 01:00
von EyDu
Hallo.
Es ist spät, ich bin müde und habe wohl gerade noch so die wesentlichen Sachen verstanden: Schau dir mal den key- und cmp-Parameter der sort-Methode an.

Je nach Menge der Daten und Komplexität der Abfragen bietet sich natürlich auch immer eine Datenbank an.

Verfasst: Montag 17. August 2009, 07:25
von BlackJack
@Yogi: Falls "Ich habe folgende Listen:" bedeutet, dass Du da verschiedene Listen hast, bei denen Elemente am gleichen Index eigentlich zusammengehören, dann ist das IMHO schon mal ein schwerer Entwurfsfehler. Wenn die Daten zusammengehören, dann sollte man *eine* Liste verwenden und die Elemente an den jeweiligen Indizes zu je einem Objekt zusammenfassen.

Verfasst: Montag 17. August 2009, 13:49
von Yogi
@Blackjack: Gesagt getan. Die Variablenstruktur ist noch offen. Ich musste erst einmal die xml so parsen, dass ich aus der Datei die für mich relevanten Teile extrahieren konnte. Also alles offen.

Ich habe es mal nach folgendem Schema versucht:

Code: Alles auswählen

liste [['nodeID','Dateiname',x,y]] # die restlichen Werte sind für die Sortierung erst einmal unwesentlich

liste.sort(key = lambda t:t[2])

 etc..
Problem da, -t[1] oder -t[2] geht aber -t[0] geibt einen Fehler aus.

Und wie kriege ich nach mehreren Kriterien sortiert?

Verfasst: Montag 17. August 2009, 14:26
von lunar
Bei zusammenhängenden Daten, die nach einem festgelegten Muster sortiert werden sollen, würde ich ein "namedtuple" erstellen, von dieser Klasse ableiten, und dort die ganzen Vergleichsoperatoren entsprechend überladen. Dann kann man auf eine Liste dieser Typen einfach ".sort()" anwenden.

Verfasst: Montag 17. August 2009, 14:36
von Yogi
lunar hat geschrieben:Bei zusammenhängenden Daten, die nach einem festgelegten Muster sortiert werden sollen, würde ich ein "namedtuple" erstellen, von dieser Klasse ableiten, und dort die ganzen Vergleichsoperatoren entsprechend überladen. Dann kann man auf eine Liste dieser Typen einfach ".sort()" anwenden.
Ahhhhhhja! ???(das ist mir noch was hoch...), aber ich habe gerade von operator.itemgetter erfahren, scheint auch damit möglich zu sein, oder übersehe ich da was?

Verfasst: Montag 17. August 2009, 14:44
von snafu
@Yogi: collections.namedtuple()

Erster Treffer bei Google nach Eingabe von "namedtuple". :)

Verfasst: Montag 17. August 2009, 14:51
von Yogi
snafu hat geschrieben:@Yogi: collections.namedtuple()

Erster Treffer bei Google nach Eingabe von "namedtuple". :)
mag ja alles sein, klingt trotzdem nicht nach einer Grundrezeptur. Aber durchlesen werde ich es mir natürlich trotzdem. Aber gibt es denn keine Standard-Lösung, die ich einfach nur übersehen habe? Wenn nicht, dann muss ich natürlich tiefer in die Materie einsteigen.

Verfasst: Montag 17. August 2009, 15:53
von lunar
Du kannst ähnliches auch erreichen, in dem Du du das "key"-Argument von "sorted()" und "list.sort()" entsprechend setzt. Dieses Argument erwartet eine Funktion, welche das eigentliche Listenelement als Argument erhält und ein sortierbares Objekt zurückgibt. Da Tupel meines Wissens lexikalisch sortiert werden, könntest Du aus deinen Elementen in dieser Funktion Tupel erzeugen, bei denen das Datum, nach dem als erstes sortiert werden soll, an erster Stelle steht, das nächste Datum an zweiter Stelle, usw.

Faktisch aber duplizierst du damit den Inhalt der Liste, da das Resultat der "key"-Funktion für jedes Element für die Dauer des Sortierens vorgehalten werden muss. Außerdem muss dieses "key"-Argument für jedes Element erzeugt werden. Es dürfte daher zumindest aus Sicht des Speicherverbrauchs effizienter sein, den Vergleich selbst zu implementieren. So schwer ist das Überladen von ".__gt__()" und ".__lt__()" ja auch nicht.

Wenn deine Liste direkt Tupel enthält, kannst Du durch eine Änderung der Reihenfolge unter Umständen erreichen, dass diese Liste direkt sortiert werden kann. Das kann dann aber natürlich umfangreiche Änderungen nach sich ziehen. Außerdem halte ich persönlich diesen Ansatz für wenig elegant, da die Sortierbarkeit so von der eigentlich ja beliebigen Reihenfolge abhängt. Der Ansatz über ein "namedtuple" oder eine eigene Klasse mit klar definierten Vergleichskriterien erscheint mir da sauberer.

Such Dir die Lösung aus, die Dir am besten gefällt, aber einfacher geht es halt nicht. Der mehrfache Aufruf von ".sort()" mit unterschiedlichen "key"s funktioniert nun mal nicht.

Verfasst: Montag 17. August 2009, 16:09
von Yogi
@lunar: also nix mit itemgetter? Hmm schade, dachte das bisschen sortieren wäre ein Klacks ud ich wäre fertig mit meinem Konverter, aber so isses halt. Werde mich dann mal in namedtuples einarbeiten...

Danke schon mal, kann sein, dass ich mich nochmal melde ;)

Edit: Nochmal davon gekommen wie's aussieht :)

Code: Alles auswählen

    liste = [['Anton',-50,-100],['Marlene',50,-200],['Anton',-10,-50],['Anton',-5,-50]]
    liste.sort(key=operator.itemgetter(0,2,1))
    print liste
Direkt mal mit den realen Daten ausprobieren.

Verfasst: Montag 17. August 2009, 20:09
von lunar
@Yogi: Ich wusste nicht, dass "operator.itemgetter()" auch Tupel erzeugen kann. Insofern kannst du auch "operator.itemgetter()" verwenden, um die Liste zu sortieren, allerdings hat diese Lösung natürlich den erwähnten Nachteil, dass die Liste, die Du sortieren möchtest, faktisch duplizierst.

Verfasst: Montag 17. August 2009, 20:44
von BlackJack
@lunar: Das gilt jetzt aber nur für die letzten Beispiele. Im ersten Beitrag stehen ja noch mehr Daten pro Eintrag statt nur dieser 3 nach denen sortiert werden soll.

Verfasst: Montag 17. August 2009, 23:15
von Yogi
Klapp bis jetzt super - ausser- bei der Sortierung wird ja nach Groß-und Kleinschreibung sortiert, das ist ja blöd. Kann ich denn mit itemgetter irgendwo noch ein .lower() unterbringen, oder muß ich generell alle filenames kleingeschrieben halten? :(

Verfasst: Montag 17. August 2009, 23:36
von lunar
Du kannst doch eine eigene Funktion schreiben:

Code: Alles auswählen

def sortable_tuple(item):
    return tuple(item[i].lower() for i in (0, 2, 1))

Verfasst: Montag 17. August 2009, 23:52
von Yogi
lunar hat geschrieben:Du kannst doch eine eigene Funktion schreiben:

Code: Alles auswählen

def sortable_tuple(item):
    return tuple(item[i].lower() for i in (0, 2, 1))
Sorry, das kapiere ich nicht und es läuft bei mir auch nicht. Gibt folgende Fehlermeldung: AttributeError: 'list' object has no attribute 'lower'


Ich hab das ganze jetzt erst einmal ganz unpythonisch so gelöst. Sieht böse aus, aber läuft.

Echt krank, sorry :oops:

Code: Alles auswählen

    for x in xrange(len(nodeIDs)):
        ren.append([filenames[x].lower(), nodeIDs[x], filenames[x], coordsX[x], coordsY[x]])
    ren.sort(key=itemgetter(0,4,3))
    for x in xrange(len(nodeIDs)):
        del ren[x][0]

Verfasst: Dienstag 18. August 2009, 00:05
von lunar
@Yogi: Die Funktion war als Ersatz für "operator.itemgetter()" gedacht, und sollte als "key"-Argument an "sorted()" oder ".sort()" übergeben werden. Du solltest nicht die zu sortierende Liste selbst übergeben! Die Indizes waren dem Beispiel in Deinem vorherigen Beitrag abgeschaut.

Verfasst: Dienstag 18. August 2009, 00:13
von EyDu
Dir ist schon klar, dass man direkt über Element iterieren kann? Immer wenn du

Code: Alles auswählen

xrange(len(nodeIDs))
einsetzt, solltest du dir Gedanken machen was du dort tust. Lösungen sind enumerate und zip.

Bist du dir wirklich sicher, dass du in der zweiten Schleife das tust, was du möchtest? Wenn du ein Element aus einer Liste löscht, dann verschieben sich die Indizes der Elemente danach um -1.

Verfasst: Dienstag 18. August 2009, 00:18
von Yogi
@lunar: Achsoooo. Aber ich verstehe trotzdem noch nicht genau was die Funktion macht.

Ich übergebe doch diese Liste an deine Funktion:

liste = [nodeIDs[x], filenames[x], coordsX[x], coordsY[x]]

dann geht doch diene Funktion die angegebenen Elemente durch(1,3,2) und führt eine lower() Operation für diese durch und gibt am Ende eine Tulpel für eine Zeile wieder. Raff ich nicht, wo da die Sortierung ist. Ist wohl schon zu spät....

Verfasst: Dienstag 18. August 2009, 00:23
von lunar
@Yogi: Lies Dir doch bitte die Dokumentation zu "sorted()" oder "list.sort()" durch. Die Funktion sortiert natürlich nicht, aber das soll sie auch gar nicht ("operator.itemgetter()" sortiert auch nicht). Die "key"-Funktion wird vom Sortieralgorithmus einmal für jedes Element aufgerufen, um für dieses Element ein sortierbares Datum zu erzeugen. Die Funktion erhält also ein einzelnes Listenelement, nimmt vom diesem die Elemente 0, 2 und 1, konvertiert sind in Kleinschreibung und gibt das so erzeugte Tupel zurück. Dieses Tupel wird dann vom Sortieralgorithmus verwendet, um die eigentliche Liste zu sortieren.

Verfasst: Dienstag 18. August 2009, 00:25
von Yogi
EyDu hat geschrieben:Dir ist schon klar, dass man direkt über Element iterieren kann? Immer wenn du

Code: Alles auswählen

xrange(len(nodeIDs))
einsetzt, solltest du dir Gedanken machen was du dort tust. Lösungen sind enumerate und zip.

Bist du dir wirklich sicher, dass du in der zweiten Schleife das tust, was du möchtest? Wenn du ein Element aus einer Liste löscht, dann verschieben sich die Indizes der Elemente danach um -1.
@EyDu: Gott sei Dank geht es da nicht um Geschwindigkeit, nur auf das was hinten raus kommt ;) Und sagen wir so, ich weiß was ich möchte und komme auch dorthin. Das es bessere Möglichkeiten gibt, ist auf jeden Fall sehr wahrscheinlich ;)

Zum zweiten Absatz, jo, da ist alles so in Ordnung. Am Ende schreibe ich die Inhalte in die einzelnen Dateien rein.