Ju Ko
Ju Ko

Reputation: 508

Consecutively deleting rows in Shiny DT table

I have a shiny App where users can upload their own data. My goal is to display an interactive table with DT that allows users to control which columns and which rows are displayed. Ultimately, Users should be able to either download all the data that they uploaded (there are some processing steps done in the actual app) or download only the data that they see in their current selection. I thus need to make a copy of the uploaded dataframe instead of editing it in place.

My Problem is that I can make the columns selectable and I can also remove selected rows, but I can't find a way to save the selected rows in between. For example: When users first select rows 1,2 and 3 and click on "Exclude Rows", the rows disappear, but when they then click on rows 4 and 5 and click on "Exclude Rows", row 4 and 5 disappear but 1,2 and 3 pop back up.

Here is what I tried so far:

# Reproducible example

# Define UI
ui <- fluidPage(

  navbarPage("Navbar",
    tabPanel("Upload Data",
             fileInput(inputId = "file", label = "Upload your .csv file",
                                                  accept = "text/csv"),
             actionButton("submit","Use this dataset")
    ),

    tabPanel("Check Table",

             sidebarPanel("Settings",

                          checkboxGroupInput("show_vars", "Select Columns to display:",

                                             choices = c("type",
                                                         "mpg",
                                                         "cyl",
                                                         "disp",
                                                         "hp",
                                                         "drat",
                                                         "wt",
                                                         "qsec",
                                                         "vs",
                                                         "am",
                                                         "gear",
                                                         "carb"
                                                         ),

                                             selected = c("type",
                                                          "mpg",
                                                          "cyl",
                                                          "disp",
                                                          "hp",
                                                          "drat",
                                                          "wt",
                                                          "qsec",
                                                          "vs",
                                                          "am",
                                                          "gear",
                                                          "carb"
                                             )),

                          tags$br(),
                          tags$br(),

                          actionButton("excludeRows", "Exlcude selected Rows")),

             mainPanel(DTOutput("frame"))),

    tabPanel("Show Selection",
             textOutput("selection"))

  )

)

# Define server logic
server <- function(input, output, session) {

  # Parsing the uploaded Dataframe according to the right input
  data <- eventReactive(input$submit, {read.csv(input$file$datapath)})

  # Render the whole dataframe when a new one is uploaded
  observeEvent(input$submit, {output$frame <- renderDT(datatable(data()[,c(input$show_vars)]))})

  # Making an internal copy for selection purposes
  CopyFrame <- eventReactive(data(),{data()})

  # excluding selected rows
  observeEvent(input$excludeRows,{

    if (exists("SelectFrame()")) {

      # Updating SelectFrame from SelectFrame
      SelectFrame <- eventReactive(input$excludeRows,{SelectFrame()[-c(input$frame_rows_selected),c(input$show_vars)]})

    } else {

      # creating SelectFrame for the first time from CopyFrame
      SelectFrame <- eventReactive(input$excludeRows,{CopyFrame()[-c(input$frame_rows_selected),c(input$show_vars)]})

    }

    # updating plot
    output$frame <- renderDT(datatable(SelectFrame()))

  })

  # show Selection
  output$selection <- renderText(input$frame_rows_selected)

}

# Run the application
shinyApp(ui = ui, server = server)

You can easily create an example file for this reproducible example with:

names(mtcars)[1] <- "type"
write.csv(mtcars, file = "testfile.csv")

Upvotes: 0

Views: 115

Answers (2)

starja
starja

Reputation: 10375

Here is a solution that works with the row numbers of the original dataframe:

library(shiny)
library(DT)

# Define UI
ui <- fluidPage(
  
  navbarPage("Navbar",
             tabPanel("Upload Data",
                      fileInput(inputId = "file", label = "Upload your .csv file",
                                accept = "text/csv"),
                      actionButton("submit","Use this dataset")
             ),
             
             tabPanel("Check Table",
                      
                      sidebarPanel("Settings",
                                   
                                   checkboxGroupInput("show_vars", "Select Columns to display:",
                                                      
                                                      choices = c("type",
                                                                  "cyl",
                                                                  "disp",
                                                                  "hp",
                                                                  "drat",
                                                                  "wt",
                                                                  "qsec",
                                                                  "vs",
                                                                  "am",
                                                                  "gear",
                                                                  "carb"
                                                      ),
                                                      
                                                      selected = c("type",
                                                                   "cyl",
                                                                   "disp",
                                                                   "hp",
                                                                   "drat",
                                                                   "wt",
                                                                   "qsec",
                                                                   "vs",
                                                                   "am",
                                                                   "gear",
                                                                   "carb"
                                                      )),
                                   
                                   tags$br(),
                                   tags$br(),
                                   
                                   actionButton("excludeRows", "Exlcude selected Rows")),
                      
                      mainPanel(DTOutput("frame"))),
             
             tabPanel("Show Selection",
                      textOutput("selection"))
             
  )
  
)

# Define server logic
server <- function(input, output, session) {
  
  # initialise index which rows are shown
  rows_shown <- reactiveVal()
  
  # Parsing the uploaded Dataframe according to the right input
  data <- eventReactive(input$submit, {
    data <- read.csv(input$file$datapath)
    data <- cbind(data, data.frame(row_number = seq_len(nrow(data))))
    data
    })
  
  # Making an internal copy for selection purposes
  CopyFrame <- eventReactive(input$submit, {data()})
  
  observeEvent(input$submit, {
    # set up row index
    rows_shown(seq_len(nrow(data())))
  })
  
  # excluding selected rows
  observeEvent(input$excludeRows,{
    # use an extra column for the row numbers to refer to the row number of the
    # original dataframe and not the subsetted one
    actual_row_numbers <- CopyFrame()[rows_shown(), "row_number"][input$frame_rows_selected]
    row_index <- !rows_shown() %in% actual_row_numbers
    new_rows <- rows_shown()[row_index]
    rows_shown(new_rows)
  })
  
  # show Selection
  output$selection <- renderText(input$frame_rows_selected)
  
  # show dataframe
  output$frame <- renderDT({
    datatable(CopyFrame()[rows_shown(), input$show_vars])
    })
  
}

# Run the application
shinyApp(ui = ui, server = server)

Upvotes: 1

Ben
Ben

Reputation: 30559

Perhaps you could use reactiveValues to store your edited data frame. When you load a new csv file, store in rv$data. Then, when you exclude rows, you can modify your data frame each time and replace rv$data with the result. Your output$frame can just show this modified rv$data and only the columns selected via input$show_vars. Would this work for you?

server <- function(input, output, session) {
  
  rv <- reactiveValues(data = NULL)
  
  observeEvent(input$submit, {
    rv$data <- read.csv(input$file$datapath)
  })
  
  observeEvent(input$excludeRows,{
    rv$data <- rv$data[-c(input$frame_rows_selected),c(input$show_vars)]
  })
  
  output$frame <- renderDT({
    datatable(rv$data[c(input$show_vars)])
  })
  
}

Upvotes: 2

Related Questions