Use plotlyProxy to add multiple traces when data changes - r

Thank to this question: SO-Q I have now understood how to remove traces. In this case, I simply remove 0:2, but I can change that to i.e. array(O:unique(factor(df$group))) to remove however many groups my model had created in a previous run.
What I haven't been able to figure out however, is how to add multiple traces, 1 for each factor in the target column, and color them by the colors in THECOLORS
library("shiny")
library("plotly")
rock[,2] <- sample(c('A', 'B', 'C'), 48, replace = T)
THECOLORS <- c('#383838', '#5b195b','#1A237E', '#000080', '#224D17', '#cccc00', '#b37400', '#990000')
ui <- fluidPage(
selectInput("dataset", "Choose a dataset:", choices = c("mtcars","rock")),
plotlyOutput("Plot1")
)
server <- function(input, output, session) {
dataSource <- reactive({switch(input$dataset,"rock" = rock,"mtcars" = mtcars)})
output$Plot1 <- renderPlotly({plot_ly(mtcars, x = ~mpg, y = ~hp, type = 'scatter', mode = 'markers', color = as.factor(mtcars$cyl), colors = THECOLORS) })
observeEvent(input$dataset, {
f <- list(
family = "Courier New, monospace",
size = 18,
color = "#7f7f7f"
)
x <- list(
title = "x Axis",
titlefont = f,
range = c(0,(max(dataSource()[,1])+ 0.1*max(dataSource()[,1])))
)
y <- list(
title = "y Axis",
titlefont = f,
range = c(0,(max(dataSource()[,4])+ 0.1*max(dataSource()[,4])))
)
plotlyProxy("Plot1", session) %>%
plotlyProxyInvoke("deleteTraces",array(0:2)) %>%
# lapply(unique(dataSource()[,2], function(x) { data <- dataSource()[which(dataSource()[,2] == x)],
# plotlyProxyInvoke("addTraces",
#
# x = data()[,1],
# y = data()[,4],
# type = 'scatter',
# mode = 'markers')}) %>%
plotlyProxyInvoke("relayout", list(xaxis = x, yaxis = y))
})
}
shinyApp(ui, server)

Basically when using plotlyProxy and than plotlyProxyInvoke with "addTraces", "addTraces" is used to add more traces.
You have to create a list of lists and each inner list would contain the details of each trace.
In your case with many traces to add maybe some of the functions from the purrr package could help in creating the list of lists defining the traces.
Take a look at the code below. It is a very simplified example, adding only two traces but the lists of list approach is there.
Regarding your comment about the speed maybe you could load data only when needed and partially if your app concept allows for that...
The code:
library("shiny")
library("plotly")
library(purrr)
ui <- fluidPage(
selectInput("dataset", "Choose a dataset:", choices = c("rock", "mtcars")),
plotlyOutput("Plot1")
)
server <- function(input, output, session) {
output$Plot1 <- renderPlotly({plot_ly(data = rock, x = ~area,
y =~peri, mode = 'markers', type = 'scatter')})
observeEvent(input$dataset, {
if (input$dataset == "rock") {
f <- list(
family = "Courier New, monospace",
size = 18,
color = "#7f7f7f"
)
x <- list(
title = "Area",
titlefont = f,
range = c(0, max(rock$area))
)
y <- list(
title = "Peri/Perm",
titlefont = f,
range = c(0, max(rock$peri))
)
plotlyProxyInvoke(plotlyProxy("Plot1", session), "addTraces", list(list(
x = rock$area,
y = rock$peri,
type = 'scatter',
mode = 'markers',
marker = list(size = 10,
color = 'rgba(255, 182, 193, .9)',
line = list(color = 'rgba(0, 255, 0, .3)',
width = 2))
),
list(
x = rock$area,
y = rock$perm,
type = 'scatter',
mode = 'markers',
marker = list(size = 10,
color = 'rgba(255, 182, 193, .9)',
line = list(color = 'rgba(152, 0, 0, .8)',
width = 2))
))
)
plotlyProxy("Plot1", session) %>%
plotlyProxyInvoke("deleteTraces", list(as.integer(0))) %>%
plotlyProxyInvoke("relayout", list(xaxis = x, yaxis = y))
} else {
f <- list(
family = "Courier New, monospace",
size = 18,
color = "#7f7f7f"
)
x <- list(
title = "hp",
titlefont = f,
range = c(0, max(mtcars$hp))
)
y <- list(
title = "mpg/cyl",
titlefont = f,
range = c(0, max(mtcars$mpg))
)
plotlyProxyInvoke(plotlyProxy("Plot1", session), "addTraces", list(list(
x = mtcars$hp,
y = mtcars$mpg,
type = 'scatter',
mode = 'markers',
marker = list(size = 10,
color = 'rgba(255, 182, 193, .9)',
line = list(color = 'rgba(0, 255, 0, .3)',
width = 2))
),
list(
x = mtcars$hp,
y = mtcars$cyl,
type = 'scatter',
mode = 'markers',
marker = list(size = 10,
color = 'rgba(255, 182, 193, .9)',
line = list(color = 'rgba(152, 0, 0, .8)',
width = 2))
))
)
plotlyProxy("Plot1", session) %>%
plotlyProxyInvoke("deleteTraces", list(as.integer(0))) %>%
plotlyProxyInvoke("relayout", list(xaxis = x, yaxis = y))
}
})
}
shinyApp(ui, server)

Related

How to change Y axis on reactive line plot in Plotly RShiny?

