Существует ли эквивалентная (или более быстрая) версия numpy.binCount в R для суммирования значений, основанных на нескольких бинах? - PullRequest
0 голосов
/ 07 июня 2019

У меня есть алгоритм, который требует, чтобы изменяющийся вектор 'wgts' из N чисел суммировался в соответствии с категориями в матрице M-by-N целых чисел 'bin'.Например, если начальные значения «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))]

Мне нужно повторить этот процесс с матрицей фиксированной категории 'bin' и изменением вектора 'wgts'.Поскольку M и N становятся большими, этот процесс занимает много времени.Я обнаружил, что пакет Python Numpy имеет полезную и быструю привязку функций для этого.Я хотел бы использовать R, поскольку остальная часть моего процесса находится в R, но пока я не могу исправить свой алгоритм в R так же быстро, как в Python

Подход в 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

Я использую R 3.4.4 и Python 3.6.1 на 64-разрядном компьютере под управлением Windows, процессор Intel XeonE5-2680, 96 ГБ ОЗУ.

Я смотрел, как Python как-то кеширует вычисления, но это не так.

Я немного поиграл с data.table 'group' вычислениями, но я не нашел хорошего способа обработки нескольких столбцов для группировки.

В R для проверки точности вычислений значение res [1, 1] равно 99967.64

1 Ответ

0 голосов
/ 07 июня 2019

может быть с data.table, но нам нужно изменить структуру бинов раньше:

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

, но в этом случае вам нужно по-разному относиться к результатам после суммирования ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...