tamaño - Convertir una lista de enteros en rango en Python
metodos de listas en python (9)
Creo que las otras respuestas son difíciles de entender, y probablemente ineficientes. Espero que esto sea más fácil y más rápido.
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
¿Existe algo en Python que pueda convertir una lista creciente de enteros en una lista de rango?
Por ejemplo, dado el conjunto {0, 1, 2, 3, 4, 7, 8, 9, 11} quiero obtener {{0,4}, {7,9}, {11,11}}.
Puedo escribir un programa para hacer esto, pero quiero saber si hay una función incorporada en Python
El uso de itertools.groupby()
produce una implementación concisa pero difícil:
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]))
Salida:
[(0, 4), (7, 9), (11, 11)]
En el caso de que Python no tenga esta característica, aquí hay una implementación
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])
Esta es una mejora sobre la muy elegante respuesta de @juanchopanza . Este cubre la entrada no única y no ordenada y también es compatible con python3 :
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]
Ejemplo:
>>> 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)]
Este generador:
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]))
Produce estos resultados:
[(0, 4), (7, 9), (11, 11)]
[(0, 4), (7, 9), (11, 11)]
[(1, 1)]
[(2, 4)]
[(0, 0), (2, 4)]
[(1, 1)]
Tenga en cuenta que las carreras de números repetidos se comprimen . No sé si eso es lo que quieres. Si no, cambie el >
a a !=
.
Entiendo tu pregunta. Busqué en itertools
e intenté pensar en una solución que se pudiera hacer en un par de líneas de Python, que se calificaría como "casi incorporada" , pero no se me ocurrió nada.
Generando pares de rango:
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
Ejemplo:
>>> 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)]
Como generador:
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)
Ejemplo:
>>> 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''
Nada incorporado, o en cualquier biblioteca que yo sepa. No es de mucha ayuda, lo sé, pero nunca he encontrado nada como lo que quieres.
Aquí hay algunas ideas para su programa al menos (en C ++, pero puede darle algunas otras ideas):
Ponlo más corto:
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)))
Puede usar una lista de comprensión con una expresión generadora y una combinación de 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]]
Primero, enumerate()
construirá tuplas a partir de los elementos de la lista y su índice respectivo:
>>> [t for t in enumerate(l)]
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 7), (6, 8), (7, 9), (8, 11)]
Luego groupby()
esas tuplas usando la diferencia entre su índice y su valor (que será igual para valores consecutivos):
>>> [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),)]
A partir de ahí, solo necesitamos crear listas a partir de los valores de la primera y la última tupla de cada grupo (que serán iguales si el grupo solo contiene un elemento).
También puede usar [(t[0][1], t[-1][1]) ...]
para crear una lista de tuplas de rango en lugar de listas anidadas, o incluso ((t[0][1], t[-1][1]) ...)
para convertir toda la expresión en un generator
iterable que generará perezosamente las tuplas de rango sobre la marcha.