【问题标题】:Efficiently counting numbers falling within each range of numbers有效地计算每个数字范围内的数字
【发布时间】:2013-07-18 20:59:42
【问题描述】:

我正在寻找以下问题的更快解决方案。我将用一个小例子来说明这个问题,然后提供代码来模拟大数据,因为这就是这个问题的重点。我的实际问题大小是列表长度 = 100 万个条目。

说,我有两个列表,如下所示:

x <- list(c(82, 18), c(35, 50, 15))
y <- list(c(1,2,3,55,90), c(37,38,95))

x 和 y 的属性:

  • x 列表中的每个元素总和为 100。
  • y 的每个元素将始终进行排序,并且始终介于 1 和 100 之间。

问题:

现在,我想要的是这个。以x[[1]]y[[1]] 为例,我想在y[[1]] 中找到 1) 82 和 c(1,2,3,55),而介于 83 和 100 之间的数字是 c(90)。对于x[[2]]y[[2]],类似地,c(0, 2, 1)。也就是说,答案应该是:

[[1]]
[1] 4 1

[[2]]
[1] 0 2 1

如果这还不清楚,请告诉我。


具有 100 万个条目的模拟数据

set.seed(1)
N <- 100
n <- 1e6
len <- sample(2:3, n, TRUE)

x <- lapply(seq_len(n), function(ix) {
    probs <- sample(100:1000, len[ix])
    probs <- probs/sum(probs)

    oo <- round(N * probs)
    if (sum(oo) != 100) {
        oo[1] <- oo[1] + (100 - sum(oo))
    }
    oo
})

require(data.table)
ss <- sample(1:10, n, TRUE)
dt <- data.table(val=sample(1:N, sum(ss), TRUE), grp=rep(seq_len(n), ss))
setkey(dt, grp, val)
y <- dt[, list(list(val)),by=grp]$V1

到目前为止我做了什么:

使用mapply(慢):

我想先将rankties.method="first"mapply 一起使用(显然选择2 个列表)并尝试了这个:

tt1 <- mapply(y, x, FUN=function(a,b) { 
    tt <- rank(c(a, cumsum(b)), ties="first")[-(1:length(a))]; c(tt[1]-1, diff(tt)-1)
})

虽然这很好用,但 100 万个条目需要花费大量时间。我认为计算rankdiff 的开销会增加很多次。这需要 241 秒

因此,我决定尝试通过使用data.table 并使用“组”列进行排序来克服rankdiff 的使用。我想出了一个更长但更快的解决方案,如下所示:

使用data.table(更快):

xl <- sapply(x, length)
yl <- sapply(y, length)
xdt <- data.table(val=unlist(x, use.names=FALSE), grp=rep(seq_along(xl), xl), type = "x")
xdt[, cumval := cumsum(val), by=grp]
ydt <- data.table(val=unlist(y, use.names=FALSE), grp=rep(seq_along(yl), yl), type = "y")
tt2 <-rbindlist(list(ydt, xdt[, list(cumval, grp, type)]))
setkey(tt2, grp, val)
xdt.pos <- which(tt2$type == "x")
tt2[, type.x := 0L][xdt.pos, type.x := xdt.pos]
tt2 <- tt2[xdt.pos][tt2[, .N, by = grp][, N := cumsum(c(0, head(N, -1)))]][, sub := type.x - N]
tt2[, val := xdt$val]

# time consuming step
tt2 <- tt2[, c(sub[1]-1, sub[2:.N] - sub[1:(.N-1)] - 1), by = grp]
tt2 <- tt2[, list(list(V1)),by=grp]$V1

这需要 26 秒。所以它快了大约 9 倍。我想知道是否有可能获得更多的加速,因为我必须在 5-10 个这样的 100 万个元素上递归地计算它。谢谢。

【问题讨论】:

  • 你试过类似hist(y[[k]],breaks=c(x[[k]][1:(n-1)],100) )的东西吗?
  • 卡尔,什么是 k,1 到 1e6?在大数据上给出 k=2 的错误。
  • 在例子中:c(0, 2, 1),0从哪里来?我认为 2 = {50}
  • @ZoltánNagy,y 中 x 的计数在 1-35、36-85、86-100 之间。
  • 阿伦,你有两个列表变量。由于您将x[[1]]y[[1]] 进行匹配,x[[2]]y[[2]] 进行匹配,所以我假设您总是这样匹配。 x[[2]]y[[2]] 在真实数据集中的值是多少?如果它们的长度为 1,那么 hist 可能会失败。

标签: r algorithm optimization data.table mapply


【解决方案1】:

这是另一种data.table 方法。 编辑我添加了一个(肮脏的?)hack,它可以加快速度并使其比 OP data.table 解决方案快约 2 倍。

# compile the data.table's, set appropriate keys
xl <- sapply(x, length)
yl <- sapply(y, length)
xdt <- data.table(val=unlist(x, use.names=FALSE), grp=rep(seq_along(xl), xl))
xdt[, cumval := cumsum(val), by=grp]
ydt <- data.table(val=unlist(y, use.names=FALSE), grp=rep(seq_along(yl), yl))

# hack #0, set key but prevent sorting, since we know data is already sorted
setattr(ydt, 'sorted', c('grp', 'val'))

# by setting the key in y to val and in x to cumval we can
# leverage the rolling joins
setattr(xdt, 'sorted', c('grp', 'cumval'))  # hack #1 set key, but prevent sorting
vals = xdt[, cumval.copy := cumval][ydt, roll = -Inf]

# hack #2, same deal as above
# we know that the order of cumval and cumval.copy is the same
# so let's convince data.table in that
setattr(vals, 'sorted', c('grp', 'cumval.copy'))

# compute the counts and fill in the missing 0's
# for when there is no y in the appropriate x interval
tt2 = vals[, .N, keyby = list(grp, cumval.copy)][xdt][is.na(N), N := 0L]

# convert to list
tt2 = tt2[order(grp, cumval.copy), list(list(N)), by = grp]$V1

【讨论】:

  • 我应该提一下,我正在测试一个稍微小的 n = 1e5 的集合,因为我的机器在 1e6 时死机了。
  • @Arun,实际上我只是在另一台机器上测试了 1e6,数字是 39s OP 和 19s 这个(在两个版本中都使用 sapply)
【解决方案2】:

这大约快 25%,但输出为矩阵而不是列表。许多人可以使用 appy/sappy 使其与列表一起使用(保存为列表会减慢速度)。

c=matrix(0,length(x),100)
for(j in 1:length(x)){
  a=-1
  b=0
  for(i in 1:length(x[[j]])){
    a=b
    b=b+x[[j]][i]
    c[j,i]=sum((a<=y[[j]])*(y[[j]]<=b))
  }
}

【讨论】:

  • 你不只是在做直方图分箱吗?
猜你喜欢
  • 2020-07-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-06
  • 2013-02-15
  • 2021-07-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多