add/remove fields in shiny web app without changing choice - r

I have the following web application example:
library(shiny)
ui <- shinyUI(pageWithSidebar(
headerPanel("Add Features"),
sidebarPanel(width=4,
fluidRow(column(12,
h3('Features'),
uiOutput('uiOutpt')
)), # END fluidRow
fluidRow(
column(4,div()),
column(4,actionButton("add", "Add!")),
column(4,actionButton("remove", "Remove!")),
column(4,actionButton('goButton',"Analyze"))
) # END fluidRow
), # END sidebarPanel
mainPanel(
textOutput("text2"),
tableOutput('tbl')
)
))
server <- shinyServer(function(input, output) {
features <- reactiveValues(renderd=c(1),
conv=c(50),
inlabels=c('A'),
outlabels=c('B'))
df <- eventReactive(input$goButton, {
out <- lapply(features$renderd,function(i){
fv <- paste0('numInp_',i)
vn <- paste0('InLabel',i)
data.frame(Variable=input[[vn]], Value=input[[fv]] )
})
do.call(rbind,out)
})
output$nText <- renderText({
ntext()
})
output$text2 <- renderText({
paste(sprintf("You have selected feature: %s", paste(features$renderd,collapse=", ")))
})
output$tbl <- renderTable({
df()
})
# Increment reactive values array used to store how may rows we have rendered
observeEvent(input$add,{
out <- lapply(features$renderd,function(i){
fv <- paste0('numInp_',i)
vn <- paste0('InLabel',i)
vo <- paste0('OutLabel',i)
data.frame(inlabels=input[[vn]],outlabels=input[[vo]], conv=input[[fv]] )
})
df<-do.call(rbind,out)
print(df)
features$inlabels <- c(as.character(df$inlabels),' ')
features$outlabels <- c(as.character(df$outlabels),' ')
print(c(features$inlabels,features$outlabels))
features$renderd <- c(features$renderd, length(features$renderd)+1)
print(features$renderd)
print(names(features))
features$conv<-c(df$conv,51-length(features$renderd))
})
observeEvent(input$remove,{
features$renderd <- features$renderd[-length(features$renderd)]
})
# If reactive vector updated we render the UI again
observe({
output$uiOutpt <- renderUI({
# Create rows
rows <- lapply(features$renderd,function(i){
fluidRow(
# duplicate choices make selectize poop the bed, use unique():
column(4, selectizeInput(paste0('InLabel',i),
label = 'Input Name',selected=features$inlabels[i-1],
choices=unique(c(features$inlabels[i-1],features$outlabels[!features$outlabels %in% features$inlabels])),
options = list(create = TRUE))),
column(4, sliderInput(paste0('numInp_',i), label="Conversion",min = 0, max = 100, value = features$conv[i-1])),
column(4, selectizeInput(paste0('OutLabel',i),
label = "Output Name", selected=features$outlabels[i-1],
choices=unique(c(features$inlabels,features$outlabels)),
options = list(create = TRUE)))
)
})
do.call(shiny::tagList,rows)
})
})
})
shinyApp(ui=ui,server=server)
The problem is that each time, when we add a new fluidRow by clicking on the buton "add", the chosen values in the previous fluidRow are refreshed. I would like to change that. If I chose for instance, inputName='B', Conversion=50, outputName='A', I would like them to be constant even thought I have add or delete rows.
I have tried this but it didn't work:
library(shiny)
ui <- shinyUI(pageWithSidebar(
headerPanel("Add Features"),
sidebarPanel(width=4,
fluidRow(column(12,
h3('Features'),
uiOutput('uiOutpt')
)), # END fluidRow
fluidRow(
column(4,div()),
column(4,actionButton("add", "Add!")),
column(4,actionButton("remove", "Remove!")),
column(4,actionButton('goButton',"Analyze"))
) # END fluidRow
), # END sidebarPanel
mainPanel(
textOutput("text2"),
tableOutput('tbl')
)
))
server <- shinyServer(function(input, output) {
features <- reactiveValues(renderd=c(1),
conv=c(50),
inlabels=c('A'),
outlabels=c('B'))
df <- eventReactive(input$goButton, {
out <- lapply(features$renderd,function(i){
fv <- paste0('numInp_',i)
vn <- paste0('InLabel',i)
data.frame(Variable=input[[vn]], Value=input[[fv]] )
})
do.call(rbind,out)
})
output$nText <- renderText({
ntext()
})
output$text2 <- renderText({
paste(sprintf("You have selected feature: %s", paste(features$renderd,collapse=", ")))
})
output$tbl <- renderTable({
df()
})
# Increment reactive values array used to store how may rows we have rendered
observeEvent(input$add,{
out <- lapply(features$renderd,function(i){
fv <- paste0('numInp_',i)
vn <- paste0('InLabel',i)
vo <- paste0('OutLabel',i)
data.frame(inlabels=input[[vn]],outlabels=input[[vo]], conv=input[[fv]] )
})
df<-do.call(rbind,out)
print(df)
features$inlabels <- c(as.character(df$inlabels),' ')
features$outlabels <- c(as.character(df$outlabels),' ')
print(c(features$inlabels,features$outlabels))
features$renderd <- c(features$renderd, length(features$renderd)+1)
print(features$renderd)
print(names(features))
features$conv<-c(df$conv,51-length(features$renderd))
})
observeEvent(input$remove,{
features$renderd <- features$renderd[-length(features$renderd)]
})
# If reactive vector updated we render the UI again
observe({
output$uiOutpt <- renderUI({
# Create rows
rows <- lapply(features$renderd,function(i){
fluidRow(
# duplicate choices make selectize poop the bed, use unique():
column(4, selectizeInput(paste0('InLabel',i),
label = 'Input Name',selected=features$inlabels[i],
choices=c('A','B','C'),
options = list(create = TRUE))),
column(4, sliderInput(paste0('numInp_',i), label="Conversion",min = 0, max = 100, value = features$conv[i])),
column(4, selectizeInput(paste0('OutLabel',i),
label = "Output Name", selected=features$outlabels[i],
choices=c('A','B','C'),
options = list(create = TRUE)))
)
})
do.call(shiny::tagList,rows)
})
})
})
shinyApp(ui=ui,server=server)
I am sure that it's very easy to figure it out but I have no idea.
Thank you for your response.

