2016-11-25 21 views
7

Tôi quen thuộc với những điều cơ bản về sáng bóng nhưng đang phải vật lộn với thứ gì đó ở đây. Tôi muốn có thể thêm một lớp ggplot khi một điểm được nhấp để làm nổi bật điểm đó. Tôi biết điều này là có thể với ggvis và có một ví dụ tốt đẹp trong bộ sưu tập, nhưng tôi muốn có thể sử dụng nearPoints() để nắm bắt các nhấp chuột như đầu vào ui.các lớp ggplot động trong sáng bóng với nearPoints()

Tôi đã thử một cái gì đó (xem bên dưới) hoạt động ngoài lớp ggplot xuất hiện và sau đó biến mất. Tôi đã thử tất cả các loại chỉnh sửa cho điều này với reactive(), eventReactive() và cứ tiếp tục như vậy.

Bất kỳ giúp đỡ được nhiều đánh giá cao ...

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) 
    }) 
    }) 
) 

Tôi nghĩ rằng tôi hiểu khái niệm tại sao điều này không hoạt động. Cốt truyện có sự phụ thuộc vào input$clicked có nghĩa là khi input$clicked thay đổi lần hiển thị lại cốt truyện nhưng điều này lần lượt đặt lại input$clicked. Bit của một tình huống bắt 22.

Trả lời

8

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.

+0

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

+1

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

+0

cảm ơn câu trả lời mở rộng - rất hữu ích. – roman

Các vấn đề liên quan