Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R/Shiny - How to write a .csv reactively including a actionButton?

Tags:

r

csv

shiny

I am trying to figure out how to write a .csv based on selections made by the end user. The selections made will subset the "geodata.csv" and write a separate "solution.csv" file in the application folder.

N.B. - I have created a github repo to make solving the question easier. It contains the geodata.csv, ui.R & server.R but no solution.csv yet!

geodata.csv

Postcode,HC,BSL,Position Location
10,1,A,C
10,1,A,D
10,1,A,D
11,1,B,C
11,1,B,C

ui.R

shinyUI(
 pageWithSidebar(
  headerPanel('Min. working example - write a csv based on user input'),

  sidebarPanel(
   selectInput("filter1", "First selection:" 
              , choices = c(Choose='', "A", "B")
              #, multiple=T
              ),

   selectInput("filter2", "Second selection:", 
              choices = c(Choose='', "C", "D")
              ),
   br(),
   p("Include actionButton to prevent write occuring before user finalises selection"),  
   actionButton("generateButton","Write Data")    
  ),
  mainPanel()
 )
)

server.R

# Load data
setwd("/Users/lukesingham/SOtestApp")
geodata <- read.csv("geodata.csv", na.string = "#N/A", row.names=NULL)

# Reactivity to subset data ####
shinyServer(function(input, output) {
  geodatasetInput <- reactive({

    # BSL switch
    selection <-switch(input$BSL
                       , A = "A"
                       , B = "B"
                       ) 
    # Location switch    
    selection2 <-switch(input$Location
                       , C = "C"
                       , D = "D"
                       )

    # subset based on selection
    Subgeodata <- subset(geodata, BSL == selection & Position.Location == selection2)

    # Execute selections on data upon button-press
    input$generateButton

    # aggregate by postcode
    Subgeodata <- Subgeodata[1:2] #no longer need other columns
    AggSubGdata <- aggregate(. ~ Postcode, data=Subgeodata, FUN=sum)
    isolate(write.csv(AggSubGdata
              , file = "/Users/lukesingham/SOtestApp/solution.csv"
              , row.names=F
    ))
  })
})

solution.csv

For example, based on user selections of A and D the solution file should look like this:

Postcode,HC
10,2
like image 609
Luke Singham Avatar asked Dec 30 '25 08:12

Luke Singham


2 Answers

Here is the working example:

# Load data
setwd("/Users/lukesingham/SOtestApp")
geodata <- read.csv("geodata.csv", na.string = "#N/A", row.names=NULL)

# Reactivity to subset data ####
shinyServer(function(input, output) {
  geodatasetInput <- observe({

    # Execute selections on data upon button-press
    if(input$generateButton == 0) return()

    inp.BSL <- isolate(input$filter1)
    inp.loc <- isolate(input$filter2)
    if (inp.BSL=='' | inp.loc=='') return()

    # BSL switch
    selection <-switch(inp.BSL
                       , A = "A"
                       , B = "B"
                       ) 
    # Location switch    
    selection2 <-switch(inp.loc
                       , C = "C"
                       , D = "D"
                       )

    # subset based on selection
    Subgeodata <- subset(geodata, BSL == selection & Position.Location == selection2)

    # browser()
    # aggregate by postcode
    Subgeodata <- Subgeodata[1:2] #no longer need other columns
    AggSubGdata <- aggregate(. ~ Postcode, data=Subgeodata, FUN=sum)
    write.csv(AggSubGdata
              , file = "solution.csv"
              , row.names=F
    )
  })
})

and a short analysis of your code:

  1. The main reason geodatasetInput does not start at all is because it is a reactive() expression. reactive() is evaluated only when it is called by something else like renderTable() in the answer of @pops. If you want it to be executed by itself, it should be observe().

  2. It might be a good idea to have input$generateButton in the beginning of your observe() expression.

  3. In ui.R, you call a numeric input field filter1, but you try to obtain its value as input$BSL from server.R; the same is true for filter2.

  4. As you want geodataSetInput to be triggered only on generateButton, all other input$ and reactive expressions withing geodataSetInput should be isolated with isolate(). On the other hand, there is no need to isolate write.csv because this particular function call does not involve any 'dynamic' parameters.

like image 189
Marat Talipov Avatar answered Dec 31 '25 20:12

Marat Talipov


you would need to Render your the table you're subsetting, perhaps someone else can explain why you need this co-dependance. Below is the working example of the way I went to solve it. It will save the solution.csv in the working directory.

rm(list = ls())
library(shiny)
# You can change this directory
setwd("/Users/lukesingham/SOtestApp")
geodata <- read.csv("geodata.csv", header = TRUE, sep = ",",stringsAsFactors =FALSE)

ui = pageWithSidebar(
  headerPanel('Min. working example - write a csv based on user input'),
  sidebarPanel(
    selectInput("filter1", "First selection:", choices = c("A", "B"),selected = "A"),    
    selectInput("filter2", "Second selection:", choices = c("C", "D"),selected = "C"),
    br(),
    p("Include actionButton to prevent write occuring before user finalises selection"),  
    actionButton("generateButton","Write Data")),mainPanel(tableOutput("test1"))
)

server = function(input, output) {

  geodatasetInput <- reactive({
    if(input$generateButton == 0){return()}

    isolate({ 
      input$generateButton
      test_data <- geodata[geodata$BSL %in% as.character(input$filter1),]
      test_data <- geodata[geodata$Position.Location %in% as.character(input$filter2),]
      test_data <- test_data[,1:2]
      test_data <- aggregate(. ~ Postcode, data=test_data, FUN=sum)
      test_data
    })
    write.csv(test_data,"solution.csv",row.names=F)
  })
  output$test1 <- renderTable({geodatasetInput()})
}

runApp(list(ui = ui, server = server)) 
like image 31
Pork Chop Avatar answered Dec 31 '25 20:12

Pork Chop