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以查看是否介于任何PosStartPosEnd之间df2。对于超过 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

我做了类似的事情:

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

}

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

提前感谢一堆。

4

2 回答 2

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

> 

然后您可以使用plyrandldply转换为列表:

> 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 中常见陷阱的出色参考。

于 2012-07-27T18:27:54.550 回答
3

虽然@Justin答案在这种情况下有效,apply但如果您不记得在调用每一行/列apply之前将您的 data.frame 转换为矩阵,则在 data.frame 上使用可能会导致令人困惑的错误。FUN

这是避免此潜在问题的更通用的解决方案:

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

更详细地说,如果df1并且df2改为定义Chr为字符,贾斯汀的解决方案会抛出一个错误,该错误无法明确导致问题的原因:

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
于 2012-07-27T20:25:24.697 回答