【问题标题】:findInterval() with right-closed intervals具有右闭区间的 findInterval()
【发布时间】:2012-11-09 02:10:55
【问题描述】:

R 中出色的 findInterval() 函数在其 vec 参数中使用左闭子区间,如其文档所示:

如果i <- findInterval(x,v),我们有v[i[j]] <= x[j] < v[i[j] + 1]

如果我想要右闭子区间,我有什么选择?我想出的最好的是:

findInterval.rightClosed <- function(x, vec, ...) {
  fi <- findInterval(x, vec, ...)
  fi - (x==vec[fi])
}

另一个也可以:

findInterval.rightClosed2 <- function(x, vec, ...) {
  length(vec) - findInterval(-x, -rev(vec), ...)
}

这是一个小测试:

x <- c(3, 6, 7, 7, 29, 37, 52)
vec <- c(2, 5, 6, 35)
findInterval(x, vec)
# [1] 1 3 3 3 3 4 4
findInterval.rightClosed(x, vec)
# [1] 1 2 3 3 3 4 4
findInterval.rightClosed2(x, vec)
# [1] 1 2 3 3 3 4 4

但如果有更好的解决方案,我想看看其他解决方案。我所说的“更好”是指“在某种程度上更令人满意”或“感觉不像是杂物”,甚至可能是“更有效率”。 =)

(注意findInterval() 有一个rightmost.closed 参数,但它是不同的——它只指最终的子区间,具有不同的含义。)

【问题讨论】:

  • 你怎么看:findInterval(x, c(-Inf, head(vec, -1)))
  • @sgibb 似乎没有用,我添加了一个示例,而您的示例并没有给出相同的结果。
  • 我在这里有点迷惑,但findInterval(x-1,vec) 是否符合您的要求?
  • @thelatemail,只要是整数?
  • 我认为可以发布您自己问题的答案。我的投票将投给您的findInterval.rightClosed2

标签: r binary-search


【解决方案1】:

编辑:主要清理所有过道。

您可以查看cut。默认情况下,cut 设置左开和右闭区间,可以使用适当的参数 (right) 进行更改。使用您的示例:

x <- c(3, 6, 7, 7, 29, 37, 52)
vec <- c(2, 5, 6, 35)
cutVec <- c(vec, max(x)) # for cut, range of vec should cover all of x

现在创建四个应该做同样事情的函数:两个来自 OP,一个来自 Josh O'Brien,然后是 cutcut 的两个参数已从默认设置更改:include.lowest = TRUE 将为最小(最左侧)间隔创建一个两边都闭合的间隔。 labels = FALSE 将导致 cut 仅返回 bin 的整数值,而不是创建一个因子,否则它会这样做。

findInterval.rightClosed <- function(x, vec, ...) {
  fi <- findInterval(x, vec, ...)
  fi - (x==vec[fi])
}
findInterval.rightClosed2 <- function(x, vec, ...) {
  length(vec) - findInterval(-x, -rev(vec), ...)
}
cutFun <- function(x, vec){
    cut(x, vec, include.lowest = TRUE, labels = FALSE)
}
# The body of fiFun is a contribution by Josh O'Brien that got fed to the ether.
fiFun <- function(x, vec){
    xxFI <- findInterval(x, vec * (1 + .Machine$double.eps))
}

所有函数都返回相同的结果吗?对。 (注意cutVec 用于cutFun

mapply(identical, list(findInterval.rightClosed(x, vec)),
  list(findInterval.rightClosed2(x, vec), cutFun(x, cutVec), fiFun(x, vec)))
# [1] TRUE TRUE TRUE

现在对 bin 的要求更高:

x <- rpois(2e6, 10)
vec <- c(-Inf, quantile(x, seq(.2, 1, .2)))

测试是否相同(注意使用unname

mapply(identical, list(unname(findInterval.rightClosed(x, vec))),
  list(findInterval.rightClosed2(x, vec), cutFun(x, vec), fiFun(x, vec)))
# [1] TRUE TRUE TRUE

和基准测试:

library(microbenchmark)
microbenchmark(findInterval.rightClosed(x, vec), findInterval.rightClosed2(x, vec),
  cutFun(x, vec), fiFun(x, vec), times = 50)
# Unit: milliseconds
#                                expr       min        lq    median        uq       max
# 1                    cutFun(x, vec)  35.46261  35.63435  35.81233  36.68036  53.52078
# 2                     fiFun(x, vec)  51.30158  51.69391  52.24277  53.69253  67.09433
# 3  findInterval.rightClosed(x, vec) 124.57110 133.99315 142.06567 155.68592 176.43291
# 4 findInterval.rightClosed2(x, vec)  79.81685  82.01025  86.20182  95.65368 108.51624

从这次运行来看,cut 似乎是最快的。

【讨论】:

  • 谢谢。我似乎记得cut 的效率较低,并且它也不允许像findInterval 那样从vec 的右边缘拖出(请参阅输出中的第15-17 行),但这部分可以工作在末尾添加Inf
  • @KenWilliams,是的,用breaks 覆盖x 的整个范围允许您剪切所有x(也请参阅我的编辑)。至于效率,好吧,至少代码已经存在了。
  • @KenWilliams,如果您在效率基准测试中计算速度,那么findIntervalcut 之间可能并没有太大区别。也许cut 的放缓通常来自转换为因素和标签制作?
  • 为了完整起见,您能否将 OP 的解决方案也包括在您的时间安排中...?
  • @KenWilliams,如果您愿意,请参阅上面的(速度)基准测试的主要更新。
【解决方案2】:

也许你可以使用left.open选项:

findInterval(x, vec, left.open=T)
[1] 1 2 3 3 3 4 4

【讨论】:

  • 是的 - 此选项是在 2016 年 10 月发布的 R 版本 3.3.2 中添加的。现在是正确的方法。
【解决方案3】:

如果您的限制是区间,您只需将正确的区间扩大一点:interval+c(0,0.1) 可以:findinterval(value, interval+c(0,0.1))

【讨论】:

  • 由于各种原因,这不起作用。最根本的是,回收将导致这改变每个奇数条目的正确间隔,而不是全部。此外,这假设您知道数据的离散化范围大于 0.1。
猜你喜欢
  • 1970-01-01
  • 2014-05-05
  • 1970-01-01
  • 2016-09-18
  • 2018-08-06
  • 1970-01-01
  • 1970-01-01
  • 2019-02-14
  • 1970-01-01
相关资源
最近更新 更多