-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathanalyze-network-data.qmd
executable file
·662 lines (539 loc) · 37.1 KB
/
analyze-network-data.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
# 网络数据分析 {#sec-analysis-network-data}
```{r}
#| echo: false
source("_common.R")
```
网络数据分析又是另一个大话题,微信、微博、论坛、知乎、豆瓣、美团等等应用都或多或少自带了社交属性。人不能脱离社会存在,社交是人的一种本能,身处洪流之中,即是复杂社会网络中的一个节点。网络分析的内容有很多,比如社区探测、节点影响力分析等。网络图是表示节点之间关系的图,核心在于关系的刻画。用来表达网络关系的是稀疏矩阵,以及为处理这种矩阵而专门优化的矩阵计算库,如 **Matrix** 包、[**rsparse**](https://github.com/rexyai/rsparse) 包和 **RcppEigen** 包[@Bates2013]等。图关系挖掘和计算的应用场景非常广泛,如社交推荐(社交 App)、风险控制(银行征信、企业查)、深度学习(图神经网络)、知识图谱(商户、商家、客户的实体关系网络)、区块链、物联网(IoT)、反洗钱(金融监管)、数据治理(数据血缘图谱)等。
本文将分析 R 语言社区开发者之间的协作关系网络。首先基于 CRAN (The Comprehensive R Archive Network)上发布的 R 包元数据信息,了解 R 语言社区 R 包及其维护者的规模,以及根据元数据中的信息发掘社区中的组织,最后,分析开发者在协作网络中的影响力,并将结果可视化。本文主要用到的工具有 [igraph](https://github.com/igraph/igraph) 包,操作图数据和图计算的 [tidygraph](https://github.com/thomasp85/tidygraph) 包,以及可视化图数据的 [ggraph](https://github.com/thomasp85/ggraph) 包。
## R 语言社区的规模 {#sec-community-capacity}
从 CRAN 上的 R 包及其开发者数量来看目前 R 语言社区规模。
```{r}
#| eval: false
#| echo: true
# 设置就近的 CRAN 镜像站点
Sys.setenv(R_CRAN_WEB = "https://mirrors.tuna.tsinghua.edu.cn/CRAN")
# 获取 R 包元数据
pdb <- tools::CRAN_package_db()
```
```{r}
#| label: cran-package-metadata
#| echo: false
pdb <- readRDS(file = "data/cran-package-db-20221231.rds")
```
截止 2022 年 12 月 31 日, CRAN 上发布的 R 包有 18976 个,CRAN 进入年末维护期 2022-12-22 至 2023-01-05。
```{r}
pdb <- subset(
x = pdb, subset = !duplicated(Package),
select = c("Package", "Maintainer", "Title", "Authors@R", "Date", "Published")
)
```
距离上次更新的时间分布,有的包是一周内更新的,也有的是 10 多年未更新的。
```{r}
pdb$date_diff <- as.integer(as.Date("2022-12-31") - as.Date(pdb$Published))
```
根据发布日期 Published 构造新的一列 --- 发布年份。
```{r}
pdb$published_year <- as.integer(format(as.Date(pdb$Published), "%Y"))
```
然后按年统计更新的 R 包数量,如 @fig-updated-package 所示,以 2020 年为例,总数 18976 个 R 包当中有 2470 个 R 包的更新日期停留在 2020 年,占比 2470 / 18976 = 13.02%。过去 1 年内更新的 R 包有 8112 个(包含新出现的 R 包),占总数 8112 / 18976 = 42.75%,过去 2 年内更新的 R 包有 11553 个,占总数 11553 / 18976 = 60.88%,这个占比越高说明社区开发者越活跃。
```{r}
#| label: fig-updated-package
#| fig-cap: "CRAN 上 R 包的更新情况"
#| fig-showtext: true
#| fig-width: 5
#| fig-height: 3.5
library(ggplot2)
aggregate(data = pdb, Package ~ published_year, FUN = length) |>
ggplot(aes(x = published_year, y = Package)) +
geom_col(fill = NA, color = "gray20") +
theme_classic() +
coord_cartesian(expand = F) +
labs(x = "年份", y = "R 包数量")
```
截止 2022-12-31,CRAN 上 R 包的维护者有 10067 人,其中有多少人在 2022 年更新了自己的 R 包呢?有 4820 个维护者,占比 47.96%,也就是说 2022 年,有 4820 个开发者更新了 8112 个 R 包,人均更新 1.68 个 R 包,下 @fig-active-maintainer 按 R 包发布年份统计开发者数量。
```{r}
# 清理维护者字段,同一个开发者可能有多个邮箱
extract_maintainer <- function(x) {
x <- gsub(pattern = "<.*?>", replacement = "", x = x)
trimws(x, which = "both", whitespace = "[ \t\r\n]")
}
# 只有 18 个维护者名字有大小写差别
pdb$Maintainer2 <- extract_maintainer(pdb$Maintainer)
# 维护者总数
length(unique(pdb$Maintainer2))
```
```{r}
#| label: fig-active-maintainer
#| fig-cap: "CRAN 上的维护者活跃情况"
#| fig-showtext: true
#| fig-width: 5
#| fig-height: 3.5
#| code-fold: true
#| echo: !expr knitr::is_html_output()
aggregate(
data = pdb, Maintainer2 ~ published_year,
FUN = function(x) { length(unique(x)) }
) |>
ggplot(aes(x = published_year, y = Maintainer2)) +
geom_col(fill = NA, color = "gray20") +
theme_classic() +
coord_cartesian(expand = F) +
labs(x = "年份", y = "开发者数量")
```
## R 语言社区的组织 {#sec-community-org}
除了 RStudio 公司出品的 [tidyverse](https://github.com/tidyverse/tidyverse) [@Wickham2019] 和 [tidymodels](https://github.com/tidymodels/tidymodels) [@Kuhn2020],还有一些数据分析、建模的工具箱,如 [mlr3verse](https://github.com/mlr-org/mlr3verse) [@Lang2023]、[easystats](https://github.com/easystats/easystats) [@Makowski2022]、[strengejacke](https://github.com/strengejacke/strengejacke) [@Daniel2019] 和 [DrWhy](https://github.com/ModelOriented/DrWhy) [@DrWhy2023]。也有的组织基本停止了开发,如 [Omegahat](https://github.com/omegahat)。还有的被商业公司收购后,不再活跃了,如 [Revolution Analytics](https://github.com/RevolutionAnalytics)。它们作为解决方案大都属于一些组织,还有深藏功与名,有待笔者挖掘的。因不存在明显的规律,下面从开发者的邮箱出发,隶属企业、组织往往有统一的邮箱后缀。
```{r}
str_extract <- function(text, pattern, ...) regmatches(text, regexpr(pattern, text, ...))
# 移除 ORPHANED
pdb <- subset(pdb, subset = Maintainer != "ORPHANED")
# 抽取邮件后缀
extract_email_suffix <- function(x) {
x <- str_extract(text = x, pattern = "<.*?>")
sub(x = x, pattern = ".*?@(.*?)>", replacement = "\\1")
}
pdb$Email_suffix <- extract_email_suffix(pdb$Maintainer)
```
按组织统计扩展包的数量(总的 R 包数量约 2 万),即各个组织开发的 R 包。
```{r}
pdb_pkg <- aggregate(
data = pdb, Package ~ Email_suffix, FUN = function(x) { length(unique(x)) }
)
head(pdb_pkg[order(pdb_pkg$Package, decreasing = TRUE), ], 20)
```
不难看出,至少有如下几类:
1. 邮件服务提供商。6968 个 R 包使用 gmail 邮箱作为联系维护者的方式,googlemail.com 也是谷歌提供的服务。hotmail.com 和 outlook.com 都是微软提供的邮箱服务,outlook.fr (法国)也是,除此之外,比较大的邮件服务提供商就是 163.com(网易)、 protonmail.com 和 yahoo.com (雅虎)等。
2. 商业组织。208 个 R 包来自 RStudio 公司的员工,这些维护者使用 RStudio 公司提供的邮箱。
3. 开源组织。R-project.org 和 r-project.org 都是 R 语言组织的联系方式,自不必多说,R 语言核心团队成员不仅维护 R 软件源码,还维护了很多 R 包。debian.org 是 Debian 组织的联系方式,都是开源组织(Open Source Org)。
4. 教育机构。berkeley.edu 、umich.edu 等以 edu 结尾的北美(国)的大学,gmx.de、 posteo.de 等以 de 结尾的德国大学,ucl.ac.uk 等以 uk 结尾的英国的大学,auckland.ac.nz 等以 nz 结尾的新西兰的大学,uwaterloo.ca 等以 ca 结尾的加拿大的大学。
按组织统计开发者的数量(总的开发者数量约 1 万),即各个组织的 R 包开发者。
```{r}
pdb_org <- aggregate(
data = pdb, Maintainer2 ~ Email_suffix, FUN = function(x) { length(unique(x)) }
)
head(pdb_org[order(pdb_org$Maintainer2, decreasing = TRUE), ], 20)
```
可见,大部分开发者采用邮件服务提供商的邮件地址。3800 个开发者使用来自谷歌的 gmail.com、197 个开发者使用来自微软的 hotmail.com 或 outlook.com,57 个开发者使用来自网易的 163.com,51 个开发者使用来自雅虎的 yahoo.com,46 个开发者使用来自 Proton 的 protonmail.com。
无论从开发者数量还是 R 包数量的角度看,都有两个显著特点。其一马太效应,往头部集中,其二,长尾分布,尾部占比接近甚至超过 50%。
### 美国、英国和加拿大 {#sec-edu-uk-ca}
1666 个开发者来自以 edu 为后缀的邮箱。各个组织(主要是大学)及其 R 包开发者数据如下:
```{r}
sum(pdb_org[grepl(pattern = "edu$", x = pdb_org$Email_suffix), "Maintainer2"])
pdb_org_edu <- pdb_org[grepl(pattern = "edu$", x = pdb_org$Email_suffix), ]
pdb_org_edu[order(pdb_org_edu$Maintainer2, decreasing = TRUE), ] |> head(20)
```
好吧,几乎全是美国各个 NB 大学的,比如华盛顿大学( uw.edu)、密歇根大学(umich.edu)、加州伯克利大学(berkeley.edu)等等。顺便一说,美国各个大学的网站,特别是统计院系很厉害的,已经帮大家收集得差不多了,有留学打算的读者自取,邮箱后缀就是学校/院官网。
有些邮箱后缀带有院系,但是并没有向上合并到学校这一级,比如 `stanford.edu` 、`stat.stanford.edu` 和 `alumni.stanford.edu` 等没有合并统计。实际上,使用 `edu` 邮箱的教育机构大部份位于美国。有的邮箱来自教育机构,但是不以 `edu` 结尾,比如新西兰奥克兰大学 `auckland.ac.nz` 、瑞士苏黎世联邦理工学院 `stat.math.ethz.ch` 等美国以外的教育机构。下面分别查看英国和加拿大的情况。
350 个开发者来自以 uk 为后缀的邮箱。各个组织(主要是大学)及其 R 包开发者数据如下:
```{r}
sum(pdb_org[grepl(pattern = "uk$", x = pdb_org$Email_suffix), "Maintainer2"])
pdb_org_uk <- pdb_org[grepl(pattern = "uk$", x = pdb_org$Email_suffix), ]
pdb_org_uk[order(pdb_org_uk$Maintainer2, decreasing = TRUE), ] |> head(20)
```
258 个开发者来自以 ca 为后缀的邮箱。各个组织(主要是大学)及其 R 包开发者数据如下:
```{r}
sum(pdb_org[grepl(pattern = "ca$", x = pdb_org$Email_suffix), "Maintainer2"])
pdb_org_ca <- pdb_org[grepl(pattern = "ca$", x = pdb_org$Email_suffix), ]
pdb_org_ca[order(pdb_org_ca$Maintainer2, decreasing = TRUE), ] |> head(10)
```
### CRAN 和 RStudio {#sec-cran-rstudio}
下面根据邮箱后缀匹配抽取 CRAN 团队及开发的 R 包,规则也许不能覆盖所有的情况,比如署名 CRAN Team 的维护者代表的是 CRAN 团队,**XML** 和 **RCurl** 包就由他们维护。再比如,Brian Ripley 的邮箱 [ripley\@stats.ox.ac.uk](mailto:ripley@stats.ox.ac.uk){.email} 就不是 CRAN 官网域名。读者若有补充,欢迎 PR 给我。
```{r}
#| label: tbl-cran-developers
#| tbl-cap: "CRAN 团队开发维护 R 包数量情况"
#| tbl-subcap:
#| - "表"
#| - "续表"
#| layout-ncol: 2
#| code-fold: true
#| echo: !expr knitr::is_html_output()
#| comment: NA
cran_dev <- subset(pdb,
subset = grepl(
x = Maintainer,
pattern = paste0(c(
"(@[Rr]-project\\.org)", # 官方邮箱
"(ripley@stats.ox.ac.uk)", # Brian Ripley
"(p.murrell@auckland.ac.nz)", # Paul Murrell
"(paul@stat.auckland.ac.nz)", # Paul Murrell
"(maechler@stat.math.ethz.ch)", # Martin Maechler
"(mmaechler+Matrix@gmail.com)", # Martin Maechler
"(bates@stat.wisc.edu)", # Douglas Bates
"(pd.mes@cbs.dk)", # Peter Dalgaard
"(ligges@statistik.tu-dortmund.de)", # Uwe Ligges
"(tlumley@u.washington.edu)", # Thomas Lumley
"(t.lumley@auckland.ac.nz)", # Thomas Lumley
"(martyn.plummer@gmail.com)", # Martyn Plummer
"(luke-tierney@uiowa.edu)", # Luke Tierney
"(stefano.iacus@unimi.it)", # Stefano M. Iacus
"(murdoch.duncan@gmail.com)", # Duncan Murdoch
"(michafla@gene.com)" # Michael Lawrence
), collapse = "|")
),
select = c("Package", "Maintainer")
) |>
transform(Maintainer = gsub(
x = Maintainer, pattern = '(<([^<>]*)>)|(")', replacement = ""
)) |>
transform(Maintainer = gsub(
x = Maintainer, pattern = "(R-core)|(R Core Team)", replacement = "CRAN Team"
)) |>
transform(Maintainer = gsub(
x = Maintainer,
pattern = "(S. M. Iacus)|(Stefano M.Iacus)|(Stefano Maria Iacus)",
replacement = "Stefano M. Iacus"
)) |>
transform(Maintainer = gsub(
x = Maintainer, pattern = "(Toby Hocking)",
replacement = "Toby Dylan Hocking"
)) |>
transform(Maintainer = gsub(
x = Maintainer, pattern = "(John M Chambers)", replacement = "John Chambers"
))
cran_dev <- aggregate(data = cran_dev, Package ~ Maintainer, FUN = function(x) length(unique(x)))
cran_dev <- cran_dev[order(cran_dev$Package, decreasing = TRUE), ]
knitr::kable(head(cran_dev, ceiling(nrow(cran_dev) / 2)),
col.names = c("团队成员", "R 包数量"), row.names = FALSE
)
knitr::kable(tail(cran_dev, floor(nrow(cran_dev) / 2)),
col.names = c("团队成员", "R 包数量"), row.names = FALSE
)
```
Kurt Hornik、Simon Urbanek、Achim Zeileis 等真是高产呐!除了维护 R 语言核心代码,还开发维护了那么多 R 包。以 Brian Ripley 为例,看看他都具体维护了哪些 R 包。
```{r}
#| label: tbl-ripley
#| tbl-cap: Brian Ripley 维护的 R 包
#| code-fold: true
#| echo: !expr knitr::is_html_output()
subset(pdb,
subset = grepl(x = Maintainer, pattern = "Brian Ripley"),
select = c("Package", "Title"), drop = TRUE
) |>
unique(by = "Package") |>
transform(Title = gsub(pattern = "(\\\n)", replacement = " ", x = Title)) |>
knitr::kable(row.names = FALSE)
```
震惊!有一半收录在 R 软件中,所以已经持续维护 **20** 多年了。下面继续根据邮箱后缀将 RStudio 团队的情况统计出来,结果见下表。
```{r}
#| label: tbl-rstudio-developers
#| tbl-cap: "RStudio 团队开发维护 R 包数量情况(部分)"
#| tbl-subcap:
#| - "表"
#| - "续表"
#| layout-ncol: 2
#| code-fold: true
#| echo: !expr knitr::is_html_output()
rstudio_dev <- subset(pdb,
subset = grepl(x = Maintainer, pattern = "(posit.co)|(rstudio.com)|(yihui.name)"),
select = c("Package", "Maintainer")
) |>
transform(Maintainer = extract_maintainer(Maintainer))
rstudio_dev <- aggregate(data = rstudio_dev, Package ~ Maintainer, FUN = function(x) length(unique(x)))
rstudio_dev <- rstudio_dev[order(rstudio_dev$Package, decreasing = TRUE), ]
knitr::kable(head(rstudio_dev, ceiling(nrow(rstudio_dev) / 2)),
col.names = c("团队成员", "R 包数量"), row.names = FALSE
)
knitr::kable(tail(rstudio_dev, floor(nrow(rstudio_dev) / 2)),
col.names = c("团队成员", "R 包数量"), row.names = FALSE
)
```
CRAN 和 RStudio 团队是 R 语言社区最为熟悉的,其它团队需借助一些网络分析算法挖掘了。
{{< pagebreak >}}
## R 语言社区的开发者 {#sec-community-developer}
### 最高产的开发者 {#sec-top-developer}
继续基于数据集 pdb ,将维护 R 包数量比较多的开发者统计出来。
```{r}
#| label: fig-top-maintainer
#| fig-showtext: true
#| fig-cap: 高产的 R 包开发者
#| fig-width: 6
#| fig-height: 6
#| code-fold: true
#| echo: !expr knitr::is_html_output()
pdb_ctb <- aggregate(data = pdb, Package ~ Maintainer2, FUN = length)
ggplot(data = pdb_ctb[pdb_ctb$Package >= 20, ]) +
geom_col(aes(x = Package, y = reorder(Maintainer2, Package)), width = .1) +
theme_classic() +
labs(x = "R 包数量", y = "开发者")
```
这些开发者的主页和主要的 R 社区贡献如下:
1. [Dirk Eddelbuettel](https://dirk.eddelbuettel.com/) 维护了 Rcpp、RcppEigen 等流行的 R 包,通过 Rcpp 包将很多优秀的 C++ 库引入 R 语言社区。
2. [Stéphane Laurent](https://github.com/stla/) 维护了很多与 shiny、htmlwidgets 相关的 R 包,比如 rAmCharts4 包。
3. [Gábor Csárdi](https://github.com/gaborcsardi/) 维护了 igraph 包以及大量帮助 R 包开发的基础设施,RStudio 雇员。
4. [Hadley Wickham](https://github.com/hadley/) 维护了 ggplot2、dplyr、devtools 等流行的 R 包,RStudio 雇员。
5. [Jeroen Ooms](https://github.com/jeroen/) 维护了 magick、curl 以及大量帮助 R 包开发的基础设施。
6. [Scott Chamberlain](https://scottchamberlain.info/) 维护了很多与 HTTP/Web 相关的 R 包,rOpenSci 联合创始人。
7. [Robin K. S. Hankin](https://github.com/RobinHankin/) 维护了很多与贝叶斯、多元统计相关的 R 包。
8. [Henrik Bengtsson](https://github.com/HenrikBengtsson/) 维护了 future 和 parallelly 等流行的 R 包,在并行计算方面有很多贡献。
9. [Jan Wijffels](https://github.com/jwijffels) 维护了很多与自然语言处理、图像识别相关的 R 包,比如 udpipe 、BTM 和 word2vec 等包,[Bnosac](https://bnosac.be/) 团队成员。
10. [Kurt Hornik](https://statmath.wu.ac.at/~hornik/) 参与维护 R 软件代码并许多与自然语言处理相关的 R 包,R 核心团队成员。
11. [Martin Maechler](https://github.com/mmaechler) 维护了 Matrix 包,R 核心团队成员。
12. [Max Kuhn](https://github.com/topepo) 维护了 tidymodels 等包,RStudio 雇员。
13. [Bob Rudis](https://rud.is/) 维护了一些与 ggplot2 相关的 R 包,如 ggalt、hrbrthemes 和 statebins 等。
14. [Kartikeya Bolar](https://kartikeyabolar.r-universe.dev/) 维护了很多统计与 shiny 结合的 R 包,比如方差分析、逻辑回归、列联表、聚类分析等。
15. [Kirill Müller](https://github.com/krlmlr/) 维护了 DBI 等大量与数据库连接的 R 包。
16. [Shannon T. Holloway](https://populationhealth.duke.edu/personnel/shannon-holloway/) 维护了许多与生存分析相关的 R 包。
17. [Simon Urbanek](https://github.com/s-u/) 维护了 rJava、Rserve 等流行的 R 包,R 核心团队成员,负责维护 R 软件中与 MacOS 平台相关的部分。
18. [Achim Zeileis](https://github.com/zeileis/) 维护了 colorspace 等流行的 R 包,R 核心团队成员。
19. [Muhammad Yaseen](https://github.com/myaseen208/) 维护了多个与 Multiple Indicator Cluster Survey 相关的 R 包。
20. [Pablo Sanchez](https://github.com/pablosanchezmart/) 维护了多个与市场营销平台连接的 R 语言接口,[Windsor.ai](https://github.com/windsor-ai/) 组织成员。
21. [Thomas Lin Pedersen](https://github.com/thomasp85/) 维护了 patchwork、 gganimate 和 ggraph 等流行的 R 包,RStudio 雇员。
22. [Torsten Hothorn](https://user.math.uzh.ch/hothorn/) 在统计检验方面贡献了不少内容,比如 coin 和 multcomp 等包,R 核心团队成员。
23. [Richard Cotton](https://github.com/richierocks/) 维护了 assertive 和 rebus 系列 R 包,代码可读性检查。
24. [Florian Schwendinger](https://github.com/FlorianSchwendinger) 维护了大量运筹优化方面的 R 包,扩展了 [ROI](https://gitlab.com/roigrp/solver) 包的能力。
25. [Guangchuang Yu](https://github.com/GuangchuangYu/) 维护了 ggtree 和 ggimage 等 R 包,在生物信息和可视化领域有不少贡献。
26. [Winston Chang](https://github.com/wch/) 维护了 shiny 等流行的 R 包,RStudio 雇员。
27. [John Muschelli](https://github.com/muschellij2/) 维护了多个关于神经图像的 R 包。
28. [Kevin R. Coombes](https://gitlab.com/krcoombes/) 维护了多个关于生物信息的 R 包,如 [oompaBase](https://oompa.r-forge.r-project.org/) 和 oompaData 等。
29. [Yihui Xie](https://yihui.org/) 维护了 knitr 、rmarkdown 等流行的 R 包,RStudio 雇员。
30. [Carl Boettiger](https://www.carlboettiger.info/) 维护了多个接口包,比如 rfishbase 等,rOpenSci 团队成员。
31. [Michael D. Sumner](https://github.com/mdsumner/) 维护了多个空间统计相关的 R 包。
32. [Emil Hvitfeldt](https://github.com/EmilHvitfeldt/) 维护了多个统计学习相关的 R 包,如 fastTextR 包等,RStudio 雇员。
33. [Georgi N. Boshnakov](https://www.rmetrics.org/) 维护了多个金融时间序列相关的 R 包,如 fGarch、timeDate 和 timeSeries 等包。
34. [Hana Sevcikova](https://github.com/hanase/) 维护了多个与贝叶斯人口统计相关的 R 包。
35. [Joe Thorley](https://github.com/joethorley/) 维护了多个与贝叶斯 MCMC 相关的 R 包,Poisson Consulting 雇员。
统计开发者数量随维护 R 包数量的分布,发现,开发 1 个 R 包的开发者有 6732 人,开发 2 个 R 包的开发者有 1685 人,第二名是第一名的五分之一,递减规律非常符合指数分布。
```{r}
table(pdb_ctb$Package)
```
过滤掉非常高产的开发者,可以发现变化规律服从幂律分布。
```{r}
#| label: fig-dist-maintainer
#| fig-cap: 开发者数量的分布
#| fig-subcap:
#| - 直方图
#| - 直方图(对数尺度)
#| fig-width: 4
#| fig-height: 3
#| fig-showtext: true
#| layout-ncol: 2
ggplot(data = pdb_ctb, aes(x = Package)) +
geom_histogram(binwidth = 1) +
theme_classic() +
labs(x = "R 包数量", y = "开发者")
ggplot(data = pdb_ctb[pdb_ctb$Package <= 20, ], aes(x = Package)) +
geom_histogram(binwidth = 1, fill = NA, color = "gray20") +
scale_y_log10() +
theme_classic() +
labs(x = "R 包数量", y = "开发者")
```
最高产 Top 1% 的开发者 131 人(开发 R 包超过 10 个的开发者)贡献了 2329 / 18976 = 12.3% 的扩展包 ,高产的是商业公司、开源组织、大学机构。
```{r}
dim(pdb_ctb[pdb_ctb$Package > 10, ])
sum(pdb_ctb[pdb_ctb$Package > 10, "Package"])
```
最低产 Bottom 的开发者 6732 人(仅开发一个 R 包的开发者)占总开发者的比例 6732 / 10067 = 66.87%, 贡献了 6732 / 18976 = 35.5 % 的扩展包 ,低产的人是主体。
### 开发者协作关系 {#sec-coauthor-relation}
如果一个开发者维护了一个 R 包,就成为维护者。一个 R 包有唯一的一个维护者,可能有一个至多个贡献者,这样,维护者和贡献者之间就形成了有向关系,贡献者可能又是另一个 R 包的维护者,也可能不是。不仅有向而且可能存在环。在一个 R 包中,A 是 B 的贡献者,而在另一个 R 包中,B 是 A 的贡献者,A 和 B 之间可能通过多个 R 包存在多次互相协作关系,这也表明 A 和 B 之间的关系密切。有向环的节点可能有 2 个以上,一个人可能同时属于多个环。
维护者 A 接受来自多个开发者的贡献,接受次数(所有贡献者人数的累和,A 的每个 R 包的贡献者人数相加)视为 A 的入度。维护者 A 作为开发者给多个维护者贡献,贡献次数(作为开发者给其它 R 包做贡献的次数,向外参与贡献的 R 包数目)视为 A 的出度。注意,A 作为维护者,必然包含 A 作为开发者,忽略 A 到 A 的贡献,只考虑贡献/协作关系。
```{r}
# 过滤重复和缺失的记录
pdb <- subset(
x = pdb, subset = !duplicated(Package) & !is.na(`Authors@R`),
select = c("Package", "Maintainer", "Authors@R")
)
# 提取维护者的名字
pdb$Maintainer <- extract_maintainer(pdb$Maintainer)
```
有些包的元数据中没有 Authors\@R 字段,有可能是没有贡献者,比如 mgcv 包、gam 包等,但也有可能是有贡献者,只是维护者没有填写这个字段,比如 Rcpp 包、RcppEigen 包等,因此将这些先过滤出来。总之,本文是以 `Authors@R` 字段作为贡献者的来源,共计 12503 个 R 包含有 `Authors@R` ,有 6000+ 个 R 包没有该字段,缺失约占 R 包总数的 1/3,在不那么考虑准确性的情况下,也可以使用。Author 字段是一段没有结构的文本,相比于 Author 字段,`Authors@R` 字段是以 R 语言中的 person 类型为存储结构的,比较规范,因此,提取贡献者的操作比较方便。作为示例,下面提取 Matrix 包的贡献者。
```{r}
tmp <- eval(parse(text = pdb[pdb$Package == "Matrix", "Authors@R"]))
tmp <- unlist(lapply(tmp, function(x) format(x, include = c("given", "family"))))
# 返回一个整洁的数据框
tmp <- data.frame(Package = "Matrix", Maintainer = pdb[pdb$Package == "Matrix", "Maintainer"], Authors = tmp)
# 去掉 Authors 是 Maintainer 的记录
subset(tmp, subset = Maintainer != Authors)
```
数据框包含 R 包(Package 字段)、及其维护者(Maintainer 字段)和贡献者(Authors 字段)。将上述过程写成一个函数,接着,将所有 R 包的贡献者提取出来,形成一个大的数据框。
```{r}
extract_authors <- function(pkg) {
sub_pdb <- pdb[pdb$Package == pkg, ]
tmp <- eval(parse(text = sub_pdb[, "Authors@R"]))
tmp <- unlist(lapply(tmp, function(x) format(x, include = c("given", "family"))))
tmp <- data.frame(Package = pkg, Maintainer = sub_pdb[, "Maintainer"], Authors = tmp)
subset(tmp, subset = Maintainer != Authors)
}
extract_authors("Matrix")
# lapply(c("Matrix", "gt"), extract_authors)
# 抽取所有 R 包的贡献者,运行需要1-2分钟时间
pdb_authors_list <- lapply(pdb[, "Package"], extract_authors)
# 合并列表
pdb_authors_dt <- data.table::rbindlist(pdb_authors_list)
```
最后整理出来的大数据框 **`pdb_authors_dt`** 含有近 26000 条记录,即边的规模大小。考虑到有些维护者和贡献者之间可能存在多次合作的情况,下面统计一下合作次数。
```{r}
pdb_authors_dt[ ,.(cnt = length(Package)) , by = c("Maintainer", "Authors")
][cnt >= 10, ][order(cnt, decreasing = T), ]
```
Authors 字段出现了不少组织的名字,这是因为有许多 R 包的维护者受雇于该组织,版权归属于该组织,组织不仅提供持续的资金,而且还提供其它帮助。以 **dplyr** 包为例,Hadley Wickham 受雇于 RStudio 公司,在 dplyr 包的元数据中,字段 `Authors@R` 中 RStudio 的角色是 cph 和 fnd ,即版权所有和资金支持。角色 cre 就是维护者,负责与 CRAN 团队的沟通。角色 aut 就是对 R 包有实质贡献的人。
```{r}
format(eval(parse(text = pdb[pdb$Package == "dplyr", "Authors@R"])),
include = c("given", "family", "role"))
```
此外,同属于一个组织的维护者之间常常合作紧密,从上面的结果可以看到,Gábor Csárdi 和 Jim Hester ,Lionel Henry 和 Hadley Wickham,Carson Sievert 和 Joe Cheng ,Jennifer Bryan 和 Hadley Wickham 等同属于 RStudio 公司,常常协作开发项目。对 RStudio、CRAN Team 和 rOpenSci 不再赘述,下面对排名靠前的其它组织略作说明。
1. [Windsor.ai](https://windsor.ai/) 提供一系列可以连接各大营销平台,获取营销效果数据 R 包。
2. [BNOSAC](https://github.com/bnosac) 提供一系列计算机视觉、图像识别、自然语言处理方面的 R 包,比如 [udpipe](https://github.com/bnosac/udpipe)、[word2vec](https://github.com/bnosac/word2vec)、[doc2vec](https://github.com/bnosac/doc2vec) 等包。
3. Microsoft 提供一系列连接和操作 [Azure 云](https://azure.microsoft.com/zh-cn)套件的 R 包,比如 [AzureR](https://github.com/Azure/AzureR) 包。
4. [Wouter Saelens](https://dynverse.org/) 提供一系列单细胞轨迹推理(single-cell trajectory inference)相关的 R 包,形成一个 [**dynverse**](https://github.com/dynverse) 家族**。**
5. [Poisson Consulting](https://github.com/poissonconsulting) 提供一系列用于计算生物学和统计生态学的 R 包和相关研究论文。
6. [Amazon.com, Inc.](https://aws.amazon.com/cn/) 提供一系列用于存储、管理、操作等 Amazon 云服务的 R 包,形成一个 [paws](https://github.com/paws-r/paws) 套件。
7. [Eli Lilly and Company](https://www.lilly.com/) 可能是 [rOpenSci](https://github.com/ropensci) 的一员,赞助了旗下的 [targets](https://github.com/ropensci/targets) 和 [jagstargets](https://github.com/ropensci/jagstargets) 等 R 包。
最后,统计协作次数的分布,网络中边的权重的分布。
```{r}
pdb_authors_net <- pdb_authors_dt[, .(cnt = .N), by = c("Maintainer", "Authors")]
table(pdb_authors_net$cnt)
```
可以发现,绝大多数人之间协作只有一次。
### 节点出入度分布 {#sec-network-degree}
下面简化这个网络,仅考虑贡献者也是维护者的情况,就是说网络中所有节点既是维护者也是贡献者,这会过滤掉组织机构、大量没有在 CRAN 发过 R 包的贡献者、从没给其它维护者做贡献的维护者。简化后,网络节点的出度、入度的分布图如下。
```{r}
#| label: fig-network-degree
#| fig-cap: 节点的入度和出度的分布
#| fig-showtext: true
#| fig-width: 4
#| fig-height: 3
#| fig-subcap:
#| - 入度的分布
#| - 出度的分布
#| layout-ncol: 2
# Maintainer 的入度
pdb_authors_net_indegree <- pdb_authors_dt[Authors %in% Maintainer,
][, .(in_degree = length(Authors)), by = "Maintainer"]
# Authors 的出度
pdb_authors_net_outdegree <- pdb_authors_dt[Authors %in% Maintainer,
][, .(out_degree = length(Maintainer)), by = "Authors"]
ggplot(pdb_authors_net_indegree, aes(x = in_degree)) +
geom_histogram(binwidth = 1) +
geom_freqpoly(binwidth = 1) +
theme_classic()
ggplot(pdb_authors_net_outdegree, aes(x = out_degree)) +
geom_histogram(binwidth = 1) +
geom_freqpoly(binwidth = 1) +
theme_classic()
```
### 可视化协作网络 {#sec-visual-coauthor-network}
节点的大小以维护者维护的 R 包数量来表示,边的大小以维护者之间协作次数来表示。为了美观起见,更为了突出重点,仅保留协作次数大于 1 的边。
```{r}
# 边
pdb_authors_net_edge <- pdb_authors_dt[Authors %in% Maintainer,
][, .(edge_cnt = .N), by = c("Authors", "Maintainer")][edge_cnt > 1, ]
pdb_authors_net_edge[order(edge_cnt, decreasing = TRUE),]
# 顶点
pdb_authors_net_vertex <- pdb_authors_dt[, .(vertex_cnt = length(unique(Package))), by = "Maintainer"
][Maintainer %in% c(pdb_authors_net_edge$Maintainer, pdb_authors_net_edge$Authors),]
pdb_authors_net_vertex[order(vertex_cnt, decreasing = TRUE),]
```
这是一个有向图,其各个字段含义如下。
- Maintainer 维护者(代表流 to)
- Authors 贡献者(代表源 from)
- `edge_cnt` 边的大小表示维护者 Maintainer 和贡献者 Authors 的协作次数
- `vertex_cnt` 顶点大小表示维护者 Maintainer 维护的 R 包数量
下面先考虑用 igraph 包可视化这个复杂的有向带权网络。`pdb_authors_net_edge` 和 `pdb_authors_net_vertex` 都是数据框,首先调用 igraph 包的函数 `graph_from_data_frame()` 将其转化为网络类型 `igraph` ,然后使用函数 `plot()` 绘制网络图。
```{r}
#| label: fig-network-igraph
#| fig-cap: 开发者的协作关系网络
#| fig-width: 6
#| fig-height: 6
#| fig-showtext: true
#| message: false
#| code-fold: true
#| echo: !expr knitr::is_html_output()
# 构造图
library(igraph)
pdb_authors_graph <- graph_from_data_frame(d = pdb_authors_net_edge, vertices = pdb_authors_net_vertex, directed = TRUE)
# 可视化
op <- par(mar = rep(0, 4))
plot(pdb_authors_graph,
edge.width = (E(pdb_authors_graph)$edge_cnt) / 2,
edge.arrow.size = .01,
edge.curved = .1,
layout = layout.kamada.kawai,
vertex.size = (V(pdb_authors_graph)$vertex_cnt) / 8,
vertex.label.cex = sqrt(V(pdb_authors_graph)$vertex_cnt) / 8
)
on.exit(par(op), add = TRUE)
```
协作关系弱的开发者占大部分,构成一个「月亮」的造型,其中,不乏维护多个 R 包的开发者,这些人要么单干,要么在专业小领域、小组织内协作。与之相对应的是协作关系较强的开发者,人数虽少,影响力却大,构成一个「太阳」的造型。协作得多往往意味着维护的 R 包也不少,甚至同属于一个组织,因此,高产的开发者、影响力大的组织聚集在一起,如 R Core Team、RStudio、rOpenSci 等。
```{r}
eb <- cluster_edge_betweenness(pdb_authors_graph)
eb
```
**igraph** 包提供多种社区探测的算法,上面简单使用函数 `cluster_edge_betweenness()` 来探测,结果显示有 181 个社区。社区 1 包含的成员如下:
```{r}
eb$names[eb$membership == 1]
```
社区 3、14、21、34、46、52、75 的成员是比较多的。其中,社区 3 是以 RStudio 为核心的大社区,社区 14 是以 CRAN 为核心的大社区。
```{r}
# RStudio 为核心的大社区
eb$names[eb$membership == 3]
# CRAN 为核心的大社区
eb$names[eb$membership == 14]
```
同时,在 RStudio 这个大社区下,有一些与之紧密相关的小社区,比如 Rob Hyndman 等人的时间序列社区、Roger Bivand 等人的空间统计社区。
```{r}
# 时间序列 Rob Hyndman
eb$names[eb$membership == 52]
# 空间统计 Roger Bivand
eb$names[eb$membership == 75]
```
结合前面的 @fig-network-igraph ,知道有很多小圈圈,这些放一边,重点关注那些大的圈圈,见下图。
```{r}
#| label: fig-network-cluster
#| fig-cap: 探测协作关系网络中的社区
#| fig-width: 6
#| fig-height: 6
#| fig-showtext: true
#| code-fold: true
#| echo: !expr knitr::is_html_output()
op <- par(mar = rep(0, 4))
plot(eb, pdb_authors_graph,
edge.width = (E(pdb_authors_graph)$edge_cnt) / 4,
edge.arrow.size = .01,
edge.curved = .1,
layout = layout.kamada.kawai,
vertex.size = (V(pdb_authors_graph)$vertex_cnt) / 8,
vertex.label.cex = sqrt(V(pdb_authors_graph)$vertex_cnt) / 8
)
on.exit(par(op), add = TRUE)
```
下面使用 **tidygraph** 包构造图数据、计算节点中心度,**dplyr** 包操作数据。中心度代表节点(开发者)的影响力(或者重要性)。最后,借助 **ggraph** 包绘制维护者之间的贡献网络,节点的大小代表维护者影响力的强弱。
```{r}
#| label: fig-network-ggraph
#| fig-cap: 开发者的影响力网络
#| fig-width: 7
#| fig-height: 6
#| fig-showtext: true
#| message: false
#| code-fold: true
#| echo: !expr knitr::is_html_output()
pdb_authors_g <- tidygraph::as_tbl_graph(pdb_authors_net_edge, directed = T) |>
dplyr::mutate(Popularity = tidygraph::centrality_degree(mode = 'in'))
library(ggraph)
ggraph(pdb_authors_g, layout = "kk") +
geom_edge_fan(aes(alpha = after_stat(index)), show.legend = FALSE) +
geom_node_point(aes(size = Popularity), alpha = 0.5) +
theme_graph(base_family = "sans")
```
前面两个网络图基于同一份数据、同样的网络布局算法,得到非常类似的结果。静态图上的标签相互重叠,影响细节的观察和探索,比如连接 CRAN 和 RStudio 两大阵营的通道。下面使用 [visNetwork](https://github.com/datastorm-open/visNetwork) 包制作交互式网络图形,它是 JS 库 [vis-network](https://github.com/visjs/vis-network) 的 R 语言接口, 使用 **visNetwork** 包绘制交互式网络图后,可以在图上使用鼠标放大、拖拽。可以发现在 CRAN 社区的 Achim Zeileis 和 RStudio 社区的 Max Kuhn 之间是由 Andri Signorell 牵线搭桥。此外,读者若有兴趣,可以使用 Richard Iannone 开发的 [DiagrammeR](https://github.com/rich-iannone/DiagrammeR) 包制作静态的矢量网页图形。
```{r}
#| label: fig-network-visnetwork
#| fig-cap: 开发者的影响力网络(visNetwork)
#| code-fold: true
#| echo: !expr knitr::is_html_output()
library(visNetwork)
# 将 igraph 对象转为 visNetwork 包可用的数据
dat <- toVisNetworkData(pdb_authors_graph)
nodes_df <- dat$nodes
nodes_df$value <- nodes_df$vertex_cnt
edges_df <- dat$edges
edges_df$value <- edges_df$edge_cnt
# 输入节点和边的数据
visNetwork(nodes = nodes_df, edges = edges_df, height = "600px") |>
visIgraphLayout(randomSeed = 20232023, layout = "layout.kamada.kawai")
```
## 扩展阅读
R 语言网络分析方面的著作有 Erick Kolaczyk 的书籍《Statistical Analysis of Network Data with R》[@Kolaczyk2020],网络可视化方面,推荐 Hadley Wickham 的著作《ggplot2: Elegant Graphics for Data Analysis》[@Hadley2024] 的第七章,Sam Tyner 等人的文章《Network Visualization with ggplot2》[@Tyner2017] 也值得一看。
在网络数据分析方面, [igraph](https://github.com/igraph/igraph) 是非常流行的分析框架 ,它是由 C 语言写成的,非常高效。同时,它提供多种语言的接口,其 R 语言接口 [igraph](https://github.com/igraph/rigraph) 包在 R 语言社区也是网络数据分析的事实标准,被很多其它做网络分析的 R 包所引用。开源的 [Gephi](https://github.com/gephi/gephi) 软件适合处理中等规模的网络分析和可视化。大规模图计算可以用 Apache Spark 的 [GraphX](https://spark.apache.org/graphx/)。R 语言这层,主要还是对应数据分析和数据产品,用在内部咨询和商业分析上。
企业级的图存储和计算框架,比较有名的是 [Neo4j](https://github.com/neo4j/neo4j) ,它有开源版本和商业版本。[Nebula Graph](https://github.com/vesoft-inc/nebula) 开源分布式图数据库,具有高扩展性和高可用性,支持千亿节点、万亿条边、毫秒级查询,有[中文文档](https://github.com/vesoft-inc/nebula-docs-cn/),有企业应用案例([美团图数据库平台建设及业务实践](https://mp.weixin.qq.com/s/aYd5tqwogJYfkJXhVNuNpg))。阿里研发的 [GraphScope](https://github.com/alibaba/GraphScope) 提供一站式大规模图计算系统,支持图神经网络计算。
## 习题 {#sec-analysis-network-data-exercise}
1. 类似开发者协作关系的分析,可以统计 R 包被多少 R 包依赖,依赖数量的分布。统计 R 包被依赖的深度(若 R 包 A 被 R 包 B 依赖,R 包 B 被 R 包 C 依赖,以此类推)。进而,构建、分析、可视化依赖关系网络,分析 R 包的影响力。
2. 本文基于 2022 年 12 月 31 日的 R 包元数据进行分析,请与 2023 年 12 月 31 日的数据比较。