Como incluir o script R (fonte) em outros scripts


108

Eu criei um script de utilitário R, util.R, que desejo usar de outros scripts em meu projeto. Qual é a maneira adequada de garantir que as funções definidas por esse script estejam disponíveis para funcionar em meus outros scripts?

Estou procurando algo semelhante à requirefunção, que carregue um pacote apenas se ele ainda não tiver sido carregado. Não quero chamar source("util.R")porque isso carregará o script toda vez que for chamado.

Eu sei que vou obter algumas respostas me dizendo para criar um pacote, como em Organizing R Source Code :) Mas não estou criando algo que será usado em outro lugar, é apenas um projeto independente.


37
Eu crio pacotes para projetos autônomos o tempo todo. Não dá muito trabalho e os benefícios são enormes. Vá em frente, você sabe que quer fazer ...
Andrie

Respostas:


93

Aqui está uma maneira possível. Use a existsfunção para verificar se há algo único em seu util.Rcódigo.

Por exemplo:

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

(Editado para incluir mode="function", como Gavin Simpson apontou)


4
Bom uso de exists()- precisa ser mode = "function"adicionado para torná-lo à prova de idiotas
Gavin Simpson

1
exists()parece lançar um erro, exceto retornar um em R 3.0.2.
Michael Schubert

O uso correto é `exists (" foo ") e a resposta foi editada.
Andrie

18

Não existe tal coisa embutida, já que R não rastreia chamadas sourcee não é capaz de descobrir o que foi carregado de onde (este não é o caso ao usar pacotes). Ainda assim, você pode usar a mesma ideia dos .harquivos C , ou seja, envolver o todo em:

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

 #Code

}

e ligue source("util.R")dentro do ifcódigo, certo?
rafalotufo

1
@rafalotufo Você faria o código fonte ("util.R") normalmente. O código na postagem de mbq iria para util.R. Você acabou de colocar o corpo inteiro do que está em util.R agora em uma instrução if () gigante, se isso fizer sentido.
Keith Twombley

10

Say util.Rproduz uma função foo(). Você pode verificar se esta função está disponível no ambiente global e originar o script se não estiver:

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

Isso encontrará qualquer coisa com o nome foo. Se você deseja encontrar uma função, então (como mencionado por @Andrie) exists()é útil, mas precisa ser informado exatamente que tipo de objeto procurar, por exemplo

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

Aqui está exists()em ação:

> 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

Nesse caso, você pode querer usar grepl(..., value=TRUE)porque seu termo de pesquisa provavelmente não é um regex. 1, por falar nisso.
Andrie

?? grepl()não tem argumento value, mas provavelmente devo consertar a regexp em ls()...
Gavin Simpson

Desculpe meu erro. Eu quis dizerfixed=TRUE
Andrie

@Andrie - Ah, ok. Não funcionou mesmo. Fui arrastado enquanto pensava nisso. exists()é melhor, mas agora vejo que você postou uma resposta nesse meio tempo.
Gavin Simpson

5

Você pode escrever uma função que recebe um nome de arquivo e um nome de ambiente, verifica se o arquivo foi carregado no ambiente e usa sys.sourcepara originar o arquivo, caso contrário.

Esta é uma função rápida e não testada (melhorias bem-vindas!):

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

Aqui está uma função que escrevi. Ele envolve a base::sourcefunção para armazenar uma lista de arquivos originados em uma lista de ambiente global chamada sourced. Ele só irá recriar um arquivo se você fornecer um .force=TRUEargumento para a chamada à fonte. Sua assinatura de argumento é idêntica à real, source()então você não precisa reescrever seus scripts para usá-la.

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, ...)
  }
}

É muito falador (muitas ligações para message()), então você pode desligar essas linhas se quiser. Agradecemos qualquer conselho de usuários veteranos do R; Eu sou muito novo na R.


0

Resolvi meu problema usando o endereço inteiro onde meu código está: Antes:

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

Depois de:

if(!exists("foo", mode="function")) source("C:/tests/utils.r")
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.