将值附加到R中的空向量吗?


160

我正在尝试学习R,但不知道如何添加到列表中。

如果这是Python我会的。。。

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

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

您如何在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

只是为了清楚起见,至少在我理解正确的情况下,这不是您在python中执行的操作。你可以简单地做vector = values; 或者您可以执行vector = vector + values。但是我可能会误解您的用例
私人

Answers:


209

在for循环中附加到对象会导致在每次迭代中复制整个对象,这使很多人说“ R慢”或“应避免R循环”。

正如BrodieG在评论中提到的:最好预先分配所需长度的向量,然后在循环中设置元素值。

以下是将值附加到向量的几种方法。不鼓励所有人。

循环附加到向量

# 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")本可以回答您的问题,并节省了您写此问题的时间(但会导致您养成不良习惯)。;-)

注意这vector <- c()不是一个空向量。是NULL。如果要使用空字符向量,请使用vector <- character()

循环前预先分配向量

如果绝对必须使用for循环,则应在循环之前预先分配整个向量。这将比附加较大的向量快得多。

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 

2
我尝试了此操作,但在我打印(vector)时得到了一个空列表
O.rka 2014年

6
+1提醒您效率低下,但也许会添加有关如何解决(vector <- character(length(values)); for(...)的详细信息?
BrodieG 2014年

20
如果不鼓励所有人,最好突出显示鼓励使用的内容,因为这是一个相当普遍的模式。
baxx

在这一点上,值得一提的是《 R inferno》这本伟大的书,该书讨论了第2圈中的增长矢量。burns
stat.com/

62

FWIW:类似于python的append():

b <- 1
b <- c(b, 2)

8
还有一个附加()将用作在R: b <- 1; b <- append(b, 2)。但是正如您提到的,c()是一种更R的做事方式。
juanbretti

31

您有几种选择:

  • c(vector, values)

  • append(vector, values)

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

第一个是标准方法。第二个选项使您可以选择在末尾以外的地方附加其他内容。最后一个有点扭曲,但是具有修改的优势vector(尽管实际上,您可以轻松地做到这一点vector <- c(vector, values)

请注意,在R中,您不需要遍历向量。您可以对它们进行整体操作。

另外,这是相当基本的内容,因此您应该阅读一些参考资料

基于OP反馈的其他一些选择:

for(i in values) vector <- c(vector, i)

我正在做一些更复杂的事情。我需要通过for-loop附加它们,因为我正在修改它们
O.rka 2014年

1
@ draconisthe0ry,为什么不提供有关您要执行的操作的更多详细信息?
BrodieG 2014年

1
哦,我明白了!而不是在for循环中执行c(vector,values [i]),您必须“ vector = c(vector,values [i])
O.rka 2014年

假设我想使用c追加数据框而不是向量?
loretoparisi

18

仅出于完整性考虑,在for循环中向向量附加值实际上并不是R中的原理。正如@BrodieG指出的那样,R通过对向量进行整体操作可以更好地工作。查看您的代码是否不能重写为:

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

输出将是返回值的向量。lapply如果值是列表而不是向量,则也可以使用。


8

有时,例如,当我们不知道需要多少次迭代才能获得结果时,就不得不使用循环。以while循环为例。以下是您绝对应避免的方法:

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 

这些效率非常低,因为R每次附加向量时都会复制该向量。

附加的最有效方法是使用索引。请注意,这次我让其迭代1e7次,但仍比快得多c

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

这是可以接受的。而我们可以通过替换[为来使其更快一些[[

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

也许您已经注意到这length可能很耗时。如果我们length用一个计数器代替:

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

正如其他用户提到的那样,预先分配向量非常有帮助。但这是在速度和内存使用之间的权衡,如果您不知道要获得结果需要多少个循环。

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 

一种中间方法是逐渐添加结果块。

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

2

在R中,您可以尝试以下方式:

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"

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"

0

您在python代码中使用的内容在python中称为列表,如果我想得到的话,它与R向量完全不同:

# 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))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.