假设我有一个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上搜索了一段时间,但是找不到任何可以帮助我解决该问题的东西。解决此问题的最有效方法是什么?
编辑:为澄清起见,原始列表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]
和[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]
]
答案 0 :(得分: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])