如何从 nltk.trees 中识别和删除踪迹树?
How to identify and remove trace trees from nltk.trees?
例如我得到了这棵小树(显然只是一棵子树):
(VP (VBZ says) (SBAR (-NONE- *0*) (S-3 (-NONE- *T*))))
跟踪树是指向形状为 *.* 的叶子的那些树。
我现在想删除所有作为跟踪树的子树。所以对于这个例子,结果应该是这样的:
(VP (VBZ says))
到目前为止,我提取了所有这些叶子:
from nltk.tree import ParentedTree
import re
traceLeaves = []
line = "( (VP (VBZ says) (SBAR (-NONE- *0*) (S-3 (-NONE- *T*)))))"
currTree = ParentedTree.fromstring(line, remove_empty_top_bracketing = True)
for leaf in currTree.leaves():
if re.search('\*', leaf):
traceLeaves.append(leaf)
但我不知道如何向上导航树,直到存在一个没有跟踪树的兄弟并从原始树中删除跟踪树。
我完全被困在这里,因为我才开始使用 nltk...
编辑:
这是我希望能够处理的一个完整句子:
( (SINV (S-3 (S (NP-SBJ-1 (-NONE- *PRO*)) (VP (VBG假设) (NP (DT that) (NN post)) (PP-TMP (IN at) (NP (NP (DT the) (NN age)) (PP (IN of) (NP (CD 35)))))) (, ,) (NP- SBJ-1 (PRP he)) (VP (VBD managed) (PP-MNR (IN by) (NP (NN consensus))) (, ,) (SBAR-ADV (IN as) (S (NP-SBJ (- NONE- *PRO*)) (VP (VBZ is) (NP-PRD (DT the) (NN rule)) (PP-LOC (IN in) (NP (NNS universities))))))) ) (, ,) (VP (VBZ 说) (SBAR (-NONE- *0*) (S-3 (-NONE- *T*))) (NP-SBJ (NP (NNP Warren) (NNP H.) (NNP Strother)) (, ,) (NP (NP (DT a) (NN university) (NN official)) (SBAR (WHNP-2 (WP who)) (S (NP -SBJ-2 (-NONE- *T*)) (VP (VBZ is) (VP (VBG researching) (NP (NP (DT a) (NN book)) (PP (IN on) (NP (NNP 先生) (NNP 哈恩))))))))) (. .)) )
叶子是规则的字符串,因此它们对在树中导航没有帮助。扫描树并寻找高度为 2 的子树。
要识别应该删除的内容,请注意 nltk 树是一种列表;所以要查看一个节点有多少 children,只需取它的 len()
。当你找到一个痕迹叶子时,只要 parent 只有一个 child 就向上移动树;然后删除子树。这应该涵盖所有内容,因为如果一个节点支配 两个 跟踪树而不是其他任何东西,那么在您删除第一个 :-)
之后它将只包含一个
下面还有一个技巧:删除一个节点会混淆 for-loop,因为分支列表会变短。为了防止在我们删除它们之前移动它们,我们向后扫描树。
for sub in reversed(list(t.subtrees())):
if sub.height() == 2 and sub[0].startswith("*"): # abbreviated test
parent = sub.parent()
while parent and len(parent) == 1:
sub = parent
parent = sub.parent()
print(sub, "will be deleted")
del t[sub.treeposition()]
这是第二种解决方案,使用 Tree.leaf_treeposition()
将叶索引映射到树路径(叶扫描基于 this answer,@b3000 建议)。再次向后扫描树以允许就地编辑。任你选。
寻找父节点使用的事实是 "tree position" 是要遵循的分支的元组;因此,如果 postn
是节点 n
的 "position",则 postn[:-1]
是 n
的父节点的位置。这样,您甚至不需要 ParentedTree
。
t = nltk.Tree.fromstring("(VP (VBZ says) (SBAR (-NONE- *0*) (S-3 (-NONE- *T*))))")
for ind, leaf in reversed(list(enumerate(t.leaves()))):
if leaf.startswith("*") and leaf.endswith("*"):
postn = t.leaf_treeposition(ind)
parentpos = postn[:-1]
while parentpos and len(t[parentpos]) == 1:
postn = parentpos
parentpos = postn[:-1]
print(t[postn], "will be deleted")
del t[postn]
例如我得到了这棵小树(显然只是一棵子树):
(VP (VBZ says) (SBAR (-NONE- *0*) (S-3 (-NONE- *T*))))
跟踪树是指向形状为 *.* 的叶子的那些树。 我现在想删除所有作为跟踪树的子树。所以对于这个例子,结果应该是这样的:
(VP (VBZ says))
到目前为止,我提取了所有这些叶子:
from nltk.tree import ParentedTree
import re
traceLeaves = []
line = "( (VP (VBZ says) (SBAR (-NONE- *0*) (S-3 (-NONE- *T*)))))"
currTree = ParentedTree.fromstring(line, remove_empty_top_bracketing = True)
for leaf in currTree.leaves():
if re.search('\*', leaf):
traceLeaves.append(leaf)
但我不知道如何向上导航树,直到存在一个没有跟踪树的兄弟并从原始树中删除跟踪树。 我完全被困在这里,因为我才开始使用 nltk...
编辑:
这是我希望能够处理的一个完整句子:
( (SINV (S-3 (S (NP-SBJ-1 (-NONE- *PRO*)) (VP (VBG假设) (NP (DT that) (NN post)) (PP-TMP (IN at) (NP (NP (DT the) (NN age)) (PP (IN of) (NP (CD 35)))))) (, ,) (NP- SBJ-1 (PRP he)) (VP (VBD managed) (PP-MNR (IN by) (NP (NN consensus))) (, ,) (SBAR-ADV (IN as) (S (NP-SBJ (- NONE- *PRO*)) (VP (VBZ is) (NP-PRD (DT the) (NN rule)) (PP-LOC (IN in) (NP (NNS universities))))))) ) (, ,) (VP (VBZ 说) (SBAR (-NONE- *0*) (S-3 (-NONE- *T*))) (NP-SBJ (NP (NNP Warren) (NNP H.) (NNP Strother)) (, ,) (NP (NP (DT a) (NN university) (NN official)) (SBAR (WHNP-2 (WP who)) (S (NP -SBJ-2 (-NONE- *T*)) (VP (VBZ is) (VP (VBG researching) (NP (NP (DT a) (NN book)) (PP (IN on) (NP (NNP 先生) (NNP 哈恩))))))))) (. .)) )
叶子是规则的字符串,因此它们对在树中导航没有帮助。扫描树并寻找高度为 2 的子树。
要识别应该删除的内容,请注意 nltk 树是一种列表;所以要查看一个节点有多少 children,只需取它的 len()
。当你找到一个痕迹叶子时,只要 parent 只有一个 child 就向上移动树;然后删除子树。这应该涵盖所有内容,因为如果一个节点支配 两个 跟踪树而不是其他任何东西,那么在您删除第一个 :-)
下面还有一个技巧:删除一个节点会混淆 for-loop,因为分支列表会变短。为了防止在我们删除它们之前移动它们,我们向后扫描树。
for sub in reversed(list(t.subtrees())):
if sub.height() == 2 and sub[0].startswith("*"): # abbreviated test
parent = sub.parent()
while parent and len(parent) == 1:
sub = parent
parent = sub.parent()
print(sub, "will be deleted")
del t[sub.treeposition()]
这是第二种解决方案,使用 Tree.leaf_treeposition()
将叶索引映射到树路径(叶扫描基于 this answer,@b3000 建议)。再次向后扫描树以允许就地编辑。任你选。
寻找父节点使用的事实是 "tree position" 是要遵循的分支的元组;因此,如果 postn
是节点 n
的 "position",则 postn[:-1]
是 n
的父节点的位置。这样,您甚至不需要 ParentedTree
。
t = nltk.Tree.fromstring("(VP (VBZ says) (SBAR (-NONE- *0*) (S-3 (-NONE- *T*))))")
for ind, leaf in reversed(list(enumerate(t.leaves()))):
if leaf.startswith("*") and leaf.endswith("*"):
postn = t.leaf_treeposition(ind)
parentpos = postn[:-1]
while parentpos and len(t[parentpos]) == 1:
postn = parentpos
parentpos = postn[:-1]
print(t[postn], "will be deleted")
del t[postn]