shiny update plot based on data.table - r

in my example app I have the user give some input and generate a data.table from it in the first tab. in the second tab I would like to show the plot, depending on the data.table. I am having quite a hard time to get the reactivity right. Unfortunately at this point I get the error: Operation not allowed without an active reactive context.
Please help me or give me hints what I am doing wrong.
the data:
tdata <- data.table(fruit = c("Apple", "Apple", "Apple", "Apple", "Apple", "Apple", "Apple", "Apple","Banana", "Banana","Banana","Banana","Banana", "Banana","Banana","Banana"),
Fertilizer = c(1,2,4,3,2,2,2,2,1,4,3,2,4,4,3,1),
amount = c(2,3,4,7,1,34,33,21,12,32,22,17,14,9,22,6),
red = rep(c("+","+","-","-"),4),
green = rep(c("+","-"),8))
tdata[, grp := do.call(paste, c(list(sep="\n"),.SD)),.SDcols = 4:5]
UI:
library(shiny)
library(data.table)
library(DT)
ui <- (fluidPage(tagList(
sidebarLayout(
sidebarPanel(uiOutput("file_input")),
mainPanel(
tabsetPanel(
tabPanel("Data",dataTableOutput('fruit_table') ),
tabPanel("Plot", plotOutput('barPlot'))
))))))
Server:
server <- function(input, output) {
fileData <- reactive(
return(tdata)
)
output$file_input <- renderUI ({
if(is.null(fileData())){
return()
}else{
tagList(
checkboxGroupInput(inputId = "fruit",
label = "fruit",
choices = c(unique(fileData()[,get("fruit")])),
selected = fileData()[1, 1, with = FALSE]),
checkboxGroupInput(inputId = "tube",
label = "Fertilizer",
choices = unique(fileData()[,get("Fertilizer")]),
selected = fileData()[1, 3, with = F]),
###build checkboxes from Loop:
lapply(1:(length(fileData())-4), function(i) {
checkboxGroupInput(inputId = paste0("color",i),
label = colnames(fileData()[,i+3, with = FALSE]),
choices = c(unique(fileData()[,get(colnames(fileData()[,i+3, with = FALSE]))])),
inline = TRUE,
selected = fileData()[1, i+3, with = FALSE])
}))}})
output$fruit_table <- renderDataTable({
if(is.null(fileData())){
return(NULL)
}else{
validate(
need(input$fruit, 'Check at least one fruit'),
need(input$tube, 'Check at least one Fertilizer'),
####loop not working in here
need(input$color1, "Check at least one !"),
need(input$color2, "Check at least one !")
)
filter_expr <- TRUE
if (!(is.null(input$fruit))) {
filter_expr <- filter_expr & fileData()[,fruit] %in% input$fruit
#print((input$fruit))
}
if (!(is.null(input$tube))) {
filter_expr <- filter_expr & fileData()[,Fertilizer] %in% input$tube
}
##non-loop-verison
if (!(is.null(input$color1))) {
filter_expr <- filter_expr & fileData()[,red] %in% input$color1
}
if (!(is.null(input$color2))) {
filter_expr <- filter_expr & fileData()[,green] %in% input$color2
}
datatable(fileData()[filter_expr,],options = list(pageLength = 25))
}})
plot.dat <- reactiveValues(main = NULL)
plot.dat$main <- ggplot(data = fileData(), mapping = aes( x = fileData()[,grp], y =fileData()[,amount]))+
geom_boxplot( stat = 'boxplot',
position = position_dodge(width=0.8),
width = 0.55)
observe({
output$barPlot <- renderPlot({
if(is.null(fileData())){
return(NULL)
}else{
validate(
need(input$fruit, 'Check at least one fruit'),
need(input$tube, 'Check at least one Fertilizer'),
need(input$color1, "Check at least one !"),
need(input$color2, "Check at least one !")
)
plot.dat$main
}})
})
}
shinyApp(ui = ui, server = server
)