I'm using Plotly in Rshiny to make reactive plots based on what variables someone checks from a checkbox.
Right now, I have working code that I like, however, I would like to add more descriptive Y-axis labels to the plots, as currently there are none.
My biggest barrier right now is getting code that is flexible enough to change the Y axis names depending on what variables are selected.
I made a reproducible data frame using the Iris dataset. In this example, lets pretend the "Sepal.Length" column is in units of micro-meters (um), whereas "Sepal.Width", "Petal.Length", an "Petal.Width" are all in inches. When a user checks off both "Sepal.Length" and one of the other values, the other value displays as a practically flat line because they are not in the same units/order of magnitude. WITHOUT converting the units (I want to keep all units as they are), how would you go about adding reactive Y-axis labels to these plots (specifically when the "stacked" plot style is selected)? Or a y-axis label such that if "sepal.width", "petal.length", and "petal.width" were selected, the Layered plot would have a Y-axis of inches?
I'll attach my reproducible code below, thanks!
library(shiny)
library(dplyr)
library(stringr)
library(readtext)
library(XML)
library(data.table)
library(ecodata)
library(shinyBS)
library(huxtable)
library(gridExtra)
library(ggplot2)
library(shinyWidgets)
iris_dataset<-iris[,colnames(iris)!="Species"]
iris_dataset$Sepal.Length<-iris_dataset$Sepal.Length*1000
iris_dataset$Year <- c(1873:2022)
###### Define UI ######
ui <- fluidPage(
navbarPage(
"Visualizing Indicators",
tabPanel("Choose Variables & Plot",width=6,
# Input: Selector for choosing dataset
selectInput(inputId = "dataset",
label = "Choose a dataset:",
choices = c("iris_dataset")),
checkboxGroupInput("variable",
label = "Variable selection (Pick up to 5)",
choiceNames = c("Sepal Length","Sepal Width", "Petal Length","Petal Width"),
choiceValues = c("Sepal.Length","Sepal.Width", "Petal.Length","Petal.Width"),
selected = c("Sepal Width",multiple = TRUE)
),
# Input: Select plotting style ----
radioButtons("Plotting_Style", "Select Plotting Style",
choices = c("Layered" = "Layered",
"Stacked" = "Stacked"),
selected = 'Layered'),
mainPanel(width = 12,
# Output
tabsetPanel(type = "tabs",
tabPanel("Plot", plotlyOutput('plot'))
) #close tabsetpanel
)), # mainpanel, tabPanel
) # navbarPage
) # fluidPage
###### Define server function ######
server <- function(input, output) {
dataDf <- reactive({
temp <- get(input$dataset)
})
output$plot <- renderPlotly({
if (input$Plotting_Style == "Layered"){
if (length(input$variable) == 3){ #if only 3 variables are chosen
plot_ly(dataDf(), x = ~Year, y =~get(input$variable[1]),
type = 'scatter', mode = 'lines', name = paste(input$variable[1])) %>%
add_trace(dataDf(), x = ~Year, y = ~get(input$variable[2]),
type = 'scatter', mode = 'lines',name = paste(input$variable[2])) %>%
add_trace(dataDf(), x = ~Year, y = ~get(input$variable[3]),
type = 'scatter', mode = 'lines',name = paste(input$variable[3])) %>%
layout(xaxis = list(title = "Year"))
} else if (length(input$variable) > 1){ #if only 2 variables are chosen
plot_ly(dataDf(), x = ~Year, y =~get(input$variable[1]),
type = 'scatter', mode = 'lines', name = paste(input$variable[1])) %>%
add_trace(dataDf(), x = ~Year, y = ~get(input$variable[2]),
type = 'scatter', mode = 'lines',name = paste(input$variable[2])) %>%
layout(xaxis = list(title = "Year"))
} else { #plot individually if only 1 is selected
fig1<- plot_ly(dataDf(), x = ~Year, y =~get(input$variable[1]),
type = 'scatter', mode = 'lines', name = paste(input$variable[1]))
fig<-subplot(fig1, nrows = 1) %>%
layout(xaxis = list(title = "Year"),
xaxis = list(
zerolinecolor = '#ffff',
zerolinewidth = 2,
gridcolor = 'ffff'),
yaxis = list(
zerolinecolor = '#ffff',
zerolinewidth = 2,
gridcolor = 'ffff'))
fig
}#close if layered option
} else {
if (length(input$variable) == 3){ #if only 3 variables are chosen
fig1<- plot_ly(dataDf(), x = ~Year, y =~get(input$variable[1]),
type = 'scatter', mode = 'lines', name = paste(input$variable[1]))
fig2<- plot_ly(dataDf(), x = ~Year, y = ~get(input$variable[2]),
type = 'scatter', mode = 'lines',name = paste(input$variable[2]))
fig3<- plot_ly(dataDf(), x = ~Year, y =~get(input$variable[3]),
type = 'scatter', mode = 'lines', name = paste(input$variable[3]))
fig<-subplot(fig1, fig2,fig3,nrows = 3, shareX = TRUE) %>%
layout(xaxis = list(title = "Year"),
plot_bgcolor='#e5ecf6',
xaxis = list(
zerolinecolor = '#ffff',
zerolinewidth = 2,
gridcolor = 'ffff'),
yaxis = list(
zerolinecolor = '#ffff',
zerolinewidth = 2,
gridcolor = 'ffff'))
fig
} else if(length(input$variable) == 2){ #plot 2 variables together
fig1<- plot_ly(dataDf(), x = ~Year, y =~get(input$variable[1]),
type = 'scatter', mode = 'lines', name = paste(input$variable[1]))
fig2<- plot_ly(dataDf(), x = ~Year, y = ~get(input$variable[2]),
type = 'scatter', mode = 'lines',name = paste(input$variable[2]))
fig<-subplot(fig1, fig2, nrows = 2, shareX = TRUE) %>%
layout(xaxis = list(title = "Year"),
plot_bgcolor='#e5ecf6',
xaxis = list(
zerolinecolor = '#ffff',
zerolinewidth = 2,
gridcolor = 'ffff'),
yaxis = list(
zerolinecolor = '#ffff',
zerolinewidth = 2,
gridcolor = 'ffff'))
fig
} else { #plot individually if only 1 is selected
fig1<- plot_ly(dataDf(), x = ~Year, y =~get(input$variable[1]),
type = 'scatter', mode = 'lines', name = paste(input$variable[1]))
fig<-subplot(fig1, nrows = 1) %>%
layout(xaxis = list(title = "Year"),
plot_bgcolor='#e5ecf6',
xaxis = list(
zerolinecolor = '#ffff',
zerolinewidth = 2,
gridcolor = 'ffff'),
yaxis = list(
zerolinecolor = '#ffff',
zerolinewidth = 2,
gridcolor = 'ffff'))
fig
}
}#close if stacked option
}) #close renderPlotly
} # server
# Create Shiny object
shinyApp(ui = ui, server = server)

