采用链接ID的简单数据框:
test <- data.frame(id1=c(10,10,1,1,24,8),id2=c(1,36,24,45,300,11))
> test
id1 id2
1 10 1
2 10 36
3 1 24
4 1 45
5 24 300
6 8 11
我现在想把所有链接的ID组合在一起。 通过“链接”,我的意思是遵循链接链,以便所有ID都在一个组中 被标记在一起。一种分支结构。即:
Group 1
10 --> 1, 1 --> (24,45)
24 --> 300
300 --> NULL
45 --> NULL
10 --> 36, 36 --> NULL,
Final group members: 10,1,24,36,45,300
Group 2
8 --> 11
11 --> NULL
Final group members: 8,11
现在我大致知道我想要的逻辑,但不知道我将如何优雅地实现它。我正在考虑递归使用match
或%in%
来关闭每个分支,但这次真的很难过。
我将追逐的最终结果是:
result <- data.frame(group=c(1,1,1,1,1,1,2,2),id=c(10,1,24,36,45,300,8,11))
> result
group id
1 1 10
2 1 1
3 1 24
4 1 36
5 1 45
6 1 300
7 2 8
8 2 11
答案 0 :(得分:6)
Bioconductor包 RBGL (BOOST图库的R接口)包含
一个函数connectedComp()
,用于标识图中连接的组件 -
正是你想要的。
(要使用此功能,您首先需要安装图形和 RBGL 包,可用here和here。)< / p>
library(RBGL)
test <- data.frame(id1=c(10,10,1,1,24,8),id2=c(1,36,24,45,300,11))
## Convert your 'from-to' data to a 'node and edge-list' representation
## used by the 'graph' & 'RBGL' packages
g <- ftM2graphNEL(as.matrix(test))
## Extract the connected components
cc <- connectedComp(g)
## Massage results into the format you're after
ld <- lapply(seq_along(cc),
function(i) data.frame(group = names(cc)[i], id = cc[[i]]))
do.call(rbind, ld)
# group id
# 1 1 10
# 2 1 1
# 3 1 24
# 4 1 36
# 5 1 45
# 6 1 300
# 7 2 8
# 8 2 11
答案 1 :(得分:3)
这是我在Josh正确方向推动后发现自己的替代答案。此答案使用igraph
包。
对于那些正在搜索并遇到此答案的人,我的test
数据集在图论中被称为“边缘列表”或“邻接列表”(http://en.wikipedia.org/wiki/Graph_theory)
library(igraph)
test <- data.frame(id1=c(10,10,1,1,24,8 ),id2=c(1,36,24,45,300,11))
gr.test <- graph.data.frame(test)
links <- data.frame(id=unique(unlist(test)),group=clusters(gr.test)$membership)
links[order(links$group),]
# id group
#1 10 1
#2 1 1
#3 24 1
#5 36 1
#6 45 1
#7 300 1
#4 8 2
#8 11 2
答案 2 :(得分:1)
不使用套餐:
# 2 sets of test data
mytest <- data.frame(id1=c(10,10,3,1,1,24,8,11,32,11,45),id2=c(1,36,50,24,45,300,11,8,32,12,49))
test <- data.frame(id1=c(10,10,1,1,24,8),id2=c(1,36,24,45,300,11))
grouppairs <- function(df){
# from wide to long format; assumes df is 2 columns of related id's
test <- data.frame(group = 1:nrow(df),val = unlist(df))
# keep moving to next pair until all same values have same group
i <- 0
while(any(duplicated(unique(test)$val))){
i <- i+1
# get group of matching values
matches <- test[test$val == test$val[i],'group']
# change all groups with matching values to same group
test[test$group %in% matches,'group'] <- test$group[i]
}
# renumber starting from 1 and show only unique values in group order
test$group <- match(test$group, sort(unique(test$group)))
unique(test)[order(unique(test)$group), ]
}
# test
grouppairs(test)
grouppairs(mytest)
答案 3 :(得分:0)
您说的是递归...,我想当我这样做时会很简洁。
测试数据
mytest <- data.frame(id1=c(10,10,3,1,1,24,8,11,32,11,45),id2=c(1,36,50,24,45,300,11,8,32,12,49))
test <- data.frame(id1=c(10,10,1,1,24,8),id2=c(1,36,24,45,300,11))
递归函数以获取分组
aveminrec <- function(v1,v2){
v2 <- ave(v1,by = v2,FUN = min)
if(identical(v1,v2)){
as.numeric(as.factor(v2))
}else{
aveminrec(v2,v1)
}
}
准备数据并简化之后
groupvalues <- function(valuepairs){
val <- unlist(valuepairs)
grp <- aveminrec(val,1:nrow(valuepairs))
unique(data.frame(grp,val)[order(grp,val), ])
}
获取结果
groupvalues(test)
groupvalues(mytest)
aveminrec()可能符合您的想法,尽管我敢打赌,有一种方法可以更直接地遍历每个分支,而不是重复本质上为split()和lapply()的ave()。也许递归地分裂和不适?实际上,这就像是重复的部分分支,或者是在不损失组信息的情况下略微简化了两个向量。
也许其中的一部分会用在一个实际的问题上,但是groupvalues()太密集而无法阅读,至少没有一些注释。我也没有检查过性能与使用ave和以这种方式翻转组的for循环相比如何。