【发布时间】:2012-11-26 01:22:38
【问题描述】:
我听说在脚本中使用setwd() 是不好的做法。
- 与之相关的风险/危险是什么?
- 有什么更好的选择?
【问题讨论】:
-
可能在您与他人共享的脚本中......因为它可能不存在,具体取决于操作系统。将文件存储在 tempdir() 中是一种替代方法
我听说在脚本中使用setwd() 是不好的做法。
【问题讨论】:
这是可重现代码的问题。如果您指定的目录在其他人的计算机上不存在,那么他们就无法使用您的代码。这对于绝对文件路径尤其糟糕,对于 Windows 文件路径尤其糟糕(在 Unix 系统上绝对不可能复制)。
我首选的解决方案是在开始运行代码之前指定用户应该位于自己系统上的相关目录中。如果为了您自己的方便,您想将 setwd(...) 放在代码的顶部,其他人可以注意到它并酌情将其注释掉,但您的代码的其余部分仅假定来自该起始目录的相对路径,那就是没关系。
Yihui Xie(knitr的作者)对此感觉特别强烈:
https://groups.google.com/forum/?fromgroups=#!topic/knitr/knM0VWoexT0
当你想要操作文件时,它们被假定在 与您的源文件相同的目录(例如 Rnw 文档)。那么你就可以 总是使用相对路径,你永远不需要 setwd()。使用 setwd() 与再现性原则相矛盾,例如你 使用 setwd('foo/bar/') 并且该目录可能不存在于其他 人的电脑。请参阅常见问题 7: https://github.com/yihui/knitr/blob/master/FAQ.md
从前面提到的常见问题 7:
你最好不要这样做[更改 knitr 代码中的工作目录 块]。您的工作目录始终是 getwd() (所有输出文件 将写在这里),但代码块在 输入文档的来源目录。改变工作 运行 R 代码时的目录通常是一种不好的做法。见#38 进行讨论。您还应该尽量避免使用绝对目录 尽可能(使用相对目录),因为它使 可重现性较差的事物。
【讨论】:
source(chdir = T)。
我想不出在我管理的服务器上运行的脚本中使用setwd() 有什么特别的问题,因为它确实返回了一个可以用try() 捕获的错误,你可以管理它。我在懒惰时使用了setwd() - 见下文!
我在脚本制作或其他方面广泛使用file.path()。处理输入目录中的文件并将输出图形和报告放在其他地方。所以类似于...(未经测试)使用setwd() 会有点乏味。
kInDir <- '~/Indir'
kOutDir <- '~/Outdir'
flist <- dir(path=kInDir, pattern='^[a-z]{2,5}\\.csv$')
# note I could have used full.names=T - but it's easier not to...
for (fnam in flist) {
# full path to the report file created
sfnam <- file.path(kOutDir, gsub('.csv', '_report.txt', fnam))
# full path to the csv file that will be created
ofnam <- file.path(kOutDir, gsub('.csv', '_b.csv', fnam))
#
# ok... we're going to process this CSV file...
r1 <- read.csv(file.path(kInDir, fnam))
#
# we''ll put the output from the analysis into this report file
sink(sfnam, split=TRUE)
# processs it... into a new data.frame k1
# blah blah blah...
#
write.csv(k1, file=ofnam, row.names=FALSE)
sink() # turn off this particular report file
}
【讨论】:
关于更好的选择问题:
我主要将 R 用于单个项目(这意味着我是主要分析师)。但是,我们确实在有时需要与他人共享的项目中使用这些。
我发现 RStudio 的 项目功能与 long way 相得益彰,可让您的文件井井有条。如果其他用户也采用 RStudio,他们会觉得能够打开单个文件(“*.Rproj”)并让项目加载到与上次保存时相同的状态。
除此之外,我还发现了一个新工具,ProjectTemplate,它更进一步!作者开发的技术用于为您正在做的事情提供结构。请访问网站了解更多详情。
【讨论】:
虽然 setwd() 的问题已成为目标,但我想在问题的 what are the alternatives 部分再添加一个。我们经常在相对路径很方便的地方使用 git
setrelwd <- function(rel_path){
curr_dir <- getwd()
abs_path <- file.path(curr_dir,rel_path)
if(dir.exists(abs_path)){
setwd(abs_path)
}
else
{
warning('Directory does not exist. Please create it first.')
}
}
> setrelwd("Summer2016")
Warning message:
In setrelwd("Summer2016") : Directory does not exist. Please create it first.
此外,如果您不想看到警告消息但立即创建文件夹,请参阅Check existence of directory and create if doesn't exist
【讨论】:
为了让我工作的地方更便携,我们都把它放在一个 Rprofile 中
hdrive=
switch(Sys.info()[[1]],
'Linux'="/mnt/hdrive",
'Windows'="H:/",
"Darwin"="/Volumes/hdrive/mnt/hdrive"
)
所以我总是有那个变量来让我进入我们的共享驱动器。然后在我的脚本中我们可以写
setwd(paste(hdrive,"/relative/path/",sep="/"))
这样我们就可以解决其他人正在谈论的一些问题。
【讨论】:
paste 需要 2 µs,file.path 在这种情况下需要 1.5 µs。因此,如果您将此脚本运行一百万次,您将节省半秒。
我个人添加了以下代码。我使用具有唯一信息的 Sys.info() 和 any()。
第一步是使用 Sys.info() 并找到您计算机的唯一标识符。
if(any(Sys.info() == "COMPUTER1")) {
setwd("c:/Users/user1/repos/project/")
}
if(any(Sys.info() == "COMPUTER2")) {
setwd("home/user1/repos/project/")
}
只需将计算机的名称添加到 if 语句并添加正确的路径。只需为每台机器添加一个新的 if。
对于复制,它不会更改任何人的工作目录,除非他们是那个特定的用户。
【讨论】: