Update shiny DT based on editable cells user input - r

A small example shiny app:
library(shiny)
library(tidyverse)
library(DT)
ui <- fluidPage(
# Application title
titlePanel("blah"),
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Show a plot of the generated distribution
mainPanel(
DT::DTOutput('ex_table')
)
)
)
server <- function(input, output) {
output$ex_table <- DT::renderDataTable(mtcars %>% select(cyl) %>% mutate(blah = cyl + 2),
selection = 'none', editable = TRUE)
}
# Run the application
shinyApp(ui = ui, server = server)
If you run that it looks like:
You can edit the cells since I added editable = TRUE within renderDataTable().
My table that feeds the datatable has the line:
mtcars %>% select(cyl) %>% mutate(blah = cyl + 2)
So feature 'blah' should always be whatever is in cyl + 2. In the screen shot I added 10,000, so desired output would be for the datatable to update to show 10,002 after hitting enter.
Is this possible? How can I do this?

You could follow these examples.
Try :
library(shiny)
library(tidyverse)
library(DT)
ui <- fluidPage(
# Application title
titlePanel("blah"),
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Show a plot of the generated distribution
mainPanel(
DT::DTOutput('modtable'),
)
)
)
server <- function(input, output,session) {
data <- mtcars %>% select(cyl) %>% mutate(blah = cyl + 2)
output$modtable <- DT::renderDT(data, selection = 'none', editable = TRUE)
proxy = dataTableProxy('modtable')
observeEvent(input$modtable_cell_edit, {
info = input$modtable_cell_edit
str(info)
i = info$row
j = info$col
v = info$value
data <<- editData(data, info)
if(j==1){data[i,j+1]<<-as.numeric(data[i,j])+2}
replaceData(proxy, data, resetPaging = FALSE)
})
}
# Run the application
shinyApp(ui = ui, server = server)

Related

Make default input values reactively reflect filtered dataset values

When I build a shiny app, I would like the default values of my filters to reflect the min and max of the working dataset.
For instance, is the app user filters to a specific subgroup (e.g., 4 cylinder cars), how can I reactively change the default values for min and max MPG in the inputs?
library(shiny)
library(shinyWidgets)
library(tidyverse)
ui <-
fluidPage(
## side panel
#############################################################################
sidebarPanel(
pickerInput(
inputId = 'cyl',
label = 'Cylinders',
choices = sort(unique(mtcars$cyl)),
options = list(`actions-box` = TRUE),
multiple = TRUE,
selected = unique(mtcars$cyl)
),
numericInput("mpg_max", "MPG max", 50, min = 0, max = 50), # hardcoded max
numericInput("mpg_min", "MPG min", 0, min = 0, max = 50), # hardcoded min
),
## main panel
#############################################################################
plotOutput("plot")
)
server <- function(input, output) {
output$plot <- renderPlot({
mtcars %>%
filter(
cyl %in% input$cyl
# between(mpg, input$mpg_min, input$mpg_max)
) %>%
ggplot(aes(x = wt, y = mpg)) +
geom_point() +
coord_cartesian(ylim = c(input$mpg_min, input$mpg_max))
})
}
shinyApp(ui, server)
If the user changes the Cylinder input to 4 there are no mpg values below 20, but I'm not sure how to update the default input values for MPG min with this info because reactive programming tends to happen in the server rather than ui area of a shiny app.
observeEvent() was the solution!
library(shiny)
library(shinyWidgets)
library(tidyverse)
ui <-
fluidPage(
## side panel
#############################################################################
sidebarPanel(
pickerInput(
inputId = 'cyl',
label = 'Cylinders',
choices = sort(unique(mtcars$cyl)),
options = list(`actions-box` = TRUE),
multiple = TRUE,
selected = unique(mtcars$cyl)
),
numericInput("mpg_max", "MPG max", 50, min = 0, max = 50),
numericInput("mpg_min", "MPG min", value = min(mtcars$mpg)-5, min = 0, max = 50),
),
## main panel
#############################################################################
plotOutput("plot")
)
server <- function(session, input, output) {
observeEvent(input$cyl, {
x <- mtcars %>% filter(cyl %in% input$cyl) %>% summarise(min(mpg)) %>% pull()
updateNumericInput(session, "mpg_min", value = x-5)
})
output$plot <- renderPlot({
mtcars %>%
filter(
cyl %in% input$cyl,
between(mpg, input$mpg_min, input$mpg_max)
) %>%
ggplot(aes(x = wt, y = mpg)) +
geom_point() +
coord_cartesian(ylim = c(input$mpg_min, input$mpg_max))
})
}
shinyApp(ui, server)

