Повторяющиеся элементы в векторе с циклом for

Я хочу сделать вектор из 3:50 в R, глядя, как

3 4 4 5 6 6 7 8 8 .. 50 50

Я хочу использовать цикл for В цикле for, но он не делает wat, который я хочу.

f <- c()
for (i in 3:50) {
  for(j in 1:2) {
    f = c(f, i)
  }
}

что с ним не так?

8 ответов


использовать rep функция, наряду с возможностью использования рециркуляции логического индексирования ...[c(TRUE, FALSE, TRUE, TRUE)]

rep(3:50, each = 2)[c(TRUE, FALSE, TRUE, TRUE)]

 ## [1]  3  4  4  5  6  6  7  8  8  9 10 10 11 12 12 13 14 14 15 16 16 17 18 18 19
## [26] 20 20 21 22 22 23 24 24 25 26 26 27 28 28 29 30 30 31 32 32 33 34 34 35 36
## [51] 36 37 38 38 39 40 40 41 42 42 43 44 44 45 46 46 47 48 48 49 50 50

если вы используете логический вектор (TRUE/FALSE) как индекс (внутри [ ]), a TRUE приводит к выбору соответствующего элемента и FALSE ведет к бездействию. Если вектор логического индекса (c(TRUE, FALSE, TRUE, TRUE)) короче индексированного вектора (rep(3:50, each = 2) в вашем случае), вектор индекса recyled.

также боковое Примечание: всякий раз, когда вы используйте код R, например

 x = c(x, something)

или

 x = rbind(x, something)

или аналогично, вы принимаете C-подобный стиль программирования в R. Это делает ваш код unnessecarily сложным и может привести к низкой производительности и проблемам с памятью, если вы работаете с большими (скажем, 200 МБ+) наборами данных. R предназначен, чтобы избавить вас от этих низкоуровневых возиться со структурами данных.

Читайте для получения дополнительной информации о обжорах и их наказании в R Inferno, круг 2: Растущие Объекты.


другой вариант-использовать встроенный rep:

rep(3:50, rep(1:2, 24))

что дает:

 [1]  3  4  4  5  6  6  7  8  8  9 10 10 11 12 12 13 14 14 15 16 16 17 18 18 19 20 20
[28] 21 22 22 23 24 24 25 26 26 27 28 28 29 30 30 31 32 32 33 34 34 35 36 36 37 38 38
[55] 39 40 40 41 42 42 43 44 44 45 46 46 47 48 48 49 50 50

это использует тот факт, что times-аргумент rep также может быть числом, вектором, который равен длине х-аргумент.

вы можете обобщить это на:

s <- 3
e <- 50
v <- 1:2

rep(s:e, rep(v, (e-s+1)/2))

еще один вариант, используя сочетание rep и rep_len:

v <- 3:50
rep(v, rep_len(1:2, length(v)))

решение на основе sapply.

as.vector(sapply(0:23 * 2 + 2, function(x)  x + c(1, 2, 2)))

# [1]  3  4  4  5  6  6  7  8  8  9 10 10 11 12 12 13 14 14 15 16 16 17 18 18 19 20 20 21 22 22 23 24 24 25 26 26
# [37] 27 28 28 29 30 30 31 32 32 33 34 34 35 36 36 37 38 38 39 40 40 41 42 42 43 44 44 45 46 46 47 48 48 49 50 50

бенчмаркинг

вот сравнение производительности для всех текущих ответов. Результат показывает, что cumsum(rep(c(1, 1, 0), 24)) + 2L (m8) является самым быстрым, в то время как rep(3:50, rep(1:2, 24))(m1) это почти так же быстро, как m8.

library(microbenchmark)
library(ggplot2)

perf <- microbenchmark(
  m1 = {rep(3:50, rep(1:2, 24))},
  m2 = {rep(3:50, each = 2)[c(TRUE, FALSE, TRUE, TRUE)]},
  m3 = {v <- 3:50; sort(c(v,v[v %% 2 == 0]))},
  m4 = {as.vector(t(cbind(seq(3,49,2),seq(4,50,2),seq(4,50,2))))},
  m5 = {as.vector(sapply(0:23 * 2 + 2, function(x)  x + c(1, 2, 2)))},
  m6 = {sort(c(3:50, seq(4, 50, 2)))},
  m7 = {rep(seq(3, 50, 2), each=3) + c(0, 1, 1)},
  m8 = {cumsum(rep(c(1, 1, 0), 24)) + 2L},
  times = 10000L
)

