Add custom data label in ggplotly scatterplot - r

I would like to display the Species for each data point when the cursor is over the point rather than the than the x and y values. I use the iris dataset. Also I want to be able to click on a data point to make the label persistent and not get disapperaed when I choose a new spot in the plot. (if possible ). The basic is the label. The persistence issue is a plus. Here is my app:
## Note: extrafont is a bit finnicky on Windows,
## so be sure to execute the code in the order
## provided, or else ggplot won't find the font
# Use this to acquire additional fonts not found in R
install.packages("extrafont");library(extrafont)
# Warning: if not specified in font_import, it will
# take a bit of time to get all fonts
font_import(pattern = "calibri")
loadfonts(device = "win")
#ui.r
library(shiny)
library(ggplot2)
library(plotly)
library(extrafont)
library(ggrepel)
fluidPage(
# App title ----
titlePanel(div("CROSS CORRELATION",style = "color:blue")),
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
# Input: Select a file ----
fileInput("file1", "Input CSV-File",
multiple = TRUE,
accept = c("text/csv",
"text/comma-separated-values,text/plain",
".csv")),
# Horizontal line ----
tags$hr(),
# Input: Checkbox if file has header ----
checkboxInput("header", "Header", TRUE),
# Input: Select separator ----
radioButtons("sep", "Separator",
choices = c(Comma = ",",
Semicolon = ";",
Tab = "\t"),
selected = ","),
# Horizontal line ----
tags$hr(),
# Input: Select number of rows to display ----
radioButtons("disp", "Display",
choices = c(Head = "head",
All = "all"),
selected = "head")
),
# Main panel for displaying outputs ----
mainPanel(
tabsetPanel(type = "tabs",
tabPanel("Table",
shiny::dataTableOutput("contents")),
tabPanel("Correlation Plot",
tags$style(type="text/css", "
#loadmessage {
position: fixed;
top: 0px;
left: 0px;
width: 100%;
padding: 5px 0px 5px 0px;
text-align: center;
font-weight: bold;
font-size: 100%;
color: #000000;
background-color: #CCFF66;
z-index: 105;
}
"),conditionalPanel(condition="$('html').hasClass('shiny-busy')",
tags$div("Loading...",id="loadmessage")
),
fluidRow(
column(3, uiOutput("lx1")),
column(3,uiOutput("lx2"))),
hr(),
fluidRow(
tags$style(type="text/css",
".shiny-output-error { visibility: hidden; }",
".shiny-output-error:before { visibility: hidden; }"
),
column(3,uiOutput("td")),
column(3,uiOutput("an"))),
fluidRow(
plotlyOutput("sc"))
))
)))
#server.r
function(input, output) {
output$contents <- shiny::renderDataTable({
iris
})
output$lx1<-renderUI({
selectInput("lx1", label = h4("Select 1st Expression Profile"),
choices = colnames(iris[,1:4]),
selected = "Lex1")
})
output$lx2<-renderUI({
selectInput("lx2", label = h4("Select 2nd Expression Profile"),
choices = colnames(iris[,1:4]),
selected = "Lex2")
})
output$td<-renderUI({
radioButtons("td", label = h4("Trendline"),
choices = list("Add Trendline" = "lm", "Remove Trendline" = ""),
selected = "")
})
output$an<-renderUI({
radioButtons("an", label = h4("Correlation Coefficient"),
choices = list("Add Cor.Coef" = cor(subset(iris, select=c(input$lx1)),subset(iris, select=c(input$lx2))), "Remove Cor.Coef" = ""),
selected = "")
})
output$sc<-renderPlotly({
p1 <- ggplot(iris, aes_string(x = input$lx1, y = input$lx2))+
# Change the point options in geom_point
geom_point(color = "darkblue") +
# Change the title of the plot (can change axis titles
# in this option as well and add subtitle)
labs(title = "Cross Correlation") +
# Change where the tick marks are
scale_x_continuous(breaks = seq(0, 2.5, 30)) +
scale_y_continuous(breaks = seq(0, 2.5, 30)) +
# Change how the text looks for each element
theme(title = element_text(family = "Calibri",
size = 10,
face = "bold"),
axis.title = element_text(family = "Calibri Light",
size = 16,
face = "bold",
color = "darkgrey"),
axis.text = element_text(family = "Calibri",
size = 11))+
theme_bw()+
geom_smooth(method = input$td)+
annotate("text", x = 10, y = 10, label = as.character(input$an))
ggplotly(p1) %>%
layout(hoverlabel = list(bgcolor = "white",
font = list(family = "Calibri",
size = 9,
color = "black")))
})
}

