在R中循环的替代方案

时间:2012-07-27 18:15:21

标签: r

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)

我正在尝试比较Pos中的df1值,以查看PosStartPosEnd之间的df2df2之间的差异。对于超过1行的df1$Pos,这可能是真的。在输出中,我尝试将df2$CoPos附加为新列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])]
    }

}

我做过类似的事情:

{{1}}

有人可以告诉我是否有办法在没有循环的情况下执行此操作。我在这里做错了什么?经过几个月的挣扎,我不认为我仍然理解循环的概念。

提前感谢一大堆。

2 个答案:

答案 0 :(得分: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练习的完美例子。如果你正在增长你的输出向量而不是分配它“你将会有一个糟糕的时间mm'kay。”

话虽如此,为了让你的循环工作,你需要先建立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中常见陷阱的绝佳参考。

答案 1 :(得分:3)

虽然@Justin answer适用于这种情况,但如果您不记得apply,则在data.frame上使用apply会导致混淆错误{1}}会在每行/每列调用FUN之前将您的data.frame转换为矩阵。

这是一个更通用的解决方案,可以避免这个潜在的问题:

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