kompliziertes Sortieren mehrerer Listen

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.
Mandos
User
Beiträge: 6
Registriert: Dienstag 21. Oktober 2008, 19:25

kompliziertes Sortieren mehrerer Listen

Beitragvon Mandos » Dienstag 21. Oktober 2008, 19:48

Hallo,
ich bin nicht so der python-crack und bei etwas komplizierterem Sortieren geht mir die Fantasie aus.
Folgendes Problem: Ich hab eine Liste, die z.B. so aussehen kann:

['2,17', '20,6','8,33-34.', '8,33', '8,37-59', '1,42','8,33-35','8,44.45', '2,10', '2,13','5']

Diese Liste möchte ich gerne sortieren und zwar erst nach der Zahl vor dem Komma und dann nach der Zahl nach dem Komma...das würde in diesem Beispiel dann so aussehen:

['1,42', '2,10', '2,13', '2,17', '5', '8,33', '8,33-34', '8,33-35', '8,37-59', '8,44.45', '20,6']

Dadurch, dass das strings sind ist die list.sort()-Funktion etwas blöd, weil er dann sowas produziert:
['1,42', '2,10', '2,13', '2,17', '20,6', '5', '8,33', '8,33-34', '8,33-35', '8,37-59', '8,44.45']

Da ist dann '20,6' nicht am Ende sondern in der Mitte, weil er halt nicht die Zahlen erkennt und nur ein Zeichen nach dem anderen vergleicht. Nun hatte ich versucht das in zwei Listen zu teilen - eine mit dem ersten Teil (vor dem Komma) und die andere mit dem zweiten Teil enthalten, aber weiter wusste ich dann auch nicht wirklich und in int kann man das mit den Bindestrichen und Punkten ja auch nicht umwandeln.
Dazu kommt noch, dass ich eine zweite Liste mit Zahlen habe, die der ersten List zugeordnet sind. Wenn also die erste (komplexere) Liste umgeordnet wird, dann müsste die zweite parallel umgeordnet werden...
Im Forum bin ich auf das hier gestoßen:

Code: Alles auswählen

# Paralleles Sortieren von zwei Listen
# Autor: Dieter Schön
# Quelle: http://www.xing.com/app/forum?op=showarticles;id=2003500

a = ['3','2','4','1']
b = ['uschi','peter','karsten','jens']

def parallel_sort_zipzip(list1, list2):
    tuples = zip(list1, list2)
    tuples.sort()
    return zip(*tuples)
   
a,b = parallel_sort_zipzip(a, b)


Das ist schon ganz hilfreich, aber das Problem mit den strings bleibt noch.
Viele Worte, aber ich glaube so schwer ist das gar nicht, wenn man nur weiß wies geht ;)
Weiß es hier jemand?
acoolon
User
Beiträge: 27
Registriert: Samstag 2. August 2008, 20:16

Beitragvon acoolon » Dienstag 21. Oktober 2008, 20:56

Code: Alles auswählen

a = ['2,17', '20,6','8,33-34.', '8,33', '8,37-59', '1,42','8,33-35','8,44.45', '2,10', '2,13','5']
b = [ i.split(',') for i in a]
b = [ [int(i[0]),i[-1]] for i in b]
b.sort()
c = [ [i[0],b.index(i)] for i in b if str(i[0]) == i[-1]]
a = [str(i[0])+','+i[-1] for i in b]
if c:
   for i in c:
      a[i[-1]] = i[0]
print a

das ist murks ohne Ende, aber es bringt bei mir das Ergebnis welches du willst... :)


mfg acoolon
Mandos
User
Beiträge: 6
Registriert: Dienstag 21. Oktober 2008, 19:25

Beitragvon Mandos » Dienstag 21. Oktober 2008, 21:16

Vielen Dank! Ich versteh zwar nicht alles, aber es funktioniert ganz gut.
Wie kann ich das jetzt mit dem parallelen sortieren Verknüpfen?
acoolon
User
Beiträge: 27
Registriert: Samstag 2. August 2008, 20:16

Beitragvon acoolon » Dienstag 21. Oktober 2008, 21:36

Code: Alles auswählen

def parallel_sort_zipzip(list1, list2):
    tuples = zip(list1, list2)
    tuples.sort()
    return zip(*tuples)