Selecting input that is filtered based on previously selected inputs

I'm working on a project where table is filtered based on the Inputs provided by the user. There are three selectInput conditions.
For better understanding lets assume the mtcars data. User can first select the number of cylinders, then the user should see a selectInput list of number of gears filtered for given value of cylinder. (**for instance, if number of cylinder is 4, then number of gears should be either 4,3,5 **)
Similarly, after selecting the Number of Cylinders and Number of gears the user must see the value of Transmission type as either 0,1.
The table should be updated and filtered based on the selected inputs.
I have tried the given code. Please help me.
#loading libraries
library(tidyverse)
library(shiny)
library(DT)
#using mtcars as dataset
df <- read.csv("https://gist.githubusercontent.com/seankross/a412dfbd88b3db70b74b/raw/5f23f993cd87c283ce766e7ac6b329ee7cc2e1d1/mtcars.csv")
# Define UI for application
ui <- fluidPage(
# Application title
titlePanel("Details of Given Cars"),
# Sidebar for input filter
sidebarLayout(
sidebarPanel(
selectInput("cylinder","Number of Cylinders",unique(df$cyl)),
selectizeInput("gears","Number of gears",choices = NULL),
selectizeInput("gearbox","Transmission Type 'AUTO=0'",choices = NULL)
),
# Show a table
mainPanel(
DT::DTOutput("table")
)
)
)
# Define server logic required
server <- function(input, output,session) {
#----reactive calculations
cyl_sel <- reactive({
df %>% filter(cyl == input$cylinder)
})
observeEvent(cyl_sel(),{
updateSelectizeInput(session,"gears", choices = cyl_sel()$gear)
# })
gearbox_sel <- reactive({
cyl_sel() %>% filter(am == input$gears)
})
observeEvent(gearbox_sel,{
updateSelectizeInput(session,"gearbox",choices = gearbox_sel()$am)
output$table <- DT::renderDT({
df %>% filter(cyl == input$cylinder,
gear == input$gears)
# am== input$gearbox) # commented because output is not shown when uncommented
})
})
})
}
# Run the application
shinyApp(ui = ui, server = server)
You could use selectizeGroupUI from library(shinyWidgets) to achive this:
library(datasets)
library(shiny)
library(shinyWidgets)
library(DT)
df <- mtcars
ui <- fluidPage(
titlePanel("Details of Given Cars"),
sidebarLayout(
sidebarPanel(
selectizeGroupUI(
id = "my-filters",
params = list(
cyl = list(inputId = "cyl", title = "Number of Cylinders:"),
gear = list(inputId = "gear", title = "Number of gears:"),
am = list(inputId = "am", title = "Transmission Type 'AUTO=0':")
),
inline = FALSE
)
),
mainPanel(
DT::DTOutput("table")
)
)
)
server <- function(input, output, session) {
res_mod <- callModule(
module = selectizeGroupServer,
id = "my-filters",
data = df,
vars = c("cyl", "gear", "am"),
inline = FALSE
)
output$table <- DT::renderDT(res_mod())
}
shinyApp(ui = ui, server = server)

edit a reactive database

