2016-11-25 1 views
7

Ich bin vertraut mit den Grundlagen der glänzend, aber mit etwas hier kämpfen. Ich möchte in der Lage sein, eine ggplot-Ebene hinzuzufügen, wenn ein Punkt angeklickt wird, um diesen Punkt hervorzuheben. Ich weiß, dass dies mit ggvis möglich ist und es gibt ein schönes Beispiel in der Galerie, aber ich würde gerne nearPoints() verwenden können, um den Klick als UI-Eingabe zu erfassen.dynamische ggplot Schichten in glänzend mit nearPoints()

Ich habe etwas versucht (siehe unten), das abgesehen von der ggplot Schicht erscheint und dann verschwindet. Ich habe alle Arten von Änderungen an diesem mit reactive(), eventReactive() und so weiter versucht.

Jede Hilfe ist sehr geschätzt ...

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

Ich glaube, ich konzeptionell verstehen, warum dies nicht funktioniert. Das Diagramm hat eine Abhängigkeit von input$clicked, was bedeutet, dass wenn input$clicked sich ändert, das Plot erneut rendert, aber dies setzt wiederum input$clicked zurück. Etwas von einem Fang 22 Situation.

Antwort

8

Bitte, versuchen Sie dies:

Ansatz 1 (empfohlen)

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

Wenn Sie einen Punkt einmal klicken sie markiert ist. Wenn Sie ein zweites Mal klicken, wird die Hervorhebung wieder ausgeschaltet (Umschalten).

Der Code verwendet eine globale Variable selected_points die tatsächlich markiert (ausgewählt) Punkte zu speichern und einen reaktiven Ausdruck selected(), das die globale Variable aktualisiert, wenn ein Punkt angeklickt wird.

Die str(selected_points) kann helfen, die Arbeit zu visualisieren, kann aber entfernt werden.

Ansatz 2 (alternative)

Es gibt einen etwas anderen Ansatz, die selected_points aus einer Funktion direkt anstelle der Rückgabe des Objekts observe() statt reactive() und Referenzen die globale Variable verwendet:

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

Natürlich können Sie die globale Variable selected_points direkt im Aufruf ggplot verwenden, anstatt die reaktive Funktion selected() aufzurufen. Sie müssen jedoch sicherstellen, dass renderPlot() ausgeführt wird, wenn input$clicked geändert wird. Daher muss der Dummy-Verweis auf input$clicked im Code innerhalb renderPlot() enthalten sein.

Jetzt wird die reaktive Funktion selected() nicht mehr benötigt und kann durch einen observe() Ausdruck ersetzt werden. Im Gegensatz zu reactive() gibt observe() keinen Wert zurück. Es aktualisiert nur die globale Variable selected_points, wenn input$clicked geändert wird.

Ansatz 3 (reaktive Werte)

Dieser Ansatz vermeidet eine globale Variable. Stattdessen wird reactiveValues verwendet, um ein listenähnliches Objekt rv mit speziellen Funktionen für die reaktive Programmierung zu erstellen (siehe ?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) 
    }) 
    }) 
) 

Bitte beachten Sie, dass in den observer Teil Verweise auf rv müssen in isolate() gekapselt werden Änderungen, um sicherzustellen, dass nur input$clicked Ausführung des Codes in observer auslösen. Ansonsten erhalten wir eine Endlosschleife. Die Ausführung von renderPlot wird immer dann ausgelöst, wenn der reaktive Wert rv geändert wird. Persönlich

Fazit

, ziehe ich Ansatz 1 reaktive Funktionen, die die Abhängigkeiten (Reaktivität) deutlicher zu machen. Ich finde den Dummy-Aufruf zur Eingabe $ geklickt in Ansatz 2 weniger intuitiv. Ansatz 3 erfordert ein gründliches Verständnis der Reaktivität und die Verwendung von isolate() an den richtigen Stellen.

+0

danke das ist perfekt. Zuerst konnte ich nicht verstehen, warum der zweite 'geom_point' Layer die Datenquelle' selected() 'hat, aber jetzt kann ich sehen, dass die' selected() 'Funktion das' selected_points' Objekt zurückgibt. könnte ich, aus Interesse, 'selected_points' als Datenquelle haben und das Objekt nicht von der Funktion zurückgeben? Wenn ja, warum würdest du das lieber tun als das andere? – roman

+1

Nun, es dreht sich alles um Reaktivität oder welcher Code wird ausgeführt, wenn bestimmte Daten geändert werden (Reaktivität). Ja, Sie können 'selected_points' verwenden, aber Sie müssen die Reaktivität durch Hinzufügen eines Dummy-Aufrufs zu 'input $ clicked' erzwingen (siehe _Approach 2_ meiner bearbeiteten Antwort). Ich bevorzuge reaktive Funktionen, die die Abhängigkeiten expliziter machen. – Uwe

+0

danke für die erweiterte Antwort - sehr hilfreich. – roman