วิธีซอร์สไฟล์ R Markdown เช่น source ('myfile.r') `


89

ฉันมักจะมีไฟล์ R Markdown หลักหรือไฟล์ Knitr LaTeX ซึ่งฉันsourceมีไฟล์ R อื่น ๆ (เช่นสำหรับการประมวลผลข้อมูล) อย่างไรก็ตามฉันคิดว่าในบางกรณีการให้ไฟล์ที่มาเหล่านี้เป็นเอกสารที่สามารถทำซ้ำได้จะเป็นประโยชน์ (เช่นไฟล์ R Markdown ที่ไม่เพียง แต่มีคำสั่งสำหรับการประมวลผลข้อมูลเท่านั้น แต่ยังสร้างเอกสารที่สามารถทำซ้ำได้ซึ่งอธิบายการตัดสินใจในการประมวลผลข้อมูล ).

ดังนั้นฉันต้องการมีคำสั่งเช่นsource('myfile.rmd')ในไฟล์ R Markdown หลักของฉัน ที่จะแยกและซอร์สรหัส R ทั้งหมดภายในส่วนรหัส R ของmyfile.rmd. แน่นอนว่าสิ่งนี้ก่อให้เกิดข้อผิดพลาด

คำสั่งต่อไปนี้ใช้งานได้:

```{r message=FALSE, results='hide'}
knit('myfile.rmd', tangle=TRUE)
source('myfile.R')
```

ที่results='hide'สามารถละเว้นได้หากต้องการผลลัพธ์ เช่น knitr outputs รหัส R จากเข้าmyfile.rmdmyfile.R

อย่างไรก็ตามดูเหมือนจะไม่สมบูรณ์แบบ:

  • ส่งผลให้มีการสร้างไฟล์พิเศษ
  • จะต้องปรากฏในส่วนรหัสของตัวเองหากจำเป็นต้องควบคุมการแสดงผล
  • source(...)มันไม่ได้เป็นสง่างามเป็นที่เรียบง่าย

ดังนั้นคำถามของฉัน: มีวิธีที่หรูหรากว่าในการจัดหารหัส R ของไฟล์ R Markdown หรือไม่?


ฉันมีปัญหาในการทำความเข้าใจคำถามของคุณอย่างยากลำบาก (อ่านหลายครั้งแล้ว) คุณสามารถซอร์สสคริปต์ R อื่น ๆ ลงในRmdไฟล์ได้อย่างง่ายดาย แต่คุณต้องการแหล่งที่มาในmarkdownไฟล์อื่น ๆลงในไฟล์ที่กำลังถักไหม?
Maiasaura

4
ฉันต้องการซอร์สรหัส R ภายในส่วนรหัส R ในไฟล์ R Markdown (เช่น * .rmd)? ฉันได้แก้ไขคำถามเล็กน้อยเพื่อพยายามทำให้ชัดเจนขึ้น
Jeromy Anglim

บางสิ่งบางอย่างตามเส้นของincludeน้ำยาง หาก markdown รองรับการรวมเอกสาร markdown อื่น ๆ การสร้างฟังก์ชันดังกล่าวจะค่อนข้างง่าย
Paul Hiemstra

@PaulHiemstra ฉันเดาว่าความสามารถในการซอร์สของข้อความและโค้ด R จะมีประโยชน์เช่นกัน ฉันคิดเฉพาะที่จะจัดหารหัสในเอกสาร R Markdown
Jeromy Anglim

คำตอบ:


35

ดูเหมือนว่าคุณกำลังมองหาซับเดียว วิธีการใส่สิ่งนี้ในของคุณ.Rprofile?

ksource <- function(x, ...) {
  library(knitr)
  source(purl(x, output = tempfile()), ...)
}

อย่างไรก็ตามฉันไม่เข้าใจว่าทำไมคุณถึงต้องการsource()โค้ดในไฟล์ Rmd เอง ฉันหมายถึงknit()จะเรียกใช้รหัสทั้งหมดในเอกสารนี้และหากคุณแยกรหัสและเรียกใช้เป็นกลุ่มรหัสทั้งหมดจะถูกเรียกใช้สองครั้งเมื่อคุณใช้knit()เอกสารนี้ (คุณเรียกใช้ตัวเองในตัวเอง) งานทั้งสองควรแยกจากกัน

ถ้าคุณอยากจะเรียกใช้รหัสทั้งหมด RStudio Ctrl + Shift + Rได้ทำนี้ค่อนข้างง่าย: โดยทั่วไปเรียกpurl()และsource()อยู่เบื้องหลัง


8
สวัสดี @Yihui ฉันคิดว่ามันมีประโยชน์เพราะบางครั้งการวิเคราะห์ของคุณอาจถูกจัดเรียงเป็นสคริปต์เล็ก ๆ แต่ในรายงานของคุณคุณต้องการมีโค้ดสำหรับทั้งไปป์ไลน์
lucacerone

9
ดังนั้นกรณีการใช้งานที่นี่คือคุณต้องการเขียนโค้ดทั้งหมดและมีการจัดทำเอกสารและอธิบายไว้เป็นจำนวนมาก แต่โค้ดจะถูกเรียกใช้โดยสคริปต์อื่น
Brash Equilibrium

4
@BrashEquilibrium มันเป็นเรื่องของการใช้source()หรือknitr::knit()การรันโค้ด ฉันรู้ว่าคนรุ่นหลังไม่ค่อยคุ้นเคย แต่purl()ไม่น่าเชื่อถือ คุณได้รับคำเตือน: github.com/yihui/knitr/pull/812#issuecomment-53088636
Yihui Xie

5
@Yihui อะไรคือทางเลือกที่เสนอสำหรับ 'source (purl (x, ... ))' ในมุมมองของคุณ? หนึ่งแหล่งที่มาหลายไฟล์ * .Rmd-Files โดยไม่พบข้อผิดพลาดเกี่ยวกับป้ายกำกับกลุ่มที่ซ้ำกันได้อย่างไร ฉันไม่อยากกลับไปที่เอกสารที่จะหาแหล่งที่มาและถักมัน ฉันใช้ * .Rmd สำหรับไฟล์จำนวนมากซึ่งฉันอาจต้องส่งออกและพูดคุยกับผู้อื่นดังนั้นจึงเป็นการดีที่จะสามารถจัดหาไฟล์ Rmd หลายไฟล์สำหรับทุกขั้นตอนของการวิเคราะห์
stats-hb

knitr แสดงข้อผิดพลาด "ข้อผิดพลาด: แพ็กเกจที่จำเป็นขาดหายไป" เมื่อแสดงไฟล์. rmd ฉันต้องรันโค้ดในไฟล์. rmd เพื่อค้นหาข้อความแสดงข้อผิดพลาดจริงที่มีชื่อของแพ็กเกจที่หายไป caretต้องใช้หนึ่งกรณีkernlabกับ svm
CW

19

แยกรหัสทั่วไปออกเป็นไฟล์ R แยกต่างหากจากนั้นซอร์สไฟล์ R นั้นลงในไฟล์ Rmd แต่ละไฟล์ที่คุณต้องการ

ตัวอย่างเช่นสมมติว่าฉันมีรายงานสองฉบับที่ฉันต้องทำคือ Flu Outbreaks และ Guns vs Butter Analysis โดยปกติฉันจะสร้างเอกสาร Rmd สองฉบับและทำด้วยเอกสารนั้น

ตอนนี้สมมติว่าเจ้านายเข้ามาและต้องการเห็นรูปแบบของการระบาดของไข้หวัดใหญ่เมื่อเทียบกับราคาบัตเตอร์ (ควบคุมสำหรับกระสุน 9 มม.)

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

วิธีแก้ปัญหาของฉันคือแยกโครงการเป็นไฟล์เหล่านี้:

  • ไข้หวัดใหญ่
    • flu_data_import.R
  • Guns_N_Butter.Rmd
    • gun_data_import.R
    • butter_data_import.R

ภายในไฟล์ Rmd แต่ละไฟล์ฉันมีบางอย่างเช่น:

```{r include=FALSE}
source('flu_data_import.R')
```

ปัญหาคือเราสูญเสียความสามารถในการทำซ้ำ วิธีแก้ปัญหาของฉันคือการสร้างเอกสารลูกทั่วไปเพื่อรวมไว้ในไฟล์ Rmd แต่ละไฟล์ ดังนั้นในตอนท้ายของทุกไฟล์ Rmd ที่ฉันสร้างฉันเพิ่มสิ่งนี้:

```{r autodoc, child='autodoc.Rmd', eval=TRUE}
``` 

และแน่นอน autodoc.Rmd:

Source Data & Code
----------------------------
<div id="accordion-start"></div>

```{r sourcedata, echo=FALSE, results='asis', warnings=FALSE}

