Shiny R Input - Add to end of data frame continuously - r

I currently have the following shiny app and the intention is to add the text input as a new row into the dataset. Currently, the new text input is overriding the old input instead of appending to the end. I understand that's happening because I'm referring to df in the eventReactive, which is the originally seeded empty data frame, but my various attempts around that have led to errors.
Does somebody know of a way to fix that? Bonus if there is a way to clear the text input box after hitting the submit button.
library(shiny)
colClasses = c("factor", "numeric")
col.names = c("Player", "1")
df <- read.table(text = "",
colClasses = colClasses,
col.names = col.names)
ui <- fluidPage(
# Application title
titlePanel("Random Draft Order"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
textInput("AddPlayer",
"Add Player",
""),
actionButton("submit", ("Submit"))
),
# Show a plot of the generated distribution
mainPanel(
tableOutput("racingbars")
)
)
)
server <- function(input, output) {
actionButton("submit", ("Submit"))
FinalData = eventReactive(input$submit,{
df = rbind(df, data.frame("Player" = input$AddPlayer, "X1" = ""))
})
output$racingbars <- renderTable({
FinalData()
})
}
# Run the application
shinyApp(ui = ui, server = server)

You could try this creating a reactiveVal that will store the data.
First, you can initialize your data.frame df as a reactiveVal:
df <- reactiveVal(data.frame(Player = character(),
X1 = character()))
To update the reactiveVal you can use:
df(new_dat)
And to reference the data later on, use:
df()
In your example, an observeEvent can be triggered by your submit button. When that happens, you can add your row of data to the reactiveVal data.frame similar to what you have done. The output can then point to the reactiveVal data.frame to display the data.
I also added updateTextInput to the observeEvent to clear the text input after the submit button is pressed. Note this requires session in the server function declaration.
server <- function(input, output, session) {
df <- reactiveVal(data.frame(Player = character(),
X1 = character()))
observeEvent(input$submit, {
new_dat <- rbind(df(), data.frame(Player = input$AddPlayer, X1 = ""))
df(new_dat)
updateTextInput(session, "AddPlayer", value = "")
})
output$racingbars <- renderTable({
df()
})
}

Related

Unable to reset reactive rhandsontable observeEvent

I have been using R for awhile but R shiny seems like a completely foreign language to me.
I have an RShiny app utilizing the rhandsontable, which takes input from both another column in the rhandsontable and outside the rhandsontable to calculate a new column. Currently, the table does update everything when any entry from within the table is modified, but I'd like to minimize the risk of user error as much as possible, either by making the rhandsontable reactive to both changes inside and outside the table (preferred) or reset when a button is clicked.
The example below is modified from How to reset to default a reactive rhandsontable? but reproduced my issue. As soon as I add an observeEvent for the rhandsontable, even before I call the external input, the reset button no longer works.
To make the observeEvent reactive to both changes in the external input and the table changes, I tried to make a reactive input (e.g., listener <- reactive(c(input$two_by_two$changes$changes, input$reset_input))), put them both in the observeEvent (e.g., input$two_by_two$changes$changes | input$reset_input), which both result in Warning: Error in do.call: second argument must be a list error.
library(shiny)
library(rhandsontable)
server <- shinyServer(function(input, output, session) {
FACTOR <- reactive(input$factor_val)
DF <- data.frame(A = c(1, 2), B = c(2, 4), row.names = c("C", "D"))
DF1 <- reactiveValues(data=DF)
output$two_by_two <- renderRHandsontable({
input$reset_input
rhandsontable(DF1$data)
})
observeEvent(input$two_by_two$changes$changes,
{DF1$data <- hot_to_r(input$two_by_two)
DF1$data[,2] = DF1$data[,1]*FACTOR() }) })
ui <- shinyUI(fluidPage(
actionButton(inputId = "reset_input", label = "Reset"),
br(),
numericInput("factor_val","Multiplier Value",min=0,max=10,value=2),
rHandsontableOutput("two_by_two")
))
shinyApp(ui, server)
Thanks for any help again.
The reason you were not able to reset was that you had changed the reactive dataframe, but were never resetting it back. I added a new observe event to handle that. Please see if this is what you are looking for.
library(shiny)
library(rhandsontable)
server <- shinyServer(function(input, output, session) {
FACTOR <- reactive(input$factor_val)
DF <- data.frame(A = c(1, 2), B = c(2, 4), row.names = c("C", "D"))
DF1 <- reactiveValues(data=DF)
observeEvent(input$reset_input, {
DF1$data <- DF
})
output$two_by_two <- renderRHandsontable({
#input$reset_input
rhandsontable(DF1$data)
})
observeEvent(input$factor_val, {
req(input$factor_val)
DF1$data <- hot_to_r(req({input$two_by_two}))
DF1$data[,2] = DF1$data[,1]*FACTOR()
})
})
ui <- shinyUI(fluidPage(
actionButton(inputId = "reset_input", label = "Reset"),
br(),
numericInput("factor_val","Multiplier Value",min=0,max=10,value=2),
rHandsontableOutput("two_by_two")
))
shinyApp(ui, server)

