Plot an 'R' package's recursive dependency graph and tabulate the
number of unique downstream dependencies added by top-level dependencies.
This helps 'R' package developers identify which of their declared
dependencies add the most downstream dependencies in order to prioritize
them for removal if needed. Uses graph stress minimization adapted from
Schoch (2023)
The goal of havel is to vizualize and tabulate dependencies in R packages. This will help R package developers identify dependencies that contribute more to the dependency load and/or are easier to remove.
You can install the development version of havel from GitHub with:
# install.packages("pak")
pak::pak("andrewGhazi/havel")
Once available on CRAN, install like so:
install.packages("havel")
havel has two main functions: plot_deps_graph() and
uniq_pkg_deps().
plot_deps_graph()Use plot_deps_graph() to look up and plot the dependency graph of a
package:
library(havel)
plot_deps_graph("ggplot2")
You can see that:
ggplot2 has 21 dependencies in total (direct and indirect).scales package contributes several dependencies
that nothing else depends on. If the ggplot2 developers were looking
to lighten their package (not that they should), eliminating the
dependency on scales might be a good place to start looking.Another example: What if I wanted to remove Rcpp as a dependency of
havel itself?
plot_deps_graph("andrewGhazi/havel") # looks it up on GitHub
Well, looks like that would be difficult. Even if I removed the direct
dependency, there would still be an indirect dependency on Rcpp
through collapse and cppRouting. I would need to remove all three to
avoid dependence on Rcpp.
uniq_pkg_deps()uniq_pkg_deps() tabulates what’s visually apparent on the plot:
uniq_pkg_deps("ggplot2")
#> p1 n_uniq uniq_pkgs
#> <char> <num> <list>
#> 1: scales 6 viridisLite,labeling,R6,farver,RColorBrewer
#> 2: cli 1
#> 3: grDevices 1
#> 4: grid 1
#> 5: gtable 1
#> 6: isoband 1
#> 7: lifecycle 1
#> 8: rlang 1
#> 9: S7 1
#> 10: stats 1
#> 11: vctrs 1
#> 12: withr 1
scales introduces six unique dependencies to ggplot2, including
itself.
Sometimes there’s no single package that introduces many unique
dependencies, but there may be a pair or triplet that does. You can
check the impact of removing each combination of order = 2 packages
like so:
uniq_pkg_deps("ggplot2", order = 2) |>
head()
#> p1 p2 n_uniq uniq_pkgs
#> <char> <char> <num> <list>
#> 1: scales withr 8 viridisLite,labeling,graphics,R6,farver,RColorBrewer
#> 2: cli scales 7 viridisLite,labeling,R6,farver,RColorBrewer
#> 3: grDevices scales 7 viridisLite,labeling,R6,farver,RColorBrewer
#> 4: grid scales 7 viridisLite,labeling,R6,farver,RColorBrewer
#> 5: gtable scales 7 viridisLite,labeling,R6,farver,RColorBrewer
#> 6: isoband scales 7 viridisLite,labeling,R6,farver,RColorBrewer
scales + withr introduce eight.
There are other packages / posts out there on this type of thing:
miniCRAN has a
vignette on similar plotting functionality: “Using miniCRAN to
identify package dependencies”.pkgnet offers an
expansive dependency analysis suite.depgraph (on GitHub only, not
CRAN) is pretty similar but uses different aesthetic mappings.DependenciesGraphs
and pkgdepR visualize
the dependencies of functions used within a package.pkgndep visualizes
similar information in a heatmap format, as well as provides different
“heaviness” metrics.havel offers some unique advantages nonetheless:
pak::pkg_deps() to look up the dependency connections
instead of tools::package_dependencies(). This enables more
versatile lookups of packages on Bioc, GitHub, etc.havel itself is fairly light and fast, depending only on pak, some
fastverse packages, and base R graphics…
gg = TRUE.Why’d you make this?
What’s with the name?
Havel’s Ring is an item in Dark Souls that helps you deal with excessive burdens. (Plus, every permutation of “pkg”/“dep”/“graph” was taken.)
methods → utils,stats)info_method = "tools"