1. Tooltip
You can change the tooltip in a number of ways, as described here. To just show Species in the tooltip, something like this should work:
library(ggplot2)
library(plotly)
p1 <- ggplot(iris, aes_string(x = "Sepal.Length",
y = "Sepal.Width",
key = "Species")) +
geom_point()
ggplotly(p1, source = "select", tooltip = c("key"))
2. Persistent Label
I'm not sure how to leave the plotly tooltip on the point upon clicking, but you could use a plotly click event to get the clicked point and then add a geom_text layer to your ggplot.
3. Minimal Example
I've adapated your code to make a simpler example. Generally, it's helpful if you create a minimal example and remove sections of your app that aren't needed to recreate your question (e.g. changing fonts).
library(shiny)
library(plotly)
library(ggplot2)
ui <- fluidPage(
plotlyOutput("iris")
)
server <- function(input, output, session) {
output$iris <- renderPlotly({
# set up plot
p1 <- ggplot(iris, aes_string(x = "Sepal.Length",
y = "Sepal.Width",
key = "Species")) +
geom_point()
# get clicked point
click_data <- event_data("plotly_click", source = "select")
# if a point has been clicked, add a label to the plot
if(!is.null(click_data)) {
label_data <- data.frame(x = click_data[["x"]],
y = click_data[["y"]],
label = click_data[["key"]],
stringsAsFactors = FALSE)
p1 <- p1 +
geom_text(data = label_data,
aes(x = x, y = y, label = label),
inherit.aes = FALSE, nudge_x = 0.25)
}
# return the plot
ggplotly(p1, source = "select", tooltip = c("key"))
})
}
shinyApp(ui, server)
Edit: Keep All Labels
You can store each click in a reactive data.frame using reactiveValues and use this data.frame for your geom_text layer.
library(shiny)
library(plotly)
library(ggplot2)
ui <- fluidPage(
plotlyOutput("iris")
)
server <- function(input, output, session) {
# 1. create reactive values
vals <- reactiveValues()
# 2. create df to store clicks
vals$click_all <- data.frame(x = numeric(),
y = numeric(),
label = character())
# 3. add points upon plot click
observe({
# get clicked point
click_data <- event_data("plotly_click", source = "select")
# get data for current point
label_data <- data.frame(x = click_data[["x"]],
y = click_data[["y"]],
label = click_data[["key"]],
stringsAsFactors = FALSE)
# add current point to df of all clicks
vals$click_all <- merge(vals$click_all,
label_data,
all = TRUE)
})
output$iris <- renderPlotly({
# set up plot
p1 <- ggplot(iris, aes_string(x = "Sepal.Length",
y = "Sepal.Width",
key = "Species")) +
geom_point() +
# 4. add labels for clicked points
geom_text(data = vals$click_all,
aes(x = x, y = y, label = label),
inherit.aes = FALSE, nudge_x = 0.25)
# return the plot
ggplotly(p1, source = "select", tooltip = c("key"))
})
}
shinyApp(ui, server)

Related

How to apply shinyjs on geomtext() to hide and show data labels

