10/09/2020

Cấu trúc điều khiển

Cấu trúc điều khiển trong R cho phép chúng ta kiểm soát chuỗi câu lệnh. Thay vì phải lặp lại câu lệnh nhiều lần một cách thủ công, ta kiểm soát nó bằng một đối tượng dạng “logic”.

Các cấu trúc điều khiển ta thường sử dụng bao gồm

  • if và else: Kiểm tra một điều kiện và hành động tương ứng
  • Vòng lặp for: Thực hiện một vòng lặp với số lần cố định.
  • Ngoài ra còn những vòng lặp khác như while,repeat, break, next.

If-Else

If-Else là cấu trúc điều khiển thường được sử dụng nhất trong R hay bất kì ngôn ngữ nào. Cấu trúc này cho phép ta kiểm tra một điều kiện, và đưa ra hành động tương ứng nếu điều kiện này đúng hay sai.

Cấu trúc như sau

if(<điều kiện>) {
## Làm gì đó
}
## Tiếp tục những code khác

If-Else

Code trên sẽ không thực hiện hành động nào khi điều kiện sai. Nếu ta có một hành động muốn thực hiện khi điều kiện là sai, ta cần mệnh đề else.

if(<điều kiện>) {
## Làm gì đó
}
else {
## Làm gì đó khác }

If-Else

Nếu có một chuỗi điều kiện

if(<điều kiện 1>) {
## Làm gì đó
}
else if(<điều kiện 2>)
## Làm gì đó khác
} else {
Làm gì đó khác hơn nữa
}

If-Else

Ví dụ về cấu trúc if/else hợp lệ.

x <- runif(1,0,10) #Tạo một phân bố uniform
if(x > 3) {
  y <- 10
} else {
  y <- 0
}

If-Else

Biểu thức trên có thể được trình bày theo cách khác.

y <- if(x > 3) {
  10
} else {
  0
}

Vòng lặp for

Vòng lặp forphổ biến nhất do có thể đáp ứng hầu hết nhu cầu trong phân tích dữ liệu.

Trong R, vòng lặp for sử dụng những giá trị tuần tự của một vector để tái lặp những hành động sau đó.

Vòng lặp for

for (i in 1:10) {
  print(i)
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10

Vòng lặp này lấy giá trị i của vector 1:10. Tức là ở mỗi lần lặp lại, lần lượt giá trị 1, 2, 3, …, 10 được sử dụng. Code trong dấu ngoặc nhọn được thực hiện tương ứng với giá trị i ở mỗi vòng lặp.

Vòng lặp for

3 vòng lặp tiếp theo đem lại kết quả như nhau.

x <- c("a", "b", "c", "d")
for (i in 1:4) {
  ## In mỗi thành phần của 'x'
  print(x[i])
}
## [1] "a"
## [1] "b"
## [1] "c"
## [1] "d"

Vòng lặp for

Hàm seq_along() thường sử dụng kèm với vòng lặp for để tạo một chuỗi số nguyên.

## Tạo một chuỗi dựa trên độ dài của 'x'
for (i in seq_along(x)) {
  print(x[i])
}
## [1] "a"
## [1] "b"
## [1] "c"
## [1] "d"

Vòng lặp for

Thay vì i, ta có thể sử dụng chữ letter (hay bất kì chữ khác).

for (letter in x) {
  print(letter)
}
## [1] "a"
## [1] "b"
## [1] "c"
## [1] "d"

Vòng lặp for

Đối với những hành động chỉ có 1 hàng, không cần dấu ngoặc nhọn cũng được.

for (i in 1:4) print(x[i])
## [1] "a"
## [1] "b"
## [1] "c"
## [1] "d"

Tuy nhiên, dấu ngoặc nhọn vẫn nên được sử dụng. Nhiều lúc chúng ta muốn mở rộng hành động, và việc quên dấu ngoặc nhọn sẽ không xảy ra.

Vòng lặp for lồng ghép

Những vòng lặp for có thể được lồng vào nhau.

x <- matrix(1:6, 2, 3)

for (i in seq_len(nrow(x))) {
  for (j in seq_len(ncol(x))) {
    print(x[i, j])
  }
}
## [1] 1
## [1] 3
## [1] 5
## [1] 2
## [1] 4
## [1] 6

Bài tập

Bài tập 1 - Single loop

Hãy viết một vòng lặp for theo vector từ 1 đến 7. Xuất ra bậc ba tương ứng với mỗi số bằng hàm print().

Đáp án bài tập 1

seq <- 1:7
for (i in seq) print((seq[i])^3) 
## [1] 1
## [1] 8
## [1] 27
## [1] 64
## [1] 125
## [1] 216
## [1] 343
bac3 <-function(x) {x^3}
seq <- 1:7
for (i in seq) print(bac3(seq[i])) 
## [1] 1
## [1] 8
## [1] 27
## [1] 64
## [1] 125
## [1] 216
## [1] 343

Bài tập 2 - Advanced single loop

Viết một vòng lặp for lặp qua tên 4 biến số đầu của tập dữ liệu iris có sẵn. Xuất từng tên biến cùng với số trung bình của biến đó trong ngoặc đơn.

Ví dụ: Sepal.Length (5.84)

Hàm cần sử dụng: for(), print(), paste(), paste0()

Trích xuất theo tên cột: data[,"varName"]

Gợi ý: Làm từng bước. Nên thử trước 1-2 trường hợp, rồi hãy chuyển sang vòng lặp.

Đáp án bài tập 2

data(iris)
varName <- colnames(iris[,-5])
for (i in varName) {
  paste(varName[i], paste0("(",mean(iris[,i]),")"))
}
data(iris)
varName <- colnames(iris[,-5])
for (i in varName) {
  print(paste(i, paste0("(",mean(iris[,i]),")")))
}
## [1] "Sepal.Length (5.84333333333333)"
## [1] "Sepal.Width (3.05733333333333)"
## [1] "Petal.Length (3.758)"
## [1] "Petal.Width (1.19933333333333)"

Bài tập 3 - Double for loop

Sử dụng if-else, chia từng ô trong data frame iris cho độ lệch chuẩn của chính biến số chứa ô đó.

Đáp án bài tập 3

data(iris)
iris1 <- iris
for (i in 1:length(iris1[,-5])) {
     for (j in 1:nrow(iris1)) {
         iris1[j,i] = (iris1[j,i]-mean(iris1[,i]))/sd(iris1[,i]) 
     }
}
iris2 <- iris
colNames <- colnames(iris2)[-5]
for (i in colNames) {
     for (j in 1:nrow(iris2)) {
         iris2[j,i] = (iris2[j,i]-mean(iris2[,i]))/sd(iris2[,i])
     }
}
identical(iris1,iris2)
## [1] TRUE

Bài tập 4 - If-else

Tạo một data frame gồm 3 cột Name, Sex, Age. Các thành phần của mỗi biến số tương ứng với các thành viên trong lớp học.

Dùng hàm if để thêm vào cột thứ tư tên là Characteristic. Biến số này có giá trị là handsome nếu Name có giá trị là THANH, còn lại là beautiful với các thành viên còn lại trong lớp.

Đáp án bài tập 4

Bài tập 5 - For loop combined with if statement

Đáp án bài tập 5

for (i in 1:length(iris1[,-5])) {
     for (j in 1:nrow(iris1)) {
         if (iris1[j,i] > 0) {
           iris1[j,i] = iris1[j,i] + 2
         } else { 
           iris1[j,i] = iris1[j,i] - 2
           }
     }
}