python - R 中是否有等效(或更快)版本的 numpy.binCount 用于对基于多个 bin 的值求和?
问题描述
我有一个算法,它需要根据整数'bins'的M×N矩阵中的类别对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 中那么快,但是因为我们将 100e6 个元素按 100x5 个组求和是有意义的。
# 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
但在这种情况下,您需要在求和后对结果进行不同的处理......
推荐阅读
- sql-server - 我们可以在 SSIS 脚本任务或任何替代方案中使用压缩功能吗?
- qt - 如何在 QML 中混合重叠的 SVG
- javascript - 使用 PHP 在 JSON 文件中动态添加 HTML 标记
- html - 如何制作玻璃形态导航栏?
- wordpress - WP_Query -- 如何查询帖子 ID 或标签 slug
- ms-access - MS Access -- 在联合查询中使用 Order By
- nlp - FileNotFoundError:[Errno 2] 没有这样的文件或目录:'./form/form0'
- flutter - 如何在调试控制台中关闭此日志(Flutter,Android Emulator)
- regex - Sed 不匹配反斜杠文字还是我做错了什么?
- spring - 对 spring 项目数据库和 keycloak 数据库使用相同的 postgres 容器