Create a variable number of `sliderInput` in Shiny - r

My data is a matrix with a variable number of columns. Also , the range of values within the matrix is variable, too.
I want to build a variable number of sliderInput, each corresponding to one column in the matrix.
The higher limit of each slider should correspond to the maxRange within the matrix.
Any suggestion how to do it in one shot?
lapply(1:ncol, function(i) {
sliderInput(
paste0('a', i),
paste0('SelectA', i),
min = min(c(1:maxRange)),
max = max(c(1:maxRange)),
value = c(1, maxRange),
step =1
)
}
)

I had broadly similar problem when I wanted to create a set of input elements corresponding to values derived from the data set (min, max list of options, etc.). Broadly speaking, I would source all the required data via global.R and then reference the concepts in the elements, so on the lines of getting min/max in a slider:
global.R
# Get dates for the slider
## Delete pointless month
dta.nom$DATE_NAME <- sub("February ", replacement = "", x = dta.nom$DATE_NAME)
## Convert to number and get min/max
dta.nom$DATE_NAME <- as.numeric(x = dta.nom$DATE_NAME)
yr.min <- min(dta.nom$DATE_NAME)
yr.max <- max(dta.nom$DATE_NAME)
Then in the slider
ui.R
# Select the dates for the data
sliderInput("sliderYears", label = h5("Years"), min = yr.min,
max = yr.max, value = c(2000, 2010), sep = "",
step = 1, animate = FALSE),
Full code is on GitHub. I'm not sure if I understood you correctly but if you are interested in dynamically connecting elements of your interface in Shiny then you can make use of the updateSelectInput. Any other problems with respect to referencing the data should be solvable with use of global code and referencing the values in your interface elements.

In case someone might bump into the same problem, here is my solution:
ui.R
....
sidebarPanel(
selectInput(
inputId = "dataName",
label = "Select your data",
choices = c("data1", "data2", "data3", "data4")
),
uiOutput(outputId = "sliders")
),
.....
server.R
.....
output$sliders <- renderUI({
numSliders <- numCols(input$dataName)
lapply(1:numSliders, function(i) {
sliderInput(
inputId = paste0('column', i),
label = paste0('Select the range for column ', i),
min = min(selectRange(input$dataName)),
max = max(selectRange(input$dataName)),
value = c(min(selectRange(input$dataName)), max(selectRange(input$dataName))),
step =1)
})
})
........
selectRange is a another function in global.R:
global.R
selectRange <- function(x){
if(x == "data1"){choices = c(1:100)}
if(x == "data2"){choices = c(1:50)}
if(x == "data3"){choices = c(1:75)}
if(x == "data4"){choices = c(1:150)}
return(choices)
}

Related

Is there a way that you can update noUiSliderInput for two values, when starting them as null?

Intro:
I have a list of 40 variables that each has 2000 entries, I am working in R Shiny in order to try automatically divide the data into 3 bins based the value at 1/3 of the data and 2/3 of the data.
Each variable has a range between -10 and 10.
UI code below:
selectInput("variable","Select your variable",choices = sort(colnames(train))),noUiSliderInput(inputId = "bins", label = "Choose bin width", min = -10, max = 10, value = c(NULL,NULL), orientation = "horizontal", margin = 1, width = 10000, color = "#68228B")
Server code:
ordered_data <- reactive({
sort_test <- c(joined_adw[,input$variable])
sort_test <- as.numeric(unlist(sort_test))
sort_test <- sort(sort_test)
})
eventReactive(
input$variable,{
updateNoUiSliderInput(session = session, inputId = "bins", value = c(ordered_data()[667], test()[1334]))
})
}
I know that there are other ways of doing this, but I need the slider in the report sheet.
Any help would be massively appreciated, as I have been trying to do this for a while, and each time I try it, the slider disappears when I use an update call.

How do I get a summary table from a custom function to react to user input of variables?

