自杀式R包 只能安装成功一次,再次重复安装就会报错。
R包的依赖处理非常奇怪,随着安装R包的数量变多,有较大概率会遇到R包依赖崩溃的情况。
如果是在Windows平台下,那么应该首先检查R包安装路径是否有问题(使用.libPaths()函数查看),尽量不要安装到".RLibrary"之类的无版本依赖的文件夹中。
而如果是linux平台下或者包安装路径并没有“.RLibrary”之类的文件夹, 那么注意R包的安装或者载入过程中是否出现如下warnning或error:
代码语言:javascript复制Error in completeSublasses(classDef2, class1, obj, where) :
trying to get slot "subclasses" from an object of a basic class ("NULL") with no slots
核心的报错提示就是`trying to get slot "subclasses" from an object of a basic class ("NULL"),而且这个报错总是在直接载入或者间接引入dbplyr包的时候报错。
为了叙述方便,把这个报错叫做“NULL subclasses error”。
如果你以后遇到了这个问题,可以尝试本文的解决思路。
如果大家搜索过这个错误会发现遇到这个问题的人还挺多的,而且看起来他们的解决方案相对是没有切合问题本质或者是比较偶发的解决方案:如重新安装一个R包,修改R包的载入顺序等等。 诸如下面的各种github链接,都在叙述这个错误:
- package or namespace load failed for ‘dbplyr’ · Issue #1176 · tidyverse/dbplyr · GitHub https://github.com/tidyverse/dbplyr/issues/1176
- slot "subclasses" from an object of a basic class ("NULL") with no slots" on Mac occasionally just happens · Issue #1077 · tidyverse/dbplyr · GitHub https://github.com/tidyverse/dbplyr/issues/1077
dbplyr
itself cannot be loaded · Issue #779 · tidyverse/dbplyr · GitHub https://github.com/tidyverse/dbplyr/issues/779
如何载入一个NULL subclasses error的包
这个问题是比较严重的,它导致这个包根本无法使用了,无法载入的报错提示如下:
代码语言:javascript复制> library("dbplyr")
Error in completeSubclasses(classDef2, class1, obj, where) :
trying to get slot "subclasses" from an object of a basic class ("NULL") with no slots
Error: package or namespace load failed for ‘dbplyr’:
.onLoad failed in loadNamespace() for 'dbplyr', details:
call: setClass(cl, contains = c(prevClass, "VIRTUAL"), where = where)
error: error in contained classes ("character") for class “ident”; class definition removed from ‘dbplyr’
其实这个问题比较好解决,大家如果测试过的话,可能会发现这个报错只会在一个特定场合出现:一个已经打开过的R或Rstudio项目,且上次退出时保存过R数据,而且大概率是你这些R数据直接/间接依赖了一个dbplyr包,如果全新打开一个R界面则这个错误并不会出现。
所以我们可以知道这个报错的原因是dbplyr作为一个被依赖包,却滞后于依赖包的顺序被加载,因此这个错误很好修复,保证优先加载dbpyr:
在Rprofile.site文件中加入library(dbplyr)即可。 Tips: 由于一些编译型R包可能也在安装时载入R,为避免可能的编译错误,建议将这所code的输出隐藏,比如我的Rprofile.site文件中是添加的如下代码: suppressWarnings(library(dbplyr))
Rprofile.site文件是R初始化的时候会执行的代码,它的高优先级保证了我们打开R进行数据分析前(此时R已经加载完毕,包括Rprofile.site里面的代码已经执行),已经把dbplyr先行载入了,自然就不会有依赖的问题了。
这个操作也会额外修复一种情况:
就是你的R界面中会疯狂的重复打印一行红色提示:
代码语言:javascript复制trying to get slot "subclasses" from an object of a basic class ("NULL") with no slots
如何安装一个NULL subclasses error的包
下图是一个示例,这个包第一次安装是成功的,再次重新安装就会报错:
可以看到这个报错的直接原因还是会定位到dbpyr包,但是它是出现在安装其他依赖这个包的包时出现的。
而且此时在Rprofile.site文件里面已经添加了载入dbplyr包的代码,包括手动载入dbplyr包也会正常载入,但是在这种情况下这里依然报错dbplyr包的NULL subclasses error。
解决方案就是想办法让dbplyr显式的置于这个包的安装依赖中,且使其依赖优先级最高。
调整R包的依赖项优先级和R包的DESCRIPTION文件有关系:
R包结构
R包源码一般是一个压缩包的形式,后缀名tar.gz。比如dbplyr包的CRAN主页CRAN - Package dbplyr,Package source那里是下载链接。
R的源码文件tar.gz压缩包解压后的结构大概如下:
代码语言:javascript复制| - DESCRIPTION
| - LICENSE
| - NAMESPACE
| - R
| | - script1.R
| | - script2.R
| - man
| | - function1.Rd
| | - function2.Rd
| | - function3.Rd
| - myutils.Rproj
而R包的依赖项管理是在DESCRIPTION中记录着,比如下面的dbplyr的DESCRIPTION里面的内容:
代码语言:javascript复制Type: Package
Package: dbplyr
Title: A 'dplyr' Back End for Databases
Version: 2.4.0
Authors@R: c(
person("Hadley", "Wickham", , "hadley@rstudio.com", role = c("aut", "cre")),
person("Maximilian", "Girlich", role = "aut"),
person("Edgar", "Ruiz", role = "aut"),
person("RStudio", role = c("cph", "fnd"))
)
Description: A 'dplyr' back end for databases that allows you to work with
remote database tables as if they are in-memory data frames. Basic
features works with any database that has a 'DBI' back end; more
advanced features require 'SQL' translation to be provided by the
package author.
License: MIT file LICENSE
URL: https://dbplyr.tidyverse.org/, https://github.com/tidyverse/dbplyr
BugReports: https://github.com/tidyverse/dbplyr/issues
Depends: R (>= 3.6)
Imports: blob (>= 1.2.0), cli (>= 3.6.1), DBI (>= 1.1.3), dplyr (>=
1.1.2), glue (>= 1.6.2), lifecycle (>= 1.0.3), magrittr,
methods, pillar (>= 1.9.0), purrr (>= 1.0.1), R6 (>= 2.2.2),
rlang (>= 1.1.1), tibble (>= 3.2.1), tidyr (>= 1.3.0),
tidyselect (>= 1.2.0), utils, vctrs (>= 0.6.3), withr (>=
2.5.0)
Suggests: bit64, covr, knitr, Lahman, nycflights13, odbc, RMariaDB (>=
1.2.2), rmarkdown, RPostgres (>= 1.4.5), RPostgreSQL, RSQLite
(>= 2.3.1), testthat (>= 3.1.10)
VignetteBuilder: knitr
Config/Needs/website: tidyverse/tidytemplate
Config/testthat/edition: 3
Encoding: UTF-8
Language: en-gb
RoxygenNote: 7.2.3
Collate: 'db-sql.R' 'utils-check.R' ...其他省略...
NeedsCompilation: no
Packaged: 2023-10-25 20:30:22 UTC; hadleywickham
Author: Hadley Wickham [aut, cre],
Maximilian Girlich [aut],
Edgar Ruiz [aut],
RStudio [cph, fnd]
Maintainer: Hadley Wickham <hadley@rstudio.com>
Repository: CRAN
Date/Publication: 2023-10-26 07:40:02 UTC
DESCRIPTION文件中有依赖项的描述:Depends、Imports、Suggests。
- Depends是代表这个依赖项会载入到全局环境中。
- Imports是代表这个依赖项只会在当前包的环境中载入。
- Suggests一般是用于帮助文档渲染时使用的依赖项。
Suggests依赖项的包可以缺失,而Depends和Imports中的依赖项是需要先于当前包安装的。当前包载入后,所有Depends中的R包也会被载入。
有关于Imports和Depends的介绍,可以见这里https://journal.r-project.org/articles/RN-2004-009/RN-2004-009.pdf,部分摘录如下:
代码语言:javascript复制We now have a much cleaner mechanism. All
packages mentioned in the ‘Depends’ field of the
‘DESCRIPTION’ file of a package are loaded in the
order they are mentioned, both before the package
is prepared for lazy-loading/save image and before
it is loaded by library
可以发现Depends的优先级很高,且会在各种lazy-loading的步骤之前。
所以只需要把自杀包的源码下载后解压,然后将dbplyr添加到Depends那里,再压缩成tar.gz格式即可(不压缩也行),然后手动安装此包即可。
手动安装R包
手动安装源码包可以通过两个方式,一个是R里面使用install.packages函数,另一 个是使用命令行工具R CMD INSTLAL。
(1) install.packages
在R中,使用install.packages(pkgs = "tar.gz文件路径", repos = NULL)即可。
pkgs参数传递的就是是刚才重新压缩的R源码压缩文件,注意repos = NULL不可省略。
(2) R CMD INSTALL
在命令行或者shell下,也可以使用R CMD INSTALL安装。R CMD INSTALL后面接修改好的tar.gz文件路径或者修改好的解压后的源包文件夹路径。