R包不裝也能用
因為功能比較高級的R包都會依賴很多包垄潮,依賴包又會依賴其他包,所以裝包是個麻煩的事。
之前那個包怕磨,不管從GitHub裝還是本地裝,裝的時候老是出現(xiàn)各種亂七八糟的錯誤消约。
但是我又不是需要你R包里的全部函數(shù)肠鲫,有些我不用的函數(shù)還要因為它裝很多其他的包或者導(dǎo)致安裝失敗就很氣。
反正R包里有源碼或粮,不如直接運行源碼吧导饲!
下載zip,然后解壓。
函數(shù)都編寫在revolver-master
的R
文件夾內(nèi)的各個文件中渣锦。
新建一個環(huán)境硝岗,來存儲函數(shù)。
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環(huán)境中運行這些代碼
}
之后就可以通過revolver$
引用這些函數(shù)了袋毙,和安裝包后的revolver::
不同型檀。
安裝好的包可以library
導(dǎo)入,暴露出函數(shù)娄猫。那這個呢贱除?
可以用attach
、detach
媳溺。
attach(revolver)
CCF()
detach(revolver)
雖然這些函數(shù)已經(jīng)存儲了月幌,但是由于我們不是正常安裝的包,所以函數(shù)在使用到其他未安裝包的時候就會有運行錯誤悬蔽。
那我就想檢查一下我source
的每個文件都導(dǎo)入或使用了哪些依賴包扯躺。
用一個文件做測試。首先讀取文件蝎困,然后寫正則提取包名录语。
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
可以提取單個字符的所有結(jié)果,對每個字符返回一個矩陣禾乘,其中()
中的分組信息存儲在矩陣第二列及之后澎埠,多個結(jié)果為多行。注意始藕,此處的text
為字符向量蒲稳,每個元素為文件中的一行。
下面為正則講解伍派,可能較為晦澀江耀。
result1
提取的是類似library(some_package)
中的包名。其中^
表示從頭匹配诉植,因為library
通常出現(xiàn)在一行的開頭祥国。\\(
和\\)
對函數(shù)兩邊的括號進(jìn)行匹配,此處的第一個\
對第二個\
轉(zhuǎn)義晾腔,第二個\
對有特殊含義的(
進(jìn)行轉(zhuǎn)義尝蠕。內(nèi)部的()
表示所關(guān)注的分組吞鸭。()
內(nèi)部為\\w*蝶缀?
,其中\w
匹配字母或數(shù)字裳凸,第一個\
用于轉(zhuǎn)義,+
表示匹配1個或多個缤至,?
為非貪婪模式潮罪。
result2
提取的是類似some_package::some_function
中的包名康谆。其中[^\\w]
表示包名前的單個字符不能是字母或數(shù)字,[]
為一組字符,內(nèi)部的^
表示取反嫉到。之后的(\\w+?)
為所關(guān)注的分組沃暗,和上面類似,匹配多個字母和數(shù)字何恶。最后的:::?
中孽锥,?
的出現(xiàn)是為了匹配0個或1個:
,這種寫法是為了兼容可能出現(xiàn)的some_package:::some_function
细层,三個冒號在R中被用于調(diào)用包內(nèi)部的函數(shù)惜辑。
str_match_all
返回列表,列表元素對應(yīng)對text中每個字符元素處理的返回值疫赎,為矩陣盛撑。
result = c(result1,result2)
result = unlist(lapply(result,function(x){if(dim(x)[1]==0)return(NULL)else{return(x[,2])}}))
隨后連接result1
和result2
。使用lapply
捧搞,對每個列表元素進(jìn)行操作胎撇,當(dāng)dim(x)[1]==0
即矩陣沒有值時返回NULL
介粘,NULL
之后在unlist
結(jié)果中被忽略晚树,如果有元素就返回矩陣的第2列姻采。使用unlist
進(jìn)行展平操作。
在實踐中爵憎,我們發(fā)現(xiàn)有這種情況席函。
if (!is.na(D))
cat(sprintf(' :: %s', D))
在第二行中督弓,result2
能夠匹配到空字符''
营曼。因此蒂阱,result
中要刪除這種意外結(jié)果并去重。
packages = setdiff(unique(result),'')
試一下吧。
out:
"revolver" "pio" "clisymbols" "reshape2"
結(jié)果不錯录煤。
下一步就是寫個循環(huán)鳄厌,生成一個列表,將文件名作為元素的名稱妈踊。
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內(nèi)含字符露筒,而不是require(x)
else{
try({install.packages(x);require(x,character.only = T)})
}
})
對包名進(jìn)行遍歷呐伞,已安裝的包require
一下,看能否正常導(dǎo)入邀窃。未安裝的包先安裝后導(dǎo)入荸哟,使用try
函數(shù),這樣當(dāng)安裝包錯誤時循環(huán)不會終止瞬捕。
最后的結(jié)果為鞍历,正常的情況下返回TRUE
,錯誤的情況返回FALSE
肪虎。因此可以提取出所有錯誤安裝的包劣砍。
names(results)[!results]
Bingo!