Apply a reactive expression to another


#1

I want to provide an option to users of my app to first read in a datafile and then optionally read in a second data file that would apply some recoding to variables in a data frame read into the app. In R, my sample code would be similar to the following below:

Create sample data file. This would be file1

xx <- c(1,2,1,1,2,2,3,4,5,5,6,3,4,2,1,3,4)
yy <- rnorm(length(xx))
orig <- xx
other <- xx+10
dat <- data.frame(xx,yy, orig, other)

Create a file to read in containing transformation rules. This would be file2

tmp <- data.frame(key = c('xx','xx','xx', 'other'), before = c(1,2,3,11), after = c(9,15,81, 110))

Function to recode

recodeRule <- function(data, rules){
	for(i in 1:nrow(rules)){
		x <- which(colnames(data) == rules$key[i])
		data[,x][data[,x] == rules[i, 'before']] <- rules[i, 'after']
	}
	data
}

Apply rules

recodeRule(dat, tmp)

Now, suppose I have a very simple app similar to the one below. What I want to provide is file1 where the user would read in the data file and then file2 would be the file containing the recoding rules. But, this is optional and may not always be read in.

So, the user will always read in file1. My goal is if they read in file2 then it would apply the recoding function to the file1 read in originally and then return an updated file1. If they do not read in file2, then file1 remains unchanged.

Thanks in advance


ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      
      fileInput("file1", "Read Data",
        accept = c(
          "text/csv",
          "text/comma-separated-values,text/plain",
          ".csv")
        ),
       fileInput("file2", "Optional Recode File",
        accept = c(
          "text/csv",
          "text/comma-separated-values,text/plain",
          ".csv")
        )
    ),
    mainPanel(
      tableOutput("contents")
    )
  )
)

server <- function(input, output) {
  
  file1 <- reactive({
    inFile <- input$file1
    if (is.null(inFile))
      return(NULL)
    read.csv(inFile$datapath)
  })
  
  file2 <- reactive({
    inFile <- input$file2
    if (is.null(inFile))
      return(NULL)
    read.csv(inFile$datapath)
  })
    
  
  output$contents <- renderTable({
    head(file1())
  })  
 
 }

shinyApp(ui, server)

#2

Updating the server code as follows gives the expected result. Perhaps because file3() is evaluated only when its dependencies change this is the right way to do it? Other ideas?

server <- function(input, output) {

recodeRule <- function(data, rules){
	for(i in 1:nrow(rules)){
		x <- which(colnames(data) == rules$key[i])
		data[,x][data[,x] == rules[i, 'before']] <- rules[i, 'after']
	}
	data
}
  
  file1 <- reactive({
    inFile <- input$file1
    if (is.null(inFile))
      return(NULL)
    read.csv(inFile$datapath)
  })
  
  file2 <- reactive({
    inFile <- input$file2
    if (is.null(inFile))
      return(NULL)
    read.csv(inFile$datapath)
  })
  
 file3 <- reactive({
 if(!is.null(input$file2)){
 	recodeRule(file1(), file2())
 } else {
	file1() 
 }
})  
  
  output$contents <- renderTable({
    head(file3())
  })  
 
 }