Визуализируйте структуру дерева синтаксического анализа

Я хотел бы отобразить синтаксический анализ (теги POS) из openNLP в виде визуализации древовидной структуры. Ниже я привожу дерево синтаксического анализа из 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))))
##     (. .)))

Структура дерева должна выглядеть примерно так:

введите здесь описание изображения

Есть ли способ отобразить эту визуализацию дерева?

Я нашел это связанное дерево, а именно вопрос для построения числовых выражений, которые могут быть полезны, но которые я не мог обобщить для визуализации разбора предложений .


person Indi    schedule 02.11.2015    source источник
comment
Хорошо, но что после этого?   -  person Indi    schedule 02.11.2015


Ответы (1)


Вот igraph версия. Эта функция принимает результат от Parse_annotator в качестве входных данных, поэтому ptext в вашем примере. NLP::Tree_parse уже создает красивую древовидную структуру, поэтому идея состоит в том, чтобы рекурсивно обойти ее и создать список краев для подключения к igraph. Edgelist — это всего лишь матрица из двух столбцов со значениями «голова->хвост».

Чтобы 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)

введите описание изображения здесь

person Rorschach    schedule 05.11.2015
comment
стоит того, что я хотел сделать это в R в течение 3 лет. Просто вернемся к пакету, который позволяет легко анализировать, и последняя часть заключалась в том, что я хотел построить график проанализированных предложений. Можете ли вы отправить мне по электронной почте вашу актуальную информацию, чтобы я мог включить вас в качестве автора на пакет? - person Tyler Rinker; 05.11.2015
comment
пока не нужно быть эффективным, просто работающим :-) Даже если он медленный/неэффективный, это лучше, чем то, что есть в R в настоящее время. Я собираюсь использовать ваш дескриптор SO и сослаться на этот вопрос, чтобы сохранить этическую целостность (т. Е. Отдать должное там, где это необходимо). Если вы передумаете в любое время, дайте мне знать, и я буду использовать ваше настоящее имя. Еще раз спасибо. - person Tyler Rinker; 05.11.2015
comment
Отличный ответ, но обратите внимание, что i <<- i+1 должен поменяться местами с edgelist[i,1:2] <<- c(val, childval) , чтобы избежать ошибки subscript out of bounds - person LI Bing; 11.11.2019
comment
@LIBing, ты уверен? Я не запускал его, но i начинается с 0, а индекс R с 1. - person Rorschach; 11.11.2019