Làm cách nào để tạo nguồn cho tệp R Markdown như `source ('myfile.r') '?


89

Tôi thường có một tệp R Markdown chính hoặc tệp LaTeX trong đó tôi có sourcemột số tệp R khác (ví dụ: để xử lý dữ liệu). Tuy nhiên, tôi đã nghĩ rằng trong một số trường hợp, sẽ có lợi nếu các tệp có nguồn gốc này là tài liệu có thể tái tạo của chính chúng (ví dụ: tệp R Markdown không chỉ bao gồm các lệnh để xử lý dữ liệu mà còn tạo ra một tài liệu có thể tái tạo giải thích các quyết định xử lý dữ liệu ).

Vì vậy, tôi muốn có một lệnh như source('myfile.rmd')trong tệp R Markdown chính của mình. sẽ trích xuất và mã nguồn tất cả mã R bên trong các đoạn mã R của myfile.rmd. Tất nhiên, điều này làm phát sinh lỗi.

Lệnh sau hoạt động:

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

nơi results='hide'có thể được bỏ qua nếu đầu ra được mong muốn. Tức là, dao xuất mã R từ myfile.rmdvào myfile.R.

Tuy nhiên, nó có vẻ không hoàn hảo:

  • nó dẫn đến việc tạo ra một tệp bổ sung
  • nó cần xuất hiện trong đoạn mã riêng của nó nếu yêu cầu kiểm soát màn hình.
  • Nó không thanh lịch như đơn giản source(...).

Vì vậy, câu hỏi của tôi: Có cách nào thanh lịch hơn để tìm nguồn mã R của tệp R Markdown không?


Tôi thực sự đang rất khó hiểu câu hỏi của bạn (tôi đã đọc nó vài lần). Bạn có thể dễ dàng nguồn các tập lệnh R khác vào một Rmdtệp. Nhưng bạn cũng muốn ghi nguồn trong các markdowntệp khác vào một tệp được dệt kim?
Maiasaura

4
Tôi muốn nguồn mã R bên trong các đoạn mã R trong tệp R Markdown (tức là * .rmd)? Tôi đã chỉnh sửa câu hỏi một chút để cố gắng làm cho mọi thứ rõ ràng hơn.
Jeromy Anglim

Một cái gì đó dọc theo các đường của includemủ. Nếu markdown hỗ trợ bao gồm các tài liệu đánh dấu khác, thì việc tạo một hàm như vậy sẽ tương đối dễ dàng.
Paul Hiemstra

@PaulHiemstra Tôi đoán rằng khả năng tạo nguồn văn bản và các đoạn mã R cũng sẽ hữu ích. Tôi đặc biệt đang nghĩ đến việc chỉ tìm nguồn cung cấp mã trong tài liệu R Markdown.
Jeromy Anglim

Câu trả lời:


35

Có vẻ như bạn đang tìm kiếm một lớp lót. Làm thế nào về việc đưa cái này vào của bạn .Rprofile?

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

Tuy nhiên, tôi không hiểu tại sao bạn muốn source()mã trong chính tệp Rmd. Ý tôi là knit()sẽ chạy tất cả mã trong tài liệu này và nếu bạn trích xuất mã và chạy nó trong một đoạn, tất cả mã sẽ được chạy hai lần khi bạn knit()tài liệu này (bạn tự chạy bên trong). Hai nhiệm vụ phải được tách biệt.

Nếu bạn thực sự muốn chạy tất cả các mã, RStudio đã làm được điều này khá dễ dàng: Ctrl + Shift + R. Về cơ bản nó gọi purl()source()đằng sau cảnh.


8
Xin chào @Yihui Tôi nghĩ điều này rất hữu ích vì đôi khi phân tích của bạn có thể được sắp xếp thành các đoạn mã nhỏ, nhưng trong báo cáo của bạn, bạn muốn có mã cho toàn bộ quy trình.
lucacerone

9
Vì vậy, trường hợp sử dụng ở đây là bạn muốn viết tất cả mã và có nhiều tài liệu và giải thích, nhưng mã được chạy bởi một số tập lệnh khác.
Cân bằng Brash

4
@BrashEquilbalance Vấn đề là sử dụng source()hoặc knitr::knit()chạy mã. Tôi biết mọi người ít quen thuộc hơn với cái sau, nhưng purl()không đáng tin cậy. Bạn đã được cảnh báo: github.com/yihui/knitr/pull/812#issuecomment-53088636
Yihui Xie

5
@Yihui Giải pháp thay thế được đề xuất cho 'nguồn (kim tuyến (x, ...))' theo quan điểm của bạn là gì? Làm thế nào để một nguồn có nhiều * .Rmd-Files mà không gặp lỗi liên quan đến nhãn phân đoạn trùng lặp? Tôi không muốn quay lại tài liệu có nguồn gốc và đan nó. Tôi sử dụng * .Rmd cho nhiều tệp mà tôi có thể phải xuất và thảo luận với những người khác, vì vậy sẽ thật tuyệt nếu có thể tạo nguồn nhiều Tệp Rmd ​​cho tất cả các bước phân tích.
stats-hb

Dantr phát ra lỗi "Lỗi: Gói bắt buộc bị thiếu", khi nó hiển thị tệp .rmd. Tôi phải thực thi mã trong tệp .rmd để tìm thông báo lỗi thực sự có chứa tên của gói bị thiếu. Một trường hợp được caretyêu cầu kernlabvới svm.
Charles

19

Phân loại mã chung thành một tệp R riêng biệt, rồi nguồn tệp R đó vào mỗi tệp Rmd ​​mà bạn muốn.

vì vậy, ví dụ, giả sử tôi có hai báo cáo cần thực hiện, Dịch cúm và Phân tích Súng vs Bơ. Đương nhiên, tôi sẽ tạo hai tài liệu Rmd và được thực hiện với nó.

Bây giờ, giả sử sếp đi cùng và muốn xem các biến thể của Dịch cúm so với giá Bơ (kiểm soát đạn 9mm).

  • Sao chép và dán mã để phân tích báo cáo vào báo cáo mới là một ý tưởng tồi cho việc sử dụng lại mã, v.v.
  • Tôi muốn nó trông đẹp.

Giải pháp của tôi là đưa dự án vào các tệp sau:

  • Flu.Rmd
    • flu_data_import.R
  • Guns_N_Butter.Rmd
    • gun_data_import.R
    • butter_data_import.R

trong mỗi tệp Rmd, tôi sẽ có một cái gì đó như:

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

Vấn đề ở đây là chúng ta mất khả năng tái tạo. Giải pháp của tôi là tạo một tài liệu con chung để đưa vào mỗi tệp Rmd. Vì vậy, ở cuối mỗi tệp Rmd ​​tôi tạo, tôi thêm đoạn này:

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

Và tất nhiên, 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, điều này được thiết kế cho quy trình làm việc Rmd -> html. Đây sẽ là một mớ hỗn độn xấu xí nếu bạn đi với latex hoặc bất cứ thứ gì khác. Tài liệu Rmd này xem xét môi trường chung cho tất cả các tệp ed source () 'và bao gồm nguồn của chúng ở cuối tài liệu của bạn. Nó bao gồm jquery ui, tableorter và thiết lập tài liệu để sử dụng kiểu accordion để hiển thị / ẩn các tệp có nguồn gốc. Đó là một công việc đang được tiến hành, nhưng hãy thoải mái điều chỉnh nó cho phù hợp với mục đích sử dụng của bạn.

Không phải là một lớp lót, tôi biết. Hy vọng nó cung cấp cho bạn một số ý tưởng ít nhất :)


4

Có lẽ người ta nên bắt đầu suy nghĩ khác. Vấn đề của tôi là như sau: Viết mọi mã bạn thường có trong một đoạn .Rmd trong tệp .R. Và đối với tài liệu Rmd mà bạn sử dụng để đan, tức là một html, bạn chỉ còn lại

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

Bằng cách này, bạn có thể sẽ tạo một loạt các tệp .R và bạn sẽ mất lợi thế khi xử lý tất cả mã "chunk sau chunk" bằng cách sử dụng ctrl + alt + n (hoặc + c, nhưng thông thường điều này không hoạt động). Tuy nhiên, tôi đã đọc cuốn sách về nghiên cứu có thể tái tạo của ông Gandrud và nhận ra rằng ông ấy chắc chắn chỉ sử dụng các tệp nội dung và .Rmd để tạo các tệp html. Bản thân Phân tích Chính là một tệp .R. Tôi nghĩ rằng tài liệu .Rmd nhanh chóng phát triển quá lớn nếu bạn bắt đầu thực hiện toàn bộ phân tích bên trong.


3

Nếu bạn chỉ theo dõi mã, tôi nghĩ rằng một số thứ dọc theo những dòng này sẽ hoạt động:

  1. Đọc tệp markdown / R với readLines
  2. Sử dụng grepđể tìm các đoạn mã, tìm kiếm các dòng bắt đầu bằng <<<ví dụ
  3. Lấy tập hợp con của đối tượng chứa các dòng gốc để chỉ lấy mã
  4. Đưa tệp này vào tệp tạm thời bằng cách sử dụng writeLines
  5. Nguồn tệp này vào phiên R của bạn

Bao bọc điều này trong một hàm sẽ cung cấp cho bạn những gì bạn cần.


1
Cảm ơn bạn, tôi đoán điều đó sẽ hiệu quả. Tuy nhiên, bốn điểm đầu tiên nghe có vẻ giống như những gì Stangle đã làm một cách đáng tin cậy đối với Sweave và những gì knit('myfile.rmd', tangle=TRUE)làm được trong nội dung. Tôi đoán tôi đang tìm kiếm một lớp lót vừa rối vừa tạo nguồn và lý tưởng là không tạo tệp nào.
Jeromy Anglim

Khi bạn bọc nó trong một hàm, nó sẽ trở thành một đường thẳng;). Những gì bạn có thể làm là sử dụng textConnectionđể bắt chước một tệp và lấy nguồn từ đó. Điều này sẽ tránh một tệp được tạo.
Paul Hiemstra

Đúng. textConnectioncó thể là nơi để xem xét.
Jeromy Anglim

2

Bản hack sau hoạt động tốt đối với tôi:

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

Tôi sử dụng chức năng tùy chỉnh sau

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

source_rmd("munge_script.Rmd")

2

Hãy thử chức năng kim tuyến từ precisiontr:

source(knitr::purl("myfile.rmd", quiet=TRUE))


1

Tôi khuyên bạn nên giữ mã phân tích và tính toán chính trong tệp .R và nhập các phần nếu cần vào tệp .Rmd. Tôi đã giải thích quá trình ở đây .


1

sys.source ("./ your_script_file_name.R", envir = domainstr :: đan_global ())

đặt lệnh này trước khi gọi các hàm có trong your_script_file_name.R.

"./" thêm vào trước your_script_file_name.R để hiển thị hướng đến tệp của bạn nếu bạn đã tạo một Dự án.

Bạn có thể xem liên kết này để biết thêm chi tiết: https://bookdown.org/yihui/rmarkdown-cookbook/source-script.html



Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.