概要

 {Rcpp}が支えるパッケージは非常に多く、全てを紹介するには時間が足りない。そこで「{Rcpp}と関わりがあり、他のパッケージと依存度が高いもの」を紹介しておいた方がよい有用なパッケージと考え、それらの代表的なパッケージを選定する。
 選定方法はパッケージのReverse dependenciesからグラフを構築し、コミュニティ抽出と抽出されたコミュニティでHUB値の高いものを代表的なパッケージとして紹介する。
 ここではコミュニティ外で関係性を考慮するため、HUB値の計算はコミュニティ抽出後のグラフではなく、最初に構築したグラフで行っている。
 なお、コミュニティ検出とHUB値の算出には{igraph}を利用した。


参考ページ



Rコード

パッケージ読み込み

SET_LOAD_LIB <- c("knitr", "dplyr", "igraph", "miniCRAN", "visNetwork")
sapply(X = SET_LOAD_LIB, FUN = library, character.only = TRUE, logical.return = TRUE)
##      knitr      dplyr     igraph   miniCRAN visNetwork 
##       TRUE       TRUE       TRUE       TRUE       TRUE

{knitr}の設定

knitr::opts_chunk$set(comment = NA)

定数設定

# 対象とするパッケージ名
SET_TARGET_PACKAGE  <- "Rcpp"

# パッケージリストを用いるCRANリポジトリ
# 再現性を持たせるため、スナップショットを指定
SET_PACKAGE_REPOSITRY <- "https://mran.revolutionanalytics.com/snapshot/2016-08-13/"

# 次数による頂点除外用の閾値
SET_DEGREE_THRESHOLD <- 1

# 各コミュニティ内のスコア上位Xまでを抽出
SET_COMMUNITY_TOP_N <- 5

{Rcpp}が依存するパッケージを可視化

{Rcpp}が依存するパッケージを確認

pdb <- miniCRAN::pkgAvail(repos = c(CRAN = SET_PACKAGE_REPOSITRY))
dplyr::as_data_frame(pdb) %>% 
  dplyr::select(Package, Version, Depends, Imports, LinkingTo, Suggests, Enhances) %>% 
  head(n = 10) %>% 
  knitr::kable(format = "markdown")
Package Version Depends Imports LinkingTo Suggests Enhances
A3 1.0.0 R (>= 2.15.0), xtable, pbapply NA NA randomForest, e1071 NA
abbyyR 0.5.0 R (>= 3.2.0) httr, XML, curl, readr, progress NA testthat, rmarkdown, knitr (>= 1.11) NA
abc 2.1 R (>= 2.10), abc.data, nnet, quantreg, MASS, locfit NA NA NA NA
ABCanalysis 1.1.1 R (>= 2.10) Hmisc, plotrix NA NA NA
abc.data 1.0 R (>= 2.10) NA NA NA NA
abcdeFBA 0.4 Rglpk,rgl,corrplot,lattice,R (>= 2.10) NA NA LIM,sybil NA
ABCoptim 0.13.11 NA NA NA NA NA
ABCp2 1.2 MASS NA NA NA NA
abcrf 1.3 R(>= 2.10.0) MASS, randomForest, parallel, foreach, doParallel, doRNG NA NA NA
abctools 1.0.4 R (>= 2.10), abc, abind, parallel, plyr NA NA ggplot2, abc.data NA
# 依存パッケージ一覧
miniCRAN::pkgDep(
  pkg = SET_TARGET_PACKAGE, availPkgs = pdb,
  depends = TRUE, suggests = TRUE, enhances = TRUE
)
[1] "Rcpp"       "RUnit"      "inline"     "rbenchmark" "highlight" 
[6] "pkgKitten" 
# 依存構造を可視化
plot(
  miniCRAN::makeDepGraph(
    pkg = SET_TARGET_PACKAGE, availPkgs = pdb, suggests = TRUE, enhances = TRUE
  )
)


{Rcpp}に依存するパッケージを可視化

{Rcpp}に依存するパッケージを抽出
{miniCRAN}では{Rcpp}が依存するパッケージは出せても{Rcpp}に依存するパッケージが抽出できなかったので、ここでは{pacman}を利用した

