developer tip

`source ( 'myfile.r')`과 같은 R Markdown 파일을 소싱하는 방법은 무엇입니까?

copycodes 2020. 10. 22. 08:13
반응형

`source ( 'myfile.r')`과 같은 R Markdown 파일을 소싱하는 방법은 무엇입니까?


나는 종종 source다른 R 파일 (예 : 데이터 처리 용)에 메인 R Markdown 파일이나 knitr LaTeX 파일을 가지고 있습니다. 그러나 어떤 경우에는 이러한 소스 파일을 자신의 재현 가능한 문서 (예 : 데이터 처리를위한 명령을 포함 할뿐만 아니라 데이터 처리를 설명하는 재현 가능한 문서를 생성하는 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는 R 코드 myfile.rmdmyfile.R.

그러나 완벽하지 않은 것 같습니다.

  • 추가 파일이 생성됩니다.
  • 디스플레이 제어가 필요한 경우 자체 코드 청크에 표시되어야합니다.
  • 단순한 것만 큼 우아하지는 않습니다 source(...).

따라서 내 질문 : R Markdown 파일의 R 코드를 소싱하는 더 우아한 방법이 있습니까?


한 줄짜리를 찾고 계신 것 같습니다. 이것을 당신의 .Rprofile?

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

그러나 source()Rmd 파일 자체의 코드 를 원하는 이유를 이해할 수 없습니다 . 내 말은 knit()이 문서에서 모든 코드를 실행하고 코드를 추출하고 덩어리에서 실행하는 경우, 모든 코드가 실행됩니다 두 번 때 knit()이 문서 (당신이 자신의 내부에 자신을 실행). 두 작업은 분리되어야합니다.

정말로 모든 코드를 실행하고 싶다면 RStudio가 이것을 상당히 쉽게 만들었습니다 Ctrl + Shift + R. 그것은 기본적으로 호출 purl()하고 source()장면 뒤에.


공통 코드를 별도의 R 파일로 추출한 다음 해당 R 파일을 원하는 각 Rmd 파일로 소싱합니다.

예를 들어 제가 작성해야하는 두 가지 보고서, 독감 발생 및 총기 대 버터 분석이 있다고 가정 해 보겠습니다. 당연히 두 개의 Rmd 문서를 작성하고 완료합니다.

이제 보스가 와서 독감 발발 대 버터 가격 (9mm 탄약 제어)의 차이를보고 싶다고 가정 해 보겠습니다.

  • 보고서를 새 보고서로 분석하기 위해 코드를 복사하고 붙여 넣는 것은 코드 재사용 등을 위해 나쁜 생각입니다.
  • 멋지게 보이길 원합니다.

내 해결책은 프로젝트를 다음 파일에 포함시키는 것입니다.

  • 독감 Rmd
    • flu_data_import.R
  • Guns_N_Butter.Rmd
    • guns_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>

N.B., this is designed for the Rmd -> html workflow. This will be an ugly mess if you go with latex or anything else. This Rmd document looks through the global environment for all the source()'ed files and includes their source at the end of your document. It includes jquery ui, tablesorter, and sets the document up to use an accordion style to show/hide sourced files. It's a work in progress, but feel free to adapt it to your own uses.

Not a one-liner, I know. Hope it gives you some ideas at least :)


Probably one should start thinking different. My issue is the following: Write every code you normally would have had in a .Rmd chunk in a .R file. And for the Rmd document you use to knit i.e. an html, you only have left

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

This way you'll probably create a bunch of .R files and you lose the advantage of processing all the code "chunk after chunk" using ctrl+alt+n (or +c, but normally this does not work). But, I read the book about reproducible research by Mr. Gandrud and realized, that he definitely uses knitr and .Rmd files solely for creating html files. The Main Analysis itself is an .R file. I think .Rmd documents rapidly grow too large if you start doing your whole analysis inside.


If you are just after the code I think something along these lines should work:

  1. Read the markdown/R file with readLines
  2. Use grep to find the code chunks, searching for lines that start with <<< for example
  3. Take subset of the object that contains the original lines to get only the code
  4. Dump this to a temporary file using writeLines
  5. Source this file into your R session

Wrapping this in a function should give you what you need.


The following hack worked fine for me:

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

I would recommend keeping the main analysis and calculation code in .R file and importing the chunks as needed in .Rmd file. I have explained the process here.


I use the following custom function

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

source_rmd("munge_script.Rmd")

참고URL : https://stackoverflow.com/questions/10966109/how-to-source-r-markdown-file-like-sourcemyfile-r

반응형