Подтвердить что ты не робот

Как исправить файл R Markdown, например `source ('myfile.r')`?

У меня часто есть основной файл R Markdown или файл 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' может быть опущено, если вывод был желательным. I.e., knitr выводит R-код из myfile.rmd в myfile.R.

Однако это не кажется идеальным:

  • это приводит к созданию дополнительного файла
  • он должен появиться в нем собственный фрагмент кода, если требуется контроль над отображением.
  • Это не так элегантно, как просто source(...).

Таким образом, мой вопрос: Есть ли более элегантный способ поиска R-кода файла R Markdown?

4b9b3361

Ответ 1

Кажется, вы ищете однострочный. Как поместить это в свой .Rprofile?

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

Однако я не понимаю, почему вы хотите source() код в самом файле Rmd. Я имею в виду, что knit() будет запускать весь код в этом документе, а если вы извлечете код и запустите его в куске, весь код будет запущен дважды, когда вы knit() этот документ (вы запустите себя внутри себя). Эти две задачи должны быть раздельными.

Если вы действительно хотите запустить весь код, RStudio сделал это довольно легко: Ctrl + Shift + R. В основном он вызывает purl() и source() за сценой.

Ответ 2

Кодируйте общий код в отдельный R файл, а затем отправляйте этот R файл в каждый Rmd файл, в котором вы хотите.

так, например, скажем, у меня есть два отчета, которые мне нужно сделать, "Вспышки гриппа" и "Пушки" против "Анализ масла". Естественно, я бы создал два документа Rmd и покончил с ними.

Теперь предположим, что босс приходит и хочет увидеть вариации цен на грипп против масла (контроль за 9-миллиметровыми боеприпасами).

  • Копирование и вставка кода для анализа отчетов в новый отчет - плохая идея повторного использования кода и т.д.
  • Я хочу, чтобы он выглядел красиво.

Мое решение состояло в том, чтобы включить проект в эти файлы:

  • Flu.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., он предназначен для рабочего процесса Rmd → html. Это будет уродливый беспорядок, если вы пойдете с латексом или чем-нибудь еще. Этот документ Rmd просматривает глобальную среду для всех исходных() файлов ed и включает их источник в конце вашего документа. Он включает jquery ui, tablesorter и устанавливает документ для использования стиля аккордеона для отображения/скрытия исходных файлов. Это незавершенная работа, но не стесняйтесь адаптировать ее к вашим собственным потребностям.

Я не знаю ни одного лайнера. Надеюсь, это даст вам несколько идей:)

Ответ 3

Вероятно, нужно начинать думать иначе. Моя проблема заключается в следующем: Напишите каждый код, который вы обычно имели бы в блоке .Rmd в файле .R. И для документа Rmd, который вы используете для вязания, т.е. Html, вы оставили только

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

Таким образом, вы, вероятно, создадите кучу файлов .R, и вы потеряете преимущество обработки всего кода "chunk after chunk" с помощью ctrl + alt + n (или + c, но обычно это не работает). Но я прочитал книгу о воспроизводимых исследованиях г-на Гандруда и понял, что он определенно использует файлы knitr и .Rmd исключительно для создания html файлов. Основной анализ сам по себе является .R файлом. Я думаю, что документы .Rmd быстро растут слишком сильно, если вы начнете весь свой анализ внутри.

Ответ 4

Если вы только после кода, я думаю, что что-то по этим строкам должно работать:

  • Прочитайте файл markdown/R с помощью readLines
  • Используйте grep, чтобы найти фрагменты кода, ища строки, начинающиеся с <<<, например
  • Возьмем подмножество объекта, который содержит исходные строки, чтобы получить только код
  • Дамп этого временного файла с помощью writeLines
  • Отправьте этот файл в свою сессию R

Обертка этого в функции должна дать вам то, что вам нужно.

Ответ 5

Следующий взлом работал отлично для меня:

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

Ответ 6

Я бы рекомендовал хранить основной код анализа и расчета в файле .R и импортировать куски по мере необходимости в файл .Rmd. Я объяснил процесс здесь.

Ответ 7

Я использую следующую пользовательскую функцию

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

source_rmd("munge_script.Rmd")