x = [1,2,3,4,5,6,7,8,9,10,11]
a =['2,17', '20,6','8,33-34.', '8,33', '8,37-59', '1,42','8,33-35','8,44.45', '2,10', '2,13','5']

b = [ i.split(',') for i in a]
b = [ [int(i[0]),i[-1]] for i in b]
b,x = parallel_sort_zipzip(b, x)
b = list(b)
c = [ [i[0],b.index(i)] for i in b if str(i[0]) == i[-1]]
a = [str(i[0])+','+i[-1] for i in b]
if c:
   for i in c:
      a[i[-1]] = i[0]

print a,x


Wir nehmen an, dass a die komplexe Liste und x die einfache Liste ist.

Code: Alles auswählen

b,x = parallel_sort_zipzip(b, x)
b = list(b)


sollte laufen...

;)
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

Beitragvon numerix » Dienstag 21. Oktober 2008, 22:16

acoolon hat geschrieben:das ist murks ohne Ende, aber es bringt bei mir das Ergebnis welches du willst... :)


Das ist nicht nur Murks, sondern funktioniert auch nicht richtig.
Zum einen ist die '5' in deiner Ergebnisliste ein Integerwert, zum anderen wird nicht richtig sortiert, wenn einstellige Angaben nach dem Komma folgen. Ersetze mal "2,17" durch "2,8" ...

Eine mögliche Lösung könnte so aussehen:

Code: Alles auswählen

def stellenwert(s):
    s = s+",1" if not "," in s else s.replace("-",",").replace(".",",")
    return "".join([v.zfill(3) for v in s.split(",")])

stellen = ['2,8', '20,6','8,33-34', '8,33', '8,37-59', '1,42','8,33-35','8,44.45', '2,10', '2,13','5']
stellen.sort(key=stellenwert)
print stellen


Liefert:

Code: Alles auswählen

['1,42', '2,8', '2,10', '2,13', '5', '8,33', '8,33-34', '8,33-35', '8,37-59', '8,44.45', '20,6']


Sollte meine Vermutung richtig sein und es sich bei den Daten um Bibelstellenangaben handeln, dann funktioniert meine Variante auch mit weiteren möglichen Variationen, wie z.B. "8,21-31.34.41" u.ä.
acoolon
User
Beiträge: 27
Registriert: Samstag 2. August 2008, 20:16

Beitragvon acoolon » Dienstag 21. Oktober 2008, 22:31

:roll:

deswegen sagte ich ja auch murks...da es soweit hingebogen wurde, bis es fuer den Fall lief ;)
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Beitragvon Leonidas » Dienstag 21. Oktober 2008, 23:00

Also so kompliziert ists nun auch nicht.

Code: Alles auswählen

import re
l = ['2,17', '20,6','8,33-34.', '8,33', '8,37-59', '1,42','8,33-35','8,44.45', '2,10', '2,13','5']
numbers = re.compile(r'\d+')
divided = [(map(int, numbers.findall(e)), index) for index, e in enumerate(l)]
divided.sort()
new_l = [l[index] for head, index in divided]
print new_l


In Zeile 5 werden alle Zahlen aus dem Listeneintrag geholt und zu richtigen Integern gemacht. Diese werden zusammen mit dem ursprünglichen Listeneintrag in einer temporären Liste abgespeichert. Diese wird dann sortiert und da wir nun Integer haben auch richtigherum. Dann wird die nun sortierte Liste durchgegangen und wir holen das Ursprüngliche Listenelement über den Index den wir uns gemerkt haben zurück, diesmal aber mit der richtigen Sortierung.

Man kann divided auch etwas zusammenfassen in dem man dort statt LCs GEs verwendet und statt dem expliziten sortieren das ``sorted()`` builtin:

Code: Alles auswählen

import re
l = ['2,17', '20,6','8,33-34.', '8,33', '8,37-59', '1,42','8,33-35','8,44.45', '2,10', '2,13','5']
numbers = re.compile(r'\d+')
divided = sorted((map(int, numbers.findall(e)), index) for index, e in enumerate(l))
new_l = [l[index] for head, index in divided]
print new_l
My god, it's full of CARs! | Leonidasvoice vs Modvoice
Mandos
User
Beiträge: 6
Registriert: Dienstag 21. Oktober 2008, 19:25

