Shiny rhandsontable automatic values depending on User - r

I have a table, in which the user will give as input some groups. As a result, I want another column to automatically update and show the frequency (or replicate) of each group:
This code creates this app:
library(shiny)
library(rhandsontable)
library(tidyverse)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Automatic data rhandsontable"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
),
# Show a plot of the generated distribution
mainPanel(
rhandsontable::rHandsontableOutput('ed_out'),
shiny::actionButton('start_input', 'save final table')
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
# This has to be reactive
data <- reactive({
df <- data.frame(Animal = c('Dog', 'Cat', 'Mouse', 'Elephant', 'Tiger'),
Group = ' ',
replicate = as.numeric(' '))
})
output$ed_out <- rhandsontable::renderRHandsontable({
df <- data()
rhandsontable(
df,
height = 500,
width = 600) %>%
hot_col('replicate', format = '0a', readOnly = TRUE) %>%
hot_col('Animal', readOnly = TRUE)
})
# This is just to save the table when the user has finished, can be ignored
group_finals <- reactiveValues()
observeEvent(input$start_input, {
group_finals$data <- rhandsontable::hot_to_r(input$ed_out)
print(group_finals$data)
}
)
}
# Run the application
shinyApp(ui = ui, server = server)
So the idea is that the user, inputs the groups and the replicate is automatically updated: (here the user gives as input B, B, A, A, B.
I am able to count the replicates of each group, but I'm not sure how where to implement this part to calculate them and display them at the same time after the user inputs each group.
df <- df %>%
group_by(Group) %>%
mutate(replicate = 1:n())
Not sure if this is the best approach, I tried a bit with the hot_to_col renderer to use javascript but I'm unfamiliar with that language.

Sorry but I'm not familiar with the tidyverse - so I switched to data.table.
hot_to_r is the right way to go:
library(shiny)
library(rhandsontable)
library(data.table)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Automatic data rhandsontable"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
),
# Show a plot of the generated distribution
mainPanel(
rhandsontable::rHandsontableOutput('ed_out'),
shiny::actionButton('start_input', 'save final table')
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
# This has to be reactive
data <- reactive({
data.frame(Animal = c('Dog', 'Cat', 'Mouse', 'Elephant', 'Tiger'),
Group = '',
replicate = NA_integer_)
})
myData <- reactiveVal()
observeEvent(data(),{
myData(data())
})
output$ed_out <- rhandsontable::renderRHandsontable({
rhandsontable(
myData(),
height = 500,
width = 600) %>%
hot_col('replicate', format = '0a', readOnly = TRUE) %>%
hot_col('Animal', readOnly = TRUE)
})
observeEvent(input$ed_out, {
userDT <- rhandsontable::hot_to_r(input$ed_out)
setDT(userDT)
userDT[, replicate := seq_len(.N), by = Group][is.na(Group) | Group == "", replicate := NA_integer_]
myData(userDT)
})
# This is just to save the table when the user has finished, can be ignored
group_finals <- reactiveValues()
observeEvent(input$start_input, {
group_finals$myData <- rhandsontable::hot_to_r(input$ed_out)
print(group_finals$myData)
})
}
# Run the application
shinyApp(ui = ui, server = server)

Related

How to get a subset of data using an input that's dependent on another input in shiny?

