Как включить (исходный) сценарий R в другие сценарии


108

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

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

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


37
Я все время создаю пакеты для автономных проектов. Это не так уж много работы, а выгода огромна. Продолжай, ты знаешь, что хочешь это сделать ...
Андри

Ответы:


93

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

Например:

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

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


4
Хорошее использование exists()- нужно mode = "function"добавить, чтобы сделать его надежным
Гэвин Симпсон

1
exists()похоже, выдает ошибку, за исключением возврата ее в R 3.0.2.
Майкл Шуберт

Правильное использование - `существует (" foo "), и ответ был отредактирован.
Андри

18

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

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

 #Code

}

а потом позвонить source("util.R")по ifкоду, верно?
rafalotufo

1
@rafalotufo Как обычно, исходный код ("util.R"). Код из сообщения mbq перейдет в util.R. Вы просто помещаете все содержимое util.R прямо сейчас в гигантский оператор if (), если это имеет смысл.
Кейт Твомбли

10

Say 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

В этом случае вы можете захотеть использовать, grepl(..., value=TRUE)потому что ваш поисковый запрос, вероятно, не является регулярным выражением. +1, кстати.
Андри

?? grepl()не имеет аргументов value, но мне, вероятно, следует исправить регулярное выражение в ls()...
Гэвин Симпсон,

Извините моя ошибка. Я имел в видуfixed=TRUE
Андри

@ Андрей - А, ладно. Все равно это не сработало. Меня утащили, пока мы обдумывали это. exists()лучше, но теперь я вижу, что вы тем временем отправили такой ответ.
Gavin Simpson

5

Вы можете написать функцию, которая принимает имя файла и имя среды, проверяет, был ли файл загружен в среду, и использует его 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
  }
}

5

Вот функция, которую я написал. Он обертывает 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.


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.