2015-07-03 132 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