I tried to add a filter to my data analysis. The filter (inputF2) is an item in a category (xInput) chosen by the user.
then I want filter out the data to do summarize analysis and plot out the mean. However, once I wrote the if statement, the program won't run.
library(datasets)
library(shiny)
library(dplyr)
library(ggplot2)
library(DT)
library(crosstalk)
data("iris")
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Analyze Iris table"),
# Sidebar with a dropdown menu selection input for key measurecomponent
sidebarLayout(
sidebarPanel(
selectInput("yInput", "Measuring element: ",
colnames(iris), selected = colnames(iris)[2]),
selectInput('xInput', 'Grouper: ',
colnames(iris), selected = colnames(iris)[5])
),
# Show a plot of the generated distribution
mainPanel(
uiOutput('filter'),
plotOutput("barPlot"),
DTOutput('table1')
)))
server <- function(input, output) {
output$filter = renderUI({
selectInput('inputF2', 'Filter Item: ',
c('Null', unique(iris %>% select(input$xInput))))
})
if(input$inputF2 != 'Null') {
iris_sub = reactive({
iris %>% filter_at(input$xInput == input$inputF2)
})
} else{ iris_sub = iris}
by_xInput <- reactive({
iris_sub %>%
group_by_at(input$xInput) %>%
summarize(n = n(), mean_y = mean(!! rlang::sym(input$yInput)))
})
output$barPlot <- renderPlot({
# as the input is a string, use `aes_string`
ggplot(data = by_xInput(), aes_string(x = input$xInput, y = "mean_y")) +
geom_bar(stat = 'identity')
})
output$table1 = renderDT(
datatable(by_xInput())
)
}
shinyApp(ui = ui, server = server)
This is the error message I got:
Error in .getReactiveEnvironment()$currentContext() :
Operation not allowed without an active reactive context. (You tried to do something that can only be done from inside a reactive expression or observer.)
The reason you were getting the active reactive content error was because of this chunk
if(input$inputF2 != 'Null') {
iris_sub = reactive({
iris %>% filter_at(input$xInput == input$inputF2)
})
} else{ iris_sub = iris}
Here you are evaluating input$inputF2 but that can change with user selection, so the test needs to be inside a reactive().
Another good practice is to wrap variables like inputF2 in req, to ensure they will have a value before being evaluated. This is because you are rendering the widget for filter on the server side, and initially it will not have a value.
Note also, that the filtering condition filter(input$xInput == input$inputF2) would fail, because filter expects an unquoted variable name in the left hand side of that expression (but input$xInput is a character). You can convert input$xInput to a name with as.name() and then use bang-bang inside filter to evaluate it: filter(!!as.name(input$xInput) == input$inputF2)
After this changes, the filtering chunk becomes:
iris_sub <- reactive({
x_in <- as.name(input$xInput)
if (req(input$inputF2) != 'Null') {
iris_sub <- iris %>% filter(!!x_in == input$inputF2)
} else{
iris_sub <- iris
}
return(iris_sub)
})
Finally, it seems like your app allowed the user to choose the same variable as measuring element and as the grouper. Not sure this is a good idea, as it might throw errors because you can't modify a grouping variable. One way to control this is to use validate inside the reactive that does the summarising and produce a meaningful error message for the user:
validate(
need(expr = input$xInput != input$yInput,
message = "Can't summarise by group when 'grouper' is the same as 'measuring element'"))
Here is the whole app with these modifications.
library(datasets)
library(shiny)
library(dplyr)
library(ggplot2)
library(DT)
library(crosstalk)
data("iris")
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Analyze Iris table"),
# Sidebar with a dropdown menu selection input for key measurecomponent
sidebarLayout(
sidebarPanel(
selectInput("yInput", "Measuring element: ",
colnames(iris), selected = colnames(iris)[2]),
selectInput('xInput', 'Grouper: ',
colnames(iris), selected = colnames(iris)[5])
),
# Show a plot of the generated distribution
mainPanel(
uiOutput('filter'),
plotOutput("barPlot"),
DTOutput('table1')
)))
server <- function(input, output) {
output$filter = renderUI({
selectInput('inputF2',
'Filter Item: ',
c('Null', iris %>% select(input$xInput) %>% unique()))
})
iris_sub <- reactive({
x_in <- as.name(input$xInput)
if (req(input$inputF2) != 'Null') {
iris_sub <- iris %>% filter(!!x_in == input$inputF2)
} else{
iris_sub <- iris
}
return(iris_sub)
})
by_xInput <- reactive({
validate(
need(expr = input$xInput != input$yInput,
message = "Can't summarise by group when 'grouper' is the same as 'measuring element'"))
iris_sub() %>%
group_by_at(input$xInput) %>%
add_tally() %>%
summarize_at(.vars = vars(input$yInput),
.funs = list("mean_y" = mean))
})
output$barPlot <- renderPlot({
# as the input is a string, use `aes_string`
ggplot(data = by_xInput(), aes_string(x = input$xInput, y = "mean_y")) +
geom_bar(stat = 'identity')
})
output$table1 = renderDT(
datatable(by_xInput())
)
}
shinyApp(ui = ui, server = server)

