Replace non-reactive data with reactive data - r

I have a complicated shiny app where I have used '<-' to create new columns on a non-reactive data.something like:
# Without reactive expression
ui <- fluidPage(tableOutput("contents"))
server1 <- function(input, output) {
myData <- mtcars
myData$New1 <- rep("NV1", nrow(myData))
myData$New2 <- rep("NV2", nrow(myData))
myData$New3 <- rep("NV3", nrow(myData))
myData$New4 <- rep("NV4", nrow(myData))
output$contents <- renderTable(myData)
}
shinyApp(ui, server1)
Now I want to replace the non-reactive data with reactive. How can I do that with minimal change?
I have tried something like:
# With reactive expression
ui <- fluidPage(tableOutput("contents"))
server2 <- function(input, output) {
all <- reactiveValues()
all$d <- mtcars
all$d$New1 <- rep("NV1", nrow(all$d))
all$d$New2 <- rep("NV2", nrow(all$d))
all$d$New3 <- rep("NV3", nrow(all$d))
all$d$New4 <- rep("NV4", nrow(all$d))
output$contents <- renderTable(all$d)
}
shinyApp(ui, server2)

You could do something like this:
ui <- fluidPage(tableOutput("contents"))
server2 <- function(input, output) {
# you can assign variables in reactiveValues(), no need to make a new line
all <- reactiveValues(d = mtcars)
observeEvent(all$d, { # update all$d, after it is created
req(!is.null(all$d))
all$d$New1 <- rep("NV1", nrow(all$d))
all$d$New2 <- rep("NV2", nrow(all$d))
all$d$New3 <- rep("NV3", nrow(all$d))
all$d$New4 <- rep("NV4", nrow(all$d))
})
output$contents <- renderTable(all$d)
}

Related

Rename Colnames from dataframe

I am making a Shiny App and I would like to rename the first variable from dataframe, to make after a corrplot.
In normal R the code is:
library(lares)
names(Dataset)[1] <- "DR"
corr_var(Dataset, DR, top=20)
And in Shiny I have something:
dataReg2 = reactive({
inFile <- input$fileReg
if (is.null(inFile))
return(NULL)
else
data1 = read_excel(inFile$datapath)
return(data1)
})
plot=reactive({
names(dataReg2())[[1]]='DR'
corr_var(dataReg2(), DR , top = 20 )
})
But it doesn't work, the error is invalid (NULL) left side of assignment...
Thank you in advance.
You cannot change the column names of reactive object. Copy the data in another variable and you can change the column name of that variable. See this simple example using mtcars.
library(shiny)
ui <- fluidPage(
tableOutput('tab')
)
server <- function(input, output) {
data <- reactive(mtcars)
output$tab <- renderTable({
new_table <- data()
names(new_table)[1] <- 'new'
head(new_table)
})
}
shinyApp(ui, server)

Can I create a bunch of reactive input-output pairs from a list in Shiny?

In Shiny, is it possible to create several reactive input-output pairs from a vector/list of strings using a functional?
i.e something like
id_list <- c("ID_1", "ID_2", "ID_3")
server <- function(input, output) {
lapply(id_list, function(x) quote(output$as.character(x) <- renderText({ input$x })))
}
The result would in effect be
output$ID_1 <- renderText({ input$ID_1 })
output$ID_2 <- renderText({ input$ID_2 })
output$ID_2 <- renderText({ input$ID_2 })
etc. for all items in the list, with each working in a reactive manner in the server function.
server <- function(input, output) {
for(id in id_list){
local({
localId <- id
output[[localId]] <- renderText({
input[[localId]]
})
})
}
}

How to change a value in a reactive tibble in R Shiny

I would like to update the content of a reactive object that is holding a tibble in response to a button push and I can't figure out the syntax. The solution that was posted here contains a solution that used to work but now it throws an error.
Below is a reprex of the issue I am having. Run the write.csv(iris, "text.csv") first.
library(shiny)
library(tidyverse)
# create the test data first
# write.csv(iris, "text.csv")
server <- shinyServer(function(input, output) {
in_data <- reactive({
inFile <- input$raw
x <- read.csv(inFile$datapath, header=TRUE)
})
subset <- reactive({
subset <- in_data() %>%
filter(Species == "setosa")
})
observeEvent(input$pushme, {
subset()$Sepal.Length[2] <- 2
})
output$theOutput <- renderTable({
subset()
})
})
ui <- shinyUI(
fluidPage(
fileInput('raw', 'Load test.csv'),
actionButton("pushme","Push Me"),
tableOutput('theOutput')
)
)
shinyApp(ui,server)
My code to change the value:
subset()$Sepal.Length[2] <- 2
Throws this error:
Error in <-: invalid (NULL) left side of assignment
What is the syntax for programmatically changing the value in a reactive tibble?
You can't modify directly the value of a reactive object. You have to define first a static object that will take the value of the reactive object, and then you can modify the value of the static object. Two options for you (no modifications in ui):
The first one is to use renderTable just after having subseted your data, and then to modify your table inside observeEvent:
server <- shinyServer(function(input, output) {
in_data <- reactive({
inFile <- input$raw
x <- read.csv(inFile$datapath, header=TRUE)
})
test1 <- reactive({
data <- in_data() %>%
filter(Species == "setosa")
data
})
output$theOutput <- renderTable({
req(input$raw)
test1()
})
observeEvent(input$pushme, {
output$theOutput <- renderTable({
req(input$raw)
test1 <- test1()
test1$Sepal.Length[2] <- 2
test1
})
})
})
The second one is to define another dataset (test2 here) that will be computed with eventReactive only if the button is pushed. Then you have to define which of the two datasets you want to use in renderTable using conditions on their existence:
server <- shinyServer(function(input, output) {
in_data <- reactive({
inFile <- input$raw
x <- read.csv(inFile$datapath, header=TRUE)
})
test1 <- reactive({
data <- in_data() %>%
filter(Species == "setosa")
data
})
test2 <- eventReactive(input$pushme, {
test1 <- test1()
test1$Sepal.Length[2] <- 2
test1
}
)
output$theOutput <- renderTable({
if (input$pushme) {
test2()
}
else {
req(input$raw)
test1()
}
})
})
By the way, you shouldn't call datasets (reactive or not) like function names. In your example, you have called subset the reactive dataset you modify. This is not good since this dataset will be used as subset() (because it is reactive). It may be confusing both for you and for the execution of the code.

