Shiny R: Interactive toggle output with `input` data selection - r

I have a question about the data selection of interactive toggle shiny app. I would like to make the data selected from selectInput but the error say: Operation not allowed without an active reactive context. (You tried to do something that can only be done from inside a reactive expression or observer.)
Is there any way to make the data interactive with the input?
Thank you!
Here is my app:
app.r:
ui <- fluidPage(
fluidRow(
column(width = 6,
selectInput("vsselection", "Choose a vs:",
choices = names(table(data.frame(mtcars$vs))),selected=0),
plotOutput("plot1", height = 350,
click = "plot1_click",
brush = brushOpts(
id = "plot1_brush"
)
),
actionButton("exclude_toggle", "Toggle points"),
actionButton("exclude_reset", "Reset")
)
)
)
server <- function(input, output) {
# For storing which rows have been excluded
vals <- reactiveValues(
keeprows = rep(TRUE, nrow(mtcars[which(mtcars$vs==input$vsselection),]))
)
output$plot1 <- renderPlot({
# Plot the kept and excluded points as two separate data sets
keep <- mtcars[which(mtcars$vs==input$vsselection),][ vals$keeprows, , drop = FALSE]
exclude <- mtcars[which(mtcars$vs==input$vsselection),][!vals$keeprows, , drop = FALSE]
ggplot(keep, aes(wt, mpg)) + geom_point() +
geom_smooth(method = lm, fullrange = TRUE, color = "black") +
geom_point(data = exclude, shape = 21, fill = NA, color = "black", alpha = 0.25) +
coord_cartesian(xlim = c(1.5, 5.5), ylim = c(5,35))
})
# Toggle points that are clicked
observeEvent(input$plot1_click, {
res <- nearPoints(mtcars[which(mtcars$vs==input$vsselection),], input$plot1_click, allRows = TRUE)
vals$keeprows <- xor(vals$keeprows, res$selected_)
})
# Toggle points that are brushed, when button is clicked
observeEvent(input$exclude_toggle, {
res <- brushedPoints(mtcars[which(mtcars$vs==input$vsselection),], input$plot1_brush, allRows = TRUE)
vals$keeprows <- xor(vals$keeprows, res$selected_)
})
# Reset all points
observeEvent(input$exclude_reset, {
vals$keeprows <- rep(TRUE, nrow(mtcars[which(mtcars$vs==input$vsselection),]))
})
}
shinyApp(ui, server)

I guess the first thing you should to, is to change vals to vals <- reactive({...}) and then when referring to it, add parenthesis, e.g. vals()$keeprows. This should solve the reactivity problem.

Finally, I solved this issue by removing the interactive part from the object of reactiveValues() by keep the interactive part of Vals.
Note that values taken from the reactiveValues object are reactive, but the reactiveValues object itself is not.
Here is my app:
app.r:
ui <- fluidPage(
fluidRow(
column(width = 6,
selectInput("vsselection", "Choose a vs:",
choices = names(table(data.frame(mtcars$vs))),selected=0),
plotOutput("plot1", height = 350,
click = "plot1_click",
brush = brushOpts(
id = "plot1_brush"
)
),
actionButton("exclude_toggle", "Toggle points"),
actionButton("exclude_reset", "Reset")
)
)
)
server <- function(input, output) {
# For storing which rows have been excluded
vals <- reactiveValues(
keeprows = rep(TRUE, nrow(mtcars))
)
output$plot1 <- renderPlot({
# Plot the kept and excluded points as two separate data sets
keep <- mtcars[which(mtcars$vs==input$vsselection),][ vals$keeprows, , drop = FALSE]
exclude <- mtcars[which(mtcars$vs==input$vsselection),][!vals$keeprows, , drop = FALSE]
ggplot(keep, aes(wt, mpg)) + geom_point(color = "blue") +
geom_smooth(method = lm, fullrange = TRUE, color = "black") +
geom_point(data = exclude, shape = 21, fill = NA, color = "black", alpha = 0.25)
})
# Toggle points that are clicked
observeEvent(input$plot1_click, {
res <- nearPoints(mtcars[which(mtcars$vs==input$vsselection),], input$plot1_click, allRows = TRUE)
vals$keeprows <- xor(vals$keeprows, res$selected_)
})
# Toggle points that are brushed, when button is clicked
observeEvent(input$exclude_toggle, {
res <- brushedPoints(mtcars[which(mtcars$vs==input$vsselection),], input$plot1_brush, allRows = TRUE)
vals$keeprows <- xor(vals$keeprows, res$selected_)
})
# Reset all points
observeEvent(input$exclude_reset, {
vals$keeprows <- rep(TRUE, nrow(mtcars[which(mtcars$vs==input$vsselection),]))
})
}