R Shiny: checkbox logic (if/else) - Why only the output of the last condition can be shown alone?

This is a simplified version of my project. It has two checkboxes: 'simple' and 'logistic'.
I hope to have the regression line shown when the corresponding checkbox is checked.
Here is what happens when I run my code:
When both boxes are checked: two regression lines show
When only the 'simple' box is checked: the simple regression line shows
BUT when only the 'logistic' box is checked: NOTHING SHOWS
I don't understand why this is happening or how to solve the problem. Does anyone have any ideas?
Thank you
server <- function(input, output) {
output$linePlot <- renderPlotly({
x<- seq(-10,10,0.1)
y1<- x*input$b1+input$a1
y2<- 1/(1+exp(-input$a2-input$b2*x))
#plot
p<-plot_ly()%>%
layout(xaxis = list(range=c(-10,10)), yaxis = list(range=c(-32,32)))
if (input$logistic){
p<-p%>%
add_trace(x=x,y=y2,type = 'scatter', mode = 'lines', name = "y2=1/(exp(-a2-b2*x))", line = list(color = 'rgb(22, 96, 167)'))
}
if (input$simple){
p<-p%>%
add_trace(x=x,y=y1,type = 'scatter', mode = 'lines', name = "y1=a1+b1*x",line = list(color = 'rgb(255, 129, 10)'))
}
})
}
ui <- fluidPage(
# Application title
titlePanel("Regression Line Simulator"),
# Sidebar with four slider inputs for a1, a2, b1, b2
sidebarLayout(
sidebarPanel(
# checkboxGroupInput("regression", label = "Type of regression",
# choices = list("Simple Linear" = 1, "Logistic" = 2),
# selected = c(1,2)),
#
checkboxInput("simple","Simple Regression", value = TRUE),
checkboxInput("logistic", "Logistic Regression", value = TRUE),
helpText("Select values on the sliders"),
conditionalPanel(
condition = "input.simple==true",
sliderInput("a1","Select value for a1",min = -0.8,max = 0.8,value = 0,step = 0.1),
sliderInput("b1","Select value for b1",min = -3,max = 3,value = 0,step = 0.1),
sliderInput("sd1","Select the standard deviation for y1_data",min = 0.1,max = 2.0,value = 0.1,step = 0.1),
),
conditionalPanel(
condition = "input.logistic==true",
sliderInput("a2", "Select value for a2",min = -0.8,max = 0.8,value = 0,step = 0.1),
sliderInput("b2","Select value for b2",min = -3,max = 3,value = 0,step = 0.1),
sliderInput("sd2","Select the standard deviation for y2_data",min = 0.1, max = 2.0,value = 0.1,step = 0.1)
),
conditionalPanel(
condition = "input.simple==true||input.logistic==true",
sliderInput("size", "Select the sample size",min = 5,max = 20,value = 5,step = 1),
actionButton("simulate", "Simulate!"),
actionButton("clear", "Clear"))
),
# Show a plot of the generated distribution
mainPanel(
plotlyOutput("linePlot")
)
)
)
I think you have to return p at the end of your renderPlotly:
output$linePlot <- renderPlotly({
x<- seq(-10,10,0.1)
y1<- x*input$b1+input$a1
y2<- 1/(1+exp(-input$a2-input$b2*x))
#plot
p<-plot_ly()%>%
layout(xaxis = list(range=c(-10,10)), yaxis = list(range=c(-32,32)))
if (input$logistic){
p<-p%>%
add_trace(x=x,y=y2,type = 'scatter', mode = 'lines', name = "y2=1/(exp(-a2-b2*x))", line = list(color = 'rgb(22, 96, 167)'))
}
if (input$simple){
p<-p%>%
add_trace(x=x,y=y1,type = 'scatter', mode = 'lines', name = "y1=a1+b1*x",line = list(color = 'rgb(255, 129, 10)'))
}
p
})

How to trigger a re-render of a plot when a column content of the plotted data changes with the use of reactive element

