2012-07-27 71 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