【问题标题】:How to "union" overlapping range to non-overlapping range?如何将重叠范围“联合”到非重叠范围?
【发布时间】:2018-10-18 18:01:08
【问题描述】:

问题: 任何人都可以提出更好或更 Pythonic 的方法,将重叠范围对减少为非重叠范围对吗?

背景: 我有一个表示开始和结束对的元组列表。我试图从本质上完成所有开始结束对的联合。输入起始端对具有重叠值,输出应代表输入起始端对,没有任何重叠。

下面的代码很接近但错误,因为它输出了一个不在输入中的额外范围(我也意识到它不是很好,为什么它错了)。谁能提出更好的方法,或者我忽略的一些内置功能?

为基本问题道歉。 感谢您的帮助!

##create example data
pairA =[(0,5),(10,12)]
pairB =[(1,2),(11,15)]
pairC =[(1,4),(10,12),(15,17)]

#combine the lists to one list
#ultimately may have n number of lists and felt it would be easier to
merged = pairA + pairB +pairC
# produce union of list * unpacks the arguments of a list
listUnion= sorted(set().union(*merged))

#this is the piece of code I am looking at improving
#it creates new start end pairs based on the union
lastElement =listUnion[-1]
outList=[]

for item in listUnion:
    #create start end pair from value i and i+1
    if item != lastElement:
        outList.append((item,listUnion[listUnion.index(item)+1]))
    else:
        #last element of the list, becomes the last element of list pair
        #it can be ignored
        pass
print outList 
"""output: [(0, 1), (1, 2), (2,4), (4, 5), (5, 10), (10, 11), (11, 12), (12, 15), (15, 
17)]
correct output: would not have (5,10) as there is no overlap here in the input """

编辑:添加了这个问题的可视化表示

【问题讨论】:

  • 查找Interval 类。抽象可能会让你从现在正在做的工作中解放出来,并直接导致这个解决方案等等。
  • (0,5)(1,2) 的超集。你为什么不完全放弃(1,2)
  • 不应该是[(0,1),(1,2),(2,4),(4,5)...]?
  • @JohnGordon 稍后需要“断点”。
  • @Accccumulation 你是对的,我更新了帖子

标签: python range aggregate union overlap


【解决方案1】:

这里有一个解决方案。它可能不是很 Pythonic,因为我对 Python 的经验非常有限,但它确实有效。

pairs_a = [(0, 5), (10, 12)]
pairs_b = [(1, 2), (11, 15)]
pairs_c = [(1, 4), (10, 12), (15, 17)]

merged = pairs_a + pairs_b + pairs_c
merged.sort()

set_list = []
cur_set = set()
cur_max = merged[0][1]
for pair in merged:
    p0, p1 = pair
    if cur_max < p0:
        set_list.append(cur_set)
        cur_set = set()
    cur_set.add(p0)
    cur_set.add(p1)
    if cur_max < p1:
        cur_max = p1
set_list.append(cur_set)

out_list = []
for my_set in set_list:
    my_list = sorted(my_set)
    p0 = my_list[0]
    for p1 in my_list[1:]:
        out_list.append((p0, p1))
        p0 = p1

# more pythonic but less readable in spite of indentation efforts:
# out_list = [pair
#             for zipped in [zip(list[:-1], list[1:])
#                            for list in [sorted(set)
#                                         for set in set_list]]
#                 for pair in zipped]

# alternate ending:
# out_list = [sorted(set) for set in set_list]

print(out_list)

这个想法是首先按第一项对所有范围对进行排序。这就是merged.sort() 所做的(它使用连续的元组成员来消除歧义,但这在这里并不重要)。然后我们遍历排序的范围对,只要我们在一堆重叠的范围内,我们就将所有开始和结束添加到当前集合中。为了知道束何时结束,我们保留所有范围结束的最大值。一旦到达超出此最大值的范围开始,我们通过将其附加到列表来存储当前集合,并开始一个新集合。最后一组必须在循环之后添加到列表中。现在我们有了一个集合列表,我们可以轻松地将其转换为列表列表或对列表。

【讨论】:

  • 谢谢沃尔特。我会调整代码,看看我能不能把它弄好。它会产生额外的重叠 (17, 10),但我很欣赏另一种方法。代码输出 (17, 10),其中包含一些其他输出对。
  • 哦,它不在我的 python 3.7.0 上。你有什么蟒蛇?
  • 我想我刚刚修复了它,将list(my_set) 替换为sorted(my_set)。显然(一些?)集合在转换为列表时是否出现排序,或者可能更一般地,在迭代时,取决于 python
  • 哇,谢谢,我应该指定的,这必须在 2.7 中。你在我之前调整了它。如果我想出一个pythonic方式。我会按照你的方式发送。
  • 好。我还添加了一个“替代结尾”,一个“列表列表”输出,您会发现它很有用。
【解决方案2】:

不确定您的环境限制,但如果您没有,您可能需要考虑:https://pypi.org/project/intervaltree/ 特别是,

result_tree = tree.union(iterable)

【讨论】:

  • 我会研究区间树
【解决方案3】:

请您澄清一下问题。我看到[(0,5), (1,2)] 产生[(0, 1), (1, 2), (2, 5)][(0,5), (1,5)] 会产生什么,[(0, 1), (1, 5), (5, 5)],或者只是 [(0,1)],或者其他什么?

【讨论】:

  • 抱歉,不够清晰,0,5 1,5 会产生 (0,1),(1,5)。
猜你喜欢
  • 2010-10-12
  • 2013-10-28
  • 1970-01-01
  • 2012-06-03
  • 2018-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多