Hello I have created a shiny app which creates a scatter plot between selected variables. Then when I click on a data point the name of the point is printed in the plot. The problem is that when I update the plot with other variables the printed are not erased. My thought is to use this kind of logic:R shiny, shinyjs, remove plot and draw it again if button is clicked but Im not sure if I got it right as I want just the data labels to get disappeared and not the whole plot
#ui.r
library(shiny)
library(ggplot2)
library(plotly)
library(dplyr)
fluidPage(
useShinyjs(),
# App title ----
titlePanel(div("CROSS CORRELATION",style = "color:blue")),
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
),
# Main panel for displaying outputs ----
mainPanel(
tabsetPanel(type = "tabs",
tabPanel("Correlation Plot",
fluidRow(
column(3, uiOutput("lx1")),
column(3,uiOutput("lx2")),
actionButton("hideshow", "Hide/show plot")),
hr(),
fluidRow(
tags$style(type="text/css",
".shiny-output-error { visibility: hidden; }",
".shiny-output-error:before { visibility: hidden; }"
)
),
fluidRow(
plotlyOutput("sc"))
)
)
)))
#server.r
function(input, output,session) {
output$lx1<-renderUI({
selectInput("lx1", label = h4("Select 1st Expression Profile"),
choices = colnames(iris[,1:4]),
selected = "Lex1")
})
output$lx2<-renderUI({
selectInput("lx2", label = h4("Select 2nd Expression Profile"),
choices = colnames(iris[,1:4]),
selected = "Lex2")
})
# 1. create reactive values
vals <- reactiveValues()
# 2. create df to store clicks
vals$click_all <- data.frame(x = numeric(),
y = numeric(),
label = character())
# 3. add points upon plot click
observe({
# get clicked point
click_data <- event_data("plotly_click", source = "select")
# get data for current point
label_data <- data.frame(x = click_data[["x"]],
y = click_data[["y"]],
label = click_data[["key"]],
stringsAsFactors = FALSE)
# add current point to df of all clicks
vals$click_all <- merge(vals$click_all,
label_data,
all = TRUE)
})
output$sc<-renderPlotly({
p1 <- ggplot(iris, aes_string(x = input$lx1, y = input$lx2,key = "Species"))+
# Change the point options in geom_point
geom_point(color = "darkblue") +
# Change the title of the plot (can change axis titles
# in this option as well and add subtitle)
labs(title = "Cross Correlation") +
# Change where the tick marks are
# Change how the text looks for each element
theme_bw()+
# 4. add labels for clicked points
geom_text(data = vals$click_all,
aes(x = x, y = y, label = label),
inherit.aes = FALSE, nudge_x = 0.25)
ggplotly(p1,source = "select", tooltip = c("key")) %>%
layout(hoverlabel = list(bgcolor = "white",
font = list(family = "Calibri",
size = 9,
color = "black")))
})
observeEvent(input$hideshow, {
# every time the button is pressed, alternate between hiding and showing the plot
toggle("sc")
})
}

Shiny app breaks down when I choose specific coordinates in a ggplot

