【问题标题】:Create a sequence of sequences of numbers创建一系列数字序列
【发布时间】:2022-01-04 13:14:35
【问题描述】:

我想通过使用rep 或任何其他函数在 R 中创建以下序列。

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)

【问题讨论】:

    标签: r math vector sequence rep


    【解决方案1】:

    使用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() [...] 获得参数 [e.g. from] 生成更复杂的序列。

    【讨论】:

    【解决方案2】:
    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")
    )
    

    正如海报已经提到的“不要做”,我们看到for 循环与n = 1000L 上的任何其他方法相比变得非常慢

      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 更好,并且只运行一次。有趣的是,复制方法是最快的。

      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
    

    【讨论】:

    • 小心这个。如果您更改第一个参数而不记得更改第二个参数,则会出现异常行为。例如,unlist(lapply(1:10, function(i) i:5)) 不正确。将第二个参数更改为 function(i) seq(from = i, to = 5, by = 1) 会更冗长,但更安全。最终版本可能类似于output &lt;- function(x) unlist(lapply(seq_len(x), function(i) seq(from = i, to = x, by = 1)))
    • 嗨@Merijn van Tilborg!也许您也可以在计时中包含sequence 答案?干杯
    • 如果可以的话,我会拥有,但我没有支持 from 参数的 R 版本。我希望它与 s1 或 s2 的速度相同,就好像我们查看旧的序列函数一样,它基本上是 R: sequence function (nvec) unlist(lapply(nvec, seq_len)) 的包装器
    • 确实,不过好像是no longer the case,所以时间上其实可能不一样。
    • a quick system.time with sequence and n = 10000 表明它比 replicate 方法快 8-9 倍。
    【解决方案3】:

    你提到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
    

    【讨论】:

    • 您的矩阵替代方案可以稍微简化:m = matrix(seq.int(n), ncol = n, nrow = n); m[lower.tri(m, diag = TRUE)](虽然不那么单调)
    • @Henrik 干得好。当我在使用byrow=TRUE 时不得不两次调用t 时,我就知道出了点问题。
    • 我完全理解。我自己在upper/lower.tri/byrow/“到t 或不到t”的迷宫中迷路了很多次。非常感谢您的单调贡献。
    • 索引可以用row(m)&gt;=col(m)打高尔夫球
    【解决方案4】:

    你可以像这样使用循环:

    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

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

    关于from 参数:

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

    另外,由于循环中使用的向量没有预先分配,它需要更多的内存,而且速度也会更慢。

    【讨论】:

    • 你能补充解释吗?
    • @PeterMortensen 确定,现在会这样做
    • @PeterMortensen 完成
    猜你喜欢
    • 2023-03-09
    • 2018-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多