You need to update the data that gets plotted. See the following working code. I extracted the data to filter in a reactive expression myFilter. This needs to be called before creating the table as well as before creating the plot.
library(shiny)
library(data.table)
library(DT)
library(ggplot2)
tdata <- data.table(fruit = c("Apple", "Apple", "Apple", "Apple", "Apple", "Apple", "Apple", "Apple","Banana", "Banana","Banana","Banana","Banana", "Banana","Banana","Banana"),
Fertilizer = c(1,2,4,3,2,2,2,2,1,4,3,2,4,4,3,1),
amount = c(2,3,4,7,1,34,33,21,12,32,22,17,14,9,22,6),
red = rep(c("+","+","-","-"),4),
green = rep(c("+","-"),8))
tdata[, grp := do.call(paste, c(list(sep="\n"),.SD)),.SDcols = 4:5]
ui <- (fluidPage(tagList(
sidebarLayout(
sidebarPanel(uiOutput("file_input")),
mainPanel(
tabsetPanel(
tabPanel("Data",dataTableOutput('fruit_table') ),
tabPanel("Plot", plotOutput('boxPlot'))
))))))
server <- function(input, output) {
fileData <- tdata # static data, doesn't change, noneed to be reactive
output$file_input <- renderUI ({
validate(need(!is.null(fileData), ''))
tagList(
checkboxGroupInput(inputId = "fruit",
label = "fruit",
choices = c(unique(fileData[,get("fruit")])),
selected = fileData[1, 1, with = FALSE]),
checkboxGroupInput(inputId = "tube",
label = "Fertilizer",
choices = unique(fileData[,get("Fertilizer")]),
selected = fileData[1, 3, with = F]),
###build checkboxes from Loop:
lapply(seq(length(fileData)-4), function(i) {
checkboxGroupInput(inputId = paste0("color",i),
label = colnames(fileData[,i+3, with = FALSE]),
choices = c(unique(fileData[,get(colnames(fileData[,i+3, with = FALSE]))])),
inline = TRUE,
selected = fileData[1, i+3, with = FALSE])
})
)
})
# build a filter according to inputs
myFilter <- reactive({
validate(need(!is.null(fileData), ''))
validate(
need(input$fruit, 'Check at least one fruit'),
need(input$tube, 'Check at least one Fertilizer'),
need(input$color1, "Check at least one !"),
need(input$color2, "Check at least one !")
)
fileData[,fruit] %in% input$fruit & fileData[,Fertilizer] %in% input$tube &
fileData[,red] %in% input$color1 & fileData[,green] %in% input$color2
})
# print the datatable matching myFilter()
output$fruit_table <- renderDataTable({
datatable(fileData[myFilter(),],options = list(pageLength = 25))
})
# build a boxPLot according to myFilter()
output$boxPlot <- renderPlot({
validate(
need(!is.null(fileData), ''),
need(input$fruit, 'Check at least one fruit'),
need(input$tube, 'Check at least one Fertilizer'),
need(input$color1, "Check at least one !"),
need(input$color2, "Check at least one !")
)
data <- fileData[myFilter(),]
ggplot(data = data, mapping = aes( x = data[,grp], y =data[,amount]))+
geom_boxplot( stat = 'boxplot',
position = position_dodge(width=0.8),
width = 0.55)
})
}
shinyApp(ui = ui, server = server)

Related

shiny: how to catch manual input of an interactive table and export it?

