6 Pacotes

Nas palavras do maior guru do R, Hadley Wickham, “pacotes são a unidade fundamental de código R reprodutível”. Toda vez que você utiliza a função library(), algum pacote está sendo carregado na sessão. Muitas vezes criar uma biblioteca de funções pode parecer uma tarefa árdua e confusa, restrita apenas a quem possui grande conhecimento da linguagem, mas essa impressão não poderia estar mais distante da realidade: pacotes para o R são bastante simples e intuitivos de fazer.

No início deste livro foi abordado o conceito de projeto. Ele não passa de um arquivo .Rproj que indica para o RStudio que aquele diretório é um ambiente de trabalho estruturado. Nesse sentido, pacotes são iguais a projetos porque eles também têm um .Rproj; pacotes na verdade são projetos.

A diferença entre os dois é que pacotes podem ser documentados e instalados, permitindo toda uma gama de novas possibilidades para quem programa. Muitas vezes uma análise de dados pode envolver dezenas de funções e diversas pessoas, fazendo com que o compartilhamento de código seja vital para que a análise não fuja do controle. Pacotes permitem gerenciar dependências, manter documentação, executar testes unitários e muito mais com o objetivo de deixar todo o time na mesma página.

Sendo assim, recomenda-se criar um pacote para qualquer análise que envolva pelo menos meia dúzia de funções complexas e mais de uma pessoa, caso contrário, um projeto já é suficiente. Outra motivação para criar um pacote é compartilhar conjuntos úteis de funções com outras pessoas; isso acaba sendo menos comum para a maioria das pessoas, mas é importante ressaltar que o R não seria a linguagem popular que é hoje se não fossem pelas famosas bibliotecas ggplot2 e dplyr.

usethis::create_package("~/Documents/demo")
#> ✔ Setting active project to '~/Documents/demo'
#> ✔ Creating 'R/'
#> ✔ Writing 'DESCRIPTION'
#> Package: demo
#> Title: What the Package Does (One Line, Title Case)
#> Version: 0.0.0.9000
#> Authors@R (parsed):
#>     * First Last <first.last@example.com> [aut, cre] (<https://orcid.org/YOUR-ORCID-ID>)
#> Description: What the package does (one paragraph).
#> License: What license it uses
#> Encoding: UTF-8
#> LazyData: true
#> ✔ Writing 'NAMESPACE'
#> ✔ Writing 'demo.Rproj'
#> ✔ Adding '.Rproj.user' to '.gitignore'
#> ✔ Adding '^demo\\.Rproj$', '^\\.Rproj\\.user$' to '.Rbuildignore'
#> ✔ Opening '~/Documents/demo/' in new RStudio session
#> ✔ Setting active project to 'demo'

A função executada acima é exatamente análoga à função de criação de projetos. A principal diferença é que ela cria um arquivo DESCRIPTION e assume que o nome do pacote é igual ao nome da pasta onde o mesmo está sendo criado (neste caso, “demo”). Alguns outros arquivos também são criados (como .Rbuildignore e NAMESPACE), mas eles não vêm ao caso. De resto, o pacote é idêntico a um projeto e pode ser sincronizado com o Git exatamente da mesma maneira.

O primeiro passo para começar a usar um pacote é atribuir a ele uma licença (caso um dia você resolva compartilhá-lo com o mundo) e preencher a descrição. Abaixo encontra-se uma função simples que adiciona uma licença MIT ao pacote.

usethis::use_mit_license("Seu Nome")
#> ✔ Setting active project to '~/Documents/demo'
#> ✔ Setting License field in DESCRIPTION to 'MIT + file LICENSE'
#> ✔ Writing 'LICENSE.md'
#> ✔ Adding '^LICENSE\\.md$' to '.Rbuildignore'
#> ✔ Writing 'LICENSE'

O arquivo de descrição, no entanto, é um pouco mais complexo porque ele tem alguns campos que precisam ser preenchidos manualmente. Quando o pacote for criado, eles já estarão populados com instruções para facilitar a vida de quem programa. Abaixo está um exemplo de como DESCRIPTION deve ficar depois de completo:

Package: demo
Title: O Que o Pacote Faz (Uma Linha)
Version: 0.0.0.9000
Authors@R: 
    person(given = "Seu",
           family = "Nome",
           role = c("aut", "cre"),
           email = "seunome@dominio.com")
Description: O que o pacote faz (um paragrafo curto terminado em ponto final).
License: MIT + file LICENSE
Encoding: UTF-8
LazyData: true

A partir deste ponto, os metadados do pacote estão essencialmente prontos e não precisam mais ser modificados. Assim como em um projeto, o que resta é adicionar arquivos com funções à pasta R/.

