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?
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)
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]
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
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
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
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)
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 ().
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.
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.