I have a shiny app which creates a scatter plot between selected variables of the mtcars dataset. As you can see I have modified the data labels in order to display the car type in every point instead of the x-y coordinates. The problem is that when I click on my trendline, on spots where there are no data -so the coordinates are displayed-the app is breaking down. Here is a reproducible example:
#ui.r
library(shiny)
library(ggplot2)
library(plotly)
library(dplyr)
fluidPage(
# App title ----
titlePanel(div("CROSS CORRELATION",style = "color:blue")),
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
),
# Main panel for displaying outputs ----
mainPanel(
tabsetPanel(type = "tabs",
tabPanel("Correlation Plot",
fluidRow(
column(3, uiOutput("lx1")),
column(3,uiOutput("lx2"))),
hr(),
fluidRow(
tags$style(type="text/css",
".shiny-output-error { visibility: hidden; }",
".shiny-output-error:before { visibility: hidden; }"
)
),
fluidRow(
plotlyOutput("sc"))
)
)
)))
#server.r
function(input, output) {
output$lx1<-renderUI({
selectInput("lx1", label = h4("Select 1st Expression Profile"),
choices = colnames(mtcars[,2:5]),
selected = "Lex1")
})
output$lx2<-renderUI({
selectInput("lx2", label = h4("Select 2nd Expression Profile"),
choices = colnames(mtcars[,2:5]),
selected = "Lex2")
})
# 1. create reactive values
vals <- reactiveValues()
# 2. create df to store clicks
vals$click_all <- data.frame(x = numeric(),
y = numeric(),
label = character())
# 3. add points upon plot click
observe({
# get clicked point
click_data <- event_data("plotly_click", source = "select")
# get data for current point
label_data <- data.frame(x = click_data[["x"]],
y = click_data[["y"]],
label = click_data[["key"]],
stringsAsFactors = FALSE)
# add current point to df of all clicks
vals$click_all <- merge(vals$click_all,
label_data,
all = TRUE)
})
output$sc<-renderPlotly({
mtcars$car <- row.names(mtcars)
p1 <- ggplot(mtcars, aes_string(x = input$lx1, y = input$lx2,key = "car",group="car"))+
# Change the point options in geom_point
geom_point(color = "darkblue") +
# Change the title of the plot (can change axis titles
# in this option as well and add subtitle)
labs(title = "Cross Correlation") +
# Change where the tick marks are
# Change how the text looks for each element
theme_bw()+
geom_smooth(aes(group = 1))+
# 4. add labels for clicked points
geom_text(data = vals$click_all,
aes(x = x, y = y, label = label),
inherit.aes = FALSE, nudge_x = 0.25)
ggplotly(p1,source = "select", tooltip = c("key")) %>%
layout(hoverlabel = list(bgcolor = "white",
font = list(family = "Calibri",
size = 9,
color = "black")))
})
}
As you said, the app breaks down after clicking on the trend line where there is no point that corresponds to a car. Let us stick to that scenario. You get following error:
Warning: Error in data.frame: arguments imply differing number of rows: 1, 0
The reason for this error is that after clicking on the trend line the data frame stored in click_data variable does not contain variable key.
You try to access this variable anyway via click_data[["key"]] and the output of it is NULL as it is not existent.
In the next step you want to build a new data.frame label_data, where label is assigned to NULL and hence the error.
label_data <- data.frame(x = click_data[["x"]], # it is fine because it is number
y = click_data[["y"]], # also fine
label = NULL, # label gets NULL
stringsAsFactors = FALSE)
We can simply reproduce this error with
> data.frame(x = 1, y = 1, label = NULL)
Error in data.frame(x = 1, y = 1, label = NULL) :
arguments imply differing number of rows: 1, 0
Now that we know why we get the error, we can find multiple solutions to it. One of them would be to require first that
click_data <- event_data("plotly_click", source = "select")
returns a data frame and then if it does not contain key variable, we set the value of label to "" with
label_ <- ifelse(is.null(click_data[["key"]]),
yes = "",
no = click_data[["key"]])
That is
observe({
# get clicked point
click_data <- event_data("plotly_click", source = "select")
# Require that click_data is available (does not return NULL)
req(click_data)
label_ <- ifelse(is.null(click_data[["key"]]),
yes = "",
no = click_data[["key"]])
# get data for current point
label_data <- data.frame(x = click_data[["x"]],
y = click_data[["y"]],
label = label_,
stringsAsFactors = FALSE)
# add current point to df of all clicks
vals$click_all <- merge(vals$click_all,
label_data,
all = TRUE)
})
Full code:
library(shiny)
library(ggplot2)
library(plotly)
library(dplyr)
ui <- fluidPage(
# App title ----
titlePanel(div("CROSS CORRELATION",style = "color:blue")),
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
),
# Main panel for displaying outputs ----
mainPanel(
tabsetPanel(type = "tabs",
tabPanel("Correlation Plot",
fluidRow(
column(3, uiOutput("lx1")),
column(3,uiOutput("lx2"))),
hr(),
fluidRow(
tags$style(type="text/css",
".shiny-output-error { visibility: hidden; }",
".shiny-output-error:before { visibility: hidden; }"
)
),
fluidRow(
plotlyOutput("sc"))
)
)
)))
#server.r
server <- function(input, output) {
output$lx1<-renderUI({
selectInput("lx1", label = h4("Select 1st Expression Profile"),
choices = colnames(mtcars[,2:5]),
selected = "Lex1")
})
output$lx2<-renderUI({
selectInput("lx2", label = h4("Select 2nd Expression Profile"),
choices = colnames(mtcars[,2:5]),
selected = "Lex2")
})
# 1. create reactive values
vals <- reactiveValues()
# 2. create df to store clicks
vals$click_all <- data.frame(x = numeric(),
y = numeric(),
label = character())
# 3. add points upon plot click
observe({
# get clicked point
click_data <- event_data("plotly_click", source = "select")
# Require that click_data is available (does not return NULL)
req(click_data)
label_ <- ifelse(is.null(click_data[["key"]]),
yes = "",
no = click_data[["key"]])
# get data for current point
label_data <- data.frame(x = click_data[["x"]],
y = click_data[["y"]],
label = label_,
stringsAsFactors = FALSE)
# add current point to df of all clicks
vals$click_all <- merge(vals$click_all,
label_data,
all = TRUE)
})
output$sc<-renderPlotly({
mtcars$car <- row.names(mtcars)
p1 <- ggplot(mtcars, aes_string(x = input$lx1, y = input$lx2,key = "car",group="car"))+
# Change the point options in geom_point
geom_point(color = "darkblue") +
# Change the title of the plot (can change axis titles
# in this option as well and add subtitle)
labs(title = "Cross Correlation") +
# Change where the tick marks are
# Change how the text looks for each element
theme_bw()+
geom_smooth(aes(group = 1))+
# 4. add labels for clicked points
geom_text(data = vals$click_all,
aes(x = x, y = y, label = label),
inherit.aes = FALSE, nudge_x = 0.25)
ggplotly(p1,source = "select", tooltip = c("key")) %>%
layout(hoverlabel = list(bgcolor = "white",
font = list(family = "Calibri",
size = 9,
color = "black")))
})
}
shinyApp(ui, server)

