【问题标题】:How to identify and remove trace trees from nltk.trees?如何从 nltk.trees 中识别和删除跟踪树?
【发布时间】:2016-02-29 14:23:06
【问题描述】:

例如我得到了这棵小树(显然只是一棵子树):

(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 Assuming) (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 管理) (PP-MNR (IN by) (NP (NN 共识))) (, ,) (SBAR-ADV (IN as) (S (NP-SBJ (-NONE- *PRO*)) (VP ( VBZ是)(NP-PRD(DT)(NN规则))(PP-LOC(IN in)(NP(NNS大学))))))))(,,)(VP(VBZ说)(SBAR( -NONE- *0*) (S-3 (-NONE- *T*)))) (NP-SBJ (NP (NNP Warren) (NNP H.) (NNP Strother)) (, ,) (NP (NP (DT a) (NN 大学) (NN 官方)) (SBAR (WHNP-2 (WP who)) (S (NP-SBJ-2 (-NONE- *T*)) (VP (VBZ is) (VP ( VBG 研究) (NP (NP (DT a) (NN book)) (PP (IN on) (NP (NNP Mr.) (NNP Hahn))))))))) (. .)))

【问题讨论】:

  • 你使用nltk.tree.ParentTree而不是nltk.Tree有什么原因吗?
  • 因为我需要检查特定树是否也有兄弟姐妹。我认为使用 ParentedTrees 会更好

标签: python tree nltk


【解决方案1】:

叶子是常规字符串,因此它们对于导航树没有帮助。扫描树并寻找高度为 2 的子树。

要识别应该删除的内容,请注意 nltk 树是一种列表;所以要查看一个节点有多少个子节点,只需获取它的len()。当你找到一个踪迹叶时,只要父母只有一个孩子,就向上移动;然后删除子树。这应该涵盖所有内容,因为如果一个节点支配 两个 跟踪树而不是其他任何东西,那么在您删除第一个之后它将只包含一个 :-)

下面还有一个技巧:删除节点会混淆 for 循环,因为分支列表变得更短。为了防止在我们删除它们之前移动它们,我们向后扫描树。

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.height() == 2'-idea 这正是我被困在atm的地方......我甚至在考虑使用 MultiparentedTrees能够处理多个兄弟姐妹,但这使得父母的处理更加复杂......!?
  • 我最大的问题之一是我有时(在树的更高级别)需要比较到目前为止尚未处理的兄弟姐妹。这就是为什么我认为通过反向子树的高度来迭代树是一个好主意......到目前为止,我还想将所有子树存储在具有“isTrace”功能的dict中,导致比较看不见的几个关键错误树......我真的很接近放弃,因为这让我发疯...... :(在问题中找到我目前的进展作为编辑......
  • 你试过我的代码了吗?它没有做你想做的事吗?那是什么? (举个例句。)据我所知,它完全符合您的要求。
  • 哦,对不起...由于复制粘贴错误,我的第一条评论似乎消失了! ;) 当我在作为编辑发布的完整示例上运行它时,它最终陷入无限循环。
  • 我注意到有一个错误(定义了parent,但忘记在循环中更新它)。使用更正后的代码重试(然后也检查替代解决方案:-))
【解决方案2】:

这是第二种解决方案,使用Tree.leaf_treeposition() 将叶索引映射到树路径(叶扫描基于this answer,由@b3000 建议)。再次向后扫描树以允许就地编辑。任君挑选。

使用“树位置”是要遵循的分支的元组这一事实来查找父级;这样如果postn 是节点n 的“位置”,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]

【讨论】:

    猜你喜欢
    • 2010-11-24
    • 2022-11-22
    • 2019-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多