I am somewhat familiar with the paste() function in base R. I am trying to learn Shiny and reactivity and don't understand how to execute a paste() inside a reactive Shiny function.
In running the demo code at the bottom, this is what first appears in the rendered "Reactive results" panel:
choice subclass
1 A 1
2 A 1
3 A 2
4 B 1
5 B 3
I'd like to paste the row number of each row in front of each "choice" column element, followed by a "." and a space, so that the "Reactive results" panel output looks like this:
choice subclass
1 1. A 1
2 2. A 1
3 3. A 2
4 4. B 1
5 5. B 3
How would I execute a paste() in this reactive example?
Demo code:
library(shiny)
data <- data.frame(choice = c("A","A","A","B","B"),subclass = c(1,1,2,1,3))
ui <- fluidPage(
h5(strong("Base data frame:")),
verbatimTextOutput("data"),
radioButtons(inputId = "showData",
label = h5(strong("Multiply base DF subclass by factor of:")),
choiceNames = c('One','Two'),
choiceValues = c('One','Two'),
selected = 'One',
inline = TRUE
),
h5(strong("Reactive results:")),
verbatimTextOutput("choices1")
)
server <- function(input, output, session) {
output$data <- renderPrint(data)
rv <- reactiveValues(choices1=c())
observeEvent(input$showData, {
if(input$showData == 'One'){rv$choices1 <- data[]}
else {rv$choices1[,2] <- 2 * data[ ,2]}
}
)
output[["choices1"]] <- renderPrint({rv$choices1})
}
shinyApp(ui, server)
Your app looks a bit complicated to me. But as I don't know about your desired final result I stick with your code and only did some slight changes in your observeEvent to achieve your desired result:
library(shiny)
data <- data.frame(choice = c("A", "A", "A", "B", "B"), subclass = c(1, 1, 2, 1, 3))
ui <- fluidPage(
h5(strong("Base data frame:")),
verbatimTextOutput("data"),
radioButtons(
inputId = "showData",
label = h5(strong("Multiply base DF subclass by factor of:")),
choiceNames = c("One", "Two"),
choiceValues = c("One", "Two"),
selected = "One",
inline = TRUE
),
h5(strong("Reactive results:")),
verbatimTextOutput("choices1")
)
server <- function(input, output, session) {
output$data <- renderPrint(data)
rv <- reactiveValues(choices1 = c())
observeEvent(input$showData, {
rv$choices1 <- data
rv$choices1$choice <- paste0(row.names(rv$choices1), ". ", rv$choices1$choice)
if (input$showData == "Two") {
rv$choices1[, 2] <- 2 * rv$choices1[, 2]
}
})
output[["choices1"]] <- renderPrint({
rv$choices1
})
}
shinyApp(ui, server)
#>
#> Listening on http://127.0.0.1:4262
Suggested modifications, in two parts.
First,
use str_glue instead of paste, and mutate to modify the dataframe,
replace choiceNames and choiceValues with a single argument choices, as they are equal,
rewrite output[["choices1"]] as output$choices1,
use dataframe column by name rather than by index,
replace reactiveValues and observeEvent with a single reactive: reactivity is automatic, you don't have to reimplement it with an oberveEvent.
Here is the code:
library(shiny)
library(dplyr)
library(magrittr)
library(stringr)
data <- data.frame(choice = c("A", "A", "A", "B", "B"),
subclass = c(1, 1, 2, 1, 3))
ui <- fluidPage(
h5(strong("Base data frame:")),
verbatimTextOutput("data"),
radioButtons(inputId = "showData",
label = h5(strong("Multiply base DF subclass by factor of:")),
choices = c("One", "Two"),
selected = "One",
inline = TRUE
),
h5(strong("Reactive results:")),
verbatimTextOutput("choices1")
)
server <- function(input, output, session) {
output$data <- renderPrint(data)
rv <- reactive({
df <- data %>% mutate(choice = str_glue("{row_number()}. {choice}"))
if (input$showData == "Two") {
df %<>% mutate(subclass = 2 * subclass)
}
df
})
output$choices1 <- renderPrint(rv())
}
shinyApp(ui, server)
Second simplification: the reactive is useless as it's only used once, in renderPrint. So put everything in renderPrint. It's still reactive.
library(shiny)
library(dplyr)
library(magrittr)
library(stringr)
data <- data.frame(choice = c("A", "A", "A", "B", "B"),
subclass = c(1, 1, 2, 1, 3))
ui <- fluidPage(
h5(strong("Base data frame:")),
verbatimTextOutput("data"),
radioButtons(inputId = "showData",
label = h5(strong("Multiply base DF subclass by factor of:")),
choices = c("One", "Two"),
selected = "One",
inline = TRUE
),
h5(strong("Reactive results:")),
verbatimTextOutput("choices1")
)
server <- function(input, output, session) {
output$data <- renderPrint(data)
output$choices1 <- renderPrint({
df <- data %>% mutate(choice = str_glue("{row_number()}. {choice}"))
if (input$showData == "Two") {
df %<>% mutate(subclass = 2 * subclass)
}
df
})
}
shinyApp(ui, server)
Related
If I have a data.frame/data.table with multiple columns needed to be filtered and, later passed to other calculations, how can I filter the data without creating multiple combinations of filtering conditions using if else.
For example, if I have a data with Age, Gender, Ethnicity, and created three selectInput().
What I would like to achieve is that,
If I select Age: 10-19 from the drop down list, then this should be passed to the data and do DT[Age %in% "10-19"]
Similary, if I select Age: 10-19 and Gender: Female, then these should be passed to the data as DT[Age %in% "10-19" & Gender %in% "Female"]
If I deselect Age, then the data will return Gender: Female, such as DT[Gender %in% "Female"]
How can I capture those conditions, and pass to the data filter automatically without explicitly going through those combinations?
Here is a non-working testing example
df <- data.table(AgeGroup = sample(c("0-9", "10-19", "20-29"), 20, replace = TRUE),
Sex = sample(c("Male", "Female"), 20, replace = TRUE))
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("AgeGroup", "Age Group", choices = c("", unique(df$AgeGroup))),
selectInput("Sex", "Sex", choices = c("", unique(df$Sex)))
),
mainPanel(
tableOutput("table")
)
)
)
server <- function(input, output, session) {
# How to modify here so that we don't need to do
# `if (input$AgeGroup) df[AgeGroup == input$AgeGroup]`
# consider multiple filters, some filters are selected and some are not.
# For example, if there are 5 filters, there would be 2^5 combinations
df_out <- reactive(df)
output$table <- renderTable(df_out())
}
shinyApp(ui, server)
We can use | and & to build a filter statement. The trick is to say input$a is either "" (which means return all rows) or a is input$a. You can use %in% instead of == when using multiple input values.
library(shiny)
library(data.table)
df <- data.table(a = c("a", "b", "c"),
b = 1:3)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("a", "Select A", choices = c("", c("a", "b", "c"))),
selectInput("b", "Select B", choices = c("", c(1, 2, 3)))
),
mainPanel(
tableOutput("table")
)
)
)
server <- function(input, output, session) {
df_out <- reactive(df[(input$a == "" | a == input$a) &
(input$b == "" | b == input$b),])
output$table <- renderTable(df_out())
}
shinyApp(ui, server)
A more programmatic solution is to use vapply() and wrap the result in rowMeans():
library(shiny)
library(data.table)
df <- data.table(a = c("a", "b", "c"),
b = 1:3)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("a", "Select A", choices = c("", c("a", "b", "c"))),
selectInput("b", "Select B", choices = c("", c(1, 2, 3)))
),
mainPanel(
tableOutput("table")
)
)
)
server <- function(input, output, session) {
df_out <- reactive({
idx_vec <- vapply(c("a", "b"),
FUN.VALUE = logical(nrow(df)),
FUN = function(x) {
input[[x]] == "" | df[[x]] == input[[x]]
})
df[rowMeans(idx_vec) >= 1,]
})
output$table <- renderTable(df_out())
}
shinyApp(ui, server)
In demo code shown at the bottom of this post, I'd like to strip out the number, ".", and space prefix from the "choice" column of the rendered choices2 dataframe.
So choice2 currently outputs this (shown in the panel labeled "Strip numbers out of reactive results ('choices2' dataframe)" when running the code):
choice subclass
1 1. A 1
2 2. A 1
3 3. A 2
4 4. B 1
5 5. B 3
I'd instead like choice2 to output this, stripping out the numeric prefixes (including the "." and the leading space):
choice subclass
1 A 1
2 A 1
3 A 2
4 B 1
5 B 3
How would I do this? I know the easy way is to simply recreate the data DF in choice2, but I'm specifically trying to learn how to strip characters out from a vector or dataframe (DF) generated in a reactive context. I don't have a clear understanding of how to work with vectors/DF in a reactive setting.
Also, the code may appear cumbersome for what it does. I'd like to preserve its overall structure (use of reactveValues() in combination with observeEvent(), instead of simply using reactive(), for example) as it's a redaction of longer code and it's also a learning exercise.
Demo code:
library(shiny)
data <- data.frame(choice = c("A", "A", "A", "B", "B"), subclass = c(1, 1, 2, 1, 3))
ui <- fluidPage(
h5(strong("Base data frame ('data' dataframe):")),
verbatimTextOutput("data"),
radioButtons(
inputId = "showData",
label = h5(strong("Multiply base DF subclass by factor of:")),
choiceNames = c("One", "Two"),
choiceValues = c("One", "Two"),
selected = "One",
inline = TRUE
),
h5(strong("Reactive results ('choices1' dataframe):")),
verbatimTextOutput("choices1"),
h5(strong("Strip numbers out of reactive results ('choices2' dataframe):")),
verbatimTextOutput("choices2")
)
server <- function(input, output, session) {
output$data <- renderPrint(data)
rv <- reactiveValues(choices1 = c())
observeEvent(input$showData, {
rv$choices1 <- data
rv$choices1$choice <- paste0(row.names(rv$choices1), ". ", rv$choices1$choice)
if (input$showData == "Two") {
rv$choices1[, 2] <- 2 * rv$choices1[, 2]
}
})
output[["choices1"]] <- renderPrint({
rv$choices1
})
# would like choices2 to strip number, ".", and the space out of choices1
output[["choices2"]] <- renderPrint({
rv$choices1
})
}
shinyApp(ui, server)
To expand on my comment above
library(shiny)
data <- data.frame(choice = c("A", "A", "A", "B", "B"), subclass = c(1, 1, 2, 1, 3))
ui <- fluidPage(
h5(strong("Base data frame ('data' dataframe):")),
verbatimTextOutput("data"),
radioButtons(
inputId = "showData",
label = h5(strong("Multiply base DF subclass by factor of:")),
choiceNames = c("One", "Two"),
choiceValues = c("One", "Two"),
selected = "One",
inline = TRUE
),
h5(strong("Reactive results ('choices1' dataframe):")),
verbatimTextOutput("choices1"),
h5(strong("Strip numbers out of reactive results ('choices2' dataframe):")),
verbatimTextOutput("choices2")
)
server <- function(input, output, session) {
output$data <- renderPrint(data)
# Edited
rv <- reactiveValues(choices1 = c(), choices2=NA)
observeEvent(input$showData, {
rv$choices1 <- data
rv$choices1$choice <- paste0(row.names(rv$choices1), ". ", rv$choices1$choice)
if (input$showData == "Two") {
rv$choices1[, 2] <- 2 * rv$choices1[, 2]
}
})
# New observeEvent
observeEvent(rv$choices1, {
rv$choices2 <- rv$choices1 %>% mutate(choice=stringr::str_sub(choice, -1))
})
output[["choices1"]] <- renderPrint({
rv$choices1
})
# would like choices2 to strip number, ".", and the space out of choices1
output[["choices2"]] <- renderPrint({
# Edit here
rv$choices2
})
}
shinyApp(ui, server)
Hi I'm relatively new to Shiny and am not sure how to do this. I am making a dashboard that should first pull the relevant dataframe based on user selectInput, after which further selectInput functions will further filter down the sheet for the relevant price. However, I can't seem to link the InputId from the selectInput to the relevant dataframe name. (Below is code)
UI.R
ui <- navbarPage(
"Dashboard",
tabPanel(
"Cost1",
fluidPage(
selectInput("type",
label = "Select Type",
choices = NULL),
textOutput("message")
)
)
)
Server.R
#load libraries, data
library(tidyr)
library(readxl)
library(dplyr)
library(purrr)
a <- read_excel('source.xlsx', sheet = 'a')
b <- read_excel('source.xlsx', sheet = 'b')
c <- read_excel('source.xlsx', sheet = 'c')
mylist <- list(a = a, b = b, c = c)
server <- function(input, output, session) {
updateSelectInput(session,
"type",
choices = names(mylist))
material = reactive(input$type)
price <- material[1,"price"]
output$message <- renderText({
paste(price)
})
}
Thank you!
There is a few things that need to correct in your original code - here is my code for 3 files global.R, server.R, and ui.R with detail explanation comments. (my habit of separating them so it easier to manage.
global.R
#load libraries, data
library(shiny)
library(tidyr)
library(readxl)
library(dplyr)
library(purrr)
# This is just a generation of sample data to be used in this answer.
set.seed(1)
generate_random_df <- function(name) {
tibble(
product = paste0(name, "-", round(runif(n = 10, min = 1, max = 100))),
price = runif(10))
}
a <- generate_random_df("a")
b <- generate_random_df("b")
c <- generate_random_df("c")
mylist <- list(a = a, b = b, c = c)
server.R
set.seed(1)
generate_random_df <- function(name) {
tibble(
product = paste0(name, "-", round(runif(n = 10, min = 1, max = 100))),
price = runif(10))
}
a <- generate_random_df("a")
b <- generate_random_df("b")
c <- generate_random_df("c")
mylist <- list(a = a, b = b, c = c)
server <- function(input, output, session) {
updateSelectInput(session,
"type",
choices = names(mylist))
# to extract the data you need to reference to mylist as the Input only take
# the name of your list not the dataset within it
price <- reactive({
# Here the material command also inside the reactive not as you do initially
material <- mylist[[input$type]]
paste0(material[1,"price"])
})
# You don't need renderText for this just assign the value to message
output$message <- price
# I also output the table for easier to see
output$price_table <- renderTable(mylist[[input$type]])
}
ui.R
ui <- navbarPage(
"Dashboard",
tabPanel(
"Cost1",
fluidPage(
selectInput("type",
label = "Select Type",
choices = NULL),
textOutput("message"),
tableOutput("price_table")
)
)
)
Here is the screenshot of the app
I am trying to assign dataframes and lists (which are later used in calculations in code) based on some user inputs in a Shiny app. One of the assignments relies on two user inputs - how would I do this? I have tried to attach some reproducible code...
library(shiny)
set.seed(4)
# lists and dataframes to be assigned based on user inputs
A <- list(rnorm(6), rnorm(6))
B <- list(rnorm(6), rnorm(6))
dfA <- as.data.frame(cbind(rnorm(6), rnorm(6)))
dfA_adj <- as.data.frame(cbind(rnorm(6), rnorm(6)))
dfB <- as.data.frame(cbind(rnorm(6), rnorm(6)))
dfB_adj <- as.data.frame(cbind(rnorm(6), rnorm(6)))
ui <- fluidPage(
titlePanel(strong("Title")),
sidebarLayout(
sidebarPanel(
#content
h4(strong("Select data sets to use in calculations:")),
selectInput('L', 'Select list to use', c("List A" = 'a', "List B" = 'b')),
selectInput('P','Select Calculation method', c("Adjusted" = 'Adj', "Standard" = 'St'))
),
mainPanel(
tableOutput("table")
)
)
)
server <- function(input, output) {
#assign dataframes and list to use in code calcs (not shown) based on user inputs
pts <- reactive({ switch(input$L, "a" = A, "b" = B) })
PET <- reactive({ switch(c(input$L, input$P),
c("a", "Adj") = dfA_adj,
c("a", "St") = dfA,
c("b", "Adj") = dfB_adj,
c("b", "St") = dfB })
output$table <- renderTable(PET())
}
shinyApp(ui = ui, server = server)
We can use an if statement:
library(shiny)
set.seed(4)
# lists and dataframes to be assigned based on user inputs
A <- list(rnorm(6), rnorm(6))
B <- list(rnorm(6), rnorm(6))
dfA <- as.data.frame(cbind(rnorm(6), rnorm(6)))
dfA_adj <- as.data.frame(cbind(rnorm(6), rnorm(6)))
dfB <- as.data.frame(cbind(rnorm(6), rnorm(6)))
dfB_adj <- as.data.frame(cbind(rnorm(6), rnorm(6)))
ui <- fluidPage(
titlePanel(strong("Title")),
sidebarLayout(
sidebarPanel(
#content
h4(strong("Select data sets to use in calculations:")),
selectInput('L', 'Select list to use', choices = c("List A" = 'A', "List B" = 'B')),
selectInput('P','Select Calculation method', choices = c("Adjusted", "Standard"))
),
mainPanel(
tableOutput("table")
)
)
)
server <- function(input, output, session) {
PET <- reactive({
if (input$L == "A" && input$P == "Adjusted") {
dfA_adj}
else if (input$L == "A" && input$P == "Standard") {
dfA
} else if (input$L == "B" && input$P == "Adjusted") {
dfB_adj
} else {
dfB
}
})
output$table <- renderTable({PET()})
}
shinyApp(ui = ui, server = server)
Say I have the following -- taking note of myList:
library(shiny)
myList <- list(
first_element = tibble(a = 1, b = 2),
second_element = tibble(a = 4:5, e = 7:8),
third_element = tibble(a = c("one", "two", "three"), x = c("another", "another one", "another two"))
)
ui <- fluidPage(
titlePanel("A Title"),
verbatimTextOutput("pretty_output")
)
server <- function(input, output, session) {
output$pretty_output <- renderPrint({
myList
})
}
shinyApp(ui, server)
This results in:
What I would like is to present myList as either individual renderTable or renderDataTable elements programmatically. The following illustrates a brute force approach, but I am looking for something a little more flexible, more D.R.Y., by leveraging a for loop, lapply, purrr::map(), and/or something else.
NOTE: The length of myList should be assumed to be unknown.
library(shiny)
library(DT)
myList <- list(
first_element = tibble(a = 1, b = 2),
second_element = tibble(a = 4:5, e = 7:8),
third_element = tibble(a = c("one", "two", "three"), x = c("another", "another one", "another two"))
)
ui <- fluidPage(
titlePanel("A Title"),
dataTableOutput("dt_01"),
dataTableOutput("dt_02"),
dataTableOutput("dt_03")
)
server <- function(input, output, session) {
output$dt_01 <- renderDataTable({
datatable(myList[[1]], caption = names(myList[1]))
})
output$dt_02 <- renderDataTable({
datatable(myList[[2]], caption = names(myList[2]))
})
output$dt_03 <- renderDataTable({
datatable(myList[[3]], caption = names(myList[3]))
})
}
shinyApp(ui, server)
Please check the following lapply approach:
library(shiny)
library(DT)
myList <- list(
first_element = data.frame(a = 1, b = 2),
second_element = data.frame(a = 4:5, e = 7:8),
third_element = data.frame(a = c("one", "two", "three"), x = c("another", "another one", "another two"))
)
ui <- fluidPage(
titlePanel("A Title"),
uiOutput("tables")
)
server <- function(input, output, session) {
lapply(names(myList), function(x) {
output[[x]] = renderDataTable({myList[[x]]})
})
output$tables <- renderUI({
lapply(names(myList), dataTableOutput)
})
}
shinyApp(ui, server)
So I think most approaches will involve renderUI, here's what I went with (which feels pretty elegant), but I will leave this unanswered for a bit to see if others can chime in. Code heavily-influenced by this post:
library(shiny)
myList <- list(
first_element = tibble(a = 1, b = 2),
second_element = tibble(a = 4:5, e = 7:8),
third_element = tibble(a = c("one", "two", "three"), x = c("another", "another one", "another two"))
)
ui <- fluidPage(
titlePanel("A Title"),
uiOutput("tables")
)
server <- function(input, output, session) {
# Create the outputs dynamically
output$tables <- renderUI({
tableList <- imap(myList, ~ {
tagList(
h4(.y), # Note we can sprinkle in other UI elements
tableOutput(outputId = paste0("table_", .y))
)
})
tagList(tableList)
})
# Now render each output
iwalk(myList, ~{
output_name <- paste0("table_", .y)
output[[output_name]] <- renderTable(.x)
})
}
shinyApp(ui, server)