按 R 中的间隔集对数据进行子集化
subset data by set of intervals in R
我想根据一组间隔从向量中排除值。
示例数据:
mydata <- sort(runif(100,0,300))
mIntervals <- data.frame(start = c(2,50,97,159) , end = c(5,75, 120, 160))
解决方案 1: 使用简单的 subset() - 不适合 - mIntervals 的长度可能相当大
解决方案 2: 使用嵌套 for 循环:
valid <- vector(length(mydata))
valid <- TRUE
for(i in 1:length(mydata){
for(j in 1:length(mIntervals){
if(mydata[i] > mIntervals[j,]$start & mydata[i] < mIntervals[j,]$end){
valid[i] <- FALSE
}
}
}
mydata[valid]
此解决方案在 R 中花费的时间太长。
解决方案3:函数findIntervals
require(FSA)
valid <- findInterval(mydata, sort(c(mIntervals$start, mIntervals$end)))
mydata[is.even(valid)]
解决方法4:用了一些包'Intervals',但是也没有合适的函数(可能是interval_overlap())
已经讨论了非常相似(但不完全相同)的问题 。但是有整数向量的解决方案,而不是连续变量。
我没有更多的想法。解决方案编号3 似乎是最好的,但我不喜欢它 - 它不够健壮 - 你必须检查重叠间隔等。
这个看起来很简单的问题有更好的解决方案吗?谢谢
真实数据:我在某些时间测量了光强度(日期时间、强度)。我还有测量设备正在维护(开始、结束)的日期时间间隔。
现在我想清理数据 = 排除在维护期间测量的值(高效!)。
这是一个 Rcpp 实现:
library(Rcpp);
set.seed(12L);
mydata <- sort(runif(100L,0,300));
mIntervals <- data.frame(start=c(2,50,97,159),end=c(5,75,120,160));
cppFunction('
LogicalVector inIntervals(DoubleVector v, DoubleVector starts, DoubleVector ends ) {
if (starts.size()!=ends.size())
throw new std::invalid_argument("starts and ends must be same length.");
LogicalVector res(v.size(),false);
for (int i = 0; i < v.size(); ++i) {
double val = v[i];
for (int j = 0; j < starts.size(); ++j)
if (val>starts[j] && val<ends[j]) {
res(i) = true;
break;
}
}
return res;
}
');
mydata[!inIntervals(mydata,mIntervals$start,mIntervals$end)];
## [1] 6.863323 10.168687 13.765236 16.585860 20.808275 28.508376 29.355912
## [8] 30.534403 33.809681 37.152610 42.659676 45.787152 46.319152 47.274177
## [15] 47.877135 49.281417 78.640425 79.475513 80.383078 80.814563 88.273175
## [22] 93.344382 94.136411 94.736104 96.603457 126.327013 130.399146 131.800295
## [29] 131.828798 137.282145 148.542361 151.430386 162.212264 162.541752 165.648249
## [36] 166.758025 167.388096 172.243474 172.603380 176.544549 182.477693 189.979382
## [43] 192.404449 192.499610 199.703949 200.945789 202.035664 208.173427 210.533571
## [50] 212.949140 214.431451 215.524016 224.951507 225.608016 229.180120 230.324658
## [57] 232.415456 236.278594 236.350904 244.164168 244.218976 244.669498 245.332560
## [64] 247.184695 253.110672 253.267796 263.339092 263.352697 264.826916 267.979469
## [71] 282.326263 282.786520 285.996158 291.379637 293.290767 294.260683
我不知道这会有多高效,但是...
vbetween <- Vectorize(dplyr::between, vectorize.args = c("left", "right"), SIMPLIFY=F)
mydata[!Reduce("|", vbetween(mydata, mIntervals$start, mIntervals$end))]
使用data.table
的development version (1.9.7),我们可以尝试%anywhere%
:
library(data.table)
# %anywhere% returns TRUE if mydata is within any mIntervals, else FALSE
ans <- mydata[!mydata %anywhere% mIntervals]
这将包括端点,但是因为 incbounds = TRUE
是默认设置。如果需要排除端点,可以使用以下语法:
mydata[!anywhere(mydata, mIntervals[, 1], mIntervals[, 2], incbounds = FALSE)]
如果重新排列间隔,可以使用 cut
函数,然后只取出奇数间隔:
NEWinterval <- c(2,5,50,75,97,120,159,160)
mydata[cut(mydata, NEWinterval,labels = F) %% 2 != 0]
我想展示另一种使用 data.table 包和 rolljoin 的方法。
首先你融化并排序间隔的数据框:
mIntervals.dt <- data.table(mIntervals)
Intervals.melt <- melt(mIntervals.dt, measure.vars = c("start", "end"))
订购数据并使用滚动连接:
mydata.dt <- data.table(mydata)
setkey(Intervals.melt, value)
setkey(mydata.dt)
final.dt <- Intervals.melt[mydata.dt, roll = -Inf]
由于您使用了 -Inf(与 mIntervals 中下一个最接近的值合并),因此仅获取具有 "end" 值的数据。
final.dt[variable == "end"]
非常快速和灵活。
我想根据一组间隔从向量中排除值。
示例数据:
mydata <- sort(runif(100,0,300))
mIntervals <- data.frame(start = c(2,50,97,159) , end = c(5,75, 120, 160))
解决方案 1: 使用简单的 subset() - 不适合 - mIntervals 的长度可能相当大
解决方案 2: 使用嵌套 for 循环:
valid <- vector(length(mydata))
valid <- TRUE
for(i in 1:length(mydata){
for(j in 1:length(mIntervals){
if(mydata[i] > mIntervals[j,]$start & mydata[i] < mIntervals[j,]$end){
valid[i] <- FALSE
}
}
}
mydata[valid]
此解决方案在 R 中花费的时间太长。
解决方案3:函数findIntervals
require(FSA)
valid <- findInterval(mydata, sort(c(mIntervals$start, mIntervals$end)))
mydata[is.even(valid)]
解决方法4:用了一些包'Intervals',但是也没有合适的函数(可能是interval_overlap())
已经讨论了非常相似(但不完全相同)的问题
我没有更多的想法。解决方案编号3 似乎是最好的,但我不喜欢它 - 它不够健壮 - 你必须检查重叠间隔等。
这个看起来很简单的问题有更好的解决方案吗?谢谢
真实数据:我在某些时间测量了光强度(日期时间、强度)。我还有测量设备正在维护(开始、结束)的日期时间间隔。 现在我想清理数据 = 排除在维护期间测量的值(高效!)。
这是一个 Rcpp 实现:
library(Rcpp);
set.seed(12L);
mydata <- sort(runif(100L,0,300));
mIntervals <- data.frame(start=c(2,50,97,159),end=c(5,75,120,160));
cppFunction('
LogicalVector inIntervals(DoubleVector v, DoubleVector starts, DoubleVector ends ) {
if (starts.size()!=ends.size())
throw new std::invalid_argument("starts and ends must be same length.");
LogicalVector res(v.size(),false);
for (int i = 0; i < v.size(); ++i) {
double val = v[i];
for (int j = 0; j < starts.size(); ++j)
if (val>starts[j] && val<ends[j]) {
res(i) = true;
break;
}
}
return res;
}
');
mydata[!inIntervals(mydata,mIntervals$start,mIntervals$end)];
## [1] 6.863323 10.168687 13.765236 16.585860 20.808275 28.508376 29.355912
## [8] 30.534403 33.809681 37.152610 42.659676 45.787152 46.319152 47.274177
## [15] 47.877135 49.281417 78.640425 79.475513 80.383078 80.814563 88.273175
## [22] 93.344382 94.136411 94.736104 96.603457 126.327013 130.399146 131.800295
## [29] 131.828798 137.282145 148.542361 151.430386 162.212264 162.541752 165.648249
## [36] 166.758025 167.388096 172.243474 172.603380 176.544549 182.477693 189.979382
## [43] 192.404449 192.499610 199.703949 200.945789 202.035664 208.173427 210.533571
## [50] 212.949140 214.431451 215.524016 224.951507 225.608016 229.180120 230.324658
## [57] 232.415456 236.278594 236.350904 244.164168 244.218976 244.669498 245.332560
## [64] 247.184695 253.110672 253.267796 263.339092 263.352697 264.826916 267.979469
## [71] 282.326263 282.786520 285.996158 291.379637 293.290767 294.260683
我不知道这会有多高效,但是...
vbetween <- Vectorize(dplyr::between, vectorize.args = c("left", "right"), SIMPLIFY=F)
mydata[!Reduce("|", vbetween(mydata, mIntervals$start, mIntervals$end))]
使用data.table
的development version (1.9.7),我们可以尝试%anywhere%
:
library(data.table)
# %anywhere% returns TRUE if mydata is within any mIntervals, else FALSE
ans <- mydata[!mydata %anywhere% mIntervals]
这将包括端点,但是因为 incbounds = TRUE
是默认设置。如果需要排除端点,可以使用以下语法:
mydata[!anywhere(mydata, mIntervals[, 1], mIntervals[, 2], incbounds = FALSE)]
如果重新排列间隔,可以使用 cut
函数,然后只取出奇数间隔:
NEWinterval <- c(2,5,50,75,97,120,159,160)
mydata[cut(mydata, NEWinterval,labels = F) %% 2 != 0]
我想展示另一种使用 data.table 包和 rolljoin 的方法。
首先你融化并排序间隔的数据框:
mIntervals.dt <- data.table(mIntervals)
Intervals.melt <- melt(mIntervals.dt, measure.vars = c("start", "end"))
订购数据并使用滚动连接:
mydata.dt <- data.table(mydata)
setkey(Intervals.melt, value)
setkey(mydata.dt)
final.dt <- Intervals.melt[mydata.dt, roll = -Inf]
由于您使用了 -Inf(与 mIntervals 中下一个最接近的值合并),因此仅获取具有 "end" 值的数据。
final.dt[variable == "end"]
非常快速和灵活。