keep selected rows when changing dataset in shiny DT datatable

I am using the DT package to display a data table in my shiny app. Since I provide different data sets, I have radio buttons to select them and the data table updates automatically.
What I would like to do is to preselect the available rows from df1 in df2 when switching the datasets. At the moment, my selection always get erased. When I try to save the selected rows (uncomment the two rows), my table get reset directly.
library(shiny)
library(DT)
df1 <- data.frame(names=letters,
values=1:26)
df2 <- data.frame(names=letters,
values=(1:26)*2)[seq(1,26,2),]
ui <- shinyUI(
fluidPage(
sidebarLayout(
sidebarPanel(
radioButtons("dataset", label=h5("Select dataset"),
choices=list("df1"='df1',
"df2"='df2'),
selected='df1', inline=TRUE)
),
mainPanel(
DT::dataTableOutput("name_table")
)
)
)
)
Server side...
server <- function(input, output, session) {
getDataset <- reactive({
result <- list()
result[['dataset']] <- switch(input$dataset,
'df1'=df1,
'df2'=df2)
# result[['selection']] <-
# as.numeric(input$name_table_rows_selected)
return(result)
})
output$name_table <- DT::renderDataTable({
DT::datatable(getDataset()[['dataset']],
options=list(pageLength=5))
})
name_proxy = DT::dataTableProxy('name_table')
}
shinyApp(ui, server)
I used the DT table, since I need the proxy and some interaction with the data table.
You can save selected rows only when going to change df like
server <- function(input, output, session) {
dd=reactiveValues(select=NULL)
observeEvent(input$dataset,{
dd$select=as.numeric(isolate(input$name_table_rows_selected))
})
getDataset <- reactive({
result <- list()
result[['dataset']] <- switch(input$dataset,
'df1'=df1,
'df2'=df2)
return(result)
})
output$name_table <- DT::renderDataTable({
DT::datatable(getDataset()[['dataset']],
options=list(pageLength=5),
selection = list(mode = 'multiple', selected =dd$select )
)
})
name_proxy = DT::dataTableProxy('name_table')
}
shinyApp(ui, server)
Or a bit modification of #drmariod variant: use eventReactive instead of reactive
server <- function(input, output, session) {
getDataset <- eventReactive(input$dataset,{
result <- list()
result[['dataset']] <- switch(input$dataset,
'df1'=df1,
'df2'=df2)
result[['selection']] <- testing()
return(result)
})
testing <- function() {
list(selected=as.numeric(input$name_table_rows_selected))
}
output$name_table <- DT::renderDataTable({
DT::datatable(getDataset()[['dataset']],
options=list(pageLength=5),
selection=getDataset()[['selection']])
})
name_proxy = DT::dataTableProxy('name_table')
}
Hm, it looks like I found a solution, but I wonder if there is a better solution.
server <- function(input, output, session) {
getDataset <- reactive({
result <- list()
result[['dataset']] <- switch(input$dataset,
'df1'=df1,
'df2'=df2)
result[['selection']] <- testing()
return(result)
})
testing <- function() {
list(selected=as.numeric(input$name_table_rows_selected))
}
output$name_table <- DT::renderDataTable({
DT::datatable(getDataset()[['dataset']],
options=list(pageLength=5),
selection=getDataset()[['selection']])
})
name_proxy = DT::dataTableProxy('name_table')
}
I wonder, sometimes comes a processing message. and on each click the table shortly "blinks"... Would be great to get a better answer.

The best way to get a data frame from user in shiny

I am trying to create a shiny app where I want the user to enter a few (but variable number of) rows of a data frame (with 3 columns). The best way would be to have the user enter a row at a time, and perhaps push a button to create a new row.
What is an intuitive way to implement this in a shiny gui?
You can do something like this:
rm(list = ls())
library(shiny)
# Sample data
my_data <- data.frame(matrix(1,nrow=1,ncol=3))
colnames(my_data) <- c("one","two","three")
emptry_row <- as.data.frame(matrix(1,nrow=1,ncol=3))
colnames(emptry_row) <- colnames(my_data)
ui =fluidPage(
sidebarPanel(actionButton("add_row", "Add a row")),
mainPanel(dataTableOutput("my_table"))
)
server = function(input, output, session){
values <- reactiveValues()
values$df <- my_data
newEntry <- observe({
if(input$add_row > 0) {
isolate(values$df <- rbind(values$df, emptry_row))
}
})
output$my_table <- renderDataTable({
if(input$add_row==0){return(values$df)}
values$df
})
}
runApp(list(ui = ui, server = server))

Resources