Пожалуйста, попробуйте следующее:
подход 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()
в нужных местах.
спасибо, что это прекрасно. сначала я не мог понять, почему второй слой 'geom_point' имеет источник данных' selected() ', но теперь я вижу, что функция' selected() 'возвращает объект' selected_points'. из интереса, могу ли я выбрать «selected_points» в качестве источника данных и не возвращать объект из этой функции? если да, почему бы вам сделать это так, а не другим? – roman
Ну, это все о реактивности или какой фрагмент кода будет выполняться при изменении конкретных данных (реактивность). Да, вы можете использовать 'selected_points', но вам нужно обеспечить реактивность, добавив фиктивный вызов' input $ clicked' (см. _Approach 2_ моего отредактированного ответа). Я предпочитаю использовать реактивные функции, которые делают зависимости более явными. – Uwe
спасибо за расширенный ответ - очень полезно. – roman