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.
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
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
danke für die erweiterte Antwort - sehr hilfreich. – roman