Як отримати файл 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'можна було б пропустити, якщо бажаний результат. Тобто, в'язальник виводить код R з myfile.rmdна myfile.R.

Однак це не здається ідеальним:

  • це призводить до створення додаткового файлу
  • він повинен відображатися у власному фрагменті коду, якщо потрібен контроль над дисплеєм.
  • Це не так елегантно, як просто source(...).

Отже, моє запитання: чи існує більш елегантний спосіб отримання коду R файлу R Markdown?


Насправді мені дуже важко зрозуміти ваше запитання (я прочитав його кілька разів). Ви можете легко завантажити інші сценарії R у Rmdфайл. Але ви також хочете шукати в інших markdownфайлах файл, який в'яжеться?
Maiasaura

4
Я хочу отримати код R всередині фрагментів коду R у файлах R Markdown (тобто * .rmd)? Я трохи відредагував запитання, щоб спробувати зрозуміти речі.
Jeromy Anglim

Щось на зразок includeлатексу. Якщо націнка підтримує включення інших документів щодо націнки, створити таку функцію має бути відносно просто.
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, я вважаю, що це корисно, оскільки іноді ваш аналіз може бути організований невеликими сценаріями, але у своєму звіті ви хочете мати код для всього конвеєру.
лукацерон

9
Отже, тут використовується випадок, що ви хочете написати весь код, щоб він був детально задокументований і пояснений, але код запускається за допомогою іншого сценарію.
Brash Equilibrium

4
@BrashEquilibrium Це питання використання source()або knitr::knit()запуску коду. Я знаю, що люди менш обізнані з останніми, але purl()вони не є надійними. Вас попереджали: github.com/yihui/knitr/pull/812#issuecomment-53088636
Yihui Xie

5
@Yihui Якою буде запропонована альтернатива "джерелу (виворіт (х, ...))" на ваш погляд? Як одне джерело може мати декілька файлів * .Rmd, не натрапивши на помилку щодо повторюваних міток фрагментів? Я волів би не хотіти повертатися до документа, який потрібно отримати, і в'язати його. Я використовую * .Rmd для багатьох файлів, які мені потенційно доводиться експортувати та обговорювати з іншими, тому було б чудово мати можливість отримувати кілька Rmd-файлів для всіх етапів аналізу.
stats-hb

knitr видає помилку "Помилка: відсутній необхідний пакет", коли він відображає файл .rmd. Мені потрібно виконати код у файлі .rmd, щоб знайти справжнє повідомлення про помилку, що містить ім'я відсутнього пакета. Для svm caretпотрібен один випадок kernlab.
Чарльз

19

Розділіть загальний код на окремий R-файл, а потім поставте цей R-файл на кожен Rmd-файл, у який ви хочете.

так, скажімо, скажімо, у мене є два звіти, які мені потрібно скласти - спалахи грипу та аналіз пістолетів проти масла Звичайно, я б створив два документи Rmd і закінчив би з цим.

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

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

Моє рішення було розбити проект на такі файли:

  • Грип
    • 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>

Примітка: це розроблено для робочого процесу Rmd -> html. Це буде потворна каша, якщо ви підете з латексом або чимсь іншим. Цей Rmd-документ переглядає глобальне середовище для всіх вихідних () випущених файлів та включає їх джерело в кінці вашого документа. Він включає jquery ui, tablesorter і встановлює документ для використання стилю акордеона для показу / приховування файлів джерела. Це незавершена робота, але сміливо пристосовуйте її до власного використання.

Я не однокласник, я знаю. Сподіваюся, це принаймні дасть вам деякі ідеї


4

Напевно, слід почати думати по-іншому. Моя проблема полягає в наступному: Напишіть кожен код, який ви зазвичай мали б, у фрагменті .Rmd у файлі .R. А для документа Rmd, який ви використовуєте для в’язання, тобто html, вам залишається лише

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

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


3

Якщо ви просто після коду, я думаю, що щось у цьому напрямку повинно працювати:

  1. Прочитайте файл розмітки / R за допомогою readLines
  2. Використовуйте , grepщоб знайти шматки коду, пошук рядка , які починаються з <<<, наприклад ,
  3. Візьміть підмножину об'єкта, що містить оригінальні рядки, щоб отримати лише код
  4. Дамп цього у тимчасовий файл за допомогою writeLines
  5. Джерело цього файлу до сесії R

Обернення цього у функцію має дати вам те, що вам потрібно.


1
Дякую, мабуть, це спрацювало б. Однак перші чотири пункти звучать так, як Stangle вже робить надійний спосіб для Sweave і що knit('myfile.rmd', tangle=TRUE)робить у в’язальниці. Я думаю, що я шукаю один лайнер, який одночасно заплутує і джерела, і в ідеалі не створює файлів.
Jeromy Anglim

Після того, як ви обернете його у функцію, він стає однолінійним;). Що ви могли б зробити, це використовувати textConnectionдля імітації файлу та джерело з нього. Це дозволить уникнути створення файлу.
Paul Hiemstra,

Так. textConnectionможливо місце для пошуку.
Джеромі Англім,

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

2

Спробуйте функцію вивороту від в'язальниці:

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


1

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


1

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

поставте цю команду перед викликом функцій, що містяться у вашому_файлі_назви_файла.R.

додавання "./" перед your_script_file_name.R, щоб показати напрямок до вашого файлу, якщо ви вже створили проект.

Ви можете переглянути це посилання для більш детальної інформації: https://bookdown.org/yihui/rmarkdown-cookbook/source-script.html



Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.