找到一系列重复元素的更优雅的方法

2024-04-27 16:55:11 发布

您现在位置:Python中文网/ 问答频道 /正文

我有这个问题。你知道吗

作为l一个只包含0和1的列表,查找表示1的重复序列的开始和结束的所有元组。 示例

l=[1,1,0,0,0,1,1,1,0,1]

回答:

[(0,2),(5,8),(9,10)]

我用下面的代码解决了这个问题,但是我认为它非常混乱,我想知道是否有更干净的方法来解决这个问题(也许使用map/reduce?)你知道吗

from collections import deque
def find_range(l):
    pairs=deque((i,i+1) for i,e in enumerate(l) if e==1)
    ans=[]
    p=[0,0]
    while(len(pairs)>1):
        act=pairs.popleft()
        nex=pairs[0]
        if p==[0,0]:
            p=list(act)
        if act[1]==nex[0]:
            p[1]=nex[1]
        else:
            ans.append(tuple(p))
            p=[0,0]
    if(len(pairs)==1):
        if p==[0,0]:
            ans.append(pairs.pop())
        else:
            ans.append((p[0],pairs.pop()[1]))
    return ans

Tags: 代码示例列表lenif序列popact
3条回答

代码:

a = [[l.index(1)]]
[l[i] and len(a[-1])==2 and a.append([i]) or l[i] or len(a[-1])==1 and a[-1].append(i) for i in range(len(l))]

输出:

[[0, 2], [5, 8], [9]]

^{}魔法

from itertools import groupby

lst = [1, 1, 0, 0, 0, 1, 1, 1, 0, 1]
indices, res = range(len(lst)), []
for k, group in groupby(indices, key=lambda i: lst[i]):
    if k == 1:
        group = list(group)
        sl = group[0], group[-1] + 1
        res.append(sl)
print(res)

输出:

[(0, 2), (5, 8), (9, 10)]

或使用更有效的发电机功能:

def get_ones_coords(lst):
    indices = range(len(lst))
    for k, group in groupby(indices, key=lambda i: lst[i]):
        if k == 1:
            group = list(group)
            yield group[0], group[-1] + 1

lst = [1, 1, 0, 0, 0, 1, 1, 1, 0, 1]
print(list(get_ones_coords(lst)))   # [(0, 2), (5, 8), (9, 10)]

作为一个简短的奖励,这里有另一种numpy方法,虽然很复杂,但它是基于连续数字之间的离散差(^{})和提取非零项的索引(^{}):

In [137]: lst = [1,1,0,0,0,1,1,1,0,1]                                                                                        

In [138]: arr = np.array(lst)                                                                                                

In [139]: np.flatnonzero(np.diff(np.r_[0, arr, 0]) != 0).reshape(-1, 2)                                                      
Out[139]: 
array([[ 0,  2],
       [ 5,  8],
       [ 9, 10]])

代码:

l=[1,1,0,0,0,1,1,1,0,1]   

indices = [ind for ind, elem in enumerate(l) if elem == 1]
diff = [0]+[x - indices[i - 1] for i, x in enumerate(indices)][1:]
change_ind = [0]+[i for i, change in enumerate(diff) if change > 1]+[len(indices)]
split_indices = [tuple(indices[i:j]) for i,j in zip(change_ind,change_ind[1:])]
proper_tuples = [(tup[0], tup[-1]) if len(tup)>2 else tup for tup in split_indices]

print(proper_tuples)

逻辑:

  1. indices是索引列表,其中l元素=1=>;[0, 1, 5, 6, 7, 9]
  2. diff计算上面找到的索引之间的差异,并在开始处附加一个0以保持它们的长度相同=>;[0, 1, 4, 1, 1, 2]
  3. change_ind表示需要进行拆分的位置,对应于diff大于1的位置。还要附加第一个索引和最后一个索引以供以后使用,否则您将只有中间元组=>;[0, 2, 5, 6]
  4. split_indices基于change_ind中连续元素中指示的范围创建元组(使用zip创建范围的组合)=>;[(0, 1), (5, 6, 7), (9,)]
  5. 最后,proper_tuples循环通过在split_indices中创建的元组,并确保如果元组的长度大于2,则只考虑第一个和最后一个元素,否则保持原样=>;[(0, 1), (5, 7), (9,)]

输出:

[(0, 1), (5, 7), (9,)]

最终意见:

尽管这与OP在原问题中的建议不符:

[(0,2),(5,8),(9,10)]

它确实更有逻辑意义,似乎遵循OP在评论中指出的。你知道吗

例如,在l的开头有两个1—因此元组应该是(0, 1)而不是(0, 2),以匹配建议的(start, end)表示法。你知道吗

同样地,在结尾只有一个元组-因此与之对应的元组是(9,)而不是(9, 10)

相关问题 更多 >