【问题标题】:Can I import variables into R from a global file?我可以将变量从全局文件导入 R 吗?
【发布时间】:2019-06-25 17:49:26
【问题描述】:

我正在将一个 R 脚本集成到一个更大的项目中,该项目与一个 Makefile 一起生成一些图形。在这个较大的项目中,我有一个名为 globals.mk 的文件,其中包含项目中许多其他脚本使用的全局变量。例如,我想运行的模拟数量是我想在这个 R 脚本中使用的一个全局变量。我可以将其“导入”为变量,还是需要在 R 脚本中手动定义每个变量?

编辑:这是我需要读入的全局变量示例。

num = 100
path = ./here/is/a/path
file = $(path)/file.csv

我希望 R 脚本将变量 num 设置为 100(或“100”),path 设置为“./here/is/a/path”,file 设置为“./here /is/a/path/file.csv"。

【问题讨论】:

  • 当然可以,但具体如何操作取决于globals.mk 文件的格式。最简单的方法是source an R file,但也可以选择其他格式,例如YAML 等。请发布您的文件示例,或指定对该文件放置哪些(如果有)约束
  • 如果 global.mk 包含像 num = 100 这样的行(这是有效的 R 源代码),那么在你的 R 程序中使用 source("global.mk") 来读入并运行它。如果它不是有效的 R 源代码,则使用 readLines 将其读入并执行您需要的任何处理,以从 readLines 返回的字符向量中提取和设置值。请注意,问题应包含完整的最小可重复示例。
  • 谢谢,我已经添加了更多细节并阐明了要求。
  • 您可以在调用 R 脚本时传递命令行参数。现在还不清楚你是如何调用脚本的。请尝试提供一个我们可以实际运行和测试的reproducible example

标签: r makefile


【解决方案1】:

如果可以用大括号替换括号,那么readRenviron 将读入此类文件并执行替换,将内容作为环境变量返回。

# write out test file globals2.mk which uses brace brackets
Lines <- "num = 100
path = ./here/is/a/path
file = ${path}/file.csv"
cat(Lines, file = "globals2.mk")

readRenviron("globals2.mk")
Sys.getenv("num")
## [1] "100"
Sys.getenv("path")
## [1] "./here/is/a/path"
Sys.getenv("file")
## [1] "./here/is/a/path/file.csv"

如果使用括号而不是大括号很重要,请读入 globals.mk,用大括号替换括号,然后再次写出文件。

# write out test file - this one uses parentheses as in question
Lines <- "num = 100
path = ./here/is/a/path
file = $(path)/file.csv"
cat(Lines, file = "globals.mk")

# read globals.mk, perform () to {} substitutions, write out and then re-read
tmp <- tempfile()
L <- readLines("globals.mk")
cat(paste(chartr("()", "{}", L), collapse = "\n"), file = tmp)
readRenviron(tmp)

【讨论】:

  • 这太完美了! Sys.getenv() 允许我调用变量,这是我需要的。
【解决方案2】:

如果.mk 文件除了直接变量扩展之外还有其他内容(例如更复杂的make-rules/tricks/functions),最好信任make 为您进行扩展,然后读进去。我发现有一个帖子here 转储了所有变量内容(处理后)。

TL;DR

expand_mkvars <- function(path, aslist = FALSE) {
  stopifnot(file.exists(mk <- Sys.which("make")))
  tf <- tempfile(fileext = ".mk")
  # needed on my windows system
  tf <- normalizePath(tf, winslash = "/", mustWork = FALSE) # tempfile should suffice
  on.exit(suppressWarnings(file.remove(tf)), add = TRUE)
  writeLines(c(".PHONY: printvars",
               "printvars:",
               "\t@$(foreach V,$(sort $(.VARIABLES)), \\",
               "\t   $(if $(filter-out environment% default automatic, \\",
               "\t   $(origin $V)),$(warning $V=$($V))))"), con = tf)
  out <- system2(mk, c("-f", shQuote(path), "-f", shQuote(tf), "-n", "printvars"),
                 stdout = TRUE, stderr = TRUE)
  out <- out[grepl(paste0("^", tf), out)]
  out <- gsub(paste0("^", tf, ":[0-9]+:\\s*"), "", out)
  known_noneed <- c(".DEFAULT_GOAL", "CURDIR", "GNUMAKEFLAGS", "MAKEFILE_LIST", "MAKEFLAGS")
  out <- out[!grepl(paste0("^(", paste(known_noneed, collapse = "|"), ")="), out)]
  if (aslist) {
    spl <- strsplit(out, "=")
    nms <- sapply(spl, `[[`, 1)
    rest <- lapply(spl, function(a) paste(a[-1], collapse = "="))
    setNames(rest, nms)
  } else out
}

在行动:

expand_mkvars("~/StackOverflow/karthikt.mk")
# [1] "file=./here/is/a/path/file.csv" "num=100"                       
# [3] "path=./here/is/a/path"         

expand_mkvars("~/StackOverflow/karthikt.mk", aslist = TRUE)
# $file
# [1] "./here/is/a/path/file.csv"
# $num
# [1] "100"
# $path
# [1] "./here/is/a/path"

我没有在其他系统上测试过,所以您可能需要调整known_noneed 以添加弹出的额外变量。根据您的需要,您可能能够更智能地进行过滤(例如,您的变量都没有以大写字母开头),但对于这个示例,我将其保留为 make 给我们的已知不想要的变量.


blog post 建议使用虚假目标

.PHONY: printvars
printvars:
    @$(foreach V,$(sort $(.VARIABLES)), \
       $(if $(filter-out environment% default automatic, \
       $(origin $V)),$(warning $V=$($V))))

(有些是制表符,不是所有的空格,对make 非常重要)

不幸的是,它产生的输出超出了您的技术需求:

$ /c/Rtools/bin/make.exe -f ~/StackOverflow/karthikt.mk printvars
C:/Users/r2/StackOverflow/karthikt.mk:10: .DEFAULT_GOAL=all
C:/Users/r2/StackOverflow/karthikt.mk:10: CURDIR=/Users/r2/Projects/Ford/shiny/shinyobjects/inst
C:/Users/r2/StackOverflow/karthikt.mk:10: GNUMAKEFLAGS=
C:/Users/r2/StackOverflow/karthikt.mk:10: MAKEFILE_LIST= C:/Users/r2/StackOverflow/karthikt.mk
C:/Users/r2/StackOverflow/karthikt.mk:10: MAKEFLAGS=
C:/Users/r2/StackOverflow/karthikt.mk:10: SHELL=sh
C:/Users/r2/StackOverflow/karthikt.mk:10: file=./here/is/a/path/file.csv
C:/Users/r2/StackOverflow/karthikt.mk:10: num=100
C:/Users/r2/StackOverflow/karthikt.mk:10: path=./here/is/a/path
make: Nothing to be done for 'printvars'.

所以我们需要一点过滤,因此函数中的大部分代码都是如此。


编辑readRenviron-to-envvar 是最适合您的方式,将这个make 调用的输出重定向到另一个文件并不难,解析出相关行,然后在该新文件上执行readRenviron。由于使用了两个临时文件,这似乎更间接,但它们已被清理,因此无需担心。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-04-18
  • 1970-01-01
  • 1970-01-01
  • 2013-06-23
  • 2014-09-11
  • 2017-04-26
  • 2020-05-17
相关资源
最近更新 更多