使用 for 循环重复向量中的元素
Repeating elements in a vector with a for loop
我想在 R 中从 3:50 制作一个矢量,看起来像
3 4 4 5 6 6 7 8 8 .. 50 50
我想在 for 循环中使用 for 循环,但它没有按照我的意愿进行。
f <- c()
for (i in 3:50) {
for(j in 1:2) {
f = c(f, i)
}
}
有什么问题吗?
使用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
) 作为索引(在 [
]
内),TRUE
会导致选择相应的元素和一个 FALSE
导致遗漏。如果逻辑索引向量(c(TRUE, FALSE, TRUE, TRUE)
)比索引向量(在您的情况下为rep(3:50, each = 2)
)短,则索引向量将被回收。
另请注意:每当您使用
这样的 R 代码时
x = c(x, something)
或
x = rbind(x, something)
或类似的,您在 R 中采用了 C-like 编程风格。这会使您的代码不必要地复杂,并且如果您使用大型(例如,200MB 以上)可能会导致性能低下和 out-of-memory 问题) 数据集。 R 旨在让您免于 low-level 修补数据结构。
在 R Inferno,第 2 圈:种植物体中阅读有关贪食者及其惩罚的更多信息。
我能找到的最简单的方法是创建另一个仅包含 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
这是一个 loop-free 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 个元素,您可以 pre-allocate 向量(通过使用 f <- vector("numeric",74)
),这样您就不会一次增加 1 个元素。您在每个阶段使用的习语 f <- c(f,i)
复制所有旧元素只是为了创建一个新向量,该向量仅长 1 个元素。这里的元素太少,无法真正发挥作用,但如果您尝试以这种方式创建大型矢量,性能可能会非常糟糕。
另一种选择是使用嵌入式 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
这利用了这样一个事实,即 rep
的 times
参数也可以是等于 x-argument.
长度的整数向量
你可以概括为:
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
这应该做到。
sort(c(3:50, seq(4, 50, 2)))
这是一种结合了其他几个答案的部分方法。
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
这应该很快。
另一个想法,尽管在速度上没有与最快的解决方案竞争:
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
我想在 R 中从 3:50 制作一个矢量,看起来像
3 4 4 5 6 6 7 8 8 .. 50 50
我想在 for 循环中使用 for 循环,但它没有按照我的意愿进行。
f <- c()
for (i in 3:50) {
for(j in 1:2) {
f = c(f, i)
}
}
有什么问题吗?
使用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
) 作为索引(在 [
]
内),TRUE
会导致选择相应的元素和一个 FALSE
导致遗漏。如果逻辑索引向量(c(TRUE, FALSE, TRUE, TRUE)
)比索引向量(在您的情况下为rep(3:50, each = 2)
)短,则索引向量将被回收。
另请注意:每当您使用
这样的 R 代码时 x = c(x, something)
或
x = rbind(x, something)
或类似的,您在 R 中采用了 C-like 编程风格。这会使您的代码不必要地复杂,并且如果您使用大型(例如,200MB 以上)可能会导致性能低下和 out-of-memory 问题) 数据集。 R 旨在让您免于 low-level 修补数据结构。
在 R Inferno,第 2 圈:种植物体中阅读有关贪食者及其惩罚的更多信息。
我能找到的最简单的方法是创建另一个仅包含 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
这是一个 loop-free 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 个元素,您可以 pre-allocate 向量(通过使用 f <- vector("numeric",74)
),这样您就不会一次增加 1 个元素。您在每个阶段使用的习语 f <- c(f,i)
复制所有旧元素只是为了创建一个新向量,该向量仅长 1 个元素。这里的元素太少,无法真正发挥作用,但如果您尝试以这种方式创建大型矢量,性能可能会非常糟糕。
另一种选择是使用嵌入式 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
这利用了这样一个事实,即 rep
的 times
参数也可以是等于 x-argument.
你可以概括为:
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
这应该做到。
sort(c(3:50, seq(4, 50, 2)))
这是一种结合了其他几个答案的部分方法。
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
这应该很快。
另一个想法,尽管在速度上没有与最快的解决方案竞争:
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