R Shiny: loop that generates as many outputs as the user chooses - r

I would like to create a loop that generates as many outputs as the user chooses.
for example, if the user chooses 10 models, 10 outputs of those 10 models will come out.
Note: I know that inputs cannot be used within the UI. I just use it as a reference to make it clearer to understand what I am trying to do.
Here is what I've tried:
shinyApp(ui= fluidPage(checkboxGroupInput('aa','model',c('a','b','c'), 'a'),
for(i in 1:length(input$aa)){
function(i){uiOutput(paste0('prob', i))}
}),
server = function(input, output){
for(i in input$aa){
assign(paste('output$prob',i,sep = ''),renderUI({paste('model',i)}))
}
})
Thanks.

In shiny, for loops don't work well, it's better to use lapply. Below you find a solution with lapply, you could also take a look at insertUI/removeUI
library(shiny)
ui <- fluidPage(checkboxGroupInput('aa','model',c('a','b','c'), 'a'),
uiOutput("formulas")
)
server <- function(input, output, session) {
output$formulas <- renderUI({
lapply(input$aa, function(model) {
verbatimTextOutput(paste0("prob_", model))
})
})
observeEvent(input$aa, {
lapply(input$aa, function(model) {
output[[paste0("prob_", model)]] <- renderPrint({paste0("model ", model)})
})
})
}
shinyApp(ui, server)

Related

How to automatically collapse code in RShiny app server (reactives, renders, etc)

