Search code examples
rcsvfile-existsreadrwrite.table

How to append data with readr::write_excel_csv without repeating column names?


The help file for readr::write_excel_csv suggests the following code usage:

write_excel_csv(x, path, na = "NA", append = TRUE,
  col_names = !append, delim = ",", quote_escape = "double")

However, col_names = !append creates an error.

library(readr)
data <- read_csv("col1, col2, col3
a,2,0
b,5,7
c,8,4")

write_excel_csv(data,"data.csv",append = TRUE, col_names=!append)

>Error in !append : invalid argument type

Is col_names = !append valid code? If not, what does it mean in the context of the help file for write_excel_csv?

col_names = TRUE appends the column names every time. I only want the column names to appear the first time.

Edit: At first I thought this code solved the problem, but it doesn't. It works with write.table but not write_excel or write_excel_csv. using column names when appending data in write.table

#!file.exists() works with write.table but not with write_excel or write_excel_csv

#column names appear
write.table(data, "data.csv", append=TRUE, col.names=!file.exists("data.csv"))

#column names don't appear
write_excel_csv(data,"data1.csv",append=TRUE, col_names=!file.exists("data1.csv"))

How can I make column names appear the first time but not subsequent times with write_excel_csv?


Solution

  • I think there is a scope problem with !append. R will try to evaluate that statement before calling readr::write_excel_csv, and if there is no object called append in your .GlobalEnv it will fail. Even worse, if there happens to be an object called append in your workspace, that value will be used in the call, which is almost certainly not what you want. Consider this:

    f = function (a = TRUE, b = !a) cat("'a' is", a, "; 'b' is", b, '\n')
    f()
    ## 'a' is TRUE ; 'b' is FALSE 
    
    f(b = !a)
    ## Error in cat("'a' is", a, "; 'b' is", b, "\n") : object 'a' not found
    
    a = FALSE
    f(b = !a)
    ## 'a' is TRUE ; 'b' is TRUE 
    

    When you call a function, R will first evaluate all statements you provide for the parameters of that function, then make the call with the corresponding values. On the other hand, if you don't provide a parameter that has a default value, that value will be evaluated within the function's scope. So when readr::write_excel_csv() evaluates the default !append, this happens within the scope of the function, where append should be defined.

    Now, the default value of col_names = !append is cleverly chosen to work as you expect, that is to write the headers when not appending, presumably writing a new file. So if you leave it alone and just make the value of append conditional it works smoothly:

    write_excel_csv(data, 'data1.csv', append=file.exists('data1.csv'))
    read_csv('data1.csv')
    
    ## Parsed with column specification:
    ## cols(
    ##   col1 = col_character(),
    ##   col2 = col_double(),
    ##   col3 = col_double()
    ## )
    ## # A tibble: 3 x 3
    ##   col1   col2  col3
    ##   <chr> <dbl> <dbl>
    ## 1 a         2     0
    ## 2 b         5     7
    ## 3 c         8     4
    
    write_excel_csv(data, 'data1.csv', append=file.exists('data1.csv'))
    read_csv('data1.csv')
    
    ## Parsed with column specification:
    ## cols(
    ##   col1 = col_character(),
    ##   col2 = col_double(),
    ##   col3 = col_double()
    ## )
    ## # A tibble: 6 x 3
    ##   col1   col2  col3
    ##   <chr> <dbl> <dbl>
    ## 1 a         2     0
    ## 2 b         5     7
    ## 3 c         8     4
    ## 4 a         2     0
    ## 5 b         5     7
    ## 6 c         8     4