Суммирование разницы всех значений одного вектора, которые меньше значений в другом - PullRequest
3 голосов
/ 26 мая 2020

У меня есть следующий код ниже, чтобы попробовать и l oop через последовательность и выбрать значения ниже этих значений в последовательности и найти отличие от другого значения. Для больших наборов данных это может занять много времени. Есть ли способ векторизовать что-то вроде этого без циклического прохождения последовательности для повышения производительности? для вычисления разницы от каждого значения, меньшего, чем значение ячейки.

library(data.table)

min.n <- 1
max.n <- 10 
a <- data.table(seq(min.n, max.n, by=0.5))
colnames(a) <- 'a'
b <- seq(min.n+1, max.n+1, by=1)

bins <- findInterval(a$a,b)
a[,bins:= bins+2]
a[, diff:= bins - a]

Ответы [ 4 ]

3 голосов
/ 26 мая 2020

С data.table это может быть достигнуто путем агрегирования в неэквивалентном соединении :

library(data.table)
data.table(a)[data.table(b), on = .(a <= b), sum(i.b - x.a), by = .EACHI]$V1
[1]   0.0   2.5   9.0  19.5  34.0  52.5  75.0 101.5 132.0 166.5

В некотором смысле, это аналогичен подходу MattB , но объединяет декартово произведение CJ() и подмножество в неэквивалентном соединении , что позволяет избежать создания данных, которые впоследствии будут отфильтрованы.

Обратите внимание, что префикс x. необходим для выбора столбца a из первой таблицы данных.


В качестве альтернативы sum(i.b - x.a) можно переписать как .N * b - sum(x.a), где специальный символ .N обозначает количество строк в группе.

data.table(a)[data.table(b), on = .(a <= b), .N * b - sum(x.a), by = .EACHI]$V1
[1]   0.0   2.5   9.0  19.5  34.0  52.5  75.0 101.5 132.0 166.5
3 голосов
/ 26 мая 2020

Вот вариант использования data.table с использованием скользящего соединения:

library(data.table)
A <- data.table(a, key="a")
B <- data.table(b, key="b")

A[, c("N", "cs") := .(.I, cumsum(a))]

A[B, on=.(a=b), roll=Inf, N * b - cs]

sum a[a <= n] можно заменить на cumsum (т.е. cs здесь), и скользящее соединение найдет те a, которые менее b. Замените sum(n - cs) математической формулой, содержащей символ суммирования, чтобы sum(constant) = количество элементов в суммировании * константа.

вывод:

[1]   0.0   2.5   9.0  19.5  34.0  52.5  75.0 101.5 132.0 166.5

изменить: некоторые тайминги для справки

код времени:

set.seed(0L)
library(data.table)
n <- 1e5L
a <- rnorm(n)
b <- rnorm(n/10L)
A <- data.table(a, key="a")
B <- data.table(b, key="b")

mtd0 <- function() A[B, on = .(a <= b), sum(i.b - x.a), by = .EACHI]$V1

mtd1 <- function() {
    A[, c("N", "cs") := .(.I, cumsum(a))]
    A[B, on=.(a=b), roll=Inf, N * b - cs]
}

all.equal(mtd0(), mtd1())
#[1] TRUE

microbenchmark::microbenchmark(times=1L, mtd0(), mtd1())

время:

Unit: milliseconds
   expr         min          lq        mean      median          uq         max neval
 mtd0() 2998.208000 2998.208000 2998.208000 2998.208000 2998.208000 2998.208000     1
 mtd1()    7.807637    7.807637    7.807637    7.807637    7.807637    7.807637     1
2 голосов
/ 26 мая 2020

Базовое решение R с findInterval, что быстро.

i <- findInterval(b, a)
sapply(seq_along(i), function(j)sum(b[j] - a[1:i[j]]))
# [1]   0.0   2.5   9.0  19.5  34.0  52.5  75.0 101.5 132.0 166.5
1 голос
/ 26 мая 2020

Что-то вроде этого?

library(data.table)
a <- seq(1, 10, by=0.25)
b <- seq(1, 10, by=1)

all.combinations <- CJ(a, b)  # Get all possible combinations
all.combinations[b>=a, sum(b-a), by=b]  # Filter for b>=a, then sum the difference for each value of b
...