I created an interactive table that takes changes both from selectizeInput and manual input. I need to write the table to a database after updating. My problem is that I can catch the changes made by selectizeInput. I don't know how to catch and save the changes made by manual input. For example,
library(shiny)
library(shinydashboard)
library(DT)
library(DBI)
ui <- dashboardPage(
dashboardHeader(title = "test"),
dashboardSidebar(),
dashboardBody(shiny::selectizeInput(inputId = "apple_lbs_filter",
label = "Apple lbs",
choices = c(1:10)),
shiny::selectizeInput(inputId = "cherry_lbs_filter",
label = "Cherry lbs",
choices = c(1:10)),
shiny::selectizeInput(inputId = "pineapple_lbs_filter",
label = "Pineapple lbs",
choices = c(1:10)),
shiny::selectizeInput(inputId = "pear_lbs_filter",
label = "Pear lbs",
choices = c(1:10)),
shiny::actionButton(inputId = "update_lbs",label = "Update lbs"),
DT::DTOutput("fruit"))
)
server <- function(input, output, session) {
fruit_df <- shiny::reactiveValues()
fruit_df$df <- data.frame(fruit_name = c("apple","cherry","pineapple","pear"),
fruit_lbs = c(2,5,6,3))
output$fruit <- DT::renderDT({
DT::datatable(fruit_df$df,editable = TRUE,extensions = 'Buttons',options = list(
dom = 'frtBip',
buttons = c('csv')
))
})
lbs_newentry <- shiny::observe({
if(input$update_lbs > 0) {
lbs_newline <- shiny::isolate(c(
input$apple_lbs_filter,
input$cherry_lbs_filter,
input$pineapple_lbs_filter,
input$pear_lbs_filter
))
shiny::isolate(fruit_df$df <- cbind(fruit_name = c("apple",
"cherry",
"pineapple",
"pear"),
fruit_lbs = lbs_newline))
# conn <- DBI::dbConnect(drv, user, password)
# DBI::dbWriteTable(conn = conn,
# SQL(schema.tbl),
# fruit_df$df)
# DBI::dbDisconnect(conn)
}
})
}
shinyApp(ui, server)
Using the code above I can write a table to database with updates made by selectizeInput,but not by manual. I commented writing to database part so that you won't run into errors when you test it. My guess is that the data I wrote to db is fruit_df$df, which does NOT catch or save manual input. What should I do to catch and save both selectizeInput and manual input and be able to export all the changes to a db?
Thanks!
You must avoid using a reactive dataframe in datatable, because when it changes then the full table is regenerated, and this can be avoided with a proxy:
library(shiny)
library(shinydashboard)
library(DT)
ui <- dashboardPage(
dashboardHeader(title = "test"),
dashboardSidebar(),
dashboardBody(
fluidRow(
column(
width = 6,
selectizeInput(inputId = "apple_lbs_filter",
label = "Apple lbs",
choices = c(1:10)),
selectizeInput(inputId = "cherry_lbs_filter",
label = "Cherry lbs",
choices = c(1:10)),
selectizeInput(inputId = "pineapple_lbs_filter",
label = "Pineapple lbs",
choices = c(1:10)),
selectizeInput(inputId = "pear_lbs_filter",
label = "Pear lbs",
choices = c(1:10))
),
column(
width = 6,
verbatimTextOutput("reactiveDF")
)
),
actionButton("update_lbs", label = "Update lbs", class = "btn-primary"),
br(), br(),
DTOutput("fruit")
)
)
server <- function(input, output, session) {
Fruits <- reactiveVal(
data.frame(
fruit_name = c("apple", "cherry", "pineapple", "pear"),
fruit_lbs = c(2, 5, 6, 3)
)
)
output[["fruit"]] <- renderDT({
datatable(
isolate(Fruits()), # isolate to avoid regenerating the table (see proxy below)
editable = list(target = "cell", disable = list(columns = c(0, 1))),
extensions = 'Buttons',
options = list(
dom = 'frtBip',
buttons = c('csv')
)
)
})
# use a proxy to update the data without regenerating the full table
proxy <- dataTableProxy("fruit")
observeEvent(input[["update_lbs"]], {
lbs_newline <- c(
input[["apple_lbs_filter"]],
input[["cherry_lbs_filter"]],
input[["pineapple_lbs_filter"]],
input[["pear_lbs_filter"]]
)
dat <- Fruits()
dat[["fruit_lbs"]] <- lbs_newline
Fruits(dat) # update the reactive dataframe
replaceData(proxy, dat, resetPaging = FALSE)
})
observeEvent(input[["fruit_cell_edit"]], {
info <- input[["fruit_cell_edit"]] # this input contains the info of the edit
Fruits(editData(Fruits(), info, proxy))
# editData() updates the data of the table
# and returns the new dataframe that we store in the reactive dataframe
})
output[["reactiveDF"]] <- renderPrint({ # just to check
Fruits()
})
}
shinyApp(ui, server)
Here the reactivity of the dataframe Fruits() is not necessary. You can proceed as follows instead:
server <- function(input, output, session) {
Fruits <- data.frame(
fruit_name = c("apple", "cherry", "pineapple", "pear"),
fruit_lbs = c(2, 5, 6, 3)
)
output[["fruit"]] <- renderDT({
datatable(
Fruits,
editable = list(target = "cell", disable = list(columns = c(0, 1))),
extensions = 'Buttons',
options = list(
dom = 'frtBip',
buttons = c('csv')
)
)
})
# use a proxy to update the data without regenerating the full table
proxy <- dataTableProxy("fruit")
observeEvent(input[["update_lbs"]], {
lbs_newline <- c(
input[["apple_lbs_filter"]],
input[["cherry_lbs_filter"]],
input[["pineapple_lbs_filter"]],
input[["pear_lbs_filter"]]
)
dat <- Fruits
dat[["fruit_lbs"]] <- lbs_newline
Fruits <<- dat
replaceData(proxy, Fruits, resetPaging = FALSE)
})
observeEvent(input[["fruit_cell_edit"]], {
info <- input[["fruit_cell_edit"]] # this input contains the info of the edit
Fruits <<- editData(Fruits, info, proxy)
})
}
But with this way you can't do the verbatimTextOutput as before.

