Why doesn't knitr respect RStudio project details? - r

In all other cases, when I am working within an RStudio project, I can make references relative to the project root in scripts. So I can, for example, dfX = read.csv("Data/somefile.csv"), where the folder Data is relative to my project root.
The same code in a knitr chunk does not find the file. I guess this is because knitr creates a bunch of temporary directories that it needs to refer to relative to the file location. Is there an easy way to change this behavior? Obviously, I would not like to add the entire path to the project folder -- I am aware that I can easily do this using knitr::opts_knit$set(root.dir = rootPath). That completely breaks maintainability across machines and OSs.
Edit: This seems closely linked to this question.

Presumably you know the path to the package directory when you call 'knit', so how about:
ENV <- new.env()
assign("workingDirectory", getcwd(), envir = ENV)
knitr::knit(...,
# THE ENVIRONMENT IN WHICH THE CODE CHUNKS ARE TO BE EVALUATED
envir=ENV)
Then in your rmd file you can do:
```{r] print(workingDirectory)```
If you're searching for the location of the current install, you can use:
PATH = NULL
for(libPath in .libPaths())
if('myPackage' %in% list.dirs(libPath,FALSE,FALSE)){
PATH = file.path(libPath,'myPackage')
}
if(is.null(PATH))
stop('could not find package directory')
ENV <- new.env()
assign("workingDirectory", PATH, envir = ENV)
knitr::knit(...,
# THE ENVIRONMENT IN WHICH THE CODE CHUNKS ARE TO BE EVALUATED
envir=ENV)

My guess is that the document that you are "knitting" is in a subdirectory itself. It seems that, when you click "Knit PDF", RStudio or knitr will setwd() to the directory containing the file being knitted. So you may need to do something like dfX = read.csv("../Data/somefile.csv") to get the reference right.
I have a working example here.

Related

Attempting to access images using R

So I am following the guide here which indicates the way to access photos is as follows:
flags <- c(
system.file("img", "flag", "au.png", package = "ggpattern"),
system.file("img", "flag", "dk.png", package = "ggpattern")
)
My goal is to now use this code for my own uses, so I saved a few images in a folder. Here is my directory:
"C:/Users/Thom/Docs/Misc/Testy"
And within the Testy folder, there is a folder called image, holding 3 images. But the following doesn't seem to work and idk why...
images <- c(
system.file("image", "image1.png", package = "ggpattern"),
system.file("image", "image2.png", package = "ggpattern")
)
system.file is for use when a file included in a package. Basically, it will look for the file starting its search path to where your R packages are installed (because this can vary from user to user). system.file will return the resolved path to the file locally
If you already know the absolute path on your local computer (i.e. "C:/Users/Thom/Docs/Misc/Testy") you can use that as just the input to a read function, e.g. readBin("C:/Users/Thom/Docs/Misc/Testy")
If you want to get a little fancy or are like me and can't ever remember which direction of a slash to use on which OS, you can also do something like this which will add in the OS specific path separator:
readBin(file.path("C:", "Users", "Thom", "Docs", "Misc", "Testy"))

Filepath error when specifying output location for write_dta

I would like to programmatically specify the location of my exports when using write.dta. I have my working directory set to a parent folder and my script is in a child folder called "Script". I want the export to be in a child folder called "Data".
setwd("~/Dropbox/Files")
file_output <- "survey"
path_out <- "./Data"
write_dta(df, paste0(file_output,".dta"), path = path_out, version = 12)
However, I keep getting an error message when R is trying to write. It says it's trying to write to the "Script" folder (where my script file is located in) rather than the desired "Data" folder.
Error: Failed to open '/Users/VancityPlanner/Dropbox/Files/Scripts' for writing
If I put the full path, I still get the same error message, whether it's a child folder or the parent folder (working directory) itself, so I don't think write permissions are an issue.
If I try not specifying the filepath, I have no error messages but it saves it to my working directory, which is not where I want it.
write_dta(df, paste0(file_output,".dta"), version = 12)
Below I show where my working directory is pointing and then I change the path of where I want to save the document in the path statement. Note it has the file path G:/ and the dataset name appended all together. I have a PC but no reason why this shouldn't work on a mac.
library(haven)
getwd()
#"C:/Users/myname/Documents"
write_dta(data = mtcars, path = "G:/mtcars.dta", version = 12)

Change knitr chunk defaults outside documents