perf
# Unit: nanoseconds
# expr   min    lq      mean median    uq     max neval
#   m1   514  1028  1344.980   1029  1542  190200 10000
#   m2  1542  2570  3083.716   3084  3085  191229 10000
#   m3 26217 30329 35593.596  31871 34442 5843267 10000
#   m4 43180 48321 56988.386  50891 55518 6626173 10000
#   m5 30843 35984 42077.543  37526 40611 6557289 10000
#   m6 40611 44209 50092.131  46779 50891  446714 10000
#   m7 13879 16449 19314.547  17478 19020 6309001 10000
#   m8     0  1028  1256.715   1028  1542   71454 10000

самый простой способ, который я могу найти, - это создать другой, содержащий только even значения (на основе намерения OP), а затем просто объединяют два вектора. Примером может быть:

v <- 3:50
sort(c(v,v[v %% 2 == 0]))

# [1]  3  4  4  5  6  6  7  8  8  9 10 10 11 12 12 13 14 14 15 16 16
#      17 18 18 19 20 20 21 22 22 23 24 24 25 26 26 27 28 28
#[40] 29 30 30 31 32 32 33 34 34 35 36 36 37 38 38 39 40 40 41 42 42
#     43 44 44 45 46 46 47 48 48 49 50 50

вот решение без петли 1 линия:

> as.vector(t(cbind(seq(3,49,2),seq(4,50,2),seq(4,50,2))))
 [1]  3  4  4  5  6  6  7  8  8  9 10 10 11 12 12 13 14 14 15 16 16 17
[23] 18 18 19 20 20 21 22 22 23 24 24 25 26 26 27 28 28 29 30 30 31 32
[45] 32 33 34 34 35 36 36 37 38 38 39 40 40 41 42 42 43 44 44 45 46 46
[67] 47 48 48 49 50 50

он формирует матрицу, первый столбец которой является нечетными числами в диапазоне 3: 50 и чьи второй и третий столбцы являются четными числами в этом диапазоне, а затем (взяв транспонирование) считывает его строка за строкой.

проблема с вашим вложенным циклом заключается в том, что фундаментальный шаблон имеет длину 3, повторяемую 24 раза (вместо шаблона длины 2, повторяемого 50 раз). Если вы хотите использовать вложенный цикл, внешний цикл может повторяться 24 раза, а внутренний-3. Первый проход через внешнюю петлю мог построить 3,4,4. Второй проход мог бы построить 5,6,6. Так далее. Поскольку есть 24*3 = 72 элемента, вы можете предварительно выделить вектор (используя f <- vector("numeric",74)) Так что вы не растете его по 1 элементу за раз. Идиома f <- c(f,i) что вы используете на каждом этапе копирует все старые элементы только для создания нового вектора, который только на 1 элемент длиннее. Здесь слишком мало элементов для того, чтобы действительно сделайте разницу, но если вы попытаетесь создать большие векторы таким образом, производительность может быть потрясающе плохой.


вот метод, который объединяет части нескольких других ответов.

rep(seq(3, 50, 2), each=3) + c(0, 1, 1)
 [1]  3  4  4  5  6  6  7  8  8  9 10 10 11 12 12 13 14 14 15 16
[21] 16 17 18 18 19 20 20 21 22 22 23 24 24 25 26 26 27 28 28 29
[41] 30 30 31 32 32 33 34 34 35 36 36 37 38 38 39 40 40 41 42 42
[61] 43 44 44 45 46 46 47 48 48 49 50 50

вот второй метод с использованием cumsum

cumsum(rep(c(1, 1, 0), 24)) + 2L

Это должно быть очень быстро.


Это слишком.

sort(c(3:50, seq(4, 50, 2)))

еще одна идея, хотя и не конкурирующая в скорости с самыми быстрыми решениями:

mat <- matrix(3:50,nrow=2)
c(rbind(mat,mat[2,]))
# [1]  3  4  4  5  6  6  7  8  8  9 10 10 11 12 12 13 14 14 15 16 16 17 18 18 19 20 20 21 22 22
# [31] 23 24 24 25 26 26 27 28 28 29 30 30 31 32 32 33 34 34 35 36 36 37 38 38 39 40 40 41 42 42
# [61] 43 44 44 45 46 46 47 48 48 49 50 50