How to create dynamic number of observeEvent in another observeEvent?

Here I asked an similar question and got a working answer. But the solution does not work if 'actionButton' of sub segment is replace by 'selectInput'. On each selection of selectInput creates two outputs. Please help.. Thanks....
library(shiny)
ui <- fluidPage(
verbatimTextOutput("txt",placeholder = T), #"It is Created for Testing"
actionButton("addSeg", "Add a Segment"),
uiOutput("myUI")
)
server <- function(input, output, session) {
alld <- reactiveValues()
alld$ui <- list()
# Action to add new Segment
observeEvent(input$addSeg,{
new_id <- length(alld$ui) + 1
sub_name <- paste0("addSub_", new_id)
alld$ui[[new_id]] <- list(selectInput(sub_name,"Add a variable", choices = c("V1","V2"), selected = NULL))
observeEvent(input[[sub_name]], {
new_text_id <- length(alld$ui[[new_id]]) + 1
alld$ui[[new_id]][[new_text_id]] <- HTML(paste0("Variable ",input[[sub_name]]," added<br>"))
}, ignoreInit = TRUE)
})
output$myUI <- renderUI({alld$ui})
output$txt <- renderText({class(alld$ui)})
}
shinyApp(ui, server)
This behaviour occurs because the custom UI element is re-rendered every time a new element is added to the list. Once you click "V2" and the new text element is added, the selectInput itself re-renders and resets to V1, which is noticed by the observer you've created.
The following might be a solution for you:
observeEvent(input$addSeg,{
new_id <- length(alld$ui) + 1
sub_name <- paste0("addSub_", new_id)
alld$ui[[new_id]] <- list(
selectInput(sub_name,
"Add a variable",
choices = c("", "V1","V2"),
selected = "")
)
observeEvent(input[[sub_name]], {
if (input[[sub_name]] == "") return()
new_text_id <- length(alld$ui[[new_id]]) + 1
alld$ui[[new_id]][[new_text_id]] <- HTML(paste0("Variable ",input[[sub_name]]," added<br>"))
}, ignoreInit = TRUE)
})
What I've done here is add an empty option to your selectInputs, and a condition to the corresponding observer that it shouldn't do anything if the input is empty. This way, I'm harnessing the "resetting" behaviour to be useful instead of annoying.

Updating a data frame in real time in RShiny