depends_reverse_pkgs <- pacman::p_depends_reverse(
  package = SET_TARGET_PACKAGE, local = FALSE, character.only = TRUE
)
str(object = depends_reverse_pkgs)
List of 4
 $ Depends  : chr [1:129] "AdaptiveSparsity" "Amelia" "ASPBay" "BaBooN" ...
 $ Imports  : chr [1:572] "AbsFilterGSEA" "accelerometry" "acebayes" "ACEt" ...
 $ LinkingTo: chr [1:719] "AbsFilterGSEA" "accelerometry" "acebayes" "ACEt" ...
 $ Suggests : chr [1:9] "crmPack" "cxxfunplus" "devtools" "hyperSpec" ...
# {Rcpp}に依存するパッケージリスト({Rcpp}を加える)
rcpp_pkgs <- c(SET_TARGET_PACKAGE, unique(x = unlist(x = depends_reverse_pkgs)))

# {Rcpp}に依存するパッケージリストをグラフ化
rcpp_pkgs_dg <- miniCRAN::makeDepGraph(
  pkg = rcpp_pkgs, suggests = TRUE, enhances = TRUE, availPkgs = pdb
)
# 試しにグラフ可視化するが、{igraph}だと頂点が多すぎてプロットの枠に収まらない
plot(rcpp_pkgs_dg)

# Reverse dependenciesに限定
rcpp_pkgs_dg <- rcpp_pkgs_dg %>% 
  igraph::delete_vertices(
    v = setdiff(
      x = igraph::as_data_frame(x = rcpp_pkgs_dg, what = "vertices")$name,
      y = rcpp_pkgs
    )
  )
# それでもプロットしきれない
plot(rcpp_pkgs_dg)


コミュニティ抽出とスコアリング

前述の通り、{Rcpp}が支えるパッケージはとても多かったので、ネットワーク分析の手法を用いて絞り込んだ。

# 次数が小さい頂点を除去
rcpp_pkgs_dg <- rcpp_pkgs_dg %>% 
  igraph::delete_vertices(
    v = rcpp_pkgs_dg %>% 
      igraph::vertex_attr(
        name = "name",
        index = igraph::V(graph = rcpp_pkgs_dg)[
          igraph::centr_degree(graph = rcpp_pkgs_dg)$res <= SET_DEGREE_THRESHOLD
        ]
      )
  )

# 焼きなまし法でコミュニティ抽出
rcpp_com <- igraph::spinglass.community(graph = rcpp_pkgs_dg)
rcpp_membership <- igraph::membership(communities = rcpp_com)

# HITSアルゴリズムのHUB値を算出
rcpp_dg_hub_score <- igraph::hub_score(graph = rcpp_pkgs_dg)$vector


# ネットワーク分析の結果をJOIN
rcpp_members <- dplyr::left_join(
  x = dplyr::data_frame(
    pkgname = names(x = rcpp_membership),
    membership = as.integer(x = rcpp_membership)
  ) %>%
    dplyr::mutate(
     type = dplyr::case_when(
        is.element(el = .$pkgname, set = depends_reverse_pkgs$Depends) ~ "Depends",
        is.element(el = .$pkgname, set = depends_reverse_pkgs$Imports) ~ "Imports",
        is.element(el = .$pkgname, set = depends_reverse_pkgs$LinkingTo) ~ "LinkingTo",
        is.element(el = .$pkgname, set = depends_reverse_pkgs$Suggests) ~ "Suggests",
        TRUE ~ ""
      )
    ),
  y = dplyr::data_frame(
    pkgname = names(x = rcpp_dg_hub_score),
    hub_score = as.numeric(x = rcpp_dg_hub_score)
  ),
  by = c("pkgname" = "pkgname")
) %>% 
  dplyr::arrange(dplyr::desc(x = hub_score))

# Reverse dependenciesを基にグラフ構築
# ターゲットとなる{Rcpp}は対象となる全パッケージを参照しており、HUB値が高い
DT::datatable(
  data = rcpp_members %>% 
    dplyr::mutate(
      membership  = as.factor(x = membership),
      type  = as.factor(x = type)
    ),
  rownames = FALSE, filter = "top"
)
# 集計
rcpp_members %>% 
  dplyr::group_by(membership) %>% 
  dplyr::summarize(
    memmber_cnt =n(),
    mean_hub_score = mean(hub_score)
  ) %>% 
  knitr::kable(format = "markdown")
