Dodać wartość do pustego wektora w R?


160

Próbuję się nauczyć R i nie mogę dowiedzieć się, jak dołączyć do listy.

Gdyby to był Python, zrobiłbym to. . .

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

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

Jak to robisz w 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

tylko dla jasności, nie jest to sposób, w jaki można to zrobić w Pythonie, przynajmniej jeśli dobrze cię rozumiem. możesz po prostu zrobić vector = values; lub możesz zrobić wektor = wektor + wartości. Ale mogę źle zrozumieć twój przypadek użycia
Prywatny

Odpowiedzi:


209

Dołączanie do obiektu w pętli for powoduje, że cały obiekt jest kopiowany w każdej iteracji, co powoduje, że wiele osób mówi „R jest wolne” lub „R pętli należy unikać”.

Jak BrodieG wspomniał w komentarzach: znacznie lepiej jest wstępnie alokować wektor o żądanej długości, a następnie ustawić wartości elementów w pętli.

Oto kilka sposobów dołączania wartości do wektora. Wszyscy są zniechęceni.

Dołączanie do wektora w pętli

# 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")odpowiedziałby na twoje pytanie i zaoszczędziłby czas, jaki zajął ci napisanie tego pytania (ale spowodowałby, że rozwinąłbyś złe nawyki). ;-)

Zauważ, że vector <- c()nie jest to pusty wektor; to jest NULL. Jeśli chcesz mieć pusty wektor znaków, użyj vector <- character().

Wstępnie przydziel wektor przed zapętleniem

Jeśli absolutnie musisz użyć pętli for, powinieneś wstępnie przydzielić cały wektor przed pętlą. Będzie to znacznie szybsze niż dołączanie do większych wektorów.

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
Próbowałem tego, ale otrzymałem listę NULL, kiedy
drukuję

6
+1 dla przypomnienia o nieefektywności, ale może dodać szczegóły, jak obejść ( vector <- character(length(values)); for(...)?
BrodieG

20
Jeśli wszyscy są zniechęceni, byłoby miło wskazać zamiast tego, do czego się zachęca, ponieważ jest to dość powszechny wzorzec.
baxx

w tym miejscu warto wspomnieć także o wielkiej książce „R inferno”, która omawia rosnące wektory w kręgu 2 burns-stat.com/pages/Tutor/R_inferno.pdf
Tjebo

62

FWIW: analogiczne do append () w pythonie:

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

8
Istnieje również append () w R. będą wykorzystywane jako: b <- 1; b <- append(b, 2). Ale jak wspomniałeś, c () jest bardziej R sposobem robienia rzeczy.
juanbretti

31

Masz kilka możliwości:

  • c(vector, values)

  • append(vector, values)

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

Pierwsza z nich to podejście standardowe. Drugi daje możliwość dodania innego miejsca niż koniec. Ten ostatni jest nieco wykrzywiony, ale ma tę zaletę, że modyfikuje vector(choć tak naprawdę można to równie łatwo zrobić vector <- c(vector, values).

Zauważ, że w R nie musisz przełączać wektorów. Możesz po prostu operować nimi w całości.

Jest to również dość podstawowa sprawa, więc powinieneś przejrzeć niektóre z odniesień .

Kilka dodatkowych opcji w oparciu o opinie OP:

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

ale robię coś bardziej skomplikowanego. Muszę dodać je do pętli for, ponieważ je modyfikuję
O.rka

1
@ draconisthe0ry, dlaczego nie podasz więcej szczegółów na temat tego, co próbujesz zrobić?
BrodieG

1
rozumiem! zamiast robić c (wektor, wartości [i]) w pętli for, musisz "vector = c (wektor, wartości [i])
O.rka

Powinienem użyć cdo dołączania dataframe zamiast wektorów?
loretoparisi

18

Tylko ze względu na kompletność, dołączanie wartości do wektora w pętli for nie jest tak naprawdę filozofią w R. R działa lepiej, działając na wektorach jako całości, jak zauważył @BrodieG. Sprawdź, czy Twojego kodu nie można przepisać jako:

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

Wyjście będzie wektorem zwracanych wartości. Możesz także użyć, lapplyjeśli wartości są listą zamiast wektorem.


8

Czasami musimy użyć pętli, na przykład, gdy nie wiemy, ile iteracji potrzebujemy, aby uzyskać wynik. Weźmy na przykład pętle while. Poniżej znajdują się metody, których absolutnie powinieneś unikać:

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 

Są one bardzo nieefektywne, ponieważ R kopiuje wektor za każdym razem, gdy jest dołączany.

Najbardziej wydajnym sposobem dołączania jest użycie indeksu. Zauważ, że tym razem pozwoliłem iterować 1e7 razy, ale nadal jest znacznie szybszy niż c.

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

To jest do zaakceptowania. I możemy zrobić to nieco szybciej, zastępując [z [[.

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

Może już zauważyłeś, że lengthmoże to być czasochłonne. Jeśli zastąpimy lengthlicznikiem:

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

Jak wspominali inni użytkownicy, wstępne przydzielenie wektora jest bardzo pomocne. Ale jest to kompromis między szybkością a zużyciem pamięci, jeśli nie wiesz, ile pętli potrzebujesz, aby uzyskać wynik.

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 

Metoda pośrednia polega na stopniowym dodawaniu bloków wyników.

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

W R możesz wypróbować w ten sposób:

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

To, czego używasz w kodzie Pythona, nazywa się listą w Pythonie i różni się całkowicie od wektorów R, jeśli dostanę to, co chcesz zrobić:

# 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))
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.