I have found the answer of my question. I think that it could be useful for someone that's why I post the follwing example:
library(shiny)
ui <- shinyUI(
fluidPage(
actionButton("addFilter", "Add filter", icon=icon("plus", class=NULL, lib="font-awesome")),
uiOutput("filterPage1")
)
)
server <- function(input, output){
i <- 0
observeEvent(input$addFilter, {
i <<- i + 1
output[[paste("filterPage",i,sep="")]] = renderUI({
list(
fluidPage(
fluidRow(
column(6, selectInput(paste("filteringFactor",i,sep=""), "Choose factor to filter by:",
choices=c("factor A", "factor B", "factor C"), selected="factor B",
width="100%")),
column(6, actionButton(paste("removeFactor",i,sep=""), "",
icon=icon("times", class = NULL, lib = "font-awesome"),
onclick = paste0("Shiny.onInputChange('remove', ", i, ")")))
)
),
uiOutput(paste("filterPage",i + 1,sep=""))
)
})
})
observeEvent(input$remove, {
i <- input$remove
output[[paste("filterPage",i,sep="")]] <- renderUI({uiOutput(paste("filterPage",i + 1,sep=""))})
})
}
shinyApp(ui, server)
Have a nice day.

Related

R shiny observeEvent() cannot isolate the reactivity when input$files parameters changes

