python - R 中是否有等效(或更快)版本的 numpy.binCount 用于基于多个 bin 求和值?

标签 python r algorithm performance numpy

我有一个算法,它需要根据 M×N 整数矩阵“bins”中的类别对 N 个数字的变化向量“wgts”求和。例如,如果 'wgts' 的初始值为 [0.2, 0.4, 0.3, 0.1, 0.7, 0.6] 并且 B 为 [[0, 1, 2, 2, 0, 1], [2, 2, 0, 0, 1, 1]],结果将是 [[(0.2 + 0.7), (0.4 + 0.6), (0.3 + 0.1)], [(0.7 + 0.6), (0.2 + 0.4), (0.3 + 0.1 )]

我需要使用固定类别矩阵“bins”并更改向量“wgts”来重复该过程。随着 M 和 N 变大,这个过程会花费很多时间。我发现 python 包 numpy 对此有一个有用且快速的函数 bincount。我想使用 R,因为我的其余过程是在 R 中进行的,但到目前为止,我无法像在 Python 中那样快速地在 R 中纠正我的算法

到目前为止,R 中似乎工作最快的方法是保存单独的逻辑 M×N 矩阵,每个类别一个。但是,它仍然需要大约两倍于我的 Python 脚本的时间,而且我认为它需要更多内存,尽管我不确定如何测量这部分。下面是我的 Python 和 R 脚本及其处理时间。

# R First attempt, "straightforward"
smplSize <- 1000000
binTypes <- 100
nIter <- 20

set.seed(1)
bins <- matrix(floor(runif(smplSize * binTypes, min=0, max=5)), 
                  nrow = smplSize)
wgts <- runif(smplSize)

tic <- Sys.time()
for (i in (1:nIter)) {

  res <- matrix(nrow=5, ncol=binTypes)
  for (j in 0:4) {
    res[j+1,] <- colSums(wgts * (bins == j))
  }

  # Some process that modifies wgts based on res
}
toc <- Sys.time()
toc - tic # 117 seconds
# Second attempt, storing category locations in separate mask matrices
tic <- Sys.time()
# Store 5 matrices identifying locations of the integers 0 - 4
binMask <- list()
for (i in 0:4) {
  binMask[[i+1]] <- bins == i
}

for (i in (1:nIter)) {
  res <- matrix(nrow=5, ncol=binTypes)
  for (j in 0:4) {
    res[j+1,] <- colSums(wgts * binMask[[j + 1]])
  }

  # Some process that modifies wgts based on res
}
toc <- Sys.time()
toc - tic # 72 seconds

print(object.size(binMask), units = "Gb") # 1.9 Gb
import numpy as np
import timeit
import sys

smplSize = 1000000
nBins = 100
nIter = 20
wgts = np.random.random_sample(smplSize)
bins = np.random.randint(0, 5, (smplSize, nBins))

tic=timeit.default_timer()
res = np.bincount(bins, wgts)
toc=timeit.default_timer()
toc - tic

tic=timeit.default_timer()
for i in range(nIter):
    res = np.apply_along_axis(np.bincount, 0, bins, wgts)
toc=timeit.default_timer()
toc - tic # 39 seconds

sys.getsizeof(bins)/(1024 ** 2) # 381 Mb

我在 64 位 Windows 桌面、Intel Xeon CPU E5-2680、96GB RAM 上运行 R 3.4.4 和 Python 3.6.1。

我研究过 Python 是否以某种方式缓存计算,但事实并非如此。

我对 data.table 'group' 的计算进行了一些尝试,但我还没有想出一个好的方法来处理多列以进行分组。

在R中,为了检查计算精度,res[1, 1]的值为99967.64

最佳答案

也许用data.table,但是我们需要先改变bins的结构:

require(data.table)
dt <- data.table(bins = as.integer(bins), # integer for reduced size
                    row = rep(1:nrow(bins), ncol(bins)),
                    col = rep(1:ncol(bins), each = nrow(bins)))

现在剩下的:

dt[, wg := wgts[row]] # add wgts for each corresponding row to data.table
rez <- dt[, .(wg_sum = sum(wg)), by = .(col, bins)] # sum by "cols" & bins
rez # your results, only in different structure
# (i would suggest keeping this, if possible)

# if needed can cast to similar structure as your original results:
rezt <- dcast(rez, bins ~ col, value.var = 'wg_sum')

但也许这并不能满足您的需求,因为您提到您还在循环中做其他事情......

只对总和计时 20 次:

tic <- Sys.time()
for (i in (1:nIter)) {
  rez <- dt[, .(wg_sum = sum(wg)), by = .(col, bins)]
}
toc <- Sys.time()
toc - tic # 48.8 45.9 45.9 38.9

不像在 python 中那么快,但是因为我们按 100x5 组对 100e6 个元素求和是有意义的。

# maybe if we split the huge dt before by bins in list:
dtl <- split(dt, by = 'bins')
tic <- Sys.time()
for (i in (1:nIter)) {
  r <- lapply(dtl, function(x) x[, sum(wg), col])
}
toc <- Sys.time()
toc - tic # 18.062

但在这种情况下,您需要在求和后对结果进行不同的处理...

关于python - R 中是否有等效(或更快)版本的 numpy.binCount 用于基于多个 bin 求和值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56494383/

相关文章:

python - TinyDB获取列表查询中的所有ID

Python OR 列表

python - 向内置类型的多重继承子类的构造函数添加可选参数?

r - 在绘图区域外添加文本

algorithm - 在未排序的数组中查找特定比率。时间复杂度

algorithm - 归并排序树状结构

python - 按索引对数组子集的标准差进行多次计算

r - 在封闭件内使用 magrittr 管

r - 如何在 R 4.0.2 中安装 "mxnet"包

c++ - <algorithm> 用对象进行 vector 排序?