我有一个data.frame对象。举个简单的例子:
> data.frame(x=c('A','A','B','B','B'), y=c('Ab','Ac','Ba', 'Ba','Bd'), z=c('Abb','Acc','Bad', 'Bae','Bdd'))
x y z
1 A Ab Abb
2 A Ac Acc
3 B Ba Bad
4 B Ba Bae
5 B Bd Bdd
在实际数据中有更多的行和列。我如何创建一个嵌套的树形结构对象呢?
|---Ab---Abb
A---|
| |---Ac---Acc
--| /--Bad
| |---Ba-------|
B---| --Bae
|---Bb---Bdd
data.frame to Newick
我获得了计算系统发育的博士学位,在这个过程中我产生了这个代码,当我得到一些非标准格式的数据(在系统发育的意义上)时,我使用了一两次。脚本遍历数据框架,就好像它是一个树…并将内容粘贴到Newick字符串中,这是一种标准格式,然后可以转换为任何类型的树对象。
我猜这个脚本可以被优化(我很少使用它,所以在它上面做更多的工作会降低整体效率),但至少分享比让它在我的硬盘上收集灰尘要好。
## recursion function
traverse <- function(a,i,innerl){
if(i < (ncol(df))){
alevelinner <- as.character(unique(df[which(as.character(df[,i])==a),i+1]))
desc <- NULL
if(length(alevelinner) == 1) (newickout <- traverse(alevelinner,i+1,innerl))
else {
for(b in alevelinner) desc <- c(desc,traverse(b,i+1,innerl))
il <- NULL; if(innerl==TRUE) il <- a
(newickout <- paste("(",paste(desc,collapse=","),")",il,sep=""))
}
}
else { (newickout <- a) }
}
## data.frame to newick function
df2newick <- function(df, innerlabel=FALSE){
alevel <- as.character(unique(df[,1]))
newick <- NULL
for(x in alevel) newick <- c(newick,traverse(x,1,innerlabel))
(newick <- paste("(",paste(newick,collapse=","),");",sep=""))
}
主函数 df2newick()
接受两个参数:
-
df
要转换的数据帧(data.frame类的对象) -
innerlabel
,告诉函数写内部节点的标签(蓝色)
在你的例子中演示一下:
df <- data.frame(x=c('A','A','B','B','B'), y=c('Ab','Ac','Ba', 'Ba','Bd'), z=c('Abb','Acc','Bad', 'Bae','Bdd'))
myNewick <- df2newick(df)
#[1] "((Abb,Acc),((Bad,Bae),Bdd));"
现在你可以用read.tree()
从ape读取它到phylo
类对象
library(ape)
mytree <- read.tree(text=myNewick)
plot(mytree)
如果你想在Newick字符串中添加内部节点标签,你可以这样做:
myNewick <- df2newick(df, TRUE)
#[1] "((Abb,Acc)A,((Bad,Bae)Ba,Bdd)B);"
希望这是有用的(也许我的博士不是一个完整的时间;-)
数据帧格式的附加说明:
正如你可以观察到的,df2newick函数忽略了带有一个子的内部模式(无论如何,这是最好的与大多数系统发育方法一起使用…只和我有关)。我最初获得并用于此脚本的df
对象的格式如下:
df <- data.frame(x=c('A','A','B','B','B'), y=c('Abb','Acc','Ba', 'Ba','Bdd'), z=c('Abb','Acc','Bad', 'Bae','Bdd'))
与你的非常相似……但是"内部单子节点"与其子节点具有相同的名称,但是这些节点的内部名称也不同,并且这些名称将被忽略……可能不相关,但你可以忽略递归函数的一部分,像这样:
traverse <- function(a,i,innerl){
if(i < (ncol(df))){
alevelinner <- as.character(unique(df[which(as.character(df[,i])==a),i+1]))
desc <- NULL
##if(length(alevelinner) == 1) (newickout <- traverse(alevelinner,i+1,innerl))
##else {
for(b in alevelinner) desc <- c(desc,traverse(b,i+1,innerl))
il <- NULL; if(innerl==TRUE) il <- a
(newickout <- paste("(",paste(desc,collapse=","),")",il,sep=""))
##}
}
else { (newickout <- a) }
}
,你会得到这样的东西:
[1] "(((Abb)Ab,(Acc)Ac)A,((Bad,Bae)Ba,(Bdd)Bd)B);"
这对我来说很奇怪,但我还是添加了它,因为它现在包含了原始数据框中的所有信息
我不太了解R中树形图的内部结构,但是下面的代码将创建一个嵌套的列表结构,它具有我认为您要寻找的层次结构:
stree = function(x,level=0) {
#x is a string vector
#resultis a hierarchical structure of lists (that contains lists, etc.)
#the names of the lists are the node values.
level = level+1
if (length(x)==1) {
result = list()
result[[substring(x[1],level)]]=list()
return(result)
}
result=list()
this.level = substring(x,level,level)
next.levels = unique(this.level)
for (p in next.levels) {
if (p=="") {
result$p = list()
} else {
ids = which(this.level==p)
result[[p]] = stree(x[ids],level)
}
}
result
}
操作的对象是string类型的vector对象。所以在数据框架的情况下,需要调用stree (as.character (df [3]))