How to remove text (data labels) from a plot in a shiny app?

Hello I have created a shiny app which creates a scatter plot between selected variables. Then when I click on a data point the name of the point is printed in the plot. The problem is that when I update the plot with other variables the printed are not erased. Generally I would like some ideas on how remove the data labels from my plot.
library(shiny)
library(ggplot2)
library(plotly)
library(dplyr)
library(htmlwidgets)
js <- HTML(
"Shiny.addCustomMessageHandler('resetValue', function(variableName){
Shiny.onInputChange(variableName, null);
}
);"
)
fluidPage(
tags$head(tags$script(js)),
# App title ----
titlePanel(div("CROSS CORRELATION",style = "color:blue")),
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(width = 3
),
# Main panel for displaying outputs ----
mainPanel(
tabsetPanel(type = "tabs",
tabPanel("Table",
shiny::dataTableOutput("contents")),
tabPanel("Correlation Plot",
tags$style(type="text/css", "
#loadmessage {
position: fixed;
top: 0px;
left: 0px;
width: 100%;
padding: 5px 0px 5px 0px;
text-align: center;
font-weight: bold;
font-size: 100%;
color: #000000;
background-color: #CCFF66;
z-index: 105;
}
"),conditionalPanel(condition="$('html').hasClass('shiny-busy')",
tags$div("Loading...",id="loadmessage")
),
fluidRow(
column(3, uiOutput("lx1")),
column(3,uiOutput("lx2"))),
hr(),
fluidRow(
tags$style(type="text/css",
".shiny-output-error { visibility: hidden; }",
".shiny-output-error:before { visibility: hidden; }"
),
column(3,uiOutput("td")),
column(3,uiOutput("an"))
),
fluidRow(
plotlyOutput("sc"))
)
)
)))
#server.r
function(input, output,session) {
output$lx1<-renderUI({
selectInput("lx1", label = h4("Select 1st Expression Profile"),
choices = colnames(mtcars)
)
})
output$lx2<-renderUI({
selectInput("lx2", label = h4("Select 2nd Expression Profile"),
choices = colnames(mtcars)
)
})
output$td<-renderUI({
radioButtons("td", label = h4("Trendline"),
choices = list("Add Trendline" = "lm", "Remove Trendline" = ""),
selected = "")
})
output$an<-renderUI({
radioButtons("an", label = h4("Correlation Coefficient"),
choices = list("Add Cor.Coef" = cor(subset(mtcars, select=c(input$lx1)),subset(mtcars, select=c(input$lx2))), "Remove Cor.Coef" = ""),
selected = "")
})
# 1. create reactive values
vals <- reactiveValues()
# 2. create df to store clicks
vals$click_all <- data.frame(x = numeric(),
y = numeric(),
label = character())
# 3. add points upon plot click
observeEvent({event_data("plotly_click", source = "select")}, {
# get clicked point
click_data <- event_data("plotly_click", source = "select")
# check if from correct curve
if(!is.null(click_data) && click_data[["curveNumber"]] == 2) {
# get data for current point
label_data <- data.frame(x = click_data[["x"]],
y = click_data[["y"]],
label = click_data[["key"]],
stringsAsFactors = FALSE)
# add current point to df of all clicks
vals$click_all <- merge(vals$click_all,
label_data,
all = TRUE)
}
})
output$sc<-renderPlotly({
mtcars$model <- row.names(mtcars)
if(input$td=="lm"){
p1 <- ggplot(mtcars, aes_string(x = input$lx1, y = input$lx2,key = "model",group = 1))+
# Change the point options in geom_point
geom_point(color = "darkblue") +
# Change the title of the plot (can change axis titles
# in this option as well and add subtitle)
labs(title = "Cross Correlation") +
# Change where the tick marks are
scale_x_continuous(breaks = seq(0, 35, 5)) +
scale_y_continuous(breaks = seq(0, 35, 5)) +
# Change how the text looks for each element
theme(title = element_text(family = "Calibri",
size = 10,
face = "bold"),
axis.title = element_text(family = "Calibri Light",
size = 16,
face = "bold",
color = "darkgrey"),
axis.text = element_text(family = "Calibri",
size = 11))+
theme_bw()+
annotate("text", x = 5, y = 5, label = as.character(input$an))+
geom_smooth(aes(group = 1))+
# 4. add labels for clicked points
geom_text(data = vals$click_all,
aes(x = x, y = y, label = label),
inherit.aes = FALSE, nudge_y = -1,5)
}
else{
mtcars$model <- row.names(mtcars)
p1 <- ggplot(mtcars, aes_string(x = input$lx1, y = input$lx2,key = "model",group = 1))+
# Change the point options in geom_point
geom_point(color = "darkblue") +
# Change the title of the plot (can change axis titles
# in this option as well and add subtitle)
labs(title = "Cross Correlation") +
# Change where the tick marks are
scale_x_continuous(breaks = seq(0, 35, 5)) +
scale_y_continuous(breaks = seq(0, 35, 5)) +
# Change how the text looks for each element
theme(title = element_text(family = "Calibri",
size = 10,
face = "bold"),
axis.title = element_text(family = "Calibri Light",
size = 16,
face = "bold",
color = "darkgrey"),
axis.text = element_text(family = "Calibri",
size = 11))+
theme_bw()+
annotate("text", x = 5, y = 5, label = as.character(input$an))+
# 4. add labels for clicked points
geom_text(data = vals$click_all,
aes(x = x, y = y, label = label),
inherit.aes = FALSE, nudge_y = -1,5)
}
ggplotly(p1,source = "select", tooltip = c("key")) %>%
layout(hoverlabel = list(bgcolor = "white",
font = list(family = "Calibri",
size = 9,
color = "black")))
})
# 5a. reset plotly click event and vals$click_all upon changing plot inputs
observeEvent(c(
input$lx1,
input$lx2
), {
session$sendCustomMessage("resetValue", ".clientValue-plotly_click-select")
vals$click_all <- data.frame(x = numeric(),
y = numeric(),
label = character())
})
}
1. Reset the plotly event input
First, add this to the ui:
js <- HTML(
"Shiny.addCustomMessageHandler('resetValue', function(variableName){
Shiny.onInputChange(variableName, null);
}
);"
)
ui <- fluidPage(
tags$head(tags$script(js)),
...
)
Then, use the message handler in the server:
session$sendCustomMessage("resetValue", ".clientValue-plotly_click-select")
Note, the plotly event data follows this format: '.clientValue-event-source', where event is the type of event (e.g. plotly_click, plotly_hover, etc) and source is specified in the plot where the click event data are coming from.
This method was adapted from this answer. This article is also a useful reference.
2. Reset the reactive dataframe
vals$click_all <- data.frame(x = numeric(),
y = numeric(),
label = character())
3. Use observeEvent to trigger "resets" when plot variables change
observeEvent(c(
input$column_x,
input$column_y
), {
session$sendCustomMessage("resetValue", ".clientValue-plotly_click-select")
vals$click_all <- data.frame(x = numeric(),
y = numeric(),
label = character())
})
Note: you need to add the session argument to your server function, like this:
server <- function(input, output, session) {
...
}
Minimal example
library(shiny)
library(plotly)
library(htmlwidgets)
js <- HTML(
"Shiny.addCustomMessageHandler('resetValue', function(variableName){
Shiny.onInputChange(variableName, null);
}
);"
)
ui <- fluidPage(
# 5b. js to reset the plotly click event
tags$head(tags$script(js)),
fluidRow(column(width = 3,
selectInput("column_x", "X Variable", colnames(mtcars)),
selectInput("column_y", "Y Variable", colnames(mtcars))),
column(width = 9,
plotlyOutput("plot")
)
)
)
server <- function(input, output, session) {
# 1. create reactive values
vals <- reactiveValues()
# 2. create df to store clicks
vals$click_all <- data.frame(x = numeric(),
y = numeric(),
label = character())
# 3. add points upon plot click
observeEvent({event_data("plotly_click", source = "select")}, {
# get clicked point
click_data <- event_data("plotly_click", source = "select")
# check if from correct curve
if(!is.null(click_data) && click_data[["curveNumber"]] == 0) {
# get data for current point
label_data <- data.frame(x = click_data[["x"]],
y = click_data[["y"]],
label = click_data[["key"]],
stringsAsFactors = FALSE)
# add current point to df of all clicks
vals$click_all <- merge(vals$click_all,
label_data,
all = TRUE)
}
})
output$plot <- renderPlotly({
mtcars$model <- row.names(mtcars)
g <- ggplot(mtcars, aes_string(x = input$column_x,
y = input$column_y,
key = "model",
group = 1)) +
geom_point() +
geom_smooth(aes(group = 1)) +
# 4. add labels for clicked points
geom_text(data = vals$click_all,
aes(x = x, y = y, label = label),
inherit.aes = FALSE, nudge_x = 1.5)
ggplotly(g, source = "select", tooltip = c("key"))
})
# 5a. reset plotly click event and vals$click_all upon changing plot inputs
observeEvent(c(
input$column_x,
input$column_y
), {
session$sendCustomMessage("resetValue", ".clientValue-plotly_click-select")
vals$click_all <- data.frame(x = numeric(),
y = numeric(),
label = character())
})
}
shinyApp(ui, server)

