добавить еще один слой в ggplot2/ggtree на основе пользовательского ввода Rshiny

В приведенном ниже примере используется ggtree, в котором я могу почистить подсказки в филогении и добавить метку аннотации («клада»). Шаги для запуска приложения -

  1. загрузить дерево - называется vert.tree
  2. проведите кистью (выделите) подсказки (проверьте с человеком и лемуром) и нажмите кнопку «аннотировать дерево», чтобы добавить метку красного цвета.

Что я хочу сделать, так это добавить еще одну аннотацию к дереву, сохранив при этом первую аннотацию (человек и лемур). Например, вторая этикетка для подсказок свиньи и коровы. По сути, я хочу иметь возможность добавлять строку в филогенетическое дерево на основе ввода пользователя, а затем повторять это на основе второго ввода пользователя, сохраняя при этом первую строку на изображении. В настоящее время метка сбрасывается каждый раз, когда я чищу другую пару, поэтому за раз отображается только одна аннотация.

# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.

library(shiny)
library(treeio)
library(ggtree)
library(phytools)
library(ape)

#make phylogenetic tree
text.string <-"(((((((cow, pig),whale),(bat,(lemur,human))),(robin,iguana)),coelacanth),gold_fish),shark);"

#read in the tree
vert.tree<-ape::read.tree(text=text.string)

# Define UI for application that draws a histogram
ui <- fluidPage(

  # Application title
  titlePanel("Test"),

  actionButton("add_annotation","Add clade annotation"),

  # Show a plot of the generated distribution
  mainPanel(plotOutput("treeDisplay", brush ="plot_brush")
  )
)

# Define server logic required to draw a histogram
server <- function(input, output) {


 #reactive that holds base tree - this is how I am building the base tree 
  make_tree <- reactive({
    ggtree::ggtree(vert.tree)+
      ggtree::geom_tiplab()+
      ggplot2::xlim(NA, 10)})

  #render base tree 
    output$treeDisplay <- renderPlot({
      make_tree()
    })

  #reactive that holds the brushed points on a plot
  dataWithSelection <- reactive({
    brushedPoints(make_tree()$data, input$plot_brush)
  })

  #add to label to vector if isTip == True
  dataWithSelection2 <- reactive({
    tipVector <- c()
    for (i in 1:length(dataWithSelection()$label)){ if(dataWithSelection()$isTip[i] == TRUE) tipVector <- c(tipVector,dataWithSelection()$label[i])}
    return(tipVector)
  })

  # incorporate the tipVector information for adding layer
  layer <- reactive({
    ggtree::geom_cladelabel(node=phytools::findMRCA(ape::as.phylo(make_tree()), dataWithSelection2()), label = "Clade", color = "red")
  })

  #display that layer onto the tree
  observeEvent(input$add_annotation, {
    output$treeDisplay <- renderPlot({make_tree() + layer()})
  })
}

# Run the application 
shinyApp(ui = ui, server = server)

Предложения очень ценятся!

обновлено для включения базового дерева (vert.tree)


person Jenna Hamlin    schedule 10.04.2020    source источник
comment
Привет, нам не нужно делать все эти шаги только для того, чтобы понять и увидеть вашу проблему. Вы должны сделать свой пример воспроизводимым, т.е. изменить свой пример так, чтобы нам нужно было только содержимое этого сообщения, чтобы увидеть, что твоя проблема. Если ссылка, которую вы разместили в своем посте, в будущем не работает, будущие пользователи не поймут, в чем заключалась ваша проблема, и, следовательно, с меньшей вероятностью поймут решение.   -  person bretauv    schedule 10.04.2020
comment
Спасибо, что указали на страницу с обсуждением воспроизводимых примеров. Текущее отображение приведенного выше кода — это минимум r-кода, необходимого для запуска и просмотра проблемы. Думаю, я мог бы включить код для создания примера филогенетического дерева, но, к сожалению, пока этого не сделал.   -  person Jenna Hamlin    schedule 10.04.2020
comment
Если вы не можете добавить образец своих данных, попробуйте воспроизвести этот пример с некоторыми данными, включенными в базу R (например, mtcars или iris).   -  person bretauv    schedule 10.04.2020
comment
Я добавил данные примера, построив дерево, которое не является файлом для чтения.   -  person Jenna Hamlin    schedule 10.04.2020


Ответы (2)


Надеюсь, вы уже нашли решение, но если нет, вот подход.

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

tree_plot <-
  ggtree::ggtree(vert.tree) +
  ggtree::geom_tiplab() +
  ggplot2::xlim(NA, 10)

tip_vector <- list(c("human", "lemur"), c("pig", "cow"))

make_layer <- function(tree, tips, label, color) {
  ggtree::geom_cladelabel(
    node = phytools::findMRCA(ape::as.phylo(tree), tips),
    label = label,
    color = color
  )
}

x + lapply(1:2, function(i)
  make_layer(
    tree_plot,
    tips = tip_vector[[i]],
    label = paste("Clade", i),
    color = "red"
  ))

Ключевой бит находится в вызове lapply, где создается слой аннотаций для каждого члена списка tip_vector.

Теперь, когда это работает, мы переходим к блестящему. В вашем приложении каждый раз, когда вы нажимаете add annotation, фрейм данных очищенных точек обновляется, и ваш вектор кончиков — это просто вектор новых очищенных кончиков. Любые ранее выбранные клады забываются.

Чтобы запомнить их, мы можем ввести два реактивных значения. Один n_annotations — это числовое reactiveVal, которое показывает, сколько раз мы нажимаем add annotation. Другой annotations — это список reactiveValues, в котором хранятся все очищенные клады под именами paste0("ann", n_annotations()).

