2012-05-02 10 views
11

mam listę liczb całkowitych ...lista grupa wskazówki przez ciągłą sekwencję

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

chciałbym grupa je na liście, gdzie każdy list podlistę zawiera liczby całkowite, których kolejność nie zostały złamane. Tak ...

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

mam raczej przylegający obejść ...

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 

Jest to najlepsze co mogłem zrobić. Czy istnieje jakiś "pythonic" sposób to zrobić? Dzięki ..

+0

myśleć o tym w kategoriach gdzie skoki są zamiast gdzie zakresy są. Możesz przechowywać wyniki w prostej tablicy int, gdzie każdy wpis jest indeksem odpowiadającym skokowi w oryginalnej tablicy. Myślę, że to jest prostsze ... i przy braku szansy, że będzie to wielokrotnego użytku lub kodu bibliotecznego można zamknąć to wszystko w działaniach klasy. – djechlin

+0

Jestem prawie pewien, że jest to duplikat, chociaż nie mogę tego teraz szukać. – jamylak

Odpowiedz

12

Zakładając listę zawsze będą w kolejności rosnącej:

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))] 
+0

Idealny! Dzięki. – b10hazard

0

pseudokod (z błędami off-by-one do ustalenia):

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

Myślę, że to rzeczywiście przypadek, w którym warto pracować z indeksami (jak w C, przed java/python/perl/etc. ulepszone na ten temat) zamiast obiektów w tablicy.

0

Oto wersja, które powinny być łatwe do odczytania:

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])) 
4

zdałem sobie sprawę, że zbyt skomplikowana to trochę, o wiele łatwiej liczyć tylko ręcznie niż stosowanie lekko zwichrowanych generatora:

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

produkujący

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

Metoda ta jest dość szybki:

Ta metoda (i stary, wykonują one niemal dokładnie takie same):

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 

To ponad 20 razy szybciej - choć oczywiście, o ile nie ma to znaczenia, nie jest to problemem.


Moje stare rozwiązanie wykorzystujące generatory:

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

produkujące:

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

Czy na pewno to działa? – Abhijit

+0

@Ahhijit Całkiem pewien, przetestowałem to. Czy znalazłeś, że zawodzi? –

+0

Nie jestem pewien, ale o/p nie jest tym, czego należy się spodziewać. Czy możesz zajrzeć do tego [IDEONE RUN] (http://ideone.com/3sCnZ) – Abhijit

2

Można to zrobić skutecznie w trzech etapach

podanych

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

Oblicz nieciągłość

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

Dodaj granice

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

teraz obliczyć rzeczywistą ciągłość waha

[(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) 
+0

+1, ładne wyjaśnienie :) –

0

podobne pytania:
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,)] 
1

Pytanie jest dość stary, ale myślałem, że będę dzielić moje rozwiązanie i tak

Zakładając 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])