Trying to edit a reactive database so that updates to the database are reflected in the output.
Have tried numerous variants, but none are working, general idea is shown - where I would like to have the figure update with changes to the database.
library(tidyverse)
library(shiny)
library(DT)
# Define UI for application that draws a histogram
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("ages", "Max age:", 10, 100, 15),
sliderInput("nsamp",
"Sample size:",
min = 10,
max = 1000,
value = 100)),
mainPanel(dt_output('Sample sizes and weighting', 'x1'),
plotOutput("fig"))
)
)
server <- function(input, output) {
x = reactive({
df = data.frame(age = 1:input$ages,
samples = input$nsamp,
weighting = 1)
})
output$x1 = renderDT(x(),
selection = 'none',
editable = TRUE,
server = TRUE,
rownames = FALSE)
output$fig = renderPlot({
ggplot(x(), aes(age, samples)) +
geom_line() +
geom_point()
})
}
shinyApp(ui = ui, server = server)
We can use input$x1_cell_edit and reactiveValues to modify the data that is passed to the plot.
Note the use of isolate inside renderDT, that is to prevent the table from re-rendering when db$database is modified.
library(tidyverse)
library(shiny)
library(DT)
# Define UI for application that draws a histogram
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("ages", "Max age:", 10, 100, 15),
sliderInput("nsamp",
"Sample size:",
min = 10,
max = 1000,
value = 100
)
),
mainPanel(
dataTableOutput("x1"),
plotOutput("fig")
)
)
)
server <- function(input, output) {
# all the data will be stored in this two objects
db <- reactiveValues(database = NULL)
# to store the modified values
edited_vals <- reactiveVal(tibble(row = numeric(), col = numeric(), value = numeric()))
# create a new table each time the sliders are changed
observeEvent(c(input$ages, input$nsamp), {
df <- data.frame(
age = 1:input$ages,
samples = input$nsamp,
weighting = 1
)
db$database <- df
})
observeEvent(input$x1_cell_edit, {
db$database[as.numeric(input$x1_cell_edit$row), as.numeric(input$x1_cell_edit$col + 1)] <- as.numeric(input$x1_cell_edit$value)
})
output$x1 <- renderDT(
{
input$ages
input$nsamp
datatable(
isolate(db$database),
selection = "none",
editable = TRUE,
rownames = FALSE,
options = list(stateSave = TRUE)
)
},
server = TRUE
)
output$fig <- renderPlot({
ggplot(db$database, aes(as.numeric(age), as.numeric(samples))) +
geom_point() +
geom_line()
})
}
shinyApp(ui = ui, server = server)

DT with editable cells error with observeEvent() editData() "Warning: Error in split.default: first argument must be a vector"

