创建一系列数字的序列集合

21

我希望在R中使用rep函数或其他函数按照以下顺序进行操作。

c(1, 2, 3, 4, 5, 2, 3, 4, 5, 3, 4, 5, 4, 5, 5)

基本上,c(1:5, 2:5, 3:5, 4:5, 5:5)

4个回答

37

使用sequence

sequence(5:1, from = 1:5)
[1] 1 2 3 4 5 2 3 4 5 3 4 5 4 5 5

第一个参数nvec表示每个序列的长度(例如:5:1),第二个参数from表示每个序列的起始点(例如:1:5)。

注意:这仅适用于R版本大于或等于4.0.0。来自R News 4.0.0的介绍:

sequence() [...]增加了参数[例如from],以生成更复杂的序列。


2
@Henrik 很久以前有一个非常类似的问题,使用了 sequence 进行了回答:https://stackoverflow.com/a/67887135/9463489 - jblood94

8
unlist(lapply(1:5, function(i) i:5))
# [1] 1 2 3 4 5 2 3 4 5 3 4 5 4 5 5

对所有提供的答案进行速度测试 请注意,如果我没有记错,OP在某处提到了10K

s1 <- function(n) { 
  unlist(lapply(1:n, function(i) i:n))
}

s2 <- function(n) {
  unlist(lapply(seq_len(n), function(i) seq(from = i, to = n, by = 1)))
}

s3 <- function(n) {
  vect <- 0:n
  unlist(replicate(n, vect <<- vect[-1]))
}

s4 <- function(n) {
  m <- matrix(1:n, ncol = n, nrow = n, byrow = TRUE)
  m[lower.tri(m)] <- 0
  c(t(m)[t(m != 0)])
}

s5 <- function(n) {
  m <- matrix(seq.int(n), ncol = n, nrow = n)
  m[lower.tri(m, diag = TRUE)]
}

s6 <- function(n) {
  out <- c()
  for (i in 1:n) { 
    out <- c(out, (1:n)[i:n])
  }
  out
}

library(rbenchmark)

n = 5

n = 5L

benchmark(
  "s1" = { s1(n) },
  "s2" = { s2(n) },
  "s3" = { s3(n) },
  "s4" = { s4(n) },
  "s5" = { s5(n) },
  "s6" = { s6(n) },
  replications = 1000,
  columns = c("test", "replications", "elapsed", "relative")
)

不要被一些“快速”解决方案所欺骗,这些解决方案几乎没有需要调用时间的函数,而且差异会被1000倍的复制放大。

  test replications elapsed relative
1   s1         1000    0.05      2.5
2   s2         1000    0.44     22.0
3   s3         1000    0.14      7.0
4   s4         1000    0.08      4.0
5   s5         1000    0.02      1.0
6   s6         1000    0.02      1.0

n = 1000

n = 1000L

benchmark(
  "s1" = { s1(n) },
  "s2" = { s2(n) },
  "s3" = { s3(n) },
  "s4" = { s4(n) },
  "s5" = { s5(n) },
  "s6" = { s6(n) },
  replications = 10,
  columns = c("test", "replications", "elapsed", "relative")
)

如楼主所说的“不要使用”的方法,我们发现在 n = 1000L 的情况下,for 循环明显比其它方法慢。

  test replications elapsed relative
1   s1           10    0.17    1.000
2   s2           10    0.83    4.882
3   s3           10    0.19    1.118
4   s4           10    1.50    8.824
5   s5           10    0.29    1.706
6   s6           10   28.64  168.471

n = 10000

n = 10000L

benchmark(
  "s1" = { s1(n) },
  "s2" = { s2(n) },
  "s3" = { s3(n) },
  "s4" = { s4(n) },
  "s5" = { s5(n) },
  # "s6" = { s6(n) },
  replications = 10,
  columns = c("test", "replications", "elapsed", "relative")
)