Related

R Shiny recoloring of points

I would like click-select points and group them based on color.
I can save selected points with color information into a new data frame and plot it, however I would like to keep track and see what was already selected on the interactive plot.
How can I show/label already selected points or make it permanent after "Add selection"?
library(shiny)
library(tidyverse)
library(DT)
library(colourpicker)
ui = fluidPage(
colourInput("col", "Select colour", "purple"),
actionButton("addToDT", "Add selection", icon = icon("plus")), hr(),
plotOutput("plot", click = "plot_click", dblclick = "plot_reset"),
DT::dataTableOutput('plot_DT'), hr(),
textOutput("clickcoord"),
DT::dataTableOutput('final_DT'),
plotOutput("plotSelected")
)
server = function(input, output, session) {
selectedPoint = reactiveVal(rep(FALSE, nrow(mtcars)))
output$clickcoord <- renderPrint({
print(input$plot_click)
})
observeEvent(input$plot_click, {
clicked = nearPoints(mtcars, input$plot_click, allRows = TRUE)$selected_
selectedPoint(clicked | selectedPoint())
})
observeEvent(input$plot_reset, {
selectedPoint(rep(FALSE, nrow(mtcars)))
})
output$plot_DT = DT::renderDataTable({
mtcars$sel = selectedPoint()
mtcars = dplyr::filter(mtcars, sel == TRUE) %>% mutate(group_color = input$col)
})
final_DT = reactiveValues()
final_DT$df = data.frame()
FinalData = eventReactive(input$addToDT, {
mtcars$sel = selectedPoint()
mtcars = dplyr::filter(mtcars, sel == TRUE) %>% mutate(group_color = input$col)
final_DT$df = bind_rows(final_DT$df, mtcars)
})
output$final_DT = renderDataTable({FinalData()})
output$plot = renderPlot({
mtcars$sel = selectedPoint()
ggplot(mtcars, aes(wt, mpg, color = mtcars$sel, fill=mpg)) +
geom_point(shape = 21, size = 6, stroke = 2) +
scale_color_manual(values = c("#ffffff00", input$col)) +
scale_fill_viridis_c() +
theme_bw()
})
output$plotSelected = renderPlot({
sel_df = FinalData()
ggplot(sel_df, aes(wt, mpg, fill = group_color, colour = group_color)) +
geom_point(shape = 21, size = 6, stroke = 2) +
scale_color_manual(values = unique(sel_df$group_color)) +
scale_fill_manual(values = unique(sel_df$group_color)) +
theme_bw()
})
observeEvent(input$addToDT, {
selectedPoint(rep(FALSE, nrow(mtcars)))
})
}
shinyApp(ui, server)
I think this is the "crux" of what your are looking for. I used a very similar example that I found in the help for entitled:
A demonstration of clicking, hovering, and brushing
(https://shiny.rstudio.com/reference/shiny/0.13.1/plotoutput)
It is very similar to your example.
I create a matrix of T/F elements where the rows are the observations and the columns are in which batch the observation is selected. So when you launch the whole matrix is False, but as you click on observations the switch to positive in the first column. Then if you click addSelection and continue you start switching the observations in the next column.
Could you confirm that this what you are looking for?
Below is the code.
shinyApp(
ui = basicPage(
fluidRow(
column(
width = 4,
plotOutput("plot",
height = 300,
click = "plot_click", # Equiv, to click=clickOpts(id='plot_click')
),
actionButton("addToDT", "Add selection", icon = icon("plus")), hr(),
h4("Clicked points"),
tableOutput("plot_clickedpoints"),
),
column(
width = 4,
verbatimTextOutput("counter"),
),
)
),
server = function(input, output, session) {
data <- reactive({
input$newplot
# Add a little noise to the cars data so the points move
cars + rnorm(nrow(cars))
})
output$plot <- renderPlot({
d <- data()
plot(d$speed, d$dist, main = paste("No of Sets Chosen", input$addToDT))
})
output$plot_clickinfo <- renderPrint({
cat("Click:
")
str(input$plot_click)
})
selectedPoints <- reactiveVal(rep(FALSE, nrow(cars)))
selectionMatrix <- reactiveVal(matrix(data = F, nrow = nrow(cars), ncol = 7))
observeEvent(input$plot_click, {
clicked <- nearPoints(data(), input$plot_click, "speed", "dist", allRows = TRUE)$selected
selectedPoints(clicked | selectedPoints())
tmp <- unlist(selectionMatrix())
tmp[, (input$addToDT + 1)] <- selectedPoints()
selectionMatrix(tmp)
})
observeEvent(input$addToDT, {
selectedPoints(rep(FALSE, nrow(cars)))
})
output$plot_clickedpoints <- renderTable({
# if (input$addToDT==0) {
res <- selectionMatrix()
return(res)
})
}
)

How can I customise the text output in a verbatimTextOuput function within a Shiny interactive plot in R

I want to change the text output to show specific information about the plot point being hovered over such as name and value and such, but I cant seem to workout how to alter the VerbatimTextOutput and what it currently show is not of much use for a quick glace of information. This is the code I am using:
```
ui <- fluidPage(
fluidRow(
column(width = 8, class = "well",
h4("Brush to select region. Left plot controls right plot"),
fluidRow(
column(width = 12,
plotOutput("plot2", height = 300,
brush = brushOpts(
id = "plot2_brush",
resetOnNew = TRUE
)
)
),
column(width = 12,
plotOutput("plot3", height = 300, hover = hoverOpts(id = "plot_hover"))
)
)
),
column(width = 3,
verbatimTextOutput("hover_info", placeholder = TRUE))
)
)
server <- function(input, output) {
# Linked plots (middle and right)
ranges2 <- reactiveValues(x = NULL, y = NULL)
output$plot2 <- renderPlot({
ggplot(adj_p_val_df, aes(x = Genes, y = p_value, col = Model))+
geom_point()+
theme(legend.position = "none")
})
output$plot3 <- renderPlot({
ggplot(adj_p_val_df, aes(x = Genes, y = p_value, col = Model))+
geom_point() +
coord_cartesian(xlim = ranges2$x, ylim = ranges2$y, expand = FALSE)
})
output$hover_info = renderPrint({
cat("Gene info:\n")
str(input$plot_hover)
})
output$placeholder = renderTe
# When a double-click happens, check if there's a brush on the plot.
# If so, zoom to the brush bounds; if not, reset the zoom.
observe({
brush <- input$plot2_brush
if (!is.null(brush)) {
ranges2$x <- c(brush$xmin, brush$xmax)
ranges2$y <- c(brush$ymin, brush$ymax)
} else {
ranges2$x <- NULL
ranges2$y <- NULL
}
})
}
shinyApp(ui, server)
```

Using a (logical) vector from a reactive expression in a reactive context / attempt to apply non-function error

I am trying to use a logical vector from a reactive expression. This generates an error in a function xor() when I try to perform a logical operation on this vector in another reactive expression. I would like to generate a reactive expression (logical vector), and then use it in another reactive function. A toy example below. The error appears when points on the plot are clicked.
In the original here, keeprows() is not reactive, but I would like to make this structured as on the schematic below (from Shiny website). The fist object is input for a reactive expression, and then a second (reactive) object (which is a user-subsetted table) is used for point selection, etc. The elements after bifurcation are the tables with the kept and excluded points. I have a problem in making this last subsetting to work.
Could someone explain to me the root of this problem?
library(ggplot2)
library(shiny)
library(dplyr)
ui <- fluidPage(
fluidRow(
column(width = 6,
plotOutput("plot1", height = 350,
click = "plot1_click",
brush = brushOpts(
id = "plot1_brush"
)
),
actionButton("exclude_toggle", "Toggle points"),
sliderInput(inputId = "efficiency", value = 20, label = "MPG", min = min(mtcars$mpg), max = max(mtcars$mpg))
)
)
)
server <- function(input, output) {
# For storing which rows have been excluded
mt_subset <- reactive(mtcars %>% filter(mpg > input$efficiency))
vals <- reactiveValues()
vals$keeprows <- reactive(rep(TRUE, nrow(mt_subset()), label = "TuProblem", quoted = FALSE))
output$plot1 <- renderPlot({
# Plot the kept and excluded points as two separate data sets
keep <- mt_subset()[ vals$keeprows(), , drop = FALSE]
exclude <- mt_subset()[!vals$keeprows(), , drop = FALSE]
ggplot(keep, aes(wt, mpg)) + geom_point() +
geom_smooth(method = lm, fullrange = TRUE, color = "black") +
geom_point(data = exclude, shape = 21, fill = NA, color = "black", alpha = 0.25) +
coord_cartesian(xlim = c(1.5, 5.5), ylim = c(5,35))
})
# Toggle points that are clicked
observeEvent(input$plot1_click, {
res <- nearPoints(mt_subset(), input$plot1_click, allRows = TRUE)
vals$keeprows <- xor(as.logical(vals$keeprows()), as.logical(res$selected_))
})
# Toggle points that are brushed, when button is clicked
observeEvent(input$exclude_toggle, {
res <- brushedPoints(mt_subset(), input$plot1_brush, allRows = TRUE)
vals$keeprows <- xor(vals$keeprows(), res$selected_)
})
}
shinyApp(ui, server)
I'm not sure if this is the output you're looking for, but this code reads in a local file and then performs the brushing point selection, greying out the brushed points after "toggle points" is hit and also adjusting the correlation.
library(ggplot2)
library(Cairo) # For nicer ggplot2 output when deployed on Linux
library(shiny)
library(readxl)
data(iris)
write.xlsx(x = iris, file = "iris.xlsx")
ui <- fluidPage(
fluidRow(
fileInput(inputId = "file",
label = "Load file"),
column(width = 6,
plotOutput("plot1", height = 350,
click = "plot1_click",
brush = brushOpts(
id = "plot1_brush"
)
),
actionButton("exclude_toggle", "Toggle points"),
actionButton("exclude_reset", "Reset")
)
)
)
server <- function(input, output) {
# Get file
getFile <- reactive({ if (is.null(input$file)) {
return(NULL)
} else {
return(input$file)
}})
# Read data
data <- reactive({ if (is.null(getFile())) {
return(NULL)
} else {
as.data.frame(read_excel(getFile()$datapath))
}})
# For storing which rows have been excluded
vals <- reactiveValues()
observeEvent(data(), {
vals$keeprows <- rep(T, nrow(data()))
})
# Toggle points that are clicked
observeEvent(input$plot1_click, {
res <- nearPoints(data(), input$plot1_click, allRows = TRUE)
vals$keeprows <- xor(vals$keeprows, res$selected_)
})
# Toggle points that are brushed, when button is clicked
observeEvent(input$exclude_toggle, {
res <- brushedPoints(data(), input$plot1_brush, allRows = TRUE)
vals$keeprows <- xor(vals$keeprows, res$selected_)
})
# Reset all points
observeEvent(input$exclude_reset, {
vals$keeprows <- rep(TRUE, nrow(data()))
})
output$plot1 <- renderPlot({
if (is.null(data())) {
return(NULL)
} else {
# Indices for keep and exclude
keep_v <- which(vals$keeprows)
exclude_v <- which(!vals$keeprows)
# Subset data
keep <- data()[keep_v, , drop = F]
exclude <- data()[exclude_v, , drop = F]
ggplot(keep, aes(Sepal.Length, Sepal.Width)) + geom_point() +
geom_smooth(method = lm, fullrange = TRUE, color = "black") +
geom_point(data = exclude, shape = 21, fill = NA, color = "black", alpha = 0.25)
}
})
}
shinyApp(ui, server)
Solved:
library(ggplot2)
library(shiny)
library(dplyr)
ui <- fluidPage(
fluidRow(
column(width = 6,
plotOutput("plot1", height = 350,
click = "plot1_click",
brush = brushOpts(
id = "plot1_brush"
)
),
actionButton("exclude_toggle", "Toggle points"),
sliderInput(inputId = "efficiency", value = 20, label = "MPG", min = min(mtcars$mpg), max = max(mtcars$mpg))
)
)
)
server <- function(input, output) {
mt_subset <- reactive(mtcars %>% filter(mpg > input$efficiency))
vals <- reactiveValues()
observeEvent(mt_subset(), {
vals$keeprows <- rep(TRUE, nrow(mt_subset()), label = "TuProblem", quoted = FALSE)
})
output$plot1 <- renderPlot({
# Plot the kept and excluded points as two separate data sets
keep <- mt_subset()[ vals$keeprows, , drop = FALSE]
exclude <- mt_subset()[!vals$keeprows, , drop = FALSE]
ggplot(keep, aes(wt, mpg)) + geom_point() +
geom_smooth(method = lm, fullrange = TRUE, color = "black") +
geom_point(data = exclude, shape = 21, fill = NA, color = "black", alpha = 0.25) +
coord_cartesian(xlim = c(1.5, 5.5), ylim = c(5,35))
})
observeEvent(input$plot1_click, {
res <- nearPoints(mt_subset(), input$plot1_click, allRows = TRUE)
vals$keeprows <- xor(vals$keeprows, res$selected_)
})
observeEvent(input$exclude_toggle, {
res <- brushedPoints(mt_subset(), input$plot1_brush, allRows = TRUE)
vals$keeprows <- xor(vals$keeprows, res$selected_)
})
}
shinyApp(ui, server)

ggplot2 interaction - toggle points

Based on this question, I found a nice example to remove points from a plot using ggplot2.
My question now is: once I delete a data point I would like to get rid of it permanently. The way it works now, every time the brush covers a coordinate where a deleted point was, that point comes back in. Any thoughts?
Example:
library(ggplot2)
library(Cairo) # For nicer ggplot2 output when deployed on Linux
ui <- fluidPage(
fluidRow(
column(width = 6,
plotOutput("plot1", height = 350,
click = "plot1_click",
brush = brushOpts(
id = "plot1_brush"
)
),
actionButton("exclude_toggle", "Toggle points"),
actionButton("exclude_reset", "Reset")
)
)
)
server <- function(input, output) {
# For storing which rows have been excluded
vals <- reactiveValues(
keeprows = rep(TRUE, nrow(mtcars))
)
output$plot1 <- renderPlot({
# Plot the kept and excluded points as two separate data sets
keep <- mtcars[ vals$keeprows, , drop = FALSE]
exclude <- mtcars[!vals$keeprows, , drop = FALSE]
ggplot(keep, aes(wt, mpg)) + geom_point() +
geom_smooth(method = lm, fullrange = TRUE, color = "black") +
geom_point(data = exclude, shape = 21, fill = NA, color = "black", alpha = 0.25) +
coord_cartesian(xlim = c(1.5, 5.5), ylim = c(5,35))
})
# Toggle points that are clicked
observeEvent(input$plot1_click, {
res <- nearPoints(mtcars, input$plot1_click, allRows = TRUE)
vals$keeprows <- xor(vals$keeprows, res$selected_)
})
# Toggle points that are brushed, when button is clicked
observeEvent(input$exclude_toggle, {
res <- brushedPoints(mtcars, input$plot1_brush, allRows = TRUE)
vals$keeprows <- xor(vals$keeprows, res$selected_)
})
# Reset all points
observeEvent(input$exclude_reset, {
vals$keeprows <- rep(TRUE, nrow(mtcars))
})
}
shinyApp(ui, server)

Redraw shiny plot after updating data frame?

I have built a Shiny app with a database backend -
Im trying to reload the dataframe current_data_frame and redraw the plot using it when input$draw_plot (action button) is pressed.
I am having trouble re-drawing the plot after recalling the data frame?
Any ideas what I am missing??
snippet from server.R
current_data_frame = data.frame(matrix(ncol = 4, nrow = 0))
names( current_data_frame ) <- c("sample_id", "call", "intensity_A" , "intensity_B")
# OBSERVE BUTTON PRESS & UPDATE DATA FRAME
observeEvent( input$draw_plot, {
current_data_frame <- get_data_frame( input$probeset_id , input$study_id , input$batch_id)
})
vals <- reactiveValues(
keeprows = rep(TRUE, nrow( current_data_frame ))
)
output$call_plot <- renderPlot({
# Lists for holding unactive_points
keep <- current_data_frame[ vals$keeprows, , drop = FALSE]
exclude <- current_data_frame[ !vals$keeprows, , drop = FALSE]
# Le plot
ggplot(keep, aes( intensity_A , intensity_B)) +
geom_point(aes(colour = factor(call), shape = factor(call)) ) #+
#geom_point(data = exclude, shape = 21 , fill = NA, colour = "black", alpha = 0.25)
})
# Toggle click points
observeEvent( input$call_plot_click, {
res <- nearPoints(current_data_frame, input$call_plot_click, allRows = TRUE)
vals$keeprows <- xor(vals$keeprows, res$selected_)
})
# Toggle points that are brushed when clicked
observeEvent(input$exclude_toggle, {
res <- brushedPoints(current_data_frame, input$call_plot_brush, allRows = TRUE)
vals$keeprows <- xor(vals$keeprows, res$selected_)
})
# Reset all points
observeEvent( input$exclude_reset, {
vals$keeprows <- rep( TRUE, nrow(current_data_frame))
})
})
You probably need to make current_data_frame a reactive value. You can either do that by returning it inside of a reactive or by adding it to your vals object, including using vals$current_data_frame everywhere you currently use current_data_frame and changing vals to look something like this:
vals <- reactiveValues(keeprows = rep(TRUE, nrow( current_data_frame )),
current_data_frame = current_data_frame
)
which will set vals$current_data_frame to the default you defined at the start of your code the first time, then allow you to change it every time the observeEvent is triggered.
Managed to answer this myself -
Wrapped the whole output$call_plot in the response function. Now it redraws + fetches new data each time the button is pressed.
Code below...
observeEvent( input$draw_plot, {
current_data_frame <- get_data_frame( input$probeset_id , input$study_id , input$batch_id)
vals <- reactiveValues(
keeprows = rep(TRUE, nrow( current_data_frame ))
)
output$call_plot <- renderPlot({
# Lists for holding unactive_points
keep <- current_data_frame[ vals$keeprows, , drop = FALSE]
exclude <- current_data_frame[ !vals$keeprows, , drop = FALSE]
# Le plot
ggplot(keep, aes( intensity_A , intensity_B)) +
geom_point(aes(colour = factor(call), shape = factor(call)) ) #+
#geom_point(data = exclude, shape = 21 , fill = NA, colour = "black", alpha = 0.25)
})
# Toggle click points
observeEvent( input$call_plot_click, {
res <- nearPoints(current_data_frame, input$call_plot_click, allRows = TRUE)
vals$keeprows <- xor(vals$keeprows, res$selected_)
})
# Toggle points that are brushed when clicked
observeEvent(input$exclude_toggle, {
res <- brushedPoints(current_data_frame, input$call_plot_brush, allRows = TRUE)
vals$keeprows <- xor(vals$keeprows, res$selected_)
})
# Reset all points
observeEvent( input$exclude_reset, {
vals$keeprows <- rep( TRUE, nrow(current_data_frame))
})
})
})

Resources