Plotly click event does not work due to range of values of in a single bar of a histogram

I have the dataframe below:
col1<-sample(500, size = 500, replace = TRUE)
col2<-sample(500, size = 500, replace = TRUE)
d<-data.frame(col1,col2)
And I create a histogram of this data frame that has click-event activated. When the user clicks on a bar the rows of the dataframe that have the relative value are displayed in a datatable. The problem is that the app works fine with a few values. If for example my dataframe had 5 rows instead of 500 with :
col1<-sample(5, size = 5, replace = TRUE)
col2<-sample(5, size = 5, replace = TRUE)
d<-data.frame(col1,col2)
But with more values the app does not work since the plotly gives a range of values in every single bar instead of a unique value.
library(plotly)
library(shiny)
library(DT)
ui <- fluidPage(
mainPanel(
plotlyOutput("heat")
),
DT::dataTableOutput('tbl4')
)
server <- function(input, output, session) {
output$heat <- renderPlotly({
render_value(d) # You need function otherwise data.frame NN is not visible
p <- plot_ly(x = d$col2, type = "histogram",source="subset") # set source so
# that you can get values from source using click_event
})
render_value=function(NN){
output$tbl4 <- renderDataTable({
s <- event_data("plotly_click",source = "subset")
print(s)
return(DT::datatable(d[d$col2==s$y,]))
})
}
}
shinyApp(ui, server)
You can try this (added code to capture the count). You need to plot a histogram of count and then you can able to get your original data based on click event.
library(plotly)
library(shiny)
library(DT)
library(dplyr)
ui <- fluidPage(
mainPanel(
plotlyOutput("heat")
),
DT::dataTableOutput('tbl4')
)
server <- function(input, output, session) {
output$heat <- renderPlotly({
col1<-sample(500, size = 500, replace = TRUE)
col2<-sample(500, size = 500, replace = TRUE)
d<-data.frame(col1,col2)
d=d %>%
group_by(col2) %>%
mutate(count = n()) # You can programatically add count for each row
render_value(d) # You need function otherwise data.frame NN is not visible
p <- plot_ly(x = d$count, type = "histogram",source="subset")
# You should histogram of count
# set source so that you can get values from source using click_event
})
render_value=function(d){
output$tbl4 <- renderDataTable({
s <- event_data("plotly_click",source = "subset")
print(s)
return(DT::datatable(d[d$count==s$x,]))
})
}
}
shinyApp(ui, server)
Screenshot from the working prototype:

Shiny DT Highlight Cells if Value Appears in Another Set

Issue:
I have a data frame where row A is the names of people in my organization. I have a separate data frame that is a subset of row A in the original table. I would like to highlight all rows in the first data table that match names in the second table. Essentially, I have two sets. Set A and Set B. Both are names, I would like to highlight the data table for all names in Set A that match Set B. However, I keep getting an error: length(levels) must be equal to length(values)
How would I avoid receiving this error?
Reproducible Example:
I have a data frame of mtcars. I am filtering the mtcars dataset based on a slider input for mpg. I would like to highlight the data frame of mtcars that meet the filtering criteria. In effect, this would mean highlighting the output table for all observations where the mpg are <= the slider input mpg.
library(shiny)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Highlight Cell Test (Sets)"),
sidebarLayout(
sidebarPanel = 'side',
sliderInput('slider', 'slider input', 1, 30, 20)),
# Show a plot of the generated distribution
mainPanel(
dataTableOutput("test")
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
subset <- reactive({
mtcars %>%
filter(mpg <= input$slider)
})
output$test <- DT::renderDataTable(
mtcars %>%
DT::datatable(
options = list(
dom = 'ftipr',
searching = TRUE
) %>%
formatStyle(
'test',
background = styleEqual(
(subset()$mpg %in% mtcars$mpg), 'lightgreen'))
)
)
}
# Run the application
shinyApp(ui = ui, server = server)
Any help is much appreciated. Thanks in advance.
You can do this via rowCallback like so:
library(shiny)
library(dplyr)
library(DT)
fnc <- JS('function(row, data, index, rowId) {','console.log(rowId)','if(rowId >= ONE && rowId < TWO) {','row.style.backgroundColor = "lightgreen";','}','}')
ui <- fluidPage(
# Application title
titlePanel("Highlight Cell Test (Sets)"),
sidebarLayout(
sidebarPanel = 'side',
sliderInput('slider', 'slider input', 1, 30, 16)),
# Show a plot of the generated distribution
mainPanel(
dataTableOutput("test")
)
)
# Define server logic required to draw a histogram
server <- function(input, output, session) {
subset <- reactive({
mtcars %>% filter(mpg <= input$slider)
})
Coloring <- eventReactive(subset(),{
a <- which(subset()$mpg %in% mtcars$mpg)
print(a)
if(length(a) <= 0){
return()
}
fnc <- sub("ONE",a[1],fnc)
fnc <- sub("TWO",max(a),fnc)
fnc
})
output$test <- DT::renderDataTable(
mtcars %>%
DT::datatable(options = list(dom = 'ftipr',searching = TRUE,pageLength = 20, scrollY = "400px",rowCallback = Coloring()))
)
}
shinyApp(ui = ui, server = server)