Затем фактическое добавление слоя аннотаций происходит, как в нереактивном примере с циклическим циклом lapply по reactiveValues.

Код приложения:

# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.

library(shiny)
library(treeio)
library(ggtree)
library(phytools)
library(ape)

#make phylogenetic tree
text.string <-"(((((((cow, pig),whale),(bat,(lemur,human))),(robin,iguana)),coelacanth),gold_fish),shark);"

#read in the tree
vert.tree<-ape::read.tree(text=text.string)

# Define UI for application that draws a histogram
ui <- fluidPage(

  # Application title
  titlePanel("Test"),

  actionButton("add_annotation","Add clade annotation"),

  # Show a plot of the generated distribution
  mainPanel(plotOutput("treeDisplay", brush ="plot_brush"),
            plotOutput("treeDisplay2")
  )
)

# Define server logic required to draw a histogram
server <- function(input, output) {

  #reactive that holds base tree - this is how I am building the base tree 
  make_tree <- reactive({
    ggtree::ggtree(vert.tree) +
      ggtree::geom_tiplab() +
      ggplot2::xlim(NA, 10)
  })

  #render base tree
  output$treeDisplay <- renderPlot({
    make_tree()
  })

  # Initialize a reactive value and set to zero
  n_annotations <- reactiveVal(0)
  annotations <- reactiveValues()

  #reactive that holds the brushed points on a plot
  dataWithSelection <- reactive({
    brushedPoints(make_tree()$data, input$plot_brush)
  })

  #add to label to vector if isTip == True
  dataWithSelection2 <- eventReactive(input$plot_brush, {
    tipVector <- c()
    for (i in 1:length(dataWithSelection()$label)) {
      if (dataWithSelection()$isTip[i] == TRUE)
        tipVector <- c(tipVector, dataWithSelection()$label[i])
    }

    tipVector
  })

  make_layer <- function(tree, tips, label, color) {
    ggtree::geom_cladelabel(
      node = phytools::findMRCA(ape::as.phylo(tree), tips),
      label = label,
      color = color
    )
  }

  #display that layer onto the tree
  anno_plot <- eventReactive(input$add_annotation, {
    # update the reactive value
    new <- n_annotations() + 1
    n_annotations(new)
    annotations[[paste0("ann", n_annotations())]] <- dataWithSelection2()

    plt <-
      make_tree() +
      lapply(1:n_annotations(), function(i)
        make_layer(
          make_tree(),
          tips = annotations[[paste0("ann", i)]],
          label = paste("Clade", i),
          color = "red"
        ))

    return(plt)
  })

  output$treeDisplay2 <- renderPlot({
    anno_plot()
  })

}

# Run the application 
shinyApp(ui = ui, server = server)

Редактировать: как реактивные значения работают без филоматериалов

Я постарался подробно это прокомментировать.



ui <- basicPage(
  actionButton("add_anno", "Add annotation"),
  helpText("n_annotation is counting clicks"),
  textOutput("n_anno"),
  helpText("clades is accumulating clades"),
  verbatimTextOutput("clades")
)

server <- function(input, output) {
  # this initializes a reactive value
  # and sets the initial state to 0
  n_anno <- reactiveVal(0)

  # makes an empty reactive list
  # this can be populated and index
  # like a normal list 
  # e.g., clades[["first"]] <- c("bird", "lizard")
  clades <- reactiveValues()

  observeEvent(input$add_anno, {
    # increment the number of clicks
    new_count <- n_anno() + 1

    # update the reactiveValue
    # works the same way we initialized it
    # except instead of zero we set the incremented value
    n_anno(new_count)

    # making a name for an element in the clades list
    # we use the n_anno number of clicks to increment the clades
    # message just prints it on console
    message( paste0("clade", n_anno() ))

    # populate the list of clades for annotations
    clades[[ paste0("clade", n_anno() ) ]] <- sample(LETTERS, 3)
  })

  output$n_anno <- renderText(n_anno())
  output$clades <- renderPrint(
    str(reactiveValuesToList(clades))
    )
}

shinyApp(ui, server)
person teofil    schedule 27.04.2020
comment
спасибо за этот ответ. Это было действительно очень полезно. Я решал это по-другому - делал реактивное значение сгенерированных слоев, но это довольно приятно. Не могли бы вы объяснить немного больше, почему вам нужно сделать это `n_annotations(new)` и почему это работает `annotations[[paste0(ann, n_annotations())]] ‹-dataWithSelection2()`. Я могу напечатать вторую строку (аннотации....) и увидеть, что она генерирует метки подсказок и работает, но неясно, что она делает. Спасибо! - person Jenna Hamlin; 06.05.2020
comment
См. небольшое приложение в обновленном ответе, чтобы узнать, как работают и взаимодействуют реактивные значения. Кроме того, не стесняйтесь пинговать меня, если у вас возникнут похожие проблемы с блеском + фило. - person teofil; 07.05.2020

хммм - хорошо, когда я проверил ваше предложение

    dataWithSelection2 <- reactive({
        tipVector <- c()
        for (i in 1:length(dataWithSelection()$label)){ 
            if(!is.null(dataWithSelection()$isTip[i])) {
                tipVector <- c(tipVector,dataWithSelection()$label[i])
            }
        }
                return(tipVector)
    })

Я получаю сообщение об ошибке: отсутствует значение там, где нужно TRUE/FALSE....

person Jenna Hamlin    schedule 10.04.2020