Добавить значение в пустой вектор в 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; или вы можете сделать вектор = вектор + значения. Но я мог бы неправильно понять ваш вариант использования
частное

Ответы:


210

Добавление к объекту в цикле 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
Я попробовал это, но получил NULL список, когда я печатаю (вектор)
O.rka

6
+1 за напоминание о неэффективности, но, возможно, добавить подробности о том, как обойти ( vector <- character(length(values)); for(...)?
BrodieG

20
Если все обескуражены, было бы неплохо выделить то, что поощряется, так как это довольно распространенная модель.
Baxx

в этот момент, возможно, также стоит упомянуть великую книгу «R inferno», в которой обсуждаются растущие векторы в круге 2 burns-stat.com/pages/Tutor/R_inferno.pdf
Tjebo

62

FWIW: аналог pynd's append ():

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

8
Существует также Append () в R. Будет использоваться как: b <- 1; b <- append(b, 2). Но, как вы упомянули, c () - это более R способ ведения дел.
Хуанбретти

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, потому что я их модифицирую
O.rka

1
@ draconisthe0ry, почему бы тебе не рассказать больше о том, что ты пытаешься сделать?
BrodieG

1
О, я вижу! вместо того, чтобы делать c (vector, values ​​[i]) в цикле for, вы должны «vector = c (vector, values ​​[i])»
O.rka

Предполагается, что я хотел бы использовать cдля добавления dataframe вместо векторов?
Лоретопариси

18

Просто для полноты, добавление значений к вектору в цикле for на самом деле не является философией в R. R работает лучше, работая с векторами в целом, как указывал @BrodieG. Посмотрите, не может ли ваш код быть переписан как:

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

Выходом будет вектор возвращаемых значений. Вы также можете использовать, lapplyесли значения это список, а не вектор.


8

Иногда нам приходится использовать циклы, например, когда мы не знаем, сколько итераций нам нужно, чтобы получить результат. Взять хотя бы циклы в качестве примера. Ниже приведены методы, которые вы должны избегать:

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))
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.