I met a problem abount R shiny observeEvent(). I have to upload three csv table files to separately show at different tabpanels. And I set a selectInput to set if to show header of table. At last I give a actionButton(ui)-observeEvent(server) to decide whether to run the showing process. But I find the selectInput just skip the observeEvent(), dynamicly change the show.That is observeEvent is invalidted.I dont'know why.I want selectInput can be under control of actionButton(). I doubt if observeEvent() is a good option to execute the job. Hope somebody can help me! Thanks in advance. Here is my demo code
# get 3 test uploaded files
data(mtcars)
test1 <- mtcars[,c(1:3)]
test2 <- mtcars[,c(5:8)]
test3 <- mtcars[c(1:3),]
write.csv(test1,file = "test1.csv")
write.csv(test2,file = "test2.csv")
write.csv(test3,file = "test3.csv")
# shiny part
library(shiny)
ui <- fluidPage(
# useShinyjs(),
sidebarLayout(
sidebarPanel(
fileInput(
inputId = "files",
label = "Choose CSV File",
multiple = TRUE,
accept = c("text/csv",
"text/comma-separated-values,text/plain",
".csv")
),
tags$hr(),
selectInput("type", "Choose Data:", choices = c('Noheader'=TRUE,'Header'=FALSE)),
tags$hr(),
actionButton("update", "show",class = "btn btn-primary btn-lg",icon = icon("refresh"), width = "120px"),
),
mainPanel(
uiOutput("mytabs"),
textOutput("text_null", container = h4)
)
)
)
server <- function(input, output, session){
values <- reactiveValues(file_data=NULL)
filedata <- reactive({
req(input$files)
upload = list()
for(nr in 1:length(input$files[, 1])){
raw_name <- sub(".csv$", "",input$files[[nr, 'name']])
upload[[raw_name]] <- read.csv(file = input$files[[nr, 'datapath']],header = as.logical(input$type))
}
return((upload))
})
observe({
output$mytabs = renderUI({
values$file_data <- filedata()
nTabs <- length(filedata())
tabNames <- names(values$file_data)
myTabs = lapply(1: nTabs, function(i) {
tabPanel( tabNames[i],
tags$div(class = "group-output",
tags$br(),
tableOutput(paste0("Group",i))#))
)
)
})
do.call(tabsetPanel, myTabs)
})
})
observeEvent(input$update, {
values$file_data <- filedata()
nn_Tabs <- length(filedata())
progress <<- shiny::Progress$new()
on.exit(progress$close())
progress$set(message = "Begin to process data", value = 0)
for (i in 1: nn_Tabs){
local({
my_n <- i
TableName <- paste0("Group",my_n)
output[[TableName]] <- renderTable({ values$file_data[[my_n]] })
print(values$file_data[[my_n]])
progress$inc(1/nn_Tabs, detail = ", Please wait...")
})
}
progress$set(message = "Finished!", value = 1)
})
}
shinyApp(ui, server)
The problem is that you wrap output$mytabs in an observe. I'm not sure why this influences also the content of the output$Group1 etc. you generate in the renderUI call and overrules the observeEvent. Anyway, you don't need the observe, outputs are automatically updated when a dependency changes:
# get 3 test uploaded files
data(mtcars)
test1 <- mtcars[,c(1:3)]
test2 <- mtcars[,c(5:8)]
test3 <- mtcars[c(1:3),]
write.csv(test1,file = "test1.csv")
write.csv(test2,file = "test2.csv")
write.csv(test3,file = "test3.csv")
# shiny part
library(shiny)
ui <- fluidPage(
# useShinyjs(),
sidebarLayout(
sidebarPanel(
fileInput(
inputId = "files",
label = "Choose CSV File",
multiple = TRUE,
accept = c("text/csv",
"text/comma-separated-values,text/plain",
".csv")
),
tags$hr(),
selectInput("type", "Choose Data:", choices = c('Noheader'=TRUE,'Header'=FALSE)),
tags$hr(),
actionButton("update", "show",class = "btn btn-primary btn-lg",icon = icon("refresh"), width = "120px"),
),
mainPanel(
uiOutput("mytabs"),
textOutput("text_null", container = h4)
)
)
)
server <- function(input, output, session){
values <- reactiveValues(file_data=NULL)
filedata <- reactive({
req(input$files)
upload = list()
for(nr in 1:length(input$files[, 1])){
raw_name <- sub(".csv$", "",input$files[[nr, 'name']])
upload[[raw_name]] <- read.csv(file = input$files[[nr, 'datapath']],header = as.logical(input$type))
}
return((upload))
})
output$mytabs = renderUI({
values$file_data <- filedata()
nTabs <- length(filedata())
tabNames <- names(values$file_data)
myTabs = lapply(1: nTabs, function(i) {
tabPanel( tabNames[i],
tags$div(class = "group-output",
tags$br(),
tableOutput(paste0("Group",i))#))
)
)
})
do.call(tabsetPanel, myTabs)
})
observeEvent(input$update, {
values$file_data <- filedata()
nn_Tabs <- length(filedata())
progress <<- shiny::Progress$new()
on.exit(progress$close())
progress$set(message = "Begin to process data", value = 0)
for (i in 1: nn_Tabs){
local({
my_n <- i
TableName <- paste0("Group",my_n)
output[[TableName]] <- renderTable({ values$file_data[[my_n]] })
print(values$file_data[[my_n]])
progress$inc(1/nn_Tabs, detail = ", Please wait...")
})
}
progress$set(message = "Finished!", value = 1)
})
}
shinyApp(ui, server)
Edit
I think this solution is more what you want. Maybe one can optimise the last observe statement to a better coding pattern:
# get 3 test uploaded files
data(mtcars)
test1 <- mtcars[,c(1:3)]
test2 <- mtcars[,c(5:8)]
test3 <- mtcars[c(1:3),]
write.csv(test1,file = "test1.csv")
write.csv(test2,file = "test2.csv")
write.csv(test3,file = "test3.csv")
# shiny part
library(shiny)
ui <- fluidPage(
# useShinyjs(),
sidebarLayout(
sidebarPanel(
fileInput(
inputId = "files",
label = "Choose CSV File",
multiple = TRUE,
accept = c("text/csv",
"text/comma-separated-values,text/plain",
".csv")
),
tags$hr(),
selectInput("type", "Choose Data:", choices = c('Noheader'=TRUE,'Header'=FALSE)),
tags$hr(),
actionButton("update", "show",class = "btn btn-primary btn-lg",icon = icon("refresh"), width = "120px"),
),
mainPanel(
uiOutput("mytabs"),
textOutput("text_null", container = h4)
)
)
)
server <- function(input, output, session){
values <- reactiveValues(file_data=NULL)
filedata <- eventReactive(input$update, {
req(input$files)
upload = list()
for(nr in 1:length(input$files[, 1])){
raw_name <- sub(".csv$", "",input$files[[nr, 'name']])
upload[[raw_name]] <- read.csv(file = input$files[[nr, 'datapath']],header = as.logical(input$type))
}
return((upload))
})
output$mytabs = renderUI({
values$file_data <- filedata()
nTabs <- length(filedata())
tabNames <- names(values$file_data)
myTabs = lapply(1: nTabs, function(i) {
tabPanel( tabNames[i],
tags$div(class = "group-output",
tags$br(),
tableOutput(paste0("Group",i))#))
)
)
})
do.call(tabsetPanel, myTabs)
})
observe({
values$file_data <- filedata()
nn_Tabs <- length(filedata())
progress <<- shiny::Progress$new()
on.exit(progress$close())
progress$set(message = "Begin to process data", value = 0)
for (i in 1: nn_Tabs){
local({
my_n <- i
TableName <- paste0("Group",my_n)
output[[TableName]] <- renderTable({ values$file_data[[my_n]] })
print(values$file_data[[my_n]])
progress$inc(1/nn_Tabs, detail = ", Please wait...")
})
}
progress$set(message = "Finished!", value = 1)
})
}
shinyApp(ui, server)

Shiny: select variables to table

