Используйте citation () в R Markdown для автоматической генерации библиографии пакетов R - PullRequest
5 голосов
/ 02 февраля 2020

Я хотел бы процитировать пакеты R, используемые в проекте, но, поскольку их довольно много, я думаю, что было бы неплохо создать два отдельных справочных раздела: один со ссылками на мой указанный домен c и один со ссылками на пакеты R.

Моей первой идеей было бы проверить, могу ли я экспортировать все цитаты пакетов, используемых одновременно, в файл .bib, но я не уверен, что R Markdown может обрабатывать оба файла .bib с список литературы, указывающий c на мой домен и файл .bib для пакетов R.

Поскольку функции citation() или toBibtex() генерируют цитаты Bibtex, я подумал, что возможно создать справочный раздел, посвященный пакетам R, с этими функциями непосредственно в файле .Rmd. Однако кажется невозможным автоматически отформатировать ссылку, когда эти команды включены в блок с R Markdown.

Вот воспроизводимый пример того, что я пытаюсь сделать:

---
title: "Cite R packages"
author: ""
date: "01/02/2020"
output: pdf_document
bibliography: test.bib
---

This is a citation of a paper: @mayer2011.

# Bibliography {-}
\setlength{\parindent}{-0.2in}
\setlength{\leftskip}{0.2in}
\noindent
<div id="refs"></div>
```{r refmgr references, results="asis", echo=FALSE}
# Print
```
\setlength{\parindent}{0in}
\setlength{\leftskip}{0in}
\setlength{\parskip}{0pt}

# Bibliography for R packages {-}
```{r}
citation("dplyr")
toBibtex(citation("dplyr"))
```

и вот содержание test.bib:

