3 Loop

3.1 General loop

ท่านสามารถสร้าง Loop ได้เหมือนภาษาอื่นๆ โดยทั่วไปใน R

for(i in 1:2){
  for (j in c("a","b"))
  print(c(i,j))
}
## [1] "1" "a"
## [1] "1" "b"
## [1] "2" "a"
## [1] "2" "b"
i <- 0
while(i <= 5){
  print(i)
  i <- i+1
}
## [1] 0
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
item_list <- list("apple", "banana", "fish")
for(i in 1:length(item_list)){
  if (item_list[[i]]== "fish"){
    print("not fruit")
  }
  else(print(item_list[[i]]))
}
## [1] "apple"
## [1] "banana"
## [1] "not fruit"

อย่างไรก็ตาม การใช้ Loop ใน R มีข้อควรระวังที่มากกว่าภาษาอื่นๆ เช่น python, java ที่ออกแบบมาเพื่อ Programming โดยเฉพาะ คือ การ Loop เข้า Memory ที่ไม่ได้มีการ Pre-allocate ไว้จะช้ามาก เมื่อเทียบกับ Pre-allocate memory loop หรือ Vectorized function

set.seed(123)
mat <- matrix(rnorm(10000000, 100, 200), nrow = 10000) # Large matrix

system.time({
for(i in 1:nrow(mat)){
 res1 <- c()
 res1[[i]] <- mean(mat[i,])
}
})
##    user  system elapsed 
##    0.08    0.03    0.40
system.time({
res2 <- vector(length = nrow(mat)) # Pre-allocate memory
for(i in 1:nrow(mat)){
 res2[i] <- mean(mat[i,])
}
}) ## 3x faster
##    user  system elapsed 
##    0.04    0.00    0.11
system.time({
rowMeans(mat)
}) ## Instant for vectorized function
##    user  system elapsed 
##    0.01    0.00    0.03

นั่นหมายความว่า การเรียกฟังก์ชันที่เร็วที่สุดควร

  1. เลือกฟังก์ชันที่มีการเขียนให้รองรับ Vectorization (เรียกคำสั่งภายในพร้อมกันทุกตัวแปร) อยู่แล้ว
  2. Pre-allocate ความจำก่อน ตามจำนวน (Length) ของข้อมูลสุดท้ายที่คาดว่าจะได้
  3. การสร้างตัวแปรใหม่ใส่เข้าไปในตัวแปรเรื่อยๆ โดยไม่ได้ Pre-allocate ไว้ ควรเป็นทางเลือกสุดท้าย

อย่างไรก็ตามถ้าท่านไม่ได้เขียน package ใช้ หรือทำการวิเคราะห์ที่เป็นแนว Programming มากนัก ท่านอาจจะไม่ต้องใส่ใจส่วนนี้มาก


3.2 Apply family

apply family เป็น Function สำหรับ Loop ใน R ซึ่งมีการ Pre-allocate memory ไว้เรียบร้อยแล้ว ซึ่งเหมาะแก่การใช้งานมากกว่าการใช้ for/while loop ตามปกติ ประกอบไปด้วย

  • lapply จะทำการเรียก Function ตามลำดับและส่งผลออกมาเป็น List
set.seed(123)
x <- 1:5
lapply(x, \(x) sample(100, size = x, replace = TRUE))
## [[1]]
## [1] 31
## 
## [[2]]
## [1] 79 51
## 
## [[3]]
## [1] 14 67 42
## 
## [[4]]
## [1] 50 43 14 25
## 
## [[5]]
## [1] 90 91 69 91 57

Note: \(x) ... หรือ function (x) ... คือการเรียกฟังก์ชันที่จะใช้ใน Loop นั้น โดยไม่ต้องสร้าง Function ขึ้นมาใหม่ เรียกว่า Anonymous function

sample_test <- function(x){
  sample(100, size = x, replace = TRUE)
}
set.seed(123)
lapply(x , sample_test) # same result with above anonymous function
## [[1]]
## [1] 31
## 
## [[2]]
## [1] 79 51
## 
## [[3]]
## [1] 14 67 42
## 
## [[4]]
## [1] 50 43 14 25
## 
## [[5]]
## [1] 90 91 69 91 57
  • sapply เหมือน lapply แต่จะส่งผลกลับมาแบบไม่เป็น list อย่างไรก็ตาม ถ้าไม่สามารถขมวดรวมกันได้ จะส่งกลับมาเป็น list เหมือน lapply
set.seed(123)
x <- c(1,1,1,1,1,1)
sapply(x, \(x) sample(100, size = x, replace = TRUE)) # not list
## [1] 31 79 51 14 67 42
x <- 1:5
sapply(x, \(x) sample(100, size = x, replace = TRUE)) # cant convert to vector
## [[1]]
## [1] 50
## 
## [[2]]
## [1] 43 14
## 
## [[3]]
## [1] 25 90 91
## 
## [[4]]
## [1] 69 91 57 92
## 
## [[5]]
## [1]  9 93 99 72 26
  • apply คือการเรียกฟังก์ชันตามมิติ โดย 1 = แถว 2 = คอลัมน์ มีไว้ใช้กับข้อมูลประเภท Matrix และ Dataframe
set.seed(123)
mat <- matrix(runif(100, 100, 200), nrow = 10) # 10x10 matrix
apply(mat, 1, sd) # sd by row
##  [1] 37.22781 22.92317 16.93264 33.84358 32.38341 27.03874 28.94264 32.21673 25.33109 28.95514
apply(mat, 2, sd) # sd by column
##  [1] 29.47400 34.48212 25.09712 32.73406 21.78202 30.93267 26.08665 25.76299 32.57488 23.84909
  • mapply คือ การเรียกฟังก์ชันจากหลาย List พร้อมกัน
x <- list(1,2,3)
y <- list(2,4,6)
z <- list(3,6,9)

mapply(FUN = \(x,y,z) x*y*z, x,y,z) # x1*y1*z1, x2*y2*z2, x3*y3*z3
## [1]   6  48 162
mapply(FUN = rep, times = 1:4, 
       MoreArgs = list(x = 42)) # rep(42, 1), rep(42, 2), ...
## [[1]]
## [1] 42
## 
## [[2]]
## [1] 42 42
## 
## [[3]]
## [1] 42 42 42
## 
## [[4]]
## [1] 42 42 42 42
  • tapply คือ การเรียกฟังก์ชันตาม Subset
tapply(iris$Sepal.Length, iris$Species, mean)
##     setosa versicolor  virginica 
##      5.006      5.936      6.588