1

假设我有一个 Python 3.6 列表,如下所示:

l1 = [
     [a,b,c], 
     [b,c], 
     [c], 
     [d, e], 
     [e]
     ...
] 

我需要使用anytree将其转换为树状结构,使其看起来像这样:

>>> print(RenderTree(l1))

l1
|__ a
|   |__b
|      |__c
|___d
    |__e

如果有任何帮助,请将对象a, b, c, d,视为一个字符串。e我目前已经阅读了很多关于 anytree 的文档,并在 StackOverflow 上搜索了一段时间,但找不到任何可以帮助我解决这个问题的东西。我可以解决这个问题的最pythonic方式是什么?

编辑:为了添加说明,原始列表l1应该代表一棵树,其中第一个元素l1是父节点,其中的每个节点都是一个子节点。每个子节点都可以是它之前节点的子节点,以此类推

编辑编辑:所以,这是原始列​​表(假设)的样子:

l1 = [
['a', 'b', 'c'],
['b', 'c'],
['c'],
['d', 'e'],
['e']
]

在这里,每个子列表的第一个元素总是最终成为该分支的父元素。将这些分支中的每一个连接在一起将使我获得所需的格式,但我一直在努力将其表达出来(现在是凌晨 2 点)。以下是我的一些尝试:

将列表转换为节点:

from anytree import Node

l = []

for x in l1:
    a = Node(x[0])
    for i in x[1:]:
        Node(i, parent = a)
    l.append(a)

但是,这会返回一个树/列表,因此:


>>> l
[Node('/a'), Node('/b'), Node('/c'), Node('/d'), Node('/e')]
>>> print(RenderTree(l[0]))
Node('/a')
├── Node('/a/b')
└── Node('/a/c')
>>> print(RenderTree(l[1]))
Node('/b')
└── Node('/b/c')
>>> print(RenderTree(l[2]))
Node('/c')
>>> print(RenderTree(l[3]))
Node('/d')
└── Node('/d/e')
>>> print(RenderTree(l[4]))
Node('/e')

为了过滤掉它,我尝试执行以下操作:

def tuple_replace(tup, pos, val):
    return tup[:pos] + (val,) + tup[pos+1:]

>>> l2=[]
>>> for pos, x in enumerate(l):
    for pos_2, i in enumerate(x.children):
        for j in l[pos+1:]:
            if j.name == i.name:
                x.children = tuple_replace(x.children, pos_2, i)
                break
        l2.append(x)

>>> for x in l2:
    print(RenderTree(x))


Node('/a')
├── Node('/a/b')
└── Node('/a/c')
Node('/a')
├── Node('/a/b')
└── Node('/a/c')
Node('/b')
└── Node('/b/c')
Node('/d')
└── Node('/d/e')

这就是我目前所处的步骤

编辑编辑编辑:

因此,树的表示方式是我有一个函数,它返回一个类似 的列表l1,并且背后有以下逻辑:

列表中的每个元素都有 2 个部分。家长,还有孩子。父元素是列表中的第一个元素,其他所有元素都是它的子元素,或者是子元素的子元素,依此类推。所以一个像:[a, b, c][d, e, f, g]元素代表分支中的所有元素,而不仅仅是不断下降的直接父母。这就是其他元素发挥作用的地方。下一个元素通常包含父母的第一个孩子:[b, c]and[e, f][g]。但是现在,元素[d, e, f, g]不同了,[a, b, c]因为它里面有 2 个不同的子分支而不是一个。所以,像这样的树:

l1
|
|_a
|   |__b
|   |__c
|
|_d
   |__e
   |    |__f
   |__g

会被描述为:

编辑:修复了输入树,因为f没有独立的分支

l1=[
 [a,b,c],
 [b, c],
 [c],
 [d,e,f,g],
 [e,f]
 [f]
 [g]
]
4

1 回答 1

1

您可以使用递归构建一个嵌套字典来表示您的树,然后遍历结果以打印所需的图表:

from functools import reduce
data = [['a', 'b', 'c'], ['b', 'c'], ['c'], ['d', 'e'], ['e']]
new_data = [a for i, a in enumerate(data) if all(a[0] not in c for c in data[:i])]
def to_tree(d):
   return d[0] if len(d) == 1 else {d[0]:to_tree(d[1:])}

tree = reduce(lambda x, y:{**x, **y}, [to_tree(i) for i in new_data])

现在,打印结构:

import re
def print_tree(d, c = 0):
   for a, b in d.items():
     yield f'{"|" if c else ""}{"   "*c}|__{a}'
     if not isinstance(b, dict):
        yield f'{"|" if (c+1) else ""}{"   "*(c+1)}|__{b}'
     else:
        yield from print_tree(b, c+1)

*r, _r = print_tree(tree)
print('l1\n{}\n{}'.format('\n'.join(r), re.sub("^\|", "", _r)))

输出:

l1
|__a
|  |__b
|     |__c
|__d
   |__e

编辑:可选的树形成方法:

当前to_tree方法假定父子节点结构将作为每个父节点的单个列表包含,即['a', 'b', 'c']是树['d', 'e']的完整路径并且也是完整路径。如果将来的输入可能不是这种情况,您可以使用下面的代码来构建字典:

def to_tree(d, s, seen = []):
   _l = [b for a, b, *_ in d if a == s and b not in seen]
   return s if not _l else {s:to_tree(d, _l[0], seen+[s, _l[0]])}

data = [['a', 'b', 'c'], ['b', 'c'], ['c'], ['d', 'e'], ['e']]
p = [a[0] for i, a in enumerate(data) if all(a[0] not in c for c in data[:i])]
c = [i for i in data if len(i) > 1]
tree = reduce(lambda x, y:{**x, **y}, [to_tree(c, i) for i in p])
于 2019-09-22T15:28:20.893 回答