在大数据量n的情况下,我们发现矩阵方法相对于其他方法变得非常缓慢。在apply中使用seq可能更加整洁,但会带来一个折衷——调用该函数n次会大大增加处理时间。尽管seq_len(n)比1:n更好,而且只运行一次。有趣的是,replicate方法是最快的。

  test replications elapsed relative
1   s1           10    5.44    1.915
2   s2           10    9.98    3.514
3   s3           10    2.84    1.000
4   s4           10   72.37   25.482
5   s5           10   35.78   12.599

1
小心使用。如果您更改第一个参数而忘记更改第二个参数,它将出现错误行为。例如,unlist(lapply(1:10, function(i) i:5)) 是不正确的。将第二个参数更改为 function(i) seq(from = i, to = 5, by = 1) 更冗长,但更安全。最终版本可能是 output <- function(x) unlist(lapply(seq_len(x), function(i) seq(from = i, to = x, by = 1))) - J. Mini
嗨,@Merijn van Tilborg!也许你可以在时间表中包括“sequence”答案?干杯 - Henrik
如果我有支持 from 参数的 R 版本,我当然会使用它,但我现在没有。如果我们看旧的 sequence 函数,它基本上是一个 R:sequence function (nvec) unlist(lapply(nvec, seq_len)) 的包装器,所以我期望它的速度与 s1 或 s2 一样。 - Merijn van Tilborg
确实如此,但看起来情况已经不同了(https://github.com/wch/r-source/blob/trunk/src/library/base/R/seq.R#L175-L177),因此时间可能会有所不同。 - Henrik
2
使用sequence和n = 10000进行快速的system.time测试表明,它比replicate方法快8-9倍。 - Henrik
1
这也可以缩短为 unlist(lapply(1:5, ':', 5)) - Robert Hacken

5

提到您的rep让我想起replicate,所以这里是一个非常有状态的解决方案。 我呈现这个方案是因为它很短而且不寻常,而不是因为它好。 这是非常不典型的R。

vect <- 0:5
unlist(replicate(5, vect <<- vect[-1]))
[1] 1 2 3 4 5 2 3 4 5 3 4 5 4 5 5

你可以通过结合replapply来完成,但基本上与Merijn van Tilborg的答案相同。
当然,真正无畏的非典型R用户会这样做,并拒绝进一步阐述。
mat <- matrix(1:5, ncol = 5, nrow = 5, byrow = TRUE)
mat[lower.tri(mat)] <- 0
c(t(mat)[t(mat != 0)])
[1] 1 2 3 4 5 2 3 4 5 3 4 5 4 5 5

1
你的矩阵替代方案可以稍微简化一下:m = matrix(seq.int(n), ncol = n, nrow = n)m[lower.tri(m, diag = TRUE)](虽然不太习惯这种写法)。 - Henrik
@Henrik 干得好。当我使用 byrow=TRUE 时,我不得不两次调用 t 时,我就知道有些不对劲了。 - J. Mini
我完全理解。我自己也曾在upper/lower.tri/byrow/"to t or not to t"的迷宫中迷失过。非典型的贡献非常受欢迎。 - Henrik
1
索引可以使用 row(m)>=col(m) 进行高尔夫球练习。 - Henrik

0
你可以像这样使用循环:
out=c();for(i in 1:5){ out=c(out, (1:5)[i:5]) }
out
# [1] 1 2 3 4 5 2 3 4 5 3 4 5 4 5 5

但那不是一个好主意!


为什么不使用循环?

使用循环会导致:

  • 速度较慢,
  • 内存利用率较低,以及
  • 难以阅读和理解。

相比之下,使用矢量化函数如sequence则恰好相反(速度更快、更高效,且易于阅读)。


更多信息

来自?sequence

序列的默认方法为每个元素i在并行(和循环)向量frombynvec中生成序列seq(from[i], by = by[i], length.out = nvec[i])。然后返回连接这些序列的结果。

关于from参数:

from:每个元素指定序列的第一个元素。

此外,由于循环中使用的向量未预先分配,因此将需要更多内存,并且速度也会变慢。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接