個人的な興味関心でCRANで公開されているRのパッケージの依存関係(ImportとSuggest)をみて、依存されている件数が多いパッケージ、つまり引用件数が多いパッケージ等をみてみたのでメモ。
- とりあえず、前処理
- ImportsもSuggestsもないパッケージは……
- 依存関係がもっとも多いパッケージは……
- 多く引用されているパッケージは……
- 相互依存関係にあるパッケージは……
- おまけ:空欄という依存関係
とりあえず、前処理
availabel.pacages()
をつかって、CARNで公開されているパッケージのリストを取り出して、Packageと、ImportsとSuggestsのデータフレームを長持ちで依存関係がないものをのぞいたもの(pkg_list
)と依存関係の有無に関わらないデータを横持ち(df
)で作っておく。
library(plyr) library(tidyverse)
## -- Attaching packages --------------------------------------- tidyverse 1.3.1 --
## v ggplot2 3.3.5 v purrr 0.3.4
## v tibble 3.1.6 v dplyr 1.0.7
## v tidyr 1.1.4 v stringr 1.4.0
## v readr 2.1.1 v forcats 0.5.1
## -- Conflicts ------------------------------------------ tidyverse_conflicts() --
## x dplyr::arrange() masks plyr::arrange()
## x purrr::compact() masks plyr::compact()
## x dplyr::count() masks plyr::count()
## x dplyr::failwith() masks plyr::failwith()
## x dplyr::filter() masks stats::filter()
## x dplyr::id() masks plyr::id()
## x dplyr::lag() masks stats::lag()
## x dplyr::mutate() masks plyr::mutate()
## x dplyr::rename() masks plyr::rename()
## x dplyr::summarise() masks plyr::summarise()
## x dplyr::summarize() masks plyr::summarize()
df <- available.packages(repos = "https://cloud.r-project.org/") df <- df %>% as_tibble() df <- df %>% select(Package, Imports, Suggests) import_list <- str_split(df$Imports, pattern = ",") suggest_list <- str_split(df$Suggests, pattern = ",") import_list <- ldply(import_list, rbind) suggest_list <- ldply(suggest_list, rbind) colnames(import_list) <- str_c("Imports", 1:ncol(import_list)) colnames(suggest_list) <- str_c("Suggests", 1:ncol(suggest_list)) df <- bind_cols(select(.data = df, Package), import_list, suggest_list) df <- df %>% map_df(.f = function(x){ x %>% str_remove_all(pattern = "\\(.+\\)") %>% str_remove_all(pattern = " ") %>% str_remove_all(pattern = "\n") %>% str_remove_all(pattern = "\\(.+\\)") %>% str_remove_all(pattern = " ") %>% str_remove_all(pattern = "\n") }) pkg_list <- pivot_longer(df, cols = !Package) pkg_list <- pkg_list %>% mutate(name = str_remove(name, pattern = "[:digit:]+"), value = if_else(value == "", NA_character_, value)) pkg_list <- pkg_list %>% na.omit()
冗長な気もするが、データが取れたのでこれを見ていく。
取り出せたCARNに登録されている現在のパッケージ数は、
nrow(df)
## [1] 18694
となる。微妙(30くらい?)にウェブページ上で示されるパッケージ数と異なるが、リポジトリに反映されるタイミングとかの差かなと思っている。
コードを実行するたび(日毎)に取り出せるパッケージ数が変わってくるのでよくわからない。
ImportsもSuggestsもないパッケージは……
ImportsもSuggestsもないパッケージ数を見てみる。つまり全く関数に依存していないデータセットのパッケージか、{base}
以外には依存していないパッケージ群になる。
df %>% filter(is.na(Imports1)) %>% filter(is.na(Suggests1)) %>% nrow()
## [1] 3173
全体比でいうと、
(df %>% filter(is.na(Imports1)) %>% filter(is.na(Suggests1)) %>% nrow()) / nrow(df)
## [1] 0.1697336
とおよそ17%程度のパッケージが依存関係がないパッケージになっている。
依存関係がもっとも多いパッケージは……
Importsが最も多いパッケージトップ5は
pkg_list %>% filter(name == "Imports") %>% select(Package) %>% table() %>% as_tibble() %>% arrange(-n) %>% head(5)
## # A tibble: 5 x 2
## . n
## <chr> <int>
## 1 Seurat 47
## 2 autostats 42
## 3 pguIMP 41
## 4 epitweetr 37
## 5 MetaIntegrator 36
となっている。
個人的には47個って多い印象。素の状態でインストールを始めたらトイレに行って帰ってこられそう。
ちなみにImportsがあるパッケージのImportsの平均数は
Import_n <- pkg_list %>% filter(name == "Imports") %>% select(Package) %>% table() %>% as_tibble() mean(Import_n$n)
## [1] 5.743405
となっている。
Suggestsが最も多いパッケージトップ5は
pkg_list %>% filter(name == "Suggests") %>% select(Package) %>% table() %>% as_tibble() %>% arrange(-n) %>% head(5)
## # A tibble: 5 x 2
## . n
## <chr> <int>
## 1 mlr 111
## 2 parameters 98
## 3 insight 88
## 4 broom 83
## 5 fscaret 67
となっている。
111個ってめちゃめちゃ多い印象。素の状態でSuggestsも含めてインストールしたらお風呂に入れそう。
こちらのSuggestsがあるパッケージのSuggestsの平均値は
Suggest_n <- pkg_list %>% filter(name == "Suggests") %>% select(Package) %>% table() %>% as_tibble() mean(Suggest_n$n)
## [1] 4.350905
となっている。
多く引用されているパッケージは……
Importsとして多く引用されているパッケージトップ10は次の通り。
pkg_list %>% filter(name == "Imports") %>% select(value) %>% table() %>% as_tibble() %>% rename("Package" = '.') %>% arrange(-n) %>% head(10)
## # A tibble: 10 x 2
## Package n
## <chr> <int>
## 1 stats 4481
## 2 utils 2896
## 3 methods 2701
## 4 dplyr 2423
## 5 ggplot2 2372
## 6 Rcpp 2221
## 7 graphics 1998
## 8 magrittr 1589
## 9 rlang 1431
## 10 grDevices 1326
R-Core
Team謹製の{stats}
、{utils}
、{methods}
の続いては{dplyr}
と{ggplot2}
が多くのパッケージでImportsとして引用されていることがわかる。
Hadley Wickham氏強い。
ただ、これはDescriptionにかかれているImportsを読んだだけなので、実際に引用数というふうに考えると孫引き等を考慮するともう少し数字は変わりそうな印象はある。
トップ20で可視化してみる
pkg_list %>% filter(name == "Imports") %>% select(value) %>% table() %>% as_tibble() %>% rename("Package" = '.') %>% arrange(-n) %>% head(20) %>% mutate(Package = factor(Package, levels = Package)) %>% ggplot(aes(x = Package, y = n)) + geom_bar(stat = "identity") + theme(axis.text.x = element_text(angle = 45, hjust = 1))
可視化してみると{stats}
が圧倒的に多いが、ある意味{stats}
で用意されている関数群はもはやRの{base}
といっても差し支えないのじゃないかと思うので(妄言)、実質{dplyr}
はトップ3に入っていると言っても過言じゃないんじゃないか(妄言2)と思う。いや、{utils}
も{methods}
も{base}
みたいなもんじゃないか(妄言3)、ということは被引用件数トップは{dplyr}
といっても過言じゃないんじゃないか(妄言4)。
でも本当に、基本的なパッケージである{stats}
や{utils}
などは普通に使っている分には勝手にロードされているのでRの{base}
と大差なくつかっている関数群になると思うので、意識して依存関係として使われているパッケージとしては{dplyr}
がトップといっても差し支えない気もする。
それはそうと、トップ20までみてみると、{tidyvese}
系のパッケージがなんと多いこと。
ちなみにSuggestsをみてみると、
pkg_list %>% filter(name == "Suggests") %>% select(value) %>% table() %>% as_tibble() %>% rename("Package" = '.') %>% arrange(-n) %>% head(10)
## # A tibble: 10 x 2
## Package n
## <chr> <int>
## 1 knitr 6413
## 2 testthat 6402
## 3 rmarkdown 5896
## 4 covr 1811
## 5 ggplot2 1080
## 6 dplyr 620
## 7 MASS 577
## 8 spelling 517
## 9 markdown 367
## 10 roxygen2 323
となっている。
{testthat}
って、テストを{testthat}
で書くと勝手に追加されたものってことでしょ?(多分)
ということはCARNに登録されているパッケージ全体の約1/3が、{testthat}
をつかってテストを書いているということ?で、やはりここでもHadley
Wickham強い。
こちらも可視化してみる。
pkg_list %>% filter(name == "Suggests") %>% select(value) %>% table() %>% as_tibble() %>% rename("Package" = '.') %>% arrange(-n) %>% head(20) %>% mutate(Package = factor(Package, levels = Package)) %>% ggplot(aes(x = Package, y = n)) + geom_bar(stat = "identity") + theme(axis.text.x = element_text(angle = 45, hjust = 1))
{kintr}
と{testthat}
、{rmarkdown}
が圧倒的に多い印象。
相互依存関係にあるパッケージは……
Importsにお互いのパッケージが記載されている、相互依存的なパッケージ、つまり片方のパッケージをいれるともう一方もインストールされる同士のパッケージは次の通りとなる。
pkg_import_list <- pkg_list %>% filter(name == "Imports") ans <- map2_df(pkg_import_list$Package, pkg_import_list$value, function(x, y){ pkg_import_list %>% filter(Package == y) %>% filter(value == x) }) ans
## # A tibble: 0 x 3
## # ... with 3 variables: Package <chr>, name <chr>, value <chr>
で(コードの誤りがなければ)なかった。
おまけ:空欄という依存関係
Discriptionを機械的に読み込んでいると、依存関係のパッケージに1つ空欄が出てくることがある。
これは必要ないのに、依存関係のパッケージの最後に,
をつけてもう一個パッケージがあるように見せかけられているから。
Importsにこのような記載があるパッケージが100以上あった。