2015-11-02 2 views
6

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

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

enter image description here

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

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

+0

Хорошо, но что после этого? – Indi

+2

Возможно через https://en.wikibooks.org/wiki/LaTeX/Linguistics#tikz-qtree? – Reactormonk

ответ

8

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

Для того, чтобы создать грани между соответствующими узлами, они должны иметь уникальные идентификаторы. Я сделал это, добавив последовательность целых чисел (используя 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) 

enter image description here

+1

Я плакал :-) Я ценю работу над этим +500 –

+0

хорошо стоит очков, которые я хотел бы сделать в R на 3 года. Просто вернусь к пакету, который позволяет легко разбираться, и последний фрагмент состоял в том, что мне нужен способ построения разбора предложений. Можете ли вы выслать мне свою фактическую информацию, чтобы я мог указать вас как автора на пакет? –

+0

не нужно быть эффективным на данный момент, просто работая :-) Даже если медленный/неэффективный он лучше, чем у R в настоящее время. Я собираюсь использовать ваш дескриптор SO и ссылаться на этот вопрос, чтобы сохранить этическую целостность (то есть дать должное должное). Если вы передумаете в любое время, дайте мне знать, и я буду использовать ваше фактическое имя. Еще раз спасибо. –

Смежные вопросы