50 votes

convertir une liste d'entiers en plage en python

Existe-t-il quelque chose en python qui puisse convertir une liste croissante d'entiers en une liste d'intervalles ?

Par exemple, étant donné l'ensemble {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 voudrais savoir s'il existe une fonction intégrée dans Python.

0 votes

Eh bien, je peux dire avec assurance que je ne connais pas une telle fonction. Il est beaucoup plus difficile de dire avec certitude que quelque chose dont je n'ai pas connaissance n'existe pas.....

1 votes

Presque la même question a été posée et répondue dans stackoverflow.com/questions/3429510/

0 votes

Je pense que le résultat que vous proposez devrait en fait être une liste de plages cf. ma réponse ci-dessous !

63voto

Utilisation de itertools.groupby() produit une implémentation concise mais délicate :

import itertools

def ranges(i):
    for a, b in itertools.groupby(enumerate(i), lambda pair: pair[1] - pair[0]):
        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)]

16voto

Frédéric Hamidi Points 123646

Vous pouvez utiliser un compréhension de la liste avec un expression de générateur et une combinaison de énumérer() y 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]]

D'abord, enumerate() va construire des tuples à partir des éléments de la liste et de leur index respectif :

>>> [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 la différence entre leur index 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à, il suffit de 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 plage au lieu de listes imbriquées, ou même ((t[0][1], t[-1][1]) ...) pour transformer l'expression entière en un itérable generator qui construira paresseusement les tuples de la gamme à la volée.

15voto

luca Points 61

Il s'agit d'une amélioration par rapport à la très élégante réponse . Celui-ci couvre non unique y non trié 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)]

4voto

Curt Points 161

Génération de paires de gammes :

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

En tant que 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'

3voto

Apalala Points 2999

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

Notez que les séries de nombres répétés obtiennent comprimé . Je ne sais pas si c'est ce que vous voulez. Si non, changez le > à un != .

Je comprends votre question. J'ai regardé dans itertools et j'ai essayé de trouver une solution qui pourrait être réalisée en quelques lignes de Python, ce qui aurait été qualifié de "presque un intégré" mais je n'ai rien trouvé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X