【问题标题】:How to Process Strings of Nodes to get their Connections?如何处理节点字符串以获取它们的连接?
【发布时间】:2021-02-09 22:10:18
【问题描述】:

我正在对节点树做一些工作,但我遇到了这个问题。 此列表包含一棵树的所有信息:

connections = ['Module/Expr/ListComp/BinOp/Name/id/i/',
           'Module/Expr/ListComp/BinOp/Sub/',
           'Module/Expr/ListComp/BinOp/Num/0.5/',
           'Module/Expr/ListComp/comprehension/Name/id/i/',
           'Module/Expr/ListComp/comprehension/Name/id/inp/']

我需要把它转换成:

{'Module':'Expr', 'Expr':'ListComp', 'ListComp':'BinOp comprehension', 
'BinOp':'Name Sub Num', 'Name':'id', 'id':'i', 'Num':'0.5', 
'comprehension':'Name', 'Name':'id', 'id':'i inp'}

目标是将连接解析为结构{'parent':'child(s)'} 的字典。为了做到这一点,我已经尝试过:

rules = {}
connections_list = [[word for word in path.split("/") if word] for path in connections]

for path in connections_list:
    for i, word in enumerate(path):
        same_level = [y[i+1] for y in connections_list if len(connections_list) > i+1]
        if same_level:
            unique_on_level = list(set(same_level))
            rules.update({word:" ".join(unique_on_level)})
        else:
            pass
    break
print(rules)

有一个输出:

{'Module': 'Expr',
 'Expr': 'ListComp',
 'ListComp': 'BinOp comprehension',
 'BinOp': 'Num Sub Name'}

我想不出办法,这里的问题发生在最后一个节点周围,但我不知道如何解决,知道如何解决这个问题吗?

【问题讨论】:

  • 这看起来像是一个 X-Y 问题:为什么你要把它解析成这么尴尬的符号?您正在尝试表达一棵树:为什么不使用图形解决整体问题?

标签: python arrays python-3.x string


【解决方案1】:

首先创建父节点到子节点的映射,然后删除重复项。

rules = {}
for connection in connections:
    parts = connection.rstrip("/").split("/")
    for parent, child in zip(parts, parts[1:]):
        if parent not in rules:
            rules[parent] = []
        rules[parent].append(child)

rules = {k: " ".join({}.fromkeys(v)) for k, v in rules.items()}

【讨论】:

  • 但是这个方法不会追加同名的节点,对吧?以防万一有两个不同的节点(在不同的级别)具有相同的名称
  • 我不明白你的意思,你能举个例子吗?
  • 如果存在不同层次的同名节点,在输出中应该分别出现,不能是字典;字典不能有重复的键,例如 {'id': 'i', 'id': 'i inp'}
  • @sabik 说得对,你知道在这些情况下我能做什么吗?
  • 正如其他人评论的那样,您最终想要实现什么?这似乎不是该数据的自然表示法......但是,如果您确实需要大致这种形式,元组列表可能是最接近的:[('id', 'i'), ('id', 'i inp')]
【解决方案2】:

根据@wim 的回答和 cmets,我认为这应该可行:

from collections import defaultdict

rule_data = defaultdict(set)
for connection in connections:
    parts = connection.rstrip("/").split("/")
    for level, (parent, child) in enumerate(zip(parts, parts[1:])):
        rule_data[level, parent].add(child)

rules = [
    (parent, " ".join(sorted(children)))
    for (_level, parent), children in rule_data.items()
]

注意事项:

  • 使用set 会丢弃孩子的顺序;如果它很重要,我们可以改为使用dict(或者,为了与旧版本的 Python 兼容,OrderedDict):

    • rule_data = defaultdict(dict)
    • rule_data[level, parent][child] = None
    • (parent, " ".join(children))
  • 为了输出的稳定性,我对子项进行排序,以便单元测试可以轻松工作,因此任何下游处理都不会看到虚假的变化。

  • 正如@Prune 所说,这似乎不是数据的自然表示:

    • 您最终要达到什么目标?
    • 这里的rule_data 中间变量在进一步处理中可能比最终形式更有用...
    • 如果任何连接包含空格,则输出将不明确。

【讨论】:

  • 级别+名称不是唯一标识符。考虑/a/x/sub1/b/x/sub2。如果需要唯一性(O.P. 尚未澄清),可能需要使用整个父前缀。
  • 真;我们需要澄清这些是应该合并还是分开。
  • 感谢您的回复。是的,在这种情况下,唯一性是一个要求,实际上我喜欢你使用的方法,我认为它需要很少的更改才能完全符合我的需要。问题是节点id f.ex。在连接中出现了 3 次,但它们是不同的节点,因为它们不在同一级别。
【解决方案3】:
connections = ['Module/Expr/ListComp/BinOp/Name/id/i/',
               'Module/Expr/ListComp/BinOp/Sub/',
               'Module/Expr/ListComp/BinOp/Num/0.5/',
               'Module/Expr/ListComp/comprehension/Name/id/i/',
               'Module/Expr/ListComp/comprehension/Name/id/inp/']

splitted_conns = [conn.strip('/').split('/') for conn in connections]
res = {}
for conn in splitted_conns:
    for root, child in zip(conn[:-1], conn[1:]):
        res[root] = res.get(root, set()) | {child}
print(res)

输出:

{'Module': {'Expr'}, 'Expr': {'ListComp'}, 'ListComp': {'BinOp', 'comprehension'}, 'BinOp': {'Sub', 'Num' , 'Name'}, 'Name': {'id'}, 'id': {'inp', 'i'}, 'Num': {'0.5'}, 'comprehension': {'Name'}}

这样的?您的预期输出包含重复的节点“id”,我想这是一个错误,不是吗?

关于解决方案,我决定为每个子组使用一个集合,这样我们就可以避免节点名称中包含空格的失败。而且,with set 很容易避免重复。如果你想将每个集合转换为空格分隔的字符串,你可以试试这个:

{k: ' '.join(v) for k, v in res.items()}

【讨论】:

  • 这会为 N 个节点创建 2*N 个临时集合对象,这些对象随后会立即被垃圾回收。不必要的低效 - 这是在 Python 中分配的相对昂贵的对象。
  • @wim 更改 '|'集合更新方法的操作符,应该可以解决这个问题。 for conn in splitted_conns: for root, child in zip(conn[:-1], conn[1:]): if root not in res: res[root] = {child} else: res[root].add(child )
  • 是的,那会更好,更 Pythonic。
  • 我需要它包含“重复”节点,(f.ex:id)因为实际上它们不是重复的,而是两个不同的节点(A/B/id!=A/id)因为它们'处于不同的水平。无论如何感谢您的回复,我想我现在可以想出解决此问题的方法
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-13
  • 2017-04-27
  • 2010-09-15
  • 2022-12-13
  • 1970-01-01
  • 2017-08-06
  • 1970-01-01
相关资源
最近更新 更多