'How to use write.table to download a dataframe into a nice csv/Excel file?

I am trying to use the write.table() function, within Shiny downloadHandler(), to download the df reactive dataframe as a .csv file, per the reproducible code at the bottom. Function write.table() allows the dropping of column names (which I need) but results in non-Excel output (Issue #1); write.csv() on the other hand results in nice Excel output but does not allow the dropping of column names (Issue #2).

Is there a way to get write.table() to work properly for CSV? If not, is there any other way to resolve this?

This image shows the issues:

enter image description here

Reproducible code:

library(dplyr)
library(DT)
library(shiny)
library(shinyWidgets)
library(tidyverse)

ui <-
  fluidPage(fluidRow(
    column(width = 8,
           h3("Click below to download:"),
           downloadButton("sumsDownload","Download",style = "width:20%;"),
           h3("Summed Data:"),
           DT::dataTableOutput("sums")
          )
  ))

server <- function(input, output, session) {
  data <- reactive({
    data.frame(
      Period = c("2020-01", "2020-02", "2020-03", "2020-01", "2020-02", "2020-03"),
      ColA = c(1000.01, 20, 30, 40, 50, 60),
      ColB = c(15.06, 25, 35, 45, 55, 65)
    )
  })
  
  summed_data <- reactive({
    data() %>%
      group_by(!!sym("Period")) %>%
      select("ColA", "ColB") %>% summarise(across(everything(), sum))
  })
  
  output$sums <- renderDT({datatable(data = summed_data(),rownames = FALSE)})
  
  df <- reactive({ # `df` modifies `summed_data` for csv download
    as.data.frame(
      rbind(
        c("Summed Data",rep(NA,ncol(summed_data())-1)),
        colnames(summed_data()),
        matrix(unlist(summed_data(), use.names=FALSE),
               nrow = nrow(summed_data()),
               )
          )
      )
  })
  
  output$sumsDownload <- downloadHandler(
    filename = function() {paste("sumsDownload","csv",sep=".")},
    content = function(file){
      write.table( # change this to write.csv to see Issue #2
        df(),
        na = "", 
        file, 
        col.names = FALSE, # comment out this line if changing to write.csv
        row.names = FALSE)
      }
  )
}

shinyApp(ui, server)


Solution 1:[1]

Below is resolved code reflecting comments posted through 05 May 2022:

library(dplyr)
library(DT)
library(shiny)
library(shinyWidgets)
library(tidyverse)

ui <-
  fluidPage(fluidRow(
    column(width = 8,
           h3("Click below to download:"),
           downloadButton("sumsDownload","Download",style = "width:20%;"),
           h3("Summed Data:"),
           DT::dataTableOutput("sums")
          )
  ))

server <- function(input, output, session) {
  data <- reactive({
    data.frame(
      Period = c("2020-01", "2020-02", "2020-03", "2020-01", "2020-02", "2020-03"),
      ColA = c(1000.01, 20, 30, 40, 50, 60),
      ColB = c(15.06, 25, 35, 45, 55, 65)
    )
  })
  
  summed_data <- reactive({
    data() %>%
      group_by(!!sym("Period")) %>%
      select("ColA", "ColB") %>% summarise(across(everything(), sum))
  })
  
  output$sums <- renderDT({datatable(data = summed_data(),rownames = FALSE)})
  
  df <- reactive({ # `df` modifies `summed_data` for csv download
    as.data.frame(
      rbind(
        c("Summed Data",rep(NA,ncol(summed_data())-1)),
        colnames(summed_data()),
        matrix(unlist(summed_data(), use.names=FALSE),
               nrow = nrow(summed_data()),
               )
          )
      )
  })
  
  output$sumsDownload <- downloadHandler(
    filename = function() {paste("sumsDownload","csv",sep=".")},
    content = function(file){
      write.table( 
        df(),
        na = "", 
        file, 
        sep = ",", # add this per road_to_quantdom on May/05/22
        col.names = FALSE, 
        row.names = FALSE)
      }
  )
}

shinyApp(ui, server)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Curious Jorge - user9788072