membership memmber_cnt mean_hub_score
1 3 0.0006937
2 258 0.0039030
3 6 0.0006108
4 6 0.0005895
5 10 0.0006221
6 16 0.0014286
7 70 0.0010892
8 6 0.0005223
9 12 0.0008648
10 3 0.0004625
11 13 0.0016364
12 41 0.0010966
13 15 0.0008164
14 173 0.0012901
15 4 0.0005345
16 31 0.0011771
17 3 0.0004823
18 59 0.0012348

{Rcpp}に依存するパッケージを{visNetwork}で可視化

{igraph}のplotでは対象が多すぎて可視化できなかったので{visNetwork}を用いて可視化(ネットワーク分析の結果を踏まえて可視化)

# {igraph}オブジェクトを{visNetwork}オブジェクトに変換
# ここで対象となるすべてのパッケージと依存関係があるRcppを取り除いておく
rcpp_network <- visNetwork::toVisNetworkData(
  igraph = rcpp_pkgs_dg %>% 
    igraph::delete_vertices(v = c("Rcpp")),
  idToLabel = TRUE
)

# 可視化用にコミュニティとHUB値をノードオブジェクトに付与
rcpp_network$nodes <- dplyr::left_join(
  x = rcpp_network$nodes %>% 
    # Rcpp以外と依存関係がないパッケージをネットワークから取り除く
    # rcpp_networkの作成でRcppの頂点を取り除いたことで孤立点となっている頂点が対象
    dplyr::filter(
      is.element(el = .$id, set = c(rcpp_network$edges$from, rcpp_network$edges$to))
    ),
  y = rcpp_members %>% 
    dplyr::select(pkgname, group = membership, value = hub_score),
  by = c("id" = "pkgname")
)


# ネットワーク可視化用にエッジの属性値を設定
rcpp_network$edges$arrows <- "middle"
rcpp_network$edges$smooth <- TRUE
rcpp_network$edges$shadow <- FALSE
rcpp_network$edges$title <- rcpp_network$edges$type

# label属性を与えておくと依存関係の種類がプロットされるが、煩雑になったので今回はコメントアウト
# rcpp_network$edges$label <- rcpp_network$edges$type

# color属性も上記と同様な理由でコメントアウト
# edge_type_palette <- leaflet::colorFactor(palette = "Dark2", domain = rcpp_network$edges$type)
# rcpp_network$edges$color <- edge_type_palette(x = rcpp_network$edges$type)

# 可視化
visNetwork::visNetwork(
  nodes = rcpp_network$nodes, edges = rcpp_network$edges,
  width = 900, height = 900
  ) %>% 
  visNetwork::visOptions(
    highlightNearest = TRUE,
    selectedBy = list(variable = "group")
  )

紹介候補パッケージの選定

rcpp_pkgs_intro_cand <- rcpp_members %>% 
  dplyr::filter(
    pkgname != SET_TARGET_PACKAGE &
    # HUB値の平均値を閾値に用いる
    hub_score > mean(x = rcpp_members$hub_score)
  ) %>% 
  dplyr::group_by(membership) %>% 
  dplyr::mutate(member_cnt = n()) %>% 
  dplyr::top_n(n = SET_COMMUNITY_TOP_N, wt = hub_score) %>% 
  dplyr::arrange(membership, dplyr::desc(x = hub_score))


rcpp_pkgs_intro_cand %>% 
  knitr::kable(format = "markdown")
