2016-11-25 2 views
7

Я знаком с основами блестящего, но борющегося с чем-то здесь. Я хотел бы иметь возможность добавить слой ggplot, когда нажимается точка, чтобы выделить эту точку. Я знаю, что это возможно с ggvis, и в галерее есть хороший пример, но я бы хотел использовать nearPoints() для захвата щелчка как ввода ui.динамические слои ggplot в блестящем с nearPoints()

Я попытался что-то (см. Ниже), который работает отдельно от слоя ggplot, и затем исчезает. Я пробовал все виды изменений с помощью reactive(), eventReactive() и так далее.

Любая помощь очень ценится ...

library(shiny) 
library(ggplot2) 

shinyApp(
    ui = shinyUI(
     plotOutput("plot", click = "clicked") 
    ), 

    server = shinyServer(function(input, output) { 
    output$plot <- renderPlot({ 
     ggplot(mtcars, aes(x = mpg, y = wt)) + 
     geom_point() + 
     geom_point(data = nearPoints(mtcars, input$clicked), colour = "red", size = 5) 
    }) 
    }) 
) 

Я думаю, я понимаю, почему это концептуально не работает. Участок имеет зависимость от input$clicked, что означает, что когда input$clicked изменяет изображение, оно снова сбрасывает input$clicked. Бит обложки 22.

ответ

8

Пожалуйста, попробуйте следующее:

подход 1 (рекомендуется)

library(shiny) 
library(ggplot2) 

# initialize global variable to record selected (clicked) rows 
selected_points <- mtcars[0, ] 
str(selected_points) 


shinyApp(
    ui = shinyUI(
    plotOutput("plot", click = "clicked") 
), 

    server = shinyServer(function(input, output) { 

    selected <- reactive({ 
     # add clicked 
     selected_points <<- rbind(selected_points, nearPoints(mtcars, input$clicked)) 
     # remove _all_ duplicates if any (toggle mode) 
     # http://stackoverflow.com/a/13763299/3817004 
     selected_points <<- 
     selected_points[!(duplicated(selected_points) | 
          duplicated(selected_points, fromLast = TRUE)), ] 
     str(selected_points) 
     return(selected_points) 
    }) 

    output$plot <- renderPlot({ 
     ggplot(mtcars, aes(x = mpg, y = wt)) + 
     geom_point() + 
     geom_point(data = selected(), colour = "red", size = 5) 
    }) 
    }) 
) 

Если нажать точку один раз, когда он будет выделен. Если вы щелкните по нему второй раз, подсветка снова выключится (переключается).

Код использует глобальную переменную selected_points для хранения фактически выделенных (выбранных) точек и реактивного выражения selected(), который обновляет глобальную переменную всякий раз, когда нажимается точка.

str(selected_points) может помочь визуализировать работу, но ее можно удалить.

подход 2 (альтернативный вариант)

Существует несколько иной подход, который использует observe() вместо reactive() и ссылки на глобальную переменную selected_points непосредственно вместо возврата объекта из функции:

library(shiny) 
library(ggplot2) 

selected_points <- mtcars[0, ] 
str(selected_points) 


shinyApp(
    ui = shinyUI(
    plotOutput("plot", click = "clicked") 
), 

    server = shinyServer(function(input, output) { 

    observe({ 
     # add clicked 
     selected_points <<- rbind(selected_points, nearPoints(mtcars, input$clicked)) 
     # remove _all_ duplicates (toggle) 
     # http://stackoverflow.com/a/13763299/3817004 
     selected_points <<- 
     selected_points[!(duplicated(selected_points) | 
          duplicated(selected_points, fromLast = TRUE)), ] 
     str(selected_points) 
    }) 

    output$plot <- renderPlot({ 
     # next statement is required for reactivity 
     input$clicked 
     ggplot(mtcars, aes(x = mpg, y = wt)) + 
     geom_point() + 
     geom_point(data = selected_points, colour = "red", size = 5) 
    }) 
    }) 
) 

Конечно , вы можете использовать глобальную переменную selected_points непосредственно в вызове ggplot вместо вызова реактивной функции selected(). Тем не менее, вы должны обеспечить, чтобы renderPlot() был выполнен, когда был изменен input$clicked. Следовательно, фиктивная ссылка на input$clicked должна быть включена в код в пределах renderPlot().

Теперь реактивная функция selected() больше не требуется и может быть заменена на выражение observe(). В отличие от reactive(), observe() не возвращает значение. Он просто обновляет глобальную переменную selected_points всякий раз, когда изменяется input$clicked.

Подход 3 (реактивные) значения

Такой подход позволяет избежать глобальной переменной. Вместо этого он использует reactiveValues для создания списка-подобного объекта rv со специальными возможностями для реактивного программирования (см. ?reactiveValues).

library(shiny) 
library(ggplot2) 

shinyApp(
    ui = shinyUI(
    plotOutput("plot", click = "clicked") 
), 

    server = shinyServer(function(input, output) { 

    rv <- reactiveValues(selected_points = mtcars[0, ]) 

    observe({ 
     # add clicked 
     rv$selected_points <- rbind(isolate(rv$selected_points), 
              nearPoints(mtcars, input$clicked)) 
     # remove _all_ duplicates (toggle) 
     # http://stackoverflow.com/a/13763299/3817004 
     rv$selected_points <- isolate(
     rv$selected_points[!(duplicated(rv$selected_points) | 
           duplicated(rv$selected_points, fromLast = TRUE)), ]) 
     str(rv$selected_points) 
    }) 

    output$plot <- renderPlot({ 
     ggplot(mtcars, aes(x = mpg, y = wt)) + 
     geom_point() + 
     geom_point(data = rv$selected_points, colour = "red", size = 5) 
    }) 
    }) 
) 

Пожалуйста, обратите внимание, что в ссылках observer части к rv должны быть заключены в isolate(), чтобы гарантировать, что только изменения в input$clicked вызовет выполнение кода в observer. В противном случае мы получим бесконечный цикл. Исполнение renderPlot запускается всякий раз, когда изменяется реактивное значение rv.

Заключение

Лично я предпочитаю подход 1, используя реактивные функции, которые делают зависимости (реакционная способность) более четко. Я считаю, что фиктивный вызов для ввода $ clicked in approach 2 менее интуитивно понятен. Подход 3 требует глубокого понимания реактивности и использования isolate() в нужных местах.

+0

спасибо, что это прекрасно. сначала я не мог понять, почему второй слой 'geom_point' имеет источник данных' selected() ', но теперь я вижу, что функция' selected() 'возвращает объект' selected_points'. из интереса, могу ли я выбрать «selected_points» в качестве источника данных и не возвращать объект из этой функции? если да, почему бы вам сделать это так, а не другим? – roman

+1

Ну, это все о реактивности или какой фрагмент кода будет выполняться при изменении конкретных данных (реактивность). Да, вы можете использовать 'selected_points', но вам нужно обеспечить реактивность, добавив фиктивный вызов' input $ clicked' (см. _Approach 2_ моего отредактированного ответа). Я предпочитаю использовать реактивные функции, которые делают зависимости более явными. – Uwe

+0

спасибо за расширенный ответ - очень полезно. – roman