@article{mayer2011,
  title = {Notes on {{CEPII}}'s {{Distances Measures}}: {{The GeoDist Database}}},
  shorttitle = {Notes on {{CEPII}}'s {{Distances Measures}}},
  journal = {SSRN Electronic Journal},
  doi = {10.2139/ssrn.1994531},
  author = {Mayer, Thierry and Zignago, Soledad},
  year = {2011}
}

Любая идея о как легко включить ссылки пакетов R в отдельный раздел ссылок?

РЕДАКТИРОВАТЬ: см. здесь для другого решения.

Ответы [ 2 ]

3 голосов
/ 06 февраля 2020

Здесь есть две отдельные, хотя и связанные проблемы:

  1. Как программно процитировать пакет
  2. Как иметь два отдельных справочных раздела в документе уценки

Существуют решения для них обоих, которые я go расскажу по очереди:


Как программно процитировать пакет

Ключом здесь является понимание того, что Pando c запишет ваш документ только после фрагментов кода R. Это дает вам возможность писать файл .bib программно как часть вашего документа по разметке R, который читается только Pando c на этапе создания документа.

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

Вам нужна функция, которая будет принимать имена пакетов, получать цитаты в формате bibtex, вставлять их все вместе и сохранять как .bib файл. Я написал пример функции здесь, чтобы показать, как это можно сделать.

Эта функция должна обрабатывать пакеты, которые выдают несколько ссылок на bibtex, и автоматически вставляет имя пакета в bibtex, чтобы вы могли ссылаться на любой пакет в вашей уценке с помощью @packagename. Он использует нестандартную оценку и аргументы ..., поэтому вам не нужно заключать в кавычки имена пакетов или заключать их в c():

citeR <- function(...)
{
  packages <- unlist(lapply(as.list(match.call()), deparse))[-1]
  Rbibs <- ""

  for(package in packages)
  {
    Rbib <- capture.output(print(citation(package), bibtex = T))    
    Rbib <- mapply(function(x, y) Rbib[x:y], 
                   grep("  @.+[{]", Rbib), 
                   which(Rbib == "  }"))

    if(class(Rbib) == "matrix"){
      Rbib[1, 1] <- gsub(",", paste0(package, ","), Rbib[1, 1])
      Rbib <- paste0(Rbib, collapse = "\n")
    } else {
      Rbib <- unlist(lapply(Rbib, function(x) {
                               x[1] <- gsub(",", paste0(package, ","), x[1]); 
                               x <- paste0(unlist(x), collapse = "\n")
                               return(x)
                             }))
    }

    if(length(Rbib) > 1) {
      if(any(grepl("@Manual", Rbib))) {
        Rbib <- Rbib[grep("@Manual", Rbib)][1]
      } else {
        Rbib <- Rbib[1]}}

    Rbibs <- paste(Rbibs, Rbib, sep = "\n\n")
  }

  writeBin(charToRaw(utf8::as_utf8(Rbibs)), "packages.bib")
}

Чтобы использовать его, вы просто помещаете его в блок R с echo = FALSE и сделайте это:

citeR(dplyr, ggplot2, knitr, pROC)

Как получить два справочных раздела

Я не могу взять кредит на эту часть ответа, которую я получил с здесь . Это более сложный, чем первая часть. Прежде всего, вы должны использовать фильтр lua, для этого требуются самые последние версии rmarkdown и Pando c, поэтому обновите до последних версий, иначе это может не сработать .

Обоснование фильтра lua описано в предоставленной ссылке, но я включу его здесь с полным подтверждением @tarleb. Вы должны сохранить следующий файл как multiple-bibliographies.lua в том же каталоге, что и уценка:

-- file: multiple-bibliographies.lua

--- collection of all cites in the document
local all_cites = {}
--- document meta value
local doc_meta = pandoc.Meta{}

--- Create a bibliography for a given topic. This acts on all divs whose ID
-- starts with "refs", followed by nothings but underscores and alphanumeric
-- characters.
local function create_topic_bibliography (div)
  local name = div.identifier:match('^refs([_%w]*)$')
  if not name then
    return nil
  end
  local tmp_blocks = {
    pandoc.Para(all_cites),
    pandoc.Div({}, pandoc.Attr('refs')),
  }
  local tmp_meta = pandoc.Meta{bibliography = doc_meta['bibliography' .. name]}
  local tmp_doc = pandoc.Pandoc(tmp_blocks, tmp_meta)
  local res = pandoc.utils.run_json_filter(tmp_doc, 'pandoc-citeproc')
  -- first block of the result contains the dummy para, second is the refs Div
  div.content = res.blocks[2].content
  return div
end

local function resolve_doc_citations (doc)
  -- combine all bibliographies
  local meta = doc.meta
  local orig_bib = meta.bibliography
  meta.bibliography = pandoc.MetaList{orig_bib}
  for name, value in pairs(meta) do
    if name:match('^bibliography_') then
      table.insert(meta.bibliography, value)
    end
  end
  doc = pandoc.utils.run_json_filter(doc, 'pandoc-citeproc')
  doc.meta.bibliography = orig_bib -- restore to original value
  return doc
end

return {
  {
    Cite = function (c) all_cites[#all_cites + 1] = c end,
    Meta = function (m) doc_meta = m end,
  },
  {Pandoc = resolve_doc_citations,},
  {Div = create_topic_bibliography,}
}

Чтобы это работало, ваш заголовок YAML должен выглядеть следующим образом:

---
title: "Cite R packages"
author: ''
date: "01/02/2020"
output:
  pdf_document:
    pandoc_args: --lua-filter=multiple-bibliographies.lua
bibliography_software: packages.bib
bibliography_normal: test.bib
---

Обратите внимание, что packages.bib не должно существовать, когда вы начинаете вязать документ, поскольку он будет создан до вызова Pando c.

Чтобы вставить разделы со ссылками, вам нужно поместить эти html фрагменты в соответствующих точках вашей уценки:

<div id = "refs_normal"></div>

и

<div id = "refs_software"></div>

Собираем все вместе

Я знаю, что это уже длинный ответ, но я подумал, что было бы хорошо включить полный рабочий пример и показать вывод в формате pdf:

---
title: "Cite R packages"
author: ''
date: "01/02/2020"
output:
  pdf_document:
    pandoc_args: --lua-filter=multiple-bibliographies.lua
bibliography_software: packages.bib
bibliography_normal: test.bib
---

This is a citation of a paper: @mayer2011.
This is a citation of an R package @dplyr
And another @ggplot2 and another @knitr plus @pROC

# Bibliography{-}
\setlength{\parindent}{-0.2in}
\setlength{\leftskip}{0.2in}
\noindent
<div id = "refs_normal"></div>
\setlength{\parindent}{0in}
\setlength{\leftskip}{0in}
\setlength{\parskip}{0pt}

# Software used{-}
\setlength{\parindent}{-0.2in}
\setlength{\leftskip}{0.2in}
\noindent
<div id = "refs_software"></div>
\setlength{\parindent}{0in}
\setlength{\leftskip}{0in}
\setlength{\parskip}{0pt}

```{r citeR, echo=FALSE}

citeR <- function(...)
{
  packages <- unlist(lapply(as.list(match.call()), deparse))[-1]
  Rbibs <- ""

  for(package in packages)
  {
    Rbib <- capture.output(print(citation(package), bibtex = T))

    Rbib <- mapply(function(x, y) Rbib[x:y], 
                   grep("  @.+[{]", Rbib), 
                   which(Rbib == "  }"))

    if(class(Rbib) == "matrix")
    {
      Rbib[1, 1] <- gsub(",", paste0(package, ","), Rbib[1, 1])
      Rbib <- paste0(Rbib, collapse = "\n")
    }
    else
    {
      Rbib <- unlist(lapply(Rbib, function(x) {
                               x[1] <- gsub(",", paste0(package, ","), x[1]); 
                               x <- paste0(unlist(x), collapse = "\n")
                               return(x)
                             }))
    }

    if(length(Rbib) > 1)
    {
      if(any(grepl("@Manual", Rbib)))
      {
        Rbib <- Rbib[grep("@Manual", Rbib)][1]
      }
      else
      {
        Rbib <- Rbib[1]
      }
    }

    Rbibs <- paste(Rbibs, Rbib, sep = "\n\n")
  }

  writeBin(charToRaw(utf8::as_utf8(Rbibs)), "packages.bib")
}

citeR(dplyr, ggplot2, knitr, pROC)

```#

и test.pdf выглядит так:

enter image description here

Если вы предпочитаете автоматически ссылаться на любые пакеты, которые вы используете, вы можете программно соскоблить имена с любых вызовов library() в вашем документе уценки. Поскольку рабочий процесс для достижения вашей цели немного запутан, вы можете рассмотреть возможность создания небольшого пакета с функцией citeR, документом lua и собственной функцией get_lib_citations_from_library_calls("my_markdown.Rmd"), которая автоматизирует все это.

0 голосов
/ 06 февраля 2020

Надеюсь, это поможет вам:

---
title: "Cite R packages"
author: ""
date: "01/02/2020"
output: pdf_document
bibliography: test.bib
---

This is a citation of a paper: @mayer2011. 

# Bibliography {-}
\setlength{\parindent}{-0.2in}
\setlength{\leftskip}{0.2in}
\noindent
<div id="refs"></div>
```{r refmgr references, results="asis", echo=FALSE}
# Print
```
\setlength{\parindent}{0in}
\setlength{\leftskip}{0in}
\setlength{\parskip}{0pt}

# Bibliography for R packages {-}
```{r, results = "asis", echo = FALSE, warning=FALSE, message=FALSE}

# library(devtools)
# install_github("cboettig/knitcitations")

library("knitcitations")

tmpfile <- 'rtext.bib'

pkgs <- c('dplyr', 'tidyverse')

citations <- do.call('c',lapply(pkgs, citation))

knitcitations::write.bibtex(citations, file = tmpfile)

knitcitations::read.bibtex(file = 'rtest.bib')

```
...