Desired Outcome
I would like to be able to click a point in my plotly::ggplotly() graph within my {shiny} app and then display some info about that point in a separate output below.
MRE
In this simple example I can make it display the curveNumber, pointNumber, x, y, but it's not easy to unambiguously retrieve a particular data point from my original data from these values. I've read about using key within the plotly::renderPlotly() call although I'm not sure how I'd access that in a separate part of the app.
library(tidyverse)
library(plotly)
library(shiny)
# UI ----
ui <- fluidPage(plotlyOutput("plot"),
verbatimTextOutput("click"))
# server ----
server <- function(input, output) {
output$plot <- renderPlotly({
p <- mtcars %>%
rownames_to_column("car") %>%
ggplot(aes(x = disp, y = mpg, color = factor(cyl),
label = car)) +
geom_point(size = 4, alpha = 0.7)
ggplotly(p) %>%
event_register("plotly_click")
})
output$click <- renderPrint({
point <- event_data(event = "plotly_click", priority = "event")
if (is.null(point)) "No Point Selected" else point
})
}
# app ----
shinyApp(ui = ui, server = server)
Using code from here I figured out how to use key to identify the points and then retrieve that elsewhere from the event_data().
library(tidyverse)
library(plotly)
library(shiny)
d <- mtcars %>%
rownames_to_column("car")
# UI ----
ui <- fluidPage(plotlyOutput("plot"),
tableOutput("click"))
# server ----
server <- function(input, output) {
output$plot <- renderPlotly({
key <- d$car
p <- d %>%
ggplot(aes(x = disp, y = mpg, color = factor(cyl),
key = key)) +
geom_point(size = 4, alpha = 0.7)
ggplotly(p) %>%
event_register("plotly_click")
})
output$click <- renderTable({
point <- event_data(event = "plotly_click", priority = "event")
req(point) # to avoid error if no point is clicked
filter(d, car == point$key) # use the key to find selected point
})
}
# app ----
shinyApp(ui = ui, server = server)
Related
I am using R Shiny and I am plotting some 3D point cloud with plot_ly from the library plotly. I can move and zoom on the points.
When I click on some point, some information about the point is stored in a variable, but then this resets the visualization.
It is possible to prevent this reinitialization? E.g. I would like to zoom on some part of the data, and then successively click on the points without any reset..
Here is a reproducible example :
library(shiny)
library(plotly)
df=iris
ui <- fluidPage(
plotlyOutput("plot3D"),
textOutput("selection")
)
server <- function(input, output, session) {
react <- reactiveValues(value = 0)
output$plot3D <- renderPlotly({
click_data <- event_data("plotly_click", priority = "event")
if (!is.null(click_data)) {
react$value<-click_data$customdata
}
fig<-plot_ly(df,
x=~Sepal.Length,y=~Sepal.Width,z=~Petal.Length,
type="scatter3d",
mode = 'markers',
customdata = ~Species
)
fig
})
output$selection <- renderPrint({
react$value
})
}
shinyApp(ui = ui, server = server)
And here is a gif about what happens :
The issue is, that you are collecting the event_data inside your renderPlotly call. Accordingly your plot is re-rendered with each click event (reactive dependency).
Please check the following:
library(shiny)
library(plotly)
library(datasets)
DF <- iris
ui <- fluidPage(plotlyOutput("plot3D"),
textOutput("selection"))
server <- function(input, output, session) {
output$plot3D <- renderPlotly({
plot_ly(
DF,
x = ~ Sepal.Length,
y = ~ Sepal.Width,
z = ~ Petal.Length,
type = "scatter3d",
mode = 'markers',
customdata = ~ Species,
source = "myscatter3d"
)
})
click_data <- reactive({
event_data("plotly_click", source = "myscatter3d", priority = "event")
})
output$selection <- renderPrint({
click_data()
})
}
shinyApp(ui = ui, server = server)
How can I get the x and y coordinates of an interactive map created with ggplot and plotly in R shiny? I want to get the x axis values and based on that display other data. Here is some dummy code.
library(shiny)
library(plotly)
library(ggplot2)
ui <- fluidPage(
plotlyOutput("distPlot")
)
server <- function(input, output) {
output$distPlot <- renderPlotly({
gg1 = iris %>% ggplot(aes(x = Petal.Length, y = Petal.Width)) + geom_point()
ggplotly(gg1)
})
}
shinyApp(ui = ui, server = server)
Maybe this is what your are looking for. The plotly package offers a function event_data() to get e.g. the coordinates of click events inside of a shiny app. See here. If you have multiple plots you could use the source argument to set an id and to get the event data for a specific plot:
library(shiny)
library(plotly)
library(ggplot2)
ui <- fluidPage(
plotlyOutput("distPlot"),
verbatimTextOutput("info")
)
server <- function(input, output) {
output$distPlot <- renderPlotly({
gg1 = iris %>% ggplot(aes(x = Petal.Length, y = Petal.Width)) + geom_point()
ggplotly(gg1, source = "Plot1")
})
output$info <- renderPrint({
d <- event_data("plotly_click", source = "Plot1")
if (is.null(d)) {
"Click events appear here (double-click to clear)"
} else {
x <- round(d$x, 2)
y <- round(d$y, 2)
cat("[", x, ", ", y, "]", sep = "")
}
})
}
shinyApp(ui = ui, server = server)
I tried to calculate the average of pedal length (or width) within in each species and then plot it in bar graph using the shinny app. But the mean function in summarize keeps giving me issues.
library(datasets)
library(shiny)
library(dplyr)
library(ggplot2)
data("iris")
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Analyze Iris table"),
# Sidebar with a dropdown menu selection input for key meausre compoenent
sidebarLayout(
sidebarPanel(
selectInput("yInput", "Measuring element: ",
colnames(iris), selected = colnames(iris)[2]),
selectInput('xInput', 'Grouper: ',
colnames(iris), selected = colnames(iris)[5])
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("barPlot")
)
)
)
Define server logic required to draw a histogram
server <- function(input, output) {
by_xInput <- reactive({
iris %>%
group_by(input$xInput) %>%
summarize(n = length(input$xInput), mean_y = mean(input$yInput))
})
output$barPlot <- renderPlot({
# generate bins based on input$bins from ui.R
ggplot(data = by_xInput(), aes(x = input$xInput, y = mean_y)) +
geom_bar(stat = 'identity')
})
}
Run the application
shinyApp(ui = ui, server = server)
Here, it is a string element, so convert to symbol and evaluate (!!)
library(dplyr)
library(shiny)
library(ggplot2)
server <- function(input, output) {
by_xInput <- reactive({
iris %>%
group_by_at(input$xInput) %>%
# n() can replace the length
# convert string to symbol and evaluate (!!)
summarize(n = n(), mean_y = mean(!! rlang::sym(input$yInput)))
})
output$barPlot <- renderPlot({
# as the input is a string, use `aes_string`
ggplot(data = by_xInput(), aes_string(x = input$xInput, y = "mean_y")) +
geom_bar(stat = 'identity')
})
}
-testing
shinyApp(ui = ui, server = server)
-output
I have a bar graph which is part of a shiny app. I have created it with plotly. I would like the user to be able to select a part of the graph (click) and on clicking a datatable would show all rows corresponding to the values given in the hover text from that part of the chart.
So far I am able to show the output from event.data which isnt very interesting. How can I show the relevant rows from the original table?
library(plotly)
library(shiny)
ui <- fluidPage(
uiOutput("ChooserDropdown"),
plotlyOutput("plot2"),
DT::dataTableOutput("tblpolypDetail2")
)
server <- function(input, output, session) {
output$plot2 <- renderPlotly({
# use the key aesthetic/argument to help uniquely identify selected observations
#key <- row.names(mtcars)
browser()
p <- ggplot(iris,aes_string(iris$Species,input$Chooser)) + geom_col()
ggplotly(p,source = "subset") %>% layout(dragmode = "select")
})
output$tblpolypDetail2 <- renderDataTable({
event.data <- event_data("plotly_click", source = "subset")
print(event.data)
})
output$ChooserDropdown<-renderUI({
selectInput("Chooser", label = h4("Choose the endoscopic documentation column"),
choices = colnames(iris) ,selected = 1
)
})
}
shinyApp(ui, server)
I created a small demo where you can highlight rows in datatable by clicking the plotly graph.
You need to do it in two steps:
Map pointNumber of a click to rows in datatable(), you can create an external table for it.
You need to create a dataTableProxy where you can update a datatable
library(plotly)
library(DT)
library(shiny)
library(dplyr)
data <- as_tibble(iris) %>%
group_by(Species) %>%
summarise(avg = mean(Sepal.Width)) %>%
mutate(Species = as.character(Species))
species_mapping <- data.frame(
Species = data$Species,
row_id = 1:length(data$Species),
stringsAsFactors = FALSE
)
ui <- fluidPage(
DT::dataTableOutput("table"),
plotlyOutput("plot")
)
server <- function(input, output, session) {
output$plot <- renderPlotly({
p <- data %>%
ggplot() +
geom_col(aes(x = Species, y = avg))
# register this plotly object
plotly_object <- ggplotly(p,source = "source1")
event_register(plotly_object,event = "plotly_click")
plotly_object
})
output$table <- DT::renderDataTable(data)
# create a proxy where we can update datatable
proxy <- DT::dataTableProxy("table")
observe({
s <- event_data("plotly_click",source = "source1")
req(!is.null(s))
# map point number to Species
row_clicked <- species_mapping[s$pointNumber + 1,"row_id"]
proxy %>%
selectRows(NULL) %>%
selectRows(row_clicked)
})
}
shinyApp(ui, server)
Using R Shiny and plotly I created a interactive scatter plot.
How can I modify my code to interactively label only the points which were selected by the user?
Example plot
Thank you so much for your help!
All the best,
Christian
library(plotly)
library(shiny)
library(dplyr)
data <- data.frame(matrix(runif(500,0,1000), ncol = 2, nrow = 100)) %>%
mutate(ID = row_number())
ui <- fluidPage(
plotlyOutput("plot"),
verbatimTextOutput("hover"),
verbatimTextOutput("click"),
verbatimTextOutput("brush"),
verbatimTextOutput("zoom"))
server <- function(input, output, session) {
output$plot <- renderPlotly({
p <- ggplot(data, aes(x = X1, y = X2, key = ID)) +
geom_point()
ggplotly(p) %>% layout(dragmode = "select")
})
}
shinyApp(ui, server)
Below is a possible solution. I use a reactive function to "label" selected points. I wasn't sure how exactly you want to display the IDs for selected points. The code adds the ID as text when a point is selected. Also, I add some jitter to move the IDs away from the points.
library(plotly)
library(shiny)
library(dplyr)
data <- data.frame(matrix(runif(500,0,1000), ncol = 2, nrow = 100)) %>%
mutate(ID = row_number())
ui <- fluidPage(
plotlyOutput("plot"),
verbatimTextOutput("hover"),
verbatimTextOutput("click"),
verbatimTextOutput("brush"),
verbatimTextOutput("zoom"))
server <- function(input, output, session) {
output$plot <- renderPlotly({
data <- get_data()
p <- ggplot(data, aes(x = X1, y = X2, key = ID)) +
geom_point() + geom_text(data=subset(data, show_id),aes(X1,X2,label=ID), position = position_jitter(width = 20,height = 20))
ggplotly(p, source = "subset") %>% layout(dragmode = "select")
})
get_data <- reactive({
event.data <- event_data("plotly_selected", source = "subset")
data <- data %>% mutate(show_id = FALSE)
if (!is.null(event.data)) {
data$show_id[event.data$pointNumber + 1] <- TRUE
}
data
})
}
shinyApp(ui, server)