วิธีรวมสคริปต์ (ที่มา) R ในสคริปต์อื่น ๆ


108

ฉันได้สร้างยูทิลิตี้ R สคริปต์ util.R ซึ่งฉันต้องการใช้จากสคริปต์อื่นในโครงการของฉัน อะไรคือวิธีที่เหมาะสมในการตรวจสอบว่าฟังก์ชันที่สคริปต์กำหนดนี้พร้อมใช้งานในสคริปต์อื่น ๆ ของฉัน

ฉันกำลังมองหาสิ่งที่คล้ายกับrequireฟังก์ชั่นที่โหลดแพ็คเกจก็ต่อเมื่อยังไม่ได้โหลด ฉันไม่ต้องการโทรsource("util.R")เพราะจะโหลดสคริปต์ทุกครั้งที่มีการเรียก

ฉันรู้ว่าฉันจะได้รับคำตอบบางอย่างที่บอกให้ฉันสร้างแพ็คเกจเช่นเดียวกับในการจัดระเบียบซอร์สโค้ด R :) แต่ฉันไม่ได้สร้างสิ่งที่จะใช้ที่อื่นมันเป็นเพียงโปรเจ็กต์แบบสแตนด์อโลน


37
ฉันสร้างแพ็คเกจสำหรับโปรเจ็กต์แบบสแตนด์อโลนตลอดเวลา มันไม่ได้ทำงานมากและผลประโยชน์ก็มาก คุณรู้ว่าคุณต้องการทำ ...
Andrie

คำตอบ:


93

นี่เป็นวิธีหนึ่งที่เป็นไปได้ ใช้existsฟังก์ชันเพื่อตรวจสอบสิ่งที่ไม่ซ้ำกันในutil.Rรหัสของคุณ

ตัวอย่างเช่น:

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

(แก้ไขเพื่อรวมmode="function"ตามที่ Gavin Simpson ชี้ให้เห็น)


4
การใช้งานที่ดีexists()- ต้องmode = "function"เพิ่มเพื่อให้พิสูจน์ได้ว่าโง่
Gavin Simpson

1
exists()ดูเหมือนว่าจะเกิดข้อผิดพลาดยกเว้นการส่งคืนหนึ่งใน R 3.0.2
Michael Schubert

การใช้งานที่ถูกต้องคือ `มีอยู่ (" foo ") และมีการแก้ไขคำตอบ
Andrie

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. คุณแค่ใส่เนื้อความทั้งหมดของสิ่งที่เป็นประโยชน์ R ตอนนี้ให้กลายเป็นคำสั่ง if () ขนาดยักษ์ถ้ามันสมเหตุสมผล
Keith Twombley

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 โดยวิธีการ
Andrie

?? grepl()ไม่มีข้อโต้แย้งvalueแต่ฉันควรจะแก้ไข regexp ในls()...
Gavin Simpson

ขออภัยความผิดพลาดของฉัน ฉันหมายถึงfixed=TRUE
Andrie

@ แอนดรี - อาตกลง มันไม่ได้ผลอยู่ดี ถูกลากออกไปในขณะที่ไตร่ตรองเรื่องนี้ 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 รุ่นเก๋า ฉันค่อนข้างใหม่สำหรับอาร์


0

ฉันแก้ไขปัญหาโดยใช้ที่อยู่ทั้งหมดโดยที่รหัสของฉันคือ Before:

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

หลังจาก:

if(!exists("foo", mode="function")) source("C:/tests/utils.r")
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.