Beitragvon Mandos » Dienstag 21. Oktober 2008, 23:39

numerix hat geschrieben:Sollte meine Vermutung richtig sein und es sich bei den Daten um Bibelstellenangaben handeln, dann funktioniert meine Variante auch mit weiteren möglichen Variationen, wie z.B. "8,21-31.34.41" u.ä.


Ja, deine Vermutung stimmt. ;)
Wie kann ich dann jetzt die zweite Liste parallel zur ersten ordnen?

Danke für eure Mühe.
Benutzeravatar
snafu
User
Beiträge: 5389
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Beitragvon snafu » Dienstag 21. Oktober 2008, 23:41

Ein guter Anfang wäre es vielleicht, die zweite Liste mal zu zeigen. ;)

Aus dem Bauch heraus würde ich ein Dictionary empfehlen.
BlackJack

Beitragvon BlackJack » Mittwoch 22. Oktober 2008, 08:25

@Mandos: Mit `zip()` die zusammengehörigen Elemente beider Listen in Tupel packen, die Schlüsselfunktion so abändern, dass sie auf das Element mit der Bibelstelle zugreift, und dann eventuell die Tupel wieder auseinander nehmen und auf zwei Listen aufteilen.

Das das alles ein wenig kompliziert aussieht, liegt vielleicht auch daran, dass die Bibelstelle als Zeichenkette schlecht zu verarbeiten ist und das es keine so gute Idee ist, zusammengehörige Information in "parallelen" Listen zu speichern.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Beitragvon sma » Mittwoch 22. Oktober 2008, 08:30

Code: Alles auswählen

a = ['2,17', '20,6','8,33-34.', '8,33', '8,37-59', '1,42','8,33-35','8,44.45', '2,10', '2,13','5']
b = range(len(a))
c = zip(a, b)
c.sort(key=lambda o: int(o[0].split(",")[0]))
print c

Stefan
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Beitragvon Leonidas » Mittwoch 22. Oktober 2008, 08:35

Hat das ``range()`` und ``zip()`` irgendeinen Unterschied gegenüber ``enumerate()``? Jetzt mal von der Position vom Index mal abgesehen, der in dem Fall sowieso nicht so relevant ist?
My god, it's full of CARs! | Leonidasvoice vs Modvoice
BlackJack

Beitragvon BlackJack » Mittwoch 22. Oktober 2008, 08:38

@sma: Das berücksichtigt jetzt aber nur die Zahl vor dem Komma.

@Leonidas: `b` sollen Dummydaten sein, die die zweite, parallel zu sortierende Liste darstellen. Ich vermute mal in "echt" lässt sich das nicht einfach durch `enumerate()` ersetzen.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Beitragvon sma » Mittwoch 22. Oktober 2008, 08:50

BlackJack hat geschrieben:@sma: Das berücksichtigt jetzt aber nur die Zahl vor dem Komma.

so hatte ich die Aufgabe interpretiert. Der zweite Teil war zum Teil ja gar keine Zahl oder fehlte.

Wenn man den zweiten Teil auch haben will, könnte sowas funktionieren:

Code: Alles auswählen

def keyfunc(o):
    a = o[0].split(",")
    return int(a[0]), a[1] if len(a) > 1 else ''
c.sort(key=keyfunc)


BlackJack hat geschrieben:@Leonidas: `b` sollen Dummydaten sein, die die zweite, parallel zu sortierende Liste darstellen. Ich vermute mal in "echt" lässt sich das nicht einfach durch `enumerate()` ersetzen.

Genau.

Stefan
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Beitragvon Leonidas » Mittwoch 22. Oktober 2008, 08:56

BlackJack hat geschrieben:`b` sollen Dummydaten sein, die die zweite, parallel zu sortierende Liste darstellen. Ich vermute mal in "echt" lässt sich das nicht einfach durch `enumerate()` ersetzen.

Ah, dadurch macht es wesentlich mehr Sinn. Ich für meinen Teil wäre zu faul eine Dummy-Liste zu machen, wenn der OP die Liste nicht von selbst verrät ;)
My god, it's full of CARs! | Leonidasvoice vs Modvoice

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder