2015-01-30 4 views
5

Ich habe eine Liste mit sich wiederholenden Werte wie unten dargestellt:Spur Wertänderungen in einer repetitiven Liste in Python

x = [1, 1, 1, 2, 2, 2, 1, 1, 1] 

Diese Liste wird von einem Muster erzeugt regulären Ausdruck entspricht (hier nicht dargestellt). Die Liste hat garantiert wiederkehrende Werte (viele, viele Wiederholungen - Hunderte, wenn nicht Tausende) und wird niemals zufällig angeordnet, weil das die Regex jedes Mal abgleicht.

Was ich will, ist die Liste Indizes bilden, an denen die Einträge aus dem vorherigen Wert. Für die obige Liste x möchte ich eine Änderungsverfolgungsliste [3, 6] erhalten, die angibt, dass sich x[3] und x[6] von ihren vorherigen Einträgen in der Liste unterscheiden.

Ich schaffte es, dies zu tun, aber ich fragte mich, ob es einen saubereren Weg gab. Hier ist mein Code:

x = [1, 1, 1, 2, 2, 2, 1, 1, 1] 

flag = [] 
for index, item in enumerate(x): 
    if index != 0: 
     if x[index] != x[index-1]: 
      flag.append(index) 

print flag 

Ausgang: [3, 6]

Frage: Gibt es einen sauberen Weg zu tun, was ich will, in weniger Codezeilen?

+0

gut es bei der Suche Sie könnten von 'lag' durch die zweite, wenn auf' = 'und auf diese Weise kann man einfach mit' Index-1 'in der zweiten if-Anweisung, und ändern Sie fallen loszuwerden das else und verschiebe diesen Code bis zum if –

+0

@JamesKent Das ist eine gute Idee. Ich habe die Frage und den Code aktualisiert. Vielen Dank. – prrao

+0

Sie haben bereits 'item', so dass Sie nicht erneut auf' x [index] 'zugreifen müssen für den Vergleich mit' x [index-1] ' –

Antwort

6

Es kann mit einer Liste Verständnis getan werden, mit einer range Funktion

>>> x = [1, 1, 1, 2, 2, 2, 3, 3, 3] 
>>> [i for i in range(1,len(x)) if x[i]!=x[i-1] ] 
[3, 6] 
>>> x = [1, 1, 1, 2, 2, 2, 1, 1, 1] 
>>> [i for i in range(1,len(x)) if x[i]!=x[i-1] ] 
[3, 6] 
+0

Danke, das ist sehr lesbar, und die offensichtlichste Antwort, denke ich! – prrao

+0

@prrao Gern geschehen. Alles Gute im Leben ... –

2

statt Multi-Indizierung, die O(n) Komplexität hat man einen Iterator zu überprüfen, für das nächste Element in der Liste verwenden kann:

>>> x =[1, 1, 1, 2, 2, 2, 3, 3, 3] 
>>> i_x=iter(x[1:]) 
>>> [i for i,j in enumerate(x[:-1],1) if j!=next(i_x)] 
[3, 6] 
+4

Dies ist quadratische Laufzeit, und es behandelt nicht den Fall '[1, 1, 1, 2, 2, 2, 1, 1, 1]' richtig. –

+0

@SvenMarnach +1, ich war aus genau diesem Grund gegen die Verwendung von 'set'. – prrao

+0

@SvenMarnach ja, du hast Recht, behoben! – Kasramvd

2

Ich bin hier, um die obligatorische Antwort hinzuzufügen, die ein Listenverständnis enthält.

flag = [i+1 for i, value in enumerate(x[1:]) if (x[i] != value)] 
+1

Gut Leute sind schnell, das war nicht einmal die erste Liste Verständnis Antwort ... bei weitem! – Roberto

+0

Good ans bro ... Es ist nicht die Geschwindigkeit, die wichtig ist, es ist die Qualität. Und du nagelst es. –

+0

@Roberto +1 Das funktioniert gut, aber ich gebe es immer noch mit der Antwort "Bereich" für bessere Lesbarkeit. Danke an alle! – prrao

3

Sie können etwas tun itertools.izip verwenden, itertools.tee und eine Liste Verständnis:

from itertools import izip, tee 
it1, it2 = tee(x) 
next(it2) 
print [i for i, (a, b) in enumerate(izip(it1, it2), 1) if a != b] 
# [3, 6] 

weitere Alternative mit itertools.groupby auf enumerate(x). groupby Gruppen ähnliche Elemente zusammen, so alles, was wir brauchen, ist der Index des ersten Elements jeder Gruppe mit Ausnahme des ersten:

from itertools import groupby 
from operator import itemgetter 
it = (next(g)[0] for k, g in groupby(enumerate(x), itemgetter(1))) 
next(it) # drop the first group 
print list(it) 
# [3, 6] 

Wenn NumPy eine Option:

>>> import numpy as np 
>>> np.where(np.diff(x) != 0)[0] + 1 
array([3, 6]) 
+3

Ich dachte 'Liste (akkumulieren (len (Liste (g)) für k, g in groupby (x))) [: - 1]' bevor ich zu meinen Sinnen kam .. – DSM

1

itertools.izip_longest ist, was Sie suchen für:

from itertools import islice, izip_longest 

flag = [] 
leader, trailer = islice(iter(x), 1), iter(x) 
for i, (current, previous) in enumerate(izip_longest(leader, trailer)): 
    # Skip comparing the last entry to nothing 
    # If None is a valid value use a different sentinel for izip_longest 
    if leader is None: 
     continue 
    if current != previous: 
     flag.append(i)