2012-06-10 9 views
47

Często mam główny plik R Markdown lub plik LaTeX knitr, gdzie I source jakiś inny plik R (np. Do przetwarzania danych). Jednak myślałem, że w niektórych przypadkach korzystne byłoby posiadanie tych plików źródłowych jako ich własnych powtarzalnych dokumentów (np. Plik R Markdown, który nie tylko zawiera polecenia do przetwarzania danych, ale także tworzy powtarzalny dokument wyjaśniający przetwarzanie danych decyzje).Jak pobrać plik R Markdown, taki jak `source ('myfile.r')`?

Tak więc, chciałbym mieć polecenie takie jak source('myfile.rmd') w moim głównym pliku R Markdown. który wyodrębniłby i pozyskał cały kod R wewnątrz fragmentów kodu R z myfile.rmd. Oczywiście powoduje to błąd.

następujące polecenie działa:

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

results='hide' gdzie mogłyby zostać pominięte, jeśli wyjście chciano. To znaczy, knitr wyprowadza kod R z myfile.rmd do myfile.R.

Jednak nie wydaje się idealne:

  • skutkuje utworzeniem dodatkowego pliku
  • musi pojawić się w jego własnym kawałku kodu, jeśli wymagana jest kontrola nad wyświetlaczem.
  • To nie jest tak eleganckie jak proste source(...).

Stąd moje pytanie: Czy jest bardziej eleganckim sposobem pozyskiwania kodu R do pliku Markdown R?

+0

ja faktycznie ma naprawdę trudno zrozumieć swoje pytanie (czytałem go kilka razy). Możesz łatwo pobrać inne skrypty R do pliku 'Rmd'. Ale czy chcesz także pozyskać w innych plikach 'markdown' pliki, które są dziane? – Maiasaura

+3

Chcę pobrać kod R wewnątrz fragmentów kodu R w plikach R Markdown (tj. * .rmd)? Trochę zmieniłem to pytanie, aby zrobić wszystko jaśniej. –

+0

Coś w rodzaju "zawierają" w lateksie. Jeśli przecena wspiera włączenie innych dokumentów przecenowych, utworzenie takiej funkcji powinno być stosunkowo łatwe. –

Odpowiedz

24

Wygląda na to, że szukasz jednej liniowej. Co powiesz na umieszczenie tego w swojej .Rprofile?

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

Jednak nie rozumiem, dlaczego chcesz source() kod w RMD złożyć sobie. Chodzi mi o to, że knit() uruchomi cały kod w tym dokumencie, a jeśli wyodrębnisz kod i uruchomisz go w kawałku, cały kod zostanie uruchomiony dwa razy, kiedy będziesz ten dokument (biegniesz sam w sobie). Te dwa zadania powinny być oddzielne.

Jeśli naprawdę chcesz uruchomić cały kod, RStudio uczyniło to dość łatwo: Ctrl + Shift + R.Zasadniczo nazywa się purl() i source() za sceną.

+3

Hi @Yihui Myślę, że jest to pomocne, ponieważ czasami twoja analiza może być zorganizowana w małe skrypty, ale w twoim raporcie chcesz mieć kod całego rurociągu. – lucacerone

+4

Tak więc przypadek użycia polega na tym, że chcesz napisać cały kod i mieć go mocno udokumentowany i wyjaśniony, ale kod jest uruchamiany przez jakiś inny skrypt. –

+2

@BrashEquilibrium Jest to kwestia użycia 'source()' lub 'knitr :: knit()' do uruchomienia kodu. Wiem, że ludzie są mniej zaznajomieni z tym ostatnim, ale 'purl()' nie jest wiarygodny. Zostałeś ostrzeżony: https://github.com/yihui/knitr/pull/812#issuecomment-53088636 –

2

