Sortieren einer List mit List als Einzelinhalt

L I V E Stammtisch ab 20:30 Uhr im Chat
  • Guten Abend,

    wieder einmal habe ich eine Frage zu Python.
    Ich habe eine List mit vielen vielen Koordinateneinträgen. Diese bestehen jeweils auch aus einer List mit jeweils 3 Einzelwerten als Float.

    Nun möchte ich eine Sortierung haben, die mit dem .sort() so nicht zustande kommt.
    Wahlweise möchte ich die Haupt-List entweder auf- oder absteigend nach dem ersten Wert sortieren. Soweit ist das alles noch keine Problem.
    Jetzt möchte ich aber auch wahlweise den zweiten Eintrag auch wieder auf- oder absteigend sortieren, jedoch soll die Sortierung der vorherigen Stellen berücksichtigt bleiben.

    Habe ich nun mehrere Einträge die zB im ersten Eintrag eine 800 haben, sollen diese Werte alle zusammenhängen. Wieder zB [700, y1, z1] , [700, y2, z2] usw.
    Nun möchte ich aber das die Sortierung der ersten Stelle so bleibt, aber alle Werte die in der ersten Stelle gleich sind, anhand der zweiten Stelle zB absteigend sortiert wird.

    Ich hoffe das war verständlich genug und wie immer Danke im voraus

    es grüßt euer
    Willy

  • Ungewoehnlich daran ist, dass die Funktion nur einen Parameter bekommt. Ueblicherweise bekommt man zwei Eintraege die man vergleichen soll.

    Wird schon sehr lange nicht mehr verwendet, da es ineffizient ist. Die key funktion ist schneller.

    Die Funktion sorted bzw. Methode list.sort nutzt die an key übergebene Funktion, um die Einträge zu sortieren.

    Die key-funktion kann einen oder mehrere Werte z.B. als Tuple ausgeben. Danach wird dann sortiert. Was nicht passieren darf, dass die Key-Funktion z.B. mal int und mal str ausgibt. Die Datentypen sollten alle gleich sein.

    Hilfreich ist operator.itemgetter

    Der Funktionsaufruf des itemgetter liefert eine Funktion zurück, die das zu erwartende Objekt durch den Callback bekommt (key).

  • Guten Tag,

    Entweder ich komme mit der Parametrierung nicht klar, aber das Ergebnis ist nicht das gewünschte.
    In der ersten Sortierung soll nach Index 0 Aufsteigend sortiert werden.
    In der zweiten Sortierung soll dann aber unter Beibehaltung der vorherigen Sortierung nach Index 0 nur die Bestandteile umsortiert werden, die im Index 0 die gleichen Werte haben, dann aber deren Reihenfolge untereinander absteigend nach Index 1.

    es grüßt euer
    Willy

  • Ein einfacher ”Trick” bei Zahlen ist den zweiten Wert zu negieren, wenn der absteigend sortiert werden soll. Und ich würde trotzdem noch den dritten Wert mit in den Sortierschlüssel nehmen, damit die Sortierung wirklich komplett beschrieben ist, auch wenn einem das eigentlich nicht so wichtig ist.

    Python
    coordinates.sort(key=lambda item: (item[0], -item[1], item[2]))

    “Dawn, n.: The time when men of reason go to bed.” — Ambrose Bierce, “The Devil's Dictionary”

  • Guten Tag,

    __blackjack__ :danke_ATDE:

    Das klappt wirklich wunderbar.

    Wie müsste man das jetzt noch dahingehend modifizieren, damit das Ganze über eine Funktion nutzbar wäre ?

    table =  Sortieren(table, 'down', 'up', 'up')

    oder so ähnlich !? Damit ich von den Ausgangsparametern innerhalb des Programms über Übergabeparameter die Sortierrichtung für jede INDEX frei bestimmen kann ?

    es grüßt euer
    Willy

  • Komplett ungetestet, funktioniert natürlich nur mit Werten die man sinnvoll mit 1 bzw. -1 multiplizieren kann.

    Python
    def sort(iterable, reverse_flags):
        return sorted(
            iterable,
            key=lambda item: [
                value * (-1 if flag else 1)
                for value, flag in zip(item, reverse_flags)
            ],
        )

    Allgemeiner kann man einfach mehrfach sortieren, nach den verschiedenen Kriterien, weil der Sortieralgorithmus (garantiert) stabil ist. Das ist auch in der Python-Dokumentation im Howto zum Sortieren beschrieben.

    “Dawn, n.: The time when men of reason go to bed.” — Ambrose Bierce, “The Devil's Dictionary”

  • Nun wohl guten Morgen schon ;)

    Danke __blackjack__ :danke_ATDE:

    Ich habe mal dein ungetestet Programm / Funktion in mein Programm zu integrieren. Ja soweit man die Sortierung nach INDEX 0 & 1 macht klappt das wunderbar.
    Nur zum Test habe ich auch mal die letzte INDEX Position auf -1 gesetzt, und dass stimmt, oder klappt dann wiederum nicht.

    Wie du anhand der Ausgabe innerhalb von Thonny sehen kannst sind beide Variablen gleich. Dazu habe ich sowohl die Funktion mit sorted(), wie auch den anderen ersten Codetext aus #6 verwendet.
    Was mich hier jedoch verwundert, ist die Tatsache, wenn ich diese beiden Zuweisungen umkehre, also das -1 in die letzte dargestellte Programmzeile bei Index 1 &2 einfrage, aber bei der Funktion (1, -1 ,1) ist dann das Ergebnis bei der Vergleichsabfrage über die Kommandozeile "False" was jedoch Richtig wäre. Es müsste mit diese, fast 3000 Listeneinträgen a 3 Koordinaten wirklich False herauskommen, wenn beide richtig arbeiten würden.

    Python
    def sort(itertable, marks):
        marks = (tuple((marks,)) if (type(marks) == int else marks)
        assert len(itertable) > 0
        assert len(itertable[0]) == len(marks)
        return sorted(itertable, key=lambda item: [value * (1 if flag == 1\
                else -1) for value, flag in zip(item, marks)],)

    So sieht dazu die angepasste Funktion aus. Erkennst du irgendwo einen Fehler ?

    es grüßt euer
    Willy

  • In dem Beispiel rufst Du die Funktion falsch auf. Sowohl 1 als auch -1 ist ”wahr”. `reversed_flags` müssen Wahrheitswerte sein, wie das `reversed`-Argument von `list.sort()` und `sorted()`. Darum ist `marks` auch ein schlechterer Name dafür, weil der nicht wirklich aussagt was dieser Wert bedeutet.

    Die veränderte `sort()`-Funktion hat einen Syntaxfehler in der Zeile wo versucht wird eine einzelne Zahl in ein Tupel zu wandeln. Was ich nicht machen würde, weil zu viel Magie mit Typprüfung was Duck-Typing unterläuft. Da würde man mindestens `isinstance()` benutzen um weniger einschränkend zu sein. Und vorher war dort ein beliebiges iterierbares Objekt möglich, jetzt nicht mehr. Also eigentlich würde man versuchen sich einen Iterator geben zu lassen und falls das mit einem `TypeError` fehl schlägt, einen Iterator über das eine Element erstellen. Aber wie gesagt, IMHO zu viel Magie.

    Ein Tupel mit `tuple()` noch mal in ein Tupel umwandeln ist einmal zu viel.

    Was ebenfalls ”falsch” ist, sind die beiden ``assert``-Anweisungen die Sequenzoperationen auf etwas das `iterable` heisst machen. Iterierbare Objekte müssen keine Sequenzen sein, es muss also weder eine Länge geben, noch ein Indexzugriff möglich sein.

    Wobei der Test auf die Länge auch nicht wichtig ist, denn man kann problemlos was leeres sortieren. Kommt halt eine leere Liste bei heraus. Ist auch das was ich von so einer Funktion erwarten würde, denn `list.sort()` funktioniert auf leeren Listen und `sorted()` auf iterierbaren Objekten die keine Elemente liefern.

    Der explizite Vergleich von `flag` mit 1 statt einfach den ”Wahrheitsgehalt” von `flag` selbst zu benutzen. Zumal das an der Stelle auch nicht mehr wirklich Sinn macht wenn man eine Funktion haben will die 1 und -1 als Werte in `marks` haben will.

    Bleibt letztlich die ursprüngliche Funktion, nur mit einem schlechteren Namen für das zweite Argument und schwerer lesbar formatiert weil mitten einem bedingten Ausdruck, in einer „list comprehension“, in einer Argumentliste, umgebrochen wird, und auf keiner dieser höheren Ebenen.

    “Dawn, n.: The time when men of reason go to bed.” — Ambrose Bierce, “The Devil's Dictionary”

  • Guten Tag __blackjack__,

    Irgendwie verstehe ich deine Aussagen nicht.
    Aber lass mich das bitte im einzelnen Auseinander nehmen,

    Ein Tupel mit `tuple()` noch mal in ein Tupel umwandeln ist einmal zu viel.

    in der Zeile:

    Code
    marks = (tuple((marks,)) if type(marks) == int else marks)

    wird, falls der Übergabeparamter nur eine einzelne Ziffer enthält eine Tuple gemacht. Ansonsten bleibt alles so wie es ist marks = marks Das habe ich auch so single getestet, und diesen dem Sinn, damit die übergebenen Sortierparameter auch der Anzahl der Einträge einer Liste entspricht, die als Folge vieler in itertable übergeben wurde. Wo findet bitte nochmals eine Wandlung Tuple in Tuple statt ? Ich kann diese Aussage / Programmstelle so nicht erblicken.

    Was ebenfalls ”falsch” ist, sind die beiden ``assert``-Anweisungen die Sequenzoperationen auf etwas das `iterable` heisst machen. Iterierbare Objekte müssen keine Sequenzen sein, es muss also weder eine Länge geben, noch ein Indexzugriff möglich sein.

    Ich verstehe hiervon kein Wort.

    In dem Beispiel rufst Du die Funktion falsch auf.

    Wieso ? Und wo ? Wenn ich die Funktion sort mit (1, 1, 1) aufrufe erfolgt die Sortierung aller Index-Stellen aufsteigend. Auch das habe ich ausprobiert. Das funktioniert.

    Der explizite Vergleich von `flag` mit 1 statt einfach den ”Wahrheitsgehalt” von `flag` selbst zu benutzen. Zumal das an der Stelle auch nicht mehr wirklich Sinn macht wenn man eine Funktion haben will die 1 und -1 als Werte in `marks` haben will.

    du meinst diese Zeile: ?

    Code
    return sorted(itemtable, key=lambda item: [value * (1 if flag == 1 else -1)\
                                            for value, flag in zip(item, marks)],)

    hier habe ich den Teil schon dahingehend abgeändert, weil es mir nach meinem Post auch aufgefallen war, dass das eigentlich Unsinn ist, dass diese Zeile nun

    Code
    return sorted(itemtable, key=lambda item: [value * (-1 if flag == -1 else 1)\
                                            for value, flag in zip(item, marks)],)

    damit nur -1 zu einer absteigenden Sortierung führt, aber jede andere auch falsche Parameterzuweisung automatisch zu einer aufsteigenden Sortierung führt.

    Aber auch das ändert nichts an der Tatsache des unterschiedlichen Verhaltens.

    Ich habe nun viele Anläufe unternommen, und auch jedes Sortierergebnis händisch geprüft, ich kann die erste Stelle = Index[0] mit 1 oder -1 parametieren, die Sortierung entspricht immer der zugewiesenen Sortierrichtung. Das selbe für die 2. Stelle = Index[1] . Funktioniert rum wie num. Aber egal was ich der 3.Stelle = Index[2] zuweise, hier ist die Sortierung immer aufsteigend. Selbst (1, 1, -1) ergibt in der Sortierung immer dem Ergebnis als hätte ich (1, 1, 1) übergeben.
    Wobei, was ich mir aber nicht vorstellen kann, und auch nicht glaube das der Fehler dort zu suchen ist, das es nur eine Wert, oder Betragsänderung gibt, die die 6. Stelle nach dem Komma betrifft. Das heiß ich habe 2 List als 3er List mit Koordinaten, wo ich via Vergleich:
    table[x1][0] == table[x2][0] True erhalte, ebenso bei table[x1][1] == table[x2][1].
    Erst bei table[x1][2] == table[x2][2] erhalte ich ein False. Nehme ich nun diese beide Zahlen X1:2 und X2:2 gibt es nur in der 6. Nachkommastelle einen Unterschied.

    es grüßt euer
    Willy

  • Wo findet bitte nochmals eine Wandlung Tuple in Tuple statt ?

    Code
    was_ist_das = ()
    print(type(was_ist_das))
    <class 'tuple'>

    Also ist '(marks,)' schon vom Typ Tuple und ein 'tuple()'-Aufruf davor ist nicht notwendig.

    Ich verstehe hiervon kein Wort.

    Du überprüfst eine Länge von etwas, bei dem es Wurst ist wie lange das ist und ob die Länge überhaupt bestimmt werden kann:

    Code
    _iterator = iter((1, 2))
    len(_iterator)
    Traceback (most recent call last):
      File "<input>", line 1, in <module>
    TypeError: object of type 'tuple_iterator' has no len()
    next(_iterator)
    1
    next(_iterator)
    2

    du meinst diese Zeile: ?

    'flag' ist vermutlich 'True' oder 'False', dann brauchst du nicht 'flag == 1' abfragen sondern 'if flag:' oder 'if not flag'.

    Der Rest darf dir __blackjack__ erklären, der hat die Aufgabenstellung schon verinnerlicht 8o Ich habe das nur beim überfliegen gesehen.

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

  • ``(marks,)`` ist ein Tupel:

    Python
    In [343]: marks = None
    
    In [344]: type( (marks,) )
    Out[344]: tuple

    ``tuple((marks,))`` bekommt also ein Tupel, und liefert eins zurück.

    Re: `iterable`: `sorted()` und auch meine Funktion nehmen beliebige (endliche) iterierbare Objekte. Auch welche die weder eine Länge haben, die sich mit `len()` abfragen lässt, noch einen Indexzugriff ermöglichen. Das machen die beiden ``assert``-Anweisungen kaputt, weil die mehr von `iterable` verlangen als eigentlich notwendig ist. `sorted()`, und damit das ursprüngliche `sort()` funktioniert beispielsweise mit Datei-Objekten oder mit dem was `csv.reader()` liefert. Beide funktionieren aber nicht als Argument für `len()` oder mit einem Indexzugriff. Also sind entweder die ``assert``\s falsch, oder der Name `iterable`, weil der mehr verspricht als die Funktion hält.

    Wenn bei (1, 1, 1) alles aufsteigend sortiert wird, dann funktioniert es nicht, denn da sollte alles absteigend sortiert werden. Und das wird es auch:

    Weil bei (1, 1, 1) alles ”wahr” ist. Bei (1, -1, 1) wird auch alles absteigend sortiert, denn da ist kein Unterschied zu (1, 1, 1) was den Wahrheitswert der Elemente angeht, auch -1 ist ”wahr”. `flag` bedeutet da werden Wahrheitswerte erwartet, also `True` und `False`, und nicht irgendwelche Zahlen. Alles aufsteigend ist (False, False, False), und erstes Element aufsteigend und zweites absteigend ist (False, True, False):

    Python
    In [348]: sort(items, (False, False, False))
    Out[348]: [(1, 5, 1), (2, 1, 2), (2, 3, 0), (3, 1, 3)]
    
    In [349]: sort(items, (False, True, False))
    Out[349]: [(1, 5, 1), (2, 3, 0), (2, 1, 2), (3, 1, 3)]

    Wie gesagt habe ich mich an die Argumente von `list.sort()` und `sorted()` gehalten, die ein `reverse`-Flag kennen, und habe das einfach ”vervielfacht”, um eine bekannte Schnittstelle zu erweitern, statt was neues zu erfinden.

    ``(-1 if flag == -1 else 1)`` ist ja auch nicht wirklich sinnvoll wenn man -1 und 1 als Werte erwarten würde. Auch hier würde man nur ``flag`` schreiben statt das so asymmetrisch zu ”korrigieren”. Und `flag` passt als Name dann auch nicht mehr wirklich.

    “Dawn, n.: The time when men of reason go to bed.” — Ambrose Bierce, “The Devil's Dictionary”

  • Das sollte aus __blackjack__ 's Beitrag auch ersichtlich sein, aber es funktioniert deswegen:

    Ach und das sage nicht ich, sondern die Doku sagt, dass da Wahrheitswerte erwartet werden:

    "reverse is a boolean value. If set to True, then the list elements..."

    are sorted as if each comparison were reversed."

    https://docs.python.org/3/library/stdtypes.html#list.sort

    Edit: Etwas zulangsam, dafür mit Beispiel und Lesestoff :P

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!