3 Loop
3.1 General loop
ท่านสามารถสร้าง Loop ได้เหมือนภาษาอื่นๆ โดยทั่วไปใน R
## [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
นั่นหมายความว่า การเรียกฟังก์ชันที่เร็วที่สุดควร
- เลือกฟังก์ชันที่มีการเขียนให้รองรับ Vectorization (เรียกคำสั่งภายในพร้อมกันทุกตัวแปร) อยู่แล้ว
- Pre-allocate ความจำก่อน ตามจำนวน (Length) ของข้อมูลสุดท้ายที่คาดว่าจะได้
- การสร้างตัวแปรใหม่ใส่เข้าไปในตัวแปรเรื่อยๆ โดยไม่ได้ Pre-allocate ไว้ ควรเป็นทางเลือกสุดท้าย
อย่างไรก็ตามถ้าท่านไม่ได้เขียน package
ใช้ หรือทำการวิเคราะห์ที่เป็นแนว Programming มากนัก ท่านอาจจะไม่ต้องใส่ใจส่วนนี้มาก
3.2 Apply family
apply
family เป็น Function สำหรับ Loop ใน R
ซึ่งมีการ Pre-allocate memory ไว้เรียบร้อยแล้ว ซึ่งเหมาะแก่การใช้งานมากกว่าการใช้ for/while loop ตามปกติ ประกอบไปด้วย
-
lapply
จะทำการเรียก Function ตามลำดับและส่งผลออกมาเป็น List
## [[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
## [1] 31 79 51 14 67 42
## [[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
## [[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