In the following app the user can select points in the plot by dragging, which should swap their Selected state between 0 and 1
points will get a shape and color depending on their 0 / 1 state, as a visual support for a user to select/deselect model parameters for the next model run.
in the version of the plots I had in my real app, the plotted data is a reactive variable values$RFImp_FP1 but I found out that the plot does not re-render when the content of column Selected of that data.table (or data.frame) changes.
Therefore I am trying to change it to a reactive object, but I'm failing to figure out how to change the Selected column of reactive data.table `RFImp
my attempts (comments in the code) so far produce either an assign error, or an infinite loop.
P.S.: Since i'm coding the stuff with lapply as I am using the code block several times in my app (identical "modules" with different serial number and using different data as the app takes the user through sequential stages of processing data), the second approach with values (app 2) has my preference as this allows me to do things like this:
lapply(c('FP1', 'FP2'), function(FP){
values[[paste('RFAcc', FP, sep = '_')]] <- ".... code to select a dataframe from model result list object values[[paste('RFResults', FP, sep = '_']]$Accuracy...."
which as far as I know can't be done with objectname <- reactive({....}) as you can't paste on the left side of the <- here
REACTIVE OBJECT APPROACH:
library(shiny)
library(plotly)
library(dplyr)
library(data.table)
ui <- fluidPage(
plotlyOutput('RFAcc_FP1', width = 450)
)
server <- function(input, output, session) {
values <- reactiveValues()
observe({
if(!is.null(RFImp_FP1()$Selected)) {
parsToChange <- event_data("plotly_selected", source = 'RFAcc_FP1')$y
if(!is.null(event_data("plotly_selected", source = 'RFAcc_FP1'))){
data_df <- RFImp_FP1()
data_df <- data_df %>% .[, Selected := if_else(Variables %in% parsToChange, 1-Selected, Selected)]
# how to get the reactive Data frame to update the selected
# values$Selected <- data_df$Selected #creates infinite loop.....
# RFImp_FP1$Selected <- data_df$Selected # throws an error
}
}
})
RFImp_FP1 <- reactive({
# in real app the dataframe RFImp_FP1 is a part of a list with randomForest results,
RFImp_FP1 <- data.table( MeanDecreaseAccuracy = runif(10, min = 0, max = 1), Variables = letters[1:10])
RFImp_FP1$Selected <- 1
# RFImp_FP1$Selected <- if(!is.null(values$Selected)){
# values$Selected } else {1 }
RFImp_FP1
})
output$RFAcc_FP1 <- renderPlotly({
RFImp_FP1()[order(MeanDecreaseAccuracy)]
RFImp_score <- RFImp_FP1()
plotheight <- length(RFImp_score$Variables) * 80
p <- plot_ly(data = RFImp_score,
source = 'RFAcc_FP1',
height = plotheight,
width = 450) %>%
add_trace(x = RFImp_score$MeanDecreaseAccuracy,
y = RFImp_score$Variables,
type = 'scatter',
mode = 'markers',
color = factor(RFImp_score$Selected),
colors = c('#1b73c1', '#797979'),
symbol = factor(RFImp_score$Selected),
symbols = c('circle','x'),
marker = list(size = 6),
hoverinfo = "text",
text = ~paste ('<br>', 'Parameter: ', RFImp_score$Variables,
'<br>', 'Mean decrease accuracy: ', format(round(RFImp_score$MeanDecreaseAccuracy*100, digits = 2), nsmall = 2),'%',
sep = '')) %>%
layout(
margin = list(l = 160, r= 20, b = 70, t = 50),
hoverlabel = list(font=list( color = '#1b73c1'), bgcolor='#f7fbff'),
xaxis = list(title = 'Mean decrease accuracy index (%)',
tickformat = "%",
showgrid = F,
showline = T,
zeroline = F,
nticks = 5,
font = list(size = 8),
ticks = "outside",
ticklen = 5,
tickwidth = 2,
tickcolor = toRGB("black")
),
yaxis = list(categoryarray = RFImp_score$Variables,
autorange = T,
showgrid = F,
showline = T,
autotick = T,
font = list(size = 8),
ticks = "outside",
ticklen = 5,
tickwidth = 2,
tickcolor = toRGB("black")
),
dragmode = "select"
) %>% add_annotations(x = 0.5,
y = 1.05,
textangle = 0,
font = list(size = 14,
color = 'black'),
text = "Contribution to accuracy",
showarrow = F,
xref='paper',
yref='paper')
p <- p %>% config(displayModeBar = F)
p
})
}
shinyApp(ui, server)
PREVIOUS reactiveValues() approach:
as you can see, with this app, the plot does not update when selecting a region in the plot even though the code changes the content of column Selected
ui <- fluidPage(
actionButton(inputId = 'Go', label = 'Go'),
plotlyOutput('RFAcc_FP1', width = 450)
)
server <- function(input, output, session) {
values <- reactiveValues()
observe({
if(!is.null(values$RFImp_FP1)) {
parsToChange <- event_data("plotly_selected", source = 'RFAcc_FP1')$y
if(!is.null(event_data("plotly_selected", source = 'RFAcc_FP1'))){
data_df <- values$RFImp_FP1
data_df <- data_df %>% .[, Selected := if_else(Variables %in% parsToChange, 1-Selected, Selected)]
values$RFImp_FP1 <- data_df
}
}
})
observeEvent(input$Go, {
values$RFImp_FP1 <- data.table(MeanDecreaseAccuracy = runif(10, min = 0, max = 1), Variables = letters[1:10])
values$RFImp_FP1$Selected <- 1
})
output$RFAcc_FP1 <- renderPlotly({
if(!is.null(values$RFImp_FP1)) {
RFImp_score <- values$RFImp_FP1[order(MeanDecreaseAccuracy)]
plotheight <- length(RFImp_score$Variables) * input$testme
p <- plot_ly(data = RFImp_score,
source = 'RFAcc_FP1',
height = plotheight,
width = 450) %>%
add_trace(x = RFImp_score$MeanDecreaseAccuracy,
y = RFImp_score$Variables,
type = 'scatter',
mode = 'markers',
color = factor(RFImp_score$Selected),
colors = c('#1b73c1', '#797979'),
symbol = factor(RFImp_score$Selected),
symbols = c('circle','x'),
marker = list(size = 6),
hoverinfo = "text",
text = ~paste ('<br>', 'Parameter: ', RFImp_score$Variables,
'<br>', 'Mean decrease accuracy: ', format(round(RFImp_score$MeanDecreaseAccuracy*100, digits = 2), nsmall = 2),'%',
sep = '')) %>%
layout(
margin = list(l = 160, r= 20, b = 70, t = 50),
hoverlabel = list(font=list( color = '#1b73c1'), bgcolor='#f7fbff'),
xaxis = list(title = 'Mean decrease accuracy index (%)',
tickformat = "%",
showgrid = F,
showline = T,
zeroline = F,
nticks = 5,
font = list(size = 8),
ticks = "outside",
ticklen = 5,
tickwidth = 2,
tickcolor = toRGB("black")
),
yaxis = list(categoryarray = RFImp_score$Variables,
autorange = T,
showgrid = F,
showline = T,
autotick = T,
font = list(size = 8),
ticks = "outside",
ticklen = 5,
tickwidth = 2,
tickcolor = toRGB("black")
),
dragmode = "select"
) %>% add_annotations(x = 0.5,
y = 1.05,
textangle = 0,
font = list(size = 14,
color = 'black'),
text = "Contribution to accuracy",
showarrow = F,
xref='paper',
yref='paper')
p$elementId <- NULL ## to surpress warning of widgetid
p <- p %>% config(displayModeBar = F)
p
} else {
p <- plot_ly( type = 'scatter', mode = 'markers', height = '400px', width = 450) %>% layout(
margin = list(l = 160, r= 20, b = 70, t = 50),
xaxis = list(title = 'Mean decrease accuracy index', range= c(0,1), nticks = 2, showline = TRUE),
yaxis = list(title = 'Model input variables', range = c(0,1), nticks = 2, showline = TRUE)) %>%
add_annotations(x = 0.5, y = 1.1, textangle = 0, font = list(size = 14, color = 'black'),
text = 'Contribution to accuracy',
showarrow = F, xref='paper', yref='paper')
p$elementId <- NULL
p <- p %>% config(displayModeBar = F)
p}
})
}
shinyApp(ui, server)
Not sure if this is what you want (it´s a bit weird that the plot updates with random numbers after selecting points ;-) ), but I hope it helps.
Instead of using a normal observer I use observeEvent that fires when selecting something in the plot. I generally prefer observeEvent to catch an event. This triggers an update ob a reactiveValues value, which will initially be NULL
library(shiny)
library(plotly)
library(dplyr)
library(data.table)
testDF <- data.table( MeanDecreaseAccuracy = runif(10, min = 0, max = 1), Variables = letters[1:10])
testDF$Selected <- T
ui <- fluidPage(
plotlyOutput('RFAcc_FP1', width = 450)
)
server <- function(input, output, session) {
values <- reactiveValues(val = NULL)
observeEvent(event_data("plotly_selected", source = 'RFAcc_FP1')$y, {
values$val <- runif(1, min = 0, max = 1)
})
RFImp_FP1 <- reactive({
RFImp_FP1 <- testDF
if(!is.null(values$val)) {
parsToChange <- event_data("plotly_selected", source = 'RFAcc_FP1')$y
RFImp_FP1 <- RFImp_FP1 %>% .[, Selected := if_else(Variables %in% parsToChange, 1-Selected, Selected)]
} else { }
# in real app the dataframe RFImp_FP1 is a part of a list with randomForest results,
RFImp_FP1
# RFImp_FP1$Selected <- if(!is.null(values$Selected)){
# values$Selected } else {1 }
})
output$RFAcc_FP1 <- renderPlotly({
RFImp_score <- RFImp_FP1()[order(MeanDecreaseAccuracy)]
plotheight <- length(RFImp_score$Variables) * 80
p <- plot_ly(data = RFImp_score,
source = 'RFAcc_FP1',
height = plotheight,
width = 450) %>%
add_trace(x = RFImp_score$MeanDecreaseAccuracy,
y = RFImp_score$Variables,
type = 'scatter',
mode = 'markers',
color = factor(RFImp_score$Selected),
colors = c('#1b73c1', '#797979'),
symbol = factor(RFImp_score$Selected),
symbols = c('circle','x'),
marker = list(size = 6),
hoverinfo = "text",
text = ~paste ('<br>', 'Parameter: ', RFImp_score$Variables,
'<br>', 'Mean decrease accuracy: ', format(round(RFImp_score$MeanDecreaseAccuracy*100, digits = 2), nsmall = 2),'%',
sep = '')) %>%
layout(
margin = list(l = 160, r= 20, b = 70, t = 50),
hoverlabel = list(font=list( color = '#1b73c1'), bgcolor='#f7fbff'),
xaxis = list(title = 'Mean decrease accuracy index (%)',
tickformat = "%",
showgrid = F,
showline = T,
zeroline = F,
nticks = 5,
font = list(size = 8),
ticks = "outside",
ticklen = 5,
tickwidth = 2,
tickcolor = toRGB("black")
),
yaxis = list(categoryarray = RFImp_score$Variables,
autorange = T,
showgrid = F,
showline = T,
autotick = T,
font = list(size = 8),
ticks = "outside",
ticklen = 5,
tickwidth = 2,
tickcolor = toRGB("black")
),
dragmode = "select"
) %>% add_annotations(x = 0.5,
y = 1.05,
textangle = 0,
font = list(size = 14,
color = 'black'),
text = "Contribution to accuracy",
showarrow = F,
xref='paper',
yref='paper')
p <- p %>% config(displayModeBar = F)
p
})
}
shinyApp(ui, server)

Why won't the plot update even though the data has changed

In the demo app below, the user can change the Selected state of the data rows by either clicking input$Go1 or select a region in the plot.
Selection the region in the plot is my intended functionality.
However, for a reason I fail to understand, the button does cause a re-render of the plot while select does not, even though both approaches have the same effect, i.e. a change in the values in column Selected of data.table RFImp_FP1
Why is it not working when I select points in the plot?
ui <- fluidPage(
actionButton(inputId = 'Go', label = 'Go'),
actionButton(inputId = 'Go2', label = 'Go2'),
plotlyOutput('RFAcc_FP1', width = 450)
)
server <- function(input, output, session) {
values <- reactiveValues()
observeEvent(input$Go, {
values$RFImp_FP1 <- data.table(MeanDecreaseAccuracy = runif(10, min = 0, max = 1), Variables = letters[1:10])
values$RFImp_FP1$Selected <- 1
})
observeEvent(input$Go2,{
values$RFImp_FP1$Selected[1:4] <- 1-values$RFImp_FP1$Selected[1:4]
print(values$RFImp_FP1$Selected)
})
observe({
if(!is.null(values$RFImp_FP1)) {
parsToChange <- event_data("plotly_selected", source = 'RFAcc_FP1')$y
if(!is.null(event_data("plotly_selected", source = 'RFAcc_FP1'))){
data_df <- values$RFImp_FP1
data_df <- data_df %>% .[, Selected := if_else(Variables %in% parsToChange, 1-Selected, Selected)]
values$RFImp_FP1$Selected <- data_df$Selected
print(values$RFImp_FP1)
}
}
})
observeEvent(values$RFImp_FP1, {
print('seeing change')
})
output$RFAcc_FP1 <- renderPlotly({
values$RFImp_FP1
if(!is.null(values$RFImp_FP1)) {
RFImp_score <- values$RFImp_FP1[order(MeanDecreaseAccuracy)]
plotheight <- length(RFImp_score$Variables) * input$testme
p <- plot_ly(data = RFImp_score,
source = 'RFAcc_FP1',
height = plotheight,
width = 450) %>%
add_trace(x = RFImp_score$MeanDecreaseAccuracy,
y = RFImp_score$Variables,
type = 'scatter',
mode = 'markers',
color = factor(RFImp_score$Selected),
colors = c('#1b73c1', '#797979'),
symbol = factor(RFImp_score$Selected),
symbols = c('circle','x'),
marker = list(size = 6),
hoverinfo = "text",
text = ~paste ('<br>', 'Parameter: ', RFImp_score$Variables,
'<br>', 'Mean decrease accuracy: ', format(round(RFImp_score$MeanDecreaseAccuracy*100, digits = 2), nsmall = 2),'%',
sep = '')) %>%
layout(
margin = list(l = 160, r= 20, b = 70, t = 50),
hoverlabel = list(font=list( color = '#1b73c1'), bgcolor='#f7fbff'),
xaxis = list(title = 'Mean decrease accuracy index (%)',
tickformat = "%",
showgrid = F,
showline = T,
zeroline = F,
nticks = 5,
font = list(size = 8),
ticks = "outside",
ticklen = 5,
tickwidth = 2,
tickcolor = toRGB("black")
),
yaxis = list(categoryarray = RFImp_score$Variables,
autorange = T,
showgrid = F,
showline = T,
autotick = T,
font = list(size = 8),
ticks = "outside",
ticklen = 5,
tickwidth = 2,
tickcolor = toRGB("black")
),
dragmode = "select"
) %>% add_annotations(x = 0.5,
y = 1.05,
textangle = 0,
font = list(size = 14,
color = 'black'),
text = "Contribution to accuracy",
showarrow = F,
xref='paper',
yref='paper')
p$elementId <- NULL ## to surpress warning of widgetid
p <- p %>% config(displayModeBar = F)
p
} else {
p <- plot_ly( type = 'scatter', mode = 'markers', height = '400px', width = 450) %>% layout(
margin = list(l = 160, r= 20, b = 70, t = 50),
xaxis = list(title = 'Mean decrease accuracy index', range= c(0,1), nticks = 2, showline = TRUE),
yaxis = list(title = 'Model input variables', range = c(0,1), nticks = 2, showline = TRUE)) %>%
add_annotations(x = 0.5, y = 1.1, textangle = 0, font = list(size = 14, color = 'black'),
text = 'Contribution to accuracy',
showarrow = F, xref='paper', yref='paper')
p$elementId <- NULL
p <- p %>% config(displayModeBar = F)
p}
})
}
shinyApp(ui, server)
select vs button result:
Don't ask me why, but I managed to get it to work with observeEvent and assigning NULL to the values$RFImp_FP1 before reassigning the altered data.table to it
values$RFImp_FP1 <- NULL
values$RFImp_FP1<- resDF
Full version:
library(shiny)
library(plotly)
library(dplyr)
library(data.table)
testDF <- data.table( MeanDecreaseAccuracy = runif(10, min = 0, max = 1), Variables = letters[1:10])
testDF$Selected <- T
ui <- fluidPage(
plotlyOutput('RFAcc_FP1', width = 450)
)
server <- function(input, output, session) {
values <- reactiveValues(RFImp_FP1 = testDF)
observeEvent(event_data("plotly_selected", source = 'RFAcc_FP1')$y, {
parsToChange <- event_data("plotly_selected", source = 'RFAcc_FP1')$y
resDF <- values$RFImp_FP1 %>% .[, Selected := if_else(Variables %in% parsToChange, !Selected, Selected)]
values$RFImp_FP1 <- NULL ## without this line the plot does not react
values$RFImp_FP1<- resDF ## re-assign the altered data.table to the reactiveValue
})
output$RFAcc_FP1 <- renderPlotly({
RFImp_score <- values$RFImp_FP1[order(MeanDecreaseAccuracy)]
plotheight <- length(RFImp_score$Variables) * 80
p <- plot_ly(data = RFImp_score,
source = 'RFAcc_FP1',
height = plotheight,
width = 450) %>%
add_trace(x = RFImp_score$MeanDecreaseAccuracy,
y = RFImp_score$Variables,
type = 'scatter',
mode = 'markers',
color = factor(RFImp_score$Selected),
colors = c('#F0F0F0', '#1b73c1'),
symbol = factor(RFImp_score$Selected),
symbols = c('x', 'circle'),
marker = list(size = 6),
hoverinfo = "text",
text = ~paste ('<br>', 'Parameter: ', RFImp_score$Variables,
'<br>', 'Mean decrease accuracy: ', format(round(RFImp_score$MeanDecreaseAccuracy*100, digits = 2), nsmall = 2),'%',
sep = '')) %>%
layout(
margin = list(l = 160, r= 20, b = 70, t = 50),
hoverlabel = list(font=list( color = '#1b73c1'), bgcolor='#f7fbff'),
xaxis = list(title = 'Mean decrease accuracy index (%)',
tickformat = "%",
showgrid = F,
showline = T,
zeroline = F,
nticks = 5,
font = list(size = 8),
ticks = "outside",
ticklen = 5,
tickwidth = 2,
tickcolor = toRGB("black")
),
yaxis = list(categoryarray = RFImp_score$Variables,
autorange = T,
showgrid = F,
showline = T,
autotick = T,
font = list(size = 8),
ticks = "outside",
ticklen = 5,
tickwidth = 2,
tickcolor = toRGB("black")
),
dragmode = "select"
) %>% add_annotations(x = 0.5,
y = 1.05,
textangle = 0,
font = list(size = 14,
color = 'black'),
text = "Contribution to accuracy",
showarrow = F,
xref='paper',
yref='paper')
p <- p %>% config(displayModeBar = F)
p
})
}
shinyApp(ui, server)
and to avoid the plotly warnings about not being registered, we can change the observe structure to
observe({
if(!is.null( values$RFImp_FP1)) {
values$Selected <- event_data("plotly_selected", source = 'RFAcc_FP1')$y
}
})
observeEvent(values$Selected, {
parsToChange <- event_data("plotly_selected", source = 'RFAcc_FP1')$y
if(!is.null(event_data("plotly_selected", source = 'RFAcc_FP1'))){
data_df <- values$RFImp_FP1
data_df <- data_df %>% .[, Selected := if_else(Variables %in% parsToChange, !Selected, Selected)]
values$RFImp_FP1 <- NULL
values$RFImp_FP1 <- data_df
}
})
One problem remains: making the same selection twice in a row does not trigger the observers as the selection is identical....

Shiny Plotly output that changes depending on conditions

I'm trying to make a shiny app for some user-friendly data analysis of some data I have, and I'd like to change the outputted Plotly plot depending on which file i'm looking at. Basically, I'd like to have one plot outputted at a time, where I can cycle through several plots (that don't change place in my shiny app) depending on which folder and criteria i'm using. Currently I'm struggeling with this, and I don't know exactly what to do from here. I've attached a few images to clarify what I mean and what I want.
This photo shows my UI and how I want my figures to be displayed. I'd like all figures to show in that same location, depending on the selected file.
When I switch to 'Datalogger', a new plot is generated, and it is outputted below the first one. I'd like it to be placed on top of it, in the exact same location.
Any help you can offer would be very welcome.
Best,
T.
Script:
# Load packages
library(shiny)
library(shinythemes)
library(dplyr)
library(readr)
library(lubridate)
library(plotly)
#picarro
time = as.character(seq(as.POSIXct("2018-06-01 12:00:00"), as.POSIXct("2018-06-01 12:10:00"), by=seconds() )); ch4.corr = runif(length(time), 1980, 2000);
data = data.frame(time, ch4.corr); data$time = as.POSIXct(time);
#datalogger
time = as.character(seq(as.POSIXct("2018-06-01 12:00:00"), as.POSIXct("2018-06-01 12:10:00"), by=seconds() )); PressureOut = runif(length(time), 1010, 1020);
dlog = data.frame(time, PressureOut); dlog$time = as.POSIXct(time);
#dronelog
time = as.character(seq(as.POSIXct("2018-06-01 12:00:00"), as.POSIXct("2018-06-01 12:10:00"), by=seconds() ));
ulog = data.frame(time); ulog$time = as.POSIXct(time);
#------------------------------------------------------------------------------
ui <- fluidPage(
titlePanel("Active AirCore analysis"),
hr(),
fluidRow(
column(3,
radioButtons("fileInput", "File",
choices = c("Picarro", "Datalogger", "Dronelog"),
selected = "Picarro"),
hr(),
conditionalPanel(
condition = "input.fileInput == 'Picarro'",
sliderInput("timeInputPicarro", "Time", as.POSIXct(data$time[1]), as.POSIXct(data$time[length(data$time)]), c(as.POSIXct(data$time[1])+minutes(1), as.POSIXct(data$time[length(data$time)])-minutes(1)), timeFormat = "%H:%M:%S", ticks = T, step = seconds(1), pre = "")),
conditionalPanel(
condition = "input.fileInput == 'Datalogger'",
sliderInput("timeInputDatalogger", "Time", as.POSIXct(dlog$time[1]), as.POSIXct(dlog$time[length(dlog$time)]), c(as.POSIXct(dlog$time[1]), as.POSIXct(dlog$time[length(dlog$time)])), timeFormat = "%H:%M:%S", ticks = T, step = seconds(1), pre = "")),
conditionalPanel(
condition = "input.fileInput == 'Dronelog'",
sliderInput("timeInputDronelog", "Time", as.POSIXct(ulog$time[1]), as.POSIXct(ulog$time[length(ulog$time)]), c(as.POSIXct(ulog$time[1])+minutes(1), as.POSIXct(ulog$time[length(ulog$time)])-minutes(1)), timeFormat = "%H:%M:%S", ticks = T, step = seconds(1), pre = "")),
hr(),
conditionalPanel(
condition = "input.fileInput == 'Picarro'",
radioButtons("picarroPlotInput", "Plot type",
choices = c("Time-series", "Process"),
selected = "Time-series")),
conditionalPanel(
condition = "input.fileInput == 'Datalogger'",
radioButtons("dataloggerPlotInput", "Plot type",
choices = c("Time-series", "Altitude"),
selected = "Time-series")),
hr(),
checkboxGroupInput(inputId='sidebarOptions',
label=('Options'),
choices=c('Blabla', 'Store data', 'BlablaBla')),
hr()),
br(),
mainPanel(
plotlyOutput("dataplot"),
hr(),
plotlyOutput("dlogplot")
)
)
)
server <- function(input, output, session) {
datasetInputPic <- reactive({ data = data; })
datasetInputPicSamp <- reactive({ dat = data[(data$time>=input$timeInputPicarro[1]) & (data$time<=input$timeInputPicarro[2]),]; })
datasetInputDatalogger <- reactive({ dlog = dlog })
datasetInputDronelog <- reactive({ ulog = ulog })
output$dataplot <- renderPlotly({
if( (input$fileInput == 'Picarro' ) & (input$picarroPlotInput == 'Time-series')){
data = datasetInputPic();
data$time = as.POSIXct(data$time);
dat = datasetInputPicSamp();
dat$time = as.POSIXct(dat$time);
sec.col = "red";
f = list(size = 8);
x <- list(title = " ")
y <- list(title = "CH<sub>4</sub> [ppb]")
p2 = plot_ly() %>%
add_trace(data = data,
x = ~time,
y = ~ch4.corr,
type = 'scatter',
mode = "markers",
marker = list(size = 3, color = 'black')) %>%
add_trace(data = dat,
x = ~time,
y = ~ch4.corr,
type = 'scatter',
mode = "markers",
marker = list(size = 3, color = sec.col)) %>%
layout(xaxis = x, yaxis = y, title = '', showlegend = F, titlefont = f);
s1 = subplot(p2, margin = 0.06,nrows=1,titleY = TRUE) %>%
layout(showlegend = F, margin = list(l=50, r=0, b=50, t=10), titlefont = f);
s1
}
})
output$dlogplot <- renderPlotly({
if( (input$fileInput == 'Datalogger' ) & (input$dataloggerPlotInput == 'Time-series')){
data = datasetInputDatalogger();
data$time = as.POSIXct(data$time);
x <- list(title = " ")
y <- list(title = "Outside pressure [mbar]")
p1 = plot_ly() %>%
add_trace(data = data,
y = ~PressureOut,
x = ~time,
type = 'scatter',
mode = "markers",
marker = list(size = 3, color = 'black'));
s1 = subplot(p1, margin = 0.07, nrows=2, titleY = TRUE, titleX = FALSE)
layout(s1, showlegend = F, margin = list(l=100, r=100, b=0, t=100), title = "Datalogger data")
s1
}
})
outputOptions(output, c("dataplot", "dlogplot"), suspendWhenHidden = TRUE)
}
runApp(list(ui = ui, server = server))
Your issue is that in your ui you have written:
mainPanel(
plotlyOutput("dataplot"),
hr(),
plotlyOutput("dlogplot")
)
Using this structure, the "dlogplot" will always display below the "dataplot" because you essentially gave it its own position in the main panel that is below the "dataplot". One solution, if you want the plots to be displayed in the same exact spot when clicking the various buttons, is to give only one plotlyOutput. Next you would put conditional if, else if and else in renderPlotly. For example:
output$dataplot <- renderPlotly({
if( (input$fileInput == 'Picarro' ) & (input$picarroPlotInput == 'Time-series')){
data = datasetInputPic();
data$time = as.POSIXct(data$time);
dat = datasetInputPicSamp();
dat$time = as.POSIXct(dat$time);
sec.col = "red";
f = list(size = 8);
x <- list(title = " ")
y <- list(title = "CH<sub>4</sub> [ppb]")
p2 = plot_ly() %>%
add_trace(data = data,
x = ~time,
y = ~ch4.corr,
type = 'scatter',
mode = "markers",
marker = list(size = 3, color = 'black')) %>%
add_trace(data = dat,
x = ~time,
y = ~ch4.corr,
type = 'scatter',
mode = "markers",
marker = list(size = 3, color = sec.col)) %>%
layout(xaxis = x, yaxis = y, title = '', showlegend = F, titlefont = f);
s1 = subplot(p2, margin = 0.06,nrows=1,titleY = TRUE) %>%
layout(showlegend = F, margin = list(l=50, r=0, b=50, t=10), titlefont = f);
s1
}
else if( (input$fileInput == 'Datalogger' ) & (input$dataloggerPlotInput == 'Time-series')){
data = datasetInputDatalogger();
data$time = as.POSIXct(data$time);
x <- list(title = " ")
y <- list(title = "Outside pressure [mbar]")
p1 = plot_ly() %>%
add_trace(data = data,
y = ~PressureOut,
x = ~time,
type = 'scatter',
mode = "markers",
marker = list(size = 3, color = 'black'));
s1 = subplot(p1, margin = 0.07, nrows=2, titleY = TRUE, titleX = FALSE)
layout(s1, showlegend = F, margin = list(l=100, r=100, b=0, t=100), title = "Datalogger data")
s1
}
})
This code will put the "dlogplot" and the "dataplot" in the same position in your main panel. (You would also need to get rid of output$dlogplot <- renderPlotly({...}) so that it isn't also trying to make that plot.)
Try this out and see if it works for your purposes.

Resources