6.1 Documentação

Para poder programar pacotes com mais facilidade, é necessário instalar o devtools. Assim como o tidyverse, este é um conjunto de pacotes (que inclui o usethis por sinal) que auxiliam no processo de criar e testar um pacote de R.

install.packages("devtools")

A partir de agora você pode, por exemplo, criar documentações para as funções do seu pacote. Quando outras pessoas o instalarem, elas poderão consultar esses manuais da mesma forma que fazem com qualquer outra função: ?funcao().

A documentação mais simples (e obrigatória) envolve dar um título para a função e descrever o que cada parâmetro significa. Para documentar uma função qualquer, basta adicionar comentários em cima dela com #' assim como no exemplo abaixo:

#' Função demonstrativa que soma e imprime
#'
#' @param x Um número ou vetor numérico
#' @param y Um número ou vetor numérico
#' @param ... Outros argumentos passados para [print()]
#'
#' @export
funcao_demo <- function(x, y, ...) {
  z <- x + y
  print(z, ...)
  return(z)
}

No RStudio, esse tipo de documentação é tratado diferentemente de outros comentários, então certas palavras-chave ficam coloridas. @param por exemplo indica a documentação de um dos parâmetros e @export indica que aquela função será exportada pelo pacote, ou seja, ficará disponível quando for executado o comando library(demo).

Para gerar a documentação do pacote, basta chamar uma outra função do devtools:

devtools::document()
#> Updating demo documentation
#> Updating roxygen version in ~/Documents/demo/DESCRIPTION
#> Writing NAMESPACE
#> Loading demo
#> Writing NAMESPACE
#> Writing funcao_demo.Rd

?funcao_demo()
#> Rendering development documentation for 'funcao_demo'

Conforme o número de funções no pacote for crescendo, basta iterar nesse ciclo descrito até aqui. Além disso, é importante lembrar (como destacado na sessão anterior) que qualquer função utilizada de outro pacote deve ser invocada na forma pacote::funcao(); neste momento, o pacote em questão se tornará uma dependência do seu pacote e deve ser declarado como tal com usethis::use_package("pacote") (mais sobre isso a seguir).

Para garantir que o R não encontrará nenhum problema no seu pacote, basta executar a função de verificação devtools::check(). Se nenhum defeito for encontrado, basta compartilhar o pacote com a comunidade e instalá-lo com devtools::install_local().

devtools::check()
#> Updating demo documentation
#> Writing NAMESPACE
#> Loading demo
#> Writing NAMESPACE
#> ── Building ───────────────────────────────────────────────────────── demo ──
#> Setting env vars:
#> ● CFLAGS    : -Wall -pedantic -fdiagnostics-color=always
#> ● CXXFLAGS  : -Wall -pedantic -fdiagnostics-color=always
#> ● CXX11FLAGS: -Wall -pedantic -fdiagnostics-color=always
#> ─────────────────────────────────────────────────────────────────────────────
#> ✔  checking for file ‘/home/clente/Documents/demo/DESCRIPTION’ ...
#> 
#> [... omitido por brevidade ...]
#> 
#> ── R CMD check results ───────────────────────────────── demo 0.0.0.9000 ────
#> Duration: 8.2s
#> 
#> 0 errors ✔ | 0 warnings ✔ | 0 notes ✔

6.2 Imports

Como dito anteriormente, um passo importante na criação de um pacote é a declaração de suas dependências. Se você tiver seguido com cuidado a sugestão de já começar a escrever o pacote declarando todas as importações explicitamente (ou seja, com pacote::funcao()), isso não será um problema, pois o devtools::check() já será capaz de apontar exatamente quais dependências ainda precisam ser registradas.

Suponha que um pacote tem exatamente duas dependências: o dplyr e o kuber (um pacote desenvolvido pela Curso-R e disponível somente via GitHub). Para passar no check() basta executar os dois comandos abaixo:

usethis::use_package("dplyr")
usethis::use_dev_package("kuber")

O dplyr, por estar disponível no CRAN, não é um pacote considerado em fase de desenvolvimento, então basta usar usethis::use_package(). Já o kuber só está disponível no GitHub e instalável via remotes::install_github("curso-r/kuber"), então precisamos neste caso utilizar usethis::use_dev_package(). Os comandos acima criam uma nova seção no arquivo DESCRIPTION:

Imports: 
    dplyr,
    kuber (>= 0.3.1.9000)
Remotes:  
    curso-r/kuber

Note que o kuber já é importado com um link remoto para o seu repositório no GitHub e um marcador da versão atualmente instalada na sua máquina. Para saber mais sobre versões, consulte o Capítulo 10.