How to add and remove geom_smooth() trendline in a ggplot2 scatter plot of a shiny app

Hello I have a simple shiny app that creates a scatter plot of mtcars' variables given as inputs. What I want to achieve is to give user the choice to display and hide the trendline created with geom_smooth(). I tried with if statement as you can see below but with no result. Any suggestions?
#ui.r
library(shiny)
library(ggplot2)
library(plotly)
library(dplyr)
fluidPage(
# App title ----
titlePanel(div("CROSS CORRELATION",style = "color:blue")),
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
),
# Main panel for displaying outputs ----
mainPanel(
tabsetPanel(type = "tabs",
tabPanel("Correlation Plot",
fluidRow(
column(3, uiOutput("lx1")),
column(3,uiOutput("lx2"))),
hr(),
fluidRow(
tags$style(type="text/css",
".shiny-output-error { visibility: hidden; }",
".shiny-output-error:before { visibility: hidden; }"
),
column(3,uiOutput("td"))
),
fluidRow(
plotlyOutput("sc"))
)
)
)))
#server.r
function(input, output) {
output$lx1<-renderUI({
selectInput("lx1", label = h4("Select 1st Expression Profile"),
choices = colnames(mtcars[,2:5]),
selected = "Lex1")
})
output$lx2<-renderUI({
selectInput("lx2", label = h4("Select 2nd Expression Profile"),
choices = colnames(mtcars[,2:5]),
selected = "Lex2")
})
output$td<-renderUI({
radioButtons("td", label = h4("Trendline"),
choices = list("Add Trendline" = "lm", "Remove Trendline" = ""),
selected = "")
})
# 1. create reactive values
vals <- reactiveValues()
# 2. create df to store clicks
vals$click_all <- data.frame(x = numeric(),
y = numeric(),
label = character())
# 3. add points upon plot click
observe({
# get clicked point
click_data <- event_data("plotly_click", source = "select")
# get data for current point
label_data <- data.frame(x = click_data[["x"]],
y = click_data[["y"]],
label = click_data[["key"]],
stringsAsFactors = FALSE)
# add current point to df of all clicks
vals$click_all <- merge(vals$click_all,
label_data,
all = TRUE)
})
output$sc<-renderPlotly({
mtcars$car <- row.names(mtcars)
p1 <- ggplot(mtcars, aes_string(x = input$lx1, y = input$lx2,key="car",group='car'))+
# Change the point options in geom_point
geom_point(color = "darkblue") +
# Change the title of the plot (can change axis titles
# in this option as well and add subtitle)
labs(title = "Cross Correlation")
# Change where the tick marks are
# Change how the text looks for each element
if(input$td=="lm"){
geom_smooth(aes(group = 1))+
# 4. add labels for clicked points
geom_text(data = vals$click_all,
aes(x = x, y = y, label = label),
inherit.aes = FALSE, nudge_x = 0.25)
}
else{
# 4. add labels for clicked points
geom_text(data = vals$click_all,
aes(x = x, y = y, label = label),
inherit.aes = FALSE, nudge_x = 0.25)
}
ggplotly(p1,source = "select", tooltip = c("key"))
})
}
Based on comment above:
if(input$td=="lm"){
p1 <- p1+geom_smooth(aes(group = 1))+
# 4. add labels for clicked points
geom_text(data = vals$click_all,
aes(x = x, y = y, label = label),
inherit.aes = FALSE, nudge_x = 0.25)
}
else{
# 4. add labels for clicked points
p1 <- p1+geom_text(data = vals$click_all,
aes(x = x, y = y, label = label),
inherit.aes = FALSE, nudge_x = 0.25)
}