I am trying to get my head around RShiny by building what I thought would be a pretty simple but useful app. What I would like the app to do is allow the user to input some data made up of dates, numeric, and characters. Then when the user presses a save/submit button this data is appended onto a pre-existing data frame made up of previous recordings and over write the .csv of these recordings. I would also like this data to be presented to the users in the form of a table in the UI which is updated as soon as the user presses the save/submit button.
I have managed to get the majority of the UI features working, however, I am having real difficulty 1) saving the data in the correct format and 2) updating the table displayed on the UI. My current method of saving the data involves creating an isolated list of the input values and rbinding this to the original data frame. However, the formats of the input values appear to all revert to factors which is especially problematic with the date as the output is meaningless as far as I am aware. In terms of updating the UI I have attempted to create a reactive object out of the data frame and use this object as the data displayed in renderDataTable but this approach seems to have no affect.
I have created a dummy minimal example below.
Thank you for all your help in advance.
require(shiny)
require(tidyverse)
require(lubridate)
require(plotly)
#Would import the data in reality using read.csv() but to allow for an easily
#recreated example I made a dummy data frame
DateRecorded <- dmy(c("10/07/2018", "11/07/2018", "13/07/2018"))
Value <- c(1, 2, 3)
Person <- c("Bob", "Amy", "Charlotte")
df <- data.frame(DateRecorded, Value, Person)
ui <- fluidPage(
#UI Inputs
dateInput(inputId = "SessionDate", label = "Date Recorded", format = "dd-mm-yyyy"),
numericInput(inputId = "SessionValue", label = "Value Recorded", value = 0),
textInput(inputId = "SessionPerson", label = "Person Recording"),
actionButton(inputId = "Save", label = "Save"),
#UI Outputs
dataTableOutput("TheData"),
textOutput("TotRecorded")
)
# Define server logic required to draw a histogram
server <- function(input, output) {
#When "Save" is pressed should append data to df and export
observeEvent(input$Save, {
newLine <- isolate(c(input$SessionDate, input$SessionValue, input$SessionPerson))
isolate(df <- rbind(as.matrix(df), unlist(newLine)))
write.csv(df, "ExampleDataFrame.csv") #This export works but the date is saved incorrectly as "17729" not sure why
})
#Create a reactive dataset to allow for easy updating
ReactiveDf <- reactive({
df
})
#Create the table of all the data
output$TheData <- renderDataTable({
ReactiveDf()
})
#Create the totals print outs
output$TotRecorded <- renderPrint({
data <- ReactiveDf()
cat(nrow(data))
})
}
# Run the application
shinyApp(ui = ui, server = server)
I made some small tweaks.
You do not need isolate in the body of the observeEvent; it does not take a reactive dependency to values in its body.
I made ReactiveDf a reactiveVal instead of a reactive. This allows you to write its value from inside an observeEvent.
Instead of rowbinding a matrix and unlisting a list - the issue is that all the new values are parsed to the same class, while they are obviously not - it might be easier to rbind two data.frames, so create the newLine with newLine <- data.frame(DateRecorded = input$SessionDate, Value = input$SessionValue, Person = input$SessionPerson)
So a working example would be as shown below. Hope this helps!
require(shiny)
require(tidyverse)
require(lubridate)
require(plotly)
#Would import the data in reality using read.csv() but to allow for an easily
#recreated example I made a dummy data frame
DateRecorded <- dmy(c("10/07/2018", "11/07/2018", "13/07/2018"))
Value <- c(1, 2, 3)
Person <- c("Bob", "Amy", "Charlotte")
df <- data.frame(DateRecorded, Value, Person)
ui <- fluidPage(
#UI Inputs
dateInput(inputId = "SessionDate", label = "Date Recorded", format = "dd-mm-yyyy"),
numericInput(inputId = "SessionValue", label = "Value Recorded", value = 0),
textInput(inputId = "SessionPerson", label = "Person Recording"),
actionButton(inputId = "Save", label = "Save"),
#UI Outputs
dataTableOutput("TheData"),
textOutput("TotRecorded")
)
# Define server logic required to draw a histogram
server <- function(input, output) {
#When "Save" is pressed should append data to df and export
observeEvent(input$Save, {
newLine <- data.frame(DateRecorded = input$SessionDate, Value = input$SessionValue, Person = input$SessionPerson)
df <- rbind(df, newLine)
ReactiveDf(df) # set reactiveVal's value.
write.csv(df, "ExampleDataFrame.csv") #This export works but the date is saved incorrectly as "17729" not sure why
})
#Create a reactive dataset to allow for easy updating
ReactiveDf <- reactiveVal(df)
#Create the table of all the data
output$TheData <- renderDataTable({
ReactiveDf()
})
#Create the totals print outs
output$TotRecorded <- renderPrint({
data <- ReactiveDf()
cat(nrow(data))
})
}
# Run the application
shinyApp(ui = ui, server = server)

Shiny - renaming factors in reactive data frame

I'm building a shiny app that queries an SQL database so the user can ggplot the data. I would like the user to be able to rename factors manually but am struggling to get going. Here is an example of what I want to do:
ui.R
library(markdown)
shinyUI(fluidPage(
titlePanel("Reactive factor label"),
sidebarLayout(
sidebarPanel(
numericInput("wafer", label = h3("Input wafer ID:"), value = NULL),
actionButton("do", "Search wafer"),
textInput("text", label = h3("Factor name to change"), value = ""),
textInput("text", label = h3("New factor name"), value = ""),
actionButton("do2", "Change name")
),
mainPanel(
verbatimTextOutput("waf"),
verbatimTextOutput("que"),
verbatimTextOutput("pos"),
dataTableOutput(outputId="tstat")
)
)
)
)
server.R
# Create example data
Name <- factor(c("Happy", "New", "Year"))
Id <- 1:3
dd <- data.frame(Id, Name)
con <- dbConnect(RSQLite::SQLite(), ":memory:")
dbWriteTable(con, "dd", dd)
query <- function(...) dbGetQuery(con, ...)
wq = data.frame()
sq = data.frame()
shinyServer(function(input, output){
# create data frame to store reactive data set from query
values <- reactiveValues()
values$df <- data.frame()
# Wait for user to search
d <- eventReactive(input$do, { input$wafer })
# Reactive SQL query
a <- reactive({ paste0("Select * from dd where Id=",d()) })
wq <- reactive({ query( a() ) })
# Check outputs
output$waf <- renderPrint(input$wafer)
output$que <- renderPrint({ a() })
output$pos <- renderPrint( wq()[1,1] )
# observe d() so that data is not added until user presses action button
observe({
if (!is.null(d())) {
sq <- reactive({ query( a() ) })
# add query to reactive data frame
values$df <- rbind(isolate(values$df), sq())
}
})
output$tstat <- renderDataTable({
data <- values$df
})
})
In static R I would normally use data table to rename factors i.e.:
DT <- data.table(df)
DT[Name=="Happy", Name:="Sad"]
But I'm not sure how to go about this with a reactiveValues i.e. values$df.
I have read this (R shiny: How to get an reactive data frame updated each time pressing an actionButton without creating a new reactive data frame?). This lead me to try this but it doesn't do anything (even no error):
observeEvent(input$do2, {
DT <- data.table(values$df)
DT[Name == input$text1, Name := input$text2]
values$df <- data.frame(values$df)
})
Perhaps there is a way around this..maybe there is a way to use an action button to "lock in" the data as a new data frame, which can then be used to rename?
Sorry for such a long winded question. My real app is much longer and more complex. I have tried to strip it down.
Your approach works but there are a few issues in your app.
In ui.R, both textInput have the same id, they need to be different so you can refer to them in the server.R. In the observeEvent you posted, you refer to input$text1 and input$text2 so you should change the id of the textInputs to text1 and text2.
In the observeEvent you posted, the last line should be values$df <- as.data.frame(DT), otherwise it does not change anything.

