识别列表中所有重叠的元组

时间:2020-08-21 19:38:29

标签: python

我目前有一个元组列表(尽管我控制该列表和元组的创建,因此可以根据需要更改它们的类型)。每个元组都有一个开始和结束整数,以及一个带该范围源ID的字符串。我想做的是确定元组中的所有重叠范围。

我现在有

T

哪些输出的信息重叠,应该可以手动检查和修复。但是,它错过了识别某些重叠部分的机会。我不禁想到有一个相对明显的解决方案,我只是想念或者没有使用正确的搜索词来查找示例。但理想情况下,我希望输出实际上是

a = [(0, 98, '122:R'), 
     (100, 210, '124:R'),
     (180, 398, '125:R'),
     (200, 298, '123:R')]
highNum = 0
highNumItem = ''
for item in a:
    if item[0] < highNum:
        print(highNumItem + ' overlaps ' + item[2])
        if item[1] > highNum:
            highNum = item[1]
            highNumItem = item[2]

    
# 124:R overlaps 125:R
# 125:R overlaps 123:R

但是使用我的比较方法,我看不到一种方法来捕获重叠超过两个相邻范围的罕见情况。如果有人可以指出适合我的功能或比较方法,我将不胜感激。

此外,如果有关系,我目前仍然使用python 2.7,但是需要在第三方应用程序允许时将解决方案移植到3.x。

5 个答案:

答案 0 :(得分:3)

检查一对中的较高编号,然后检查另一对中的较低编号:

a = [(0, 98, '122:R'), (100, 210, '124:R'), (180, 398, '125:R'), (200, 298, '123:R')]

for i, base_data in enumerate(a):
    for check_data in a[i + 1:]:
        if base_data[1] > check_data[0]:
            print(f"{base_data[2]} overlaps {check_data[2]}")
#prints:
#124:R overlaps 125:R
#124:R overlaps 123:R
#125:R overlaps 123:R

如果您希望将其存储在组中:

from collections import defaultdict

a = [(0, 98, '122:R'), (100, 210, '124:R'), (180, 398, '125:R'), (200, 298, '123:R')]
d = defaultdict(list)

for i, base_data in enumerate(a):
    for check_data in a[i + 1:]:
        if base_data[1] > check_data[0]:
            d[base_data[2]].append(check_data[2])


print(d)
#prints:
#defaultdict(<class 'list'>, {'124:R': ['125:R', '123:R'], '125:R': ['123:R']})
#but can *easily* be iterated over to pretty print:
print("\n".join([f'{key} overlaps {" & ".join(d[key])}' for key in d]))
#prints:
#124:R overlaps 125:R & 123:R
#125:R overlaps 123:R

字典比打印要好得多,因为它实际上存储数据而不是仅打印数据。打印数据也可以得到更好的控制。另外,在defaultdict上使用dict会使代码更紧凑。

答案 1 :(得分:3)

这里是一个使用intspan来计算重叠的示例。 (使用Python 3.8)

from itertools import combinations
from intspan import intspan

a = [(0, 98, '122:R'), (100, 210, '124:R'), (180, 398, '125:R'), (200, 298, '123:R')]

d = {}
for one, two in combinations(a,2):
    # if the 2 ranges intersect
    if intspan.from_range(*one[0:2]) & intspan.from_range(*two[0:2]):
        d.setdefault(one[2], []).append(two[2])

for key, v in d.items():
    print(key + ',' + ','.join(v))

打印:

124:R,125:R,123:R
125:R,123:R

答案 2 :(得分:2)

这是python 3.x中的解决方案

    a = [(0, 98, '122:R'), (100, 210, '124:R'), (180, 398, '125:R'), (200, 298, '123:R')]
    
    for i in range(len(a)-1):
        i_low, i_high, i_id = a[i]
        for j in range(i+1, len(a)):
            j_low, j_high, j_id = a[j]
            if i_low < j_low < i_high or j_low < i_low < j_high:
                print(i_id, " overlaps with ", j_id)

以防python 2不支持该拆包系统:

    a = [(0, 98, '122:R'), (100, 210, '124:R'), (180, 398, '125:R'), (200, 298, '123:R')]
    
    for i in range(len(a)-1):
        i_low, i_high, i_id = a[i][0], a[i][1], a[i][2]
        for j in range(i+1, len(a)):
            j_low, j_high, j_id =a[j][0], a[j][1], a[j][2]
            if i_low < j_low < i_high or j_low < i_low < j_high:
                print(i_id, " overlaps with ", j_id)

答案 3 :(得分:2)

这应该有效:

import operator

def get_overlaps(end, remaining):
    output = []
    for r in remaining:
        if r[0] < end:
            # starts before the end
            output.append(r[2])
            continue
        break
    return output

def get_all_overlaps(lst):
    # thanks @Elan-R for this simplification
    for i, (start, end, name) in enumerate(lst):        
        overlaps = get_overlaps(end, lst[i+1:])
        if overlaps:
            print(name, "overlaps", " & ".join(overlaps))


a = [(0, 98, '122:R'), (100, 210, '124:R'), (180, 398, '125:R'), (200, 298, '123:R')]

# sort by start time
a.sort(key=operator.itemgetter(0)) # thanks to @moonGoose
get_all_overlaps(a)

输出:

124:R overlaps 125:R & 123:R
125:R overlaps 123:R

此代码遍历列表中的每个项目,然后检查每个后续项目以查看开始时间是否小于当前项目的结束时间。如果是这样,它将名称添加到重叠列表中。否则,随着开始时间的增加,它将停止检查当前项目,因此不会有任何重复。

(已针对Python 3.6进行了测试,但可以在任何版本上使用)

答案 4 :(得分:2)

首先,请确保您已按照时间间隔起始值对列表进行了排序。然后,您只需要遍历它们并将组元组组合在一起,只要它们的间隔起始值低于第一个组项目的终止值即可。

items = [(0, 98, '122:R'), (100, 210, '124:R'), (180, 398, '125:R'), (200, 298, '123:R')]
    
sorted(items, key=lambda x: x[0])

overlaps = []
while items:
  overlap = list(takewhile(lambda item:item[0] < items[0][1],items))
  overlaps.append(overlap)
  items = items[len(overlap):]

导致overlaps

[
   [(0, 98, '122:R')],
   [(100, 210, '124:R'), (180, 398, '125:R'), (200, 298, '123:R')]
]