0

I'm trying to download the source data (mpg) I used to generate the selected rows in a DT table. The table shows aggregated results based on an input grouper from the source data.

I tried to: first get the row number using the _rows_selected function, then accumulate a list of the value in the grouper column, then identify the row indices of these value in the source data, then write the exported the csv based on these row indices and the source data.

But it didn't seem to work and I couldn't figure out why.


library(datasets)
library(shiny)
library(dplyr)
library(plotly)
library(ggplot2)
library(DT)
library(crosstalk)

data("mpg")
mpg = data.frame(mpg)
#convert all column input from character to factor (or assure they are all factor)
for(i in 1:dim(mpg)[2]){
  mpg[,i] = type.convert(mpg[,i])
  i = i+1
}

#mpg$manufacturer = type.convert(mpg$manufacturer)

# Define UI for application that draws a histogram
ui <- fluidPage(

  # Application title
  titlePanel("Analyze MPG table"),

  # Sidebar with a dropdown menu selection input for key meausre component
  sidebarLayout(
    sidebarPanel(
      selectInput("yInput", "Measuring element: ", 
                  colnames(mpg), selected = colnames(mpg)[9]), 
      selectInput('xInput', 'Grouper: ', 
                  colnames(mpg), selected = colnames(mpg)[1]), 
      selectInput('xInput2', 'Filter Column: ', 
                  colnames(mpg), selected = colnames(mpg)[2]),
      p(downloadButton('x0', 'Download Source Data of Selected Rows', 
                       class = 'text-center'))
    ),

    # Show a plot of the generated distribution
    mainPanel(
      uiOutput('filter'),
      plotlyOutput("barPlot"),
      DTOutput('table1')

    )
  )
)



server <- function(input, output) {


  output$filter = renderUI({
    selectInput('inputF2', 'Filter Item: ', 
                c('No Filter', unique(mpg %>% select(input$xInput2))))
  })



  mpg_sub <- reactive({  
    if (req(input$inputF2) != 'No Filter') {
      mpg_sub <- mpg %>% filter_at(vars(input$xInput2), any_vars(. == input$inputF2))
    } else{
      mpg_sub <- mpg
    }
    return(mpg_sub)
  })



  by_xInput <- reactive({  
    mpg_sub() %>% 
      group_by_at(input$xInput) %>% 
      # n() can replace the length
      # convert string to symbol and evaluate (!!)
      summarize(n = n(), mean_y = mean(!! rlang::sym(input$yInput)))
  })



  output$table1 = renderDT(
    datatable(by_xInput(), 
              extensions = 'Buttons', 
              options = list(dom = 'Bfrtip',
                             buttons = c('copy', 'csv', 'excel', 'pdf', 'print'))

              )
  )

  #####here is where I have the issue... 

  output$x0 = downloadHandler(
    'Source Data_selected.csv', 
    content = function(file){
      s0 = input$table1_rows_selected
      grouper = by_xInput()[s0, 1]
      big = mpg_sub()[, match(as.character(input$xInput), colnames(mpg_sub()))]
      position = which(!is.na(match(big, grouper)))

      write.csv(mpg_sub()[position, , drop = F], file)
    }
  )




}


shinyApp(ui = ui, server = server)

Yitong Li
  • 57
  • 6
  • 1
    Welcome to StackOverflow! Please read the info about [how to ask a good question](https://stackoverflow.com/help/how-to-ask) and [how to give a reproducible example](https://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example). This will make it much easier for others to help you. Your example is neither minimal nor reproducible. – thothal Sep 20 '19 at 15:35
  • Also - what is `input$inputC1` in your `downloadHandler` referring to? – Ben Sep 20 '19 at 15:40
  • @Ben, oops sorry inputC1 should be the xInput in this case. I forgot to change it from the actual project I was working on. – Yitong Li Sep 20 '19 at 17:08
  • @thothal, I used the mpg data set that comes with the ggplot2 package. Why is this not reproducible? – Yitong Li Sep 20 '19 at 18:17

2 Answers2

1

A few things seem like they were needed:

  1. Correct input$inputC1 to input$xInput as in comment.
  2. big and grouper appear to be tibbles, not vectors. Change match statement as below to get single column as vector from tibble.
  3. position should be rows to abstract from mpg_sub() not columns. Put position before the comma.

Please let me know if this works.

output$x0 = downloadHandler(
    'Source Data_selected.csv', 
    content = function(file){
      s0 = input$table1_rows_selected
      grouper = by_xInput()[s0, 1]
      big = mpg_sub()[, match(as.character(input$xInput), colnames(mpg_sub()))]
      position = which(!is.na(match(big[[1]], grouper[[1]])))
      write.csv(mpg_sub()[position , , drop = F], file)
    }
  )
Ben
  • 28,684
  • 5
  • 23
  • 45
  • Thanks @Ben! I caught number 3. – Yitong Li Sep 20 '19 at 18:15
  • But with the interactive input, I don't always use the manufacturer column, it could be model, or drv. How would you address this ? – Yitong Li Sep 20 '19 at 18:16
  • No that doesn't work. position is supposed to return a list of all row numbers in grouper that is a match of any element is big. How do you know that both big and grouper are tibbles? – Yitong Li Sep 20 '19 at 18:30
  • 1
    OMG it's working now!!! Thank you so much @Ben! I copied pasted your entire chunk this time instead of just revise big and grouper. I was playing around with the as.character function before. Guess they have to work together. Thank you for sharing your knowledge! – Yitong Li Sep 20 '19 at 18:41
  • Also @Ben, do you mind sharing the function of [[1]]? – Yitong Li Sep 20 '19 at 18:52
  • The double bracket is an extractor like the $ dollar sign. In this case it will extract the first (and only) column, without using the column name. The result will be a vector, which is needed for the match function. This might also be helpful: https://stackoverflow.com/questions/7070173/convert-data-frame-column-to-a-vector – Ben Sep 20 '19 at 18:57
  • thanks for the explanation @Ben! I see. So it's because both big and grouper are one column table (tibble), I have to specify the column name for matching? That is super cool! – Yitong Li Sep 20 '19 at 19:06
  • another issue @Ben.... I tried the same thing with my actual project and it didn't work. I found out that it's because the original data I feed into the analysis is formatted as data.frame. (aka: mpg = data.frame(mpg) ). Do you have a solution for this? – Yitong Li Sep 20 '19 at 20:14
  • I think I just made it work @Ben. Let me upload my code. – Yitong Li Sep 20 '19 at 20:29
1

Ben's answer works with the original mpg data; if mpg is formatted into data.frame, this will work. Ben's comments helped me a lot to come up with this solution!!


output$x0 = downloadHandler(
    'Source Data_selected.csv', 
    content = function(file){
      s0 = input$table1_rows_selected
      grouper = by_xInput()[s0, 1]

      big2 = mpg_sub() %>% pull(!!rlang::sym(input$xInput))
      position2 = which(!is.na(match(big2, grouper[[1]])))

      write.csv(mpg_sub()[position2 , , drop = F], file)
    }
  )
Yitong Li
  • 57
  • 6