Выберите ближайшие x элементов по индексу в списке / векторе - PullRequest
0 голосов
/ 01 мая 2018

Если у меня есть такой вектор, как x <-c(1,2,3,4,5,6,7,8,9), я хочу функцию f такую, чтобы f(vector,index,num) где он берет вектор и дает мне num "ближайших" элементов к этому в индексе Примеры: f(x,3,4) = c(1,2,4,5) f(x,1,5) = c(2,3,4,5,6) f(x,8,3) = c(6,7,9)

Так как существует проблема, когда у нас есть нечетное число, нам нужно будет выбрать, выбирать ли левую или правую сторону по симметрии, давайте перейдем к выбору левой стороны (но с правой стороной тоже все в порядке) т.е. f(x,4,5) = c(1,2,3,5,6) and f(x,7,3) = c(5,6,8)

Надеюсь, мой вопрос ясен, спасибо за любую помощь / ответы!

edit: исходный вектор c(1:9) является произвольным, вектор может быть вектором строк или вектором длины 1000 с перемешанными числами с повторениями и т. Д.

т.е. c(1,7,4,2,3,7,2,6,234,56,8)

Ответы [ 3 ]

0 голосов
/ 01 мая 2018

Вроде так:

f <- function (vec, elem, n) {
  elems <- seq(elem - ceiling(n/2), elem + floor(n/2))
  if (max(elems) > length(vec)) elems <- elems - (max(elems) - length(vec))
  if (elems[1] < 1) elems <- elems + (1 - elems[1])
  elems <- setdiff(elems, elem)
  vec[elems]
}

Дать результаты:

> f(1:9, 1, 5)
[1] 2 3 4 5 6
> f(1:9, 9, 5)
[1] 4 5 6 7 8
> f(1:9, 2, 5)
[1] 1 3 4 5 6
> f(1:9, 4, 5)
[1] 1 2 3 5 6
> f(1:9, 4, 4)
[1] 2 3 5 6
> f(1:9, 2, 4)
[1] 1 3 4 5
> f(1:9, 1, 4)
[1] 2 3 4 5
> f(1:9, 9, 4)
[1] 5 6 7 8
0 голосов
/ 01 мая 2018

Сначала запустите функцию с аргументом переменной 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" 
0 голосов
/ 01 мая 2018
num_closest_by_indices <- function(v, idx, num) {
  # Try the base case, where idx is not within (num/2) of the edge
  i <- abs(seq_along(x) - idx)
  i[idx] <- +Inf # sentinel

  # If there are not enough elements in the base case, incrementally add more
  for (cutoff_idx in seq(floor(num/2), num)) {
    if (sum(i <= cutoff_idx) >= num) {
      # This will add two extra indices every iteration. Strictly if we have an even length, we should add the leftmost one first and `continue`, to break ties towards the left.
      return(v[i <= cutoff_idx])
    }
  }
} 

Вот иллюстрация этого алгоритма: мы ранжируем индексы в порядке желательности, затем выбираем самые низкие num допустимые:

> seq_along(x)
  1 2 3 4 5 6 7 8 9
> seq_along(x) - idx
  -2 -1  0  1  2  3  4  5  6
> i <- abs(seq_along(x) - idx)
   2  1  0  1  2  3  4  5  6
> i[idx] <- +Inf # sentinel to prevent us returning the element itself
   2   1 Inf   1   2   3   4   5   6

Теперь мы можем просто найти num элементов с наименьшими значениями (разрывать связи произвольно, если у вас нет предпочтения (слева)). Наше первое предположение - все индексы <= (num / 2); этого может быть недостаточно, если <code>index находится в пределах (num/2) от начала / конца.

> i <= 2
  TRUE  TRUE FALSE  TRUE  TRUE FALSE FALSE FALSE FALSE
> v[i <= 2]
  1 2 4 5

Итак, адаптируем код @ dash2 для обработки угловых случаев, когда некоторые индексы недопустимы (неположительные или> length (x)), т. Е. ! %in% 1:L. Тогда min(elems) будет числом недопустимых индексов, которые мы не можем выбрать, поэтому мы должны выбрать abs(min(elems)) больше.

Примечания:

  • В конце код становится проще и быстрее обрабатывать его в трех кусочных случаях. Aww.
  • На самом деле кажется, что все упрощается, если мы выберем (num+1) индексы, а затем удалим idx перед возвратом ответа. Используйте result[-idx], чтобы удалить его.
...