[答疑]行数不同,不能cbind了肿么办
0.问题
这个问题来自于gdc下载的数据整理,整理过程中发现同一列表里的数据框行数不相同,绝大多数行数是68,少数几个是69~73,因此无法用cbind合并到一起。
1.思路
(1)cbind换成一个可以连接行数不同的数据框的函数
(2)把行数69~73的数据框里面,多出来的几行去掉,拿共同的行去连接
2.重现问题
下载的是来自TCGA-BLCA的临床数据。
Rlibrary(XML)
xmls = dir("clinical/",pattern = "*.xml$",recursive = T)
td = function(x){
result <- xmlParse(file.path("clinical/",x))
rootnode <- xmlRoot(result)
xmldataframe <- xmlToDataFrame(rootnode[2])
return(t(xmldataframe))
}
cl = lapply(xmls,td)
# 看看cl的每个元素都有多少列,发现68~73不等,正常来说列数应该是完全相同的。
a = sapply(cl, length)
unique(a)
#> [1] 68 69 72 71 70 73
# 因此合并时就会报错
cl_df <- t(do.call(cbind,cl))
#> Error in (function (..., deparse.level = 1) : 矩阵的行数必需相符(见arg5)
好咯,报错了。第一步,搜一搜
思路1:cbind.fill或者merge
https://stackoverflow.com/questions/3699405/how-to-cbind-or-rbind-different-lengths-vectors-without-repeating-the-elements-o
非常好找的一个链接,里面有个函数:rowr::cbind.fill
接下来就是安装这个R包,装了几下没有成功,搜索发现包被cran移除了,但是可以找到历史版本:
https://cran.r-project.org/src/contrib/Archive/rowr/rowr_1.1.3.tar.gz
下载放在工作目录下,本地安装install.packages("rowr_1.1.3.tar.gz",repos = NULL)
cl_df <- t(do.call(rowr::cbind.fill,cl))
colnames(cl_df) = rownames(cl[[which.max(a)]])
dim(cl_df)
#> [1] 409 73
因为总共73列,所以估计列名是按照最长的数据框行名来算。
连接行数不同的表格的函数,怎么能忘了merge。
cl2 = lapply(cl, function(x){
tibble::rownames_to_column(as.data.frame(x),var = "a")}
)
cl_df2 <- do.call("merge",cl2,list(by = "a"))
#> Error in if (quote) args <- lapply(args, enquote): 参数不能作为逻辑值来用
# 发现do.call搞不定的merge,可以交给Reduce
#https://stackoverflow.com/questions/22644780/merging-multiple-csv-files-in-r-using-do-call
m = Reduce(function(x, y) merge(x, y, by="a",all = T), cl2)
dat = as.data.frame(t(tibble::column_to_rownames(m,"a")))
rownames(dat) = NULL
dat[1:4,1:4]
#> additional_studies age_at_initial_pathologic_diagnosis
#> 1 74
#> 2 62
#> 3 56
#> 4 83
#> age_began_smoking_in_years anatomic_neoplasm_subdivision
#> 1 18 Wall Lateral
#> 2 Bladder - NOS
#> 3 27 Wall Anterior
#> 4 14 Bladder - NOS
head(dat$stage_event,10)
#> [1] "7thStage IVT1T2N2MX" "6thStage IIIT3N0M0" "7thStage IVT4aN2M0"
#> [4] "6thStage IVT3bN2MX6" "NO" "7thStage IVT3aN2MX7"
#> [7] "6thStage IIIT3bN0MX" "7thStage IIT2T2bNXM0" "22"
#> [10] "7thStage IIIT3N0M0"
table(dat$vital_status)
#>
#> Alive Dead
#> 301 108
检查发现,这样的做法导致了一些列的错位,一开始我以为是cbind.fill的锅,但merge也是一样,我又回去检查了xml文件,发现是xml文件读进来就是错的了,不是循环代码的锅。那就只能哪一个病人的信息错位了单独手动去处理了。
思路2 去掉多余的行
检查数据发现,多出来的几行,行名都是NA
setdiff(rownames(cl[[5]]),rownames(cl[[1]]))
#> [1] NA
setdiff(rownames(cl[[241]]),rownames(cl[[1]]))
#> [1] NA
把行名是NA的行去掉,就恢复到了相同的行数
cl3 = lapply(cl, function(x){x[!is.na(rownames(x)),]})
unique(sapply(cl3, length))
#> [1] 68
去掉na的情况仅仅适用于当前场景,有一个更加普适的方式,就是直接对所有的行名取交集,然后再各自取子集,合并
library(tinyarray)
n = intersect_all(lapply(cl, rownames))
cl3 = lapply(cl, function(x){x[n,]})
所以这个解决办法是最简单的咯。
cl_df <- t(do.call(cbind,cl3))
cl_df[1:3,1:3]
#> additional_studies tissue_source_site patient_id
#> [1,] "" "GU" "AATP"
#> [2,] "" "CF" "A1HR"
#> [3,] "" "GV" "A3QK"
clinical = data.frame(cl_df)
clinical[1:4,1:4]
#> additional_studies tissue_source_site patient_id bcr_patient_barcode
#> 1 GU AATP TCGA-GU-AATP
#> 2 CF A1HR TCGA-CF-A1HR
#> 3 GV A3QK TCGA-GV-A3QK
#> 4 XF A9SJ TCGA-XF-A9SJ
插个小广告! 生信零基础入门学习小组 生信入门班(四周线上直播课,长期开班) 数据挖掘班(医生/医学生首选,三周线上直播课,长期开班) 一起来学单细胞吗? 生信星球答疑公告