Сначала запустите функцию с аргументом переменной x
и ссылкой table
и n
после
.nearest_n <- function(x, table, n) {
Алгоритм предполагает, что table
является числовым, без дубликатов и все значения конечны; n
должно быть меньше или равно длине таблицы
## assert & setup
stopifnot(
is.numeric(table), !anyDuplicated(table), all(is.finite(table)),
n <= length(table)
)
Сортировка таблицы и затем фиксирование максимальных и минимальных значений
## sort and clamp
table <- c(-Inf, sort(table), Inf)
len <- length(table)
Найдите интервал в table
, где встречается x
; findInterval()
использует эффективный поиск. Используйте индекс интервала в качестве начального нижнего индекса и добавьте 1 к верхнему индексу, оставаясь в пределах.
## where to start?
lower <- findInterval(x, table)
upper <- min(lower + 1L, len)
Найдите ближайших соседей n
, сравнив нижнее и верхнее индексное расстояние с x
, запишите ближайшее значение и, при необходимости, увеличьте нижний или верхний индекс и убедитесь, что они находятся в пределах
## find
nearest <- numeric(n)
for (i in seq_len(n)) {
if (abs(x - table[lower]) < abs(x - table[upper])) {
nearest[i] = table[lower]
lower = max(1L, lower - 1L)
} else {
nearest[i] = table[upper]
upper = min(len, upper + 1L)
}
}
Затем верните решение и завершите функцию
nearest
}
Код может показаться многословным, но на самом деле он относительно эффективен, поскольку единственные операции над всем вектором (sort()
, findInterval()
) эффективно реализованы в R.
Особое преимущество этого подхода заключается в том, что он может быть векторизован в своем первом аргументе, вычисляя тест для использования нижнего (use_lower = ...
) в качестве вектора и используя pmin()
/ pmax()
в качестве зажимов.
.nearest_n <- function(x, table, n) {
## assert & setup
stopifnot(
is.numeric(table), !anyDuplicated(table), all(is.finite(table)),
n <= length(table)
)
## sort and clamp
table <- c(-Inf, sort(table), Inf)
len <- length(table)
## where to start?
lower <- findInterval(x, table)
upper <- pmin(lower + 1L, len)
## find
nearest <- matrix(0, nrow = length(x), ncol = n)
for (i in seq_len(n)) {
use_lower <- abs(x - table[lower]) < abs(x - table[upper])
nearest[,i] <- ifelse(use_lower, table[lower], table[upper])
lower[use_lower] <- pmax(1L, lower[use_lower] - 1L)
upper[!use_lower] <- pmin(len, upper[!use_lower] + 1L)
}
# return
nearest
}
Например
> set.seed(123)
> table <- sample(100, 10)
> sort(table)
[1] 5 29 41 42 50 51 79 83 86 91
> .nearest_n(c(30, 20), table, 4)
[,1] [,2] [,3] [,4]
[1,] 29 41 42 50
[2,] 29 5 41 42
Обобщите это, взяв любой аргумент и приведя его к требуемой форме, используя справочную справочную таблицу table0
и индексы в нее table1
nearest_n <- function(x, table, n) {
## coerce to common form
table0 <- sort(unique(c(x, table)))
x <- match(x, table0)
table1 <- match(table, table0)
## find nearest
m <- .nearest_n(x, table1, n)
## result in original form
matrix(table0[m], nrow = nrow(m))
}
Как пример ...
> set.seed(123)
> table <- sample(c(letters, LETTERS), 30)
> nearest_n(c("M", "Z"), table, 5)
[,1] [,2] [,3] [,4] [,5]
[1,] "o" "L" "O" "l" "P"
[2,] "Z" "z" "Y" "y" "w"