How to link 2 widget options dynamically in R Shiny?

I'm trying to link the two options Type Selection & Subtype Selection as shown in the image. So I expect if I click Beer under Type Selection, I wouldn't see ICE WINE RED in Subtype Selection as it isn't a subtype of beer. Any idea to link Type Selection & Subtype Selection, so everytime I check some types in Type Selection, I wouldn't see irrelated subtypes in Subtype Selection?
Not sure if you can see bcl dataset, if can't here is the data screenshot:
enter image description here
Here is my code for these 2 functions:
dput(head(bcl))
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("priceInput", "Price", 0, 100, c(25, 40), pre = "$"),
checkboxGroupInput(
"type",
label = "Type Selection:",
choices = c("BEER" = "BEER", "WINE" = "WINE", "SPIRITS" = "SPIRITS", "REFRESHMENT" = "REFRESHMENT"),
selected = c("BEER", "WINE")
),
checkboxInput("filterSubType", "Filter by Subtype", FALSE),
conditionalPanel(
condition = "input.filterSubType",
uiOutput("SubtypeSelectOutput")
),
)
),
mainPanel(
plotOutput("plot"),
br(), br(),
#tableOutput("prices")
DT::dataTableOutput("prices")
)
)
)
server <- function(input, output, session) {
output$SubtypeSelectOutput <- renderUI({
selectInput("subtypeInput", "Subtype",
sort(unique(bcl$Subtype)),
selected = "ALMOND",
multiple = TRUE)
})
output$summaryText <- renderText({
numOptions <- nrow(prices())
if (is.null(numOptions)) {
numOptions <- 0
}
paste0("We found ", numOptions, " options for you")
})
prices <- reactive({
prices <- bcl
if (is.null(input$subtypeInput)) {
return(NULL)
}
prices <- dplyr::filter(prices, Type %in% input$type)
if (input$filterSubType) {
prices <- dplyr::filter(prices, Subtype == input$subtypeInput)
}
prices <- dplyr::filter(prices, Price >= input$priceInput[1],
Price <= input$priceInput[2]
)
if(nrow(prices) == 0) {
return(NULL)
}
prices
})
output$plot <- renderPlot({
if (is.null(prices())) {
return(NULL)
}
ggplot(prices(), aes(Alcohol_Content, fill = Type)) +
geom_histogram(colour = "black") +
theme_classic(20)
})
output$prices <- DT::renderDataTable({
prices()
})
}
shinyApp(ui = ui, server = server)
Update your SubtypeSelectionOutput so that it filters based on the first input.
output$SubtypeSelectOutput <- renderUI({
selectInput("subtypeInput", "Subtype",
choices = bcl %>%
filter(type %in% input$type) %>%
pull(Subtype) %>%
unique() %>% sort(),
selected = "ALMOND",
multiple = TRUE)
})
This may not solve all of your issues with this app, but it will properly link Type Selection & Subtype Selection.

Restyling traces using plotlyProxy in a scatterplot is unstable when points are colored according to category

