循环递增时避免for循环

In R, I try systematically to avoid "for" loops and use lapply() family instead.
But how to do so when an iteration contains an increment step ?

For example : is it possible to obtain the same result as below with a lapply approach ?

a <- c()
b <- c()
for (i in 1:10){
  a <- c(a, i)
  b <- c(b, (paste(a, collapse = "-")))
}
data.frame(a, b)

> data.frame(a, b)
>     a                    b
> 1   1                    1
> 2   2                  1-2
> 3   3                1-2-3
> 4   4              1-2-3-4
> 5   5            1-2-3-4-5
> 6   6          1-2-3-4-5-6
> 7   7        1-2-3-4-5-6-7
> 8   8      1-2-3-4-5-6-7-8
> 9   9    1-2-3-4-5-6-7-8-9
> 10 10 1-2-3-4-5-6-7-8-9-10
评论
  • Wolf
    Wolf 回复

    Another way is to use Reduce with accumulate = TRUE, i.e.

    df$new <- do.call(rbind, Reduce(paste, split(df, seq(nrow(df))), accumulate = TRUE))
    

    这使,

        a                  new
    1   1                    1
    2   2                  1 2
    3   3                1 2 3
    4   4              1 2 3 4
    5   5            1 2 3 4 5
    6   6          1 2 3 4 5 6
    7   7        1 2 3 4 5 6 7
    8   8      1 2 3 4 5 6 7 8
    9   9    1 2 3 4 5 6 7 8 9
    10 10 1 2 3 4 5 6 7 8 9 10
    
  • wsed
    wsed 回复

    Another way of using Reduce, different to the approach by @Sotos

    df$b <- Reduce(function(...) paste(...,sep = "-"), df$a, accumulate = T)
    

    这样

    > df
        a                    b
    1   1                    1
    2   2                  1-2
    3   3                1-2-3
    4   4              1-2-3-4
    5   5            1-2-3-4-5
    6   6          1-2-3-4-5-6
    7   7        1-2-3-4-5-6-7
    8   8      1-2-3-4-5-6-7-8
    9   9    1-2-3-4-5-6-7-8-9
    10 10 1-2-3-4-5-6-7-8-9-10
    
  • qodio
    qodio 回复

    You can use sapply (lapply would work too but it returns a list) and iterate over every value of a in df and create a sequence and paste the value together.

    df <- data.frame(a = 1:10)
    df$b <- sapply(df$a, function(x) paste(seq(x), collapse = "-"))
    df
    
    #    a                    b
    #1   1                    1
    #2   2                  1-2
    #3   3                1-2-3
    #4   4              1-2-3-4
    #5   5            1-2-3-4-5
    #6   6          1-2-3-4-5-6
    #7   7        1-2-3-4-5-6-7
    #8   8      1-2-3-4-5-6-7-8
    #9   9    1-2-3-4-5-6-7-8-9
    #10 10 1-2-3-4-5-6-7-8-9-10
    

    If there could be non-numerical values in data on which we can not use seq like

    df <- data.frame(a =letters[1:10])
    

    在那种情况下,我们可以使用

    df$b <- sapply(seq_along(df$a), function(x) paste(df$a[seq_len(x)], collapse = "-"))
    df
    
    #   a                   b
    #1  a                   a
    #2  b                 a-b
    #3  c               a-b-c
    #4  d             a-b-c-d
    #5  e           a-b-c-d-e
    #6  f         a-b-c-d-e-f
    #7  g       a-b-c-d-e-f-g
    #8  h     a-b-c-d-e-f-g-h
    #9  i   a-b-c-d-e-f-g-h-i
    #10 j a-b-c-d-e-f-g-h-i-j
    
  • 行文华
    行文华 回复

    For the sake of completeness, there is also the accumulate() function from the purrr package.

    So, building on the answers of Sotos and ThomasIsCoding:

    df <- data.frame(a = 1:10)
    df$b <- purrr::accumulate(df$a, paste, sep = "-")
    df
    
        a                    b
    1   1                    1
    2   2                  1-2
    3   3                1-2-3
    4   4              1-2-3-4
    5   5            1-2-3-4-5
    6   6          1-2-3-4-5-6
    7   7        1-2-3-4-5-6-7
    8   8      1-2-3-4-5-6-7-8
    9   9    1-2-3-4-5-6-7-8-9
    10 10 1-2-3-4-5-6-7-8-9-10
    

    The difference to Reduce() is

    • that accumulate() is a function verb on its own (no additional parameter accumulate = TRUE required)
    • and that additional arguments like sep = "-" can be passed on to the mapped function which may help to avoid the creation of an anonymous function.