pkgname membership type hub_score member_cnt
geiger 3 Imports 0.0036647 1
pROC 4 Imports 0.0035368 1
RcppParallel 6 Suggests 0.0190512 2
RSpectra 6 Imports 0.0030412 2
RcppEigen 7 Imports 0.0454681 5
lme4 7 LinkingTo 0.0112952 5
rstan 7 Imports 0.0060563 5
minqa 7 Imports 0.0035721 5
Rmixmod 7 Depends 0.0032758 5
forecast 8 Imports 0.0024432 1
bigmemory 9 Imports 0.0051938 1
TAM 11 Imports 0.0038349 5
sirt 11 Imports 0.0038294 5
miceadds 11 Imports 0.0030765 5
CDM 11 Imports 0.0023015 5
mirt 11 Imports 0.0023007 5
devtools 12 Suggests 0.0095628 7
roxygen2 12 Imports 0.0094429 7
raster 12 Imports 0.0094213 7
pander 12 Imports 0.0042658 7
htmltools 12 Imports 0.0034781 7
inline 13 Suggests 0.0079045 1
RcppArmadillo 14 Imports 0.1946658 4
RcppProgress 14 Imports 0.0113162 4
mice 14 Depends 0.0030460 4
gRbase 14 Imports 0.0023738 4
prodlim 15 Imports 0.0021381 1
dplyr 16 Imports 0.0229406 4
tidyr 16 Imports 0.0063965 4
readr 16 Imports 0.0028615 4
tibble 16 Imports 0.0027780 4
plyr 18 Imports 0.0301552 3
reshape2 18 Imports 0.0251029 3
scales 18 Imports 0.0143476 3

紹介候補パッケージの依存関係を可視化

plotDepGraph <- function (intro_cand_pkgs, random_seed = 71) {
  
  rcpp_pkgs_intro_cand_graph <- miniCRAN::makeDepGraph(
    pkg = intro_cand_pkgs,
    suggests = TRUE, enhances = TRUE, availPkgs = pdb
  )
  
  # ターゲットノードを赤に
  igraph::V(graph = rcpp_pkgs_intro_cand_graph)$color[
    igraph::vertex_attr(
      graph = rcpp_pkgs_intro_cand_graph, name = "name"
    ) == SET_TARGET_PACKAGE
  ] <- "red"
  
  # 候補パッケージを橙に
  igraph::V(graph = rcpp_pkgs_intro_cand_graph)$color[
    is.element(
      el = igraph::vertex_attr(graph = rcpp_pkgs_intro_cand_graph, name = "name"),
      set =  intro_cand_pkgs
    )
  ] <- "orange"
  
  return(
    visNetwork::visIgraph(
      igraph = rcpp_pkgs_intro_cand_graph,
      idToLabel = TRUE, smooth = FALSE, physics = TRUE, randomSeed = random_seed
    ) %>% 
#      visNetwork::visHierarchicalLayout(direction = "LR") %>% 
      visNetwork::visOptions(
        highlightNearest = list(
          enabled = TRUE, algorithm = "hierarchical"
        ),
        width = 900, height = 500
      )
  )
}


rcpp_pkgs_intro_cand_plot <- lapply(
   X = rcpp_pkgs_intro_cand$pkgname,
   FUN = plotDepGraph
)

# リストに格納するとPlotで表示できなくなるので、力技で一部表示
rcpp_pkgs_intro_cand_plot[[1]]
rcpp_pkgs_intro_cand_plot[[2]]
rcpp_pkgs_intro_cand_plot[[3]]


まとめ

 パッケージの依存関係からなるグラフにネットワーク分析を用いて、{Rcpp}が支える代表的なパッケージを選別しました。これにより、パッケージをグルーピングして紹介ができると思います。
 しかしながら、今回のアプローチでは有名なパッケージばかりが出てきてしまい、{Rcpp}ならではのパッケージの選定ができているとは言い難い結果でした。コミュニティ抽出の方法を変えたり、スコアリング方法を変えたりなど、ネットワーク分析の勉強が必要になりそうです。
 また、ネットワーク分析を行うために{igraph}を用いました。2015年6月に1系メジャーリリースがされ、変数名がドット区切りから変更されたり、チェイン演算子が使えるようになるなど、今後の活用の幅が広がることが期待できます。

