我想将 openNLP 中的解析(POS标记)显示为树结构可视化。下面我提供了来自 openNLP 的解析树,但我无法将其绘制为Python's parsing共有的可视树。
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))))
## (. .)))
树结构看起来应该类似于:
有没有办法显示这个树形象?
我发现了this related tree viz问题,用于绘制可能有用的数字表达式,但我无法概括为句子解析可视化。
答案 0 :(得分:10)
这是igraph
版本。此函数将Parse_annotator
的结果作为输入,因此示例中为ptext
。 NLP::Tree_parse
已经创建了一个漂亮的树结构,因此这里的想法是递归遍历它并创建一个边缘列表以插入igraph
。边缘列表只是头部 - 尾部值的2列矩阵。
为了使igraph
在适当的节点之间创建边缘,它们需要具有唯一标识符。我通过在使用regmatches<-
之前将一系列整数(使用Tree_parse
)附加到文本中的单词来完成此操作。
内部函数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)