2015-01-30 18 views
5

tôi có một danh sách với các giá trị lặp đi lặp lại như hình dưới đây:Theo dõi thay đổi giá trị trong một danh sách lặp đi lặp lại bằng Python

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

Danh sách này được tạo ra từ một mô hình phù hợp với biểu thức chính quy (không hiển thị ở đây). Danh sách được đảm bảo có giá trị lặp lại (nhiều, nhiều lần lặp lại - hàng trăm, nếu không phải hàng nghìn) và không bao giờ được sắp xếp ngẫu nhiên vì đó là những gì regex phù hợp với từng thời điểm.

Điều tôi muốn là theo dõi chỉ mục danh sách mà tại đó các mục nhập thay đổi từ giá trị trước đó. Vì vậy, đối với danh sách trên x, tôi muốn có danh sách theo dõi thay đổi [3, 6] cho biết rằng x[3]x[6] khác với các mục nhập trước đó của chúng trong danh sách.

Tôi đã làm được điều này, nhưng tôi đã tự hỏi liệu có cách nào sạch hơn không. Dưới đây là mã của tôi:

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 

Output: [3, 6]

Câu hỏi: Có cách nào sạch hơn để làm những gì tôi muốn, trong ít dòng mã?

+0

cũng nhìn vào nó, bạn có thể loại bỏ 'lag' bằng cách chỉ sử dụng' index-1' trong câu lệnh if thứ hai và thay đổi thứ hai nếu thành '! =' và cách bạn có thể thả người khác và di chuyển mã đó đến nếu –

+0

@JamesKent Đó là một ý tưởng hay. Tôi đã cập nhật câu hỏi và mã. Cảm ơn. – prrao

+0

Bạn đã có 'mục', vì vậy bạn không cần truy cập lại' x [index] 'để so sánh với' x [index-1] ' –

Trả lời

6

Nó có thể được thực hiện bằng cách sử dụng danh sách hiểu, với một chức năng 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

Cảm ơn, đó là rất dễ đọc, và câu trả lời rõ ràng nhất tôi đoán! – prrao

+0

@prrao Bạn được chào đón. Tất cả tốt nhất trong cuộc sống ... –

2

thay vì đa indexing có O(n) phức tạp, bạn có thể sử dụng một iterator để kiểm tra các phần tử tiếp theo trong danh sách:

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

Đây là thời gian chạy bậc hai, và nó không xử lý trường hợp '[1, 1, 1, 2, 2, 2, 1, 1, 1]' một cách chính xác. –

+0

@SvenMarnach +1, tôi đã chống lại việc sử dụng 'set' vì lý do này. – prrao

+0

@SvenMarnach có, bạn đã đúng, đã sửa! – Kasramvd

2

Tôi ở đây để thêm câu trả lời bắt buộc có chứa một danh sách hiểu.

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

Mọi người đều nhanh chóng, đây thậm chí không phải là câu trả lời hiểu danh sách đầu tiên ... cho đến nay! – Roberto

+0

Tốt ans bro ... Nó không phải là tốc độ đó là quan trọng, nó là chất lượng. Và bạn làm móng nó. –

+0

@Roberto +1 Điều đó hoạt động tốt, nhưng tôi vẫn đưa nó vào câu trả lời với 'dải ô' để dễ đọc hơn. Cảm ơn tất cả mọi người! – prrao

3

Bạn có thể làm một cái gì đó như thế này sử dụng itertools.izip, itertools.tee và một danh sách-hiểu:

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] 

Một thay thế sử dụng itertools.groupby trên enumerate(x). groupby nhóm mặt hàng tương tự với nhau, vì vậy tất cả chúng ta cần là chỉ số của mặt hàng đầu tiên của mỗi nhóm ngoại trừ thứ nhất:

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] 

Nếu NumPy là một lựa chọn:

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

Tôi đã suy nghĩ 'danh sách (tích lũy (len (danh sách (g)) cho k, g trong groupby (x))) [: - 1]' trước khi tôi đến giác quan của tôi .. – DSM

1

itertools.izip_longest là những gì bạn đang tìm kiếm cho:

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) 
Các vấn đề liên quan