I am attempting to create a shiny app with editable cells where the underlying data frame updates depending on user input. I asked a similar question earlier and was pointed to this link.
My app:
library(shiny)
library(tidyverse)
library(DT)
ui <- fluidPage(
# Application title
titlePanel("blah"),
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Show a plot of the generated distribution
mainPanel(
DT::DTOutput('ex_table'),
)
)
)
server <- function(input, output,session) {
example_data <- data.frame(x = rnorm(10, 0, 1) %>% round) %>% mutate(y = x + 1)
output$ex_table <- DT::renderDT(example_data, selection = 'none', editable = TRUE)
# from https://yihui.shinyapps.io/DT-edit/
observeEvent(input$ex_table_cell_edit, {
example_data <<- editData(example_data, input$ex_table, 'ex_table', rownames = FALSE)
})
}
# Run the application
shinyApp(ui = ui, server = server)
This app loads when you press run in rstudio. But when trying to edit a cell in column x, the app crashes with error message 'Warning: Error in split.default: first argument must be a vector'.
This is the problem code block:
# from https://yihui.shinyapps.io/DT-edit/
observeEvent(input$ex_table_cell_edit, {
example_data <<- editData(example_data, input$ex_table, 'ex_table', rownames = FALSE)
})
Screens:
The app loads up fine. Y is always x + 1 due to the data frame definition:
example_data <- data.frame(x = rnorm(10, 0, 1) %>% round) %>% mutate(y = x + 1)
When a user edits the x column, I wouldlike the y column to update to be whatever x is plus one:
When I press enter, desired behavior is to have y = 101.
Per the link suggested, https://yihui.shinyapps.io/DT-edit/, I'd prefer to use editData() as opposed to what was provided in my previous post, because editData() approach looks simpler and more readable.
But when I try it my shiny app always crashes?
Your existing program works fine if you put rownames=FALSE in output$ex_table. However, it only allows you to edit table cells. If you still want to maintain the dependency y=x+1, you need to define like #Waldi did in his answer earlier. Also, once you modify, you need to feed it back to the output via replaceData() of Proxy or define a reactiveValues object as shown below.
ui <- fluidPage(
# Application title
titlePanel("blah"),
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Show a plot of the generated distribution
mainPanel(
DTOutput('ex_table'),
)
)
)
server <- function(input, output,session) {
DF1 <- reactiveValues(data=NULL)
example_data <- data.frame(x = rnorm(10, 0, 1) %>% round) %>% mutate(y = x + 1)
DF1$data <- example_data
output$ex_table <- renderDT(DF1$data, selection = 'none', editable = TRUE, rownames = FALSE)
observeEvent(input$ex_table_cell_edit, {
info = input$ex_table_cell_edit
str(info)
i = info$row
j = info$col + 1 ## offset by 1
example_data <<- editData(example_data, input$ex_table_cell_edit, 'ex_table', rownames = FALSE)
if(j==1){example_data[i,j+1]<<-as.numeric(example_data[i,j])+1} ### y = x + 1 dependency
DF1$data <- example_data
})
}
# Run the application
shinyApp(ui = ui, server = server)

Shiny nearPoints() with slider input

I was wondering if I can get rows data using nearPoints() from an interactive graph with slider input. My app.R file looks like:
library('shiny')
library('ggplot2')
dt <-read.csv('file.csv')
ui <- fluidPage(
plotOutput("plot1", height = 550, click = "plot1_click"),
fluidRow(
column(3,
sliderInput("Obs", "Number of Books", min = 1, max = nrow(up), value = 50)
),
column(3, offset = 3,
h4("Legends"),
verbatimTextOutput("selected")
)
)
)
server <- function(input, output) {
mydata <- reactive({
dt[1:as.numeric(input$Obs),]
})
output$plot1 <- renderPlot({
test <- mydata()
ggplot(data = test, aes(x = test[,2], y = test[,1])) + geom_point()
})
output$selected <- renderPrint({
file <- mydata()
nearPoints(file, input$plot1_click, threshold = 10, maxpoints = 1,
addDist = FALSE)
})
}
shinyApp(ui = ui, server = server)
Shiny nearPoints() is working perfectly without this slider input. When I used slider input, I can't get the row data until max. Is there any approach to work with the slider input? Any help is appreciated.
The following code works for me. It seems nearPoints is not able to tell which columns of your dataset are displayed because of the aes(x = test[,2], y = test[,1]) statement. Another possible fix sould be to set the parameters xvar and yvar in nearPoints.
library('shiny')
library('ggplot2')
dt <-mtcars
ui <- fluidPage(
plotOutput("plot1", height = 550, click = "plot1_click"),
fluidRow(
column(3,
sliderInput("Obs", "Number of Cars", min = 1, max = nrow(dt), value = 50)
),
column(3, offset = 3,
h4("Legends"),
verbatimTextOutput("selected")
)
)
)
server <- function(input, output) {
mydata <- reactive({
dt[1:as.numeric(input$Obs),]
})
output$plot1 <- renderPlot({
test <- mydata()
ggplot(data = test, aes(mpg, wt)) + geom_point()
})
output$selected <- renderPrint({
file <- mydata()
nearPoints(file, input$plot1_click, threshold = 100, maxpoints = 1,
addDist = FALSE)
})
}
shinyApp(ui = ui, server = server)
Quick note: Please try to make the code in your question reproducible by using one of the default datasets in R. You can get a list of all available datasets by calling data().

Resources