Найти все соседние комбинации

У меня есть вектор:

data <- c("ta", "tb", "tc", "tk", "tf")

Как мне преобразовать этот вектор в список, в который включены все возможные комбинации n (где n варьируется от 2 до длины (данные)-1) соседних элементов? Список должен выглядеть следующим образом (каждая строка представляет элемент списка):

"ta", "tb"
"tb", "tc"
"tc", "tk"
"tk", "tf"
"ta", "tb", "tc"
"tb", "tc", "tk"
"tc", "tk", "tf"
"ta", "tb", "tc", "tk"
"tb", "tc", "tk", "tf"

Мне нужно выполнить эту операцию много раз, поэтому важна скорость. Спасибо!


person user1389960    schedule 23.11.2013    source источник
comment
Каков ваш типичный length(data)?   -  person flodel    schedule 23.11.2013


Ответы (3)


Изменить: новая функция

myFun <- function(Data) {
  A <- lapply(2:(length(Data)-1L), sequence)
  B <- lapply(rev(lengths(A))-1L, function(x) c(0, sequence(x)))
  unlist(lapply(seq_along(A), function(x) {
    lapply(B[[x]], function(y) Data[A[[x]]+y])
  }), recursive = FALSE, use.names = FALSE)
}

Применение:

myFun(data)

Оригинальная функция

Вот решение с использованием rollapply из «зоопарка»:

myFun <- function(Data, singles = FALSE) {
  require(zoo)
  x <- 2:(length(Data)-1)
  out <- lapply(x, function(y) rollapply(Data, FUN = c, width = y))
  if (isTRUE(singles)) {
    out <- unlist(lapply(out, function(y) split(y, sequence(nrow(y)))),
                  recursive = FALSE, use.names = FALSE)
  }
  out
}

Если вы хотите разделить вывод вышеуказанного по строке (каждый элемент в новом элементе списка), используйте аргумент singles = TRUE:

myFun(data, singles = TRUE)
# [[1]]
# [1] "ta" "tb"
# 
# [[2]]
# [1] "tb" "tc"
# 
#######
# SNIP
#######
# 
# [[8]]
# [1] "ta" "tb" "tc" "tk"
# 
# [[9]]
# [1] "tb" "tc" "tk" "tf"
person A5C1D2H2I1M1N2O1R2T1    schedule 23.11.2013

Вот способ использования функции vecseq data.table. По сути, vecseq(x, y, clamp) берет для каждого числа в x значение y из соответствующего индекса и строит последовательность x:(x+len). То есть vecseq(c(1L, 4L), c(2L, 6L), 5L) создает последовательность c(1L, 2L, 4L, 5L, 6L). Аргумент clamp — это просто аргумент, который должен быть >= длины ответа. Если вы не знаете, вы можете просто назвать относительно большое число.

Идея состоит в том, чтобы сгенерировать индексы, используя vecseq, а затем разделить, чтобы получить соответствующий список. Я надеюсь, что при выполнении приведенного ниже кода все должно быть очевидно (с небольшими усилиями :)).

data <- c("ta", "tb", "tc", "tk", "tf")
require(data.table)
ff <- data.table:::vecseq
my_fun <- function(data) {
    xmin = 2L
    xmax = length(data)-1L
    len = xmax-xmin+1L
    tot = sum(xmax:xmin)

    t1 = ff(rep(1L, len), xmax:xmin, tot)
    t2 = rep.int(xmin:xmax, xmax:xmin)
    idx = ff(t1,t2,sum(t2))
    dt = data.table(x=data[idx], id=rep.int(seq_along(t2), t2))
    setattr(dt, 'sorted', 'id')
    dt[J(seq_along(t2)), list(list(x))]$V1
}

Это кажется довольно быстрым и соответствует (отличному) ответу @flodel. Когда длина данных достигает около 250, разница составляет около 0,2 секунды (это решение быстрее). Так что особой разницы нет.

person Arun    schedule 23.11.2013

Вот способ:

adj.poss <- function(x) {
  n <- length(x)
  stopifnot(n > 2L)
  idx <- expand.grid(start = 1L:n, len = 2L:(n-1L))
  idx$end <- idx$start + idx$len - 1L
  idx <- idx[idx$end <= n, ]
  Map(function(start, end) x[start:end], idx$start, idx$end)
}

где adj.poss(data) дает ожидаемый результат в том же порядке, в котором вы указали.

person flodel    schedule 23.11.2013
comment
Я пошел с ответом foldel. Для моих данных (типичная длина ‹ 10) это было самое быстрое решение. - person user1389960; 23.11.2013