Este é o primeiro post de uma nova série sobre meta-análises de pacotes R. Com o pacote miniCRAN é possível baixar logs de downloads de pacotes de R por meio do espelho (mirror) do RStudio do CRAN. Cada linha nesses logs representa um download de um pacote por um usuário.

O objetivo desta série é analisar os dados gerados por esses logs.

Para este primeiro post, é mostrado:

  • Como baixar os logs de downloads de pacotes do CRAN do RStudio de forma automatizada com o pacote miniCRAN;
  • Como selecionar os pacotes R mais populares pelo Princípio de Pareto;
  • Os 20 pacotes mais populares;
  • Um grafo de redes criado a partir dos pacotes mais populares filtrados e suas dependências.

Os pacotes usados neste post são:

#library(installr)
suppressMessages(library(dplyr)) # Usado para agregar os dados em pacotes
suppressMessages(library(igraph)) # Usado para plotar o grafo criado pelo miniCRAN
library(miniCRAN)
library(ggplot2) 
library(ggthemes)
library(scales)
library(feather) # Usado para carregar arquivos

As linhas abaixo mostram como eu baixei os logs da mirror do RStudio do CRAN para o período entre 24 de Abril de 2016 a 24 de Maio a 2016. Os logs de cada dia desse período são salvos na pasta indicada no argumento log_folder, totalizando cerca de 250 MB. O dataframe gerado com o código gerado é enorme, por isso é recomendável removê-lo da memória após realizar os filtros desejados a partir dele.

temp_dir <- download_RStudio_CRAN_data(START = '2016-04-24',END = '2016-05-24', log_folder="/home/sillas/R/data")
df_cran <- read_RStudio_CRAN_data("/home/sillas/R/data")
#save(df_cran, file = "/home/sillas/R/data/df_cran.Rdata")
#load("/home/sillas/R/data/df_cran.Rdata")

# Agregar logs por pacote:

# df_pkgs <- df_cran %>%
#   group_by(package) %>%
#   summarise(downloads = n()) %>%
#   arrange(desc(downloads)) %>%
#   mutate(downloads_acum = cumsum(downloads))

#rm(df_cran)

Para não ter de carregar o objeto df_cran toda vez que eu renderizo o arquivo markdown deste post, salvei uma cópia em disco do dataframe df_pkgs. Para isso, usei o pacote feather, que torna os processos de escrita e leitura de arquivos no R muito rápidas.

#write_feather(df_pkgs, "df_pkgs.feather")
df_pkgs <- read_feather("/home/sillas/R/Projetos/PaixaoPorDados/sillasgonzaga.github.io/data/df_pkgs.feather")
print(df_pkgs, 10)
## Source: local data frame [9,236 x 3]
## 
##          package downloads downloads_acum
##            <chr>     <int>          <int>
## 1  RcppArmadillo    425443         425443
## 2           Rcpp    285869         711312
## 3        ggplot2    246536         957848
## 4         digest    210749        1168597
## 5        stringr    207837        1376434
## 6           plyr    203498        1579932
## 7        stringi    202125        1782057
## 8       magrittr    195198        1977255
## 9         scales    194719        2171974
## 10      reshape2    182363        2354337
## ..           ...       ...            ...
dim(df_pkgs)
## [1] 9236    3

Temos que 9236 diferentes pacotes foram baixados no período analisado.

Para determinar a quantidade de pacotes a serem analisados como membros de uma rede, usei o Princípio de Pareto:

(total_downloads <- sum(df_pkgs$downloads))
## [1] 17491320
(limite80 <- total_downloads * 0.80)
## [1] 13993056
df_pkgs_pareto <- filter(df_pkgs, downloads_acum <= limite80)

nrow(df_pkgs_pareto)
## [1] 335
100*nrow(df_pkgs_pareto)/nrow(df_pkgs)
## [1] 3.627111

Temos que 335 pacotes, cerca de 3,6% do total, equivalem a 80% de todos os downloads de pacotes nos últimos 30 dias, o que mostra que a regra de Pareto é aplicável aqui e que, apesar de haver milhares de pacotes disponíveis no CRAN, a grande maioria deles não são baixados muitas vezes, como mostram as seguintes estatísticas:

summary(df_pkgs$downloads)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##       1     137     182    1894     315  425400

O número mediano de downloads por usuário é de 182, muito distante dos 10 mais populares mostrados acima.

Os vinte pacotes mais baixados são:

df_pkgs_pareto %>%
  top_n(20, wt = downloads) %>%
  ggplot(aes(x = reorder(package, downloads), y = downloads)) +
    geom_bar(stat = "identity", fill = "#014d64") +
    labs(x = "", y = "Quantidade de downloads\n entre Abril e Maio/2016") +
    scale_y_continuous(labels = comma) +
    coord_flip() +
    theme_economist() 

center

