Home > Blockchain >  How do I copy a complete data frame object to clipboard?
How do I copy a complete data frame object to clipboard?

Time:05-03

I'm trying to create an actionButton() (ID is transCopy in below code) to copy/paste rendered tables from Shiny to Excel. In the below code, it works for an incomplete extract of the transition table (object results()), but not for the completed object per output$resultsDT which shows the transitions (like results()) PLUS the periods transiting from/to along the top rows of the table rendered in Shiny.

I tried pulling datatable(...) out of output$resultsDT and creating a new reactive object with it, feeding it into both output$resultsDT and the clipboard copy function write.table(x = ...) inside the single observeEvent() below, but got "Error in <-: object of type 'closure' is not subsettable". I tried other things but with no luck yet.

So how would I change this so the user can copy/paste a more complete version of the table to Excel? The format doesn't need to be exactly the same (though it would be nice if it were), even 2 rows along the top of the pasted table specifying "From = x" and "To = y" would be helpful so the user can later see the inputs that were used in deriving the table post-paste into Excel.

The images at the bottom better explain.

Finally, I like sticking with base R (such as write.table()) if possible because otherwise, in the fuller code this is intended for, I'm suffering from the effects of package-bloat.

Reproducible code:

library(DT)
library(shiny)
library(dplyr)
library(htmltools)
library(data.table)

data <- 
  data.frame(
    ID = c(1,1,1,2,2,2,3,3,3),
    Period = c(1, 2, 3, 1, 2, 3, 1, 2, 3),
    Values = c(5, 10, 15, 0, 2, 4, 3, 6, 9),
    State = c("X0","X1","X2","X0","X2","X0", "X2","X1","X9")
  )

numTransit <- function(x, from=1, to=3){
  setDT(x)
  unique_state <- unique(x$State)
  all_states <- setDT(expand.grid(list(from_state = unique_state, to_state = unique_state)))
  dcast(x[, .(from_state = State[from], 
              to_state = State[to]), 
          by = ID]
        [,.N, c("from_state", "to_state")]
        [all_states,on = c("from_state", "to_state")], 
        to_state ~ from_state, value.var = "N"
  )
}

ui <- fluidPage(
  tags$head(tags$style(".datatables .display {margin-left: 0;}")), 
  h4(strong("Transition table inputs:")),
  numericInput("transFrom", "From period:", 1, min = 1, max = 3),
  numericInput("transTo", "To period:", 2, min = 1, max = 3),
  actionButton(inputId = "transCopy", "Copy", width = "20%"),
  h4(strong("Output transition table:")), 
  DTOutput("resultsDT"),
)

server <- function(input, output, session) {
  results <- 
    reactive({
      results <- numTransit(data, input$transFrom, input$transTo) %>% 
        replace(is.na(.), 0) %>%
        bind_rows(summarise_all(., ~(if(is.numeric(.)) sum(.) else "Sum")))
      results <- cbind(results, Sum = rowSums(results[,-1]))
    })
 
  output$data <- renderTable(data)
  
  output$resultsDT <- renderDT(server=FALSE, {
    datatable(
      data = results(),
      rownames = FALSE,
      filter = 'none',
      container = tags$table(
        class = 'display',
        tags$thead(
          tags$tr(
            tags$th(rowspan = 2, 
                    sprintf('To state where end period = %s', input$transTo), 
                    style = "border-right: solid 1px;"),
            tags$th(colspan = 10, 
                    sprintf('From state where initial period = %s', input$transFrom)
                    )
          ),
          tags$tr(
            mapply(tags$th, colnames(results())[-1], style = sprintf("border-right: solid %spx;", rep(0, ncol(results()) - 1L)), SIMPLIFY = FALSE)
          )
        )
      ),
      options = list(scrollX = F
                     , dom = 'ft'
                     , lengthChange = T
                     , pagingType = "numbers"
                     , autoWidth = T
                     , info = FALSE
                     , searching = FALSE
      ),
      class = "display"
    ) %>%
      formatStyle(c(1), `border-right` = "solid 1px")
  })
  
  observeEvent(input$transCopy,
               write.table(x = results(),
                           file = "clipboard",
                           sep = "\t",
                           row.names = FALSE,
                           col.names = TRUE
               ))
  
}

shinyApp(ui, server)

UI when first invoking App:

enter image description here

Now paste from clipboard to Excel:

enter image description here

CodePudding user response:

Although I haven't tried it myself, clipr seems to do what you want.

library(shiny)
library(clipr)
library(rhandsontable)

ui <- fluidPage(
  actionButton(inputId = 'click',label = 'COPY'),
  p('Click COPY and paste the results below witch Ctrl V.'),
  rHandsontableOutput('rhot')
)

server <- function(input, output, session) {
  
  
  output$rhot = renderRHandsontable({
    df = data.frame(lapply(1:10, function(x){rep('',10)}))
    colnames(df) = paste('c',1:10)
    rhandsontable(df)
  })
  
  observeEvent(input$click,{
    clipr::write_clip(mtcars)
  })
  
}

shinyApp(ui, server)

CodePudding user response:

You can use JavaScript to copy the whole table with the added headings if that is what you are after.

In the example below I have added a HTML() chunk based on answers found here.

library(DT)
library(shiny)
library(dplyr)
library(htmltools)
library(data.table)

data <-
  data.frame(
    ID = c(1, 1, 1, 2, 2, 2, 3, 3, 3),
    Period = c(1, 2, 3, 1, 2, 3, 1, 2, 3),
    Values = c(5, 10, 15, 0, 2, 4, 3, 6, 9),
    State = c("X0", "X1", "X2", "X0", "X2", "X0", "X2", "X1", "X9")
  )

numTransit <- function(x, from = 1, to = 3) {
  setDT(x)
  unique_state <- unique(x$State)
  all_states <-
    setDT(expand.grid(list(
      from_state = unique_state, to_state = unique_state
    )))
  dcast(x[, .(from_state = State[from],
              to_state = State[to]),
          by = ID]
        [, .N, c("from_state", "to_state")]
        [all_states, on = c("from_state", "to_state")],
        to_state ~ from_state, value.var = "N")
}

ui <- fluidPage(
  tags$head(tags$style(".datatables .display {margin-left: 0;}")),
  h4(strong("Transition table inputs:")),
  numericInput("transFrom", "From period:", 1, min = 1, max = 3),
  numericInput("transTo", "To period:", 2, min = 1, max = 3),
  actionButton(inputId = "transCopy", "Copy", width = "20%"),
  h4(strong("Output transition table:")),
  DTOutput("resultsDT"),
  HTML(
    '
       <script type="text/javascript">

function copytable(el) {
    var urlField = document.getElementById(el)
    var range = document.createRange()
    range.selectNode(urlField)
    window.getSelection().addRange(range)
    document.execCommand(\'copy\')
}

</script>

<input type=button value="Copy to Clipboard" onClick="copytable(\'DataTables_Table_0\')">

       ')
)

server <- function(input, output, session) {
  results <-
    reactive({
      results <- numTransit(data, input$transFrom, input$transTo) %>%
        replace(is.na(.), 0) %>%
        bind_rows(summarise_all(., ~ (if (is.numeric(.))
          sum(.)
          else
            "Sum")))
      results <- cbind(results, Sum = rowSums(results[,-1]))
    })
  
  output$data <- renderTable(data)
  
  output$resultsDT <- DT::renderDT(server = FALSE, {
    DT::datatable(
      data = results(),
      rownames = FALSE,
      filter = 'none',
      container = tags$table(class = 'display',
                             tags$thead(
                               tags$tr(
                                 tags$th(
                                   rowspan = 2,
                                   sprintf('To state where end period = %s', input$transTo),
                                   style = "border-right: solid 1px;"
                                 ),
                                 tags$th(
                                   colspan = 10,
                                   sprintf('From state where initial period = %s', input$transFrom)
                                 )
                               ),
                               tags$tr(
                                 mapply(
                                   tags$th,
                                   colnames(results())[-1],
                                   style = sprintf("border-right: solid %spx;", rep(0, ncol(results(
                                     
                                   )) - 1L)),
                                   SIMPLIFY = FALSE
                                 )
                               )
                             )),
      options = list(
        scrollX = F,
        dom = 'ft',
        lengthChange = T,
        pagingType = "numbers",
        autoWidth = T,
        info = FALSE,
        searching = FALSE,
        extensions = c("Buttons"),
        buttons = list('copy')
      ),
      class = "display"
    ) %>%
      formatStyle(c(1), `border-right` = "solid 1px")
  })
  
  observeEvent(input$transCopy, {
    print(results())
    clipr::write_clip(content = results())
  })
  
}

shinyApp(ui, server)
  • Related