【问题标题】:group list of ints by continuous sequence按连续序列分组整数列表
【发布时间】:2012-05-02 19:36:46
【问题描述】:

我有一个整数列表...

[1,2,3,4,5,8,9,10,11,200,201,202]

我想将它们分组到一个列表列表中,其中每个子列表都包含序列未被破坏的整数。像这样……

[[1,5],[8,11],[200,202]]

我有一个相当笨重的工作......

lSequenceOfNum = [1,2,3,4,5,8,9,10,11,200,201,202]

lGrouped = []
start = 0
for x in range(0,len(lSequenceOfNum)):
    if x != len(lSequenceOfNum)-1:
        if(lSequenceOfNum[x+1] - lSequenceOfNum[x]) > 1:
            lGrouped.append([lSequenceOfNum[start],lSequenceOfNum[x]])
            start = x+1

    else:
        lGrouped.append([lSequenceOfNum[start],lSequenceOfNum[x]])
print lGrouped

这是我能做的最好的。有没有更“pythonic”的方式来做到这一点?谢谢..

【问题讨论】:

  • 考虑跳跃的位置而不是范围的位置。您可以将结果存储在一个简单的整数数组中,其中每个条目都是对应于原始数组中的跳转的索引。我认为这更简单......而且很有可能这将是可重用的或库代码,您可以将所有这些封装在一个类的工作中。
  • 我很确定这是重复的,虽然我现在找不到它。

标签: python


【解决方案1】:

假设列表始终按升序排列:

from itertools import groupby, count

numberlist = [1,2,3,4,5,8,9,10,11,200,201,202]

def as_range(g):
    l = list(g)
    return l[0], l[-1]

print [as_range(g) for _, g in groupby(numberlist, key=lambda n, c=count(): n-next(c))]

【讨论】:

    【解决方案2】:

    我意识到我有点过于复杂了,手动计算比使用稍微复杂的生成器要容易得多:

    def ranges(seq):
        start, end = seq[0], seq[0]
        count = start
        for item in seq:
            if not count == item:
                yield start, end
                start, end = item, item
                count = item
            end = item
            count += 1
        yield start, end
    
    print(list(ranges([1,2,3,4,5,8,9,10,11,200,201,202])))
    

    制作:

    [(1, 5), (8, 11), (200, 202)]
    

    这个方法很快:

    这种方法(和旧的方法几乎完全一样):

    python -m timeit -s "from test import ranges" "ranges([1,2,3,4,5,8,9,10,11,200,201,202])"
    1000000 loops, best of 3: 0.47 usec per loop
    

    Jeff Mercado's Method:

    python -m timeit -s "from test import as_range; from itertools import groupby, count" "[as_range(g) for _, g in groupby([1,2,3,4,5,8,9,10,11,200,201,202], key=lambda n, c=count(): n-next(c))]"
    100000 loops, best of 3: 11.1 usec per loop
    

    这快了 20 倍以上 - 当然,除非速度很重要,否则这不是一个真正的问题。


    我使用生成器的旧解决方案:

    import itertools
    
    def resetable_counter(start):
        while True:
            for i in itertools.count(start):
                reset = yield i
                if reset:
                    start = reset
                    break
    
    def ranges(seq):
        start, end = seq[0], seq[0]
        counter = resetable_counter(start)
        for count, item in zip(counter, seq): #In 2.x: itertools.izip(counter, seq)
            if not count == item:
                yield start, end
                start, end = item, item
                counter.send(item)
            end = item
        yield start, end
    
    print(list(ranges([1,2,3,4,5,8,9,10,11,200,201,202])))
    

    制作:

    [(1, 5), (8, 11), (200, 202)]
    

    【讨论】:

    • @Abhijit 很确定,我测试过了。你发现它失败了吗?
    • 嗯,不确定,但 o/p 不是预期的。你能看看这个IDEONE RUN
    • @Abhijit 刚刚检查,这似乎是 Python 2.x 与 3.x 的问题。在 3.x 下它工作正常...我会尝试找出原因。
    • 当然,zip() 在 2.x 中并不懒惰 - 你需要 itertools.izip() - 现在是 Fixed
    【解决方案3】:

    您可以通过三个步骤有效地做到这一点

    给定

    list1=[1,2,3,4,5,8,9,10,11,200,201,202]
    

    计算不连续性

         [1,2,3,4,5,8,9,10,11 ,200,201,202]
    -      [1,2,3,4,5,8,9 ,10 ,11 ,200,201,202]
    ----------------------------------------
           [1,1,1,1,3,1,1 ,1  ,189,1  ,1]
    (index) 1 2 3 4 5 6 7  8   9   10  11 
                    *          *
    rng = [i+1 for i,e in enumerate((x-y for x,y in zip(list1[1:],list1))) if e!=1] 
    >>> rng
    [5, 9]
    

    添加边界

    rng = [0] + rng + [len(list1)]
    >>> rng
    [0, 5, 9,12]
    

    现在计算实际的连续性范围

    [(list1[i],list1[j-1]) for i,j in zip(list2,list2[1:])]
    [(1, 5), (8, 11), (200, 202)]
    
    LB                [0,   5,    9,  12]
    UB             [0, 5,   9,    12]
         -----------------------
    indexes (LB,UB-1) (0,4) (5,8) (9,11)
    

    【讨论】:

      【解决方案4】:

      这个问题很老了,但我想我还是会分享我的解决方案

      假设import numpy as np

      a = [1,2,3,4,5,8,9,10,11,200,201,202]
      
      np.split(a, array(np.add(np.where(diff(a)>1),1)).tolist()[0])
      

      【讨论】:

        【解决方案5】:

        伪代码(需要修复一个错误):

        jumps = new array;
        for idx from 0 to len(array)
        if array[idx] != array[idx+1] then jumps.push(idx);
        

        我认为这实际上是一种使用索引(如在 C 中,在 java/python/perl/etc 对此进行改进之前)而不是数组中的对象的情况。

        【讨论】:

          【解决方案6】:

          这是一个应该易于阅读的版本:

          def close_range(el, it):
              while True:
                  el1 = next(it, None)
                  if el1 != el + 1:
                      return el, el1
                  el = el1
          
          def compress_ranges(seq):
              iterator = iter(seq)
              left = next(iterator, None)
              while left is not None:
                  right, left1 = close_range(left, iterator)
                  yield (left, right)
                  left = left1
          
          list(compress_ranges([1, 2, 3, 4, 5, 8, 9, 10, 11, 200, 201, 202]))
          

          【讨论】:

            【解决方案7】:

            类似问题:
            Python - find incremental numbered sequences with a list comprehension
            Pythonic way to convert a list of integers into a string of comma-separated ranges

            input = [1, 2, 3, 4, 8, 10, 11, 12, 17]
            
            i, ii, result = iter(input), iter(input[1:]), [[input[0]]]
            for x, y in zip(i,ii):
                if y-x != 1:
                    result.append([y])
                else:
                    result[-1].append(y)
            
            >>> result
            [[1, 2, 3, 4], [8], [10, 11, 12], [17]]
            
            >>> print ", ".join("-".join(map(str,(g[0],g[-1])[:len(g)])) for g in result)
            1-4, 8, 10-12, 17
            
            >>> [(g[0],g[-1])[:len(g)] for g in result]
            [(1, 4), (8,), (10, 12), (17,)]
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2022-11-10
              • 2017-05-04
              • 1970-01-01
              • 2011-06-29
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多