I teach a lab and I have my students write their answers in .Rmd files. For grading, I download and render them as pdfs in a batch. I use the following script to render everything and save in a file.
library(rmarkdown)
# Handy functions for concatenating strings because I want to do it like a Python
# programmer damnit!
`%s%` <- function(x,y) {paste(x,y)}
`%s0%` <- function(x,y) {paste0(x,y)}
# You should set the working directory to the one where the assignments are
# located. Also, make sure ONLY .rmd files are there; anything else may cause
# a problem.
subs <- list.files(getwd()) # Get list of files in working directory
errorfiles <- c() # A vector for names of files that produced errors
for (f in subs) {
print(f)
tryCatch({
# Try to turn the document into a PDF file and save in a pdfs subdirectory
# (you don't need to make the subdirectory; it will be created automatically
# if it does not exist).
render(f, pdf_document(), output_dir = getwd() %s0% "/pdfs")
},
# If an error happens, complain, then save the name in errorfiles
error = function(c) {
warning("File" %s% "did not render!")
warning(c)
errorfiles <- c(errorfiles, f)
})
}
This last assignment I forgot to set error=TRUE in the chunks, so documents will fail to compile if errors are found and I will have to go hunt those errors down and fix them. I tried to modify this code so that I set the parameter error=TRUE as default outside the document. Unfortunately, I've been working at this for hours and have found no way to do so.
How can I change this code so I can change this parameter outside the documents? (Bear in mind that I don't own the computer so I cannot install anything, but the packages knitr and rmarkdown are installed.)

Copy script after executing

Is there a possibility to copy the executed lines or basically the script into the working directory?
So my normal scenario is, I have stand alone script which just need to be sourced within a working directory and they will do everything I need.
After a few month, I made update to these scripts and I would love to have a snapshot from the script when I executed the source...
So basically file.copy(ITSELF, '.') or something like this.
I think this is what you're looking for:
file.copy(sys.frame(1)$ofile,
to = file.path(dirname(sys.frame(1)$ofile),
paste0(Sys.Date(), ".R")))
This will take the current file and copy it to a new file in the same directory with the name of currentDate.R, so for example 2015-07-14.R
If you want to copy to the working directory instead of the original script directory, use
file.copy(sys.frame(1)$ofile,
to = file.path(getwd(),
paste0(Sys.Date(), ".R")))
Just note that the sys.frame(1)$ofile only works if a saved script is sourced, trying to run it in terminal will fail. It is worth mentioning though that this might not be the best practice. Perhaps looking into a version control system would be better.
Explanation:
TBH, I might not be the best person to explain this (I copied this idea from somewhere and use it sometimes), but I'll try. Basically in order to have information about the script file R needs to be running it as a file inside an environment with that information, and when that environment is a source call it contains the ofile data. We use (1) to select the next (source()'s) environment following the global environment (which is 0). When you're running this from terminal, there's no frame/environment other than Global (that's the error message), since no file is being ran - the commands are sent straight to terminal.
To illustrate that, we can do a simple test:
> sys.frame(1)
Error in sys.frame(1) : not that many frames on the stack
But if we call that from another function:
> myf <- function() sys.frame(1)
> myf()
<environment: 0x0000000013ad7638>
Our function's environment doesn't have anything in it, so it exists but, in this case, does not have ofile:
> myf <- function() names(sys.frame(1))
> myf()
character(0)
I just wanted to add my solution since I decided to use a try function before executing the copy command... Because I have the feeling I miss some control...
try({
script_name <- sys.frame(1)$ofile
copy_script_name <-
paste0(sub('\\.R', '', basename(script_name)),
'_',
format(Sys.time(), '%Y%m%d%H%M%S'),
'.R')
file.copy(script_name,
copy_script_name)
})
This will copy the script into the current directory and also adds a timestamp to the filename. In case something goes wrong, the rest of the script will still execute.
I originally posted this other thread, and I think it addresses your problem: https://stackoverflow.com/a/62781925/9076267
In my case, I needed a way to copy the executing file to back up the original >script together with its outputs. This is relatively important in research.
What worked for me while running my script on the command line, was a mixure of >other solutions presented here, that looks like this:
library(scriptName)
file_dir <- paste0(gsub("\\", "/", fileSnapshot()$path, fixed=TRUE))
file.copy(from = file.path(file_dir, scriptName::current_filename()) ,
to = file.path(getwd(), scriptName::current_filename()))
Alternatively, one can add to the file name the date and our to help in >distinguishing that file from the source like this:
file.copy(from = file.path(current_dir, current_filename()) ,
to = file.path(getwd(), subDir, paste0(current_filename(),"_", Sys.time(), ".R")))

R: sourcing files using a relative path

Sourcing files using a relative path is useful when dealing with large codebases. Other programming languages have well-defined mechanisms for sourcing files using a path relative to the directory of the file being sourced into. An example is Ruby's require_relative. What is a good way to implement relative path sourcing in R?
Below is what I pieced together a while back using various recipes and R forum posts. It's worked well for me for straight development but is not robust. For example, it breaks when the files are loaded via the testthat library, specifically auto_test(). rscript_stack() returns character(0).
# Returns the stack of RScript files
rscript_stack <- function() {
Filter(Negate(is.null), lapply(sys.frames(), function(x) x$ofile))
}
# Returns the current RScript file path
rscript_current <- function() {
stack <- rscript_stack()
r <- as.character(stack[length(stack)])
first_char <- substring(r, 1, 1)
if (first_char != '~' && first_char != .Platform$file.sep) {
r <- file.path(getwd(), r)
}
r
}
# Sources relative to the current script
source_relative <- function(relative_path, ...) {
source(file.path(dirname(rscript_current()), relative_path), ...)
}
Do you know of a better source_relative implementation?
After a discussion with #hadley on GitHub, I realized that my question goes against the common development patterns in R.
It seems that in R files that are sourced often assume that the working directory (getwd()) is set to the directory they are in. To make this work, source has a chdir argument whose default value is FALSE. When set to TRUE, it will change the working directory to the directory of the file being sourced.
In summary:
Assume that source is always relative because the working directory of the file being sourced is set to the directory where the file is.
To make this work, always set chdir=T when you source files from another directory, e.g., source('lib/stats/big_stats.R', chdir=T).
For convenient sourcing of entire directories in a predictable way I wrote sourceDir, which sources files in a directory in alphabetical order.
sourceDir <- function (path, pattern = "\\.[rR]$", env = NULL, chdir = TRUE)
{
files <- sort(dir(path, pattern, full.names = TRUE))
lapply(files, source, chdir = chdir)
}

Resources