Как включить (исходный) R скрипт в другие скрипты - PullRequest
104 голосов
/ 23 июня 2011

Я создал утилиту R скрипт, util.R, которую я хочу использовать из других скриптов в моем проекте. Как правильно обеспечить доступность функции, определяемой этим сценарием, для других моих сценариев?

Я ищу что-то похожее на функцию require, которая загружает пакет, только если он еще не загружен. Я не хочу вызывать source("util.R"), потому что он будет загружать скрипт каждый раз, когда он вызывается.

Я знаю, что получу несколько ответов, говорящих мне о создании пакета, как в Организация кода R :) Но я не создаю что-то, что будет использоваться в другом месте, это просто отдельный проект.

Ответы [ 6 ]

89 голосов
/ 23 июня 2011

Вот один из возможных способов. Используйте функцию exists, чтобы найти что-то уникальное в вашем коде util.R.

Например:

if(!exists("foo", mode="function")) source("util.R")

(отредактировано для включения mode="function", как указал Гэвин Симпсон)

18 голосов
/ 23 июня 2011

Нет такой встроенной функции, поскольку R не отслеживает вызовы на source и не может выяснить, что было загружено откуда (это не так при использовании пакетов). Тем не менее, вы можете использовать ту же идею, что и в файлах C .h, то есть обернуть все в:

if(!exists('util_R')){
 util_R<-T

 #Code

}
10 голосов
/ 23 июня 2011

Скажите util.R производит функцию foo().Вы можете проверить, доступна ли эта функция в глобальной среде, и получить исходный текст сценария, если это не так:

if(identical(length(ls(pattern = "^foo$")), 0))
    source("util.R")

Это найдет что-нибудь с именем foo.Если вы хотите найти функцию, тогда (как упомянуто @Andrie) exists() полезно, но нужно точно сказать, какой тип объекта искать, например,

if(exists("foo", mode = "function"))
    source("util.R")

Здесь exists()в действии:

> exists("foo", mode = "function")
[1] FALSE
> foo <- function(x) x
> exists("foo", mode = "function")
[1] TRUE
> rm(foo)
> foo <- 1:10
> exists("foo", mode = "function")
[1] FALSE
5 голосов
/ 16 мая 2013

Вот функция, которую я написал. Он упаковывает функцию base::source для хранения списка исходных файлов в глобальном списке среды с именем sourced. Файл будет перераспределен только в том случае, если вы предоставите аргумент .force=TRUE для вызова источника. В противном случае подпись аргумента идентична реальной source(), поэтому вам не нужно переписывать свои сценарии, чтобы использовать это.

warning("overriding source with my own function FYI")
source <- function(path, .force=FALSE, ...) {
  library(tools)
  path <- tryCatch(normalizePath(path), error=function(e) path)
  m<-md5sum(path)

  go<-TRUE
  if (!is.vector(.GlobalEnv$sourced)) {
    .GlobalEnv$sourced <- list()
  }
  if(! is.null(.GlobalEnv$sourced[[path]])) {
    if(m == .GlobalEnv$sourced[[path]]) {
      message(sprintf("Not re-sourcing %s. Override with:\n  source('%s', .force=TRUE)", path, path))
      go<-FALSE
    }
    else {
      message(sprintf('re-sourcing %s as it has changed from: %s to: %s', path, .GlobalEnv$sourced[[path]], m))
      go<-TRUE
    }
  } 
  if(.force) {
    go<-TRUE
    message("  ...forcing.")
  }
  if(go) {
    message(sprintf("sourcing %s", path))
    .GlobalEnv$sourced[path] <- m
    base::source(path, ...)
  }
}

Это довольно болтливо (много звонков на message()), так что вы можете убрать эти строки, если хотите. Любой совет от опытных пользователей R приветствуется; Я довольно новичок в R.

5 голосов
/ 23 июня 2011

Вы можете написать функцию, которая принимает имя файла и имя среды, проверяет, загружен ли файл в среду, и использует sys.source для получения файла, если нет.

Вот быстрая и непроверенная функция (улучшения приветствуются!):

include <- function(file, env) {
  # ensure file and env are provided
  if(missing(file) || missing(env))
    stop("'file' and 'env' must be provided")
  # ensure env is character
  if(!is.character(file) || !is.character(env))
    stop("'file' and 'env' must be a character")

  # see if env is attached to the search path
  if(env %in% search()) {
    ENV <- get(env)
    files <- get(".files",ENV)
    # if the file hasn't been loaded
    if(!(file %in% files)) {
      sys.source(file, ENV)                        # load the file
      assign(".files", c(file, files), envir=ENV)  # set the flag
    }
  } else {
    ENV <- attach(NULL, name=env)      # create/attach new environment
    sys.source(file, ENV)              # load the file
    assign(".files", file, envir=ENV)  # set the flag
  }
}
0 голосов
/ 28 мая 2019

Я решил свою проблему, используя полный адрес, где мой код: До:

if(!exists("foo", mode="function")) source("utils.r")

После:

if(!exists("foo", mode="function")) source("C:/tests/utils.r")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...