提取每个终端节点的路径
Extract path for each terminal node
我有一个 python 嵌套字典结构,如下所示。
这是一个小例子,但我有更大的例子,可以有不同层次的嵌套。
由此,我需要提取一个列表:
- 每个终端一条记录'leaf'节点
- 表示通向该节点的逻辑路径的字符串、列表或对象
- (例如'nodeid_3: X < 0.500007 and X < 0.279907')
这个周末的大部分时间我都在尝试让一些东西工作,并且意识到我在递归方面有多么糟糕。
# Extract json string
json_string = booster.get_dump(with_stats=True, dump_format='json')[0]
# Convert to python dictionary
json.loads(json_string)
{u'children': [{u'children': [
{u'cover': 2291, u'leaf': -0.0611795, u'nodeid': 3},
{u'cover': 1779, u'leaf': -0.00965727, u'nodeid': 4}],
u'cover': 4070,
u'depth': 1,
u'gain': 265.811,
u'missing': 3,
u'no': 4,
u'nodeid': 1,
u'split': u'X',
u'split_condition': 0.279907,
u'yes': 3},
{u'cover': 3930, u'leaf': -0.0611946, u'nodeid': 2}],
u'cover': 8000,
u'depth': 0,
u'gain': 101.245,
u'missing': 1,
u'no': 2,
u'nodeid': 0,
u'split': u'X',
u'split_condition': 0.500007,
u'yes': 1}
你的数据结构是递归的。如果一个节点有children键,那么我们可以认为它不是终端
要分析您的数据,您需要一个递归函数来跟踪祖先(路径)。
我会这样实现:
def find_path(obj, path=None):
path = path or []
if 'children' in obj:
child_obj = {k: v for k, v in obj.items()
if k in ['nodeid', 'split_condition']}
child_path = path + [child_obj]
children = obj['children']
for child in children:
find_path(child, child_path)
else:
pprint.pprint((obj, path))
如果您致电:
find_path(data)
您得到 3 个结果:
({'cover': 2291, 'leaf': -0.0611795, 'nodeid': 3},
[{'nodeid': 0, 'split_condition': 0.500007},
{'nodeid': 1, 'split_condition': 0.279907}])
({'cover': 1779, 'leaf': -0.00965727, 'nodeid': 4},
[{'nodeid': 0, 'split_condition': 0.500007},
{'nodeid': 1, 'split_condition': 0.279907}])
({'cover': 3930, 'leaf': -0.0611946, 'nodeid': 2},
[{'nodeid': 0, 'split_condition': 0.500007}])
当然,您可以将对 pprint.pprint()
的调用替换为 yield
以将此函数转换为生成器:
def iter_path(obj, path=None):
path = path or []
if 'children' in obj:
child_obj = {k: v for k, v in obj.items()
if k in ['nodeid', 'split_condition']}
child_path = path + [child_obj]
children = obj['children']
for child in children:
# for o, p in iteration_path(child, child_path):
# yield o, p
yield from iter_path(child, child_path)
else:
yield obj, path
注意递归调用的yield from
用法。您可以像下面这样使用此生成器:
for obj, path in iter_path(data):
pprint.pprint((obj, path))
您还可以更改 child_obj
对象的构建方式以满足您的需要。
要保持对象的顺序:反转 if
条件:if 'children' not in obj: …
。
我有一个 python 嵌套字典结构,如下所示。 这是一个小例子,但我有更大的例子,可以有不同层次的嵌套。
由此,我需要提取一个列表:
- 每个终端一条记录'leaf'节点
- 表示通向该节点的逻辑路径的字符串、列表或对象
- (例如'nodeid_3: X < 0.500007 and X < 0.279907')
这个周末的大部分时间我都在尝试让一些东西工作,并且意识到我在递归方面有多么糟糕。
# Extract json string
json_string = booster.get_dump(with_stats=True, dump_format='json')[0]
# Convert to python dictionary
json.loads(json_string)
{u'children': [{u'children': [
{u'cover': 2291, u'leaf': -0.0611795, u'nodeid': 3},
{u'cover': 1779, u'leaf': -0.00965727, u'nodeid': 4}],
u'cover': 4070,
u'depth': 1,
u'gain': 265.811,
u'missing': 3,
u'no': 4,
u'nodeid': 1,
u'split': u'X',
u'split_condition': 0.279907,
u'yes': 3},
{u'cover': 3930, u'leaf': -0.0611946, u'nodeid': 2}],
u'cover': 8000,
u'depth': 0,
u'gain': 101.245,
u'missing': 1,
u'no': 2,
u'nodeid': 0,
u'split': u'X',
u'split_condition': 0.500007,
u'yes': 1}
你的数据结构是递归的。如果一个节点有children键,那么我们可以认为它不是终端
要分析您的数据,您需要一个递归函数来跟踪祖先(路径)。
我会这样实现:
def find_path(obj, path=None):
path = path or []
if 'children' in obj:
child_obj = {k: v for k, v in obj.items()
if k in ['nodeid', 'split_condition']}
child_path = path + [child_obj]
children = obj['children']
for child in children:
find_path(child, child_path)
else:
pprint.pprint((obj, path))
如果您致电:
find_path(data)
您得到 3 个结果:
({'cover': 2291, 'leaf': -0.0611795, 'nodeid': 3},
[{'nodeid': 0, 'split_condition': 0.500007},
{'nodeid': 1, 'split_condition': 0.279907}])
({'cover': 1779, 'leaf': -0.00965727, 'nodeid': 4},
[{'nodeid': 0, 'split_condition': 0.500007},
{'nodeid': 1, 'split_condition': 0.279907}])
({'cover': 3930, 'leaf': -0.0611946, 'nodeid': 2},
[{'nodeid': 0, 'split_condition': 0.500007}])
当然,您可以将对 pprint.pprint()
的调用替换为 yield
以将此函数转换为生成器:
def iter_path(obj, path=None):
path = path or []
if 'children' in obj:
child_obj = {k: v for k, v in obj.items()
if k in ['nodeid', 'split_condition']}
child_path = path + [child_obj]
children = obj['children']
for child in children:
# for o, p in iteration_path(child, child_path):
# yield o, p
yield from iter_path(child, child_path)
else:
yield obj, path
注意递归调用的yield from
用法。您可以像下面这样使用此生成器:
for obj, path in iter_path(data):
pprint.pprint((obj, path))
您还可以更改 child_obj
对象的构建方式以满足您的需要。
要保持对象的顺序:反转 if
条件:if 'children' not in obj: …
。