This is my first attempt at using Shiny.
I have a simulated patient-level dataset with 4 variables:
group: Categorical, takes on values A, B and C. Represents 3 different treatment types that were used in the study.
week: Numeric variable, takes on values 1, 4, 8.Represents follow-up week.
painscore: Numeric variable, score on scale of 1-10, with 1 indicating no pain, 10 indicating extreme pain.
dependscore: Numeric variable, score on scale of 1-10, with 1 indicating no dependency on pain meds, 10 indicating extreme dependency.
Trying to build a simple app that accepts two inputs: the week, and the variable, and provides two outputs:
A boxplot of distribution of scores for the selected variable for the selected week. The x axis would represent the 3 levels of group (A, B and C).
A summary table the shows the number of observations, median, 25th percentile, 75th percentile and number of missing.
I was able to create the interactive boxplot, but I am unable to create the summary table. I was able to create static versions of this table in RMarkdown using the summaryBy function from doBy, but I am not able to implement it in Shiny. Tried following the advice here and here but I'm missing something.
Here's my code for reproducibility. Excuse the extensive annotations, (I'm a complete beginner) they are more for myself than for anyone else.
#libraries--------------------
library(shiny)
library(tidyverse)
library(knitr)
library(doBy)
#----------------------------
#input data
set.seed(123)
mydf <- data.frame( group = rep(rep(c("A","B","C"), each = 3), times = 3),
week = rep(rep(c(1,4,8), each = 9)),
painscore = sample(1:10, 27, replace = TRUE),
dependscore = sample(1:10, 27, replace = TRUE) )
#--------------------------
#define custom function to calculate summary statistics for column of interest.
#function explained in a little more detail when applied in the server function.
fun <- function(x) {
c( n = length(x),
m = median(x),
firstq = round(quantile(x)[2], 1),
lastq = round(quantile(x)[4], 1),
missing = mean(is.na(x)))
}
#-------------------------
#UI
ui <- fluidPage(
titlePanel("Shiny Boxplot and Table"),
#User can provide two different inputs
sidebarLayout(
sidebarPanel(
#1. allow user to pick week using radiobuttons
radioButtons(inputId = "pickedwk",
label = "week you want to display",
choices = c(1,4,8),
selected = 1),
#2. user can pick variable to visualize using dropdownboxes
selectInput(inputId = "var",
label = "variable to visualize",
list("How much pain did you feel today?" = "painscore",
"How dependent are you on medication?" = "dependscore")),
#helpertext
helpText("Enter week/variable choices here")
),
#Spaceholders for output
mainPanel(
plotOutput("boxplot"), #boxplot placeholder
htmlOutput("descriptives") #kable html table placeholder
)
)
)
#-------------------------
#Server
server <- function(input, output) {
#create dataset that subsets down to the week picked by user.
weeksub <- reactive({
mydf %>% filter(week == input$pickedwk[1])
})
#1. use reactive datasubset to render boxplot.
output$boxplot <- renderPlot({
ggplot(weeksub(), aes_string(x = "group", y = input$var)) + #input$var works here
geom_boxplot(fill = "red", outlier.shape = 15, outlier.color = "black") +
scale_y_continuous(name = as.character(input$var)) +
scale_x_discrete(name = "group") +
ggtitle(paste("Distribution of", as.character(input$var), "by treatment group"))
})
#2. use same reactive datasubset to render kable descriptive statistics for the variable picked.
output$descriptives <- renderText({
kable(summaryBy(input$var ~ group, data = as.data.frame(weeksub()), FUN = fun),
#note: here, I'm using the summaryBy function from package doBy. It takes the form var~ categoricalvar
# so, either painscore ~ group, or dependscore ~ group depending on what the user picked, and uses
#my custom function to return a table of count, median, 25th percentile, 75th percentile and missing count for
#the 3 levels of the group variable (for A, B, and C)
col.names = c("Number", "Median", "1Q", "3Q", "Missing"))
})
}#server function ends
# Run the application
shinyApp(ui = ui, server = server)
There are a couple of problems in your code:
The formula notation doesn't know how to deal with input$var. summaryBy supports an alternate syntax that works better. (You could also use as.formula and paste to build a formula.)
You are missing the "Group" column in col.names
You have to generate HTML from kable and pass it as HTML to the UI.
Change your table output to this:
output$descriptives <- renderUI({
HTML(kable(summaryBy(list(input$var, 'group'), data = as.data.frame(weeksub()), FUN = fun),
col.names = c('Group', "Number", "Median", "1Q", "3Q", "Missing"),
format='html'
))
})

R Shiny: Update with multiple sources of input

