Як включити (джерело) R-скрипт в інші сценарії


108

Я створив скрипт утиліти R, util.R, який я хочу використовувати з інших сценаріїв у своєму проекті. Який правильний спосіб переконатися, що функція, визначена цим сценарієм, доступна для роботи в інших моїх сценаріях?

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

Я знаю, що я отримаю декілька відповідей, які б сказали мені створити пакет, як в Organisation R Source Code :) Але я не створюю те, що буде використано в іншому місці, це просто окремий проект.


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

Скажімо, 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
Андрі

@Andrie - Ага, гаразд. Це все одно не спрацювало. Мене потягнуло, обдумуючи це. exists()краще, але тепер я бачу, що ви опублікували такий відповідь тим часом.
Гевін Сімпсон

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 вдячна; Я досить новачок у Р.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.