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)
Related
I am trying to colour specific cells in a data frame with RShiny, based on their values.
I have managed to highlight the cells successfully using the formatStyle function from the DT library, however the output format of formatStyle appears to be a list, which is a problem since I would now like to add formatting options to the renderDataTable function (such as the scroll bar using scrollX=TRUE).
Is there someway of transforming the output from formatStyle to a data frame?
So here is some reproducible example code which works:
library(shiny)
library(reticulate)
library(DT)
ui <- fluidPage(
mainPanel(
# first header title
h3("MTCars"),
# prepare the first output table
DT::dataTableOutput('table1'),
)
)
server <- function(input, output, session) {
myData <- mtcars
myData$wheelDiameter <- myData$wt
myData$windscreenHeight <- myData$mpg
myData$carTint <- myData$vs
myData$color <- rep(c("red","black","green","yellow"),4)
colourWeights <- reactive({
highlightData <- datatable(myData) %>% formatStyle(
'wt',
backgroundColor = styleInterval(c(1.5,3.0), c("red","yellow","green")),
fontWeight = 'bold'
)
return(highlightData)
})
# display the first output table
output$table1 <- DT::renderDataTable({
colourWeights()
})
}
shinyApp(ui, server)
And a screen shot of the output:
Shiny output table
And here is some example code which doesn't work since the formatStyle output is not a dataframe:
library(shiny)
library(reticulate)
library(DT)
ui <- fluidPage(
mainPanel(
# first header title
h3("MTCars"),
# prepare the first output table
DT::dataTableOutput('table1'),
)
)
server <- function(input, output, session) {
myData <- mtcars
myData$wheelDiameter <- myData$wt
myData$windscreenHeight <- myData$mpg
myData$carTint <- myData$vs
myData$color <- rep(c("red","black","green","yellow"),4)
colourWeights <- reactive({
highlightData <- datatable(myData) %>% formatStyle(
'wt',
backgroundColor = styleInterval(c(1.5,3.0), c("red","yellow","green")),
fontWeight = 'bold'
)
return(highlightData)
})
# display the first output table
output$table1 <- DT::renderDataTable({
datatable(colourWeights(),
options = list(
scrollX = TRUE,
autoWidth = FALSE,
dom = 'Blrtip'
)
)
})
}
shinyApp(ui, server)
This is the error I get:
Error: 'data' must be 2-dimensional (e.g. data frame or matrix
Thanks in advance
It shows that colourWeights is already a datatable. Thus, moving the options to the highlightData part will work.
library(shiny)
library(reticulate)
library(DT)
ui <- fluidPage(
mainPanel(
# first header title
h3("MTCars"),
# prepare the first output table
DT::dataTableOutput('table1'),
)
)
server <- function(input, output, session) {
myData <- mtcars
myData$wheelDiameter <- myData$wt
myData$windscreenHeight <- myData$mpg
myData$carTint <- myData$vs
myData$color <- rep(c("red","black","green","yellow"),4)
colourWeights <- reactive({
highlightData <- datatable(myData,
options = list(
scrollX = TRUE,
autoWidth = FALSE,
dom = 'Blrtip'
)) %>% formatStyle(
'wt',
backgroundColor = styleInterval(c(1.5,3.0), c("red","yellow","green")),
fontWeight = 'bold'
)
return(highlightData)
})
# display the first output table
output$table1 <- DT::renderDataTable({
colourWeights()
})
}
shinyApp(ui, server)
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)
Suppose that I have the following code where I want to make the radioButtons filter a data.table on the 'vs' column= 0 or 1, is this possible?
I am looking to filter the renderDataTable (not mtcars) so that you can keep on using '_rows_selected' capabilities etc.
library(DT)
ui <- basicPage(
h2("The mtcars data"),
radioButtons("vs_radio", label="filter on vs",
c("0" = "0",
"1" = "1"), inline = TRUE),
DT::dataTableOutput("mytable")
)
server <- function(input, output) {
output$mytable = DT::renderDataTable({
mtcars
})
}
shinyApp(ui, server)
Assuming that you are interested in getting the original row index of the mtcars dataset, you can add an additional column to the mtcars with the row index. Later you can filter that new dataset using the radio button inside a reactive expression. You can use the options on the renderDataTable to hide the additional column. Finally, you can use the _rows_selected to retrieve the original row index by reading the additional column (row) of the reactive dataset. Maybe there is a better solution, but the code below could be useful.
library(shiny)
library(DT)
ui <- basicPage(
h2("The mtcars data"),
radioButtons("vs_radio", label="filter on vs",
c("0" = "0",
"1" = "1"), inline = TRUE),
DT::dataTableOutput("mytable"),
h4("Selected rows:"),
verbatimTextOutput("selectedRows")
)
server <- function(input, output) {
# add an additional column with the row index of the original dataset
dat <- mtcars
dat$row <- 1:nrow(dat)
# react to the radio button to filter dataset
filteredDat <- reactive({
vs <- as.numeric(input$vs_radio)
dat[dat$vs == vs, ]
})
output$mytable = DT::renderDataTable({
filteredDat()},
options = list(
# hide column "row"
columnDefs = list(list(targets = 12, visible = FALSE))
)
)
output$selectedRows <- renderPrint({
idx <- input$mytable_rows_selected
filteredDat()[idx, "row"]
})
}
shinyApp(ui, server)
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:
I am running into trouble using the tapply function. I am pulling two vectors from the same data frame which was created from a reactive variable. The first I am calling from a user inputted selection, and the second is one that I have created to keep my code generalisable and to use in my sort function. My sample code is shown below using the r-bloggers example. The data is here.
https://redirect.viglink.com/?format=go&jsonp=vglnk_150821851345614&key=949efb41171ac6ec1bf7f206d57e90b8&libId=j8v6cnh201021u9s000DAhzunvtas&loc=https%3A%2F%2Fwww.r-bloggers.com%2Fbuilding-shiny-apps-an-interactive-tutorial%2F&v=1&out=http%3A%2F%2Fdeanattali.com%2Ffiles%2Fbcl-data.csv&ref=https%3A%2F%2Fduckduckgo.com%2F&title=Building%20Shiny%20apps%20%E2%80%93%20an%20interactive%20tutorial%20%7C%20R-bloggers&txt=here
The error it throws is that they are not the same length, even though their attribute and class print outs are exactly the same.
I know that this is not the best code in the world, but I just threw together a quick example.
library(shiny)
library(tidyverse)
bcl <- read.csv("bcl-data.csv", stringsAsFactors = FALSE)
ui <- fluidPage(titlePanel("Sampling Strategies"),
sidebarLayout(
sidebarPanel(
selectInput("XDATA","xdata",
choices = c(names(bcl))),
selectInput("YDATA","ydata",
choices = c(names(bcl)))
),
mainPanel(
tabsetPanel(
tabPanel("The table",tableOutput("mytable"))
))
))
server <- function(input, output, session) {
filtered <- reactive({
bcl <- bcl %>% mutate(ID = 1:nrow(bcl))
})
output$mytable <- renderTable({
dataset <- filtered() %>% mutate(sampled = "white")
sample.rows <- sample(dataset$ID, 5, replace = FALSE)
dataset$sampled[sample.rows] <- "black"
final <- tapply(dataset[input$XDATA], list(dataset$sampled),mean)[["black"]]
return(final)
})
}
shinyApp(ui = ui, server = server)
Cheers
Edit* Sorry my bad, forgot to change over the drop list codes. All I am interested is one generic xdata vector that can be selected from the loaded data set. I then sample it, and want to find the mean value from the sampled indices.
One of the problems is in the subsetting. the [ still returns a data.frame. So, we need [[. If we look at ?tapply
tapply(X, INDEX, FUN = NULL, ..., default = NA, simplify = TRUE)
where
X is an atomic object, typically a vector
ui <- fluidPage(titlePanel("Sampling Strategies"),
sidebarLayout(
sidebarPanel(
selectInput("XDATA","xdata",
choices = c(names(bcl)[5:7])),
selectInput("YDATA","ydata",
choices = c(names(bcl)))
),
mainPanel(
tabsetPanel(
tabPanel("The table",tableOutput("mytable"))
))
))
server <- function(input, output, session) {
filtered <- reactive({
bcl <- bcl %>% mutate(ID = row_number())
})
output$mytable <- renderTable({
dataset <- filtered() %>% mutate(sampled = "white")
sample.rows <- sample(dataset$ID, 20, replace = FALSE)
dataset$sampled[sample.rows] <- "black"
final <- tapply(dataset[[input$XDATA]], list(dataset$sampled),mean, na.rm = TRUE, simplify = TRUE)
return(final)
})
}
shinyApp(ui = ui, server = server)
-output