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.