I am creating a shiny app where I would like to offer users two ways to give two input values: Either directly via two input sliders, or via two data tables on which the app performs some computation with two values as output.
The output values depend on each other, and should sum to one.
I get both these methods working separately: Slider 1 is updated when the value of slider 2 is changed and vice versa, and I managed to compute the output values using the data (See MWE).
Now I would like to combine them such that each slider is updated when a.) the other slider is updated manually; or b.) the data table is changed.
I would like to use these updated slider values for computations in another function.
Any suggestions to change the value of updateSliderInput() when two sources of input are available?
MWE:
library(shiny)
library(rhandsontable)
#### Initialization ####
# Initial data
datA <- datB <- data.frame(success=c(1,1,2), failure=c(1,1,2), total=c(2,2,4))
colnames(datA) <- colnames(datB) <- c(expression(x[T*","*2]==1), expression(x[T*","*2]==0)," " )
rownames(datA) <- rownames(datB) <- c(expression(x[T*","*1]==1), expression(x[T*","*1]==0)," " )
# Dummy function to perform some computation on input data
ComputeWeights <- function(datA,datB){
c(sum(datA)/sum(datA,datB),sum(datB)/sum(datA,datB))
}
#### Server ####
server <- shinyServer(function(input, output, session) {
# Allow users to provide slider input via data table
prevA <- reactive({datA})
prevB <- reactive({datB})
changeA <- reactive({
if(is.null(input$hotA)){return(prevA())}
else{
datA <- as.data.frame(hot_to_r(input$hotA))
datA[c(1,2),3] <- rowSums(datA[c(1,2),c(1,2)])
datA[3,c(1,2)] <- colSums(datA[c(1,2),c(1,2)])
datA[3,3] <- sum(datA[c(1,2),3])
datA
}
})
changeB <- reactive({
if(is.null(input$hotB)){return(prevB())}
else{
datB <- as.data.frame(hot_to_r(input$hotB))
datB[c(1,2),3] <- rowSums(datB[c(1,2),c(1,2)])
datB[3,c(1,2)] <- colSums(datB[c(1,2),c(1,2)])
datB[3,3] <- sum(datB[c(1,2),3])
datB
}
})
output$hotA <- renderRHandsontable({rhandsontable(changeA(), width=375,
rowHeaders=c(expression(x[T*","*2]==1), expression(x[T*","*2]==0)," " ),
colHeaders=c(expression(x[T*","*1]==1), expression(x[T*","*1]==0)," " )) %>%
hot_table(rowHeaderWidth=50) %>%
hot_cols(colWidths = c(50,50,50), format="0", allowInvalid=FALSE)%>%
hot_col(3, readOnly=TRUE)%>%
hot_row(3, readOnly=TRUE)})
output$hotB <- renderRHandsontable({rhandsontable(changeB(), width=375,
rowHeaders=c(expression(x[T*","*2]==1), expression(x[T*","*2]==0)," " ),
colHeaders=c(expression(x[T*","*1]==1), expression(x[T*","*1]==0)," " )) %>%
hot_table(rowHeaderWidth=50) %>%
hot_cols(colWidths = c(50,50,50), format="0", allowInvalid=FALSE)%>%
hot_col(3, readOnly=TRUE)%>%
hot_row(3, readOnly=TRUE)})
# Slider updated with input from slider
observe({
updateSliderInput(session, "w2", value = 1-input$w1,
min = 0, max = 1, step=0.01)
})
observe({
updateSliderInput(session, "w1", value = 1-input$w2,
min = 0, max = 1, step=0.01)
})
output$weights <- renderPrint({
ComputeWeights(changeA(),changeB())
})
})
#### UI ####
ui <- shinyUI(fluidPage(
# First input source: sliders
sidebarLayout(sliderInput("w1", "W1",
min = 0, max = 1, value = 0.5, step=0.01),
sliderInput("w2", "W2",
min = 0, max = 1, value = 0.5, step=0.01)),
mainPanel(
# Second input source: data
rHandsontableOutput("hotA", width="100%"),
rHandsontableOutput("hotB", width="100%"),
textOutput("weights")))
)
shinyApp(ui=ui, server=server)
Would it be possible to just add an observe for changeA and changeB which are reactive?
observe({
wts <- ComputeWeights(changeA(),changeB())
updateSliderInput(session, "w1", value = wts[1], min = 0, max = 1, step=0.01)
updateSliderInput(session, "w2", value = wts[2], min = 0, max = 1, step=0.01)
})
Then you could set the slider input to each value of your returned vector from ComputeWeights.
Let me know if this is what you had in mind.

problem with noUiSliderInput output having only 2 decimals