Is it possible to adjust the distance between data points and data labels with pixels instead of axes values in plotly?

Hello I have created a shiny app which creates a scatter plot between different variables of iris dataset. The problem is that when the plot is updated by choosing different variables the data label changes its position when clicked as it is affected by max value of the selected variable. I tried using hjust = -.05 which works for ggplot2 but not with plotly. I was wondering if instead of nudge_x=0.25I could set the distance to pixels or something else that would keep the distance between data point and data label unaffected by the the axes values.
#ui.r
library(shiny)
library(ggplot2)
library(plotly)
library(dplyr)
fluidPage(
useShinyjs(),
# App title ----
titlePanel(div("CROSS CORRELATION",style = "color:blue")),
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
),
# Main panel for displaying outputs ----
mainPanel(
tabsetPanel(type = "tabs",
tabPanel("Correlation Plot",
fluidRow(
column(3, uiOutput("lx1")),
column(3,uiOutput("lx2")),
actionButton("hideshow", "Hide/show plot")),
hr(),
fluidRow(
tags$style(type="text/css",
".shiny-output-error { visibility: hidden; }",
".shiny-output-error:before { visibility: hidden; }"
)
),
fluidRow(
plotlyOutput("sc"))
)
)
)))
#server.r
function(input, output,session) {
output$lx1<-renderUI({
selectInput("lx1", label = h4("Select 1st Expression Profile"),
choices = colnames(iris[,1:4]),
selected = "Lex1")
})
output$lx2<-renderUI({
selectInput("lx2", label = h4("Select 2nd Expression Profile"),
choices = colnames(iris[,1:4]),
selected = "Lex2")
})
# 1. create reactive values
vals <- reactiveValues()
# 2. create df to store clicks
vals$click_all <- data.frame(x = numeric(),
y = numeric(),
label = character())
# 3. add points upon plot click
observe({
# get clicked point
click_data <- event_data("plotly_click", source = "select")
# get data for current point
label_data <- data.frame(x = click_data[["x"]],
y = click_data[["y"]],
label = click_data[["key"]],
stringsAsFactors = FALSE)
# add current point to df of all clicks
vals$click_all <- merge(vals$click_all,
label_data,
all = TRUE)
})
output$sc<-renderPlotly({
p1 <- ggplot(iris, aes_string(x = input$lx1, y = input$lx2,key = "Species"))+
# Change the point options in geom_point
geom_point(color = "darkblue") +
# Change the title of the plot (can change axis titles
# in this option as well and add subtitle)
labs(title = "Cross Correlation") +
# Change where the tick marks are
# Change how the text looks for each element
theme_bw()+
# 4. add labels for clicked points
geom_text(data = vals$click_all,
aes(x = x, y = y, label = label),
inherit.aes = FALSE, nudge_x = 0.25)
ggplotly(p1,source = "select", tooltip = c("key")) %>%
layout(hoverlabel = list(bgcolor = "white",
font = list(family = "Calibri",
size = 9,
color = "black")))
})
observeEvent(input$hideshow, {
# every time the button is pressed, alternate between hiding and showing the plot
toggle("sc")
})
}

Resources