if(!exists(autodoc.skip.df)) {
  autodoc.skip.df <- list()
}

#Generate the following table:
for (i in ls(.GlobalEnv)) {
  if(!i %in% autodoc.skip.df) {
    itm <- tryCatch(get(i), error=function(e) NA )
    if(typeof(itm)=="list") {
      if(is.data.frame(itm)) {
        cat(sprintf("### %s\n", i))
        print(xtable(itm), type="html", include.rownames=FALSE, html.table.attributes=sprintf("class='exportable' id='%s'", i))
      }
    }
  }
}
```
### Source Code
```{r allsource, echo=FALSE, results='asis', warning=FALSE, cache=FALSE}
fns <- unique(c(compact(llply(.data=llply(.data=ls(all.names=TRUE), .fun=function(x) {a<-get(x); c(normalizePath(getSrcDirectory(a)),getSrcFilename(a))}), .fun=function(x) { if(length(x)>0) { x } } )), llply(names(sourced), function(x) c(normalizePath(dirname(x)), basename(x)))))

for (itm in fns) {
  cat(sprintf("#### %s\n", itm[2]))
  cat("\n```{r eval=FALSE}\n")
  cat(paste(tryCatch(readLines(file.path(itm[1], itm[2])), error=function(e) sprintf("Could not read source file named %s", file.path(itm[1], itm[2]))), sep="\n", collapse="\n"))
  cat("\n```\n")
}
```
<div id="accordion-stop"></div>
<script type="text/javascript">
```{r jqueryinclude, echo=FALSE, results='asis', warning=FALSE}
cat(readLines(url("http://code.jquery.com/jquery-1.9.1.min.js")), sep="\n")
```
</script>
<script type="text/javascript">
```{r tablesorterinclude, echo=FALSE, results='asis', warning=FALSE}
cat(readLines(url("http://tablesorter.com/__jquery.tablesorter.js")), sep="\n")
```
</script>
<script type="text/javascript">
```{r jqueryuiinclude, echo=FALSE, results='asis', warning=FALSE}
cat(readLines(url("http://code.jquery.com/ui/1.10.2/jquery-ui.min.js")), sep="\n")
```
</script>
<script type="text/javascript">
```{r table2csvinclude, echo=FALSE, results='asis', warning=FALSE}
cat(readLines(file.path(jspath, "table2csv.js")), sep="\n")
```
</script>
<script type="text/javascript">
  $(document).ready(function() {
  $('tr').has('th').wrap('<thead></thead>');
  $('table').each(function() { $('thead', this).prependTo(this); } );
  $('table').addClass('tablesorter');$('table').tablesorter();});
  //need to put this before the accordion stuff because the panels being hidden makes table2csv return null data
  $('table.exportable').each(function() {$(this).after('<a download="' + $(this).attr('id') + '.csv" href="data:application/csv;charset=utf-8,'+encodeURIComponent($(this).table2CSV({delivery:'value'}))+'">Download '+$(this).attr('id')+'</a>')});
  $('#accordion-start').nextUntil('#accordion-stop').wrapAll("<div id='accordion'></div>");
  $('#accordion > h3').each(function() { $(this).nextUntil('h3').wrapAll("<div>"); });
  $( '#accordion' ).accordion({ heightStyle: "content", collapsible: true, active: false });
</script>

NB นี้ออกแบบมาสำหรับเวิร์กโฟลว์ Rmd -> html นี่จะเป็นเรื่องที่น่าเกลียดถ้าคุณใช้น้ำยางหรือสิ่งอื่นใด เอกสาร Rmd นี้จะตรวจสอบสภาพแวดล้อมส่วนกลางสำหรับไฟล์ source () 'ed ทั้งหมดและรวมซอร์สไว้ที่ท้ายเอกสารของคุณ ประกอบด้วย jquery ui, tableorter และตั้งค่าเอกสารให้ใช้สไตล์หีบเพลงเพื่อแสดง / ซ่อนไฟล์ที่มา กำลังดำเนินการอยู่ แต่อย่าลังเลที่จะปรับให้เข้ากับการใช้งานของคุณเอง

ไม่ใช่ซับเดียวฉันรู้ หวังว่ามันจะให้ความคิดกับคุณอย่างน้อย :)


4

อาจเป็นไปได้ว่าควรเริ่มคิดต่าง ปัญหาของฉันมีดังต่อไปนี้: เขียนทุกรหัสที่ปกติจะมีเป็นกลุ่ม. และสำหรับเอกสาร Rmd ที่คุณใช้ในการถักเช่น html คุณจะเหลือเพียง

```{R Chunkname, Chunkoptions}  
source(file.R)  
```

ด้วยวิธีนี้คุณจะสร้างไฟล์. R จำนวนมากและคุณจะเสียประโยชน์ในการประมวลผลโค้ดทั้งหมด "chunk after chunk" โดยใช้ ctrl + alt + n (หรือ + c แต่โดยปกติจะใช้ไม่ได้) แต่ฉันอ่านหนังสือเกี่ยวกับงานวิจัยที่ทำซ้ำได้โดย Mr. Gandrud และตระหนักว่าเขาใช้ไฟล์ knitr และ. rmd ในการสร้างไฟล์ html เท่านั้น การวิเคราะห์หลักคือไฟล์. R ฉันคิดว่าเอกสาร. rm มีขนาดใหญ่เกินไปอย่างรวดเร็วหากคุณเริ่มทำการวิเคราะห์ทั้งหมดภายใน


3

หากคุณอยู่หลังรหัสฉันคิดว่าบางอย่างตามบรรทัดเหล่านี้ควรใช้งานได้:

  1. อ่านไฟล์ markdown / R ด้วยไฟล์ readLines
  2. ใช้grepเพื่อค้นหาโค้ดส่วนค้นหาบรรทัดที่ขึ้นต้นด้วย<<<เช่น
  3. ใช้ชุดย่อยของอ็อบเจ็กต์ที่มีบรรทัดดั้งเดิมเพื่อรับเฉพาะโค้ด
  4. ถ่ายโอนข้อมูลนี้ไปยังไฟล์ชั่วคราวโดยใช้ writeLines
  5. ซอร์สไฟล์นี้ในเซสชัน R ของคุณ

การห่อสิ่งนี้ในฟังก์ชันควรให้สิ่งที่คุณต้องการ


1
ขอบคุณฉันเดาว่าจะได้ผล อย่างไรก็ตามสี่คะแนนแรกฟังดูเหมือนสิ่งที่ Stangle ทำอยู่แล้วในวิธีที่เชื่อถือได้สำหรับ Sweave และสิ่งที่knit('myfile.rmd', tangle=TRUE)ทำใน Knitr ฉันเดาว่าฉันกำลังมองหาซับเดียวที่ทั้งพันกันและแหล่งที่มาและไม่ต้องสร้างไฟล์ใด ๆ
Jeromy Anglim

เมื่อคุณห่อมันในฟังก์ชันมันจะกลายเป็น oneliner;) สิ่งที่คุณทำได้คือใช้textConnectionเพื่อเลียนแบบไฟล์และแหล่งที่มาจากไฟล์นั้น สิ่งนี้จะหลีกเลี่ยงการสร้างไฟล์
Paul Hiemstra

ใช่. textConnectionอาจเป็นสถานที่ที่น่ามอง
Jeromy Anglim

2

แฮ็คต่อไปนี้ใช้งานได้ดีสำหรับฉัน:

library(readr)
library(stringr)
source_rmd <- function(file_path) {
  stopifnot(is.character(file_path) && length(file_path) == 1)
  .tmpfile <- tempfile(fileext = ".R")
  .con <- file(.tmpfile) 
  on.exit(close(.con))
  full_rmd <- read_file(file_path)
  codes <- str_match_all(string = full_rmd, pattern = "```(?s)\\{r[^{}]*\\}\\s*\\n(.*?)```")
  stopifnot(length(codes) == 1 && ncol(codes[[1]]) == 2)
  codes <- paste(codes[[1]][, 2], collapse = "\n")
  writeLines(codes, .con)
  flush(.con)
  cat(sprintf("R code extracted to tempfile: %s\nSourcing tempfile...", .tmpfile))
  source(.tmpfile)
}

2

ฉันใช้ฟังก์ชันที่กำหนดเองต่อไปนี้

source_rmd <- function(rmd_file){
  knitr::knit(rmd_file, output = tempfile())
}

source_rmd("munge_script.Rmd")


1

ฉันขอแนะนำให้เก็บรหัสการวิเคราะห์และการคำนวณหลักไว้ในไฟล์. R และนำเข้าชิ้นส่วนตามต้องการในไฟล์. Rmd ฉันได้อธิบายกระบวนการแล้วที่นี่แล้ว


1

sys.source ("./ your_script_file_name.R", envir = knitr :: knit_global ())

ใส่คำสั่งนี้ก่อนเรียกใช้ฟังก์ชันที่มีอยู่ใน your_script_file_name.R

การเพิ่ม "./" ก่อน your_script_file_name.R เพื่อแสดงทิศทางไปยังไฟล์ของคุณหากคุณได้สร้างโครงการไว้แล้ว

คุณสามารถดูรายละเอียดเพิ่มเติมได้ที่ลิงค์นี้: https://bookdown.org/yihui/rmarkdown-cookbook/source-script.html



โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.