2015-07-03 182 views
3

我想操縱兩列矩陣中的列數據並將其輸出爲data.frame。R優化雙循環,矩陣操作

我擁有的矩陣是這種格式,其中開始和結束列中的值都在增加並且不重疊。此外,總是有更多的開始條目比有結束條目。

假設我開始與這個矩陣:

#  Start End 
# [1,]  1  6 
# [2,]  2  9 
# [3,]  3 15 
# [4,]  7 NA 
# [5,]  8 NA 
# [6,] 11 NA 
# [7,] 12 NA 
# [8,] 14 NA 

我想這雙for循環輸出組織了所有的初始值小於終值和同夥它與最終價值data.frame。

爲了澄清我想這個輸出:

#  Start End 
# 1 1,2,3  6 
# 2  7,8  9 
# 3 11,12,14 15 

我嘗試了雙for循環,但我需要的東西更快,因爲我想用這種方法在更大的矩陣〜5 MB。

start_end <- matrix(c(1, 6, 2, 9, 3, 15, 7, NA, 8, NA, 11, NA, 12, NA, 14, NA), 
    nrow=8, 
    ncol=2) 

# of non NA rows in column 2 
non_nacol <- sum(is.na(start_end[,2])) 

sorted.output <- data.frame(matrix(NA, nrow = nrow(start_end), ncol = 0)) 
sorted.output$start <- 0 
sorted.output$end <- 0 

#Sort and populate data frame 
for (k in 1:non_nacol) { 
    for (j in 1:nrow(start_end)) { 
     if (start_end[j,1]<start_end[k,2]) { 
     S <- (start_end[j,1]) 
     E <- (start_end[k,2]) 
     sorted.output$start[j] <- S 
     sorted.output$end[j] <- E 
     } 
    } 
} 

感謝您的幫助!

回答

3

你可以使用RCPP:

start_end <- matrix(c(1, 6, 2, 9, 3, 15, 7, NA, 8, NA, 11, NA, 12, NA, 14, NA), 
        nrow=8, 
        ncol=2, byrow = TRUE) 

