Hãy thử này:
Tiếp cận 1 (đề nghị)
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)
})
})
)
Nếu bạn nhấp vào một điểm một thời gian nó được tô sáng. Nếu bạn nhấp vào nó lần thứ hai thì điểm đánh dấu sẽ bị tắt một lần nữa (bật tắt).
Mã sử dụng biến toàn cầu selected_points
để lưu trữ các điểm được đánh dấu (được chọn) thực sự và biểu thức phản ứng selected()
cập nhật biến toàn cục bất cứ khi nào một điểm được nhấp.
str(selected_points)
có thể giúp hình dung hoạt động nhưng có thể bị xóa.
Tiếp cận 2 (thay thế)
Có một cách tiếp cận hơi khác nhau trong đó sử dụng observe()
thay vì reactive()
và tham khảo các biến toàn cầu selected_points
trực tiếp thay vì trả lại đối tượng từ một hàm:
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)
})
})
)
Dĩ nhiên , bạn có thể sử dụng biến toàn cầu selected_points
trực tiếp trong cuộc gọi ggplot
thay vì gọi hàm phản hồi selected()
. Tuy nhiên, bạn phải đảm bảo rằng renderPlot()
được thực hiện bất cứ khi nào input$clicked
được thay đổi. Do đó, tham chiếu giả tới input$clicked
phải được bao gồm trong mã trong vòng renderPlot()
.
Hiện tại, chức năng phản ứng selected()
không còn cần thiết và có thể được thay thế bằng biểu thức observe()
. Trái ngược với reactive()
, observe()
không trả lại giá trị. Nó chỉ cập nhật biến toàn cầu selected_points
bất cứ khi nào input$clicked
được sửa đổi.
Cách tiếp cận 3 (giá trị phản hồi)
Phương pháp này tránh biến toàn cầu. Thay vào đó, nó sử dụng reactiveValues
để tạo đối tượng giống như danh sách rv
với khả năng đặc biệt cho lập trình phản ứng (xem ?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)
})
})
)
Xin vui lòng, lưu ý rằng trong observer
tài liệu tham khảo phần để rv
cần phải được đóng gói trong isolate()
để đảm bảo rằng chỉ có những thay đổi để input$clicked
sẽ kích hoạt thực thi mã trong observer
. Nếu không, chúng tôi sẽ nhận được một vòng lặp vô tận. Thực hiện renderPlot
được kích hoạt bất cứ khi nào giá trị phản ứng rv
bị thay đổi.
Kết luận
Cá nhân, tôi thích cách tiếp cận 1 bằng cách sử dụng các chức năng phản ứng làm cho phụ thuộc (phản ứng) rõ ràng hơn. Tôi tìm thấy các cuộc gọi giả để đầu vào $ nhấp vào phương pháp tiếp cận 2 ít trực quan hơn. Phương pháp 3 đòi hỏi sự hiểu biết kỹ lưỡng về độ phản ứng và sử dụng isolate()
ở đúng nơi.
cảm ơn bạn thật hoàn hảo. lúc đầu tôi không thể hiểu tại sao lớp 'geom_point' thứ hai có nguồn dữ liệu' selected() 'nhưng bây giờ tôi có thể thấy rằng hàm' selected() 'trả về đối tượng' selected_points'. không quan tâm, tôi có thể có 'selected_points' làm nguồn dữ liệu và không trả về đối tượng từ hàm không? nếu vậy, tại sao bạn lại làm theo cách này chứ không phải cái kia? – roman
Vâng, đó là tất cả về phản ứng hoặc những gì đoạn mã sẽ được thực hiện khi một dữ liệu cụ thể được thay đổi (phản ứng). Có, bạn có thể sử dụng 'selected_points' nhưng bạn phải thực thi phản ứng bằng cách thêm một cuộc gọi giả để' input $ clicks' (xem _Approach 2_ của câu trả lời đã chỉnh sửa của tôi). Tôi thích sử dụng các hàm phản ứng làm cho các phụ thuộc rõ ràng hơn. – Uwe
cảm ơn câu trả lời mở rộng - rất hữu ích. – roman