Liste in Sublisten unterteilen

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.
Antworten
mathematik
User
Beiträge: 28
Registriert: Dienstag 16. April 2013, 12:40

Hallo zusammen,

ich habe folgendes Problem:
meine Liste A enthält eine aufsteigende Folge von Zahlen, die aber nicht notwendigerweise aufeinander folgen.

Code: Alles auswählen

A=[1,2,4,5,6,7,10,15,16,17,19,20] 
diese Liste möchte ich nun in Sublisten zerlegen, so dass in jeder Subliste aufeinanderfolgende Zahlen enthalten sind. Sobald eine Reihe abbricht, will ich eine neue Subliste haben. Also:

Code: Alles auswählen

A=[[1,2],[4,5,6,7],[10],[15,16,17],[19,20]]
Wie kann ich so etwas möglichst geschickt machen? Wenn ich eine immer gleiche Anzahl and aufeinanderfolgenden Zahlen hätte, wäre das kein Problem. Für mein Problem hatte ich mir überlegt immer aufeinanderfolgede Zahlen als Zweierpaare zu nehmen und diese nur dann zu speichern, wenn sie aufeinander folgen und das ganze dann mit zwei Zweierpaaren usw. Dies ist allerdings ziemlich uneffektiv, wenn ich lange Listen habe...

Vielen Dank!
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@mathematik: Deine Problembeschreibung enthält doch gar keine Zweierpaare, sondern nur eine Zahl und ihren Vorgänger. Wie würdest Du das Vorgehen in Worten beschreiben: Für jede Zahl in einer Liste, nehme diese Zahl und ...
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

mathematik hat geschrieben:Wie kann ich so etwas möglichst geschickt machen?
Mit itertools. Alle coolen Dinge gehen mit itertools. :mrgreen:

Code: Alles auswählen

from itertools import count, groupby

data = [1, 2, 4, 5, 6, 7, 10, 15, 16, 17, 19, 20]
result = [list(value) for _, value in groupby(data, lambda value, c=count(): value - next(c))]
print(result)
groupby sollte man sich auf jeden Fall mal ansehen und damit experimentieren. Der lambda-Ausdruck ist gar nicht so dramatisch wie er vielleicht auf den ersten Blick aussieht. Im Endeffekt erzeugt er nur das Schlüsselkriterium für die Gruppierung. In diesem Fall ist es die Differenz des jeweiligen Wertes zu einem mitlaufenden Zähler. Solange der Abstand der aufeinander folgenden Werte 1 ist gehören sie zu einer Gruppe. Ist der Abstand größer oder kleiner so beginnt eine neue Gruppe.

Sollte das im ersten Moment etwas überwältigend wirken, so kann man sich mit folgendem Code anschauen wie das Gruppierungskriterium gebildet wird.

Code: Alles auswählen

data = [1, 2, 4, 5, 6, 7, 10, 15, 16, 17, 19, 20]
for value, counter in zip(data, count()):
    print('value - counter: {} - {} = {}'.format(value, counter, value - counter))
Ergebnis

Code: Alles auswählen

value - counter: 1 - 0 = 1
value - counter: 2 - 1 = 1
value - counter: 4 - 2 = 2
value - counter: 5 - 3 = 2
value - counter: 6 - 4 = 2
value - counter: 7 - 5 = 2
value - counter: 10 - 6 = 4
value - counter: 15 - 7 = 8
[...]
mathematik
User
Beiträge: 28
Registriert: Dienstag 16. April 2013, 12:40

Oh vielen Dank /me!
Itertools sollte ich mir doch glatt mal merken, wenn man damit alle coolen Sachen macht! :)
Und jetzt gehts ans rumspielen mit groupby :D

@Sirius3: Ich hätte mein Vorgehen folgendermaßen beschrieben:
Erster Schritt: Für jede Zahl in einer Liste, nehme diese Zahl und die auf diese folgende Zahl. Wenn die Differenz beider Zahlen 1 ist, erstelle eine Sublist aus diesen beiden Zahlen. (Damit würden dann einzeln stehende Zahlen herausfallen, was mich für meine weiteren Analysen nicht stört....)
Zweiter Schritt: Nun überprüfe für alle Sublisten, ob der letzte Eintrag einer Subliste gleich dem ersten Einrag der folgenden Subliste ist. Ist dies der Fall, fasse beide Sublisten zusammen. Wenn nicht, erhalte die Subliste.
usw.
Dies ist aber vermutlich deutlich aufwändiger als itertools. Deshalb werd ich mich diesem widmen.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

mathematik hat geschrieben:Dies ist aber vermutlich deutlich aufwändiger als itertools.
So ganz spontan könnte das etwa auf den folgenden Code hinauslaufen.

Code: Alles auswählen

data = [1, 2, 4, 5, 6, 7, 10, 15, 16, 17, 19, 20]
new_data = []
sublist = []
last_value = data[0]
for value in data:
    if value - last_value != 1:
        if sublist:
            new_data.append(sublist)
        sublist = [value]
    else:
        sublist.append(value)
    last_value = value
new_data.append(sublist)
print(new_data)
Es wird jeder Wert der Liste durchlaufen und ermittelt, ob die Differenz zwischen aktuellem Wert und folgendem Wert ungleich 1 ist. Wenn das der Fall ist wird die bestehende Subliste der Liste hinzugefügt und eine neue Subliste erstellt. Ist die Differenz 1, dann wird der Wert an die existierende Subliste angehängt. Die letzte Subliste muss man dann noch außerhalb der Schleife hinzufügen.

groupby macht eigentlich das gleiche, nur handlicher.
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@/me: jetzt hast Du meine Hausaufgaben für mathematik gemacht :P

Man kann das ganze natürlich noch etwas kompakter schreiben:

Code: Alles auswählen

data = [1, 2, 4, 5, 6, 7, 10, 15, 16, 17, 19, 20]
new_data = [[data[0]]]
for value in data[1:]:
    if value - new_data[-1][-1] != 1:
        new_data.append([])
    new_data[-1].append(value)
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Unlesbar, aber mit reduce :D

Code: Alles auswählen

reduce(
    lambda x, y: x[:-1] + [x[-1] + [y]] if x[-1][-1] + 1 == y else x + [[y]],
    data[1:],
    [[data[0]]])
Hat natürlich ein "kleines" Problem mit leeren Listen.
Das Leben ist wie ein Tennisball.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Sirius3 hat geschrieben:Man kann das ganze natürlich noch etwas kompakter schreiben:
Aber das ist doch dann gar nicht mehr so umständlich ... :wink:
Antworten