I have a Shiny app that builds a scatterplot and highlights the clicked points by restyling the marker outline via plotlyProxy.
The app also subsets the data and moves the entries corresponding to the clicked points from the original "Data table" to an "Outlier table".
This seems to work fine when the markers are all the same color, or when they are colored by a continuous variable. But when I color the points by a categorical variable (like "Species"), it has a weird behavior, restyling a marker from each category instead of the clicked one. The data subsets correctly.
I think the restyle function should update all traces unless specified otherwise, so I am not sure where exactly lies the problem.
Here is my code:
library(plotly)
library(DT)
ui <- fluidPage(
mainPanel(
fluidRow(
div(
column(
width = 2,
uiOutput('chartOptions')),
column(width = 5,
h3("Scatter plot"),
plotlyOutput("scatterplot"),
verbatimTextOutput("click")
)
)
),
hr(),
div(
column(width = 6,
h2("Data Table"),
div(
DT::dataTableOutput(outputId = "table_keep"),
style = "height:auto; overflow-y: scroll;overflow-x: scroll;")),
column(width = 6,
h2("Outlier Data"),
div(
DT::dataTableOutput(outputId = "table_outliers"),
style = "height:auto; overflow-y: scroll;overflow-x: scroll;"))
)
))
server <- function(input, output, session){
datasetInput <- reactive({
df <- iris
return(df)
})
output$chartOptions <- renderUI({#choose variables to plot
if(is.null(datasetInput())){}
else {
list(
selectizeInput("xAxisSelector", "X Axis Variable",
colnames(datasetInput())),
selectizeInput("yAxisSelector", "Y Axis Variable",
colnames(datasetInput())),
selectizeInput("colorBySelector", "Color By:",
c(c("Do not color",colnames(datasetInput()))))
)
}
})
vals <- reactiveValues(#define reactive values for:
data = NULL,
data_keep = NULL,
data_exclude = NULL)
observe({
vals$data <- datasetInput()
vals$data_keep <- datasetInput()
})
## Datatable
output$table_keep <- renderDT({
vals$data_keep
},options = list(pageLength = 5))
output$table_outliers <- renderDT({
vals$data_exclude
},options = list(pageLength = 5))
# mechanism for managing selected points
keys <- reactiveVal()
observeEvent(event_data("plotly_click", source = "outliers", priority = "event"), {
req(vals$data)
is_outlier <- NULL
key_new <- event_data("plotly_click", source = "outliers")$key
key_old <- keys()
if (key_new %in% key_old){
keys(setdiff(key_old, key_new))
} else {
keys(c(key_new, key_old))
}
is_outlier <- rownames(vals$data) %in% keys()
vals$data_keep <- vals$data[!is_outlier, ]
vals$data_exclude <- vals$data[is_outlier, ]
plotlyProxy("scatterplot", session) %>%
plotlyProxyInvoke(
"restyle",
list(marker.line = list(
color = as.vector(ifelse(is_outlier,'black','grey')),
width = 2
))
)
})
observeEvent(event_data("plotly_doubleclick", source = "outliers"), {
req(vals$data)
keys(NULL)
vals$data_keep <- vals$data
vals$data_exclude <- NULL
plotlyProxy("scatterplot", session) %>%
plotlyProxyInvoke(
"restyle",
list(marker.line = list(
color = 'grey',
width = 2
)
))
})
output$scatterplot <- renderPlotly({
req(vals$data,input$xAxisSelector,input$yAxisSelector)
dat <- vals$data
key <- rownames(vals$data)
x <- input$xAxisSelector
y <- input$yAxisSelector
if(input$colorBySelector != "Do not color"){
color <- dat[, input$colorBySelector]
}else{
color <- "orange"
}
scatterplot <- dat %>%
plot_ly(x = dat[,x], y = dat[,y], source = "outliers") %>%
add_markers(key = key,color = color,
marker = list(size = 10, line = list(
color = 'grey',
width = 2
))) %>%
layout(showlegend = FALSE)
return(scatterplot)
})
output$click <- renderPrint({#click event data
d <- event_data("plotly_click", source = "outliers")
if (is.null(d)) "click events appear here (double-click to clear)" else d
})
}
shinyApp(ui, server)
The problem with your above code is that no traceIndices argument is provided for restyle. Please see this.
In your example, once you switch coloring to the factor Species plotly no longer creates one trace, but three. This happens in JS so counting is done from 0 to 2.
To restyle those traces you can address them via curveNumber (in this case 0:2) and pointNumber (50 data points in each trace 0:49)
With a single trace your example works as your key and your trace have the same length (150).
As your provided code is pretty long I just focused on the "Species" problem. It won't work in all other cases, but you should be able to deduce a more general approach from it:
library(shiny)
library(plotly)
library(DT)
ui <- fluidPage(
mainPanel(
fluidRow(
div(
column(
width = 2,
uiOutput('chartOptions')),
column(width = 5,
h3("Scatter plot"),
plotlyOutput("scatterplot"),
verbatimTextOutput("click")
)
)
),
hr(),
div(
column(width = 6,
h2("Data Table"),
div(
DT::dataTableOutput(outputId = "table_keep"),
style = "height:auto; overflow-y: scroll;overflow-x: scroll;")),
column(width = 6,
h2("Outlier Data"),
div(
DT::dataTableOutput(outputId = "table_outliers"),
style = "height:auto; overflow-y: scroll;overflow-x: scroll;"))
)
))
server <- function(input, output, session){
datasetInput <- reactive({
df <- iris
df$is_outlier <- FALSE
return(df)
})
output$chartOptions <- renderUI({#choose variables to plot
if(is.null(datasetInput())){}
else {
list(
selectizeInput("xAxisSelector", "X Axis Variable",
colnames(datasetInput())),
selectizeInput("yAxisSelector", "Y Axis Variable",
colnames(datasetInput())),
selectizeInput("colorBySelector", "Color By:",
c(c("Do not color",colnames(datasetInput()))))
)
}
})
vals <- reactiveValues(#define reactive values for:
data = NULL,
data_keep = NULL,
data_exclude = NULL)
observe({
vals$data <- datasetInput()
vals$data_keep <- datasetInput()
})
## Datatable
output$table_keep <- renderDT({
vals$data_keep
},options = list(pageLength = 5))
output$table_outliers <- renderDT({
vals$data_exclude
},options = list(pageLength = 5))
# mechanism for managing selected points
keys <- reactiveVal()
myPlotlyProxy <- plotlyProxy("scatterplot", session)
observeEvent(event_data("plotly_click", source = "outliers", priority = "event"), {
req(vals$data)
is_outlier <- NULL
plotlyEventData <- event_data("plotly_click", source = "outliers")
key_new <- plotlyEventData$key
key_old <- keys()
if (key_new %in% key_old){
keys(setdiff(key_old, key_new))
} else {
keys(c(key_new, key_old))
}
vals$data[keys(),]$is_outlier <- TRUE
is_outlier <- vals$data$is_outlier
vals$data_keep <- vals$data[!is_outlier, ]
vals$data_exclude <- vals$data[is_outlier, ]
print(paste("pointNumber:", plotlyEventData$pointNumber))
print(paste("curveNumber:", plotlyEventData$curveNumber))
plotlyProxyInvoke(
myPlotlyProxy,
"restyle",
list(marker.line = list(
color = as.vector(ifelse(vals$data[vals$data$Species %in% vals$data[plotlyEventData$key, ]$Species, ]$is_outlier,'black','grey')),
width = 2
)), plotlyEventData$curveNumber
)
})
observeEvent(event_data("plotly_doubleclick", source = "outliers"), {
req(vals$data)
keys(NULL)
vals$data_keep <- vals$data
vals$data_exclude <- NULL
plotlyProxyInvoke(
myPlotlyProxy,
"restyle",
list(marker.line = list(
color = 'grey',
width = 2
)
))
})
output$scatterplot <- renderPlotly({
req(datasetInput(),input$xAxisSelector,input$yAxisSelector)
dat <- datasetInput()
key <- rownames(dat)
x <- input$xAxisSelector
y <- input$yAxisSelector
if(input$colorBySelector != "Do not color"){
color <- dat[, input$colorBySelector]
}else{
color <- "orange"
}
scatterplot <- dat %>%
plot_ly(x = dat[,x], y = dat[,y], source = "outliers") %>%
add_markers(key = key,color = color,
marker = list(size = 10, line = list(
color = 'grey',
width = 2
))) %>%
layout(showlegend = FALSE)
return(scatterplot)
})
output$click <- renderPrint({#click event data
d <- event_data("plotly_click", source = "outliers")
if (is.null(d)) "click events appear here (double-click to clear)" else d
})
}
shinyApp(ui, server)
As a quick workaround, to avoid creating 3 traces, I simply converted the categorical variable assigned to color to numeric, and I hid the colorbar, so the output looks like this:
output$scatterplot <- renderPlotly({
req(vals$data,input$xAxisSelector,input$yAxisSelector)
dat <- vals$data
key <- rownames(vals$data)
x <- input$xAxisSelector
y <- input$yAxisSelector
if(input$colorBySelector != "Do not color"){
color <- as.numeric(dat[, input$colorBySelector])
}else{
color <- "orange"
}
scatterplot <- dat %>%
plot_ly(x = dat[,x], y = dat[,y], source = "outliers") %>%
add_markers(key = key,color = color,
marker = list(size = 10, line = list(
color = 'grey',
width = 2
))) %>%
layout(showlegend = FALSE) %>%
hide_colorbar()%>%
event_register("plotly_click")
return(scatterplot)
})
Update:
Another solution that I found is to make a loop of plotly proxies for each trace / category in the click event.
So the click event looks like this:
observeEvent(event_data("plotly_click", source = "outliers", priority = "event"), {
req(vals$data)
is_outlier <- NULL
key_new <- event_data("plotly_click", source = "outliers")$key
key_old <- keys()
#keys(c(key_new, key_old))
if (key_new %in% key_old){
keys(setdiff(key_old, key_new))
} else {
keys(c(key_new, key_old))
}
is_outlier <- rownames(vals$data) %in% keys()
vals$data_keep <- vals$data[!is_outlier, ]
vals$data_exclude <- vals$data[is_outlier, ]
indices <- list()
p <- plotlyProxy("scatterplot", session)
if(input$colorBySelector != "Do not color"){
if(is.factor(vals$data[,input$colorBySelector])){
for (i in 1:length(levels(vals$data[,input$colorBySelector]))){
indices[[i]] <- rownames(vals$data[which(vals$data[,input$colorBySelector] == levels(vals$data[,input$colorBySelector])[i]), ]) #retrieve indices for each category
plotlyProxyInvoke(p,
"restyle",
list(marker.line = list(
color = as.vector(ifelse(is_outlier[as.numeric(indices[[i]])],'black','grey')),
width = 2
)), c(i-1) #specify the trace (traces are indexed from 0)
)
}
}else{
p %>%
plotlyProxyInvoke(
"restyle",
list(marker.line = list(
color = as.vector(ifelse(is_outlier,'black','grey')),
width = 2
))
)
}
}else{
p %>%
plotlyProxyInvoke(
"restyle",
list(marker.line = list(
color = as.vector(ifelse(is_outlier,'black','grey')),
width = 2
))
)
}
})

