I have a shiny app that displays a number of products depending on the search.
It subsets a large dataset and shows me the products I want that match.
I have another dataframe that has the reviews of said products.
I would like that when a specific row is clicked the review information appears in a different datatable.
The second datatable also needs to be a subset based on the Catalog Number (here is Acolumn.
All help appreciated.
df1 <- data.frame(A= c("BX002","BX006", "BX008"),
B= c("Actin","Tubulin", "GAPDH"),
C = c("Mouse","Human", "Human"),
D = c("WB","WB", "IHC"))
df2 <- data.frame(A= c("BX002","BX006", "BX008"),
B= c("Actin","Tubulin", "GAPDH"),
C = c("5","5", "4"),
D = c("Good","Good", "Bad"),
E = c("Kidney", "Liver", "Heart"))
library(shinydashboard)
library(shiny)
app <- shinyApp(
ui = dashboardPage(
dashboardHeader(
title = "Search"),
dashboardSidebar(
sidebarMenu(
menuItem("Search Product", tabName = "product", icon = icon("search")))),
dashboardBody(
tabItems(
tabItem("product",
fluidPage(
sidebarLayout(
sidebarPanel(textInput("name", "Protein name", value = ""),
submitButton("Search")),
mainPanel(
tabsetPanel(
tabPanel("Products", dataTableOutput("table1")))))))))),
server = shinyServer(function(input, output, session) {
output$table1 <- DT::renderDataTable({
validate(need(input$name != "", "Please select a Protein Name"))
search <- input$name
df <- subset(df1, grepl(search, df1$B, ignore.case = TRUE)==TRUE)
datatable(df, escape = FALSE, selection = "single")
})
observeEvent(input$table1_rows_selected,
{
df <- subset(df2, df2$A == input$table1_rows_selected$A)
showModal(modalDialog(
title = "Reviews",
df
))
})
})
)
I have tried a few methods but cant make it work.
This is my last atempt, no box popup, no error message nothing.
best
I took some liberties with your formatting to make it simpler to recreate.
This should give you the jist of how the modalDialogfunction works with DT.
Here's a working example:
library(shiny)
library(DT)
ui <- fluidPage(
textInput('name','Protein Name'),
dataTableOutput('table1')
)
server <- function(input, output,session) {
df1 <- data.frame(A= c("BX002","BX006", "BX008"),
B= c("Actin","Tubulin", "GAPDH"),
C = c("Mouse","Human", "Human"),
D = c("WB","WB", "IHC"))
df2 <- data.frame(A= c("BX002","BX006", "BX008"),
B= c("Actin","Tubulin", "GAPDH"),
C = c("5","5", "4"),
D = c("Good","Good", "Bad"),
E = c("Kidney", "Liver", "Heart"))
tbl1 <- reactive({
if(nchar(input$name)>0){
df1[which(tolower(input$name) == tolower(df1$B)),]
}
})
output$table1 <- renderDataTable({
if(nchar(input$name)>0){
datatable(tbl1(),selection = 'single')
}
})
review_tbl <- reactive({
df2[which(df1[input$table1_rows_selected,1]==df2$A),]
})
observeEvent(input$table1_rows_selected,{
showModal(
modalDialog(
renderDataTable({
review_tbl()
})
))
})
}
# Run the application
shinyApp(ui = ui, server = server)
Related
I'm trying to build a checklist that will show me different parts of my data in Rstudio using Shiny.
For example:
If I check question A:
Row 1:2 should show in my table
If I check question B while A is still checked:
I want to see row 1:2 & 5.
If I uncheck question B but check question C while A is still checked:
I want to see row 1:2 & 3:4
I made 2 versions but I'm stuck on both
Version 1 (were I'm having trouble with the if statements in the server):
if (interactive()) {
library(shiny)
library(readxl)
Sheet2 <- read_excel("Sheet2.xlsx")
Sheet3 <- read_excel("Sheet3.xlsx")
Sheet4 <- read_excel("Sheet4.xlsx")
ui <- fluidPage(
title = "Checklist",
sidebarLayout(
sidebarPanel(
checkboxInput(inputId = "A",
label = strong("A"),
value = FALSE),
conditionalPanel(condition = "input.A == true",
checkboxInput(inputId = "B",
label = strong("B"),
value = FALSE)),
checkboxInput(inputId = "C",
label = strong("C"),
value = FALSE)
),
mainPanel(
tabsetPanel(
id = 'dataset',
tabPanel("Sheet2", DT::dataTableOutput("mytable1")),
tabPanel("Sheet3", DT::dataTableOutput("mytable2")),
tabPanel("Sheet4", DT::dataTableOutput("mytable3"))
)
)
)
)
server <- function(input, output) {
output$mytable1 <-
DT::renderDataTable({
if (input$A){
DT::datatable(Sheet2[1:2,])}
if (input$C){
DT::datatable(Sheet2[3:4,])}
if (input$B){
DT::datatable(Sheet2[5,])
}
})
output$mytable3 <-
DT::renderDataTable({if ((input$A)){
DT::datatable(Sheet3[3:4,])}
})
}
shinyApp(ui, server)
}
And version 2 (were i cant add multiple rows to my groupinput):
if (interactive()) {
library(shiny)
library(readxl)
Sheet2 <- read_excel("Sheet2.xlsx")
Sheet3 <- read_excel("Sheet3.xlsx")
Sheet4 <- read_excel("Sheet4.xlsx")
B3 <- 3:4
ui2 <- fluidPage(
title = "M&A Checklist",
mainPanel(
tabsetPanel(
id = 'dataset',
tabPanel("Sheet1", checkboxGroupInput(inputId = "show_vars",
label = "Questions:",
choiceNames = c("AAAAAAAAAA","BBBBBBBBBB","CCCCCCCCCCC"),
choiceValues = c("1","B3","5") )),
tabPanel("Sheet2", DT::dataTableOutput("mytable1")),
tabPanel("Sheet3", DT::dataTableOutput("mytable2")),
tabPanel("Sheet4", DT::dataTableOutput("mytable3"))
)
)
)
server2 <- function(input, output) {
# choose columns to display
output$mytable1 <- DT::renderDataTable({
DT::datatable(Sheet2[input$show_vars,])
})
# sorted columns are colored now because CSS are attached to them
output$mytable2 <- DT::renderDataTable({
DT::datatable(Sheet3[input$show_vars,])
})
# customize the length drop-down menu; display 5 rows per page by default
output$mytable3 <- DT::renderDataTable({
DT::datatable(Sheet4[input$show_vars,] )
})
}
shinyApp(ui2, server2)
}
Any suggestions, on how this can be done efficiently?
One way to approach this, is to create an empty vector that will contain the rows of data to filter your data on. Then, for each if statement, you can add the additional rows to this vector.
This example is based on your first version. Let me know if this achieves what you need.
library(shiny)
library(DT)
df <- data.frame(
a = 1:5,
b = 6:10
)
Sheet2 <- Sheet3 <- Sheet4 <- df
ui <- fluidPage(
title = "Checklist",
sidebarLayout(
sidebarPanel(
checkboxInput(inputId = "A",
label = strong("A"),
value = FALSE),
conditionalPanel(condition = "input.A == true",
checkboxInput(inputId = "B",
label = strong("B"),
value = FALSE)),
checkboxInput(inputId = "C",
label = strong("C"),
value = FALSE)
),
mainPanel(
tabsetPanel(
id = 'dataset',
tabPanel("Sheet2", DT::dataTableOutput("mytable1")),
tabPanel("Sheet3", DT::dataTableOutput("mytable2")),
tabPanel("Sheet4", DT::dataTableOutput("mytable3"))
)
)
)
)
server <- function(input, output) {
selected_rows <- reactive({
my_rows <- c()
if (input$A) {
my_rows <- c(my_rows, 1:2)
}
if (input$C) {
my_rows <- c(my_rows, 3:4)
}
if (input$B) {
my_rows <- c(my_rows, 5)
}
return(my_rows)
})
output$mytable1 <-
DT::renderDataTable({
DT::datatable(Sheet2[selected_rows(),])
})
output$mytable2 <-
DT::renderDataTable({
DT::datatable(Sheet3[selected_rows(),])
})
output$mytable3 <-
DT::renderDataTable({
DT::datatable(Sheet4[selected_rows(),])
})
}
shinyApp(ui, server)
I have the dataframe below:
DF2 = data.frame(agency_postcode = factor(rep(c(12345,45678,24124,32525,32325),2)),
car_group=factor(rep(c("Microcar","City car","Supermini","Compact","SUV"),2)),
transmission=factor(rep(c("automatic","manual"),5)))
which I use and display as rhandsontable in order to create a second table. First you are supposed to select one or more options from filter by input and then a level from the selected filter(s). Then you press search. What I basically want to do is subset the second table based on the first row of every selected column of the first table. The issue is in line 30 of server.r in which I should give the input$sel
#ui.r
library(shiny)
library(rhandsontable)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(width=2,
selectInput("sel","Filter by:",
choices = c("agency_postcode","date_start","days","car_group","transmission","driver_age"),
multiple=T,selected = "agency_postcode"),
actionButton("sr","Search")
),
mainPanel(
fluidRow(
column(4,offset = 0, style='padding:0px;',rHandsontableOutput("hot")),
column(8,offset = 0, style='padding:0px;',rHandsontableOutput("hot2"))
)
)
)
)
#server.r
#server.r
library(shiny)
library(rhandsontable)
library(jsonlite)
server <- function(input, output) {
#Create rhandsontable as a reactive expression
DFR2<-reactive({
rhandsontable(DF2[1,1:2], rowHeaders = NULL,height = 200)%>%
hot_col(colnames(DF2)[1:2])
})
#Display the rhandsontable
output$hot <- renderRHandsontable({
DFR2()
})
#Convert the rhandsontable to a daraframe
DFR3<-reactive({
req(input$hot)
hot_to_r(input$hot)
})
#Subset the initial dataframe by value of the 1st row-1st column cell of DF3
DFR4 <- reactive({
req(DFR3())
D<-DF2[ which(DF2[,1] %in% DFR3()[1, 1]), ] #input$sel is supposed to be used here instead of 1
for(i in 1:ncol(D)){
D[,i] <- factor(D[,i])
}
D
})
#Display the new rhandsontable
output$hot2 <- renderRHandsontable({
input$sr
isolate(rhandsontable(DFR4()[1,], rowHeaders = NULL,height = 200)%>%
hot_col(colnames(DFR4())) )
})
}
OK. Here is an app that uses a small table to filter a larger one using inner_join. I am not sure this will match the design you had in mind. It is still unclear to me where the filter levels are coming from, or what the hands on tables are for. But you should be able to adapt this approach to your design. Note also that I am not using hands on tables. A direct replacement of the calls to renderTable with renderRHandsontable should work too.
library(shiny)
library(dplyr)
library(purrr)
sub_cars <- mtcars[, c("cyl", "gear", "am")]
ui <- fluidPage(
column(width=3,
selectInput(
inputId = "sel_col",
label = "Select variables",
multiple = TRUE,
choices = c("cyl", "gear", "am"),
selectize = TRUE),
uiOutput("cyl"),
uiOutput("gear"),
uiOutput("am")
),
column(width = 3,
tableOutput("filter_table")),
column(width = 6,
tableOutput("large_table"))
)
server <- function(input, output) {
output$cyl <- renderUI({
if ("cyl" %in% input$sel_col) {
selectInput(
inputId = "sel_cyl",
label = "Select cylinders",
choices = unique(sub_cars$cyl),
multiple = TRUE,
selectize = TRUE
)
}
})
output$gear <- renderUI({
if ("gear" %in% input$sel_col) {
selectInput(
inputId = "sel_gear",
label = "Select gears",
choices = unique(sub_cars$gear),
multiple = TRUE,
selectize = TRUE
)
}
})
output$am <- renderUI({
if ("am" %in% input$sel_col) {
selectInput(
inputId = "sel_am",
label = "Select am",
choices = unique(sub_cars$am),
multiple = TRUE,
selectize = TRUE
)
}
})
# make a small filter table
filter_df <- reactive({
validate(
need(!is_null(input$sel_col),
message = "Please select a column"))
cols <- input$sel_col
cols_vals <- map(cols, function(x) input[[paste0("sel_", x, collapse="")]])
df <- map2_dfr(cols, cols_vals, function(x, y)
filter(sub_cars,!!as.name(x) %in% y)) %>%
select(one_of(cols)) %>%
distinct()
return(df)
})
output$filter_table <- renderTable({
validate(
need(nrow(filter_df()) > 0,
message = "Please select filter values"))
filter_df()
})
# inner join the larger table
large_df <- reactive({
validate(
need(nrow(filter_df()) > 0,
message = "Please select filter values"))
cols <- input$sel_col
inner_join(x=filter_df(), y=mtcars, by = cols)
})
output$large_table <- renderTable({large_df()})
}
shinyApp(ui, server)
Here is a gif of what it does.
I am trying to understand the reactive part in R shiny. In that process I am trying to update an output table based on the input change while selecting values from the age drop down. It seems to do it by the first value but when I change any value from the age drop down it won't update my table. The input I am using is chooseage. Below is the code which I am using.
library(shiny)
library(shinydashboard)
library(shinyBS)
library(knitr)
library(kableExtra)
library(shiny)
library(shinythemes)
ui <- dashboardPage(
dashboardHeader(disable = F, title = "PATH Study"),
dashboardSidebar(sidebarMenu(
menuItem(
"Population Filter",
uiOutput("choose_age")
)
)),
dashboardBody(box(
width = 12,
tabBox(
width = 12,
id = "tabBox_next_previous",
tabPanel("Initiation",
fluidRow(
box(
width = 5,
solidHeader = TRUE,
status = "primary",
tableOutput("smoke"),
collapsible = F
)
))
),
uiOutput("Next_Previous")
))
)
server <- function(input, output, session) {
# Drop-down selection box for which Age bracket to be selected
age_levels <- c("18 to 24 years old","25 to 34 years old","35 to 44 years old")
output$choose_age <- renderUI({
selectInput("selected_age", "Age", as.list(age_levels))
})
myData <- reactive({
with_demo_vars %>%
filter(age == input$choose_age) %>%
pct_ever_user(type = "SM")
})
output$smoke <-
renderTable({
head(myData())
})
}
shinyApp(ui = ui, server = server)
Here is a quick prototype for your task
library(shiny)
library(tidyverse)
library(DT)
# 1. Dataset
df_demo <- data.frame(
age = c(16, 17, 18, 19, 20),
name = c("Peter", "Mary", "Mike", "Nick", "Phillipe"))
# 2. Server
server <- function(input, output, session) {
# 1. UI element 'Age'
output$ui_select_age <- renderUI({
selectInput("si_age", "Age", df_demo$age)
})
# 2. Reactive data set
df_data <- reactive({
# 1. Read UI element
age_selected <- input$si_age
# 2. Filter data
df <- df_demo %>%
filter(age == age_selected)
# 3. Return result
df
})
# 3. Datatable
output$dt_table <- renderDataTable({
datatable(df_data())
})
}
# 3. UI
ui <- fluidPage(
fluidRow(uiOutput("ui_select_age")),
fluidRow(dataTableOutput("dt_table"))
)
# 4. Run app
shinyApp(ui = ui, server = server)
I think youre shinyApp is over-reactive, as all functions in the server are executed straight away, without waiting for any selected input. So either it will break down or behave weird. So you have to delay the reactivity with req(), validate() / need() or with any observeEvent or eventReactive() function.
Maybe this snippet might help you, although there would be several ways to achieve the desired behaviour.
library(shiny)
library(shinydashboard)
library(dplyr)
data(mtcars)
mtcars$age <- sample(x = c(10,20,30,40,50), size = nrow(mtcars), replace = T)
with_demo_vars <- mtcars
ui <- dashboardPage(
dashboardHeader(disable = F, title = "PATH Study"),
dashboardSidebar(sidebarMenu(
menuItem(text = "Population Filter",
uiOutput("choose_age")
)
)
),
dashboardBody(
tableOutput("smoke")
)
)
server <- function(input, output, session) {
output$choose_age <- renderUI({
selectInput("selected_age", "Age", with_demo_vars$age)
})
myData <- reactive({
with_demo_vars %>%
dplyr::filter(age == input$selected_age)
})
output$smoke <- renderTable({
req(input$selected_age)
head(myData())
})
}
shinyApp(ui = ui, server = server)
i have a simple shiny app and i want to create data table which will provide the rows based on the name i choose in my selectinput(). While it works normally when i choose names one after the other(first,second,third...etc) it does not respond when i choose first name, then third name, then second etc. Any suggestions?
nba <- data.frame(
player = c("James", "Durant", "Curry", "Harden", "Paul", "Wade"),
team = c("CLEOH", "GSWOAK", "GSWOAK", "HOUTX", "HOUTX", "CLEOH"),
day1points = c("25","23","30","41","26","20"),
day2points = c("24","25","33","45","26","23"),
rating=c("1","2","3","4","5","1")
)
ui.r
library(shiny)
library(DT)
ui <- navbarPage(
title="SADDAS",
sidebarLayout(
sidebarPanel(uiOutput("var1_select")),
mainPanel(tableOutput("reportOutput"))
))
server.r
server <- function(input, output) {
output$var1_select<-renderUI({
selectInput("ind_var_select","Select Names", choices =c(as.character(nba[,1] )),multiple = TRUE,selected = nba[1,1])
})
output$reportOutput = renderTable(
{subset(nba[,1:3],player==input$ind_var_select)},
options = list(scrollX = TRUE)
)
}
Try this:
library(shiny)
nba <- data.frame(
player = c("James", "Durant", "Curry", "Harden", "Paul", "Wade"),
team = c("CLEOH", "GSWOAK", "GSWOAK", "HOUTX", "HOUTX", "CLEOH"),
day1points = c("25","23","30","41","26","20"),
day2points = c("24","25","33","45","26","23"),
rating=c("1","2","3","4","5","1")
)
ui <- navbarPage(
title="SADDAS",
sidebarLayout(
sidebarPanel(uiOutput("var1_select")),
mainPanel(tableOutput("reportOutput"))
))
server <- function(input, output) {
output$var1_select<-renderUI({
selectInput("ind_var_select","Select Names", choices =c(as.character(nba[,1] )),multiple = TRUE,selected = nba[1,1])
})
output$reportOutput = renderTable({
nba[,1:3][nba$player %in% input$ind_var_select,]
},
options = list(scrollX = TRUE)
)
}
shinyApp(ui, server)
I am trying to create a shiny-app that load data-set, present the variable list and their classes and allow the user to modify the class of a selected variable. All the functions in the following code are working except to the last function in the server- observeEvent which not working when trying to modify the variable class. Any suggestions?
Thank you in advance,
Rami
`
rm(list = ls())
library(shiny)
library(shinydashboard)
library(DT)
ui <- dashboardPage(
dashboardHeader(title = "Shiny Example"),
#--------------------------------------------------------------------
dashboardSidebar(
sidebarMenu(
menuItem("Data", tabName = "data", icon = icon("th"))
)
),
#--------------------------------------------------------------------
dashboardBody(
#--------------------------------------------------------------------
tabItem(tabName = "data",
fluidPage(
fluidRow(
box(
selectInput('dataset', 'Select Dataset', list(GermanCredit = "GermanCredit",
cars = "cars",
iris = "iris")),
title = "Datasets",width = 4, status = "primary",
checkboxInput("select_all", "Select All Variable", value = TRUE),
conditionalPanel(condition = "input.select_all == false",
uiOutput("show.var"))
),
box(
title = "Variable Summary", width = 4, status = "primary",
DT::dataTableOutput('summary.data')
),
box(
title = "Modify the Variable Class", width = 4, status = "primary",
radioButtons("choose_class", label = "Modify the Variable Class",
choices = list(Numeric = "numeric", Factor = "factor",
Character = "character"),
selected = "numeric"),
actionButton("var_modify", "Modify")
)
)
)
)
)
)
#--------------------------------------------------------------------
# Server Function
#--------------------------------------------------------------------
server <- function(input, output,session) {
#--------------------------------------------------------------------
# loading the data
get.df <- reactive({
if(input$dataset == "GermanCredit"){
data("GermanCredit")
GermanCredit
}else if(input$dataset == "cars"){
data(cars)
cars
}else if(input$dataset == "iris"){
data("iris")
iris
}
})
# Getting the list of variable from the loaded dataset
var_list <- reactive(names(get.df()))
# Choosing the variable - checkbox option
output$show.var <- renderUI({
checkboxGroupInput('show_var', 'Select Variables', var_list(), selected = var_list())
})
# Setting the data frame based on the variable selction
df <- reactive({
if(input$select_all){
df <- get.df()
} else if(!input$select_all){
df <- get.df()[, input$show_var, drop = FALSE]
}
return(df)
})
# create list of variables
col.name <- reactive({
d <- data.frame(names(df()), sapply(df(),class))
names(d) <- c("Name", "Class")
return(d)
})
# render the variable list into table
output$summary.data <- DT::renderDataTable(col.name(), server = FALSE, rownames = FALSE,
selection = list(selected = 1, mode = 'single'),
options = list(lengthMenu = c(5, 10, 15, 20), pageLength = 20, dom = 'p'))
# storing the selected variable from the variables list table
table.sel <- reactive({
df()[,which(colnames(df()) == col.name()[input$summary.data_rows_selected,1])]
})
# Trying to modify the variable class
observeEvent(input$var_modify,{
modify.row <- which(colnames(df()) == col.name()[input$summary.data_rows_selected,1])
if( input$choose_class == "numeric"){
df()[, modify.row] <- as.numeric(df()[, modify.row])
} else if( input$choose_class == "factor"){
df()[, modify.row] <- as.factor(df()[, modify.row])
} else if( input$choose_class == "character"){
df()[, modify.row] <- as.character(df()[, modify.row])
}
})
}
shinyApp(ui = ui, server = server)
`
I would use reactiveValues() instead.
library(shiny)
# Define UI for application that draws a histogram
ui <- shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("classType", "Class Type:", c("as.numeric", "as.character"))
),
mainPanel(
textOutput("class")
)
)
))
server <- shinyServer(function(input, output) {
global <- reactiveValues(sample = 1:9)
observe({
global$sample <- get(input$classType)(global$sample)
})
output$class <- renderText({
print(class(global$sample))
})
})
shinyApp(ui = ui, server = server)
In case you are interested:
Concerning your attempt: reactive() is a function and you called the output of the function by df()[, modify.row]. So in your code you try to change the output of the function, but that does not change the output of futures calls of that function.
Maybe it is easier to see in a simplified version:
mean(1:3) <- 1
The code can not change the mean function to output 1 in future. So thats what reactiveValues() help with :). Hope that helps!