2011-01-07 17 views
18

C'è qualcosa che esiste in python che può convertire un elenco crescente di numeri interi in una lista gammaconversione di una lista di interi in range in pitone

Ad es dato il set {0, 1, 2, 3, 4, 7, 8, 9, 11} voglio ottenere {{0,4}, {7,9}, {11,11}}.

posso scrivere un programma per fare questo, ma vogliono sapere se v'è una funzione incorporata in Python

+1

Quasi la stessa domanda è stato chiesto e ha risposto in http://stackoverflow.com/questions/3429510/pythonic-way-to-convert-a-list-of-integers-into-a-string-of -comma-range-range/3430231 # 3430231 – Apalala

+1

'>>> import this' – Apalala

+0

Bene, posso dire con certezza che non conosco tale funzione. È molto più difficile dire con certezza che qualcosa di cui non sono a conoscenza non esiste ... –

risposta

25

Utilizzando itertools.groupby produce una concisa ma difficile realizzazione:

import itertools 

def ranges(i): 
    for a, b in itertools.groupby(enumerate(i), lambda (x, y): y - x): 
     b = list(b) 
     yield b[0][1], b[-1][1] 

print list(ranges([0, 1, 2, 3, 4, 7, 8, 9, 11])) 

uscita:

[(0, 4), (7, 9), (11, 11)] 
+1

Questo è veramente utile, mi chiedo se potresti spiegare come funziona questo metodo, quindi posso capire la funzionalità. questo sarebbe fantastico se possibile. – openCivilisation

+0

Per gestire l'input non univoco e non ordinato surround 'i' con 'ordinati (set (i))', consultare: https://stackoverflow.com/a/43091576/1201614 – luca

+0

Questa ricetta è disponibile anche in 'more_itertools .consecutive_groups'. Vedi dimostrazione [qui] (https://stackoverflow.com/a/47642650/4531270). – pylang

1

Nulla built-in, o in qualsiasi libreria, che io sappia. Non molto utile, lo so, ma non ho mai incontrato nulla di simile.

Ecco alcune idee per il vostro atleast programma (in C++, ma può darvi alcune altre idee):

Converting sets of integers into ranges

1

Nel caso non v'è alcuna funzionalità quali in python, qui è un'implementazione

p = [] 
last = -2                
start = -1 

for item in list: 
    if item != last+1:       
     if start != -1: 
      p.append([start, last]) 
     start = item 
    last = item 

p.append([start, last]) 
2

Questo generatore:

def ranges(p): 
    q = sorted(p) 
    i = 0 
    for j in xrange(1,len(q)): 
     if q[j] > 1+q[j-1]: 
      yield (q[i],q[j-1]) 
      i = j 
    yield (q[i], q[-1]) 

sample = [0, 1, 2, 3, 4, 7, 8, 9, 11] 
print list(ranges(sample)) 
print list(ranges(reversed(sample))) 
print list(ranges([1])) 
print list(ranges([2,3,4])) 
print list(ranges([0,2,3,4])) 
print list(ranges(5*[1])) 

P roduces questi risultati:

[(0, 4), (7, 9), (11, 11)] 
[(0, 4), (7, 9), (11, 11)] 
[(1, 1)] 
[(2, 4)] 
[(0, 0), (2, 4)] 
[(1, 1)] 

Nota che corre di numeri ripetuti ottenere compressa. Non so se è quello che vuoi. In caso contrario, cambiare > in un !=.

Ho capito la tua domanda. Ho esaminato itertools e ho cercato di pensare a una soluzione che potesse essere eseguita in un paio di righe di Python, che sarebbe stato qualificato come "quasi integrato", ma non riuscivo a inventarmi nulla.

7

è possibile utilizzare un list comprehension con un generator expression e una combinazione di enumerate() e itertools.groupby():

>>> import itertools 
>>> l = [0, 1, 2, 3, 4, 7, 8, 9, 11] 
>>> [[t[0][1], t[-1][1]] for t in 
... (tuple(g[1]) for g in itertools.groupby(enumerate(l), lambda (i, x): i - x))] 
[[0, 4], [7, 9], [11, 11]] 

Innanzitutto, enumerate() costruirà tuple dalle voci di elenco e il loro rispettivo indice:

>>> [t for t in enumerate(l)] 
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 7), (6, 8), (7, 9), (8, 11)] 