I am working with a very large RShiny app and want to take advantage of code folding to organize the server.R file in this application. However, when I use the code-fold hotkey, it does not fold the various elements defined in the server (the reactive, render, etc. elements).
I'd like to be able to take this
# observe some things
observe({
query <- parseQueryString(session$clientData$url_search)
if (!is.null(query[['tab']])) {
updateTabItems(session, "sidebarMenu", selected = query[['tab']])
}
if (!is.null(query[['player']])) {
updateSelectInput(session, "profile", selected = query[['player']])
}
})
# Lots of "reactive" data fetching functions
league_stats <- reactive({
get1 <- fetch('yada')
return(get1)
})
# another reactive
shooting <- reactive({
get1$SHORT_MR_MADE<-sum(get1$short_mr_fgm,na.rm=T)
...
...
)}
and collapse it into this (or something like this) by just hitting the code-collapse hotkey.
# observe some things
observe({--})
# Lots of "reactive" data fetching functions
league_stats <- reactive({--})
# another reactive
shooting <- reactive({--})
Is this possible to do with R / RStudio? I would like to avoid using the 4 # signs #### above the function to code fold, as this will hide the shooting <- reactive({--}) strings as well, however I'd like to still have show (and just hide the code inside).
I will oftentimes wrap code in functions since functions collapse, however I cannot wrap RShiny reactive elements in functions (or, i'm not sure how), as it seems like this breaks the app.
Shiny reactives behave as other functions, but you need to take care about passing to them the input, session or other reactives (as function, not as value) they need.
As an illustration :
library(shiny)
generateUI <- function() {fluidPage(
actionButton("do", "Click Me"),
textOutput('counter')
)}
ui <- generateUI()
myobserver <- function(input,counter) {
observeEvent(input$do, {
cat('Clicked \n')
counter(counter()+1)
})
}
myformater <- function(counter) {
renderText(paste('count is',counter()))
}
server <- function(input, output, session) {
counter <- reactiveVal(0)
myobserver(input,counter)
output$counter <- myformater(counter)
}
shinyApp(ui, server)
Collapsed code :
Another way to do this without creating them as functions is to put an identifier above each code chunk:
library(shiny)
# Generate UI ----
generateUI <- function() {fluidPage(
actionButton("do", "Click Me"),
textOutput('counter')
)}
ui <- generateUI()
# Observer ----
myobserver <- function(input,counter) {
observeEvent(input$do, {
cat('Clicked \n')
counter(counter()+1)
})
}
# Formatter ----
myformater <- function(counter) {
renderText(paste('count is',counter()))
}
# Server ----
server <- function(input, output, session) {
counter <- reactiveVal(0)
myobserver(input,counter)
output$counter <- myformater(counter)
}
shinyApp(ui, server)
You will then be able to collapse code segments in between the two identifiers to view as shown below:

Using Shiny to update dataframe values and access it in the local environment after Shiny session ends

I have been going through most of the Q&As related to dataframe manipulation within Shiny and I still don't understand how to do something which, in my mind, should be very simple. I don't have experience writing Shiny apps and I'm still struggling with concepts like reactive events.
I have a dataframe A, loaded into R. I want to be able to see a specific value in a specific column in the dataframe in the UI and then edit it. After I edit the dataframe, I want to close the Shiny app and then see the edited dataframe in the Environment tab of RStudio. How do I go about doing this?
I think this might be a workable example.
Assume df is your data frame (I used iris to test, commented out below). Create a reactiveVal to hold your data, and use for editing with datatable. After editing, you can store the data back into your global environment dataframe df with <<-. An alternative is to do this when exiting the shiny app (such as through the onStop or session$onSessionEnded method).
library(shiny)
library(DT)
#df <- iris
ui <- fluidPage(
DT::dataTableOutput('data'),
)
server <- function(input, output) {
rv <- reactiveVal(df)
output$data <- DT::renderDataTable ({
DT::datatable(rv(), editable = TRUE)
})
observeEvent(input$data_cell_edit, {
info <- input$data_cell_edit
newdf <- rv()
newdf[info$row, info$col] <- info$value
rv(newdf)
df <<- rv()
})
}
shinyApp(ui = ui, server = server)
Alternative with replacing global df on exiting (requires session):
server <- function(input, output, session) {
rv <- reactiveVal(df)
output$data <- DT::renderDataTable ({
DT::datatable(rv(), editable = TRUE)
})
observeEvent(input$data_cell_edit, {
info <- input$data_cell_edit
newdf <- rv()
newdf[info$row, info$col] <- info$value
rv(newdf)
})
session$onSessionEnded(function() {
df <<- isolate(rv())
})
}
If you don't want to use reactive values, I suppose you could try the following. This can update your data.frame in the global environment as edits are made. Note that server = FALSE is added to handle changes in pages:
server <- function(input, output) {
output$data <- DT::renderDT (df, editable = TRUE, server = FALSE)
observeEvent(input$data_cell_edit, {
info <- input$data_cell_edit
df[info$row, info$col] <<- info$value
})
}

R Shiny Handsontable automatically updating function after changing a single cell

I am trying to get familiar with the rhandsontable package. So I tried something I thought should be pretty easy but I can't find a solution. Here is the idea:
I am creating a dataframe with random numbers and in a text box. The mean of column 1 of the dataframe should be displayed. Furthermore, that number should be updated as soon as I change the value of a cell in the dataframe.
My code:
ui <- fluidPage(
textOutput("num"),
rHandsontableOutput(outputId="frame")
)
server <- function(input, output, session) {
datavalue <- reactiveValues(data=df)
observeEvent(input$frame$changes$changes,{
mean_col1 <- mean(datavalue$data[[1]][1:10])
})
output$num <- renderText({
mean(datavalue$data[[1]][1:10])
})
output$frame <- renderRHandsontable({
rhandsontable(datavalue$data)
})
}
shinyApp(ui = ui, server = server)
I think you want to use hot_to_r to convert the handsontable to an R object when there is a change. You can update your reactiveValue datavalue$data when that happens, and your output$num will account for this change as well with the new mean.
Try using this in your observeEvent:
datavalue$data <- hot_to_r(input$frame)
As an alternative, you can do a general observe as follows:
observe({
req(input$frame)
datavalue$data <- hot_to_r(input$frame)
})

Is it possible to create a user-defined function that takes reactive objects as input? How do I do it?

So, I've been on google for hours with no answer.
I want to create a user-defined function inside the server side that takes inputs that I already know to wrap reactive({input$feature)} but the issue is how to incorporate reactive values as inputs too.
The reason why I want to do this is because I have a navbarPage with multiple tabs that shares elements such as same plots. So I want a user defined function that creates all the similar filtering and not have to create multiple of the same reactive expression with different input and reactive variable names which take up 2000+ lines of code.
server <- function(input, output) {
filtered_JointKSA <- reactiveVal(0)
create_filtered_data <- function(df, input_specialtya, filtered_JointKSA) {
if (input_specialtya == 'manual') {
data <- filter(data, SPECIALTY %in% input_specialtyb)
}
if (filtered_JointKSA != 0) {
data <- filter(data, SPECIALTY %in% filtered_JointKSA)
}
reactive({return(data)})
}
filtered_data <- create_filtered_data(df,
reactive({input$specialty1}),
filtered_JointKSA())
observeEvent(
eventExpr = input$clickJointKSA,
handlerExpr = {
A <- filtered_JointKSA(levels(fct_drop(filtered_data()$`Joint KSA Grouping`))[round(input$clickJointKSA$y)])
A
}
)
This gets me an error:
"Error in match(x, table, nomatch = 0L) :
'match' requires vector arguments"
The error is gone if I comment out where I try to create filtered_data but none of my plots are created because filtered_data() is not found.
What is the correct approach for this?
Ideally, I would like my observeEvents to be inside user defined functions as well if that has a different method.
This example may provide some help, but it's hard to tell without a working example. The change is to wrap the call to your function in reactive({}) rather than the inputs to that function, so that the inputs are all responsive to user input and the function will update.
library(shiny)
ui <- fluidPage(
numericInput("num", "Number", value = NULL),
verbatimTextOutput("out")
)
server <- function(input, output){
## User-defined function, taking a reactive input
rvals <- function(x){
req(input$num)
if(x > 5){x * 10} else {x*1}
}
# Call to the function, wrapped in a reactive
n <- reactive({ rvals(input$num) })
# Using output of the function, which is reactive and needs to be resolved with '()'
output$out <- renderText({ n() })
}
shinyApp(ui, server)

Connect R script with selectizeInput in shiny

I have an R script (let us call it myscript.R) which is a function of input$year.
Once I select the year in the shinyapp I want that the computer run "myscript.R" ?
I tried kind of the following structure,but it does not work
fun=function(input,ouput,session){
year= input$year
}
observeEvent(input$year,{
fun(input,output,session)
})
Your answers are appreciated!
I am not sure if a function from a script is really what you want here. If you want to make output dependent on input, this is how you do it in Shiny:
library(shiny)
ui <- fluidPage(
selectInput("year","Year: ",choices=c(2000,2001,2002)),
textOutput("test")
)
server <- function(input, output, session) {
test_reactive <- reactive({
year = as.numeric(input$year)
year = year + 1
return(year)
})
output$test <- renderText({
test_reactive()
})
}
runApp(shinyApp(ui, server))
If you really want to call a function from a script, and within script.R you have a function, like:
my_function <- function(year)
{
...
}
You should do source(script.R) somewhere above the server function, and do my_function(year) where I have added 1 to the year.
Hope this helps.

Resources