web-dev-qa-db-fra.com

Comment trouver un recouvrement de plage en python?

Quel est le meilleur moyen, en Python, de déterminer quelles valeurs de deux plages se chevauchent?

Par exemple:

x = range(1,10)
y = range(8,20)

(The answer I am looking for would be the integers 8 and 9.)

Étant donné une plage, x, quel est le meilleur moyen de parcourir une autre plage, y et de générer toutes les valeurs partagées par les deux plages? Merci d'avance pour l'aide.

MODIFIER:

En guise de suivi, j'ai réalisé que je devais également savoir si x chevauchait ou non y. Je cherche un moyen de parcourir une liste de plages et de faire un certain nombre de choses supplémentaires avec des plages qui se chevauchent. Existe-t-il une simple affirmation Vrai/Faux pour accomplir cela?

24
drbunsen

Si le pas est toujours +1 (valeur par défaut de range), les éléments suivants devraient être plus efficaces que la conversion de chaque liste en un ensemble ou l'itération sur l'une des listes:

range(max(x[0], y[0]), min(x[-1], y[-1])+1)
56
Andrew Clark

Vous pouvez utiliser set s pour cela, mais sachez que set(list) supprime toutes les entrées en double de list:

>>> x = range(1,10)
>>> y = range(8,20)
>>> list(set(x) & set(y))
[8, 9]
13
plaes

Une option consiste à utiliser une compréhension de liste comme:

x = range(1,10) 
y = range(8,20) 

z = [i for i in x if i in y]
print z
9
TimothyAWiseman

Pour "si x chevauche ou non y":

for a,b,c,d in ((1,10,10,14),
                (1,10,9,14),
                (1,10,4,14),
                (1,10,4,10),
                (1,10,4,9),
                (1,10,4,7),
                (1,10,1,7),
                (1,10,-3,7),
                (1,10,-3,2),
                (1,10,-3,1),
                (1,10,-11,-5)):
    x = range(a,b)
    y = range(c,d)
    print 'x==',x
    print 'y==',y
    b = not ((x[-1]<y[0]) or (y[-1]<x[0]))
    print '    x %s y' % ("does not overlap","   OVERLAPS  ")[b]
    print

résultat

x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [10, 11, 12, 13]
    x does not overlap y

x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [9, 10, 11, 12, 13]
    x    OVERLAPS   y

x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
    x    OVERLAPS   y

x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [4, 5, 6, 7, 8, 9]
    x    OVERLAPS   y

x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [4, 5, 6, 7, 8]
    x    OVERLAPS   y

x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [4, 5, 6]
    x    OVERLAPS   y

x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [1, 2, 3, 4, 5, 6]
    x    OVERLAPS   y

x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6]
    x    OVERLAPS   y

x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [-3, -2, -1, 0, 1]
    x    OVERLAPS   y

x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [-3, -2, -1, 0]
    x does not overlap y

x== [1, 2, 3, 4, 5, 6, 7, 8, 9]
y== [-11, -10, -9, -8, -7, -6]
    x does not overlap y

Modifier 1

Comparaison des vitesses:

from time import clock

x = range(-12,15)
y = range(-5,3)
te = clock()
for i in xrange(100000):
    w = set(x).intersection(y)
print '                     set(x).intersection(y)',clock()-te


te = clock()
for i in xrange(100000):
    w = range(max(x[0], y[0]), min(x[-1], y[-1])+1)
print 'range(max(x[0], y[0]), min(x[-1], y[-1])+1)',clock()-te

résultat

                     set(x).intersection(y) 0.951059981087
range(max(x[0], y[0]), min(x[-1], y[-1])+1) 0.377761978129

Le ratio des temps d'exécution est 2.5

4
eyquem

C’est la réponse pour la plage simple avec step = 1 cas (99% du temps), ce qui peut être 2500x plus rapide comme indiqué dans l’indice de référence lors de la comparaison de plages étendues à l’aide d’ensembles (lorsque vous souhaitez simplement savoir si il y a un chevauchement):

