196

I'm trying to learn R and I can't figure out how to append to a list.

If this were Python I would . . .

#Python
vector = []
values = ['a','b','c','d','e','f','g']

for i in range(0,len(values)):
    vector.append(values[i])

How do you do this in R?

#R Programming
> vector = c()
> values = c('a','b','c','d','e','f','g')
> for (i in 1:length(values))
+ #append value[i] to empty vector
tumultous_rooster
  • 12,150
  • 32
  • 92
  • 149
O.rka
  • 29,847
  • 68
  • 194
  • 309
  • 2
    just for clarity's sake, this is not how you'd do this in python, at least if I understand you correctly. you could simply do `vector = values`; or you could do vector = vector + values. But I might be misunderstanding your use case – Private Mar 22 '19 at 08:59

9 Answers9

254

Appending to an object in a for loop causes the entire object to be copied on every iteration, which causes a lot of people to say "R is slow", or "R loops should be avoided".

As BrodieG mentioned in the comments: it is much better to pre-allocate a vector of the desired length, then set the element values in the loop.

Here are several ways to append values to a vector. All of them are discouraged.

Appending to a vector in a loop

# one way
for (i in 1:length(values))
  vector[i] <- values[i]
# another way
for (i in 1:length(values))
  vector <- c(vector, values[i])
# yet another way?!?
for (v in values)
  vector <- c(vector, v)
# ... more ways

help("append") would have answered your question and saved the time it took you to write this question (but would have caused you to develop bad habits). ;-)

Note that vector <- c() isn't an empty vector; it's NULL. If you want an empty character vector, use vector <- character().

Pre-allocate the vector before looping

If you absolutely must use a for loop, you should pre-allocate the entire vector before the loop. This will be much faster than appending for larger vectors.

set.seed(21)
values <- sample(letters, 1e4, TRUE)
vector <- character(0)
# slow
system.time( for (i in 1:length(values)) vector[i] <- values[i] )
#   user  system elapsed 
#  0.340   0.000   0.343 
vector <- character(length(values))
# fast(er)
system.time( for (i in 1:length(values)) vector[i] <- values[i] )
#   user  system elapsed 
#  0.024   0.000   0.023 
Joshua Ulrich
  • 173,410
  • 32
  • 338
  • 418
  • 2
    I tried this but got a NULL list when i print(vector) – O.rka Mar 06 '14 at 21:11
  • 6
    +1 for reminder about inefficiency, but maybe add details on how to work around (`vector <- character(length(values)); for(...`)? – BrodieG Mar 06 '14 at 21:12
  • 34
    If all are discouraged it would be nice to highlight what's encouraged instead, as this is a fairly common pattern. – baxx Oct 28 '18 at 16:50
  • 1
    at this point, it may be worth also to mention the great book "R inferno" which discusses growing vectors in circle 2 https://www.burns-stat.com/pages/Tutor/R_inferno.pdf – tjebo Jun 06 '20 at 17:01
  • just moment, I am not sure for this is true or false. But thanks. – Dev Solution Aug 31 '20 at 00:40
  • In practice many (most?) of the times you do not know how large your vector will be such as when you only add something if a condition is met inside the for loop. How do you handle this? – Brian Wiley Nov 27 '20 at 22:43
  • Note that as of R4.0.3 at least, preallocating hardly makes any difference at data sizes 1e4 (milliseconds difference) or 1e5 (centiseconds difference). – Soumya Mar 14 '21 at 05:04
  • For info `append` is slower than `vec[i] <- i` – Julien Mar 03 '23 at 15:11
74

FWIW: analogous to python's append():

b <- 1
b <- c(b, 2)
tumultous_rooster
  • 12,150
  • 32
  • 92
  • 149
  • 9
    There is also the **append()** in R. Will be used as: `b <- 1; b <- append(b, 2)`. But as you mention, **c()** is a more R way of doing things. – juanbretti Jul 29 '17 at 18:48
38

You have a few options:

  • c(vector, values)

  • append(vector, values)

  • vector[(length(vector) + 1):(length(vector) + length(values))] <- values

