la conversion d'une liste d'entiers en gamme en python
y a-t-il quelque chose existant en python qui peut convertir une liste croissante d'entiers en une liste de plages
E. g. compte tenu de la série {0, 1, 2, 3, 4, 7, 8, 9, 11} je veux obtenir { {0,4}, {7,9}, {11,11} }.
je peux écrire un programme pour le faire, mais je veux savoir s'il y a une fonction intégrée en python
9 réponses
utilisant itertools.groupby()
produit une mise en œuvre concise mais délicate:
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]))
sortie:
[(0, 4), (7, 9), (11, 11)]
vous pouvez utiliser un list comprehension avec une generator expression et une combinaison de enumerate () et 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]]
Première", 151930920" va construire les tuples à partir de la liste des éléments et de leurs index respectifs:
>>> [t for t in enumerate(l)]
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 7), (6, 8), (7, 9), (8, 11)]
puis groupby()
regroupera ces tuples en utilisant le différence entre leur indice et leur valeur (qui sera égale pour les valeurs consécutives):
>>> [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),)]
à partir de là, nous n'avons qu'à construire des listes à partir des valeurs du premier et du dernier tuples de chaque groupe (qui seront les mêmes si le groupe ne contient qu'un seul élément).
vous pouvez également utiliser [(t[0][1], t[-1][1]) ...]
pour construire une liste de tuples de gamme au lieu de listes imbriquées, ou même ((t[0][1], t[-1][1]) ...)
pour transformer l'expression entière en une itérable generator
qui va construire paresseusement la gamme tuples à la volée.
c'est une amélioration par rapport au très élégant @juanchopanza réponse . Celui-ci couvre non-unique et non-triés entrée et est python3 compatible aussi:
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]
exemple:
>>> 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)]
ce générateur:
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]))
produit ces résultats:
[(0, 4), (7, 9), (11, 11)]
[(0, 4), (7, 9), (11, 11)]
[(1, 1)]
[(2, 4)]
[(0, 0), (2, 4)]
[(1, 1)]
Remarque que les séries de chiffres répétés obtenir compressé . Je ne sais pas si c'est ce que vous voulez. Dans la négative, remplacer le >
par un !=
.
je comprends votre question. J'ai regardé dans itertools
et essayé de penser à une solution qui pourrait être fait en quelques lignes de Python, ce qui aurait qualifié comme "presque un construit dans " , mais je ne pouvais pas venir avec quoi que ce soit.
Génération de la gamme de paires:
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
exemple:
>>> 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)]
comme générateur:
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)
exemple:
>>> 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'
rien d'incorporé, ni dans aucune bibliothèque que je connaisse. Pas très utile, je sais, mais je n'ai jamais rencontré quelque chose comme ce que vous voulez.
Voici quelques idées pour votre programme atleast (en C++, mais il peut vous donner d'autres idées):
dans le cas où il n'y a pas de telle fonctionnalité en python, voici une implémentation
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])
Mettre plus courte:
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)))
je pense que les autres réponses sont difficiles à comprendre, et probablement inefficace. J'espère que c'est plus facile et plus rapide.
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