ropensci-review-tools/pkgstats

GitHub: ropensci-review-tools/pkgstats

针对 R 包进行静态代码分析,提取代码行数、函数定义、调用网络、外部依赖等多维度统计指标的工具库。

Stars: 20 | Forks: 6

[![R build status](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/e776f380ae002012.svg)](https://github.com/ropensci-review-tools/pkgstats/actions?query=workflow%3AR-CMD-check.yaml) [![codecov](https://codecov.io/gh/ropensci-review-tools/pkgstats/branch/main/graph/badge.svg)](https://app.codecov.io/gh/ropensci-review-tools/pkgstats) [![Project Status: Active](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) [![CRAN_Status_Badge](https://www.r-pkg.org/badges/version/pkgstats)](https://cran.r-project.org/package=pkgstats/) [![CRAN Downloads](https://cranlogs.r-pkg.org/badges/grand-total/pkgstats?color=orange)](https://cran.r-project.org/package=pkgstats) # pkgstats 提取 R 包结构和功能的汇总统计数据。当然,并不是所有的统计数据,但它在提供有洞察力的统计数据和确保计算可行性之间取得了良好的平衡。`pkgstats` 是一个*静态*代码分析工具,因此通常非常快(对于非常大的包,最多也只需几秒钟)。安装说明在[单独的 vignette](https://docs.ropensci.org/pkgstats/articles/installation.html) 中进行了描述。 ## 提取哪些统计数据? 统计数据来源于以下几个主要方面: 1. 每个目录和语言中的代码行数、文档行数和空白行(包括行间和行内空白)数量。 2. 包的 `DESCRIPTION` 文件摘要及相关包的元统计数据。 3. 跨多种语言通过包代码创建的所有对象,以及包含源代码的所有目录(`./R`、`./src` 和 `./inst/include`)的对象摘要。 4. 从[代码标记库 `ctags`](https://ctags.io) 获取的函数定义,以及从[另一个标记库 `gtags`](https://www.gnu.org/software/global/) 获取的对这些函数的引用(“调用”)所派生出的函数调用网络。该网络大致将每个发起调用的对象(作为 `from`)与每个被调用的对象(`to`)连接起来。 5. 一个额外的函数调用网络,将 R 函数内部的调用连接到来自其他 R 包的所有函数。 [`pkgstats()` 主函数](https://docs.ropensci.org/pkgstats/reference/pkgstats.html) 返回包含这些不同组件的列表,包括上述最后三个组件的完整 `data.frame` 对象。此列表的统计属性可以通过 [`pkgstats_summary()` 函数](https://docs.ropensci.org/pkgstats/reference/pkgstats_summary.html) 进行聚合,该函数返回一个只有单行汇总统计数据的 `data.frame`。下面将演示此函数,包括对所有提取的统计数据的完整说明。 ## 演示 以下代码使用本包内部打包的 `.tar.gz` “tarball” 演示了主函数 `pkgstats` 的输出。`system.time` 调用表明 `pkgstats` 的静态代码分析通常非常快。 ``` library (pkgstats) tarball <- system.file ("extdata", "pkgstats_9.9.tar.gz", package = "pkgstats") system.time ( p <- pkgstats (tarball) ) ``` ``` ## 用户 系统 已用 ## 1.701 0.124 1.802 ``` ``` names (p) ``` ``` ## [1] "loc" "vignettes" "data_stats" "desc" ## [5] "translations" "objects" "network" "external_calls" ``` 结果是一个包含从代码中提取的各种数据的列表。除了 `objects` 和 `network` 之外,其他均代表汇总数据: ``` p [!names (p) %in% c ("objects", "network", "external_calls")] ``` ``` ## $loc ## # A tibble: 4 × 12 ## 语言 目录 文件数 代码行数 代码数 文档数 空行数 空格数 字符数 表达式数 制表符数 ## ## 1 C++ src 3 365 277 21 67 933 7002 1 0 ## 2 R R 19 3741 2698 536 507 27575 94022 1 0 ## 3 R tests 2 146 121 1 24 395 2423 1 0 ## 4 R tests/tes… 5 202 145 9 48 375 3738 1 0 ## # ℹ 1 个额外变量: indentation ## ## $vignettes ## vignettes 演示 ## 0 0 ## ## $data_stats ## 总数 总大小 中位数大小 ## 0 0 0 ## ## $desc ## 包 版本 日期 许可证 ## 1 pkgstats 9.9 2022-05-12 11:41:22 GPL-3 ## 链接 ## 1 https://docs.ropensci.org/pkgstats/,\nhttps://github.com/ropensci-review-tools/pkgstats ## 问题 作者 贡献者 基金 审阅者 致谢 ## 1 https://github.com/ropensci-review-tools/pkgstats/issues 1 0 0 0 0 ## 翻译 依赖 导入 ## 1 0 NA brio, checkmate, dplyr, fs, igraph, methods, readr, sys, withr ## 建议 ## 1 hms, knitr, pbapply, pkgbuild, Rcpp, rmarkdown, roxygen2, testthat, visNetwork ## 增强 链接到 ## 1 NA cpp11 ## ## $translations ## [1] NA ``` 这些结果的各个组件在[主包 vignette](https://docs.ropensci.org/pkgstats/articles/pkgstats.html) 中有更详细的描述。 ### 统计数据和 `pkgstats_summary()` 函数概述 通过将 `pkgstats()` 返回的对象提交给 [`pkgstats_summary()` 函数](https://docs.ropensci.org/pkgstats/reference/pkgstats_summary.html),可以获得 `pkgstats` 数据的摘要: ``` s <- pkgstats_summary (p) ``` 此函数将 [`pkgstats()` 函数](https://docs.ropensci.org/pkgstats/reference/pkgstats_summary.html) 的结果缩减为包含 95 个条目的单行数据,表示为一个具有一行和相应列数的 `data.frame`。此格式旨在允许通过简单地按行绑定来聚合多个包的汇总统计数据。虽然 95 个统计数据看起来很多,但 [`pkgstats_summary()` 函数](https://docs.ropensci.org/pkgstats/reference/pkgstats_summary.html) 旨在返回尽可能多的可用原始统计数据,以便灵活地允许通过组合和聚合来派生更高层级的统计数据。这 95 个统计数据可以大致分为以下几类(未按实际出现顺序显示),每个描述后面括号中为变量名。某些统计数据被汇总为逗号分隔的字符串,例如翻译成的人类语言,或在“depends”、“imports”或“suggests”下列出的其他包。这使得能够对其内容进行后续分析,例如分析实际翻译的语言,或者分析所有包依赖项的总数和具体细节,如下文立即演示的那样。 **包摘要** - 名称 (`package`) - 包版本 (`version`) - 包日期,如未明确说明则为 `DESCRIPTION` 文件的修改时间 (`date`) - 许可证 (`license`) - 语言,作为单个逗号分隔的字符值 (`languages`),不包括 `R` 本身。 - 如果包包含翻译文件,则列出其翻译,以(口语)语言代码列表的形式给出 (`translations`)。 **来自 `DESCRIPTION` 文件的信息** - 包 URL(s) (`url`) - BugReports 的 URL (`bugs`) - 角色为 *author* (`desc_n_aut`)、*contributor* (`desc_n_ctb`)、*funder* (`desc_n_fnd`)、*reviewer* (`desc_n_rev`)、*thesis advisor* (`ths`) 和 *translator* (`trl`,与计算机语言而非口语之间的翻译有关) 的贡献者数量。 - 所有 `depends`、`imports`、`suggests` 和 `linking_to` 包的逗号分隔字符条目。 可以通过简单的 `strsplit` 调用获取最后两类条目的数量,如下所示: ``` deps <- strsplit (s$suggests, ", ") [[1]] length (deps) print (deps) ``` ``` ## [1] 9 ``` ``` print (deps) ``` ``` ## [1] "hms" "knitr" "pbapply" "pkgbuild" "Rcpp" ## [6] "rmarkdown" "roxygen2" "testthat" "visNetwork" ``` **文件及相关数据数量** - vignette 数量 (`num_vignettes`) - 演示数量 (`num_demos`) - 数据文件数量 (`num_data_files`) - 所有包数据的总大小 (`data_size_total`) - 包数据文件的中位大小 (`data_size_median`) - 主要子目录中的文件数量 (`files_R`、`files_src`、`files_inst`、`files_vignettes`、`files_tests`),其数量递归计算所有子目录,且 `inst` 仅计算 `inst/include` 子目录中的文件。 **代码行数统计** - 每个子目录中的总代码行数 (`loc_R`、`loc_src`、`loc_ins`、`loc_vignettes`、`loc_tests`)。 - 每个子目录中的总空行数 (`blank_lines_R`、`blank_lines_src`、`blank_lines_inst`、`blank_lines_vignette`、`blank_lines_tests`)。 - 每个子目录中的总注释行数 (`comment_lines_R`、`comment_lines_src`、`comment_lines_inst`、`comment_lines_vignettes`、`comment_lines_tests`)。 - 每个子目录中相对空白的度量 (`rel_space_R`、`rel_space_src`、`rel_space_inst`、`rel_space_vignettes`、`rel_space_tests`),以及 `R/`、`src/` 和 `inst/` 目录的整体度量 (`rel_space`)。 - 用于缩进代码的空格数 (`indentation`),值为 -1 表示使用制表符缩进。 - 每行代码嵌套表达式的中位数,仅计算包含表达式的行 (`nexpr`)。 **单个对象(包括函数)的统计** 这些统计数据均提及“函数”,但实际上代表了更通用的“对象”,例如全局变量或类定义(通常来自 R 以外的语言),详见下文。 - R 中的函数数量 (`n_fns_r`) - 已导出和未导出的 R 函数数量 (`n_fns_r_exported`、`n_fns_r_not_exported`) - 其他计算机语言中的函数(或对象)数量 (`n_fns_src`),包括 `src` 和 `inst/include` 目录中的函数。 - R 和所有其他 (`src`) 目录中每个文件的函数(或对象)数量 (`n_fns_per_file_r`、`n_fns_per_file_src`)。 - 每个已导出 R 函数的参数数量的中位数和平均值 (`npars_exported_mn`、`npars_exported_md`)。 - R 和其他语言中每个函数的平均和中位代码行数,包括已导出和未导出 R 函数的区别 (`loc_per_fn_r_mn`、`loc_per_fn_r_md`、`loc_per_fn_r_exp_m`、`loc_per_fn_r_exp_md`、`loc_per_fn_r_not_exp_mn`、`loc_per_fn_r_not_exp_m`、`loc_per_fn_src_mn`、`loc_per_fn_src_md`)。 - 每个函数对应的平均和中位文档行数的等效值 (`doclines_per_fn_exp_mn`、`doclines_per_fn_exp_md`、`doclines_per_fn_not_exp_m`、`doclines_per_fn_not_exp_md`、`docchars_per_par_exp_mn`、`docchars_per_par_exp_m`)。 **网络统计** `network` 表的完整结构如下所述,汇总统计包括: - 边数,包括不同语言之间的区别 (`n_edges`、`n_edges_r`、`n_edges_src`)。 - 包网络中不同集群的数量 (`n_clusters`)。 - 所有网络边的平均和中位中心性,从网络的有向和无向表示中计算 (`centrality_dir_mn`、`centrality_dir_md`、`centrality_undir_mn`、`centrality_undir_md`)。 - 排除中心性为零的边后的等效中心性值 (`centrality_dir_mn_no0`、`centrality_dir_md_no0`、`centrality_undir_mn_no0`、`centrality_undir_md_no`)。 - 终端边的数量 (`num_terminal_edges_dir`、`num_terminal_edges_undir`)。 - 节点度的汇总统计数据 (`node_degree_mn`、`node_degree_md`、`node_degree_max`)。 **外部调用统计** [`pkgstats_summary()` 函数](https://docs.ropensci.org/pkgstats/reference/pkgstats_summary.html) 结果中的最后一列汇总了 `external_calls` 对象,该对象详细说明了对所有外部包(包括基础包和推荐包)的调用。此汇总同样表示为单个字符串。每个包列出了函数调用的总数和唯一函数调用的总数。每个包的数据由逗号分隔,而包内的数据由冒号分隔。 ``` s$external_calls ``` ``` ## [1] "base:447:78,brio:7:1,dplyr:7:4,fs:4:2,graphics:10:2,hms:1:1,igraph:3:3,pbapply:1:1,pkgstats:99:60,readr:8:5,stats:16:2,sys:13:1,tools:2:2,utils:10:7,visNetwork:3:2,withr:5:1" ``` 这种结构允许使用类似以下的代码轻松提取对所有包的调用数量: ``` calls <- do.call ( rbind, strsplit (strsplit (s$external_call, ",") [[1]], ":") ) calls <- data.frame ( package = calls [, 1], n_total = as.integer (calls [, 2]), n_unique = as.integer (calls [, 3]) ) print (calls) ``` ``` ## 包 总数 唯一数 ## 1 base 447 78 ## 2 brio 7 1 ## 3 dplyr 7 4 ## 4 fs 4 2 ## 5 graphics 10 2 ## 6 hms 1 1 ## 7 igraph 3 3 ## 8 pbapply 1 1 ## 9 pkgstats 99 60 ## 10 readr 8 5 ## 11 stats 16 2 ## 12 sys 13 1 ## 13 tools 2 2 ## 14 utils 10 7 ## 15 visNetwork 3 2 ## 16 withr 5 1 ``` 这两个数字列分别显示了对每个包的总调用次数,以及在这些包中使用的唯一函数的总数。这些结果提供了有关对其他 R 包(包括基础包和推荐包)进行的调用次数以及所使用函数的详细信息。 最后,汇总统计数据以 `afferent_pkg` 和 `efferent_pkg` 这两个进一步的统计数据结束。这些是包文件之间[传入与传出耦合](https://en.wikipedia.org/wiki/Software_package_metrics)的包内部度量。*传入*耦合 (`ca`) 是从包内其他地方定义的函数到包中每个文件的*传入*调用次数,而*传出*耦合 (`ce`) 是从包中每个文件到包内其他地方定义的函数的*传出*调用次数。这些可用于得出“内部包不稳定性”的度量,即传出耦合与总耦合的比率 (`ce / (ce + ca)`)。 主函数 `pkgstats()` 返回的许多其他“原始”统计数据并未在 `pkgstats_summary()` 中体现。[主包 vignette](https://docs.ropensci.org/pkgstats/articles/pkgstats.html) 提供了关于完整结果的更多详细信息。 以下小节提供了关于 `objects`、`network` 和 `external_call` 项的更多细节,这些项可用于提取此处描述之外的额外统计数据。 ## 行为准则 请注意,本包在发布时附带了一份[贡献者行为准则](https://ropensci.org/code-of-conduct/)。通过为此项目做贡献,您同意遵守其条款。
kellijohnson-NOAA
ScottClaessens
schneiderpy
标签:CRAN, CTags, GTags, ROpenSci, R语言, SEO检索词, SOC Prime, WebSocket, 二进制发布, 代码复杂度, 代码结构, 代码统计, 依赖分析, 函数调用网络, 包分析, 安全专业人员, 开发工具, 开源工具, 软件度量, 错误基检测, 静态代码分析