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

21
demandé sur Akhil 2011-01-07 20:24:47

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)]
30
répondu Graham 2018-06-08 19:40:46

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.

9
répondu Frédéric Hamidi 2011-01-07 21:28:28

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)]
4
répondu luca 2017-10-05 08:05:41

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.

3
répondu Apalala 2011-01-07 20:14:03

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'
2
répondu Curt 2015-01-29 20:52:51

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

la Conversion des ensembles d'entiers dans les gammes

1
répondu Mark Loeser 2017-05-23 12:34:39

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])
1
répondu Akhil 2011-01-07 17:56:49

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)))
1
répondu Neuer 2012-10-29 23:53:07

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
0
répondu Mike Amy 2017-05-10 06:04:00