Подтвердить что ты не робот

Вставить элементы в вектор в R

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

a = c(2,3,4,9,10,2,4,19)

скажем, я хочу эффективно вставить следующие векторы, b и c,

b = c(2,1)
d = c(0,1)

в позициях 3 и 7, в результате чего

e = c(2,3,4,2,1,9,10,2,4,0,1,19)

Как бы я сделал это эффективно в R, без рекурсивного использования cbind или так.

Я нашел пакет R.basic, но его не было частью пакетов CRAN, поэтому я подумал об использовании поддерживаемой версии.

4b9b3361

Ответ 1

Попробуйте следующее:

result <- vector("list",5)
result[c(TRUE,FALSE)] <- split(a, cumsum(seq_along(a) %in% (c(3,7)+1)))
result[c(FALSE,TRUE)] <- list(b,d)
f <- unlist(result)

identical(f, e)
#[1] TRUE

EDIT: обобщение на произвольное число вставок является простым:

insert.at <- function(a, pos, ...){
    dots <- list(...)
    stopifnot(length(dots)==length(pos))
    result <- vector("list",2*length(pos)+1)
    result[c(TRUE,FALSE)] <- split(a, cumsum(seq_along(a) %in% (pos+1)))
    result[c(FALSE,TRUE)] <- dots
    unlist(result)
}


> insert.at(a, c(3,7), b, d)
 [1]  2  3  4  2  1  9 10  2  4  0  1 19

> insert.at(1:10, c(4,7,9), 11, 12, 13)
 [1]  1  2  3  4 11  5  6  7 12  8  9 13 10

> insert.at(1:10, c(4,7,9), 11, 12)
Error: length(dots) == length(pos) is not TRUE

Обратите внимание на бонусную проверку, если количество позиций и вставок не совпадает.

Ответ 2

Вы можете использовать следующую функцию,

ins(a, list(b, d), pos=c(3, 7))
# [1]  2  3  4  2  1  9 10  2  4  0  1  4 19

где:

ins <- function(a, to.insert=list(), pos=c()) {

  c(a[seq(pos[1])], 
    to.insert[[1]], 
    a[seq(pos[1]+1, pos[2])], 
    to.insert[[2]], 
    a[seq(pos[2], length(a))]
    )
}

Ответ 3

Здесь другая функция, используя синтаксис Рикардо, раскол Фердинанда и @Arun чередование трюка из другого вопроса:

ins2 <- function(a,bs,pos){
    as <- split(a,cumsum(seq(a)%in%(pos+1)))
    idx <- order(c(seq_along(as),seq_along(bs)))
    unlist(c(as,bs)[idx])
}

Преимущество заключается в том, что это должно распространяться на большее количество вставок. Однако он может вызывать странный вывод при передаче недопустимых аргументов, например, с помощью any(pos > length(a)) или length(bs)!=length(pos).

Вы можете изменить последнюю строку на unname(unlist(..., если вы не хотите, чтобы элементы были названы.

Ответ 4

Прямой подход:

b.pos <- 3
d.pos <- 7
c(a[1:b.pos],b,a[(b.pos+1):d.pos],d,a[(d.pos+1):length(a)])
[1]  2  3  4  2  1  9 10  2  4  0  1 19

Обратите внимание на важность скобок для границ оператора :.

Ответ 5

После использования функции Фердинанда я попытался написать свой собственный, и, на удивление, это намного эффективнее. Здесь моя:

insertElems = function(vect, pos, elems) {

l = length(vect)
  j = 0
  for (i in 1:length(pos)){
    if (pos[i]==1)
      vect = c(elems[j+1], vect)
    else if (pos[i] == length(vect)+1)
      vect = c(vect, elems[j+1])
    else
      vect = c(vect[1:(pos[i]-1+j)], elems[j+1], vect[(pos[i]+j):(l+j)])
    j = j+1
  }
  return(vect)
}

tmp = c(seq(1:5))
insertElems(tmp, c(2,4,5), c(NA,NA,NA))
# [1]  1 NA  2  3 NA  4 NA  5

insert.at(tmp, c(2,4,5), c(NA,NA,NA))
# [1]  1 NA  2  3 NA  4 NA  5

И там результат теста:

> microbenchmark(insertElems(tmp, c(2,4,5), c(NA,NA,NA)), insert.at(tmp, c(2,4,5), c(NA,NA,NA)), times = 10000)
Unit: microseconds
                                        expr    min     lq     mean median     uq      max neval
 insertElems(tmp, c(2, 4, 5), c(NA, NA, NA))  9.660 11.472 13.44247  12.68 13.585 1630.421 10000
   insert.at(tmp, c(2, 4, 5), c(NA, NA, NA)) 58.866 62.791 70.36281  64.30 67.923 2475.366 10000

мой код работает еще лучше для некоторых случаев:

> insert.at(tmp, c(1,4,5), c(NA,NA,NA))
# [1]  1  2  3 NA  4 NA  5 NA  1  2  3
# Warning message:
# In result[c(TRUE, FALSE)] <- split(a, cumsum(seq_along(a) %in% (pos))) :
#   number of items to replace is not a multiple of replacement length

> insertElems(tmp, c(1,4,5), c(NA,NA,NA))
# [1] NA  1  2  3 NA  4 NA  5

Ответ 6

Здесь альтернатива, использующая append. Это прекрасно для небольших векторов, но я не могу себе представить, что он эффективен для больших векторов, так как новый вектор создается на каждой итерации цикла (что, очевидно, плохо). Хитрость заключается в том, чтобы перевернуть вектор вещей, которые нужно вставить, чтобы получить append, чтобы вставить их в нужное место относительно исходного вектора.

a = c(2,3,4,9,10,2,4,19)
b = c(2,1)
d = c(0,1)

pos <- c(3, 7)
z <- setNames(list(b, d), pos)
z <- z[order(names(z), decreasing=TRUE)]


for (i in seq_along(z)) {
  a <- append(a, z[[i]], after = as.numeric(names(z)[[i]]))
}

a
#  [1]  2  3  4  2  1  9 10  2  4  0  1 19