x = range(1,10) 
y = range(8,20)

def range_overlapping(x, y):
    if x.start == x.stop or y.start == y.stop:
        return False
    return ((x.start < y.stop  and x.stop > y.start) or
            (x.stop  > y.start and y.stop > x.start))

>>> range_overlapping(x, y)
True

Pour trouver les valeurs qui se chevauchent:

def overlap(x, y):
    if not range_overlapping(x, y):
        return set()
    return set(range(max(x.start, y.start), min(x.stop, y.stop)+1))

Aide visuelle:

|  |           |    |
  |  |       |    |

Référence:

x = range(1,10)
y = range(8,20)

In [151]: %timeit set(x).intersection(y)
2.74 µs ± 11.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [152]: %timeit range_overlapping(x, y)
1.4 µs ± 2.91 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

Conclusion : même pour les petites gammes, c'est deux fois plus rapide.

x = range(1,10000)
y = range(50000, 500000)

In [155]: %timeit set(x).intersection(y)
43.1 ms ± 158 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [156]: %timeit range_overlapping(x, y)
1.75 µs ± 88.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

Conclusion : vous souhaitez utiliser la fonction range_overlapping dans ce cas car elle est 2500x plus rapide (mon enregistrement personnel en accélération)

1
PascalVKooten

Si vous voulez trouver le chevauchement des plages avec des étapes arbitraires, vous pouvez utiliser mon paquet https://github.com/avnr/rangeplus qui fournit Une classe Range () compatible avec Python range (), plus quelques goodies, y compris les intersections:

>>> from rangeplus import Range
>>> Range(1, 100, 3) & Range(2, 100, 4)
Range(10, 100, 12)
>>> Range(200, -200, -7) & range(5, 80, 2)  # can intersect with Python range() too
Range(67, 4, -14)

Range () peut également être non lié (lorsque stop est défini sur None, la plage continue jusqu'à +/- infini):

>>> Range(1, None, 3) & Range(3, None, 4)
Range(7, None, 12)
>>> Range(253, None, -3) & Range(208, 310, 5)
Range(253, 207, -15)

L'intersection est calculée et non itérée, ce qui rend l'efficacité de l'implémentation indépendante de la longueur de Range ().

1
avnr

Si vous recherchez le chevauchement entre deux intervalles bornés à valeurs réelles, alors c'est assez agréable:

def overlap(start1, end1, start2, end2):
    """how much does the range (start1, end1) overlap with (start2, end2)"""
    return max(max((end2-start1), 0) - max((end2-end1), 0) - max((start2-start1), 0), 0)

Je ne pouvais pas trouver ce site en ligne, alors je l'ai trouvé et je poste ici.

1
Yetti

En supposant que vous travaillez exclusivement avec des plages, avec une étape de 1, vous pouvez le faire rapidement avec les mathématiques.

def range_intersect(range_x,range_y):
    if len(range_x) == 0 or len(range_y) == 0:
        return []
    # find the endpoints
    x = (range_x[0], range_x[-1]) # from the first element to the last, inclusive
    y = (range_y[0], range_y[-1])
    # ensure min is before max
    # this can be excluded if the ranges must always be increasing
    x = Tuple(sorted(x))
    y = Tuple(sorted(y))
    # the range of the intersection is guaranteed to be from the maximum of the min values to the minimum of the max values, inclusive
    z = (max(x[0],y[0]),min(x[1],y[1]))
    if z[0] < z[1]:
        return range(z[0], z[1] + 1) # to make this an inclusive range
    else:
        return [] # no intersection

Sur une paire de plages comportant chacune plus de 10 ^ 7 éléments, cela a pris moins d'une seconde, indépendamment du nombre d'éléments superposés. J'ai essayé avec environ 10 ^ 8 éléments, mais mon ordinateur a gelé pendant un moment. Je doute que vous travailliez avec des listes aussi longues.

0
Zoey Hewll