I am trying to update text and plot both with ActionButton click.
My Attempt-
library(shiny)
library(ggplot2)
library(shinyWidgets)
ui <- fluidPage(
actionGroupButtons(
inputIds = c("Bar", "Histogram", "Line"),
labels = list("Bar", "Histogram","Line"),
status = "danger",
fullwidth = T
),
plotOutput('plot',height = '563px'),
verbatimTextOutput('text')
)
server <- function(input, output) {
output$plot <- renderPlot({
if(req(input$Bar)!=0) {
isolate({
data <- iris
ggplot(data, aes(Species,Petal.Length)) +
geom_bar(stat="identity")
})
} else if(req(input$Histogram)>0){
isolate({
data <- iris
ggplot(data, aes(Petal.Length)) +
geom_histogram()
})
} else if(req(input$Line)>0){
isolate({
data <- iris
ggplot(data, aes(Petal.Length,Sepal.Length)) +
geom_line()
})
}
})
output$text <- renderText({
if(req(input$Bar)!=0) {
"Bar"
} else if(req(input$Histogram)>0){
"Histogram"
} else if(req(input$Line)>0){
"Line"
}
})
}
shinyApp(ui, server)
I want to change plot and text when the appropriate action button is clicked.
Here would be one way to do it.
In it's essence the approach is pointed out in the action button example no. 3 from RStudio.
library(shiny)
library(ggplot2)
library(shinyWidgets)
ui <- fluidPage(
actionGroupButtons(
inputIds = c("Bar", "Histogram", "Line"),
labels = list("Bar", "Histogram","Line"),
status = "danger",
fullwidth = T
),
plotOutput('plot',height = '563px'),
verbatimTextOutput('text')
)
server <- function(input, output) {
v <- reactiveValues(data = iris,
plot = NULL,
text = NULL)
observeEvent(input$Bar, {
v$plot <- ggplot(v$data, aes(Species,Petal.Length)) +
geom_bar(stat="identity")
v$text <- "Bar"
})
observeEvent(input$Histogram, {
data <- iris
v$plot <- ggplot(v$data, aes(Petal.Length)) +
geom_histogram()
v$text <- "Histogram"
})
observeEvent(input$Line, {
data <- iris
v$plot <- ggplot(v$data, aes(Petal.Length,Sepal.Length)) +
geom_line()
v$text <- "Line"
})
output$plot <- renderPlot({
if (is.null(v$plot)) return()
v$plot
})
output$text <- renderText({
if (is.null(v$text)) return()
v$text
})
}
shinyApp(ui, server)
Update
In case you are using Input filters on your data in a reactive, then you have to adjust the Approach above a litte:
library(shiny)
library(ggplot2)
library(shinyWidgets)
ui <- fluidPage(
selectInput(inputId = "species", label = "Select species:",
choices = unique(as.character(iris$Species)),
selected = "setosa"),
sliderInput("sepal_length", "Limit sepal length:",
round = 0,
min = range(iris$Sepal.Length)[1], max = range(iris$Sepal.Length)[2],
range(iris$Sepal.Length),
step = 0.1),
actionGroupButtons(
inputIds = c("Bar", "Histogram", "Line"),
labels = list("Bar", "Histogram","Line"),
status = "danger",
fullwidth = T
),
plotOutput('plot',height = '563px'),
verbatimTextOutput('text')
)
server <- function(input, output) {
data <- reactive({
temp <- subset(iris, Species == input$species)
subset(temp, Sepal.Length < input$sepal_length)
})
v <- reactiveValues(plot = NULL,
text = NULL)
observeEvent(input$Bar, {
v$plot <- ggplot(data(), aes(Species,Petal.Length)) +
geom_bar(stat="identity")
v$text <- "Bar"
})
observeEvent(input$Histogram, {
v$plot <- ggplot(data(), aes(Petal.Length)) +
geom_histogram()
v$text <- "Histogram"
})
observeEvent(input$Line, {
v$plot <- ggplot(data(), aes(Petal.Length,Sepal.Length)) +
geom_line()
v$text <- "Line"
})
output$plot <- renderPlot({
if (is.null(v$plot)) return()
v$plot
})
output$text <- renderText({
if (is.null(v$text)) return()
v$text
})
}
shinyApp(ui, server)
Related
My application has two selectInputs. It updates the secound selectInput depending on the first selectInput and then it plots a timeline for df data. The app works completely well, but when I try to modularize it, it doesn't work properly (just the selectInputs work, but no plot is built). I have created a minimal example. I really appreciate any help everybody can provide.
library(shiny)
library(plotly)
library(reshape2)
# data preparation
df<-data.frame(Name1<-c("Aix galericulata","Grus grus"," Alces alces"),
Name2<-c("Mandarin Duck","Common Crane" ,"Elk"),
eventDate<-c("2015-03-11","2015-03-10","2015-03-10"),
individualCount<-c(1, 10, 1)
)
colnames(df)<-c("Name1","Name2","eventDate","individualCount")
#----------------------------------------------------------------------------------------
# module dataselect
dataselect_ui<- function(id) {
ns<-NS(id)
tagList(
selectInput(ns("Nametype"),"Select a name type",
choices=c("Name1","Name2","choose"),selected = "choose"),
selectInput(ns("Name"),"Select a name",
choices="",selected = "",selectize=TRUE)
)
}
dataselect_server <- function(id) {
moduleServer(id, function(input, output, session) {
# Putting columns Name1 and Nam2 of df in one column called nameType using melt()function
# This format of data is needed for the choices argument of updateSelectizeInput()
df2<-reshape2::melt(df,id=c("eventDate","individualCount"))
colnames(df2)<-c("eventDate","individualCount","nameType","Name")
observeEvent(
input$Nametype,
updateSelectizeInput(session, "Name", "Select a name",
choices = unique(df2$Name[df2$nameType==input$Nametype]),selected = ""))
# finalDf() is the data used to plot the timeline
finalDf<-reactive({
if(input$Name=="choose"){
return(NULL)
}
if(input$Name==""){
return(NULL)
}
if(input$Nametype=="choose"){
return(NULL)
}
# if the first selectInput is set to Name1, from df select rows their Name1 column is
# equal to the second selectInput value
else if(input$Nametype=="Name1"){
finalDf<-df[which(df$Name1==input$Name) ,]
}
# if the first selectInput is set to Name2, from df select rows their Name2 column is
# equal to the second selectInput value
else if(input$Nametype=="Name2"){
finalDf<-df[which(df$Name2==input$Name) ,]
}
return(
reactive({
input$Name
})
)
})
})
}
#-------------------------------------------------------------------------------------
# application
ui <- fluidPage(
# Application title
navbarPage(
"app",
tabPanel("plot",
sidebarPanel(
dataselect_ui("dataselect")
),
mainPanel(
plotlyOutput("timeline")
)
)
)
)
server <- function(session,input, output) {
dataselect_server("dataselect")
# timeline plot
output$timeline <- renderPlotly({
req(input$Name)
p<-ggplot(finalDf(),aes(x=eventDate,y=individualCount)) +geom_point(alpha=0.2, shape=21, color="black",fill="red",size=5)+
labs( x = "Date Event",y= "Individual Count") +theme_bw()
p<-ggplotly(p)
p
})
}
shinyApp(ui = ui, server = server)
If you return input$Name from the server module, as you correctly do, you have to use the returned value of this module in renderPlotly:
server <- function(session,input, output) {
input_Name <- dataselect_server("dataselect")
# timeline plot
output$timeline <- renderPlotly({
req(input_Name()) # don't forget the parentheses!
p<-ggplot(finalDf(),aes(x=eventDate,y=individualCount)) +geom_point(alpha=0.2, shape=21, color="black",fill="red",size=5)+
labs( x = "Date Event",y= "Individual Count") +theme_bw()
p<-ggplotly(p)
p
})
}
EDIT
There is a problem in your code: your return statement of reactive(input$Name) is inside the reactive conductor finalDf.
Moreover you need to return finalDf as well, to use it outside the module.
So:
dataselect_server <- function(id) {
moduleServer(id, function(input, output, session) {
......
finalDf <- reactive({
if(input$Name=="choose"){
return(NULL)
}
if(input$Name==""){
return(NULL)
}
if(input$Nametype=="choose"){
return(NULL)
}
if(input$Nametype=="Name1") {
finalDf <- df[which(df$Name1==input$Name) ,]
} else if(input$Nametype=="Name2") {
finalDf <- df[which(df$Name2==input$Name) ,]
}
return(finalDf)
})
return(
list("finalDf" = finalDf, "input_Name" = reactive(input$Name))
)
})
}
and:
server <- function(session,input, output) {
module_outputs <- dataselect_server("dataselect")
input_Name <- module_outputs$input_Name
finalDf <- module_outputs$finalDf
# timeline plot
output$timeline <- renderPlotly({
req(input_Name()) # don't forget the parentheses!
p <- ggplot(finalDf(), aes(x = eventDate, y = individualCount))
+ geom_point(alpha = 0.2, shape = 21, color = "black", fill = "red", size = 5) +
labs(x = "Date Event", y = "Individual Count") + theme_bw()
ggplotly(p)
})
}
In my app, I want plot1 to display by default, and then if an action button is clicked, have plot2 replace plot1. If it is clicked again, revert to plot1, and so on.
server <- function(input, output, session) {
plot1 <- (defined here)
plot2 <- (defined here)
which_graph <- reactive({
if (input$actionbutton == 1) return(plot1)
if (input$actionbutton == 2) return(plot2)
})
output$plot <- renderPlot({
which_graph()
})
}
You can create a reactiveValue and use an actioButton to toggle that value. For example
library(shiny)
ui <- fluidPage(
plotOutput("plot"),
actionButton("button", "Click")
)
server <- function(input, output, session) {
whichplot <- reactiveVal(TRUE)
plot1 <- ggplot(mtcars) + aes(mpg, cyl) + geom_point()
plot2 <- ggplot(mtcars) + aes(hp, disp) + geom_point()
observeEvent(input$button, {
whichplot(!whichplot())
})
which_graph <- reactive({
if (whichplot()) {
plot1
} else {
plot2
}
})
output$plot <- renderPlot({
which_graph()
})
}
shinyApp(ui, server)
Here whichplot starts off as TRUE and then evertime you press the actionButton it toggles between TRUE/FALSE. This way you are not changing the value of the actionButton; you are just updating state each time it's pressed.
If your plots need any input from the user, you can make them reactive as well
ui <- fluidPage(
selectInput("column", "Column", choices=names(mtcars)),
plotOutput("plot"),
actionButton("button", "Click")
)
server <- function(input, output, session) {
whichplot <- reactiveVal(TRUE)
plot1 <- reactive({ggplot(mtcars) + aes(mpg, .data[[input$column]]) + geom_point()})
plot2 <- reactive({ggplot(mtcars) + aes(.data[[input$column]], disp) + geom_point()})
observeEvent(input$button, {
whichplot(!whichplot())
})
which_graph <- reactive({
if (whichplot()) {
plot1()
} else {
plot2()
}
})
output$plot <- renderPlot({
which_graph()
})
}
When I click one point in the chart, that point is highlighted as red.
But soon it goes back to black.
Is there any way to hold the selection?
library(shiny)
library(ggplot2)
server <- function(input, session, output) {
mtcars$cyl = as.character(mtcars$cyl)
D = reactive({
nearPoints(mtcars, input$click_1,allRows = TRUE)
})
output$plot_1 = renderPlot({
set.seed(123)
ggplot(D(),aes(x=cyl,y=mpg)) +
geom_boxplot(outlier.shape = NA) +
geom_jitter(aes(color=selected_),width=0.02,size=4)+
scale_color_manual(values = c("black","red"),guide=FALSE)
})
output$info = renderPrint({
D()
})
}
ui <- fluidPage(
plotOutput("plot_1",click = clickOpts("click_1")),
verbatimTextOutput("info")
)
shinyApp(ui = ui, server = server)
Okay, my approach is slightly different to Valter's: selected points become red, whilst you can deselect them and they turn back to black.
The key to achieve this effect (or even Valter's answer with 1 selected point) is to use reactiveValues to keep track of the selected points.
library(shiny)
library(ggplot2)
server <- function(input, session, output) {
mtcars$cyl = as.character(mtcars$cyl)
vals <- reactiveValues(clicked = numeric())
observeEvent(input$click_1, {
# Selected point/points
slt <- which(nearPoints(mtcars, input$click_1,allRows = TRUE)$selected)
# If there are nearby points selected:
# add point if it wasn't clicked
# remove point if it was clicked earlier
# Else do nothing
if(length(slt) > 0){
remove <- slt %in% vals$clicked
vals$clicked <- vals$clicked[!vals$clicked %in% slt[remove]]
vals$clicked <- c(vals$clicked, slt[!remove])
}
})
D = reactive({
# If row is selected return "Yes", else return "No"
selected <- ifelse(1:nrow(mtcars) %in% vals$clicked, "Yes", "No")
cbind(mtcars, selected)
})
output$plot_1 = renderPlot({
set.seed(123)
ggplot(D(),aes(x=cyl,y=mpg)) +
geom_boxplot(outlier.shape = NA) +
geom_jitter(aes(color=selected),width=0.02,size=4)+
scale_color_manual(values = c("black","red"),guide=FALSE)
})
output$info = renderPrint({
D()
})
}
ui <- fluidPage(
plotOutput("plot_1",click = clickOpts("click_1")),
verbatimTextOutput("info")
)
shinyApp(ui = ui, server = server)
I am not sure what is the problem but this is the first workaround I have come up to:
library(shiny)
library(ggplot2)
server <- function(input, session, output) {
mtcars$cyl = as.character(mtcars$cyl)
df <- reactiveValues(dfClikced = mtcars)
observe({
if (!is.null(input$click_1)) {
df$dfClikced <- nearPoints(mtcars, input$click_1, allRows = TRUE)
}})
output$plot_1 = renderPlot({
set.seed(123)
if (names(df$dfClikced)[NCOL(df$dfClikced)]== "selected_") {
ggplot(df$dfClikced,aes(x=cyl,y=mpg)) +
geom_boxplot(outlier.shape = NA) +
geom_jitter(aes(color=selected_),width=0.02,size=4)+
scale_color_manual(values = c("black","red"),guide=FALSE)
} else {
ggplot(df$dfClikced,aes(x=cyl,y=mpg)) +
geom_boxplot(outlier.shape = NA) +
geom_jitter(width=0.02,size=4)+
scale_color_manual(values = c("black","red"),guide=FALSE)
}
})
output$info = renderPrint({
df$dfClikced
})
}
ui <- fluidPage(
plotOutput("plot_1",click = clickOpts("click_1")),
verbatimTextOutput("info")
)
shinyApp(ui = ui, server = server)
let me know...
The following code does not work. What do I have to change?
Specific:
I want the X-Axis to change to logaritmic scale when a checkbox is ticked.
ui <- shinyUI(fluidPage(
...
checkboxInput("logarithmicX", "show x-axis in log10", FALSE),
checkboxInput("logarithmicY", "show y-axis in log10", FALSE),
...
))
server <- shinyServer(function(input, output) {
output$distPlot <- renderPlot({
...
xlog <- reactive({
switch(input$logarithmicX,
TRUE == TRUE,
FALSE == FALSE)
})
ylog <- reactive({
switch(input$logarithmicY,
TRUE == TRUE,
FALSE == FALSE)
})
ggplot(datasetInput(), aes(size)) + geom_histogram(bins = biins) + theme_bw() + scale_x_log10("logarithmicX") +scale_y_log10("logarithmicY")
})
Something like this:
output$distPlot <- renderPlot({
mygg <- ggplot(datasetInput(), aes(size)) +
geom_histogram(bins = biins) +
theme_bw()
if(input$logarithmicX)
mygg <- mygg + scale_x_log10()
if(input$logarithmicY)
mygg <- mygg + scale_y_log10()
return(mygg)
})
Edit: working example.
library(shiny)
library(ggplot2)
runApp(
shinyApp(
ui = {
pageWithSidebar(
headerPanel('http://stackoverflow.com/questions/38327254'),
sidebarPanel(
checkboxInput("logarithmicX", "show x-axis in log10", FALSE),
checkboxInput("logarithmicY", "show y-axis in log10", FALSE)
),
mainPanel(
plotOutput('distPlot')
)
)
},
server = function(input, output) {
output$distPlot <- renderPlot({
mygg <- ggplot(mtcars, aes(mpg)) +
geom_histogram()
if(input$logarithmicX)
mygg <- mygg + scale_x_log10()
if(input$logarithmicY)
mygg <- mygg + scale_y_log10()
return(mygg)
})
}
)
)
In a shiny plot I am trying to highlight points matching a clicked point (based on nearPoints() and click).
It sort of works. However, the reactive parts of the shiny app are refreshed twice and the second iteration seems to clear the clicked information.
How can I avoid the second refresh of the app?
Here is the MWE:
library("Cairo")
library("ggplot2")
library("shiny")
ui <- fluidPage(
fluidRow(
titlePanel('Phenotype Plots')
),
fluidRow(
uiOutput("plotui")
),
hr(),
fluidRow(
wellPanel(
h4("Selected"),
tableOutput("info_clicked")
##dataTableOutput("info_clicked") ## overkill here
)
)
)
server <- function(input, output, session) {
selected_line <- reactive({
nearPoints(mtcars, input$plot_click,
maxpoints = 1,
addDist = TRUE)
})
output$plotui <- renderUI({
plotOutput("plot", height=600,
click = "plot_click"
)
})
output$plot <- renderPlot({
p <- ggplot(mtcars) +
facet_grid(am ~ cyl) +
theme_bw() +
geom_point(aes(x=wt, y=mpg))
sline <- selected_line()
if (nrow(sline) > 0) {
p <- p +
geom_point(aes(x=wt, y=mpg),
data=mtcars[mtcars$gear == sline$gear,],
colour="darkred",
size=1)
}
p
})
##output$info_clicked <- renderDataTable({
output$info_clicked <- renderTable({
res <- selected_line()
## datatable(res)
res
})
}
shinyApp(ui, server)
Finally(!) found a workaround for avoiding double refresh on click in Shiny: capture click to a reactiveValue(), using the observeEvent(). Seemingly works on my project, and for your MWE, too. See updated code section below.
library("Cairo")
library("ggplot2")
library("shiny")
ui <- fluidPage(
fluidRow(
titlePanel('Phenotype Plots')
),
fluidRow(
uiOutput("plotui")
),
hr(),
fluidRow(
wellPanel(
h4("Selected"),
tableOutput("info_clicked")
##dataTableOutput("info_clicked") ## overkill here
)
)
)
server <- function(input, output, session) {
## CHANGE HERE
## Set up buffert, to keep the click.
click_saved <- reactiveValues(singleclick = NULL)
## CHANGE HERE
## Save the click, once it occurs.
observeEvent(eventExpr = input$plot_click, handlerExpr = { click_saved$singleclick <- input$plot_click })
## CHANGE HERE
selected_line <- reactive({
nearPoints(mtcars, click_saved$singleclick, ## changed from "input$plot_click" to saved click.
maxpoints = 1,
addDist = TRUE)
})
output$plotui <- renderUI({
plotOutput("plot", height=600,
click = "plot_click"
)
})
output$plot <- renderPlot({
p <- ggplot(mtcars) +
facet_grid(am ~ cyl) +
theme_bw() +
geom_point(aes(x=wt, y=mpg))
sline <- selected_line()
if (nrow(sline) > 0) {
p <- p +
geom_point(aes(x=wt, y=mpg),
data=mtcars[mtcars$gear == sline$gear,],
colour="darkred",
size=1)
}
p
})
##output$info_clicked <- renderDataTable({
output$info_clicked <- renderTable({
res <- selected_line()
## datatable(res)
res
})
}
shinyApp(ui, server)