如何从 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]