The first one is the standard approach. The second one gives you the option to append someplace other than the end. The last one is a bit contorted but has the advantage of modifying vector (though really, you could just as easily do vector <- c(vector, values).

Notice that in R you don't need to cycle through vectors. You can just operate on them in whole.

Also, this is fairly basic stuff, so you should go through some of the references.

Some more options based on OP feedback:

for(i in values) vector <- c(vector, i)
Abel Callejo
  • 13,779
  • 10
  • 69
  • 84
BrodieG
  • 51,669
  • 9
  • 93
  • 146
18

Just for the sake of completeness, appending values to a vector in a for loop is not really the philosophy in R. R works better by operating on vectors as a whole, as @BrodieG pointed out. See if your code can't be rewritten as:

ouput <- sapply(values, function(v) return(2*v))

Output will be a vector of return values. You can also use lapply if values is a list instead of a vector.

antoine
  • 2,036
  • 25
  • 17
12

Sometimes we have to use loops, for example, when we don't know how many iterations we need to get the result. Take while loops as an example. Below are methods you absolutely should avoid:

a=numeric(0)
b=1
system.time(
  {
    while(b<=1e5){
      b=b+1
      a<-c(a,pi)
    }
  }
)
# user  system elapsed 
# 13.2     0.0    13.2 

a=numeric(0)
b=1
system.time(
  {
    while(b<=1e5){
      b=b+1
      a<-append(a,pi)
    }
  }
)
# user  system elapsed 
# 11.06    5.72   16.84 

These are very inefficient because R copies the vector every time it appends.

The most efficient way to append is to use index. Note that this time I let it iterate 1e7 times, but it's still much faster than c.

a=numeric(0)
system.time(
  {
    while(length(a)<1e7){
      a[length(a)+1]=pi
    }
  }
)
# user  system elapsed 
# 5.71    0.39    6.12  

This is acceptable. And we can make it a bit faster by replacing [ with [[.

a=numeric(0)
system.time(
  {
    while(length(a)<1e7){
      a[[length(a)+1]]=pi
    }
  }
)
# user  system elapsed 
# 5.29    0.38    5.69   

Maybe you already noticed that length can be time consuming. If we replace length with a counter:

a=numeric(0)
b=1
system.time(
  {
    while(b<=1e7){
      a[[b]]=pi
      b=b+1
    }
  }
)
# user  system elapsed 
# 3.35    0.41    3.76

As other users mentioned, pre-allocating the vector is very helpful. But this is a trade-off between speed and memory usage if you don't know how many loops you need to get the result.

a=rep(NaN,2*1e7)
b=1
system.time(
  {
    while(b<=1e7){
      a[[b]]=pi
      b=b+1
    }
    a=a[!is.na(a)]
  }
)
# user  system elapsed 
# 1.57    0.06    1.63 

An intermediate method is to gradually add blocks of results.

a=numeric(0)
b=0
step_count=0
step=1e6
system.time(
  {
    repeat{
      a_step=rep(NaN,step)
      for(i in seq_len(step)){
        b=b+1
        a_step[[i]]=pi
        if(b>=1e7){
          a_step=a_step[1:i]
          break
        }
      }
      a[(step_count*step+1):b]=a_step
      if(b>=1e7) break
      step_count=step_count+1
    }
  }
)
#user  system elapsed 
#1.71    0.17    1.89
lovetl2002
  • 910
  • 9
  • 23
2

In R, you can try out this way:

X = NULL
X
# NULL
values = letters[1:10]
values
# [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j"
X = append(X,values)
X
# [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j"
X = append(X,letters[23:26])
X
# [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "w" "x" "y" "z"
Rich Scriven
  • 97,041
  • 11
  • 181
  • 245
Sowmya S. Manian
  • 3,723
  • 3
  • 18
  • 30
2
> vec <- c(letters[1:3]) # vec <- c("a","b","c") ; or just empty vector: vec <- c()

> values<- c(1,2,3)

> for (i in 1:length(values)){
      print(paste("length of vec", length(vec))); 
      vec[length(vec)+1] <- values[i]  #Appends value at the end of vector
  }

[1] "length of vec 3"
[1] "length of vec 4"
[1] "length of vec 5"

> vec
[1] "a" "b" "c" "1" "2" "3"
Surya
  • 11,002
  • 4
  • 57
  • 39
1

What you're using in the python code is called a list in python, and it's tottaly different from R vectors, if i get what you wanna do:

# you can do like this if you'll put them manually  
v <- c("a", "b", "c")

# if your values are in a list 
v <- as.vector(your_list)

# if you just need to append
v <- append(v, value, after=length(v))
0

in R you create a "list" doing this:

v <- numeric() (is a numeric vector, or int in Python)

v <- character() (is a character vector, or str in Python)

then, if you want yo append a single value you have to do this:

v[1] <- 10 (append to vector "v", in position "1" a value 10)

v[2] <- 11 (append to vector "v", in position "2" a value 11)

So, if yoy want yo append multiple values in a for loop, try this:

v <- numeric()

for (value in 1:10) {

    v[value] <- value
    
}

v

[1]  1  2  3  4  5  6  7  8  9 10