Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Switching writing from file to stdout using "sink()" in R

I implemented the following procedure that aims to write some files and print a message in the end of each file when the writing is done:

# Print one file per piaf
output_dir_piafs <- "OUTPUT_dataset_piafs"
unlink(output_dir_piafs, recursive = TRUE, force = TRUE)
dir.create(output_dir_piafs)
for (i in 1:length(lst_sorted)) {
    sink()      # Generates warnings...
    filename <- paste(output_dir_piafs, "/piaf_", i, ".txt", sep="")
    sink(file = filename, append = TRUE)
    sink(type = "message")
    cat("  ", colnames(file1), "\n")

    for (j in 1:length(lst_sorted[[i]])) {
        cat(j, " ")
        lapply( lst_sorted[[i]][[j]], 
                function(x) { 
                    cat(as.character(x), " ")
                }
        )
        cat("\n")
    }

    ## back to the console
    sink()
    cat(paste(filename, "done !\n"))
    #flush(stdout())        # Tested, no particular effect
}

My problem is that, if I do not add a sink() at the very beginning of the loop, the final writing on the standard output (cat(paste(filename, "done !\n"))) has no effect. On the other hand, adding this early sink() generates warnings that I would like to avoid:

There were 50 or more warnings (use warnings() to see the first 50)
> warnings()
Warning messages:
1: In sink() : no sink to remove
2: In sink() : no sink to remove
3: In sink() : no sink to remove

Does anyone has an idea on how sink() behaves, and/or how to get rid of those warnings?

Note: I also tried try(sink(), silent=TRUE), but the silent option prevents only from errors...

like image 525
Gauthier Boaglio Avatar asked Mar 22 '23 18:03

Gauthier Boaglio


2 Answers

You don't have to use sink, have a look at ?cat, which can directly write to a file.

The following code should work:

output_dir_piafs <- "OUTPUT_dataset_piafs"
unlink(output_dir_piafs, recursive = TRUE, force = TRUE)
dir.create(output_dir_piafs)
lst_sorted <- c(1,2,3)
file1 <- c(a=1, b=2, c=3)
for (i in 1:length(lst_sorted)) {
  filename <- paste(output_dir_piafs, "/piaf_", i, ".txt", sep="")
  cat("  ", colnames(file1), "\n", file=filename, append=T)
  for (j in 1:length(lst_sorted[[i]])) {
    cat(j, " ", file=filename, append=T)
    lapply( lst_sorted[[i]][[j]], 
           function(x) { 
             cat(as.character(x), " ", file=filename, append=T)
           }
           )
    cat("\n", file=filename, append=T)
  }
  cat(paste(filename, "done !\n"))
}

Please note that I set the variables lst_sorted and file1 in order to make the code reproducible.

Does the cat solution work for you?

like image 191
user1981275 Avatar answered Mar 31 '23 22:03

user1981275


I finally fixed the issue by adding a sink(type="output") when catching the interruption - Ctrl-C - (this makes the things right for later usage of the standard output - which would remain locked/diversion otherwise):

tryCatch({

    # Print one file per piaf
    output_dir_piafs <- "OUTPUT_dataset_piafs"
    unlink(output_dir_piafs, recursive = TRUE, force = TRUE)
    dir.create(output_dir_piafs)
    for (i in 1:length(lst_sorted)) {
        filename <- paste(output_dir_piafs, "/piaf_", i, ".txt", sep="")
        sink(file = filename, append = TRUE)
        sink(type = "message")
        cat("  ", colnames(file1), "\n")

        for (j in 1:length(lst_sorted[[i]])) {
            cat(j, " ")
            lapply( lst_sorted[[i]][[j]], 
                    function(x) { 
                        cat(as.character(x), " ")
                    }
            )
            cat("\n")
        }

        ## back to the console
        sink(type="output")
        cat(paste(filename, "done !\n"))
    }

}, interrupt = function(ex) {

    ##cat("An interrupt was detected.\n")
    sink(type="output")            # Restore the standard output !
    ##print(ex)

}) # tryCatch()


The other way to do (based on the help of user1981275 and Karl Forner), would have been:

tryCatch({

    # Print one file per piaf
    output_dir_piafs <- "OUTPUT_dataset_piafs_2"
    unlink(output_dir_piafs, recursive = TRUE, force = TRUE)
    dir.create(output_dir_piafs)
    for (i in 1:length(lst_sorted)) {
        filename <- paste(output_dir_piafs, "/piaf_", i, ".txt", sep="")
        f <- file(filename, 'w')
        cat("  ", colnames(file1), "\n", file=f)

        for (j in 1:length(lst_sorted[[i]])) {
            cat(j, " ", file=f)
            lapply( lst_sorted[[i]][[j]], 
                    function(x) { 
                        cat(as.character(x), " ", file=f)
                    }
            )
            cat("\n", file=f)
        }

        flush(f)
        close(f)
        ## back to the console
        cat(paste(filename, "done !\n"))
    }

}, interrupt = function(ex) {

    closeAllConnections()

}) # tryCatch()

This solution has the great advantage to not make a diversion of the standard output, meaning that, even without catching the interruption, the standard output remains active.

Note that Keyboard Interruption handling is still necessary to avoid warnings like:

> warnings()
Warning message:
closing unused connection 3 (OUTPUT_dataset_piafs_2/piaf_16.txt)

Accordingly to ?closeAllConnections:

‘closeAllConnections’ closes (and destroys) all user connections, restoring all ‘sink’ diversions as it does so.

Last Note: No real speed difference regarding file writing between those two methods.

like image 21
Gauthier Boaglio Avatar answered Mar 31 '23 23:03

Gauthier Boaglio