2020-013 R包不装也能用

2022-01-15  本文已影响0人  SSSimonYang

R包不装也能用

因为功能比较高级的R包都会依赖很多包,依赖包又会依赖其他包,所以装包是个麻烦的事。

之前那个包,不管从GitHub装还是本地装,装的时候老是出现各种乱七八糟的错误。

但是我又不是需要你R包里的全部函数,有些我不用的函数还要因为它装很多其他的包或者导致安装失败就很气。

反正R包里有源码,不如直接运行源码吧!

下载zip,然后解压。

函数都编写在revolver-masterR文件夹内的各个文件中。

新建一个环境,来存储函数。

base_dir = "../revolver-master/R"
files = list.files(base_dir)
revolver = new.env() 
for (file in files) {
  source(paste(base_dir,file,sep='/'),local=revolver)   #在revolver环境中运行这些代码
}

之后就可以通过revolver$引用这些函数了,和安装包后的revolver::不同。

image-20200812194321445

安装好的包可以library导入,暴露出函数。那这个呢?

可以用attachdetach

attach(revolver)
CCF()
detach(revolver)

虽然这些函数已经存储了,但是由于我们不是正常安装的包,所以函数在使用到其他未安装包的时候就会有运行错误。

那我就想检查一下我source的每个文件都导入或使用了哪些依赖包。

用一个文件做测试。首先读取文件,然后写正则提取包名。

file = 'revolver_cohort.R'
text = readLines(paste(base_dir,file,sep='/'))
result1 = stringr::str_match_all(text,'^library\\((\\w+?)\\)')
result2 = stringr::str_match_all(text,'[^\\w](\\w+?):::?')

stringr包中的str_match_all可以提取单个字符的所有结果,对每个字符返回一个矩阵,其中()中的分组信息存储在矩阵第二列及之后,多个结果为多行。注意,此处的text为字符向量,每个元素为文件中的一行。

下面为正则讲解,可能较为晦涩。

result1提取的是类似library(some_package)中的包名。其中^表示从头匹配,因为library通常出现在一行的开头。\\(\\)对函数两边的括号进行匹配,此处的第一个\对第二个\转义,第二个\对有特殊含义的(进行转义。内部的()表示所关注的分组。()内部为\\w*?,其中\w匹配字母或数字,第一个\用于转义,+表示匹配1个或多个,?为非贪婪模式。

result2提取的是类似some_package::some_function中的包名。其中[^\\w]表示包名前的单个字符不能是字母或数字,[]为一组字符,内部的^表示取反。之后的(\\w+?)为所关注的分组,和上面类似,匹配多个字母和数字。最后的:::?中,?的出现是为了匹配0个或1个:,这种写法是为了兼容可能出现的some_package:::some_function,三个冒号在R中被用于调用包内部的函数。

str_match_all返回列表,列表元素对应对text中每个字符元素处理的返回值,为矩阵。

result = c(result1,result2)
result = unlist(lapply(result,function(x){if(dim(x)[1]==0)return(NULL)else{return(x[,2])}}))

随后连接result1result2。使用lapply,对每个列表元素进行操作,当dim(x)[1]==0即矩阵没有值时返回NULLNULL之后在unlist结果中被忽略,如果有元素就返回矩阵的第2列。使用unlist进行展平操作。

在实践中,我们发现有这种情况。

    if (!is.na(D))
      cat(sprintf(' :: %s', D))

在第二行中,result2能够匹配到空字符''。因此,result中要删除这种意外结果并去重。

packages = setdiff(unique(result),'')

试一下吧。

out:

"revolver"   "pio"        "clisymbols" "reshape2"

结果不错。

下一步就是写个循环,生成一个列表,将文件名作为元素的名称。

base_dir = "../revolver-master/R"
files = list.files(base_dir)
all_packages = NULL
for (file in files) {
  text = readLines(paste(base_dir,file,sep='/'))
  result1 = stringr::str_match_all(text,'^library\\((\\w+?)\\)')
  result2 = stringr::str_match_all(text,'[^\\w](\\w+?):::?')
  result = c(result1,result2)
  result = unlist(lapply(result,function(x){if(dim(x)[1]==0)return(NULL)else{return(x[,2])}}))
  packages = setdiff(unique(result),'')
  if(length(packages)==0)next             
  packages = list(packages)             
  names(packages) = file                       # 直接用list(file = packages)的话,name就是file本身了。
  all_packages = append(all_packages,packages) # 使用append连接list
}
all_packages

out:

$color_palettes.R
[1] "RColorBrewer"

$compute_clone_trees.R
[1] "easypar" "ctree"   "pio"    

$compute_mutation_trees.R
[1] "easypar" "mtree"   "pio"    

$DET_index.R
[1] "vegan"

$input_custom_trees.R
[1] "easypar" "ctree"   "pio"    

$plot_CCF_histogram.R
[1] "reshape2"
······

其实也可以将得到的这些packages批量安装一下,看哪些可以顺利安装,哪些不能。

all_packages = unique(unlist(all_packages))
results = sapply(all_packages, function(x){
  if(x %in% row.names(installed.packages()))
    require(x,character.only = T)         #使用character.only = T获取x内含字符,而不是require(x)
  else{
    try({install.packages(x);require(x,character.only = T)})
    }
  })

对包名进行遍历,已安装的包require一下,看能否正常导入。未安装的包先安装后导入,使用try函数,这样当安装包错误时循环不会终止。

最后的结果为,正常的情况下返回TRUE,错误的情况返回FALSE。因此可以提取出所有错误安装的包。

names(results)[!results]

Bingo!

上一篇下一篇

猜你喜欢

热点阅读