在R中循环的替代方案

use*_*898 1 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)
Run Code Online (Sandbox Code Playgroud)

我想比较Posdf1,看看是否任何的落差PosStartPosEnddf2.对于超过1行的情况,这可能是真的df2.在输出中,我试图将其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
Run Code Online (Sandbox Code Playgroud)

我做过类似的事情:

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])]
    }

}
Run Code Online (Sandbox Code Playgroud)

有人可以告诉我,如果没有循环,有没有办法做到这一点.我在这里做错了什么?经过几个月的挣扎,我不认为我仍然理解循环的概念.

提前感谢一堆.

Jus*_*tin 5

你可以apply检查每一行df2:

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

这将返回满足条件的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

> 
Run Code Online (Sandbox Code Playgroud)

然后你可以使用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
> 
Run Code Online (Sandbox Code Playgroud)

编辑评论:

这在for循环中很难做到.你不知道你提前有多少场比赛.可能是每一行都df1匹配每一行,df2或者没有一行或两者之间.因此,您不知道您的输出需要多大.这是for loopR 中不良练习的完美例子.如果你正在增长你的输出向量而不是分配它"你将会有一个糟糕的时间mm'kay."

话虽如此,为了使你的循环工作,你需要首先使CoPos列.

df2$CoPos <- NA
Run Code Online (Sandbox Code Playgroud)

然后执行类似于循环的操作:

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]
            }
    }

}
Run Code Online (Sandbox Code Playgroud)

但是,如果您找到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]))
            }
    }

}
Run Code Online (Sandbox Code Playgroud)

但是,不要这样做......请不要:)虽然我正在传福音,但请看一下R-Inferno,以便对R中常见的陷阱进行参考.

  • 或者,保持在基础R:`do.call(rbind,apply(df1,1,myfun))`,但是基数R不会给你相同的行名. (3认同)