Se você tem um script de R para executar uma determinada rotina em uma frequência predeterminada, talvez seja interessante investir em economia de tempo automatizando essa tarefa. Nesse tutorial demonstramos como utilizar o GitHub Actions para rodar scripts de R automaticamente, se livrando de qualquer trabalho manual e repetitivo.
São diversos os contextos e aplicações nos quais a automatização de um script de R pode ser útil, dentre os principais exemplos destaca-se:
- Relatórios dinâmicos com R Markdown
- Atualização de dashboards
- Extração e atualização de uma base de dados
- Web scrapping de uma página
- Rotinas de robustez contra erros em pacotes
Muitos desses exemplos, e a depender do contexto, requerem que o usuário de R execute o script em uma frequência mensal, semanal, diária ou até mesmo de 1 em 1 hora. Dessa forma, a automatização não é apenas interessante, mas vital para um bom fluxo de trabalho.
Vamos pegar um desses exemplos como demonstração nesse tutorial: faremos um web-scrapping da página de criptomoedas do site TradingView para obter dados de cotações, volume, retornos %, etc. e, adicionalmente, criaremos uma dashboard básica com esses dados para demonstrar uma possibilidade de uso. Por fim, automatizaremos esse processo de modo que a dashboard seja atualizada de hora em hora.
Pré-requisitos
Antes de começar, para esse tutorial deve-se cumprir os seguintes pré-requisitos (pule essa etapa se você já os cumpre):
Se houverem dificuldades com o Git, principalmente se for a primeira vez, recomendo fortemente dar uma lida no Happy Git and GitHub for the useR, que é um tutorial de como usar o Git e GitHub integrado ao R/RStudio.
Criar um pacote
O primeiro passo é criar um pacote R que vai conter nosso script na forma de uma função. Mas não se assuste, é mais fácil do que parece pois, felizmente, a comunidade de R já criou diversas ferramentas para facilitar esse processo. Ao final, utilizaremos a função para a rotina de web-scrapping dos dados de criptomoedas, da mesma forma que a maioria dos pacotes de coleta de dados funcionam.
Aqui utilizamos o pacote usethis para criar um pacote básico. Basta passar para função create_package o caminho (path) de onde se deseja armazenar os arquivos que serão gerados, no nosso caso ficará dentro de uma pasta chamada cryptoscrap.
usethis::create_package("cryptoscrap")
Uma nova sessão do R será aberta com um R Project já pré configurado e com os arquivos mínimos do nosso pacote.
Em sequência, abrimos o arquivo DESCRIPTION recém criado e preenchemos algumas informações básicas como título do pacote, descrição, informações de autor, etc. e, opcionalmente pode ser utilizado uma licença como a MIT (neste caso pode ser usado a função usethis::use_mit_license()). Para saber mais sobre esse preenchimento básico, assim como outras informações sobre desenvolvimento de pacotes, recomendo conferir o livro do Hadley Wickham e Jenny Bryan intitulado R Packages.
Finalizado esse procedimento inicial, podemos prosseguir para o script de R.
Escrever um script de R
Conforme mencionado, faremos um web-scrapping de uma tabela com dados de criptomoedas provenientes do TradingView. Utilizaremos apenas dois pacotes nesta etapa, o rvest e o purrr, e colocaremos a rotina dentro de uma função que será solicitada na dashboard mais adiante.
Para começar a criar a função usamos novamente o pacote usethis, gerando um novo arquivo de scripts dentro da pasta R do nosso projeto (entre aspas o nome do arquivo .R que será gerado):
usethis::use_r("crypt_scrap")
No novo arquivo .R criado escrevemos nossa função:
crypt_scrap <- function() { # Crytocurrency page URL crypto_url <- rvest::read_html( "https://www.tradingview.com/markets/cryptocurrencies/prices-all/" ) # Get raw data from URL crypto_raw <- rvest::html_nodes(crypto_url, css = "table") # Convert from list to tibble crypto_table <- rvest::html_table(crypto_raw) crypto_table <- purrr::pluck(crypto_table, 1) crypto_table <- purrr::set_names( crypto_table, c( "Name", "Mkt Cap", "FD Mkt Cap", "Last", "Avail. Coins", "Total Coins", "Traded Vol", "Chg %" ) ) # Return tibble return(crypto_table) }
Em sequência, devemos adicionar as dependências do nosso pacote, ou seja, estes dois pacotes que estamos utilizando na nossa função. Isso faz com que, ao carregar localmente o pacote que estamos criando por exemplo, estes dois pacotes externos também sejam carregados para a sessão do R ao utilizar a função crypt_scrap que criamos.
Para adicionar as dependências é muito simples:
usethis::use_package("rvest") usethis::use_package("purrr")
Agora já temos um rascunho de pacote pronto para ser testado! Para testar, é importante verificar se a função está funcionando localmente. Outro teste que pode ser feito é através do pacote devtools, que oferece ferramentas para desenvolvimento de pacotes com uma série de parametrizações úteis que indicam se o pacote e suas funções seguem as regras e recomendações gerais do CRAN. Para isso basta rodar:
devtools::check()
Ao final dos testes é indicado se houve errors, warnings ou notes. Se estiver tudo ok, o que foi o caso, podemos prosseguir.
Criar um repositório no GitHub
Agora vamos criar um repositório no GitHub para hospedar nosso pacote com os arquivos. Tudo que precisa ser feito é:
- Estar logado no GitHub
- Clicar no botão New
- Definir um nome do repositório (aqui usamos crypto_scrap)
- Clicar em Create repository
Opcionalmente pode ser incluído uma descrição, similar ao que preenchemos no arquivo DESCRIPTION do nosso pacote.
Em seguida o GitHub criará um link para o repositório criado, direcionando para esta página, onde aparecerão alguns comandos em Git que podemos utilizar para integrar nosso projeto local com o repositório. Tudo que precisamos fazer é copiar o código que aparece na sugestão intitulada "…or create a new repository on the command line" e colar no Terminal do RStudio para fazer a configuração necessária. No nosso exemplo executamos o código abaixo:
echo "# crypto_scrap" >> README.md git init git add README.md git commit -m "first commit" git branch -M main git remote add origin https://github.com/schoulten/crypto_scrap.git git push -u origin main
Com efeito, nossa pasta de pacote local estará configurada como um repositório Git e assim já podemos subir nossos arquivos (fazer o push) para o GitHub. Para isso, executamos no Terminal:
git add . git commit -m "adding package" git push
Ao voltarmos na página do GitHub, dando uma atualizada na página, todos os nossos arquivos locais também estarão lá disponíveis.
Criar uma dashboard
Agora vamos deixar o GitHub em stand by para voltar ao R e criar uma dashboard com os dados das criptomoedas. Nesse exemplo vamos criar uma dashboard estática contendo apenas uma tabela com os dados (bem básico), e utilizamos o pacote flexdashboard para tal. Os códigos em .rmd para gerar a dashboard podem ser conferidos nesse link. O resultado é esse:
Criado o nosso "produto final", podemos partir para a etapa final de automatização.
Usar o renv para gerenciar dependências
Em seguida, fazemos um passo adicional em nosso projeto que é criar um arquivo renv.lock que conterá uma série de configurações de gerenciamento de dependências do nosso projeto/pacote. O que basicamente isso significa é que será tirada uma "foto" do estado atual de configurações, pacotes, dependências que estamos utilizando, para que o projeto seja reprodutível em outro ambiente, como o GitHub Actions.
Esse assunto é bastante complexo e não é o foco aqui se aprofundar no tema, de modo que tudo que precisa ser feito é executar no Console:
renv::init() renv::snapshot()
Agendar tarefa com o GitHub Actions
Como pode ter sido observado, criamos um fluxo de trabalho aqui:
- Temos um pacote com uma função para extrair dados de criptomoedas
- Usamos essa função em uma dashboard que gera uma tabela com os dados
Agora o que queremos é delegar esse fluxo de trabalho para que o GitHub Actions faça isso automaticamente para nós, em uma periodicidade de 60 em 60 minutos, já que os dados oferecem essa tempestividade.
Portanto, o que faremos na prática é:
- Criar um arquivo (workflow) que agenda uma tarefa no GitHub Actions
- Especificar um intervalo de agendamento da tarefa
- Configurar o ambiente R no GH Actions
- Executar script de web-scrapping + atualização da dashboard
- Salvar (commit e push) os resultados
Importante: a indentação do código é importante aqui, se tiver dificuldade em identificar os espaçamentos corretos, confira neste link como ficou o resultado final dessa parte do nosso código.
Criar arquivo de workflow do GitHub Actions
Primeiro precisamos criar o arquivo de workflow, que conterá toda a rotina do que queremos que o GitHub Actions execute. Ele deve estar dentro de uma pasta .github/workflows a partir da raiz do projeto que estamos trabalhando e ter uma extensão .yaml. Em nosso exemplo, ficou assim:
├── .github
│ ├── workflows
│ ├── update.yaml
O nome do arquivo update.yaml pode ser qualquer um.
Especificar um intervalo de agendamento da tarefa
Utilizaremos um agendador de tarefas denominado "CRON" para executar periodicamente nossos códigos.
A parte mais importante para se compreender é como especificar o intervalo de tempo com o qual estamos trabalhando utilizando uma expressão: o script deve ser executado a cada 5 minutos? De hora em hora? Diariamente? Mensalmente? Para cada uma dessas possibilidades há uma expressão específica de configuração e neste link podemos encontrar alguns exemplos dessas expressões para tomar como base.
Em nosso caso, como queremos a execução de 60 em 60 minutos, usamos a expressão "0 * * * *". No arquivo de workflow a sintaxe é conforme abaixo, logo nas primeiras linhas:
on: push: branches: - main - master schedule: - cron: "0 * * * *"
Configurar o ambiente R no GH Actions
Em seguida, vamos configurar uma sessão de R no servidor do GitHub Actions, já que, basicamente, o GH Actions é nada mais do que um computador localizado em algum lugar do mundo que o GitHub nos disponibiliza para executarmos algo nele, em nosso caso são scripts de R. A abordagem aqui utilizada se baseia em alguns templates fornecidos pela equipe do r-lib.
Resumidamente, esta parte do arquivo de workflow:
- Especifica o tipo de sistema operacional a ser usado (usaremos Windows)
- Define as variáveis de ambiente R e configura a autenticação de acesso ao repositório GitHub
- Instala o R
- Instala/restaura os pacotes necessários
name: Dashboard update jobs: Dashboard-update: runs-on: windows-latest env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v2 - uses: r-lib/actions/setup-r@v1 - name: Cache packages uses: actions/cache@v2 with: path: ~\AppData\Local\renv key: ${{ runner.os }}-renv-${{ hashFiles('**/renv.lock') }} restore-keys: | ${{ runner.os }}-renv- - name: Restore packages run: | if (!requireNamespace("renv", quietly = TRUE)) install.packages("renv") renv::restore() shell: Rscript {0} - name: Install workflow dependencies run: | install.packages(c("rcmdcheck", "sessioninfo", "devtools")) shell: Rscript {0}
Executar script de web-scrapping + atualização da dashboard
Com o R configurado, agora executa-se os scripts que escrevemos. No nosso exemplo, a função de web-scrapping do pacote que criamos será chamada dentro do arquivo .rmd que gera a dashboard.
- name: Install pandoc run: | choco install pandoc - name: Render Rmarkdown/Update data run: | devtools::load_all() rmarkdown::render("index.Rmd") shell: Rscript {0}
Salvar (commit e push) os resultados
Até aqui o workflow terá gerado uma nova dashboard com os dados de criptomoedas atualizada, mas tudo isso (esses arquivos) estarão no servidor do GitHub Actions. Portanto, precisamos, por fim, fazer o commit & push dessas atualizações para o nosso repositório do GitHub. Isso é feito conforme abaixo:
- name: Commit and push run: | git config --local user.email "actions@github.com" git config --local user.name "GitHub Actions" git add . git commit -m "Dashboard update" git push - name: Session info run: | options(width = 500) pkgs <- installed.packages()[, "Package"] sessioninfo::session_info(pkgs, include_base = TRUE) shell: Rscript {0}
Salvar resultados no repositório
Ok, o trabalho duro já passou, agora só falta fazermos um novo check em nosso pacote, verificar se está tudo ok e salvo e subir as últimas modificações para o repositório do GitHub.
Primeiro o check no Console do RStudio:
devtools::check()
Apareceram 2 notes e para corrigir adicionamos as seguintes linhas no arquivo .Rbuildignore:
index.Rmd index.html ^\.github$
Agora podemos prosseguimos com o commit & push, no Terminal do RStudio:
git add . git commit -m "create pkg, dash and workflow" git push
Ao visitarmos novamente a página do nosso repositório criado no site do GitHub, veremos todas as modificações realizadas. E pronto!
Além disso, no botão "Actions" do repositório aparecerá o nosso workflow desenhado para executar toda essa rotina que trabalhamos aqui, de hora em hora. Portanto, estamos por fim "livres" de trabalho manual e o nosso produto final, uma dashboard, está totalmente automatizada. Legal, não?!
Recomendo esperar até 24 horas para verificar se o workflow está funcionando, pois o GitHub pode ter um delay para começar a executar toda a rotina definida.
O resultado final desse exemplo disponibilizei neste repositório e a dashboard pode ser acessada por este link.
Ressalvas
Esse tutorial introdutório resumiu demasiadamente diversos pontos que merecem atenção, de modo que, possivelmente, não cobre alguns possíveis obstáculos que podem surgir ao longo do desenvolvimento de um projeto desses. Recomendamos utilizar exaustivamente as documentações dos pacotes e ferramentas aqui utilizadas, de modo a entender mais profundamente o que está sendo feito.
Por fim, eu adoraria saber como como foi a experiência individual ao tentar reproduzir esse tutorial. Sinta-se livre para mandar um feedback, caro leitor(a).