2015-01-30 16 views
5

Ho un elenco con valori ripetuti come mostrato di seguito:valore pista cambiamenti in un elenco ripetitivo in Python

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

Questo elenco viene generato da un modello corrispondente espressione regolare (non illustrato qui). L'elenco è garantito per avere valori ripetuti (molte, molte ripetizioni - centinaia, se non migliaia), e non è mai organizzato in modo casuale perché è quello che la regex corrisponde ogni volta.

Quello che voglio è quello di monitorare gli indici della lista in cui le voci cambiano dal valore precedente. Pertanto, per l'elenco sopra riportato x, desidero ottenere un elenco di rilevamento delle modifiche [3, 6] che indica che x[3] e x[6] sono diversi dalle loro voci precedenti nell'elenco.

Sono riuscito a farlo, ma mi chiedevo se c'era un modo più pulito. Ecco il mio codice:

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 

uscita: [3, 6]

Domanda: C'è un modo più pulito per fare quello che voglio, in un minor numero di righe di codice?

+0

ben guardarlo si potrebbe sbarazzarsi di 'lag' da solo utilizzando' index-1' nella vostra seconda istruzione if, e cambiare il secondo caso a '=' e in questo modo si può cadere! il resto e sposta tale codice fino al valore –

+0

@JamesKent Questa è una buona idea. Ho aggiornato la domanda e il codice. Grazie. – prrao

+0

Hai già 'item', quindi non hai più bisogno di accedere a' x [index] 'per il confronto a' x [index-1] ' –

risposta

6

Esso può essere fatto utilizzando una lista di comprensione, con una funzione di range

>>> 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

Grazie, è molto leggibile e la risposta più ovvia credo! – prrao

+0

@prrao Prego. Tutto il meglio della vita ... –

2

invece multi-indicizzazione che ha O(n) complessità è possibile utilizzare un iteratore per verificare l'elemento successivo nella lista:

>>> 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

Questo è un runtime quadratico, e non gestisce il caso '[1, 1, 1, 2, 2, 1, 1, 1]' correttamente. –

+0

@SvenMarnach +1, ero contrario all'uso di 'set' proprio per questo motivo. – prrao

+0

@SvenMarnach sì, hai ragione, risolto! – Kasramvd

2

Sono qui per aggiungere la risposta obbligatoria che contiene una lista di comprensione.

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

Bene le persone sono veloci, questa non era nemmeno la prima risposta alla comprensione dell'elenco ... di gran lunga! – Roberto

+0

Buon ans bro ... Non è la velocità che è importante, è la qualità. E lo inchiodi. –

+0

@Roberto +1 Funziona benissimo, ma continuo a darlo alla risposta con 'range' per una migliore leggibilità. Grazie a tutti! – prrao

3

si può fare qualcosa di simile utilizzando itertools.izip, itertools.tee e un elenco-di comprensione:

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] 

Un'altra alternativa utilizzando itertools.groupby su enumerate(x). groupby gruppi articoli simili insieme, quindi tutti abbiamo bisogno è l'indice della prima voce di ogni gruppo ad eccezione della prima:

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] 

Se NumPy è un'opzione:

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

Stavo pensando 'lista (accumula (len (lista (g)) per k, g in groupby (x))) [: - 1]' prima che tornassi in me .. – DSM

1

itertools.izip_longest è quello che stai cercando per:

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) 
Problemi correlati