I have one question to open the topic already. Well, I'm trying to do a similar app to this one Shiny: dynamic dataframe construction; renderUI, observe, reactiveValues. And I would like to add a new category at the beginning which will select the variables from the table. I can not combine variables with other elements in an application. Could someone explain to me what I'm doing wrong?
As you can see on the graphics program does not work well.
Below is a script
#rm(list = ls())
library(shiny)
data <- data.frame(Category1 = rep(letters[1:3],each=15),
Info = paste("Text info",1:45),
Category2 = sample(letters[15:20],45,replace=T),
Size = sample(1:100, 45),
MoreStuff = paste("More Stuff",1:45))
ui <- fluidPage(
titlePanel("Test Explorer"),
sidebarLayout(
sidebarPanel(
selectizeInput("show_vars", "Columns to show:",
choices = colnames(data), multiple = TRUE,
selected = c("Category1","Info","Category2")),
uiOutput("category1"),
uiOutput("category2"),
uiOutput("sizeslider")
),
mainPanel(
tableOutput("table")
)
)
)
server <- function(input, output,session) {
data2 <- reactive({
req(input$table)
if(input$table == "All"){
return(data)
}
data[,names(data) %in% input$show_vars]
})
output$category1 <- renderUI({
selectizeInput('cat1', 'Choose Cat 1', choices = c("All",sort(as.character(unique(data$Category1)))),selected = "All")
})
df_subset <- eventReactive(input$cat1,{
if(input$cat1=="All") {df_subset <- data}
else{df_subset <- data[data$Category1 == input$cat1,]}
})
df_subset1 <- reactive({
if(is.null(input$cat2)){df_subset()} else {df_subset()[df_subset()$Category2 %in% input$cat2,]}
})
output$category2 <- renderUI({
selectizeInput('cat2', 'Choose Cat 2 (optional):', choices = sort(as.character(unique(df_subset()$Category2))), multiple = TRUE,options=NULL)
})
output$sizeslider <- renderUI({
sliderInput("size", label = "Size Range", min=min(df_subset1()$Size), max=max(df_subset1()$Size), value = c(min(df_subset1()$Size),max(df_subset1()$Size)))
})
df_subset2 <- reactive({
if(is.null(input$size)){df_subset1()} else {df_subset1()[df_subset1()$Size >= input$size[1] & df_subset1()$Size <= input$size[2],]}
})
output$table <- renderTable({
df_subset2()
})
}
shinyApp(ui, server)
You don't need data2 since you are not using it and instead you can just use the same condition to filter columns with %in% everywhere you are displaying the dataframe.
#rm(list = ls())
library(shiny)
data <- data.frame(Category1 = rep(letters[1:3],each=15),
Info = paste("Text info",1:45),
Category2 = sample(letters[15:20],45,replace=T),
Size = sample(1:100, 45),
MoreStuff = paste("More Stuff",1:45))
ui <- fluidPage(
titlePanel("Test Explorer"),
sidebarLayout(
sidebarPanel(
selectizeInput("show_vars", "Columns to show:",
choices = colnames(data), multiple = TRUE,
selected = c("Category1","Info","Category2")),
uiOutput("category1"),
uiOutput("category2"),
uiOutput("sizeslider")
),
mainPanel(
tableOutput("table")
)
)
)
server <- function(input, output,session) {
output$category1 <- renderUI({
selectizeInput('cat1', 'Choose Cat 1', choices = c("All",sort(as.character(unique(data$Category1)))),selected = "All")
})
df_subset <- eventReactive(input$cat1,{
if(input$cat1=="All") {df_subset <- data}
else{df_subset <- data[data$Category1 == input$cat1,names(data) %in% input$show_vars]}
})
df_subset1 <- reactive({
if(is.null(input$cat2)){df_subset()} else {df_subset()[df_subset()$Category2 %in% input$cat2,names(data) %in% input$show_vars]}
})
output$category2 <- renderUI({
selectizeInput('cat2', 'Choose Cat 2 (optional):', choices = sort(as.character(unique(df_subset()$Category2))), multiple = TRUE,options=NULL)
})
output$sizeslider <- renderUI({
sliderInput("size", label = "Size Range", min=min(df_subset1()$Size), max=max(df_subset1()$Size), value = c(min(df_subset1()$Size),max(df_subset1()$Size)))
})
df_subset2 <- reactive({
if(is.null(input$size)){df_subset1()} else {df_subset1()[df_subset1()$Size >= input$size[1] & df_subset1()$Size <= input$size[2],names(data) %in% input$show_vars]}
})
output$table <- renderTable({
df_subset2()
})
}
shinyApp(ui, server)

How to dynamically rename multiple columns in R Shiny based on drop down inputs?

