Nối giá trị vào vectơ trống trong R?


159

Tôi đang cố gắng học R và tôi không thể tìm ra cách để thêm vào danh sách.

Nếu đây là Python tôi sẽ làm. . .

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

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

Làm thế nào để bạn làm điều này trong 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

chỉ để rõ ràng, đây không phải là cách bạn làm điều này với python, ít nhất là nếu tôi hiểu bạn chính xác. bạn có thể làm một cách đơn giản vector = values; hoặc bạn có thể làm vector = vector + giá trị. Nhưng tôi có thể hiểu nhầm trường hợp sử dụng của bạn
Private

Câu trả lời:


208

Việc gắn vào một đối tượng trong một vòng lặp for sẽ khiến toàn bộ đối tượng bị sao chép trên mỗi lần lặp, điều này khiến nhiều người nói "R chậm" hoặc "nên tránh các vòng lặp R".

Như BrodieG đã đề cập trong các ý kiến: sẽ tốt hơn nhiều khi phân bổ trước một vectơ có độ dài mong muốn, sau đó đặt các giá trị phần tử trong vòng lặp.

Dưới đây là một số cách để nối các giá trị vào một vectơ. Tất cả đều nản lòng.

Nối vào một vectơ trong một vòng lặp

# 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")sẽ trả lời câu hỏi của bạn và tiết kiệm thời gian bạn viết câu hỏi này (nhưng sẽ khiến bạn hình thành thói quen xấu). ;-)

Lưu ý rằng đó vector <- c()không phải là một vector trống; nó NULL. Nếu bạn muốn một vector ký tự trống, sử dụng vector <- character().

Phân bổ trước vectơ trước khi lặp

Nếu bạn hoàn toàn phải sử dụng vòng lặp for, bạn nên phân bổ trước toàn bộ vectơ trước vòng lặp. Điều này sẽ nhanh hơn nhiều so với việc thêm vào các vectơ lớn hơn.

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
Tôi đã thử điều này nhưng có một danh sách NULL khi tôi in (vectơ)
O.rka

6
+1 để nhắc nhở về sự không hiệu quả, nhưng có thể thêm chi tiết về cách khắc phục ( vector <- character(length(values)); for(...)?
BrodieG

20
Nếu tất cả đều nản lòng, thật tốt khi làm nổi bật những gì được khuyến khích thay vào đó, vì đây là một mô hình khá phổ biến.
baxx

tại thời điểm này, có lẽ cũng đáng để đề cập đến cuốn sách tuyệt vời "R inferno" nói về các vectơ đang phát triển trong vòng tròn 2 burns-stat.com/pages/Tutor/R_inferno.pdf
Tjebo

62

FWIW: tương tự như append của python ():

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

8
Ngoài ra còn có phần phụ () trong R. Sẽ được sử dụng như : b <- 1; b <- append(b, 2). Nhưng như bạn đã đề cập, c () là cách làm R nhiều hơn.
juanbretti

31

Bạn có một vài lựa chọn:

  • c(vector, values)

  • append(vector, values)

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

Cách thứ nhất là cách tiếp cận tiêu chuẩn. Cái thứ hai cung cấp cho bạn tùy chọn để nối thêm một nơi nào đó ngoài phần cuối. Cái cuối cùng là một chút mâu thuẫn nhưng có lợi thế là sửa đổi vector(mặc dù thực sự, bạn có thể dễ dàng làm như vậy vector <- c(vector, values).

Lưu ý rằng trong R bạn không cần phải quay vòng qua các vectơ. Bạn chỉ có thể hoạt động trên chúng trong toàn bộ.

Ngoài ra, đây là công cụ khá cơ bản, vì vậy bạn nên xem qua một số tài liệu tham khảo .

Một số tùy chọn khác dựa trên phản hồi của OP:

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

Tôi đang làm một cái gì đó phức tạp hơn một chút tho. tôi cần nối chúng qua vòng lặp vì tôi đang sửa đổi chúng
O.rka

1
@ draconisthe0ry, tại sao bạn không cung cấp thêm chi tiết về những gì bạn đang cố gắng làm?
BrodieG

1
oh tôi hiểu rồi thay vì thực hiện c (vectơ, giá trị [i]) trong vòng lặp for, bạn phải "vector = c (vectơ, giá trị [i])
O.rka

giả sử tôi muốn sử dụng cđể nối thêm dataframe thay vì vectơ?
loretoparisi

18

Chỉ vì mục đích hoàn chỉnh, việc thêm các giá trị vào một vectơ trong vòng lặp for không thực sự là triết lý trong R. R hoạt động tốt hơn bằng cách vận hành trên các vectơ nói chung, như @BrodieG chỉ ra. Xem nếu mã của bạn không thể được viết lại thành:

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

Đầu ra sẽ là một vectơ của các giá trị trả về. Bạn cũng có thể sử dụng lapplynếu các giá trị là một danh sách thay vì một vectơ.


8

Đôi khi, chúng ta phải sử dụng các vòng lặp, ví dụ, khi chúng ta không biết có bao nhiêu lần lặp chúng ta cần để có kết quả. Lấy vòng lặp trong khi làm ví dụ. Dưới đây là những phương pháp bạn tuyệt đối nên tránh:

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 

Đây là rất không hiệu quả vì R sao chép vectơ mỗi lần nó xuất hiện.

Cách hiệu quả nhất để chắp thêm là sử dụng chỉ mục. Lưu ý rằng lần này tôi cho phép lặp lại 1e7 lần, nhưng nó vẫn nhanh hơn nhiều c.

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

Điều này được chấp nhận. Và chúng ta có thể làm cho nó nhanh hơn một chút bằng cách thay thế [bằng [[.

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

Có lẽ bạn đã nhận thấy rằng lengthcó thể tốn thời gian. Nếu chúng ta thay thế lengthbằng một bộ đếm:

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

Như những người dùng khác đã đề cập, việc phân bổ trước vector là rất hữu ích. Nhưng đây là sự đánh đổi giữa tốc độ và mức sử dụng bộ nhớ nếu bạn không biết mình cần bao nhiêu vòng để có kết quả.

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 

Một phương pháp trung gian là thêm dần các khối kết quả.

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

Trong R, bạn có thể thử theo cách này:

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

Những gì bạn đang sử dụng trong mã python được gọi là một danh sách trong python và nó khác với các vectơ R, nếu tôi nhận được những gì bạn muốn làm:

# 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))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.