可视化解析树结构

8
我希望能够展示来自openNLP的解析(POS标注)作为树形结构可视化。下面提供了来自openNLP的解析树,但我无法像Python解析那样绘制成通用的可视化树。
install.packages(
    "http://datacube.wu.ac.at/src/contrib/openNLPmodels.en_1.5-1.tar.gz",  
    repos=NULL, 
    type="source"
)

library(NLP)
library(openNLP)

x <- 'Scroll bar does not work the best either.'
s <- as.String(x)

## Annotators
sent_token_annotator <- Maxent_Sent_Token_Annotator()
word_token_annotator <- Maxent_Word_Token_Annotator()
parse_annotator <- Parse_Annotator()

a2 <- annotate(s, list(sent_token_annotator, word_token_annotator))
p <- parse_annotator(s, a2)
ptext <- sapply(p$features, `[[`, "parse")
ptext
Tree_parse(ptext)

## > ptext
## [1] "(TOP (S (NP (NNP Scroll) (NN bar)) (VP (VBZ does) (RB not) (VP (VB work) (NP (DT the) (JJS best)) (ADVP (RB either))))(. .)))"
## > Tree_parse(ptext)
## (TOP
##   (S
##     (NP (NNP Scroll) (NN bar))
##     (VP (VBZ does) (RB not) (VP (VB work) (NP (DT the) (JJS best)) (ADVP (RB either))))
##     (. .)))

树形结构应该类似于这样:

enter image description here

有没有一种方法可以显示这个树形可视化?

我找到了与此相关的树形可视化问题 (链接),它可以用于绘制数值表达式,但我无法将其推广到句子解析可视化。


好的,但之后呢? - Indi
2
也许可以通过 https://en.wikibooks.org/wiki/LaTeX/Linguistics#tikz-qtree 来实现? - Reactormonk
1个回答

10

这是一个igraph版本。此函数以Parse_annotator的结果作为输入,因此使用您示例中的ptextNLP :: Tree_parse已经创建了一个很好的树形结构,因此这里的想法是递归遍历它并创建一个边缘列表以插入到igraph中。边缘列表只是一个头->尾值的二列矩阵。

为了使igraph在正确的节点之间创建边缘,它们需要具有唯一的标识符。我通过在使用Tree_parse之前将文本中的单词附加一个整数序列(使用regmatches< - )来实现这一点。

内部函数edgemaker遍历树,在遍历过程中填充edgelist。可以选择单独为叶子节点着色,但如果传递选项vertex.label.color,则会将它们全部涂成相同的颜色。

## Make a graph from Tree_parse result
parse2graph <- function(ptext, leaf.color='chartreuse4', label.color='blue4',
                        title=NULL, cex.main=.9, ...) {
    stopifnot(require(NLP) && require(igraph))

    ## Replace words with unique versions
    ms <- gregexpr("[^() ]+", ptext)                                      # just ignoring spaces and brackets?
    words <- regmatches(ptext, ms)[[1]]                                   # just words
    regmatches(ptext, ms) <- list(paste0(words, seq.int(length(words))))  # add id to words

    ## Going to construct an edgelist and pass that to igraph
    ## allocate here since we know the size (number of nodes - 1) and -1 more to exclude 'TOP'
    edgelist <- matrix('', nrow=length(words)-2, ncol=2)

    ## Function to fill in edgelist in place
    edgemaker <- (function() {
        i <- 0                                       # row counter
        g <- function(node) {                        # the recursive function
            if (inherits(node, "Tree")) {            # only recurse subtrees
                if ((val <- node$value) != 'TOP1') { # skip 'TOP' node (added '1' above)
                    for (child in node$children) {
                        childval <- if(inherits(child, "Tree")) child$value else child
                        i <<- i+1
                        edgelist[i,1:2] <<- c(val, childval)
                    }
                }
                invisible(lapply(node$children, g))
            }
        }
    })()

    ## Create the edgelist from the parse tree
    edgemaker(Tree_parse(ptext))

    ## Make the graph, add options for coloring leaves separately
    g <- graph_from_edgelist(edgelist)
    vertex_attr(g, 'label.color') <- label.color  # non-leaf colors
    vertex_attr(g, 'label.color', V(g)[!degree(g, mode='out')]) <- leaf.color
    V(g)$label <- sub("\\d+", '', V(g)$name)      # remove the numbers for labels
    plot(g, layout=layout.reingold.tilford, ...)
    if (!missing(title)) title(title, cex.main=cex.main)
}

因此,使用您的示例,字符串x及其带注释的版本ptext,看起来像这样

x <- 'Scroll bar does not work the best either.'
ptext
# [1] "(TOP (S (NP (NNP Scroll) (NN bar)) (VP (VBZ does) (RB not) (VP (VB work) (NP (DT the) (JJS best)) (ADVP (RB either))))(. .)))"

通过调用函数创建图表

library(igraph)
library(NLP)

parse2graph(ptext,  # plus optional graphing parameters
            title = sprintf("'%s'", x), margin=-0.05,
            vertex.color=NA, vertex.frame.color=NA,
            vertex.label.font=2, vertex.label.cex=1.5, asp=0.5,
            edge.width=1.5, edge.color='black', edge.arrow.size=0)

在此输入图片描述


1
这真的值得我花费这些时间。我已经想在R中实现这个功能3年了。现在我回到了一个可以轻松解析的软件包,而最后一步是我想要一种绘制解析句子的方法。你能给我发邮件,告诉我你的实际信息,这样我就可以将你列为该软件包的作者了吗? - Tyler Rinker
1
暂时不需要高效,只要能工作就行 :-) 即使速度慢/效率低下,也比R目前可用的好。我将使用您的SO用户名并引用此问题以保持道德完整性(即在应该给予信用的地方给予信用)。如果您随时改变主意,请告诉我,我会使用您的实际姓名。再次感谢。 - Tyler Rinker
非常好的答案,但请注意 i <<- i+1 应该与 edgelist[i,1:2] <<- c(val, childval) 交换位置,以避免 subscript out of bounds 错误。 - LI Bing
@LIBing 你确定吗?虽然我还没有运行它,但 i 从0开始,而R的索引从1开始。 - Rorschach

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接