I have a shiny app like shown below :
require(shiny)
require(dplyr)
server <- function(input, output) {
dataa <- reactive({
table1 <- mtcars
return(table1)
})
output$contents <- renderDataTable({
dataa()
})
output$mpg <- renderUI({
selectizeInput(
'MPG', 'MPG: ', choices = c("",as.list(colnames(dataa()))),
options = list(
placeholder = 'Please select',
onInitialize = I('function() { this.setValue(""); }')
)
)
})
output$cyl <- renderUI({
selectizeInput(
'CYL', 'CYL: ', choices = c("",as.list(colnames(dataa()))),
options = list(
placeholder = 'Please select',
onInitialize = I('function() { this.setValue(""); }')
)
)
})
DataRename <- reactive({
Data <- dataa()
DataNew1<-Data
MPG <- input$MPG
CYL <- input$CYL
if(!is.null(MPG)){
StatRenameEmp1 <- paste0("DataNew1 <- dplyr::rename(DataNew1,Mileage=",MPG,")")
eval(parse(text=StatRenameEmp1))
} else{
DataNew1<-Data
}
if(!is.null(CYL)){
StatRenameEmp1 <- paste0("DataNew1 <- dplyr::rename(DataNew1,Cylinders=",CYL,")")
eval(parse(text=StatRenameEmp1))
}else{
DataNew1<-Data
}
return(DataNew1)
})
output$rename <- renderDataTable({
DataRename()
})
}
ui <- shinyUI({
navbarPage("Dynamic Rename",
tabPanel("Data",
fluidPage(
titlePanel("mtcars"),
dataTableOutput('contents'))
),
tabPanel("Variables",
fluidPage(
sidebarLayout(
sidebarPanel(
uiOutput("mpg"),
uiOutput("cyl")
),
mainPanel(
dataTableOutput("rename")
)
)
)
)
)
})
shinyApp(ui = ui, server = server)
The aim is to rename the columns of the dataframe (mtcars) where I have some standard names given to the final data and the user is supposed to select the corresponding variable from the input dataset. Eg., I want to rename a certain column selected by the user (mpg in this case) as "Mileage", cyl to "Cylinder" and so on.
My problem is I am not able to rename multiple columns at a go. Only the first column is being renamed not the remaining.
Second, I may not select any column, in that case the column name need not be changed, i.e., if MPG is not selected then it should remain as mpg
How do I get all the columns renamed according to a name set by me, based on the user inputs?
The renaming works good if I select all dropdowns.
Why not simple ?
colnames(df)[which(colnames(df)=="mpg")]="New name"
Or may be i not understands your aims...
Shiny example
require(shiny)
require(dplyr)
server <- function(input, output) {
dataa <- reactive({
table1 <- mtcars
return(table1)
})
output$contents <- renderDataTable({
dataa()
})
output$mpg <- renderUI({
selectizeInput(
'MPG', 'MPG: ', choices = c("",as.list(colnames(dataa()))),
options = list(
placeholder = 'Please select',
onInitialize = I('function() { this.setValue(""); }')
)
)
})
output$cyl <- renderUI({
selectizeInput(
'CYL', 'CYL: ', choices = c("",as.list(colnames(dataa()))),
options = list(
placeholder = 'Please select',
onInitialize = I('function() { this.setValue(""); }')
)
)
})
DataRename <- reactive({
Data <- dataa()
DataNew1<-Data
MPG <- input$MPG
CYL <- input$CYL
if(!is.null(MPG)){
colnames(DataNew1)[which(colnames(DataNew1)==MPG)]="Mileage"
}
if(!is.null(CYL)){
colnames(DataNew1)[which(colnames(DataNew1)==CYL)]="Cylinders"
}
return(DataNew1)
})
output$rename <- renderDataTable({
DataRename()
})
}
ui <- shinyUI({
navbarPage("Dynamic Rename",
tabPanel("Data",
fluidPage(
titlePanel("mtcars"),
dataTableOutput('contents'))
),
tabPanel("Variables",
fluidPage(
sidebarLayout(
sidebarPanel(
uiOutput("mpg"),
uiOutput("cyl")
),
mainPanel(
dataTableOutput("rename")
)
)
)
)
)
})
shinyApp(ui = ui, server = server)
Or if you want use text input to rename colums you can do it in such way
require(shiny)
require(dplyr)
server <- function(input, output) {
dataa <- reactive({
table1 <- mtcars
return(table1)
})
output$contents <- renderDataTable({
dataa()
})
output$renamer <- renderUI({
lapply(colnames(dataa()),function(i){
textInput(paste0("col_",i),i,i)
})
})
DataRename <- reactive({
Data <- dataa()
DataNew1<-Data
for ( i in names(input) ){
if(grepl(pattern = "col_",i)){
colnames(DataNew1)[which(colnames(DataNew1)==substr(i,5,nchar(i)))]=input[[i]]
}
}
return(DataNew1)
})
output$rename <- renderDataTable({
DataRename()
})
}
ui <- shinyUI({
navbarPage("Dynamic Rename",
tabPanel("Data",
fluidPage(
titlePanel("mtcars"),
dataTableOutput('contents'))
),
tabPanel("Variables",
fluidPage(
sidebarLayout(
sidebarPanel(
uiOutput("renamer")
),
mainPanel(
dataTableOutput("rename")
)
)
)
)
)
})
shinyApp(ui = ui, server = server)

How do you reference values of sliders in shiny?