Dynamic select input not working with loaded data

Using R shiny, I am developing a simple app that allows user to input data from a Rdata file. I want the app to load the data, show the names of numeric variables in a select input field, and after the user selected one of variables do some analysis. But I can not get it working. In the code provided I obtain two outputs: summary, which works fine, and the MEAN of the selected variable which I can not get work.
server.R
library(shiny)
library(shinydashboard)
library(data.table)
library(DT)
shinyServer(function(input, output) {
#### DATA LOAD
df <- reactive({
df <- input$datafile
if (is.null(df)) {
# User has not uploaded a file yet
return(NULL)
}
objectsLoaded <- load(input$datafile$name)
# the above returns a char vector with names of objects loaded
df <- eval(parse(text=objectsLoaded[1]))
# the above finds the first object and returns it
df<-data.table(df)
})
#### SELECTS
num <- reactive({
num <- sapply(df(),is.numeric)
num <- names(num)
})
output$var_num <- renderUI({
vector.num <- as.vector(num())
selectInput("var_num", "Select Variables :", as.list(vector.num), multiple = FALSE)
})
#### OUTPUTS
### SUMMARY
output$summary_num <-renderDataTable({
x<-t(sapply(df(), summary))
x<-as.data.frame(x)
x<-setDT(x, keep.rownames = TRUE)[]
colnames(x) <- c("Variable","Mínimo","1er Quartil", "Mediana", "Media", "3er Quartil","Máximo")
datatable(x)
})
### MEAN OF SELECTED VAR
output$test <-renderPrint ({
if(is.null(df()))
return()
dat<- df()
dat <- dat[,num(), drop = FALSE]
mean(dat[,input$var_num])
})
})
UI.R
dashboardPage(
dashboardHeader(title = "TITLE", titleWidth = 500),
dashboardSidebar(disable = TRUE), #---> fin SIDEBAR
dashboardBody(
fluidRow(
box(width=12, status = "primary",
tabsetPanel(
tabPanel("Test",
fileInput("datafile", label = h3("File input")),
uiOutput("var_num"),
br(),hr(),br(),
fluidRow(column(width=4, uiOutput("var_caracter"),textOutput("test"))),
br(),hr(),br(),
fluidRow(column(width=8, "Variables Numericas", dataTableOutput("summary_num")))
)
) # fin tabsetPanel
) # fin box
)# fin fluidRow
)# fin dashboardBody
)# fin dashboardPage
When I run the app everything goes fine (select input, summary, etc) except the calculation and printing of the MEAN of the selected variable. I guess for some reason the subsetted dataframe is empty, but I do not know why...
Any help will be great! Thanks in advance.
I get it working.
The solution was to define the dataset I used as.data.frame:
### MEAN OF SELECTED VAR
output$test <-renderPrint ({
if(is.null(df()))
return()
dat<- as.data.frame(df()) ## THIS IS THE CORRECTION
dat <- dat[,num(), drop = FALSE]
mean(dat[,input$var_num])
})
I do not really understand why... The reactive file df() was defined as data.table and dat shoul inherit that, but for some reason it was necesary an explicit definition as dataframe.

Resources