R Markdown dosyası `` source ('myfile.r') `gibi nasıl kaynaklanır?


89

Genellikle bir ana R Markdown dosyam veya sourcebaşka bir R dosyası (örneğin, veri işleme için) olan örgü LaTeX dosyam olur . Bununla birlikte, bazı durumlarda bu kaynaklı dosyaların kendi çoğaltılabilir belgeleri olmasının yararlı olacağını düşünüyordum (örneğin, yalnızca veri işleme için komutlar içeren değil, aynı zamanda veri işleme kararlarını açıklayan yeniden üretilebilir bir belge üreten bir R Markdown dosyası) ).

Bu nedenle, source('myfile.rmd')ana R Markdown dosyamdaki gibi bir komuta sahip olmak istiyorum . Bu, R kod parçalarının içindeki tüm R kodunu çıkarır ve kaynak sağlar myfile.rmd. Elbette bu bir hataya neden olur.

Aşağıdaki komut çalışır:

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

results='hide'çıktı istenirse nerede ihmal edilebilir. Yani, knitr R kodunu içinden myfile.rmdiçine çıkarır myfile.R.

Ancak mükemmel görünmüyor:

  • fazladan bir dosyanın oluşturulmasıyla sonuçlanır
  • ekran üzerinde kontrol gerekliyse, kendi kod yığınında görünmesi gerekir.
  • O kadar basit değil source(...).

Böylece sorum: Bir R Markdown dosyasının R kodunu elde etmenin daha zarif bir yolu var mı?


Aslında sorunuzu anlamakta gerçekten zorlanıyorum (birkaç kez okudum). Diğer R komut dosyalarını kolayca bir Rmddosyaya kaynaklayabilirsiniz. Ama diğer markdowndosyalarda örülen bir dosyaya kaynak mı eklemek istiyorsunuz ?
Maiasaura

4
R Markdown dosyalarındaki (yani, * .rmd) R kodu yığınlarının içindeki R kodunu kaynaklamak istiyorum? İşleri daha net hale getirmek için soruyu biraz düzenledim.
Jeromy Anglim

includeLateks çizgisinde bir şey . Markdown, diğer markdown belgelerinin dahil edilmesini destekliyorsa, böyle bir işlevi oluşturmak nispeten kolay olmalıdır.
Paul Hiemstra

@PaulHiemstra Metni ve R kodu parçalarını kaynaklama yeteneğinin de yararlı olacağını tahmin ediyorum. Özellikle bir R Markdown belgesindeki kodu kaynak sağlamayı düşünüyorum.
Jeromy Anglim

Yanıtlar:


35

Görünüşe göre tek astar arıyorsunuz. Bunu senin içine koymaya ne dersin .Rprofile?

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

Ancak, neden source()Rmd dosyasındaki kodu istediğinizi anlamıyorum . Demek istediğim knit(), bu belgedeki tüm kodu çalıştıracak ve kodu çıkarır ve bir yığın halinde çalıştırırsanız, knit()bu belgeyi oluşturduğunuzda tüm kod iki kez çalıştırılır (kendi içinde çalıştırırsınız). İki görev ayrı olmalıdır.

Gerçekten bütün kod çalıştırmak istiyorsanız, RStudio bu oldukça kolay yapmıştır: Ctrl + Shift + R. Temelde arar purl()ve source()sahnenin arkasında.


8
Merhaba @ Yihui Bence yararlıdır çünkü bazen analiziniz küçük betiklerde düzenlenebilir, ancak raporunuzda tüm boru hattı için koda sahip olmak istersiniz.
lucacerone

9
Yani buradaki kullanım durumu, tüm kodu yazmak ve kapsamlı bir şekilde belgelendirilmesini ve açıklanmasını istemenizdir, ancak kod başka bir komut dosyası tarafından çalıştırılır.
Brash Equilibrium

4
@BrashEquilibrium Bu, kodu kullanma source()veya knitr::knit()çalıştırma meselesidir . İnsanların ikincisine daha az aşina purl()olduğunu ama güvenilir olmadığını biliyorum . Uyarıldınız: github.com/yihui/knitr/pull/812#issuecomment-53088636
Yihui Xie

5
@Yihui Size göre 'kaynak (purl (x, ...))' için önerilen alternatif ne olabilir? Yinelenen yığın etiketleriyle ilgili bir hatayla karşılaşmadan birden fazla * .Rmd-Dosyasını nasıl kaynaklayabilirim? Kaynak alınacak belgeye geri dönüp onu örmek istemem. Muhtemelen dışa aktarmam ve başkalarıyla tartışmam gereken birçok dosya için * .Rmd kullanıyorum, bu nedenle analizin tüm adımları için birden fazla Rmd-Dosyasını kaynaklamak harika olurdu.
istatistik-hb

knitr, .rmd dosyasını oluştururken "Hata: Gerekli paket eksik" hatası veriyor. Eksik paketin adını içeren gerçek hata mesajını bulmak için .rmd dosyasında kod yürütmem gerekiyor. Svm ile bir durum caretgereklidir kernlab.
CW

19

Ortak kodu ayrı bir R dosyasına ayırın ve ardından bu R dosyasını, içinde olmasını istediğiniz her Rmd dosyasına kaynaklayın.

Örneğin, hazırlamam gereken iki rapor olduğunu varsayalım, Grip Salgınları ve Silahlar ve Tereyağı Analizi. Doğal olarak iki Rmd belgesi oluşturup onunla işim biterdi.

Şimdi, patronun gelip Grip Salgınları ile Tereyağı fiyatlarının varyasyonlarını görmek istediğini varsayalım (9 mm cephaneyi kontrol ediyor).

  • Raporları analiz etmek için kodu kopyalayıp yeni rapora yapıştırmak, kodun yeniden kullanımı vb. İçin kötü bir fikirdir.
  • Güzel görünmesini istiyorum.

Çözümüm, projeyi şu dosyalara dahil etmekti:

  • Grip.Rmd
    • flu_data_import.R
  • Guns_N_Butter.Rmd
    • guns_data_import.R
    • butter_data_import.R

her Rmd dosyasında şöyle bir şey olurdu:

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

Buradaki sorun, tekrarlanabilirliği kaybetmemizdir. Buna çözümüm, her Rmd dosyasına dahil edilecek ortak bir alt belge oluşturmaktır. Oluşturduğum her Rmd dosyasının sonuna şunu ekliyorum:

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

Ve tabii ki 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, bu Rmd -> html iş akışı için tasarlanmıştır. Lateks veya başka bir şeyle giderseniz bu çirkin bir karışıklık olacaktır. Bu Rmd belgesi, tüm kaynak () 'ed dosyaları için küresel ortama bakar ve bunların kaynağını belgenizin sonunda içerir. Jquery ui, tableorter içerir ve belgeyi, kaynaklı dosyaları göstermek / gizlemek için bir akordeon stili kullanacak şekilde ayarlar. Devam eden bir çalışmadır, ancak kendi kullanımlarınıza uyarlamaktan çekinmeyin.

Tek satırlık değil, biliyorum. Umarım size en azından bazı fikirler verir :)


4

Muhtemelen farklı düşünmeye başlanmalı. Benim sorunum şudur: Normalde bir .Rmd yığınında sahip olabileceğiniz her kodu bir .R dosyasında yazın. Ve örmek için kullandığınız Rmd belgesi, yani bir html için, yalnızca

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

Bu şekilde, büyük olasılıkla bir grup .R dosyası oluşturursunuz ve ctrl + alt + n (veya + c, ancak normalde bu çalışmaz) kullanarak tüm "öbek öbek" kodunu işleme avantajını kaybedersiniz. Ancak, Bay Gandrud'un tekrarlanabilir araştırmalarla ilgili kitabını okudum ve kesinlikle knitr ve .Rmd dosyalarını sadece html dosyaları oluşturmak için kullandığını fark ettim. Ana Analizin kendisi bir .R dosyasıdır. Tüm analizinizi içeride yapmaya başlarsanız .Rmd belgelerinin hızla çok büyüdüğünü düşünüyorum.


3

Kodun hemen peşindeyseniz, şu satırlardaki bir şeyin çalışması gerektiğini düşünüyorum:

  1. Markdown / R dosyasını şununla okuyun: readLines
  2. Örneğin grepile başlayan satırları arayarak kod parçalarını bulmak için kullanın<<<
  3. Yalnızca kodu almak için orijinal satırları içeren nesnenin alt kümesini alın
  4. Bunu kullanarak geçici bir dosyaya boşaltın writeLines
  5. Bu dosyayı R oturumunuza kaynaklayın

Bunu bir fonksiyona sarmak size ihtiyacınız olanı vermelidir.


1
Teşekkür ederim, sanırım bu işe yarar. Bununla birlikte, ilk dört nokta, Stangle'ın Sweave için zaten güvenilir bir şekilde knit('myfile.rmd', tangle=TRUE)yaptığı ve örgüde ne yaptığı gibi geliyor. Sanırım hem karışan hem de kaynak oluşturan ve ideal olarak dosya oluşturmayan tek bir astar arıyorum.
Jeromy Anglim

Onu bir işleve sardığınızda oneliner olur;). Yapabileceğiniz şey textConnectionbir dosyayı taklit etmek ve bundan kaynak yapmaktır . Bu, bir dosyanın oluşturulmasını engeller.
Paul Hiemstra

Evet. textConnectionbakılacak yer olabilir.
Jeromy Anglim

2

Aşağıdaki hack benim için iyi çalıştı:

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

Aşağıdaki özel işlevi kullanıyorum

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

source_rmd("munge_script.Rmd")

2

Knitr'den ters çevirme işlevini deneyin:

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


1

Ana analiz ve hesaplama kodunu .R dosyasında tutmanızı ve parçaları gerektiği gibi .Rmd dosyasında içe aktarmanızı öneririm. Süreci burada anlattım .


1

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

bu komutu komut dosyası_dosyanızın_adı.R içinde bulunan işlevleri çağırmadan önce koyun.

Zaten bir Proje oluşturduysanız dosyanızın yönünü göstermek için your_script_file_name.R'den önce "./" eklenmesi.

Daha fazla ayrıntı için bu bağlantıyı görebilirsiniz: https://bookdown.org/yihui/rmarkdown-cookbook/source-script.html



Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.