comparison of methods for saving text from R: writeLines(), cat(), and sink()

Viewed 195

This question, Write lines of text to a file in R, shows three different for saving outputs to a plain text file. Using the example from the question, let's say that we want to create a file named output.txt with this text:

Hello
World

The question's answers show three methods:

  1. Using writeLines():
fileConn<-file("output.txt")
writeLines(c("Hello","World"), fileConn)
close(fileConn)
  1. Using sink():
sink("outfile.txt")
cat("hello")
cat("\n")
cat("world")
sink()
  1. Using cat():
cat("Hello",file="outfile.txt",sep="\n")
cat("World",file="outfile.txt",append=TRUE)

Some of the answers and comments note that cat() would be slower than the other two methods. However, my questions are:

  1. Are there situations when one method is better than the others?
  2. If one method is more idiomatically correct or quicker than the other two methods in R, why?

I searched SO and only found the linked answer. I have found other why question on SO (e.g., Why is processing a sorted array faster than processing an unsorted array?) so I think this question is on topic for the site.

3 Answers

A short performance comparison give clear advantage to writeLines:

system.time(writeLines(con = 'writeLines.txt',text = paste('Line ',1:100000)))
#>        user      system       total 
#>        0.17        0.01        0.18

system.time(cat( paste('Line ',1:100000),file="cat.txt",sep="\n"))
#>        user      system       total 
#>        0.25        0.85        1.11

Looking at C code, cat uses RPrintf located in builtin.c.

All printing in R is done via the functions Rprintf and REprintf or their (v) versions Rvprintf and REvprintf.
These routines work exactly like (v)printf(3). Rprintf writes to ``standard output''.
It is redirected by the sink() function

As stated above, using sink() allows to redirect the connection. Redirecting once to a file using sink() is definitely faster than opening a new connection / appending data / closing for each new line as with cat(file=..., append=TRUE).

writeLines uses a dedicated C function Rconn_printf located in connections.c and is quicker.

To sum up :

  • cat is the standard R output to console
  • sink allows to redirect cat output to another connection, for example a file, allowing to write multiple lines without reopening the connection
  • writeLines is quicker than sink+cat for file output

Suggestion, when to use which:

  • I like cat() to log R script progress in console mode. Once the script is validated, sink() allows to redirect this output to a file if needed and might be useful for script automation.
  • I use writeLines() when I specifically want to write data (not just log) to a file because of it's better performance.

cat uses R objects and writeLines one character vector.
cat converts numeric/complex elements in the same way as print and not in the way as as.character, so options digits and scipen are relevant.

options(digits = 3)
x <- 0.123456789

cat("Result:", x)
#Result: 0.123

writeLines(paste("Result:", x))
#Result: 0.123456789

writeLines(paste("Result:", format(x)))
#Result: 0.123

writeLines(c("Result:", format(x)), sep=" ")
#Result: 0.123

Using R objects and converting them will take time.
In case of having a character vector writeLines will be as convenient as cat but more efficient.
In case having different objects cat will be more convenient but slower.

Regarding performance in R it is almost always worth looking into library(data.table).

Here is a benchmark taking into account data.table::fwrite:

library(data.table)
library(microbenchmark)

microbenchmark(
  writeLines = {
    writeLines(con = 'writeLines.txt', text = paste('Line', 1:100000))
  },
  cat = {
    cat(paste('Line', 1:100000), file = "cat.txt", sep = "\n")
  },
  fwrite = {
    fwrite(as.list(paste('Line', 1:100000)), file = "fwrite.txt", sep = "\n")
  },
  times = 1L
)

Unit: milliseconds
       expr       min        lq      mean    median        uq       max neval
 writeLines  202.5904  202.5904  202.5904  202.5904  202.5904  202.5904     1
        cat 1234.6644 1234.6644 1234.6644 1234.6644 1234.6644 1234.6644     1
     fwrite  106.8576  106.8576  106.8576  106.8576  106.8576  106.8576     1
Related