仅提取文件的部分源代码

17

我的R工作流程通常是打开一个文件,然后在其中输入R命令,并希望在一个单独打开的R shell中执行这些命令。

最简单的方法是在R中输入source('the-file.r')。然而,这将重新加载整个文件,如果处理大量数据,则可能需要相当长的时间。同时,它还要求我再次指定文件名。

理想情况下,我希望只从文件中源引入特定的行(或多行)(我正在使用不支持复制和粘贴的终端)。

source似乎没有提供此功能。是否有其他方法可以实现这一点?


2
那是什么终端?DEC VT100吗?要么换一个更好的终端或更好的环境 - 比如emacs?- 要么重新考虑你的工作流程,或将文件分成其他文件并创建一堆嵌套的源文件,或者做得更好一些,增加更多函数... - Spacedman
1
那又怎样?我一直在使用远程tmux和远程emacs;仍然可以在emacs中获得缓冲区,以便您可以根据需要源化部分。而且,Emacs ESS甚至允许_本地_ Emacs连接到_远程_ R会话(如果您愿意的话,我不这样做)。 - Dirk Eddelbuettel
1
也许在这里使用http://projecttemplate.net/的数据处理工具会很有用。 - daroczig
@Konrad:你知道这个 Vim 插件 http://www.vim.org/scripts/script.php?script_id=2628 吗? - Dirk Eddelbuettel
1
@Konrad -- 很棒!按区域工作并从文件中读取是正确的方法。 - Dirk Eddelbuettel
显示剩余12条评论
2个回答

24

这里是另一种仅使用R的方法:

source2 <- function(file, start, end, ...) {
    file.lines <- scan(file, what=character(), skip=start-1, nlines=end-start+1, sep='\n')
    file.lines.collapsed <- paste(file.lines, collapse='\n')
    source(textConnection(file.lines.collapsed), ...)
}

9

使用合适的工具来完成任务…

正如评论中所讨论的,真正的解决方法是使用IDE,允许对文件的特定部分进行源代码编辑。有许多现成的解决方案:

需要特别注意的是,以上所有解决方案都可在本地和服务器上(通过SSH连接等方式)工作。如果正确设置,R甚至可以在HPC集群上运行,仍可与IDE进行通信。

…或者… 不使用

如果由于任何原因以上解决方案均无法使用,这里有一个小型模块[gist]可以完成任务。尽管如此,我通常不建议使用它。1

#' (Re-)source parts of a file
#'
#' \code{rs} loads, parses and executes parts of a file as if entered into the R
#' console directly (but without implicit echoing).
#'
#' @param filename character string of the filename to read from. If missing,
#' use the last-read filename.
#' @param from first line to parse.
#' @param to last line to parse.
#' @return the value of the last evaluated expression in the source file.
#'
#' @details If both \code{from} and \code{to} are missing, the default is to
#' read the whole file.
rs = local({
    last_file = NULL

    function (filename, from, to = if (missing(from)) -1 else from) {
        if (missing(filename)) filename = last_file

        stopifnot(! is.null(filename))
        stopifnot(is.character(filename))

        force(to)
        if (missing(from)) from = 1

        source_lines = scan(filename, what = character(), sep = '\n',
                            skip = from - 1, n = to - from + 1,
                            encoding = 'UTF-8', quiet = TRUE)
        result = withVisible(eval.parent(parse(text = source_lines)))

        last_file <<- filename # Only save filename once successfully sourced.
        if (result$visible) result$value else invisible(result$value)
    }
})

使用示例:

# Source the whole file:
rs('some_file.r')
# Re-soure everything (same file):
rs()
# Re-source just the fifth line:
rs(from = 5)
# Re-source lines 5–10
rs(from = 5, to = 10)
# Re-source everything up until line 7:
rs(to = 7)

1 有趣的故事:最近我发现自己在一个配置混乱的集群上,无法安装所需软件,但由于严峻的截止日期需要调试 R 工作流程。我只能手动复制和粘贴 R 代码行到控制台中。这是一个可能会派上用场的情况。是的,这真的发生了。


1
我认为使用 <<- 存储上一个源文件不是一个好主意(实际上,几乎从来不是一个好主意)。另一个问题是 sed 在所有平台上可能都不可用(例如 Windows)。 - Gabor Csardi
我认为如果你真的想保留最后一个文件名(在这种情况下非常明智),那么将隐藏环境放置在.GlobalEnv中确实是正确的方法。 - Gabor Csardi
要想摆脱 sed,可以使用 readLines() 读取文件,然后选择所需的行并对其进行求值。 - Gabor Csardi
在R中优雅的处理可变参数列表的方式是定义它们并测试它们是否使用了missing - mbq
1
@KonradRudolph,是的,你说得对,它不一定要成为一个环境。我的主要观点是将其存储在.GlobalEnv中更好,如果变量名以点开头,则很好,因为它不会列在ls()中,也不会在rm(list=ls())中被删除等等。 - Gabor Csardi
@Gabor,我已经厌倦了人们提到这个糟糕的答案,所以我完全重写了它。我告诉你这件事是因为我正在清理(现在已经过时的)评论;也许你想做同样的事情。 - Konrad Rudolph

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接