我试图在两列矩阵中操纵列数据并将其作为data.frame输出。
我所拥有的矩阵采用这种格式,其中起始列和结束列中的值都在增加且不重叠。此外,始终条目总是比结束条目多。
假设我从这个矩阵开始:
# 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
我希望这个double for循环输出一个data.frame,它将所有Start值分组为小于End值并将其与该End值相关联。
澄清我想输出这个:
# Start End
# 1 1,2,3 6
# 2 7,8 9
# 3 11,12,14 15
我尝试了一个双循环,但我需要更快的东西,因为我想在更大的矩阵~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
}
}
}
感谢您的帮助!
答案 0 :(得分:5)
以下是围绕findInterval()
,split()
和paste()
构建的解决方案:
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
向量不匹配。
这是一个固定的解决方案:
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
向量的值是空字符串,我认为这是一个明智的结果。如果需要,您可以在以后更改结果。
最后,这是一个使用你发布到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
答案 1 :(得分: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
答案 2 :(得分: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
)
})
答案 3 :(得分: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=", "))
编辑 - 感谢@bgoldst
使用findInterval