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
.
::create_package("~/Documents/demo")
usethis#> ✔ 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.
::use_mit_license("Seu Nome")
usethis#> ✔ 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
<- function(x, y, ...) {
funcao_demo <- x + y
z 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
:
::document()
devtools#> 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()
.
::check()
devtools#> 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:
::use_package("dplyr")
usethis::use_dev_package("kuber") usethis
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.