library(Rcpp) 
cppFunction(' 
      DataFrame fun(const IntegerMatrix& Mat) { 
       IntegerVector start = na_omit(Mat(_, 0)); // remove NAs from starts 
       std::sort(start.begin(), start.end()); // sort starts 
       IntegerVector end = na_omit(Mat(_, 1)); // remove NAs from ends 
       std::sort(end.begin(), end.end()); // sort ends 
       IntegerVector res = clone(start); // initialize vector for matching ends 
       int j = 0; 
       for (int i = 0; i < start.length(); i++) { // loop over starts 
       while (end(j) < start(i) && j < (end.length() - 1)) { // find corresponding end 
        j++; 
       } 
       if (end(j) >= start(i)) res(i) = end(j); // assign end 
       else res(i) = NA_INTEGER; // assign NA if no end >= start exists 
       } 
       return DataFrame::create(_["start"]= start, _["end"]= res); // return a data.frame 
      } 
      ') 

Res <- fun(start_end) 

library(data.table) 
setDT(Res) 
Res[, .(start = paste(start, collapse = ",")), by = end] 
# end start 
#1: 6 1,2,3 
#2: 9  7,8 
#3: 15 11,12,14 
+0

這很快就會奏效謝謝羅蘭 – ALKI

2

下面是一個簡單基礎R版本

with(as.data.frame(dat), { 
    data.frame(
    Start=tapply(Start, cut(Start, c(0, End)), c), 
    End=na.omit(End) 
) 
}) 
#  Start End 
# 1 1, 2, 3 6 
# 2  7, 8 9 
# 3 11, 12, 14 15 

另一個

with(as.data.frame(dat), { 
    group <- as.integer(cut(Start, c(0, End)))     # assign Start values to End groups 
    data.frame(
    Start=unclass(by(dat, group, function(g) g[["Start"]])), # combine Start groups 
    End=unique(na.omit(End))         # Remove duplicate/NA End values 
) 
}) 
+0

是DAT對象的矩陣? – ALKI

+0

@chani不是它是'data.frame',我更新 – jenesaisquoi

+1

@Chani更新版本在你的保管箱文件上工作,只需要大約一秒 – jenesaisquoi

2

醜陋dplyr溶液:

library(dplyr) 
df <- as.data.frame(df) 

df %>% mutate(End = V2[findInterval(V1, na.omit(V2)) + 1]) %>% 
     group_by(End) %>% 
     summarise(Start = paste(V1, collapse=", ")) 

編輯 - 使用findInterval由於@bgoldst

5

這裏是paste()各地findInterval()split()內置的解決方案,以及:

m <- matrix(c(1,2,3,7,8,11,12,14,6,9,15,NA,NA,NA,NA,NA),ncol=2,dimnames=list(NULL,c('Start','End'))); 
data.frame(Start=sapply(split(m[,'Start'],findInterval(m[,'Start'],na.omit(m[,'End']))),paste,collapse=','),End=na.omit(m[,'End'])); 
##  Start End 
## 0 1,2,3 6 
## 1  7,8 9 
## 2 11,12,14 15 

編輯:您遇到的問題是由於這樣的事實,在你的真實數據輸入End值之間存在一些間隔不包含任何輸入Start值。上面的解決方案錯誤地忽略了輸出Start向量中的那些間隔,這會導致與輸出向量的長度不匹配。

這裏是一個固定的解決方案:

end <- na.omit(m[,'End']); 
data.frame(Start=unname(sapply(split(m[,'Start'],findInterval(m[,'Start'],end))[as.character(0:c(length(end)-1))],paste,collapse=',')),End=end); 
##  Start End 
## 1 1,2,3 6 
## 2  7,8 9 
## 3 11,12,14 15 

這裏有一個測試矩陣的示威,有一個空區間:

m <- matrix(c(1,2,3,11,12,14,6,9,15,NA,NA,NA),ncol=2,dimnames=list(NULL,c('Start','End'))); 
m; 
##  Start End 
## [1,]  1 6 
## [2,]  2 9 
## [3,]  3 15 
## [4,] 11 NA 
## [5,] 12 NA 
## [6,] 14 NA 
end <- na.omit(m[,'End']); 
data.frame(Start=unname(sapply(split(m[,'Start'],findInterval(m[,'Start'],end))[as.character(0:c(length(end)-1))],paste,collapse=',')),End=end); 
##  Start End 
## 1 1,2,3 6 
## 2   9 
## 3 11,12,14 15 

正如你所看到的,對於一個空區間時,值結果輸出Start vector是空字符串,我認爲這是一個明智的結果。如果需要,您可以隨後更改結果。

最後,這裏是一個使用你發佈到Dropbox的真實數據演示:

m <- read.table('start_end.txt',col.names=c('Start','End')); 
head(m); 
## Start End 
## 1 11165 10548 
## 2 12416 11799 
## 3 12466 11900 
## 4 12691 11976 
## 5 12834 13336 
## 6 13320 14028 
end <- na.omit(m[,'End']); 
system.time({ out <- data.frame(Start=unname(sapply(split(m[,'Start'],findInterval(m[,'Start'],end))[as.character(0:c(length(end)-1))],paste,collapse=',')),End=end); }); 
## user system elapsed 
## 21.234 0.015 21.251 
head(out); 
##       Start End 
## 1        10548 
## 2       11165 11799 
## 3        11900 
## 4        11976 
## 5 12416,12466,12691,12834,13320 13336 
## 6  13425,13571,13703,13920 14028 
nrow(out); 
## [1] 131668 
+0

感謝您的回答。當我使用大矩陣時,我不斷收到這個錯誤。錯誤data.frame(開始= sapply(拆分(start_end [,「開始」],findInterval(start_end [,: 參數意味着不同的行數:95954,131668 – ALKI

+0

您可以發佈您的完整矩陣的某處,也許pastebin或東西?我必須看到它來確定問題 – bgoldst

+0

可能有間隔沒有「開始」值 – bgoldst