Use data.table package in shiny app

I want to use the data.table package in a Shiny App to improve speed.
However, it's not clear to me how to select the right columns based on the user input.
The following example works for the case when the data is in the data.frame format but not when it is in data.table format.
# load packages
library(data.table)
# global ----------------------------------------
library(shiny)
# use cars dataset
data(cars)
# create datatable from cars data
cars <- as.data.table(cars)
# user interface ---------------------------------
ui <- fluidPage(
sidebarLayout(
selectInput(inputId = 'col', label = 'column', choices = names(cars)),
numericInput(inputId = 'filter',label = 'filter', value = 5)
),
mainPanel(plotOutput("plot"))
)
# server ----------------------------------------
server <- function(input, output) {
output$plot <- renderPlot({
# filter example
d1 <- cars[speed>input$filter,]
x <- d1[[input$col]]
hist(x)
})
}
# run app --------------------------------------
shinyApp(ui = ui, server = server)

Filtering data.tables in shiny with 2 inputs

Code Below. I want to filter a data.frame based on two inputs. input$SelectGroup4 will be a column name in a data.frame and input$subsetSelect is a value in that column. Is this possible to do? Note: the whole code base is much much larger, so I took out only the key parts to this code. This code probably won't run on it's own, but it's just to get a general idea.
library(shiny)
library(data.table)
ui = fluidPage(
uiOutput('textField'),
uiOutput('docIdField'),
fluidRow(column(4,textInput("keyword", "Enter keyword :", "WB")),
fluidRow(column(4, sliderInput("context", "Enter number of words for context :",
min = 1, max = 10,
value = 5))),
fluidRow(column(4,uiOutput('selectGroup4'))),
fluidRow(column(4,uiOutput('subsetSelect'))),
fluidRow(column(10,DT::dataTableOutput("kwicTable"))))
}
server = function(input,output){
df_corpus1 <- reactive({
dTemp = as.data.table(datasetInput())
dTemp = dTemp %>% filter(input$selectGroup4==input$subsetSelect)
})
output$kwicTable=renderDataTable({
dtemp = df_corpus1()
dtemp = corpus(as.data.frame(dtemp),text_field=input$textField,docid_field=input$docIdField)
x = kwic(x = dtemp,pattern=input$keyword,window=input$context)
x = as.data.table(x)
x[,4:6]
})
}
shinyApp(ui,server)
Yes, you can do that. Since we do not have access to your dataset, here is a working example with the mtcars dataset.
Hope this helps!
library(shiny)
ui <- fluidPage(
selectInput('col','Column',colnames(mtcars)),
uiOutput('ui_col'),
dataTableOutput('table')
)
server <- function(input,output){
# Create a new input element with the unique values of the selected column
output$ui_col <- renderUI({
req(input$col)
selectizeInput('val','Value',unique(mtcars[[input$col]]),multiple=T)
})
# If both inputs are not null, filter the table
output$table <- renderDataTable({
df <- mtcars
if(!is.null(input$col) & !is.null(input$val))
{
df = df[df[[input$col]] %in% input$val,]
}
df
})
}
shinyApp(ui = ui, server = server)

Resources