根据值配对列表中的项目

时间:2015-12-15 12:30:20

标签: python xml list tuples

我有一个xml文件,如下所示:

<edge from="0/0" to="0/1" speed="10"/>
<edge from="0/0" to="1/0" speed="10"/>
<edge from="0/1" to="0/0" speed="10"/>
<edge from="0/1" to="0/2" speed="10"/>
...

注意,存在成对的from-to,反之亦然。 (在上面的示例中,只有("0/0","0/1")("0/1","0/0")对可见,但每个条目都有一个合作伙伴。)另请注意,这些对不是有序的。

该文件描述了SUMO网络模拟中的边缘。我想随机为不同的街道分配新的速度。但是,每个<edge>条目仅描述街道的一个方向(车道)。因此,我需要找到它的合作伙伴&#34;。

以下代码仅以车道方式分配速度值:

import xml.dom.minidom as dom
import random
edgexml = dom.parse("plain.edg.xml")
MAX_SPEED_OPTIONS = ["8","9","10"]
for edge in edgexml.getElementsByTagName("edge"):
    x = random.randint(0,2)
    edge.setAttribute("speed", MAX_SPEED_OPTIONS[x])

是否有一种简单的(pythonic)方法可以在元组中收集这些对,然后为两者分配相同的值?

如果您知道使用SUMO工具解决问题的更好方法,我也会感到高兴。但是我仍然对如何在python中解决给定的抽象列表问题感兴趣,因为它不仅仅是一个像相关问题中的简单拉链。

1 个答案:

答案 0 :(得分:1)

好吧,您可以遍历边缘列表并在所有边上嵌套另一个迭代以搜索可能的合作伙伴。由于这是二次复杂度,我们甚至可以通过仅在嵌套运行中遍历尚未访问的边来减少计算时间。

解决方案

(详细说明,向下滚动)

import xml.dom.minidom as dom
import random

edgexml = dom.parse('sampledata/tmp.xml')
MSO = [8, 9, 10]

edge_groups = []
passed = []
for idx, edge in enumerate(edgexml.getElementsByTagName('edge')):
    if edge in passed:
        continue

    partners = []
    for partner in edgexml.getElementsByTagName('edge')[idx:]:
        if partner.getAttribute('from') == edge.getAttribute('to') \
        and partner.getAttribute('to') == edge.getAttribute('from'):
            partners.append(partner)

    edge_groups.append([edge] + partners)
    passed.extend([edge] + partners)

for e in edge_groups:
    print('NEW EDGE GROUP')
    x = random.choice(MSO)
    for p in e:
        p.setAttribute('speed', x)
        print('  E from "%s" to "%s" at "%s"' % (p.getAttribute('from'), p.getAttribute('to'), x))

产生输出:

NEW EDGE GROUP
  E from "0/0" to "0/1" at "8"
  E from "0/1" to "0/0" at "8"
NEW EDGE GROUP
  E from "0/0" to "1/0" at "10"
NEW EDGE GROUP
  E from "0/1" to "0/2" at "9"

详细说明

edge_groups = []
passed = []

初始化结果结构edge_groups,它将是包含 partnered 边缘的列表的列表。附加列表passed将帮助我们避免结果中的冗余边缘。

for idx, edge in enumerate(edgexml.getElementsByTagName('edge')):

开始迭代所有边的列表。我在这里使用enumerate来同时获取索引,因为我们的嵌套迭代只会迭代从当前索引开始的子列表以降低复杂性。

    if edge in passed:
        continue

停止,如果我们之前在任何时间点访问过此边缘。只有在之前已将边缘识别为另一个列表的伙伴(由于基于索引的子列表)时才会发生这种情况。如果它被视为另一个列表的合作伙伴,我们可以毫无疑问地省略它。

    partners = []
    for partner in edgexml.getElementsByTagName('edge')[idx:]:
        if partner.getAttribute('from') == edge.getAttribute('to') \
        and partner.getAttribute('to') == edge.getAttribute('from'):
            partners.append(partner)

初始化帮助程序列表以存储标识的伙伴边。然后,从当前索引开始,遍历剩余列表中的所有边缘。即不要遍历已在外部迭代中传递的边。如果潜在合作伙伴是实际合作伙伴(来自/匹配),请将其附加到我们的partners列表。

    edge_groups.append([edge] + partners)
    passed.extend([edge] + partners)

嵌套迭代已通过,partners包含当前edge的所有已识别合作伙伴。将它们推入一个列表并将其附加到结果变量edge_groups。由于检查2级列表edge_groups以查看我们是否已经在下次运行中遍历了边缘是不必要的复杂,我们还将保留已使用节点的列表并将其称为passed

for e in edge_groups:
    print('NEW EDGE GROUP')
    x = random.choice(MSO)
    for p in e:
        p.setAttribute('speed', x)
        print('  E from "%s" to "%s" at "%s"' % (p.getAttribute('from'), p.getAttribute('to'), x))

最后,我们遍历结果edge_groups中的所有边缘组,从MSO中随机抽取速度(提示:使用random.choice()从列表中随机选择),然后分配它属于这一组的所有边缘。