I am looking to find a way to add a button to a datatable in r shiny that:
Adds an empty column to the data table each time it is clicked (and dataframe that made the table in the first place
Which the user can fill himself
With numeric or text values
And set their own column name to describe the type of entry values.
To for instance add lab note records to instrument data that is already in the shiny app manually
I am asking in case a more skilled person who has a better clue than me knows how to do this.
I have read a bunch of pages, but at best the example I found provides 1 fixed empty column with a fixed name
empty column
A dummy table from the package :
library(DT)
ui <- basicPage(
h2("The mtcars data"),
DT::dataTableOutput("mytable")
)
server <- function(input, output) {
output$mytable = DT::renderDataTable({
mtcars
})
}
shinyApp(ui, server)
You can use a button to add a new column to a R shiny datatable like this:
ui <- fluidPage(
h2("The mtcars data"),
DT::dataTableOutput("mytable"),
textInput('NewCol', 'Enter new column name'),
radioButtons("type", "Column type:",
c("Integer" = "integer",
"Floating point" = "numeric",
"Text" = "character")),
actionButton("goButton", "Update Table")
)
server <- function(input, output) {
mydata <- mtcars
output$mytable = DT::renderDataTable(df())
df <- eventReactive(input$goButton, {
if(input$NewCol!="" && !is.null(input$NewCol) && input$goButton>0){
if (input$type == "integer") v1 <- integer(NROW(mydata))
if (input$type == "numeric") v1 <- numeric(NROW(mydata))
if (input$type == "character") v1 <- character(NROW(mydata))
newcol <- data.frame(v1)
names(newcol) <- input$NewCol
mydata <<- cbind(mydata, newcol)
}
mydata
}, ignoreNULL = FALSE)
}
shinyApp(ui,server)
If you also need to edit the contents of the cells interactively, you can use renderRHandsontable instead of renderDataTable. Like this:
library(rhandsontable)
ui <- fluidPage(
h2("The mtcars data"),
rHandsontableOutput("mytable"),
textInput('NewCol', 'Enter new column name'),
radioButtons("type", "Column type:",
c("Integer" = "integer",
"Floating point" = "numeric",
"Text" = "character")),
actionButton("goButton", "Update Table")
)
server <- function(input, output) {
mydata <- mtcars[1:5,]
output$mytable = renderRHandsontable(df())
df <- eventReactive(input$goButton, {
if(input$NewCol!="" && !is.null(input$NewCol) && input$goButton>0){
if (input$type == "integer") v1 <- integer(NROW(mydata))
if (input$type == "numeric") v1 <- numeric(NROW(mydata))
if (input$type == "character") v1 <- character(NROW(mydata))
newcol <- data.frame(v1)
names(newcol) <- input$NewCol
mydata <<- cbind(mydata, newcol)
}
rhandsontable(mydata, stretchH = "all")
}, ignoreNULL = FALSE)
observe(if (!is.null(input$mytable)) mydata <<- hot_to_r(input$mytable))
}
shinyApp(ui,server)
Related
I'm trying to set codes to recode in shiny web application. However, it doesn't work for me.
Here's my code.
library(shiny)
library(rlang)
library(dplyr)
ui <- fluidPage(
titlePanel("Short Form Web App"),
sidebarPanel(
numericInput("num1","previous vector", value = NULL),
numericInput("num2","post vector", value = NULL),
selectInput("var","select Variable",names(mtcars)),
textInput("new_var","new variable names")
),
mainPanel(
verbatimTextOutput("tab1"),
verbatimTextOutput("tab2"),
actionButton("do","Do")
)
)
server <- function(input, output) {
output$tab1 <- renderPrint({
table(mtcars[["cyl"]])
})
rv <- reactiveValues(data = NULL)
rv$data <- mtcars
observeEvent(input$do,{
new_var <- input$new_var
new <- rv$data %>% transmute(!!new_var := case_when(input$var == input$num1 ~ input$num2))
rv$data <- bind_cols(rv$data,new)
output$tab2 <- renderPrint({
str(rv$data)
})
})
}
shinyApp(ui,server)
What I'm trying to do is recode previous vector to new vector like recode, but the result keeps showing NA..
Could anyone help me fix this problem?
I would very be very appreciated with your helps.
Thank you in advance.
Two issues:
As input$var is character you first have to convert to a symbol, i.e. use !!sym(input$var)
In your case_when you missed to set a default value. Hence, all values not specified to be recoded will be assigned NA.
Try this:
library(shiny)
library(rlang)
library(dplyr)
ui <- fluidPage(
titlePanel("Short Form Web App"),
sidebarPanel(
numericInput("num1","previous vector", value = NULL),
numericInput("num2","post vector", value = NULL),
selectInput("var","select Variable",names(mtcars)),
textInput("new_var","new variable names")
),
mainPanel(
verbatimTextOutput("tab1"),
verbatimTextOutput("tab2"),
actionButton("do","Do")
)
)
server <- function(input, output) {
output$tab1 <- renderPrint({
table(mtcars[["cyl"]])
})
rv <- reactiveValues(data = NULL)
rv$data <- mtcars
observeEvent(input$do,{
new_var <- input$new_var
new <- rv$data %>% transmute(!!sym(new_var) := case_when(
!!sym(input$var) == input$num1 ~ as.double(input$num2),
TRUE ~ !!sym(input$var)))
rv$data <- bind_cols(rv$data,new)
output$tab2 <- renderPrint({
str(rv$data)
})
})
}
I am very new to Shiny and struggle to understand reactivity.
Context : I want user to choose a name for a column, add this column to a reactive table and then edit this table. The table is reactive (it comes from an uploaded file filtered by user).
Thanks to this answer everything work fine with a non-reactive table (see mydata <- mtcars[1:5,]).
But it doesn't work when mydata becomes reactive!
Here is a reproductible working example with NON-REACTIVE data from #dww answer:
library(rhandsontable)
ui <- fluidPage(
h2("The mtcars data"),
rHandsontableOutput("mytable"),
textInput('NewCol', 'Enter new column name'),
radioButtons("type", "Column type:",
c("Integer" = "integer",
"Floating point" = "numeric",
"Text" = "character")),
actionButton("goButton", "Update Table")
)
server <- function(input, output) {
mydata <- mtcars[1:5,]
output$mytable = renderRHandsontable(df())
df <- eventReactive(input$goButton, {
if(input$NewCol!="" && !is.null(input$NewCol) && input$goButton>0){
if (input$type == "integer") v1 <- integer(NROW(mydata))
if (input$type == "numeric") v1 <- numeric(NROW(mydata))
if (input$type == "character") v1 <- character(NROW(mydata))
newcol <- data.frame(v1)
names(newcol) <- input$NewCol
mydata <<- cbind(mydata, newcol)
}
rhandsontable(mydata, stretchH = "all")
}, ignoreNULL = FALSE)
observe(if (!is.null(input$mytable)) mydata <<- hot_to_r(input$mytable))
}
shinyApp(ui,server)
I have unsuccessfully tried these changes inside the code (basically I have changed all mydata for mydata()):
server <- function(input, output) {
# mydata <- reactive({ }) #make mydata a reactive object
output$mytable = renderRHandsontable(df())
df <- eventReactive(input$goButton, {
if(input$NewCol!="" && !is.null(input$NewCol) && input$goButton>0){
if (input$type == "integer") v1 <- integer(NROW(mydata()))
if (input$type == "numeric") v1 <- numeric(NROW(mydata()))
if (input$type == "character") v1 <- character(NROW(mydata()))
newcol <- data.frame(v1)
names(newcol) <- input$NewCol
mydata <<- cbind(mydata(), newcol)
}
rhandsontable(mydata(), stretchH = "all")
}, ignoreNULL = FALSE)
observe(if (!is.null(input$mytable)) mydata() <<- hot_to_r(input$mytable))}
I did not find this question answers/comments useful to answer my problem).
Could you explain how to use a reactive mydata inside #dww awesome answer?
[EDIT : title updated to better fit the answer]
I trimmed some extra features, like column data types... As a general rule - anything which you'd be rendering, can become reactive just by wrapping it in "reactive". Below I use "reactiveValues" but other reactive methods would work too.
A generalised way of making your output reactive to changes in the data's input -
foo_func = function() return(mydata)
foo_func_reactive = reactive(foo_func)
output$foo = renderMethod( foo_func_reactive() )
For your example:
shinyApp(
ui = fluidPage(
rHandsontableOutput("out_tbl"),
textInput(inputId = "in_txt", label = "New column name"),
actionButton(inputId = "in_btn1", label = "Add new column to the table above ..."),
actionButton(inputId = "in_btn2", label = "... Or, generate new data")
),
server = function(input, output, session) {
# establishes tbl_react as the holder for our reactive data, and pre-fills it for the first display with 1,2,3
tbl_react <- reactiveValues(tbl =
data.frame(a = c(1,2,3))
)
# button one adds a new column with the inputted name
observeEvent(input$in_btn1,{
newcolname <- as.character(input$in_txt)
newcol <- character(NROW(tbl_react$tbl))
tbl_react$tbl <- cbind(tbl_react$tbl, newcol)
colnames(tbl_react$tbl) <- c(colnames(tbl_react$tbl)[1:ncol(tbl_react$tbl)-1], newcolname)
})
# to show our output data is reactive, we can take a dependancy on button two to generate new data - this could instead be using an uploaded file
observeEvent(input$in_btn2,{
tbl_react$tbl <- data.frame(b = c(9,10,11))
})
output$out_tbl = renderRHandsontable( rhandsontable(tbl_react$tbl) )
}
)
I have these data I want to scatter plot using an R shiny server:
library(dplyr)
library(permute)
set.seed(1)
meta.df <- data.frame(gene_id=paste0("id",1:10),symbol=paste0("n",rep(permute::shuffle(5),2)),stringsAsFactors=F)
clusters.df <- data.frame(cell=paste0("c",1:100),cluster=rep(permute::shuffle(10),10),sample=paste0("s",rep(permute::shuffle(5),20)),stringsAsFactors=F)
mat <- matrix(rnorm(10*100),10,100,dimnames=list(meta.df$gene_id,clusters.df$cell))
tsne.obj <- Rtsne::Rtsne(t(mat))
tsne.df <- as.data.frame(tsne.obj$Y) %>% dplyr::rename(tSNE1=V1,tSNE2=V2) %>% cbind(clusters.df)
samples <- c("all",unique(clusters.df$sample))
samples.choices <- 1:length(samples)
names(samples.choices) <- samples
Since I want to be able to select a specific meta.df$symbol, which is redundant within meta.df$gene_id, each has a selection list, where the second is conditioned on the first.
Since the data are comprised of several samples, I'd like to be able to subset the data by sample in a reactive way, hence I have a sample choice checkbox, with the "all" option that selects all samples (just coz it's easier than checking all boxes).
So here's my shiny code:
server <- function(input, output)
{
chosen.samples <- reactive({
validate(
need(input$samples.choice != "",'Please choose at least one of the sample checkboxes')
)
samples.choice <- input$samples.choice
if("all" %in% samples.choice) samples.choice <- samples[-which(samples == "all")]
samples.choice
})
output$gene_id <- renderUI({
selectInput("gene_id", "Gene ID", choices = unique(dplyr::filter(meta.df,symbol == input$symbol)$gene_id))
})
scatter.plot <- reactive({
if(!is.null(input$symbol) & !is.null(input$gene_id)){
# subset of data
gene.symbol <- input$symbol
gene.id <- input$gene_id
row.idx <- which(rownames(mat) == gene.id)
col.idx <- which(colnames(mat) %in% dplyr::filter(clusters.df,sample %in% chosen.samples())$cell)
gene.df <- suppressWarnings(dplyr::left_join(tsne.df %>% dplyr::filter(sample %in% chosen.samples()),data.frame(cell=colnames(mat)[col.idx],value=mat[row.idx,col.idx],stringsAsFactors=F),by=c("cell"="cell")))
scatter.plot <- plotly::plot_ly(marker=list(size=12),type='scatter',mode="markers",color=~gene.df$value,x=~gene.df$tSNE1,y=~gene.df$tSNE2,showlegend=F) %>%
plotly::layout(xaxis=list(title="tSNE1",zeroline=F,showticklabels=F),yaxis=list(title="tSNE2",zeroline=F,showticklabels=F))
scatter.plot
}
})
output$Embedding <- renderPlot({
scatter.plot()
})
output$save <- downloadHandler(
filename = function() {
paste0(dplyr::filter(meta.df,symbol == input$symbol,gene_id == input$gene_id)$symbol,"_",dplyr::filter(meta.df,symbol == input$symbol,gene_id == input$gene_id)$gene_id,".pdf")
},
content = function(file) {
plotly::export(scatter.plot(),file=file)
}
)
}
ui <- fluidPage(
# App title ----
titlePanel("Results Explorer"),
# Sidebar layout with a input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
# select samples
checkboxGroupInput("samples.choice", "Samples",choices = samples.choices,selected=1),
# select gene symbol
selectInput("symbol", "Gene Symbol", choices = unique(meta.df$symbol)),
# select gene id
uiOutput("gene_id"),
# select plot type
selectInput("plot.type", "Plot Type", choices = c("tSNE","PCA")),
# save plot as html
downloadButton('save', 'Save as PDF')
),
# Main panel for displaying outputs ----
mainPanel(
# The plot is called Embedding and will be created in ShinyServer part
plotOutput("Embedding")
)
)
)
shinyApp(ui = ui, server = server)
The problem is that it doesn't seem to actually select the samples, and hence the plot that is displayed has no points.
It works find if I simply eliminate the samples selection code by replacing:
col.idx <- which(colnames(mat) %in% dplyr::filter(clusters.df,sample %in% chosen.samples())$cell)
gene.df <- suppressWarnings(dplyr::left_join(tsne.df %>% dplyr::filter(sample %in% chosen.samples()),data.frame(cell=colnames(mat)[col.idx],value=mat[row.idx,col.idx],stringsAsFactors=F),by=c("cell"="cell")))
with:
col.idx <- which(colnames(mat) %in% dplyr::filter(clusters.df,sample %in% samples[2:3])$cell)
gene.df <- dplyr::left_join(tsne.df %>% dplyr::filter(sample %in% samples[2:3]),data.frame(cell=colnames(mat)[col.idx],value=mat[row.idx,col.idx],stringsAsFactors=F),by=c("cell"="cell"))
I see that in this example the entire data are subsetted in the dat_reac reactive block. I'd expect simply getting the samples to subset by would be enough. Any idea why it doesn't work and how to get it right?
There are two mistakes in your code. The first one is in checkboxGroupInput
Instead of
checkboxGroupInput("samples.choice", "Samples",choices = samples.choices,selected=1)
it should be
checkboxGroupInput("samples.choice", "Samples",choices = names(samples.choices),selected="all")
The second is scatter.plot() is plotly object hence you should use plotly::plotlyOutput("Embedding") and output$Embedding <- plotly::renderPlotly({
scatter.plot()
})
Here is the code with above modification which should work:
server <- function(input, output)
{
chosen.samples <- reactive({
validate(
need(input$samples.choice != "",'Please choose at least one of the sample checkboxes')
)
samples.choice <- input$samples.choice
if("all" %in% samples.choice) samples.choice <- samples[-which(samples == "all")]
samples.choice
})
output$gene_id <- renderUI({
selectInput("gene_id", "Gene ID", choices = unique(dplyr::filter(meta.df,symbol == input$symbol)$gene_id))
})
scatter.plot <- reactive({
if(!is.null(input$symbol) & !is.null(input$gene_id)){
# subset of data
gene.symbol <- input$symbol
gene.id <- input$gene_id
row.idx <- which(rownames(mat) == gene.id)
col.idx <- which(colnames(mat) %in% dplyr::filter(clusters.df,sample %in% chosen.samples())$cell)
gene.df <- suppressWarnings(dplyr::left_join(tsne.df %>% dplyr::filter(sample %in% chosen.samples()),data.frame(cell=colnames(mat)[col.idx],value=mat[row.idx,col.idx],stringsAsFactors=F),by=c("cell"="cell")))
scatter.plot <- plotly::plot_ly(marker=list(size=12),type='scatter',mode="markers",color=~gene.df$value,x=~gene.df$tSNE1,y=~gene.df$tSNE2,showlegend=F) %>%
plotly::layout(xaxis=list(title="tSNE1",zeroline=F,showticklabels=F),yaxis=list(title="tSNE2",zeroline=F,showticklabels=F))
scatter.plot
}
})
output$Embedding <- plotly::renderPlotly({
scatter.plot()
})
output$save <- downloadHandler(
filename = function() {
paste0(dplyr::filter(meta.df,symbol == input$symbol,gene_id == input$gene_id)$symbol,"_",dplyr::filter(meta.df,symbol == input$symbol,gene_id == input$gene_id)$gene_id,".pdf")
},
content = function(file) {
plotly::export(scatter.plot(),file=file)
}
)
}
ui <- fluidPage(
# App title ----
titlePanel("Results Explorer"),
# Sidebar layout with a input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
# select samples
checkboxGroupInput("samples.choice", "Samples",choices = names(samples.choices),selected="all"),
# select gene symbol
selectInput("symbol", "Gene Symbol", choices = unique(meta.df$symbol)),
# select gene id
uiOutput("gene_id"),
# select plot type
selectInput("plot.type", "Plot Type", choices = c("tSNE","PCA")),
# save plot as html
downloadButton('save', 'Save as PDF')
),
# Main panel for displaying outputs ----
mainPanel(
# The plot is called Embedding and will be created in ShinyServer part
# plotOutput("Embedding")
plotly::plotlyOutput("Embedding")
)
)
)
shinyApp(ui = ui, server = server)
Hope it helps!
Below a simplified version of my shiny app. I looked through some of the examples in the shinyjs package and I did not find anything that could help me.
I want to disable the Submit button if one of the data frame uploaded (in my real example) or selected has a specific column name (Col 3 in the example below).
Can this be done with shinyjs?
library(rhandsontable)
library(shiny)
library(shinyjs)
df1 <- data.frame(col1 = rnorm(20),
col2 = rep(T, 20))
df2 <- data.frame(col1 = rnorm(20),
col2 = rep(F, 20),
col3 = rnorm(20))
server <- function(input, output) {
values = reactiveValues()
values[["df1"]] <- df1
values[["df2"]] <- df2
df <- reactive({
if (input$df == "df1") {
df <- values[["df1"]]
} else {
df <- values[["df2"]]
}
df
})
observeEvent(input$Submit, {
shinyjs::alert("Thank you!")
})
#observe({
# if (is.null(input$df) || input$df == "df1") {
# shinyjs::disable("submit")
#} else {
# shinyjs::enable("submit")
#}
#})
output$out <- renderRHandsontable({
hot <- rhandsontable(df())
hot
})
}
ui <- fluidPage(
shinyjs::useShinyjs(),
sidebarLayout(sidebarPanel(
selectInput(
'df', 'Select data.frame:',
choices = c('df1', 'df2'),
selected = 'df1'
),
actionButton("Submit", label = "Submit")
),
mainPanel(rHandsontableOutput("out"))))
shinyApp(ui = ui, server = server)
First, there is a small typo: Notice the capital "S".
shinyjs::disable("Submit")
Edit: To check for "col3" take the following code:
observe({
if (is.null(input$df) || sum(colnames(df()) == "col3")) {
shinyjs::disable("Submit")
}else{
shinyjs::enable("Submit")
}
})
Same for enable of course.
I have created a shinyapp, with tabs, that takes a dataframe and allows the user to filter it - then create a histogram based on the filters. This app works great when I run it on my local machine. However, When I publish it to shinyapps.io I get the following error:
ERROR: object of type 'closure' is not subsettable
I understand from other posts that the dataframe needs to be reactive or may need to be reactive? However when I enclose the data frame in reactive like so:
dat <- reactive(df)
I get the same error.
The files are located at github
or here:
library(shiny)
library(DT)
#server.R
shinyServer(function(input, output) {
df <- read.csv('orioles.csv')
output$table <- DT::renderDataTable(DT::datatable({
dat <- df
if (input$opp != "All") {
dat <- dat[dat$Opponent == input$opp,]
}
if (input$prk != "All") {
dat <- dat[dat$parkId == input$prk,]
}
if (input$strt != "All") {
dat <- dat[dat$Day.or.Night == input$strt,]
}
dat
}))
output$plot1 <- renderPlot({
dat <- df
if (input$opp != "All") {
dat <- dat[dat$Opponent == input$opp,]
}
if (input$prk != "All") {
dat <- dat[dat$parkId == input$prk,]
}
if (input$strt != "All") {
dat <- dat[dat$Day.or.Night == input$strt,]
}
hist(dat$BAL_SCORE, main = "Based on Filters", xlab = "Runs", ylab = "Frequency of runs scored", breaks = 20, col = "orange" )
})
})
UI:
library(shiny)
library(DT)
navbarPage(
title = 'Navigation',
tabPanel('Data Table', fluidPage(
titlePanel("Baltimore Orioles Game Results from 2010-2015"),
# Create a new Row in the UI for selectInputs
fluidRow(
column(4,
selectInput("opp",
"Opponent:",
c("All",
unique(as.character(df$Opponent))))
),
column(4,
selectInput("prk",
"Ball Park:",
c("All",
unique(as.character(df$parkId))))
),
column(4,
selectInput("strt",
"Day or Night Game:",
c("All",
unique(as.character(df$Day.or.Night))))
)
),
# Create a new row for the table.
fluidRow(
DT::dataTableOutput("table")
)
)),
tabPanel('Runs', fluidPage(
titlePanel("Runs Scored in filtered games"),
fluidRow(
column(6,
plotOutput("plot1", width = 800, height = 600)
)
)
))
)
I was able to get my App Working. The error was occuring because the data frame was not loading the way I had it set up. Solution is posted on github link shown above.
You'll get a more concise code using reactive and combining your subsetting conditions:
shinyServer(function(input, output) {
df <- read.csv('orioles.csv')
dat <- reactive(
df[(input$opp == "All" | df$Opponent == input$opp) &
(input$prk != "All" | df$parkId == input$prk) &
(input$strt != "All"| df$Day.or.Night == input$strt),])
output$table <- DT::renderDataTable(DT::datatable(dat()))
output$plot1 <- renderPlot({
hist(dat()$BAL_SCORE, main = "Based on Filters", xlab = "Runs",
ylab = "Frequency of runs scored", breaks = 20, col = "orange" )
})
})