Быстрое минимальное расстояние (интервал) между элементами 2 логических векторов (дубль 2)

Я задал связанный с этим вопрос здесь, но понял, что слишком много времени уделяю вычислению этой сложной меры (и цель состоит в том, чтобы использовать с рандомизированным тестом, чтобы скорость была проблемой). Поэтому я решил сбросить вес и просто использовать минимальное расстояние между двумя мерами. Так что здесь у меня есть 2 вектора (в кадре данных для демонстрационных целей, но на самом деле это два вектора.

       x     y
1  FALSE  TRUE
2  FALSE FALSE
3   TRUE FALSE
4  FALSE FALSE
5  FALSE  TRUE
6  FALSE FALSE
7  FALSE FALSE
8   TRUE FALSE
9  FALSE  TRUE
10  TRUE  TRUE
11 FALSE FALSE
12 FALSE FALSE
13 FALSE FALSE
14 FALSE  TRUE
15  TRUE FALSE
16 FALSE FALSE
17  TRUE  TRUE
18 FALSE  TRUE
19 FALSE FALSE
20 FALSE  TRUE
21 FALSE FALSE
22 FALSE FALSE
23 FALSE FALSE
24 FALSE FALSE
25  TRUE FALSE

Здесь у меня есть некоторый код, разработанный для нахождения минимального расстояния, но мне нужно больше скорости (удаление ненужных вызовов и лучшая векторизация). Может быть, я не могу идти быстрее на базе R.

## MWE EXAMPLE: THE DATA
x <- y <- rep(FALSE, 25)
x[c(3, 8, 10, 15, 17, 25)] <- TRUE
y[c(1, 5, 9, 10, 14, 17, 18, 20)] <- TRUE

## Code to Find Distances
xw <- which(x)
yw <- which(y)

min_dist <- function(xw, yw) {
    unlist(lapply(xw, function(x) {
        min(abs(x - yw))
    }))
}

min_dist(xw, yw)

Есть ли способ улучшить производительность в базе R? С помощью dplyr или же data.table?

Мои векторы намного длиннее (10000 + элементов).

Отредактируйте за скамью Флоделя. flodel есть проблема, которую я ожидал в своем MWE, и я не уверен, как ее исправить. Проблема возникает, если любая позиция x меньше минимальной позиции y.

x <- y <- rep(FALSE, 25)
x[c(3, 8, 9, 15, 17, 25)] <- TRUE
y[c(5, 9, 10, 13, 15, 17, 19)] <- TRUE


xw <- which(x)
yw <- which(y)

flodel <- function(xw, yw) {
   i <- findInterval(xw, yw)
   pmin(xw - yw[i], yw[i+1L] - xw, na.rm = TRUE)
}

flodel(xw, yw)

## [1] -2 -1 -6 -2 -2 20
## Warning message:
## In xw - yw[i] :
##   longer object length is not a multiple of shorter object length

2 ответа

Решение
flodel <- function(x, y) {
  xw <- which(x)
  yw <- which(y)
  i <- findInterval(xw, yw, all.inside = TRUE)
  pmin(abs(xw - yw[i]), abs(xw - yw[i+1L]), na.rm = TRUE)
}

GG1 <- function(x, y) {
  require(zoo)
  yy <- ifelse(y, TRUE, NA) * seq_along(y)
  fwd <- na.locf(yy, fromLast = FALSE)[x]
  bck <- na.locf(yy, fromLast = TRUE)[x]
  wx <- which(x)
  pmin(wx - fwd, bck - wx, na.rm = TRUE)
}

GG2 <- function(x, y) {
  require(data.table)
  dtx <- data.table(x = which(x))
  dty <- data.table(y = which(y), key = "y")
  dty[dtx, abs(x - y), roll = "nearest"] 
}

Пример данных:

x <- y <- rep(FALSE, 25)
x[c(3, 8, 10, 15, 17, 25)] <- TRUE
y[c(1, 5, 9, 10, 14, 17, 18, 20)] <- TRUE

X <- rep(x, 100)
Y <- rep(y, 100)

Модульный тест:

identical(flodel(X, Y), GG1(X, Y))
# [1] TRUE

тесты:

library(microbenchmark)
microbenchmark(flodel(X,Y), GG1(X,Y), GG2(X,Y))
# Unit: microseconds
#          expr       min         lq     median        uq        max neval
#  flodel(X, Y)   115.546   131.8085   168.2705   189.069   1980.316   100
#     GG1(X, Y)  2568.045  2828.4155  3009.2920  3376.742  63870.137   100
#     GG2(X, Y) 22210.708 22977.7340 24695.7225 28249.410 172074.881   100

[Отредактировано Мэттом Доулом] 24695 микросекунд = 0,024 секунды. Выводы, сделанные на микробенчмарках с крошечными данными, редко содержат значимые размеры данных.

[Редактировать flodel] Мои векторы имели длину 2500, что было довольно значимым, учитывая утверждение Тайлера (10k), но хорошо, давайте попробуем с векторами длины 2.5e7. Я надеюсь, что вы простите меня за использование system.time учитывая обстоятельства:

X <- rep(x, 1e6)
Y <- rep(y, 1e6)
system.time(flodel(X,Y))
#    user  system elapsed 
#   0.694   0.205   0.899 
system.time(GG1(X,Y))
#    user  system elapsed 
#  31.250  16.496 112.967 
system.time(GG2(X,Y))
# Error in `[.data.table`(dty, dtx, abs(x - y), roll = "nearest") : 
#   negative length vectors are not allowed

[Правка из Аруна] - Тест для 2.5e7 с использованием 1.8.11:
[Редактировать 2 из Аруна] - Обновление времени после недавнего более быстрого двоичного поиска / слияния Мэтта

require(data.table)
arun <- function(x, y) {
    dtx <- data.table(x=which(x))
    setattr(dtx, 'sorted', 'x')
    dty <- data.table(y=which(y))
    setattr(dty, 'sorted', 'y')
    dty[, y1 := y]
    dty[dtx, roll="nearest"][, abs(y-y1)]
}

# minimum of three consecutive runs
system.time(ans1 <- arun(X,Y))
#   user  system elapsed 
#  1.036   0.138   1.192 

# minimum of three consecutive runs
system.time(ans2 <- flodel(X,Y))
#   user  system elapsed 
#  0.983   0.197   1.221 

identical(ans1, ans2) # [1] TRUE

Вот два решения. Ни используйте цикл, ни примените функцию.

1) Первое совпадает с решением, которое я разместил на ваш предыдущий вопрос, если z равно 1, за исключением того, что упрощенные предположения позволяют нам несколько сократить его, и мы сократили ответ на 1 относительно этого.

library(zoo)

yy <- ifelse(y, TRUE, NA) * seq_along(y)
fwd <- na.locf(yy, fromLast = FALSE)[x]
bck <- na.locf(yy, fromLast = TRUE)[x]
wx <- which(x)
pmin(wx - fwd, bck - wx, na.rm = TRUE)

2) Второе решение - data.table. data.table может занять roll="nearest" аргумент, который кажется именно то, что вам нужно:

library(data.table)

dtx <- data.table(x = which(x))
dty <- data.table(y = which(y), key = "y")
dty[dtx, abs(x - y), roll = "nearest"] 

Я не уверен, если это имеет значение, но я использую data.table версии 1.8.11 (версия CRAN в настоящее время 1.8.10).

Другие вопросы по тегам