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




создать пустой вектор в r (5)

У меня есть вектор в 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, поэтому я подумал об использовании поддерживаемой версии.


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

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

Вот альтернатива, которая использует 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

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

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(... если вы не хотите, чтобы элементы a были названы.


Попробуй это:

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

РЕДАКТИРОВАТЬ: обобщение на произвольное количество вставок просто:

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

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


Простой подход:

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

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





r