2012-11-27 48 views
2

我的零和一的名單,看起來像這樣:增量列表

lst = [0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1] 

我怎樣才能改變這個LST成這樣:

transformed_lst = lst = [0, 1, 1, 1, 1, 0, 0, 0, 2, 2, 0, 0, 0, 3, 0, 4, 4] 

基本上,在每次出現1,將其轉換爲n + 1整數。我確信有一個優雅的方式可以通過itertools/groupby/functools來完成。這是一種嘗試,但並不完全正確的:使用itertools.count()itertools.chain()itertools.groupby()

from itertools import cycle 

ints = cycle(range(len(lst))) 
transformed_lst = [next(ints) if i != 0 in lst else 0 for i in lst] 

>>> [0, 0, 1, 2, 3, 0, 0, 0, 4, 5, 0, 0, 0, 6, 0, 7, 8] 
+0

沒有,'map'不會做。它將一個函數應用於列表,但在這裏您需要一些狀態來記憶兩次調用。這不是很好地處理一個函數。發電機功能應該非常適合。 – Ber

回答

3

In [14]: from itertools import * 

In [15]: c=count(1) 

In [16]: lis=[0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1] 

In [17]: list(chain(*[list(g) if k!=1 else [next(c)]*len(list(g)) for k,g in groupby(lis)])) 

Out[17]: [0, 1, 1, 1, 1, 0, 0, 0, 2, 2, 0, 0, 0, 3, 0, 4, 4] 

在這裏你還可以代替len(list(g))

使用sum(1 for _ in g)所要求,可讀使用發電機功能的版本:

In [27]: def func(l): 
    c=count(1) 
    for k,g in groupby(l): 
     if k==1: 
      for x in [next(c)]*sum(1 for _ in g): 
       yield x 
     else: 
      for x in g: 
       yield x 
    ....:     

In [28]: list(func(lis)) 
Out[28]: [0, 1, 1, 1, 1, 0, 0, 0, 2, 2, 0, 0, 0, 3, 0, 4, 4] 
+0

光滑,我會發布一些更醜陋的東西:) – arynaq

+0

可維護性等零點。但它是一個單行:) – Ber

+0

這確實是混淆的代碼:/(但它的工作原理) –

6

你基本上有兩種狀態 - 「讀書0 s」和「讀書1的」 - 當你之間(即從那些到零)開關,然後將delta應用於後續1更迭:

reading_zeroes = True 
delta = 0 
for x in input: 
    if x: 
     reading_zeroes = False 
     x += delta 
    elif not reading_zeroes: 
     delta += 1 
     reading_zeroes = True 
    yield x 
0
>>> lst = [0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1] 
>>> transformed = [] 
>>> idx = 1 
>>> for key, grp in groupby(lst): 
... if key: 
...  transformed += [idx] * len(list(grp)) 
...  idx += 1 
... else: 
...  transformed += list(grp) 
... 
>>> transformed 
[0, 1, 1, 1, 1, 0, 0, 0, 2, 2, 0, 0, 0, 3, 0, 4, 4] 
>>> 
0

我覺得這是可讀性和簡潔之間很好地融合(不需要跟蹤狀態):

from itertools import groupby 

def transform(numbers): 
    counter = 0 
    for value, iterator in groupby(numbers): 
     if value: 
      counter += 1 

     for i in iterator: 
      if value: 
       yield counter 
      else: 
       yield 0 

pre_transformed = [0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1] 
print list(transform(pre_transformed)) 

回報:

[0, 1, 1, 1, 1, 0, 0, 0, 2, 2, 0, 0, 0, 3, 0, 4, 4] 
2
>>> lst = [0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1] 
>>> from itertools import groupby 
>>> [i//2 if k else 0 for i, (k, g) in enumerate(groupby(lst), 2) for j in g] 
[0, 1, 1, 1, 1, 0, 0, 0, 2, 2, 0, 0, 0, 3, 0, 4, 4] 
+0

+1爲嵌套iterable拆包和整體可讀性 – twneale

3

請注意,你只有一個「下一組」的時候,當前元素爲1和上一個元素爲0

previous = 0 
grp = 0 
for elem in lst: 
    if elem and not previous: 
     grp += 1 
    previous = elem 
    yield (grp if elem else 0) 
+0

我喜歡這一個。你甚至可以縮短它:'grp + = bool(elem而不是之前的)' - 儘管這可能不會更好:)。同樣的道理,你可以做'yeild grp * bool(elem)',但是可能不會更好。 – mgilson

+0

@mgilson:我正在考慮'grp * bool(elem)',但是。並不完全是自我記錄。考慮到我不知道Python,不想得到*太麻煩。 :) – cHao

+0

在這種有限的情況下('1和'0),你甚至不需要'bool' - 'grp * elem'可以正常工作。但做一般情況只是更加努力:)。無論如何,+1是一個很好的解決方案,並走高路:^) – mgilson