1

I want to update a dataframe (A) using another dataframe (B). B is in ui and the user has option to update data in table B. Based on updated values in B, I want to update A. See the code below, the code allows user to update B but A is not getting updated after clicking action button:

df2 <- structure(list(A = c(1L, 4L, 0L, 1L), 
                      B = c("3", "*", "*", "2"), 
                      C = c("4", "5", "2", "*"), 
                      D = c("*", "9", "*", "4")), 
                 .Names = c("A", "B", "C", "D"), 
                 class = "data.frame", 
                 row.names = c(NA, -4L))
df1 <- structure(list(variable = c("A", "B", "C", "D"), 
                      Value = c(2L,1L, 9L, 0L)), 
                 .Names = c("variable", "Value"), 
                 class = "data.frame",
                 row.names = c(NA, -4L))
shinyApp(
  ui <-
    fluidPage(
      titlePanel("Sample"),

      # Create a new row for the table.
      sidebarLayout(
        sidebarPanel(
          selectInput("select", label = h3("Select Variable"), 
                      choices = unique(df1$variable), 
                      selected = unique(df1$variable)[1]),
          numericInput("num", label = h3("Replace * with"), 
                          value = unique(df1$variable)[1]),
          actionButton("applyChanges", "Apply Changes")),
        mainPanel(
          dataTableOutput(outputId="table")
        ))),
  Server <- function(input, output) {

    # Filter data based on selections
    output$table <- renderDataTable({
      df1$Value[df1$variable==input$select] <<- input$num
      df1
    })
    df2_new <- eventReactive(input$applyChanges,{
      df1[as.character(df1$variable)] <- Map(function(x, y)
        replace(x, x=="*", y), df2[as.character(df1$variable)], df1$Value)
      df2_new <- df2
      return(df2_new)
    })
  })

Any help would be highly appreciated. Thanks!!

Mike Wise
  • 22,131
  • 8
  • 81
  • 104

1 Answers1

1

This does what you intend I think:

library(shiny)
# old df2
dfaa <- data.frame(A = c( 1L, 4L, 0L, 1L), 
                   B = c("3","*","*","2"), 
                   C = c("4","5","2","*"), 
                   D = c("*","9","*","4"),stringsAsFactors = F) 
# old df1
dfbb <- data.frame(variable = c("A","B","C","D"), 
                  Value    = c( 2L, 1L, 9L, 0L),stringsAsFactors = F)

ui <-  fluidPage(titlePanel("Sample"),
  sidebarLayout(
    sidebarPanel(
      selectInput("select", label = h3("Select Variable"), 
                  choices = unique(dfbb$variable), 
                  selected = unique(dfbb$variable)[1]),
      numericInput("num", label = h3("Replace * in A with"), 
                   value = unique(dfbb$Value)[1]),
      actionButton("applyChanges", "Apply Changes specified in B to A")),
    mainPanel(
      h3("Table A"),  dataTableOutput(outputId="tableA"),
      h3("Table B"),  dataTableOutput(outputId="tableB")
)))

server <- function(input, output) {
  rv <- reactiveValues(dfA=dfaa,dfB=dfbb)
  observe({
    # update dfB immediately when the variable or value in the ui changes
    rv$dfB$Value[rv$dfB$variable==input$select] <- input$num
  })

  observeEvent(input$applyChanges,{
    # Here we apply the changes that were specified
    dfAcol <-as.character(rv$dfB$variable)
    rv$dfA[dfAcol] <- 
          Map(function(x, y) replace(x, x=="*", y), rv$dfA[dfAcol], rv$dfB$Value)
  })
  output$tableB <- renderDataTable({ rv$dfB })
  output$tableA <- renderDataTable({ rv$dfA })
}
shinyApp(ui=ui,server=server)

Notes:

  • It is actually pretty close to the original code you posted.
  • To implement the data updating I used reactiveValues and observe instead of eventReactive, you could do it with eventReactive as I mentioned before and you tried, I tried it too, but this way was cleaner and clearer and avoided the dreaded <<-.
  • Added Table B to the main panel so as to see what is going on.
  • I re-named df2 to dfaa and df1 to dfbb. I just could not keep df1 as Table B and df2 as Table A in my head, it was too confusing.
  • I also made the initializer more human friendly - dput output is hard to read.
  • The map and the input$num assignment would be more clearly done with dplyr. I recommend using dplyr as it really does make for cleaner and less error-prone code.
  • I pulled out the ui and server functions from the shinyApp call, this gives you more indentation room and the more common pattern.

Mandatory screenshot:

enter image description here

Mike Wise
  • 22,131
  • 8
  • 81
  • 104
  • @ Mike Wise A big thank you. I am new to R and still struggling to write codes. Thanks for the help again! – shivani gupta Mar 18 '17 at 05:56
  • A small question. Is there an option to commit the changes made in ui permanent in data sets? The changes should be seen even after closing the ui in global environment? Thanks in advance – shivani gupta Mar 18 '17 at 12:16
  • Not in ui. You can only write code that executes in the server part. However nothing to prevent you from putting together an action button that is wired to an observeEvent, that then does a write.csv to a file that you want. – Mike Wise Mar 18 '17 at 12:43
  • Thank you! This is exactly what I was looking for. Thanks again! – shivani gupta Mar 19 '17 at 10:47
  • @ Mike Wise: I am stuck with a new problem in the same above code. Can you help in that? Would highly appreciate it. The link of new question is below http://stackoverflow.com/questions/43182663/dependencies-in-functions-two-functions-working-individually-but-when-combined – shivani gupta Apr 03 '17 at 10:59