I'm trying to link a log scaled noUiSliderInput with a linear scaled numericInput without creating an eternal update loop.
Normally I would stop the update lines with this:
input$Histoslider != log10(input$Threshold_box)
This created some issues with decimals, which I can't seem to fix properly.
The main problem seems to be that noUiSliderInput always rounds its output to 2 decimals, causing the rounding problem for the conversion to 10^ and log10 back and forth
Detailed description:
I have an app where the user can set threshold filters in 2 ways:
1: type the number in a numericInput in un-transformed numbers
or
2: by changing the bar on a vertical noUiSliderInput.
The noUiSliderInput however is expressed as log10 numbers, as it is lined up with a plot of the data on log10 scale. so if the plot runs from 10^-1 to 10^4.5, the slider has values running from -1 to 4.5
The numericInput and noUiSliderInputare linked, so change one should update the other.
This created a lot of difficulty with decimals. The numericInput has to be restricted in the app to 2 decimals. So to do that I add some transformation, rounding and transformation back again to get matching numbers.
I can get it to work for a normal sliderInput, but somehow not for a noUiSliderInput, eventhough they should spit out the same format of data.
The reason I have to stick with a noUiSliderInput is because I need a slider on both the x and y axis of the plot.
Try to type 1256 in the numericInput to see an example of the problem
The app:
# install.packages("devtools")
devtools::install_github("dreamRs/shinyWidgets")
library(shiny)
library(shinyWidgets)
# function to see how many decimal places we need
decimalplaces <- function(x) {
if ((x %% 1) != 0) {
deci <- nchar(strsplit(sub('0+$', '', as.character(x)), ".", fixed=TRUE)[[1]][[2]])
if(deci >2) { deci <- 2}
return(deci)
} else {
return(0)
}
}
# starting values for the sliders
minval <- round(-1, digits = 6)
maxval <- round(4.5, digits = 6)
ui <- fluidPage(
tags$br(),
fluidRow(
column(3,
div(numericInput("Threshold_box", "Normal value: ", min = 0, max = 100, value = 1, step=0.01), style = "display:inline-block") ),
column(2,
# div(sliderInput( inputId = "Histoslider", label = NULL, min = minval, max = maxval, value = 0, step = 0.000001), style = 'display:inline-block; position:relative')
noUiSliderInput(inputId = "Histoslider", label = NULL, min = minval, max = maxval, tooltips = FALSE, value = 0, step = 0.000001, direction = "rtl", orientation = "vertical", width = "100px", height = "276px")
)))
server <- function(input, output, session) {
#setting decimals to 6 as that seemed to work in the end for a normal sliderInput
values <- reactiveValues( transformDecimal = 2)
observeEvent(input$Threshold_box, {
if(!is.na(input$Threshold_box)) { values$transformDecimal <- decimalplaces(input$Threshold_box)
if(input$Histoslider != log10(input$Threshold_box)) {
newval <- log10(input$Threshold_box)
# updateSliderInput(session, 'Histoslider', value = newval)
updateNoUiSliderInput(session, 'Histoslider', value = newval)
}}}, ignoreInit = T)
observeEvent(input$Histoslider, {
## next three lines are to get matching set between a 2 decimal numer and the log value
sliderFull <- 10^input$Histoslider
sliderRound <- round(sliderFull, digits = values$transformDecimal)
sliderLog <- log10(sliderRound)
updateSliderInput(session, 'Histoslider', value = sliderLog)
if(sliderLog != log10(input$Threshold_box)) {
updateNumericInput(session, 'Threshold_box', value = round(10^sliderLog, digits = values$transformDecimal))
}
}, ignoreInit = T)
}
shinyApp(ui, server)

Trying to create a future value calculator with shiny app in R and get error: argument 1 (type 'closure') cannot be handled by 'cat'

This is the code I have on my server file:
shinyServer(function(input, output) {
output$P = renderText(input$Slider1)
output$n = renderText(input$numeric1)
output$r = renderText(input$numeric2/100)
futureValue <- reactive({
principal <- output$P
numberOfPeriods <- output$n
rate <- output$r
fvalue <- principal*(((1+rate)^numberOfPeriods-1)/rate)
return(fvalue)
})
output$fv <- renderText(futureValue)
})
Code for main panel on UI File:
sidebarPanel(
h4("Select Monthly Investment Amount:"),
sliderInput("Slider1","Select Monthly Investment Amount:", 100, 1000,
100),
numericInput("numeric1", "Select Number of Payments:", value = 12, min
= 6, max = 60, step = 1),
numericInput("numeric2", "Select Interest Rate Percentage:", value =
3.0, min = 0.1, max = 5.0, step = 0.1)
mainPanel
(
h4("Monthly Investment Amount:"),
textOutput("P"),
h4("Number of Periods:"),
textOutput("n"),
h4("Interest Rate:"),
textOutput("r"),
h4("Under the given circumstances, the future value of your
investment is:"),
textOutput("fv")
)
Everything works except for the last part where I'm performing the calculations for future value. Would anyone be able to tell me what I'm doing wrong?
Hi reactivs are functions not variables
output$fv <- renderText(futureValue() )
and change the reactive function like this
futureValue <- reactive({
principal <- input$Slider1
numberOfPeriods <- input$numeric1
rate <- input$numric2
fvalue <-principal*(((1+rate)^numberOfPeriods-1)/rate)
return(fvalue) })
should solve it.
Hope this helps!

Resources