Jeśli tylko po kodzie Myślę, że coś w tym kierunku powinno działać:

  1. Przeczytaj plik Przecena/R z readLines
  2. użytkowania grep znaleźć fragmenty kodu, szukając linii, które zacząć <<< np
  3. Weź podzbiorem obiektu, który zawiera oryginalne linie dostać tylko kod
  4. Dump to do pliku tymczasowego wykorzystaniem writeLines
  5. Źródło ten plik do swojego R sesji

Opakowanie to w funkcji powinien dać ci to, czego potrzebujesz.

+1

Dziękuję, myślę, że to zadziała. Jednak pierwsze cztery punkty brzmią jak to, co Stangle już robi w niezawodny sposób dla Sweave, a to, co 'knitr ('myfile.rmd', plątanina = TRUE) robi w dzianinie. Domyślam się, że szukam jednej linijki, która splata się ze źródłami i idealnie nie tworzy żadnych plików. –

+0

Po zawinięciu w funkcję staje się onelinerem;). Możesz użyć 'textConnection' do naśladowania pliku i źródła. Pozwoli to uniknąć tworzenia pliku. –

+0

Tak. 'textConnection' może być miejscem do patrzenia. –

13

Factor wspólny kod się w osobnym pliku R, a następnie pozyskać ten plik R do każdego RMD złożyć chcesz go.

tak na przykład powiedzmy mam dwa raporty muszę dokonać, grypa ognisk i Guns vs. Butter Analysis. Naturalnie utworzyłbym dwa dokumenty Rmd i skończyłem z tym.

Przypuśćmy teraz, że szef przyjdzie i chce zobaczyć zmiany grypy w porównaniu z cenami masła (kontrolując amunicję 9mm).

  • Kopiowanie i wklejanie kodu do analizowania raportów do nowego raportu jest złym pomysłem dla ponownego użycia kodu, itp
  • chcę ładnie wyglądać.

Moje rozwiązanie było czynnikiem projektu do tych plików:

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

wewnątrz każdego pliku RMD bym coś takiego:

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

Problem polega na tym, że tracimy powtarzalność. Moim rozwiązaniem jest utworzenie wspólnego dokumentu podrzędnego, który będzie dołączany do każdego pliku Rmd. Tak na końcu każdego pliku RMD tworzę, to dodam:

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

I, oczywiście, 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, ten jest przeznaczony dla RMD -> HTML workflow . To będzie brzydki bałagan, jeśli pójdziesz z lateksem lub cokolwiek innego. Ten dokument Rmd przegląda globalne środowisko dla wszystkich plików ed() i zawiera ich źródło na końcu dokumentu. Zawiera jquery ui, tablesorter i ustawia dokument na używanie stylu akordeonu do pokazywania/ukrywania plików źródłowych. Jest to praca w toku, ale możesz ją dostosować do własnych potrzeb.

Ani jeden liniowiec, wiem. Mam nadzieję, że daje ci przynajmniej kilka pomysłów :)

1

Prawdopodobnie należy zacząć myśleć inaczej. Mój problem jest następujący: Napisz każdy kod, który normalnie miałbyś w porcji .Rmd w pliku .R. I dokumentu RMD użyć do dzianina czyli html, tylko nie zostawili

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

ten sposób będziesz prawdopodobnie utworzyć kilka plików .r i stracić przewagę przetwarzania cały kod „klocek po porcji "używając ctrl + alt + n (lub + c, ale normalnie to nie działa). Ale czytałem książkę o powtarzalnych badaniach pana Gandruda i zdałem sobie sprawę, że zdecydowanie używa knitr i.Pliki RMD przeznaczone wyłącznie do tworzenia plików html. Główna analiza sama w sobie jest plikiem .R. Myślę, że .Rmd dokumenty szybko rosną zbyt duże, jeśli zaczniesz robić całą analizę w środku.

0

Zalecam zachowanie głównego kodu analizy i obliczeń w pliku .R i importowanie porcji w razie potrzeby w pliku .Rmd. Wyjaśniłem proces here.

1

Poniższy Hack pracował dobrze dla mnie:

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