how can I get more than one plot from several selected items in a checkbox?

Good morning,
in my dashboard I inserted a checkbox to select one or more output to display. In the ui I entered the checkbox and in the server all the conditions (if ... else if ...). When I launch the app it only shows me a plot, even when I select more than one choice in the checkbox. In addition it gives me this error in console:
"Warning in if (input$checkGroup == 1) { :the condition has length > 1 and only the first element will be used"
I suppose it's telling me that I can't handle more than one choice, how do I view all the plots I choose?
ui <- fluidPage(titlePanel("IULM Dashboard"), sidebarLayout(sidebarPanel(
selectInput("selection", "Choose a Dataset:",
choices = datasets),
("Barplot","Network",'Wordcloud', "LDA-Latent topic"),
#selected = "Barplot", inline = TRUE),
checkboxGroupInput("checkGroup", label = ("Checkbox group"),
choices = list("Barplot" = 1, "Network" = 2), selected = 1, inline = TRUE),
actionButton("update", "Change"))
, mainPanel(
uiOutput("plot")))
server <- function(input, output){
datasetInput <- reactive({
input$update
isolate({
withProgress({
setProgress(message = "Processing corpus...")
getTermMatrix(input$selection)
})
})
})
output$plot <- renderUI({
if(input$checkGroup== 1 ){
output$barplot <- renderPlot({
v=datasetInput()
dtm1 = removeSparseTerms(v, 0.992)
freq <- colSums(as.matrix(dtm1))
wf = data.frame(term = names(freq), occurrences = freq)
wf <- wf[order(wf$occurrences, decreasing = TRUE),]
wf2 = subset(wf[1:input$maxB,])
ggplot(wf2, aes(term, occurrences)) +
geom_bar(stat="identity", fill="darkred", colour="black", width=0.5)+
theme(axis.text.x=element_text(angle=45, hjust=1))+
ggtitle("Word barplot")})
plotOutput(outputId = "barplot", width = 600, height = 400)
}
else if(input$checkGroup== 2 ){
output$network <- renderPlot({
v=datasetInput()
dtm1 = removeSparseTerms(v, 0.992)
rowTotals <- apply(dtm1 , 1, sum)
dtm2 <- dtm1[rowTotals> 0, ]
wdtm <- weightTf(dtm2)
dtm1 <- removeSparseTerms(wdtm, 0.96)
dfm <- as.dfm(dtm1)
textplot_network(dfm, min_freq = 0.5, omit_isolated = TRUE,
edge_color = "#1F78B4", edge_alpha = 0.5, edge_size = 2,
vertex_color = "#4D4D4D", vertex_size = 2,
vertex_labelsize = 5, offset = NULL)})
plotOutput(outputId = "network", width = 600, height = 600)}
})
}
shinyApp(ui = ui, server = server)
You can try
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
checkboxGroupInput("variable", "Variables to show:",
c("Cylinders" = "cyl",
"Transmission" = "am",
"Gears" = "gear"))
),
mainPanel(
uiOutput("plots")
)))
server <- function(input, output) {
output$plots <- renderUI({
req(input$variable)
output = tagList()
if(any(input$variable %in% "cyl")){
tmp <- mtcars$cyl
output[[1]] <- renderPlot({plot(mtcars$mpg, tmp)})
}
if(any(input$variable %in% "am")){
tmp <- mtcars$am
output[[2]] <- renderPlot({boxplot(mtcars$mpg, tmp)})
}
output
})
}
shinyApp(ui = ui, server = server)

