2012-07-27 64 views
1
循环
df1 <- data.frame(Chr=1, Pos= c(100,200,300,400),stringsAsFactors=F) 

df2 <- data.frame(Chr=1, PosStart= c(25,25,150,175,225,275,375),PosEnd= c(150,75,275,300,400,500,750),stringsAsFactors=F) 

我想给Posdf1比较,看看是否有PosStartdf2PosEnd的落差。对于多于1行的df2,这可能是正确的。在输出中,我试图附加df1$Pos作为新列df2$CoPos;每次条件成立。输出应该像成才:替代计划中的R

Chr PosStart PosEnd CoPos 
1  25 150 100 
1  150 275 200 
1  175 300 200 
1  225 400 300 
1  275 500 300 
1  375 750 400 

我做一样的东西:

for(i in 1:length(df1$Pos)){ 

    for(j in 1:length(df2$PosStart){ 

      df2$CoPos[j]<- df1$Pos[which(df2$PosStart[j] < df1$Pos[i] < df2$PosEnd[j])] 
    } 

} 

是否有人可以告诉我,如果有一种方法可以做到这一点没有循环。另外我在这里做错了什么?经过数月的拼杀,我不认为我仍然理解循环的概念。

非常感谢你。

回答

5

可以apply支票的df2每一行:

myfun <- function(x) { 
    data.frame(df2[x['Pos'] < df2$PosEnd & x['Pos'] > df2$PosStart,], Pos=x['Pos']) 
} 

将从DF2在满足条件还有Pos值返回一行或多行。

> apply(df1, 1, myfun) 
[[1]] 
    Chr PosStart PosEnd Pos 
1 1  25 150 100 

[[2]] 
    Chr PosStart PosEnd Pos 
3 1  150 275 200 
4 1  175 300 200 

[[3]] 
    Chr PosStart PosEnd Pos 
5 1  225 400 300 
6 1  275 500 300 

[[4]] 
    Chr PosStart PosEnd Pos 
6 1  275 500 400 
7 1  375 750 400 

> 

那么你可以使用plyrldply转换到一个列表:

发表评论
> library(plyr) 
> ldply(apply(df1, 1, myfun), as.data.frame) 
    Chr PosStart PosEnd Pos 
1 1  25 150 100 
2 1  150 275 200 
3 1  175 300 200 
4 1  225 400 300 
5 1  275 500 300 
6 1  275 500 400 
7 1  375 750 400 
> 

编辑:

这是一个很难的事情在for循环做。你不知道你会提前多少场比赛。可能是df1中的每一行都匹配df2中的每一行,或者没有任何行或两者之间的任何行。因此,你不知道你的输出需要多大。这就是R中不好的for loop练习的完美例子。如果你正在增长输出向量而不是分配给它,“你将会有一段糟糕的时间。

就是说,为了让你的循环工作,你需要首先制作CoPos列。

df2$CoPos <- NA 

然后执行类似的循环东西:

for (i in 1:length(df1$Pos)) { 
    for (j in 1:length(df2$PosStart)) { 
      if (df2$PosStart[j] < df1$Pos[i] & df2$PosEnd[j] > df1$Pos[i]) { 
        df2$CoPos[j] <- df1$Pos[i] 
      } 
    } 

} 

但是,如果你在df1适合您的约束发现两行,你会只记录您找到合适的行第二个在df2

相反,你可以长出新的data.frame这样的:

df3 <- data.frame(Chr=1, Pos= c(100, 125, 200,300,400),stringsAsFactors=F) 

out <- data.frame() 

for (i in 1:length(df3$Pos)) { 
    for (j in 1:length(df2$PosStart)) { 
      if (df2$PosStart[j] < df3$Pos[i] & df2$PosEnd[j] > df3$Pos[i]) { 
        out <- rbind(out, cbind(df2[j,], df3$Pos[i])) 
      } 
    } 

} 

但是,不这样做......请不要:)虽然我传福音,看看R-Inferno作为R中常见陷阱的优秀参考。

+3

或者,住在基地R:'do.call(rbind,应用(DF1,1,myfun))',但基础R不会给你相同的行名。 – A5C1D2H2I1M1N2O1R2T1 2012-07-27 18:36:38

+0

我总是忘记'do.call'!好决定。 – Justin 2012-07-27 18:37:40

+0

非常感谢你们!这真太了不起了!!!!!!!!!它的作用像一个魅力,没有循环,我完全理解它。我想R真的会迫使人们以不同的方式思考。然而,只是为了它,你可以告诉我我的错在哪里for循环。我在两个条件之间添加了&符号。 – user1079898 2012-07-27 18:57:34

3

虽然@Justinanswer在这种情况下工作,使用apply上的数据。如果您不记得apply在每行/列上调用FUN之前将数据帧转换为矩阵,可能会导致混淆错误。

下面是避免这种潜在的问题更广泛的解决方案:

compareFun <- function(x) { 
    data.frame(df2[x > df2$PosStart & x < df2$PosEnd,], Pos=x) 
} 
do.call(rbind, lapply(df1$Pos, compareFun)) 

具体地说就是,如果df1df2是不是与Chr是字符定义,Justin的解决方案将抛出一个错误,并不能使它明确是什么原因导致的问题:

df1 <- data.frame(Chr="1", Pos=c(100,200,300,400), stringsAsFactors=FALSE) 
df2 <- data.frame(Chr="1", PosStart=c(25,25,150,175,225,275,375), 
    PosEnd=c(150,75,275,300,400,500,750), stringsAsFactors=FALSE) 
apply(df1, 1, myfun) 
# Error in data.frame(df2[x["Pos"] < df2$PosEnd & x["Pos"] > df2$PosStart, : 
# arguments imply differing number of rows: 0, 1 
+0

+1优秀的解释和解决方案,谢谢。 – Justin 2012-07-27 21:52:40