I need to create some sliders based on number items in a vector:
ui code:
library(shiny)
library(shinydashboard)
library(leaflet)
library(data.table)
library(ggplot2)
library(ggthemes)
library(usl)
ui<-dashboardPage(skin="green",
dashboardHeader(title = "ADM Logical Capacity Planning Service",titleWidth = 350),
dashboardSidebar(
sidebarMenu(
menuItem("Visualize & Create Model", tabName = "visualize",icon=icon("area-chart")),
menuItem("Forecast", tabName = "capacity", icon=icon("line-chart")) )
),
dashboardBody(
tags$head(tags$style(HTML('
.skin-blue .main-header .logo {
background-color: #3c8dbc;
}
.menuItem .main-header .logo:hover {
background-color: #3c8dbc;
}
'))),
tabItems(
tabItem("capacity",
fluidRow(
column(3,
wellPanel(
span("Given the growth rate, forecast the underlying dependent variable")
),
wellPanel(
# Create a uiOutput to hold the sliders
uiOutput("sliders")
),
# Generate a row with a sidebar
#sliderInput("capacity", "Growth Rate in Volume:", min=0, max=100, value=0,post="%"),
#br(),
#sliderInput("add_capacity", "Add Capacity in %:", min=0, max=100, value=0,post="%"),
br(),
wellPanel(
actionButton("calcbtn", "Calculate Forecast")
)
),
mainPanel(
h4("Prediction"),
verbatimTextOutput("forecast_summary"),
h4("Available Capacity"),
verbatimTextOutput("capacity_summary")
#h4("Peak Capacity"),
#verbatimTextOutput("peak_capacity")
)
)
),
tabItem("visualize",
pageWithSidebar(
headerPanel("Logical Capacity Planning Dashboard"),
sidebarPanel(
fileInput('file1', 'Upload CSV File to Create a Model',
accept=c('text/csv','text/comma-separated-values,text/plain','.csv')),
tags$hr(),
checkboxInput('header', 'Header', TRUE),
fluidRow(
column(6,checkboxGroupInput("xaxisGrp","X-Axis:", c("1"="1","2"="2"))),
column(6,radioButtons("yaxisGrp","Y-axis:", c("1"="1","2"="2")))
),
radioButtons('sep', 'Separator',
c(Comma=',', Semicolon=';',Tab='\t'), ','),
radioButtons('quote', 'Quote',
c(None='','Double Quote'='"','Single Quote'="'"),'"'),
uiOutput("choose_columns")
),
mainPanel(
tabsetPanel(
tabPanel("Data", tableOutput('contents')),
tabPanel("Create Model & Plot",plotOutput("plot"),verbatimTextOutput("PeakCapacity")),
tabPanel("Model Summary",verbatimTextOutput("summary"))
)
)
)
)
)
)
)
server code:
server <- function(input, output, session)
{
###
output$sliders <- renderUI({
xv <- input$xaxisGrp
# First, create a list of sliders each with a different name
sliders <- lapply(1:length(xv), function(i) {
inputName <- xv[i]
sliderInput(inputName, inputName, min=0, max=100, value=0, post="%")
})
# Create a tagList of sliders (this is important)
do.call(tagList, sliders)
})
###
observeEvent(input$calcbtn, {
n <- isolate(input$calcbtn)
if (n == 0) return()
output$forecast_summary <- renderPrint({
n<-pred.model()
n<-data.frame(n)
row.names(n)<-NULL
print(n)
})
output$capacity_summary <- renderPrint({
n<-pred.model()
n<-data.frame(n)
row.names(n)<-NULL
#c<-round(peak.scalability(usl.model()),digits=0)
available<-round(((c-n[1,1])/c)*100,digits=0)
row.names(available)<-NULL
print(paste0(available,"%"))
})
# output$peak_capacity <- renderPrint({
# print(paste("Maximum Capacity: ", round(peak.scalability(pred.model()),digits=0)))
# })
output$plot_forecast <- renderPlot({
df <- data_set()
new_df<- pred.model()
print(sliders)
if (!is.null(df)){
xv <- input$xaxisGrp
yv <- input$yaxisGrp
print(xv)
print(yv)
if (!is.null(xv) & !is.null(yv)){
if (sum(xv %in% names(df))>0){ # supress error when changing files
df1<-data.frame(usl.model()$fitted)
colnames(df1)<-c("Model")
df<-cbind(df,df1)
Model=c("Model")
#ggplot(df, aes_string(xv,yv))+geom_point(size=3,colour="blue")+geom_line(data=df, aes_string(xv,Model),colour="orange",size=1)+
#geom_point(data=new_df,aes(new_df[,1],new_df[,2]), colour="red",size=10)+theme_bw()+theme(legend.position = "none")
#max_capacity<-round(peak.scalability(usl.model()),digits=0)
Ninety_Fifth_Perc<-quantile(df[,2], 0.95)
#peak<-round(peak.scalability(usl.model()),digits=0)
#available<-round(((max_capacity-Ninety_Fifth_Perc)/max_capacity)*100,digits=0)
new_d<-pred.model()
ggplot(df, aes_string(xv,yv))+geom_point(size=4,shape=21, fill="blue")+geom_line(data=df, aes_string(xv,Model),colour="orange",size=1)+
geom_point(data=new_df,aes(new_df[,1],new_df[,2]), colour="red",size=10)+
theme_bw()+theme(legend.position = "none")+geom_vline(xintercept=new_df[,1], colour="green",size=1.5)
}
}
}
})
})
###pred function
pred.model <- reactive({
xv <- input$xaxisGrp
yv <- input$yaxisGrp
#latest_df<-do.call(data.frame,setNames(lapply(xv,function(e) vector(typeof(e))),xv))
latest_df<-data.frame()
new_df1 = data.frame()
for(i in 1:length(xv)){
##xv[i]<-as.numeric(input$xv[i])
# capacity<-as.numeric(input$capacity)
#add_capacity<-as.numeric(input$add_capacity)
df <- data_set()
if (!is.null(df)){
if (!is.null(xv) & !is.null(yv)){
if (sum(xv[i] %in% names(df))>0){ # supress error when changing files
#usl.model <- usl(as.formula(paste(yv, '~', xv)), data = df)
#new_growth<-tail(df[,xv],1)*(1+capacity/100)
new_growth<-quantile(df[,xv[i]],0.95)*(1+input$xv[i]/100)
new_cap<-new_growth
new_df1[1,i] = setNames(data.frame(new_cap),xv[i])
row.names(new_df1)<-NULL
}
}
}
}
latest_df=new_df1
prediction<-predict(usl.model(),newdata = latest_df)
prediction<-data.frame(prediction)
prediction<-prediction[1,1]
return(prediction)
})
##end of pred function
###visualize section
dsnames <- c()
data_set <- reactive({
inFile <- input$file1
data(specsdm91)
if (is.null(inFile))
return(specsdm91)
data_set<-read.csv(inFile$datapath, header=input$header,
sep=input$sep, quote=input$quote,stringsAsFactors=F)
})
output$contents <- renderTable({data_set()})
observe({
dsnames <- names(data_set())
cb_options <- list()
cb_options[ dsnames] <- dsnames
updateCheckboxGroupInput(session, "xaxisGrp",
label = "X-Axis",
choices = cb_options,
selected = "")
updateRadioButtons(session, "yaxisGrp",
label = "Y-Axis",
choices = cb_options,
selected = "")
})
output$choose_dataset <- renderUI({
selectInput("dataset", "Data set", as.list(data_sets))
})
usl.model <- reactive({
df <- data_set()
if (!is.null(df)){
xv <- input$xaxisGrp
yv <- input$yaxisGrp
print(xv)
print(yv)
if (!is.null(xv) & !is.null(yv)){
if (sum(xv %in% names(df))>0){ # supress error when changing files
xv <- paste(xv, collapse="+")
lim <- lm(as.formula(paste(yv, '~', xv)), data = df)
return(lim)
}
}
}
})
##plot
output$plot = renderPlot({
df <- data_set()
if (!is.null(df)){
xv <- input$xaxisGrp
yv <- input$yaxisGrp
print(xv)
print(yv)
if (!is.null(xv) & !is.null(yv)){
if (sum(xv %in% names(df))>0){ # supress error when changing files
#plot(as.formula(paste(yv, '~', xv)), data = df, pch = 21)
#plot(usl.model(),add=TRUE)
df1<-data.frame(usl.model()$fitted)
colnames(df1)<-c("Best_Fit_Model")
#df<-cbind(df,df1)
Model<-c("Best_Fit_Model")
df1<-cbind(df[yv],df1)
#max_capacity<-round(peak.scalability(usl.model()),digits=0)
#Ninety_Fifth_Perc<-quantile(df[,2], 0.95)
#peak<-round(peak.scalability(usl.model()),digits=0)
#available<-round(((max_capacity-Ninety_Fifth_Perc)/max_capacity)*100,digits=0)
#new_d<-pred.model()
df.melt=melt(df, id=yv)
xx<-c("value")
ggplot(df.melt,aes_string(x = xx, y = yv)) + geom_point() +facet_wrap(~variable, scale="free")+theme_bw()+
geom_smooth(method="lm", se=F, colour="red")
# p2<-ggplot(df1,aes_string(x = yv, y = Model)) + geom_point() + theme_bw()+
# geom_smooth(method="lm", se=F, colour="red")
}
}
}
} )
##
output$summary <- renderPrint({
summary(usl.model())
})
output$choose_columns <- renderUI({
if(is.null(input$dataset))
return()
colnames <- names(contents)
checkboxGroupInput("columns", "Choose columns",
choices = colnames,
selected = colnames)
})
}
EDIT: You're also referencing xaxisGrp as an input (which it isn't). That's causing some issues. Turns out fixing that (see the example below) makes things work nicely. I didn't realize that! Cool stuff.
Updating based on your comment, you should be able to access each input using bracket notation. Your question is still referencing input$xaxisGrp which doesn't exist, though. I'm also not sure why you're calling renderPlot({}) since nothing's being plotted.
library(shiny)
ui <- shinyUI(
fluidPage(
sidebarLayout(
sidebarPanel(
uiOutput("sliders")
),
mainPanel(
)
))
server <- shinyServer(function(input, output, session) {
xaxisGrp <- c("CPU", "Memory", "Disk")
output$sliders <- renderUI({
xv <- xaxisGrp
sliders <- lapply(1:length(xv), function(i) {
inputName <- xv[i]
sliderInput(inputName, inputName, min=0, max=100, value=0, post="%")
})
do.call(tagList, sliders)
})
output$plot_forecast <- renderPlot({
xv <- xaxisGrp
for(i in 1:length(xv)) {
value <- input[xv[i]]
}
})
})
I'm a little unsure why you're constructing the sliders this way. Have you looked into namespacing? Or even just write 3 separate outputs? For example (you can run this to see each input <key, value> pair):
library(shiny)
ui <- shinyUI(
fluidPage(
sidebarLayout(
sidebarPanel(
uiOutput("slider1"),
uiOutput("slider2"),
uiOutput("slider3"),
uiOutput("sliders")
),
mainPanel(
verbatimTextOutput("inputVals")
)
)
))
server <- shinyServer(function(input, output, session) {
output$slider1 <- renderUI({
sliderInput("CPU2", "CPU2", min=0, max=100, value=0, post="%")
})
output$slider2 <- renderUI({
sliderInput("Memory2", "Memory2", min=0, max=100, value=0, post="%")
})
output$slider3 <- renderUI({
sliderInput("Disk2", "Disk2", min=0, max=100, value=0, post="%")
})
output$sliders <- renderUI({
xv <- c("CPU","Memory","Disk")
sliders <- lapply(1:length(xv), function(i) {
inputName <- xv[i]
sliderInput(inputName, inputName, min=0, max=100, value=0, post="%")
})
do.call(tagList, sliders)
})
output$inputVals <- renderPrint({
print(reactiveValuesToList(input))
})
})
# Run the application
shinyApp(ui = ui, server = server)
In your case, it looks like your inputs are all rendering without any ID (xaxisGrp isn't a valid input in your example). That's bad, they each need a unique one. Namespacing is one way to solve for this by abstracting the UI-generating functions and guarantee unique IDs for each input. Less cumbersome most times (unless, I don't know, you need to dynamically generate them based on some external factor) is to just create multiple individual inputs.
Once you're building the inputs correctly, then to access any given input's value, just use the input$inputId syntax within any reactive context:
output$CPUValue <- renderText({
input$CPU
})

Dynamically add UI elements and gather their input in a dataframe in shiny

My ui.R function is as shown below.
library(shiny)
shinyUI(pageWithSidebar(
headerPanel("Add Features"),
sidebarPanel(width=4,
fluidRow(
column(6, selectInput("features", label = h3("Features"),
choices = list("Feature1","Feature2","Feature3"), selected = "Feature1")),
br(),
br(),
column(6, numericInput("n", label="",min = 0, max = 100, value = 50)),
br(),
column(2, actionButton("goButton", "Add!"))
#column(3, submitButton(text="Analyze"))
)),
mainPanel(
verbatimTextOutput("nText"),
textOutput("text2")
)
))
My server.R function is as below:
library(shiny)
shinyServer(function(input, output) {
selFeatures <- data.frame()
valFeatures <- data.frame()
# builds a reactive expression that only invalidates
# when the value of input$goButton becomes out of date
# (i.e., when the button is pressed)
ntext <- eventReactive(input$goButton, {
selFeatures <- rbind(selFeatures,input$features)
valFeatures <- rbind(valFeatures,input$n)
paste("The variables are",input$features,input$n)
paste("The variables are",selFeatures,valFeatures)
})
output$nText <- renderText({
ntext()
})
output$text2 <- renderText({
paste("You have selected", input$features)
})
})
What I want to do is ask user to input some variables. Here Feature1, Feature2, and Feature3. User has to input Feature1 but Feature2 and Feature3 are optional. So, here user selects a feature, inputs its value in numericInput and presses button Add. When Add is pressed after selecting Feature1, user can select to submit the form or add features 2 and 3 using the add button. I finally, want to use these three variables to learn a prediction model. How can I collect all the imputed information in the dataframe to process it. Also, if possible to remove Feature1 from the selectBox after it has been added. I want my UI to look like the following before Pressing the add button
and it should look like the following after pressing the add button.
The feature1 here need not be in the select box, just a way to display that it has been added is fine.
I wasn't quite sure why you wanted to use selectInputs for setting the variable values so here's a general example on how to access inputs from dynamically generated content:
library(shiny)
ui <- shinyUI(pageWithSidebar(
headerPanel("Add Features"),
sidebarPanel(width=4,
fluidRow(column(12,
h3('Features'),
uiOutput('uiOutpt')
)), # END fluidRow
fluidRow(
column(4,div()),
column(4,actionButton("add", "Add!")),
column(4,actionButton('goButton',"Analyze"))
) # END fluidRow
), # END sidebarPanel
mainPanel(
verbatimTextOutput("nText"),
textOutput("text2"),
tableOutput('tbl')
)
))
server <- shinyServer(function(input, output) {
features <- reactiveValues(renderd=c(1))
ntext <- eventReactive(input$goButton, {
out <- lapply(features$renderd,function(i){
fv <- paste0('numInp_',i)
vn <- paste0('Feature',i)
# Get input values by namw
sprintf( 'Variable: %s, Value: %5.3f',input[[vn]],input[[fv]] )
})
do.call(paste,c(out,sep="\n"))
})
df <- eventReactive(input$goButton, {
out <- lapply(features$renderd,function(i){
fv <- paste0('numInp_',i)
vn <- paste0('Feature',i)
data.frame(Variable=input[[vn]], Value=input[[fv]] )
})
do.call(rbind,out)
})
output$nText <- renderText({
ntext()
})
output$text2 <- renderText({
sprintf("You have selected feature: %s", paste(features$renderd,collapse=", "))
})
output$tbl <- renderTable({
df()
})
# Increment reactive values used to store how may rows we have rendered
observeEvent(input$add,{
if (max(features$renderd) > 2) return(NULL)
features$renderd <- c(features$renderd, max(features$renderd)+1)
})
# If reactive vector updated we render the UI again
observe({
output$uiOutpt <- renderUI({
# Create rows
rows <- lapply(features$renderd,function(i){
fluidRow(
column(6, selectInput(paste0('Feature',i),
label = "",
choices = list("Feature1","Feature2","Feature3"),
selected = paste0('Feature',i))),
column(6, numericInput(paste0('numInp_',i), label="",min = 0, max = 100, value = runif(1,max=100)))
)
})
do.call(shiny::tagList,rows)
})
})
})
shinyApp(ui=ui,server=server)
I'm simply storing the ID's of the dynamically generated content in a vector that helps me keep track of what I've generated. To access the values i simply reconstruct the elements ID from the numbers stored in the vector.
Oskar's answer was very useful to me for a similar challenge I faced; for unlimited features, I figured out how to enable a "remove" button and to keep values when pressing the "add" button. For posterity, here are my modifications to Oskar's code:
library(shiny)
ui <- shinyUI(pageWithSidebar(
headerPanel("Add Features"),
sidebarPanel(width=4,
fluidRow(column(12,
h3('Features'),
uiOutput('uiOutpt')
)), # END fluidRow
fluidRow(
column(4,div()),
column(4,actionButton("add", "Add!")),
column(4,actionButton("remove", "Remove!")),
column(4,actionButton('goButton',"Analyze"))
) # END fluidRow
), # END sidebarPanel
mainPanel(
textOutput("text2"),
tableOutput('tbl')
)
))
server <- shinyServer(function(input, output) {
features <- reactiveValues(renderd=c(1),
conv=c(50),
inlabels=c('A'),
outlabels=c('B'))
df <- eventReactive(input$goButton, {
out <- lapply(features$renderd,function(i){
fv <- paste0('numInp_',i)
vn <- paste0('InLabel',i)
data.frame(Variable=input[[vn]], Value=input[[fv]] )
})
do.call(rbind,out)
})
output$nText <- renderText({
ntext()
})
output$text2 <- renderText({
paste(sprintf("You have selected feature: %s", paste(features$renderd,collapse=", ")))
})
output$tbl <- renderTable({
df()
})
# Increment reactive values array used to store how may rows we have rendered
observeEvent(input$add,{
out <- lapply(features$renderd,function(i){
fv <- paste0('numInp_',i)
vn <- paste0('InLabel',i)
vo <- paste0('OutLabel',i)
data.frame(inlabels=input[[vn]],outlabels=input[[vo]], conv=input[[fv]] )
})
df<-do.call(rbind,out)
print(df)
features$inlabels <- c(as.character(df$inlabels),' ')
features$outlabels <- c(as.character(df$outlabels),' ')
print(c(features$inlabels,features$outlabels))
features$renderd <- c(features$renderd, length(features$renderd)+1)
print(features$renderd)
print(names(features))
features$conv<-c(df$conv,51-length(features$renderd))
})
observeEvent(input$remove,{
features$renderd <- features$renderd[-length(features$renderd)]
})
# If reactive vector updated we render the UI again
observe({
output$uiOutpt <- renderUI({
# Create rows
rows <- lapply(features$renderd,function(i){
fluidRow(
# duplicate choices make selectize poop the bed, use unique():
column(4, selectizeInput(paste0('InLabel',i),
label = 'Input Name',selected=features$inlabels[i],
choices=unique(c(features$inlabels[i],features$outlabels[!features$outlabels %in% features$inlabels])),
options = list(create = TRUE))),
column(4, sliderInput(paste0('numInp_',i), label="Conversion",min = 0, max = 100, value = features$conv[i])),
column(4, selectizeInput(paste0('OutLabel',i),
label = "Output Name", selected=features$outlabels[i],
choices=unique(c(features$inlabels,features$outlabels)),
options = list(create = TRUE)))
)
})
do.call(shiny::tagList,rows)
})
})
})
shinyApp(ui=ui,server=server)

Resources