おまけ

 今回は「{igraph}オブジェクトを{visNetwork}オブジェクトに変換」でRcppを取り除いておりますが、そのままで可視化するとRcppにすべてエッジが貼られます。可視化をするとかなりブラウザが重くなりますが、{Rcpp}を中心にネットワークが形成され、パッケージを支えている感がとても出ます。
 作成手順は下記に示しますが、とても重くなるのでここでは可視化はしません。興味がある方は下記のリンクをご参照ください(要注意。ブラウザがフリーズする)。
   {Rcpp}が支えるパッケージ
 (下記のような可視化されたネットワークをインタラクティブに触れます)


{Rcpp}が支えるパッケージのスクリーショット画像


繭の作成手順

# include {Rcpp}
rcpp_network <- visNetwork::toVisNetworkData(
  igraph = rcpp_pkgs_dg,
  idToLabel = TRUE
)

# 以下は同じ
rcpp_network$nodes <- dplyr::left_join(
  x = rcpp_network$nodes,
  y = rcpp_members %>% 
    dplyr::select(pkgname, group = membership, value = hub_score),
  by = c("id" = "pkgname")
)

rcpp_network$edges$arrows <- "middle"
rcpp_network$edges$smooth <- TRUE
rcpp_network$edges$shadow <- FALSE
rcpp_network$edges$title <- rcpp_network$edges$type

visNetwork::visNetwork(
  nodes = rcpp_network$nodes, edges = rcpp_network$edges,
  width = 900, height = 1200
  ) %>% 
  visNetwork::visOptions(
    highlightNearest = TRUE,
    selectedBy = list(variable = "group")
  )


実行環境

library(devtools)
devtools::session_info()
Session info --------------------------------------------------------------
 setting  value                       
 version  R version 3.3.1 (2016-06-21)
 system   x86_64, darwin13.4.0        
 ui       X11                         
 language (EN)                        
 collate  ja_JP.UTF-8                 
 tz       Asia/Tokyo                  
 date     2016-08-14                  
Packages ------------------------------------------------------------------
 package     * version  date       source        
 assertthat    0.1      2013-12-06 CRAN (R 3.3.1)
 DBI           0.4-1    2016-05-08 CRAN (R 3.3.1)
 devtools    * 1.12.0   2016-06-24 CRAN (R 3.3.0)
 digest        0.6.9    2016-01-08 CRAN (R 3.3.0)
 dplyr       * 0.5.0    2016-06-24 CRAN (R 3.3.1)
 DT            0.1      2015-06-09 CRAN (R 3.3.1)
 evaluate      0.9      2016-04-29 CRAN (R 3.3.1)
 formatR       1.4      2016-05-09 CRAN (R 3.3.1)
 highr         0.6      2016-05-09 CRAN (R 3.3.1)
 htmltools     0.3.5    2016-03-21 CRAN (R 3.3.1)
 htmlwidgets   0.6      2016-02-25 CRAN (R 3.3.1)
 httr          1.2.1    2016-07-03 CRAN (R 3.3.0)
 igraph      * 1.0.1    2015-06-26 CRAN (R 3.3.0)
 jsonlite      1.0      2016-07-01 CRAN (R 3.3.0)
 knitr       * 1.13     2016-05-09 CRAN (R 3.3.1)
 lazyeval      0.2.0    2016-06-12 CRAN (R 3.3.1)
 magrittr      1.5      2014-11-22 CRAN (R 3.3.1)
 memoise       1.0.0    2016-01-29 CRAN (R 3.3.0)
 miniCRAN    * 0.2.5    2016-04-13 CRAN (R 3.3.1)
 R6            2.1.2    2016-01-26 CRAN (R 3.3.0)
 Rcpp          0.12.5   2016-05-14 CRAN (R 3.3.1)
 rmarkdown     1.0      2016-07-08 CRAN (R 3.3.1)
 stringi       1.1.1    2016-05-27 CRAN (R 3.3.1)
 stringr       1.0.0    2015-04-30 CRAN (R 3.3.1)
 tibble        1.1      2016-07-04 CRAN (R 3.3.1)
 visNetwork  * 1.0.1    2016-06-20 CRAN (R 3.3.0)
 withr         1.0.2    2016-06-20 CRAN (R 3.3.0)
 XML           3.98-1.4 2016-03-01 CRAN (R 3.3.1)
 yaml          2.1.13   2014-06-12 CRAN (R 3.3.1)