Poi groupby() volontà gruppo tali tuple utilizzando la differenza tra il loro indice e il loro valore (che sarà uguale per valori consecutivi):

>>> [tuple(g[1]) for g in itertools.groupby(enumerate(l), lambda (i, x): i - x)] 
[((0, 0), (1, 1), (2, 2), (3, 3), (4, 4)), ((5, 7), (6, 8), (7, 9)), ((8, 11),)] 

da lì, abbiamo solo bisogno di costruire liste dai valori della prima e dell'ultima tuple di ogni gruppo (che sarà lo stesso se il gruppo contiene un solo articolo).

È inoltre possibile utilizzare [(t[0][1], t[-1][1]) ...] per costruire una lista di tuple gamma invece di liste annidate, o anche ((t[0][1], t[-1][1]) ...) di trasformare l'intera espressione in un iterabile generator che pigramente costruire le tuple gamma al volo.

1

metterlo brevi:

ranges=lambda l:map(lambda x:(x[0][1],x[-1][1]),map(lambda (x,y):list(y),itertools.groupby(enumerate(l),lambda (x,y):x-y))) 
+2

Più breve non è un miglioramento, secondo me. – madth3

1

Generazione coppie range:

def ranges(lst): 
    s = e = None 
    r = [] 
    for i in sorted(lst): 
     if s is None: 
      s = e = i 
     elif i == e or i == e + 1: 
      e = i 
     else: 
      r.append((s, e)) 
      s = e = i 
    if s is not None: 
     r.append((s, e)) 
    return r 

Esempio:

>>> lst = [1, 5, 6, 7, 12, 15, 16, 17, 18, 30] 
>>> print repr(ranges(lst)) 
[(1, 1), (5, 7), (12, 12), (15, 18), (30, 30)] 

come generatore:

def gen_ranges(lst): 
    s = e = None 
    for i in sorted(lst): 
     if s is None: 
      s = e = i 
     elif i == e or i == e + 1: 
      e = i 
     else: 
      yield (s, e) 
      s = e = i 
    if s is not None: 
     yield (s, e) 

Esempio:

>>> lst = [1, 5, 6, 7, 12, 15, 16, 17, 18, 30] 
>>> print repr(','.join(['%d' % s if s == e else '%d-%d' % (s, e) for (s, e) in gen_ranges(lst)])) 
'1,5-7,12,15-18,30' 
3

Si tratta di un miglioramento rispetto al molto elegante @juanchopanza answer. Questo copre non univoco e non ordinato di ingresso ed è python3 compatibile:

import itertools 

def to_ranges(iterable): 
    iterable = sorted(set(iterable)) 
    for key, group in itertools.groupby(enumerate(iterable), 
             lambda t: t[1] - t[0]): 
     group = list(group) 
     yield group[0][1], group[-1][1] 

Esempio:

>>> x 
[44, 45, 2, 56, 23, 11, 3, 4, 7, 9, 1, 2, 2, 11, 12, 13, 45] 

>>> print(list(to_ranges(x))) 
[(1, 4), (7, 7), (9, 9), (11, 13), (23, 23), (44, 45), (56, 56)] 
0

penso che le altre risposte sono difficili da capire, e probabilmente inefficiente. Spero che questo sia più facile e più veloce.

def ranges(ints): 
    ints = sorted(set(ints)) 
    range_start = previous_number = ints[0] 
    for number in ints[1:]: 
     if number == previous_number + 1: 
      previous_number = number 
     else: 
      yield range_start, previous_number 
      range_start = previous_number = number 
    yield range_start, previous_number 
Problemi correlati