Pessoalmente, não fiquei surpreso ao ver que, dos 6 pacotes mais baixados, 3 (ggplot2, stringr e plyr) fazem parte do Hadleyverse, ou seja, foram criados pelo gênio Hadley Wickham, que revolucionou o modo como o R é usado e é ídolo para muitos usuários da linguagem, como eu :).

Após filtrar os pacotes que entrarão na análise, o pacote miniCRAN é usado para extrair as dependências de cada um e formar uma rede deles. A função makeDepGraph extrai as dependências dos pacotes indicados na função e cria um grafo. Por exemplo:

pkgDep("ggplot2")
##  [1] "ggplot2"       "digest"        "gtable"        "MASS"         
##  [5] "plyr"          "reshape2"      "scales"        "Rcpp"         
##  [9] "stringr"       "RColorBrewer"  "dichromat"     "munsell"      
## [13] "labeling"      "colorspace"    "stringi"       "magrittr"     
## [17] "jsonlite"      "rex"           "httr"          "crayon"       
## [21] "withr"         "memoise"       "mime"          "curl"         
## [25] "openssl"       "R6"            "lazyeval"      "lattice"      
## [29] "survival"      "Formula"       "latticeExtra"  "cluster"      
## [33] "rpart"         "nnet"          "acepack"       "foreign"      
## [37] "gridExtra"     "data.table"    "chron"         "Matrix"       
## [41] "maps"          "sp"            "nlme"          "mvtnorm"      
## [45] "TH.data"       "sandwich"      "codetools"     "zoo"          
## [49] "praise"        "SparseM"       "MatrixModels"  "evaluate"     
## [53] "formatR"       "highr"         "markdown"      "yaml"         
## [57] "knitr"         "htmltools"     "caTools"       "bitops"       
## [61] "gdtools"       "BH"            "covr"          "ggplot2movies"
## [65] "hexbin"        "Hmisc"         "mapproj"       "maptools"     
## [69] "mgcv"          "multcomp"      "testthat"      "quantreg"     
## [73] "rmarkdown"     "svglite"
makeDepGraph("ggplot2")
## IGRAPH DN-- 74 123 -- 
## + attr: name (v/c), type (e/c)
## + edges (vertex names):
##  [1] magrittr    ->rex          lazyeval    ->rex         
##  [3] jsonlite    ->httr         mime        ->httr        
##  [5] curl        ->httr         openssl     ->httr        
##  [7] R6          ->httr         memoise     ->crayon      
##  [9] digest      ->memoise      lattice     ->latticeExtra
## [11] RColorBrewer->latticeExtra gtable      ->gridExtra   
## [13] chron       ->data.table   Matrix      ->survival    
## [15] digest      ->ggplot2      gtable      ->ggplot2     
## + ... omitted several edges

Assim, dos 335 pacotes mais populares, são gerados dois grafos: o da esquerda, com o método plot com as modificações nativas realizadas pelo miniCRAN e o da direita, feita pelo pacote igraph.

set.seed(123)
list_pkgs <- df_pkgs_pareto$package
g <- makeDepGraph(list_pkgs)

par(mfrow=(c(1,2)))
plot(g, vertex.size=10, cex=0.7, main = "") # método plot.pkgDepGraph

plot.igraph(g)

center

Como pode-se ver, ambos os gráficos acima são visualmente poluídos e não dá para aprender muita coisa a partir deles. Além disso, a fim de analisar a centralidade de um pacote em um grafo, é importante saber o que o argumento suggests da função makeDepGraph significa. Segundo Hadley Wickham, quando o pacote A sugere um outro pacote B, significa que o A pode usar o pacote B, mas ele não é requerido. Este pode ser usado para rodar testes, montar vignettes (tutoriais de pacotes), etc.

Vamos, então, como fica o grafo dos 20 pacotes mais populares, com e sem suggests, com o pacote ggplot2 destacado:

list_pkgs <- df_pkgs$package[1:20]

par(mfrow=(c(1,2)))
g <- makeDepGraph(list_pkgs, enhances = FALSE, suggests = FALSE)
set.seed(123)
plot(g, pkgsToHighlight = "ggplot2",vertex.size=20, cex=1, main = "Argumento suggests = FALSE", legendPosition = NULL)

g <- makeDepGraph(list_pkgs, enhances = FALSE, suggests = TRUE)
set.seed(123)
plot(g, pkgsToHighlight = "ggplot2", vertex.size=20, cex=1, main = "Argumento suggests = TRUE", legendPosition = c(-1, -1))

center

Agora sim já é possível aprender algumas coisas a partir do grafo. O sentido da linha vermelha indica que, por exemplo, o ggplot2 importa vários pacotes(digest, gtable, MASS, reshape2, plyr e scales), mas não é importado por nenhum outro. Já o grafo da direita mostra que o ggplot2 sugere muitos outros, o que aumenta sua centralidade na rede.

Conclusão: para realizar análises de centralidade de pacotes R, é necessário deixar o argumento suggests como FALSE.