Filter a table in Shiny

The following data set is given (in reality much more cases):
data_test = data.frame(ID = c ("1","2","3","4","5"),
product = c("A","B","C","A","C"),
milieu = c("good","medium","bad","medium","bad"),
online = c(1,0,1,1,0),
ooh = c(0,1,0,1,1),
event = c(1,1,0,0,0))
Now I want to built a shiny app where someone can choose a milieu lets say "good" and a product "A" and all online which have "1" and the data table with these settings is given back. In the Example ID 1.
I tried the following
ui:
shinyUI(fluidPage(
titlePanel("product milieu"),
sidebarLayout(
sidebarPanel("select",
selectInput("select_milieu",
label = "Milieu",
choices = list("good",
"medium",
"bad")
),
selectInput("select_product",
label = "Product",
choices = list("A",
"B",
"C")
),
selectInput("select_online",
label = "Online",
choices = list(1,
0)
),
selectInput("select_ooh",
label = "ooh",
choices = list(1,
0)
),
selectInput("select_Event",
label = "Event",
choices = list(1,
0)
)
),
mainPanel("My table",
textOutput("output_milieu"),
textOutput("output_product"),
textOutput("output_event"),
textOutput("output_online"),
textOutput("output_ooh"),
tableOutput("gapminder_table")
)
)
))
server:
shinyServer(function(input, output) {
output$gapminder_table <- renderTable({
subset(data_test,
milieu == input$select_milieu & product == input$select_product &
online == input$select_online)
})
output$output_milieu <- renderText({
paste("milieu", input$select_milieu)
})
output$output_product <- renderText({
paste("product", input$select_product)
})
output$output_event <- renderText({
paste("Event", input$select_Event)
})
output$output_online <- renderText({
paste("Online", input$select_Online)
})
output$output_ooh <- renderText({
paste("out of Home", input$select_ooh)
})
})
My problem is now how to filter for "event" and "ooh". Does anyone has an advice?
Thanks in advance!
You can make this much simpler if you begin to explore the DT package for datatables with shiny. With this, you can just type in whatever filter criteria you like above the respective columns.
server.R
library(shiny)
library(DT)
data_test = data.frame(ID = c ("1","2","3","4","5"),
product = c("A","B","C","A","C"),
milieu = c("good","medium","bad","medium","bad"),
online = c(1,0,1,1,0),
ooh = c(0,1,0,1,1),
event = c(1,1,0,0,0))
shinyServer(function(input, output) {
output$gapminder_table <- renderDataTable({
data_test
},
filter = 'top',
rownames = FALSE)
})
ui.R
library(shiny)
library(DT)
shinyUI(fluidPage(
titlePanel("product milieu"),
sidebarLayout(
sidebarPanel("Place for other criteria"
),
mainPanel("My table",
dataTableOutput("gapminder_table")
)
)
))

Resources