Phân tích dữ liệu với R - Phần I

Trong nội dung này, chúng ta cùng làm quen với các kiểu dữ liệu và cấu trúc dữ liệu thường dùng trong R.

Dữ liệu thường gặp được lưu trữ trong R có thể bắt gặp ở 4 dạng sau:

  • numeric

  • integer

  • logical

  • character

Các kiểu dữ liệu trong R

Các kiểu dữ liệu chính

Dữ liệu kiểu Numeric ở đây là số thực, ví dụ như 2.345, số pi,…

Dữ liệu kiểu Integer ở đây là kiểu dữ liệu số nguyên, trong R số nguyên được định nghĩa là thêm hậu tố L vào sau phần số. Ví dụ: 2L, 3L, 8L…

Dữ liệu kiểu Logical là dữ liệu kiểu dạng logic TRUE hoặc FALSE.

Dữ liệu kiểu Character là dữ liệu kiểu chuỗi, xâu ký tự, giống như Python, dữ liệu kiểu này được đặt trong dấu ‘text’ hoặc “text”.

Cấu trúc dữ liệu

Khi lưu trữ dữ liệu, trong R phân thành các dạng dữ liệu mà nội dung trong cấu trúc đó dữ liệu có thể là đồng nhất (kiểu dữ liệu là như nhau - homogeneous), hoặc các dữ liệu lưu trữ là khác kiểu nhau (heterogenous).

Về cấu trúc dữ liệu dạng lưu trữ, các dạng phổ biến thường gặp trong R và trong khoa học dữ liệu bao gồm:

  • vectors

  • matrices

  • lists

  • data frames

Data Structures in R

Kiểu Véc tơ

Vectơ là một chuỗi một chiều của các phần tử dữ liệu cùng kiểu.

Các vectơ được xây dựng với hàm c (). Để gán một vectơ cho một biến, hãy sử dụng toán tử <- . Hàm c() là viết tắt của concatenation - cho phép ghép, kết nối các phần tử lại với nhau. Ví dụ:

# Một véc tơ các phần tử số
x <- c(1L,2L,3L,3.14)
x
## [1] 1.00 2.00 3.00 3.14

Dấu mũi tên chỉ ra là toán tử gán, không giống như các ngôn ngữ lập trình khác, sử dụng dấu =. Trong R sử dụng dấu mũi tên để làm toán tử gán (assignment operator).

Để biết được kiểu dữ liệu trong các cấu trúc dữ liệu là gì, chúng ta sử dụng hàm str()

x <- c(1L,2L,3L,3.14)
str(x)
##  num [1:4] 1 2 3 3.14

Trong một số trường hợp chúng ta quan tâm tới số phần tử của véc tơ hay nói cách khác là chiều dài của véc tơ, sử dụng hàm length()

x <- c(1L,2L,3L,3.14)
print(length(x))
## [1] 4

Kết quả trả về là 4 phần tử. Hàm c() còn cho chúng ta kết nối các véc tơ con khác nhau như sau:

x <- c(1L,2L,3L,3.14)
y <- c(1,2,3,5,4,6)
z <- c(x, y)
z
##  [1] 1.00 2.00 3.00 3.14 1.00 2.00 3.00 5.00 4.00 6.00
# Ghép các véc tơ con
t <- c(x,y,z, c(1,2,3,4,5,6))
t
##  [1] 1.00 2.00 3.00 3.14 1.00 2.00 3.00 5.00 4.00 6.00 1.00 2.00 3.00 3.14 1.00
## [16] 2.00 3.00 5.00 4.00 6.00 1.00 2.00 3.00 4.00 5.00 6.00

Trong cấu trúc dữ liệu kiểu Véc tơ, chúng ta sử dụng một số hàm chức năng khác để thao tác với cấu trúc dữ liệu này:

  • Sử dụng hàm seq(from=…, to=…, by=…) để tạo ra một chuỗi số cách đều nhau. Trong đó cho phép chúng ta tạo ra một véc tơ số bất kỳ với tham số by có nghĩa là khoảng cách giữa các số.

  • Sử dụng toán tử slicing (:) . Giống với Python, R cung cấp toán tử trượt (slicing operator) để thao tác với véc tơ.

    num1 <- seq(from = 1, to = 5, by = 1)
    num2 <- c(1:8)
    num1 
    ## [1] 1 2 3 4 5
    num2
    ## [1] 1 2 3 4 5 6 7 8
    # Viết tóm tắt
    num3 <- seq(1,5,1)
    num3
    ## [1] 1 2 3 4 5

Bài tập áp dụng:

  1. Hãy tạo một véc tơ số từ 1 đến 50, khoảng cách giữa các số liên tiếp là 3 đơn vị.
  2. Tạo ra một véc tơ số có dạng (1,2,3,…,30,29,28,…,1).
  3. Tạo ra một véc tơ số theo biểu thức \(e^xcos(x)\). Với x là một véc tơ có giá trị (1,2,3,4,…10)

Kiểu Factor

Factors là một cấu trúc dữ liệu đặc biệt để làm việc với dữ liệu phân loại. Dữ liệu phân loại biểu thị dữ liệu chỉ khác nhau theo nhãn (chẳng hạn như ‘có’ / ‘không’) hoặc xếp hạng (chẳng hạn như ‘thứ nhất’, ‘thứ hai’, v.v.). Trong R, Factors là một kiểu đặc biệt của vectơ số nguyên có nhãn.

# Tạo ra một véc tơ factor
weekday_factor <- factor(c('M', 'T', 'W', 'Th', 'F', 'M', 'W'),
                        levels = c('M', 'T', 'W', 'Th', 'F'), 
                        labels =  c('Monday', 'Tuesday', 'Wednesday',
                                    'Thursday', 'Friday'))
weekday_factor
## [1] Monday    Tuesday   Wednesday Thursday  Friday    Monday    Wednesday
## Levels: Monday Tuesday Wednesday Thursday Friday

Hàm str () sẽ cho chúng ta biết rằng vectơ là factors, hiển thị một số cấp độ và hiển thị ánh xạ cơ bản của các cấp độ. Hàm summary () sẽ tự động đếm sự xuất hiện của các nhãn nhân tố.

str(weekday_factor)
##  Factor w/ 5 levels "Monday","Tuesday",..: 1 2 3 4 5 1 3
summary(weekday_factor)
##    Monday   Tuesday Wednesday  Thursday    Friday 
##         2         1         2         1         1

Các véc tơ factor cũng có thể được tạo bằng các vectơ số làm đầu vào. Giả sử chúng ta có một vectơ gồm các số 0 và 1 (giả sử dữ liệu của biến nhị phân nào đó được mã hóa), trong đó 1 biểu thị sự xuất hiện của một sự kiện và 0 là không xuất hiện. Đoạn mã dưới đây cho biết cách tạo một yếu tố được gắn nhãn từ dữ liệu.

event_indicator <- c(1, 0, 0, 1, 0, 0)

event_fct <- factor(event_indicator,
                    levels = c(0, 1),
                    labels = c('No', 'Yes'))

summary(event_fct)
##  No Yes 
##   4   2

Sử dụng hàm levels() cho phép xem các cấp độ của biến factor:

levels(event_fct)
## [1] "No"  "Yes"

Theo mặc định, nếu không cung cấp đầu vào cho các đối số cấp độ và nhãn trong factor (), các cấp sẽ tự động được gán theo thứ tự bảng chữ cái (đối với vectơ ký tự) hoặc thứ tự số. Các nhãn sau đó được đặt thành các giá trị mức.

fct_from_chr <- factor(c('Yes', 'No', 'No', 'Yes'))

str(fct_from_chr)
##  Factor w/ 2 levels "No","Yes": 2 1 1 2
fct_from_num <- factor(c(1, 1, 1, 4, 5))

str(fct_from_num)
##  Factor w/ 3 levels "1","4","5": 1 1 1 2 3

Thực hành với factor:

Giả sử chúng ta có một biến khảo sát (survey) dưới đây ghi lại mức độ hài lòng của khách hàng khi về dịch vụ phục vụ của nhân viên và thanh toán tiền khi vào trung tâm mua sắm ở một siêu thị tại thành phố Thái Nguyên, với các mức độ được đưa ra như sau:

  1. Không hài lòng
  2. Hài lòng
  3. Rất hài lòng

Hãy sử dụng kiến thức về factor, thống kê xem trong phiếu khảo sát trên có bao nhiêu người hài lòng, không hài lòng, rất hài lòng với chất lượng dịch vụ phục vụ?

Luật ép kiểu trong R (Coercion Rules in R)

Tất cả các phần tử của vectơ phải cùng kiểu. Khi kết hợp các kiểu dữ liệu khác nhau thành một vectơ, véc tơ đó sẽ bị ép kiểu dữ liệu theo thứ tự ưu tiên sau:

  1. character

  2. numeric

  3. integer

  4. logical

Điều này có nghĩa là nếu kết hợp các phần tử ký tự với phần tử số và số nguyên, thì tất cả các phần tử sẽ được chuyển đổi thành ký tự - character (vì nó có mức độ ưu tiên cao hơn).

vector_1 <- c(2.45, 5.1, 1L, 'character')
str(vector_1)
##  chr [1:4] "2.45" "5.1" "1" "character"

Chú ý rằng R chuyển đổi các giá trị logic theo cách sau: TRUE trở thành 1 và FALSE trở thành 0.

vector_2 <- c(4.234, 10L, TRUE, T, FALSE)
str(vector_2)
##  num [1:5] 4.23 10 1 1 0
vector_3 <- c(10L, 5L, TRUE, FALSE)
str(vector_3)
##  int [1:4] 10 5 1 0

Kiểu Ma trận

Ma trận là một cấu trúc dữ liệu R lưu trữ một tập hợp dữ liệu được sắp xếp trong một bảng 2 chiều với các hàng và cột. Giống như vectơ, tất cả các phần tử dữ liệu của ma trận phải cùng kiểu.

Nếu chúng ta xây dựng một ma trận với các vectơ với các kiểu dữ liệu khác nhau, ma trận sẽ bị ép kiểu dữ liệu với các quy tắc ưu tiên tương tự như đã nói ở phía trên. Ma trận không thể lưu trữ cột số cũng như cột ký tự. Ma trận đầu ra khi này sẽ là một ma trận chuyển thành kiểu ký tự.

Chúng ta có thể tạo ma trận bằng hàm matrix (). Các vectơ được tạo bằng hàm c (). Với ma trận sẽ được chuyển thành cbind () hoặc rbind (). Đoạn mã dưới đây thể hiện các hàm này:

Để biết được tên hàng hoặc tên cột trong ma trận, sử dụng rownames() và colnames()

# Tạo ma trận A kích thước 2x2
A <- matrix(data = c(1, 2, 3, 4), # Dữ liệu được đưa vào 
                  nrow = 2, # Số hàng của ma trận
                  ncol = 2, # Số cột của ma trận
                  byrow = TRUE) # Đọc dữ liệu theo hàng, mặc định là FALSE
# Sử dụng hàm dim() để kiểm tra chiều của ma trận
dim(A)
## [1] 2 2
# Xem ma trận A
A
##      [,1] [,2]
## [1,]    1    2
## [2,]    3    4

Ma trận có thể được tạo ra từ một véc tơ số định nghĩa sẵn

vector2 <- c(1:9)
B <- matrix(vector2, nrow = 3, ncol = 3, byrow = TRUE)
B
##      [,1] [,2] [,3]
## [1,]    1    2    3
## [2,]    4    5    6
## [3,]    7    8    9

Sử dụng cbind() để nối cột và rbind() để nối hàng

# Tạo 2 véc tơ số và ghép vào nhau.
myvec1 <- c(1:4)
myvec2 <- c(5:8)
cbind(myvec1, myvec2) -> C
C
##      myvec1 myvec2
## [1,]      1      5
## [2,]      2      6
## [3,]      3      7
## [4,]      4      8
# Xem tên cột
colnames(C)
## [1] "myvec1" "myvec2"
# Tạo ma trận theo hàng với rbind()
D <- rbind(myvec1, myvec2)
D
##        [,1] [,2] [,3] [,4]
## myvec1    1    2    3    4
## myvec2    5    6    7    8
# Xem tên hàng
rownames(D)
## [1] "myvec1" "myvec2"

Kiểu List (Danh sách)

Giống như ma trận, danh sách là đối tượng R có thể chứa nhiều vectơ. Nhưng không giống như ma trận, chúng là một chiều và có thể lưu trữ các kiểu dữ liệu hỗn hợp. Kiểu dữ liệu này rất giống với kiểu List của Python.

Ưu điểm của danh sách là chúng có thể chứa nhiều loại dữ liệu khác nhau với độ dài và kích thước khác nhau. Hãy coi danh sách như các vectơ đặc biệt có thể lưu trữ các cấu trúc dữ liệu khác nhau ở mỗi vị trí. Danh sách có thể đệ quy, nghĩa là một danh sách có thể chứa một danh sách.

Danh sách rất quan trọng vì hầu hết đầu ra từ các mô hình thống kê trong R, chẳng hạn như hồi quy tuyến tính hoặc phân cụm, được trả về dưới dạng danh sách.

Danh sách được tạo bằng hàm list (). Để lấy nội dung được đặt tên của một danh sách, nếu có, hãy sử dụng hàm names ().

Ví dụ: Chúng ta muốn khởi tạo một list có tên là my_list, trong list này có chứa các kiểu dữ liệu khác nhau (chuỗi ký tự, số thập phân, một ma trận , một list con khác). Lưu ý: List có thể tự đệ quy chính nó (trong list chứa một list khác như trong trường hợp này).

my_list <- list(char_vector = c('A', 'B'),
                numeric_vector = c(1.2, 3.4, 5, 12.01),
                a_matrix = cbind(c(1, 2), c(3, 4)),
                a_list = list(c(1L, 4L), c('A', 'D', 'E')))
my_list
## $char_vector
## [1] "A" "B"
## 
## $numeric_vector
## [1]  1.20  3.40  5.00 12.01
## 
## $a_matrix
##      [,1] [,2]
## [1,]    1    3
## [2,]    2    4
## 
## $a_list
## $a_list[[1]]
## [1] 1 4
## 
## $a_list[[2]]
## [1] "A" "D" "E"

Sử dụng hàm names() để liệt kê các thành phần của my_list

names(my_list)
## [1] "char_vector"    "numeric_vector" "a_matrix"       "a_list"

Data Frames

Cấu trúc dữ liệu phổ biến nhất trong R là dataframe. Đây là dạng dữ liệu kiểu list có 2 chiều, trong đó các biến số được liệt kê theo dòng và các cột có thể coi là các quan sát (observations). Để khởi tạo dataframe từ các vector, sử dụng data.frame().

Lưu ý: Các véc tơ trong kiểu cấu trúc dữ liệu này phải có chiều dài bằng nhau.

Để lấy tên của các biến trong khung dữ liệu, hãy sử dụng tên () hoặc colnames (). Để lấy số hàng trong khung dữ liệu, hãy sử dụng nrow () hoặc dim ().

Để lấy tên các cột biến, sử dụng names() hoặc colnames() . Để lấy được số hàng (số quan sát của dữ liệu). Sử dụng nrow() hoặc dim() . Dim ở đây là viết tắt của dimension (chiều dữ liệu). Ví dụ:

# Khởi tạo dataframe gồm 4 biến số (4 véc tơ cùng chiều dài.)
my_data <- data.frame(student_id = c(100234, 132454, 453123),
                      test_1_grade = c(82, 93, 87),
                      hw_1_grade = c(92, 89, 98),
                      session = c("7 AM", "7 PM", "7 AM"))
my_data
##   student_id test_1_grade hw_1_grade session
## 1     100234           82         92    7 AM
## 2     132454           93         89    7 PM
## 3     453123           87         98    7 AM
# Xem số quan sát
nrow(my_data)
## [1] 3
# Xem chiều của dữ liệu
dim(my_data)
## [1] 3 4

Toán tử và dữ liệu con

Các phép toán số học

R cung cấp các toán tử thường gặp như cộng trừ nhân chia đối với tất cả các phép toán. Có một điều lưu ý là khi thực hiện các phép tính (mathematical expressions) này với một véc tơ số, tất cả các phần tử của véc tơ đều được tính toán theo.

v1 <- c(1,2,3,4,5)
v2 <- (5:9)
v1 + v2
## [1]  6  8 10 12 14
v1 - v2
## [1] -4 -4 -4 -4 -4
v1 * v2
## [1]  5 12 21 32 45
v1/v2
## [1] 0.2000000 0.3333333 0.4285714 0.5000000 0.5555556
5*v1 + 6*v2
## [1] 35 46 57 68 79

Các toán tử quan hệ

Kết quả của phép toán này là True hoặc False, các phần tử trong từng véc tơ được so sánh với nhau và đưa ra giá trị dưới dạng True hoặc False.

v3 <- c(1,1,2,3,4)
v4 <- c(2,3,4,5,1)
v3 > v4
## [1] FALSE FALSE FALSE FALSE  TRUE
v3 >= v4
## [1] FALSE FALSE FALSE FALSE  TRUE
v3 != v4
## [1] TRUE TRUE TRUE TRUE TRUE

So sánh AND: Khi muốn so sánh các điều kiện xảy ra đồng thời, sử dụng phép toán AND (&). Nếu sử dụng phép so sánh OR (Sử dụng ký hiệu | ).

Cấu trúc dữ liệu con

Trong trường hợp chúng ta muốn trích xuất dữ liệu từ các véc tơ và lọc ra các phần tử thỏa mãn điều kiện nào đó, sử dụng các toán tử như []; [[]] hoặc $

Tập véc tơ con

Chúng ta có thể tách các phần tử của tập véc tơ ban đầu thành các véc tơ con khác thỏa mãn một điều kiện cho trước như sau:

# Tìm các tập con của myvec1 thỏa mãn các phần tử > 3
myvec1 <- c(1,2,3,4,5)
myvec1 > 3
## [1] FALSE FALSE FALSE  TRUE  TRUE

Như vậy có 2 phần tử là 4 và 5 của myvec1 thỏa mãn. Để liệt kê chính xác các phần tử này, sử dung toán tử []

# Liệt kê chính xác giá trị các phần tử.
myvec1 <- c(1,2,3,4,5)
myvec1[myvec1 > 3]
## [1] 4 5
# Kết hợp với điều kiện bổ sung
myvec1[myvec1 < 2 | myvec1 > 3]
## [1] 1 4 5

Giống như Python, có thể truy cập vào từng phần tử của véc tơ thông qua slicing operator (toán tử trượt). Tuy nhiên phần tử đầu tiên trong véc tơ đánh chỉ số (index) từ 1 không phải là số 0!

myvec1 <- c(1,2,3,4,5)
# Truy cập vào phần tử cuối của myvec1, hàm length() trả về độ dài véc tơ
myvec1[length(myvec1)]
## [1] 5
# Lấy dữ liệu phần tử thứ 1 và thứ 3
myvec1[c(1,3)]
## [1] 1 3

Tập con danh sách

Danh sách là một nội dung phổ biến và thường dùng trong khoa học dữ liệu, tính chất của nó đặc trưng bởi việc nó cho phép lưu trữ dữ liệu ở nhiều dạng khác nhau.

Danh sách là tập hợp của nhiều cấu trúc dữ liệu khác nhau. Để truy cập các cấu trúc dữ liệu được lưu trữ trong danh sách, chúng ta có thể sử dụng các hàm [], [[]] hoặc $.

Ví dụ nếu chúng ta có một danh sách là my_list, thì my_list [1] trả về một danh sách có phần tử đầu tiên của my_list. Điều này khác với my_list [[1]], trả về nội dung của phần tử đầu tiên của my_list.

# Khởi tạo danh sách
student_list <- list(student_id = c(12, 15),
                     section = c('001', '003'),
                     age = c(26, 20))

# Lấy phần tử đầu tiên của danh sách trên
student_list[1]
## $student_id
## [1] 12 15
# Truy xuất vào các phần tử trong phần tử đầu tiên
student_list[[1]]
## [1] 12 15
# Truy xuất vào phần tử đầu tiên của phần tử thứ nhất trong student_list
student_list$student_id[1]
## [1] 12

Tập Dataframe con

Để tập hợp con các hàng và cột của khung dữ liệu, chúng ta có thể sử dụng cú pháp sau:

my_data_frame [điều kiện hàng, điều kiện cột]

# Khởi tạo dataframe
my_data_frame <- data.frame(make = c("Toyota","Honda","Vinfast", "Toyota", 
                                     "Ford", "Honda"),
                            mpg = c(34, 33, 22, 32, 29, 27),
                            cylinders = c(4, 4, 8, 6, 6, 8))
my_data_frame
##      make mpg cylinders
## 1  Toyota  34         4
## 2   Honda  33         4
## 3 Vinfast  22         8
## 4  Toyota  32         6
## 5    Ford  29         6
## 6   Honda  27         8
# Truy cập theo chỉ số dòng, cột
# Truy cập vào dòng 1 đến dòng 3, cột 1 và cột 2
my_data_frame[1:3,1:2]
##      make mpg
## 1  Toyota  34
## 2   Honda  33
## 3 Vinfast  22
# Truy cập dòng 2, cột 1 và cột 2
my_data_frame[2, c(1,2)]
##    make mpg
## 2 Honda  33
# Truy cập dòng 2, 3 và tất cả các cột
my_data_frame[c(2,3),]
##      make mpg cylinders
## 2   Honda  33         4
## 3 Vinfast  22         8

Chỉ số Logic

Chúng ta cũng có thể chuyển các vectơ logic vào điều kiện hàng để có được một tập con dữ liệu của chúng ta. Giả sử chúng ta muốn tìm các dữ liệu trong tập dữ liệu my_data_frame các dòng xe ô tô có dung tích xi lanh lớn hơn hoặc bằng 6.

# Kiểm tra điều kiện logic
logical_condition <- my_data_frame$cylinders >= 6
logical_condition
## [1] FALSE FALSE  TRUE  TRUE  TRUE  TRUE
my_data_frame[logical_condition, ]
##      make mpg cylinders
## 3 Vinfast  22         8
## 4  Toyota  32         6
## 5    Ford  29         6
## 6   Honda  27         8

Thực tế, các nội dung trên có thể tóm gọn lại như sau:

my_data_frame[my_data_frame$cylinders >= 6, ]
##      make mpg cylinders
## 3 Vinfast  22         8
## 4  Toyota  32         6
## 5    Ford  29         6
## 6   Honda  27         8

Mở rộng dữ liệu DATAFRAME

Vì bản chất dữ liệu của Dataframe này là một danh sách (List), để trích xuất dữ liệu từ dataframe, chúng ta có thể sử dụng tách dữ liệu từ dataframe như dạng list

# Trích xuất dữ liệu theo vector, lấy cột đầu tiên của dataframe
my_data_frame[[1]] -> value1
value1
## [1] "Toyota"  "Honda"   "Vinfast" "Toyota"  "Ford"    "Honda"

Thực hành:

Trong nội dung này, chúng ta sẽ thực hành với bài tập kiểu list như sau, tập dữ liệu về điểm trung bình, tuổi và id của các sinh viên của một lớp học.

my_list <- list(classes_offered = c("MIS 431", "MIS 310", "MIS 410", "MIS 412"),
                student_data = data.frame(student_id = c(54, 100, 32, 423, 
                                                         2, 19, 39),
                                          age = c(18, 22, 27, 18, 29, 
                                                  22, 20),
                                          gpa = c(3.1, 2.8, 3.7, 3.4, 3.2, 
                                                  3.4, 3.2),
                                          stringsAsFactors = FALSE))

Viết mã R để tính giá trị trung vị của biến gpa trong tập student_data.

Gợi ý viết hàm median()

my_median <- function(x){
    # Sắp xếp dữ liệu theo chiều tăng hoặc giảm dần trước
   x <- sort(x)
   # Nếu số phần tử của véc tơ x là chẵn
   if((length(x) %% 2) == 0){
    return((x[length(x)/2] + x[length(x)/2 + 1]) / 2)
   }
   # Nếu số phần tử của véc tơ x là lẻ
   else{
     return(x[(length(x)/2) + 0.5])
   }
}

Các hàm sắp xếp sort(), order() và rank()

Đây là ba hàm cơ bản về sắp xếp theo thứ bậc của dữ liệu. Hàm sort theo mặc định sẽ sắp xếp dữ liệu theo chiều giảm dần. Hàm order cho phép hiển thị ra chỉ mục dòng của dữ liệu gốc sau khi đã sắp xếp và hàm rank cho phép hiển thị các chỉ mục theo thứ hạng (mặc định số bé nhất có hạng là 1). Chúng ta cùng quan sát hình sau để thấy rõ sự khác biệt giữa ba hàm này.

Sắp xếp chỉ số

Chúng ta cùng đến với một ví dụ cụ thể như sau: Trong tập dữ liệu murders của gói dslabs có chứa thông tin về dân số và số lượng tội phạm sử dụng súng tại 51 bang của nước Mỹ được thống kê bởi FBI vào năm 2010, hãy sử dụng các hàm sắp xếp trên để đưa ra thông tin:

  1. Liệt kê ra tên của 5 Bang có số lượng tội phạm lớn nhất Mỹ, các Bang này nằm ở các dòng nào của dữ liệu?

  2. Liệt kê ra tên của 5 Bang có số lượng dân số thấp nhất nước Mỹ, các Bang này nằm ở các dòng nào của dữ liệu?

library(dslabs)
data("murders")
# Lưu trữ véc tơ tên của các Bang, số tội phạm
toipham <- murders$total
states <- murders$state
population <- murders$population
# Tạo chỉ mục véc tơ dùng order
index <- order(-murders$total)
# Liệt kê theo tên các Bang có tội phạm lớn nhất
mydata1 <- data.frame(states[index], toipham[rank(index)], rank(index))
c("State","Number of murders","Index Row") -> tenbien
colnames(mydata1) <- tenbien
head(mydata1, n = 5)
##          State Number of murders Index Row
## 1   California              1257         5
## 2        Texas               805        44
## 3      Florida               669        10
## 4     New York               517        33
## 5 Pennsylvania               457        39
# Liệt kê ra 5 bang có số dân thấp nhất của Mỹ
index2 <- order(murders$population)
mydata2 <- data.frame(states[index2], population[rank(index2)], rank(index2))
c("State","Population","Index Row") -> tenbien1
colnames(mydata2) <- tenbien1
head(mydata2, n = 5)
##                  State Population Index Row
## 1              Wyoming     563626        51
## 2 District of Columbia     601723         9
## 3              Vermont     625741        46
## 4         North Dakota     672591        35
## 5               Alaska     710231         2

Z-score

Trong phân tích dữ liệu thống kê, z-score là một nội dung rất quan trọng mà chúng ta cần quan tâm. Điểm z rất quan trọng vì là cơ sở trong xác suất thống kê, chúng ta cần làm rõ ba câu hỏi:

  • Điểm z (z-score) là gì

  • Tại sao lại cần sử dụng điểm z

  • Điểm z dùng làm gì?

Giả sử chúng ta có kết quả điểm thi như sau

Điểm thi của bạn
Môn thi Điểm thi của bạn Điểm trung bình của lớp Độ lệch chuẩn
Tin học Đại Cương 26/60 31.7 5.5
Lập trình Python 37/70 50.1 10.6

Câu hỏi đặt ra là: Bạn đạt điểm thi môn nào tốt hơn, Python hay Tin Đại cương? và làm thế nào để đánh giá được.

Để trả lời cho câu hỏi này, chúng ta có ba cách:

  • So sánh mức điểm cần đạt với mức yêu cầu (So sánh thang điểm 10)

  • So sánh với điểm trung bình của cả lớp

  • So sánh với các bạn trong lớp, xem là mình ở vị trí thứ bao nhiêu

Việc quan trọng trong câu hỏi này là chúng ta nên quy chuẩn về cùng một thang điểm và so sánh, điều này còn được gọi là chuẩn hóa. Ví dụ chuyển thang điểm 60 và 70 ở trên về cùng một tháng điểm 10

Như vậy điểm z-score còn gọi là điểm chuẩn z, là phương pháp chuẩn hóa bằng cách chuyển giá trị thô thành thang điểm độ lệch chuẩn (so với giá trị trung bình).

Chúng ta có công thức tính điểm z như sau, đối với tổng thể và mẫu

\[ z-score=\frac{X-\mu}{\sigma} \]

\[ z-score=\frac{X-\bar{X}}{S} \]

Môn thi Điểm thi của bạn Điểm trung bình của lớp Độ lệch chuẩn z-score
Tin học Đại Cương 26/60 31.7 5.5 -1.04
Lập trình Python 37/70 50.1 10.6 -1.24

Để tính điểm z-score trong R, chúng ta làm như sau:

data <- c(8, 7, 7, 10, 13, 14, 15, 16, 18) 
zscore <- function(x){
  z_scores <- (x-mean(x))/sd(x)
  return(z_scores)
}

zscore(data)
## [1] -0.9701425 -1.2126781 -1.2126781 -0.4850713  0.2425356  0.4850713  0.7276069
## [8]  0.9701425  1.4552138

Phân phối chuẩn của điểm z

Như vậy giá trị điểm z của một quan sát chính là khoảng cách từ quan sát đó đến giá trị trung bình, đây là bản chất của điểm z. Phân phối chuẩn của điểm z rất quan trọng vì dựa vào phân phối của z có thể tính xác suất của quan sát.

Điểm z có thể sử dụng cho 4 mục đích:

  1. Điểm z được sử dụng để tính % phân vị

  2. Để tính khoảng cách giữa hai quan sát

  3. Để so sánh hai quan sát có thang điểm khác nhau

  4. Để xác định giá trị ngoại biên (outlier)

Chúng ta cùng quan sát lại giá trị điểm z và % phân vị của hai môn học quan sát ở phía trên và tính điểm z cho các giá trị là 26 và 37

Để tính % phân vị cho điểm z, chúng ta sử dụng hàm pnorm()

diemthi_zcore <- c(-1.04, -1.24)
pnorm(diemthi_zcore)
## [1] 0.1491700 0.1074877

% phân vị cho các quan sát

Như vậy đối với môn học Tin học Đại cương chúng ta có điểm % z-score là 14.9% thì vùng da cam chiếm 14.9%, phần còn lại là: 85.1%, hay nói cách khác là bạn đang đứng ở phân vị thứ 14.9%, nếu lớp học có 100 người thì bạn đứng thứ 15.

Hoàn toàn tương tự như vậy đối với môn Lập trình Python thì % z-score là 10.7% hay bạn đứng ở vị trí thứ 11 nếu lớp có 100 bạn.

Điểm z-score được sử dụng để tính khoảng cách giữa hai quan sát. Quan sát một ví dụ như sau:

z-score để sử dụng để tính khoảng cách giữa hai quan sát

Dựa vào hình trên, chúng ta thấy là khoảng cách giữa hai người A và B là 71.7%

Điều quan trọng nhất của điểm z là so sánh hai quan sát có thang đo khác nhau như trong trường hợp này. Ngoài ra điểm z-score còn được dùng để xác định giá trị ngoại lai (outlier).

Giá trị ngoại lai

Phân tích dữ liệu cơ bản

Trong nội dung này, chúng ta cùng bàn chi tiết về phân tích dữ liệu trong R với sự trợ giúp của hệ sinh thái tidyverse

Khái niệm hàm

Function được định nghĩa là hàm trong lập trình, giúp cho người lập trình tiết kiệm thời gian, linh hoạt hơn trong việc xử lý tính toán và phân tích dữ liệu. Hàm được viết ra để giúp ngắn thời gian viết code lệnh và tăng khả năng thực thi và xử lý lệnh lặp lại trong chương trình.

Trong R và các ngôn ngữ khác, khái niệm built-in functions ám chỉ các hàm được xây dựng sẵn và chỉ cần truyền tham số vào hàm là có thể sử dụng được. Một số hàm thống kê cơ bản trong R có thể liệt kê là:

  • min() Đưa ra giá trị nhỏ nhất

  • max() Đưa ra giá trị lớn nhất

  • range() Đưa ra giá trị khoảng biến thiên của dữ liệu

  • median() Đưa ra giá trị trung vị của dữ liệu

  • fivenum() Kết quả đưa ra là một véc tơ gồm 5 giá trị bao gồm: giá trị nhỏ nhất, tứ phân vị 25, trung vị, tứ phân vị 75, giá trị lớn nhất

  • quantile() Đưa ra giá tị của phân vị do người dùng định nghĩa

Giả sử chúng ta có một véc tơ số như sau:

myvector1 <- c(1,2,3,5,1,2,3,8,4,12,19,30,21,12)
min(myvector1)
## [1] 1
max(myvector1)
## [1] 30
median(myvector1)
## [1] 4.5
range(myvector1)
## [1]  1 30
# Phân vị 30 %
quantile(myvector1, 0.3)
## 30% 
## 2.9
# Phân vị 30, 60 và 90
quantile(myvector1, c(0.3, 0.6, 0.9))
##  30%  60%  90% 
##  2.9  7.4 20.4
fivenum(myvector1)
## [1]  1.0  2.0  4.5 12.0 30.0

Tổng và tổng tích lũy

Sử dụng hàm cumcumsum để tính tổng tích lũy

myvector2 <- c(1:9)
sum(myvector2)
## [1] 45
cumsum(myvector2)
## [1]  1  3  6 10 15 21 28 36 45

Hàm rank

Hàm này cho phép sắp xếp theo thứ hạng của từng phần tử trong tập dữ liệu. Mặc định, phần tử có giá trị nhỏ nhất sẽ có hàng là 1.

negative_data <- c(-2, 4.5, -6, 10, 12)
abs(negative_data) -> abs_data
rank(abs_data)
## [1] 1 2 3 4 5
rank(-abs_data)
## [1] 5 4 3 2 1

Xây dựng hàm trong R

Thông thường để xây dựng (viết) hàm trong R, chúng ta sử dụng cú pháp như sau:

Xây dựng hàm trong R

Trong nội dung này, chúng ta sẽ cùng xây dựng một số hàm cơ bản trong thống kê như sau:

  • Hàm trả về giá trị tính tổng của véc tơ
  • Hàm trả về giá trị min(hoặc max) của một véc tơ
  • Hàm tính phương sai, độ lệch chuẩn của một véc tơ
  • Hàm tính hiệp phương sai, hiệp biến (covariance) của một véc tơ
  • Hàm tính hệ số tương quan Pearson của hai biến tuân theo phân phối chuẩn.
# Xây dựng véc tơ x gồm 10 phần tử từ 1 đến 10
z <- c(1:10)
# Xây dựng hàm tính tổng các phần tử của một véc tơ
tinhtong <- function(x){
  tong = 0
  for(i in 1:length(x)){
    tong = tong + x[i]
  }
  return(tong)
}
# Kiểm tra hàm xây dựng
tinhtong(z)
## [1] 55
sum(z)
## [1] 55
sum(z) == tinhtong(z)
## [1] TRUE
# Xây dựng hàm tìm giá trị nhỏ nhất
min_value <- function(x) {
  my_min = x[1]
  for (i in seq_along(x)) {
    if (x[i] < my_min) my_min = x[i]
  }
  return(my_min)
}
# Kiểm tra hàm
min_value(z)
## [1] 1
# Viết hàm xác định tính chẵn, lẻ của một số, chia module %%, chia integer %/%
chanle <- function(x){
  if(x %% 2 == 0){
    print(paste0(x,": là số chẵn"))
  }
  else{
    print(paste0(x,": là số lẻ"))
  }
}
# Test hàm
chanle(5)
## [1] "5: là s<U+1ED1> l<U+1EBB>"
# Sử dụng hàm ifelse(condition, TRUE, FALSE)
x <- 1:5
ifelse(x %%2 == 0, paste0(x,": là số chẵn"), paste0(x, ": là số lẻ"))
## [1] "1: là s<U+1ED1> l<U+1EBB>"   "2: là s<U+1ED1> ch<U+1EB5>n"
## [3] "3: là s<U+1ED1> l<U+1EBB>"   "4: là s<U+1ED1> ch<U+1EB5>n"
## [5] "5: là s<U+1ED1> l<U+1EBB>"
# Viết hàm tính tổng, trung bình, phương sai, min, max của một véc tơ số
tong <- function(x){
  total = 0
  for(i in 1:length(x)){
    total = total + x[i]
  }
  return (total)
}

# Test hàm
y <- c(1:100)
tong(y)
## [1] 5050
# Hàm tính giá trị trung bình
trungbinh <- function(x){
  trungbinh_x <- tong(x)/length(x)
  return(trungbinh_x)
}
# Test hàm
trungbinh(y)
## [1] 50.5
#  Hàm tính phương sai của một véc tơ số
phuongsai <- function(x){
  binhphuong <- (x - trungbinh(x))^2
  total = 0
  for (i in 1:length(x)){
    total = total + binhphuong[i]
  }
  ketqua = total/(length(x)-1)
  return(ketqua)
}
# Test hàm
phuongsai(y)
## [1] 841.6667
var(y)
## [1] 841.6667
# Xây dựng hàm tính độ lệch chuẩn
ham_sd <- function(x){
  ketqua = sqrt(phuongsai(x))
  return(ketqua)
}
ham_sd(y)
## [1] 29.01149
sd(y)
## [1] 29.01149
# Hàm tính giá trị nhỏ nhất
ham_min <- function(x){
  nhonhat = x[1]
  for(i in 1:length(x)){
    if (nhonhat > x[i]){
      nhonhat = x[i]
    }
  }
  return(nhonhat)
}
# Test hàm
ham_min(y)
## [1] 1
min(y)
## [1] 1
# Viết hàm tính hệ số tương quan giữa 2 biến số trong R
# Viết hàm tính hiệp phương sai covariance
covariance <- function(x, y){
  a = (x - mean(x))*(y-mean(y))
  sum = 0
  if(length(x) == length(y)){
    for (i in 1:length(x)){
      sum = sum + a[i]
    }
  }
  else {
    print("Hai véc tơ phải cùng chiều dài!")
    break
  }
  return(sum/(length(x)-1))
}
x1 <- c(1:9)
y1 <- c(2:10)
covariance(x1, y1)
## [1] 7.5
cov(x1,y1)
## [1] 7.5
# Tương quan Pearson
pearson <- function(x, y){
  heso <- covariance(x, y)/(sqrt(phuongsai(x)*phuongsai(y)))
  return(heso)}

Giới thiệu về Tidyverse

Phần này sẽ trình bày những kiến thức cơ bản về thao tác dữ liệu bằng cách sử dụng thư viện tidyverse. Tidyverse là một hệ sinh thái gồm các thư viện chuyên biệt được thiết kế cho việc phân tích khoa học dữ liệu.

Thao tác dữ liệu cơ bản

Trong nội dung này, chúng ta cùng xem xét tập dữ liệu Employee Attrition Data (Phân bổ nhân viên). Dữ liệu bao gồm 1.470 hồ sơ nhân viên cho một công ty kinh doanh sản phẩm có trụ sở tại Hoa Kỳ.

Để hiểu rõ các biến số của tập dữ liệu này, tham khảo bảng sau đây:

library(readr)
library(dplyr)
library(skimr)
## Warning: package 'skimr' was built under R version 4.1.2
employee_data <- read_rds("employee_data.rds")

# View data
employee_data %>% 
  head(n=10)
## # A tibble: 10 x 13
##    left_company department       job_level   salary weekly_hours business_travel
##    <fct>        <fct>            <fct>        <dbl>        <dbl> <fct>          
##  1 Yes          Sales            Director    1.19e5           56 Rarely         
##  2 No           Sales            Senior Man~ 8.56e4           42 Frequently     
##  3 Yes          Product Develop~ Associate   4.62e4           56 Rarely         
##  4 No           IT and Analytics Director    1.17e5           50 Frequently     
##  5 No           Sales            Associate   3.66e4           46 Rarely         
##  6 No           Marketing        Senior Man~ 8.35e4           48 Frequently     
##  7 No           Marketing        Senior Man~ 8.86e4           44 Rarely         
##  8 No           Sales            Director    1.22e5           47 Rarely         
##  9 No           Finance and Ope~ Senior Man~ 9.46e4           50 Frequently     
## 10 No           Product Develop~ Director    1.25e5           51 Rarely         
## # ... with 7 more variables: yrs_at_company <int>, yrs_since_promotion <int>,
## #   previous_companies <dbl>, job_satisfaction <fct>, performance_rating <fct>,
## #   marital_status <fct>, miles_from_home <int>
skim(employee_data, left_company, department, salary, weekly_hours)
Table 1: Data summary
Name employee_data
Number of rows 1470
Number of columns 13
_______________________
Column type frequency:
factor 2
numeric 2
________________________
Group variables None

Variable type: factor

skim_variable n_missing complete_rate ordered n_unique top_counts
left_company 0 1 FALSE 2 No: 1233, Yes: 237
department 0 1 FALSE 6 IT : 399, Res: 293, Sal: 252, Mar: 238

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
salary 0 1 94076.25 37590.24 29848.56 70379.48 88555.53 117099.9 212134.7 ▃▇▃▁▁
weekly_hours 0 1 50.02 4.82 40.00 47.00 49.00 52.0 66.0 ▂▇▃▂▁

Sau đây chúng ta cùng thao tác với một vài hàm xử lý dữ liệu cơ bản:

# Lọc ra dữ liệu các nhân viên rời công ty
filter(employee_data, left_company == "Yes") %>% 
  head()
## # A tibble: 6 x 13
##   left_company department          job_level salary weekly_hours business_travel
##   <fct>        <fct>               <fct>      <dbl>        <dbl> <fct>          
## 1 Yes          Sales               Director  1.19e5           56 Rarely         
## 2 Yes          Product Development Associate 4.62e4           56 Rarely         
## 3 Yes          Marketing           Manager   6.39e4           57 Rarely         
## 4 Yes          Finance and Operat~ Manager   5.65e4           58 Rarely         
## 5 Yes          Marketing           Director  1.09e5           54 Rarely         
## 6 Yes          Research            Director  1.19e5           62 Frequently     
## # ... with 7 more variables: yrs_at_company <int>, yrs_since_promotion <int>,
## #   previous_companies <dbl>, job_satisfaction <fct>, performance_rating <fct>,
## #   marital_status <fct>, miles_from_home <int>
# Lọc dữ liệu với nhân viên rời công ty, làm ở bộ phận Sales
filter(employee_data, left_company == "Yes", department == "Sales") %>% 
  head()
## # A tibble: 6 x 13
##   left_company department job_level       salary weekly_hours business_travel
##   <fct>        <fct>      <fct>            <dbl>        <dbl> <fct>          
## 1 Yes          Sales      Director       118681.           56 Rarely         
## 2 Yes          Sales      Associate       38539.           57 Rarely         
## 3 Yes          Sales      Manager         82916.           63 Frequently     
## 4 Yes          Sales      Associate       37529.           60 Rarely         
## 5 Yes          Sales      Associate       44875.           59 Rarely         
## 6 Yes          Sales      Senior Manager  95996.           60 Rarely         
## # ... with 7 more variables: yrs_at_company <int>, yrs_since_promotion <int>,
## #   previous_companies <dbl>, job_satisfaction <fct>, performance_rating <fct>,
## #   marital_status <fct>, miles_from_home <int>
# Lọc dữ liệu với nhân viên làm bộ phân Sales hoặc Marketing
filter(employee_data, department == "Sales" | department == "Marketing") %>% 
  head()
## # A tibble: 6 x 13
##   left_company department job_level       salary weekly_hours business_travel
##   <fct>        <fct>      <fct>            <dbl>        <dbl> <fct>          
## 1 Yes          Sales      Director       118681.           56 Rarely         
## 2 No           Sales      Senior Manager  85576.           42 Frequently     
## 3 No           Sales      Associate       36635.           46 Rarely         
## 4 No           Marketing  Senior Manager  83520.           48 Frequently     
## 5 No           Marketing  Senior Manager  88556.           44 Rarely         
## 6 No           Sales      Director       122281.           47 Rarely         
## # ... with 7 more variables: yrs_at_company <int>, yrs_since_promotion <int>,
## #   previous_companies <dbl>, job_satisfaction <fct>, performance_rating <fct>,
## #   marital_status <fct>, miles_from_home <int>
# Sử dụng toán tử %in% lặp lại công việc như trên
filter(employee_data, department %in% c('Sales', 'Marketing')) %>% 
  head()
## # A tibble: 6 x 13
##   left_company department job_level       salary weekly_hours business_travel
##   <fct>        <fct>      <fct>            <dbl>        <dbl> <fct>          
## 1 Yes          Sales      Director       118681.           56 Rarely         
## 2 No           Sales      Senior Manager  85576.           42 Frequently     
## 3 No           Sales      Associate       36635.           46 Rarely         
## 4 No           Marketing  Senior Manager  83520.           48 Frequently     
## 5 No           Marketing  Senior Manager  88556.           44 Rarely         
## 6 No           Sales      Director       122281.           47 Rarely         
## # ... with 7 more variables: yrs_at_company <int>, yrs_since_promotion <int>,
## #   previous_companies <dbl>, job_satisfaction <fct>, performance_rating <fct>,
## #   marital_status <fct>, miles_from_home <int>
# Tìm ra các nhân viên có lương trên 80000 và làm ở bộ phận Sales hoặc Marketing
filter(employee_data, salary > 80000, department %in% c('Sales', 'Marketing')) %>% 
  head()
## # A tibble: 6 x 13
##   left_company department job_level       salary weekly_hours business_travel
##   <fct>        <fct>      <fct>            <dbl>        <dbl> <fct>          
## 1 Yes          Sales      Director       118681.           56 Rarely         
## 2 No           Sales      Senior Manager  85576.           42 Frequently     
## 3 No           Marketing  Senior Manager  83520.           48 Frequently     
## 4 No           Marketing  Senior Manager  88556.           44 Rarely         
## 5 No           Sales      Director       122281.           47 Rarely         
## 6 No           Sales      Director       122821.           46 Rarely         
## # ... with 7 more variables: yrs_at_company <int>, yrs_since_promotion <int>,
## #   previous_companies <dbl>, job_satisfaction <fct>, performance_rating <fct>,
## #   marital_status <fct>, miles_from_home <int>
# Lựa chọn các biến hiển thị 
select(employee_data, left_company, department, job_level) %>% 
  head()
## # A tibble: 6 x 3
##   left_company department          job_level     
##   <fct>        <fct>               <fct>         
## 1 Yes          Sales               Director      
## 2 No           Sales               Senior Manager
## 3 Yes          Product Development Associate     
## 4 No           IT and Analytics    Director      
## 5 No           Sales               Associate     
## 6 No           Marketing           Senior Manager
# Cách khác lựa chọn cột chỉ số 1,2,3
# select(employee_data, c(1, 2, 3))
# Không hiển thị các biến job_level, department
select(employee_data, -department, -job_level) %>% 
  head()
## # A tibble: 6 x 11
##   left_company  salary weekly_hours business_travel yrs_at_company
##   <fct>          <dbl>        <dbl> <fct>                    <int>
## 1 Yes          118681.           56 Rarely                       6
## 2 No            85576.           42 Frequently                  10
## 3 Yes           46236.           56 Rarely                       0
## 4 No           117227.           50 Frequently                   8
## 5 No            36635.           46 Rarely                       2
## 6 No            83520.           48 Frequently                   7
## # ... with 6 more variables: yrs_since_promotion <int>,
## #   previous_companies <dbl>, job_satisfaction <fct>, performance_rating <fct>,
## #   marital_status <fct>, miles_from_home <int>
# Hiển thị tất cả các biến có chứa từ khóa là job
select(employee_data, contains('job')) %>% 
  head()
## # A tibble: 6 x 2
##   job_level      job_satisfaction
##   <fct>          <fct>           
## 1 Director       Very High       
## 2 Senior Manager Medium          
## 3 Associate      High            
## 4 Director       High            
## 5 Associate      Medium          
## 6 Senior Manager Very High
# Hiển thị tất cả các biến có bắt đầu bằng ký tự y
select(employee_data, starts_with("y")) %>% 
  head()
## # A tibble: 6 x 2
##   yrs_at_company yrs_since_promotion
##            <int>               <int>
## 1              6                   0
## 2             10                   1
## 3              0                   0
## 4              8                   3
## 5              2                   2
## 6              7                   3
# Sắp xếp tăng dần theo tình trạng và lương
arrange(employee_data, left_company, salary) %>% 
  head()
## # A tibble: 6 x 13
##   left_company department          job_level salary weekly_hours business_travel
##   <fct>        <fct>               <fct>      <dbl>        <dbl> <fct>          
## 1 No           IT and Analytics    Associate 29849.           50 Rarely         
## 2 No           Marketing           Associate 30559.           48 Rarely         
## 3 No           Sales               Associate 32306.           50 Rarely         
## 4 No           Product Development Associate 32444.           48 None           
## 5 No           Research            Associate 33277.           49 Frequently     
## 6 No           Research            Associate 33319.           52 Rarely         
## # ... with 7 more variables: yrs_at_company <int>, yrs_since_promotion <int>,
## #   previous_companies <dbl>, job_satisfaction <fct>, performance_rating <fct>,
## #   marital_status <fct>, miles_from_home <int>
# Thống kê lương trung bình của toàn nhân viên
summarise(employee_data, average_salary = mean(salary))
## # A tibble: 1 x 1
##   average_salary
##            <dbl>
## 1         94076.
# thống kê tổng quan một số đại lượng khác
summarise(employee_data, salary_min = min(salary),
                         salary_25th = quantile(salary, 0.25),
                         salary_50th = median(salary),
                         salary_75th = quantile(salary, 0.75),
                         salary_max =  max(salary))
## # A tibble: 1 x 5
##   salary_min salary_25th salary_50th salary_75th salary_max
##        <dbl>       <dbl>       <dbl>       <dbl>      <dbl>
## 1     29849.      70379.      88556.     117100.    212135.
# Tính z-score cho biến salary
employee_data_scaled <-  mutate(employee_data, 
                                salary_scaled = (salary - mean(salary))/sd(salary)) 
select(employee_data_scaled, salary, salary_scaled) %>% 
  head()
## # A tibble: 6 x 2
##    salary salary_scaled
##     <dbl>         <dbl>
## 1 118681.         0.655
## 2  85576.        -0.226
## 3  46236.        -1.27 
## 4 117227.         0.616
## 5  36635.        -1.53 
## 6  83520.        -0.281

Thao tác dữ liệu nâng cao

Trong nội dung này, chúng ta cùng tìm hiểu thêm một số thao tác phân tích dữ liệu nâng cao, sử dụng toán tử pipe (%>%) trong thư viện dplyr. Để tìm hiểu về toán tử pipe, xem thêm ở đây.

Thống kê theo nhóm

Tiếp tục tìm hiểu về tập dữ liệu phân bổ nhân viên việc làm, cùng tìm hiểu về dữ liệu để trả lời cho câu hỏi sau: Đối với những nhân viên đã rời công ty thì số giờ làm việc có lớn hơn giờ làm việc trung bình/tuần của tất cả các nhân viên không?

# Khởi tạo một biến mới theo z-score đặt tên là hours_scaled
employee_data %>% 
  mutate(hours_scaled = (weekly_hours - mean(weekly_hours)) / sd(weekly_hours)) %>% 
  filter(left_company == 'Yes') %>% 
  summarise(avg_weekly_hours = mean(hours_scaled))
## # A tibble: 1 x 1
##   avg_weekly_hours
##              <dbl>
## 1             1.78

Theo dữ liệu được phân tích ở trên, chúng ta đã chuẩn hóa thành z-score cho biến giờ làm việc, khi đó giờ trung bình làm việc sẽ quy đổi về 0 với tất cả các nhân viên. Như vậy với các nhân viên đã rời công ty thì xu hướng đều làm việc nhiều hơn so với trung bình với các nhân viên khác là khoảng gần 2 tiếng (1.78 giờ).

Bây giờ, chúng ta sẽ đến với một câu hỏi khác: Hãy tính giá trị lương trung bình của tất cả các nhân viên theo các phòng ban chức năng?

employee_data %>% 
  group_by(department) %>% # Sắp xếp theo nhóm phòng ban
  summarise(average_salary = mean(salary)) # Thống kê theo từng nhóm
## # A tibble: 6 x 2
##   department             average_salary
##   <fct>                           <dbl>
## 1 Marketing                      93266.
## 2 Sales                          90149.
## 3 Research                       99425.
## 4 Product Development            92429.
## 5 IT and Analytics               93898.
## 6 Finance and Operations         93893.

Mở rộng thêm vấn đề, liệu lương trung bình của tất cả các phòng ban này đối với các đối tượng vẫn đang làm việc và các đối tượng hiện không còn làm việc có sự khác biệt hay không?

employee_data %>% 
  group_by(left_company, department) %>% # Nhóm theo tình trạng làm việc và phòng ban chức năng
  summarise(average_salary = mean(salary)) # Tính toán thống kê theo tổ hợp nhóm
## `summarise()` has grouped output by 'left_company'. You can override using the
## `.groups` argument.
## # A tibble: 12 x 3
## # Groups:   left_company [2]
##    left_company department             average_salary
##    <fct>        <fct>                           <dbl>
##  1 No           Marketing                      98656.
##  2 No           Sales                          96305.
##  3 No           Research                      100321.
##  4 No           Product Development            98406.
##  5 No           IT and Analytics               94137.
##  6 No           Finance and Operations        100265.
##  7 Yes          Marketing                      66582.
##  8 Yes          Sales                          76158.
##  9 Yes          Research                       71157.
## 10 Yes          Product Development            77131.
## 11 Yes          IT and Analytics               90950.
## 12 Yes          Finance and Operations         77657.

Một kết quả khá hiển nhiên là với những nhân viên đã rời công ty thì lương của họ khi còn ở vị trí công tác tại các phòng chức năng thấp hơn so với các đối tượng hiện đang công tác.

Có thể hiển thị kết quả trên theo một cách khác để tiện so sánh hơn:

employee_data %>% 
  group_by( department,left_company) %>% # Nhóm theo tình trạng làm việc và phòng ban chức năng
  summarise(average_salary = mean(salary)) # Tính toán thống kê theo tổ hợp nhóm
## `summarise()` has grouped output by 'department'. You can override using the
## `.groups` argument.
## # A tibble: 12 x 3
## # Groups:   department [6]
##    department             left_company average_salary
##    <fct>                  <fct>                 <dbl>
##  1 Marketing              No                   98656.
##  2 Marketing              Yes                  66582.
##  3 Sales                  No                   96305.
##  4 Sales                  Yes                  76158.
##  5 Research               No                  100321.
##  6 Research               Yes                  71157.
##  7 Product Development    No                   98406.
##  8 Product Development    Yes                  77131.
##  9 IT and Analytics       No                   94137.
## 10 IT and Analytics       Yes                  90950.
## 11 Finance and Operations No                  100265.
## 12 Finance and Operations Yes                  77657.

Kết hợp với mutate để tính z-score cho từng nhân viên theo các bộ phận phòng ban

employee_data %>% 
  group_by(department) %>% 
  mutate(salary_scaled_dept = (salary - mean(salary)) / sd(salary)) %>% 
  select(department, salary_scaled_dept) %>% 
  head()
## # A tibble: 6 x 2
## # Groups:   department [4]
##   department          salary_scaled_dept
##   <fct>                            <dbl>
## 1 Sales                            0.756
## 2 Sales                           -0.121
## 3 Product Development             -1.24 
## 4 IT and Analytics                 0.624
## 5 Sales                           -1.42 
## 6 Marketing                       -0.267

Trong trường hợp chúng ta muốn so sánh độ lệch không phải cho toàn bộ các nhân viên mà so sánh độ lệch về lương của từng nhân viên cho từng nhóm, thao tác như sau:

employee_data %>% 
  mutate(salary_scaled = (salary - mean(salary)) / sd(salary)) %>% # Chuẩn hóa cho toàn bộ nhân viên
  group_by(department) %>% # Sắp xếp theo nhóm
  mutate(salary_scaled_dept = (salary - mean(salary)) / sd(salary)) %>% # Chuẩn hóa cho từng nhóm riêng biệt
  select(department, salary_scaled, salary_scaled_dept) %>% 
  head()
## # A tibble: 6 x 3
## # Groups:   department [4]
##   department          salary_scaled salary_scaled_dept
##   <fct>                       <dbl>              <dbl>
## 1 Sales                       0.655              0.756
## 2 Sales                      -0.226             -0.121
## 3 Product Development        -1.27              -1.24 
## 4 IT and Analytics            0.616              0.624
## 5 Sales                      -1.53              -1.42 
## 6 Marketing                  -0.281             -0.267

Group_by và skim

Hàm skim cho phép tạo thống kê theo nhóm rất nhanh và tiện dụng

# Thống kê toàn bộ tập dữ liệu theo phòng ban chức năng
 #employee_data %>% 
 # group_by(department) %>% 
  #skim()
# Thống kê theo biến lựa chọn
employee_data %>% group_by(department) %>% 
  skim(left_company, salary)
Table 2: Data summary
Name Piped data
Number of rows 1470
Number of columns 13
_______________________
Column type frequency:
factor 1
numeric 1
________________________
Group variables department

Variable type: factor

skim_variable department n_missing complete_rate ordered n_unique top_counts
left_company Marketing 0 1 FALSE 2 No: 198, Yes: 40
left_company Sales 0 1 FALSE 2 No: 175, Yes: 77
left_company Research 0 1 FALSE 2 No: 284, Yes: 9
left_company Product Development 0 1 FALSE 2 No: 128, Yes: 50
left_company IT and Analytics 0 1 FALSE 2 No: 369, Yes: 30
left_company Finance and Operations 0 1 FALSE 2 No: 79, Yes: 31

Variable type: numeric

skim_variable department n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
salary Marketing 0 1 93265.58 36514.10 30559.08 72344.72 87856.66 117603.1 206581.5 ▂▇▃▁▁
salary Sales 0 1 90148.67 37746.84 30488.15 66664.75 85180.55 116104.2 197965.1 ▅▇▅▁▂
salary Research 0 1 99425.10 37606.81 33276.54 73047.08 91205.32 119657.4 203528.9 ▂▇▅▁▂
salary Product Development 0 1 92429.42 37322.31 32443.79 68360.73 87066.84 114993.6 195345.4 ▅▇▅▁▂
salary IT and Analytics 0 1 93897.64 37367.83 29848.56 70115.29 88521.84 116088.1 211621.0 ▃▇▃▁▁
salary Finance and Operations 0 1 93893.34 39945.11 37115.29 70317.59 88760.26 113516.5 212134.7 ▆▇▃▁▂

Thống kê dòng với n() và count()

Hàm n() và count() cho phép thống kê số lượng dòng theo từng nhóm kết hợp với sử dụng group_by()

# Thống kê tổng số nhân viên đã rời công ty và đang làm việc
employee_data %>% group_by(left_company) %>% 
                  summarise(number_employees = n())
## # A tibble: 2 x 2
##   left_company number_employees
##   <fct>                   <int>
## 1 No                       1233
## 2 Yes                       237

Hàm count() cho phép thao tác thống kê đếm với trường hợp sử dụng cho biến factor không quá nhiều giá trị. Ví dụ câu hỏi đặt ra: Hãy thống kê số lượng sinh viên theo từng phòng ban chức năng của công ty?

employee_data %>% 
  count(department)
## # A tibble: 6 x 2
##   department                 n
##   <fct>                  <int>
## 1 Marketing                238
## 2 Sales                    252
## 3 Research                 293
## 4 Product Development      178
## 5 IT and Analytics         399
## 6 Finance and Operations   110

Trong trường hợp chúng ta muốn sắp xếp theo giá trị giảm dần thì làm như sau

# Thống kê và sắp xếp giảm dần
employee_data %>% 
  count(department, sort = TRUE)
## # A tibble: 6 x 2
##   department                 n
##   <fct>                  <int>
## 1 IT and Analytics         399
## 2 Research                 293
## 3 Sales                    252
## 4 Marketing                238
## 5 Product Development      178
## 6 Finance and Operations   110

Hoặc thêm tên cho biến số lượng thay vì hiển thị là n

employee_data %>% 
  count(department, sort = TRUE, name = "number_of_employees")
## # A tibble: 6 x 2
##   department             number_of_employees
##   <fct>                                <int>
## 1 IT and Analytics                       399
## 2 Research                               293
## 3 Sales                                  252
## 4 Marketing                              238
## 5 Product Development                    178
## 6 Finance and Operations                 110

Hàm count() cũng cho phép truyền thêm tổ hợp đối số vào hàm để đưa ra thông tin phân loại hữu ích hơn, ví dụ: Thống kê số lượng nhân viên của từng phòng ban và theo tình trạng làm việc:

employee_data %>% 
  count(left_company, department, name = 'number_of_employees')
## # A tibble: 12 x 3
##    left_company department             number_of_employees
##    <fct>        <fct>                                <int>
##  1 No           Marketing                              198
##  2 No           Sales                                  175
##  3 No           Research                               284
##  4 No           Product Development                    128
##  5 No           IT and Analytics                       369
##  6 No           Finance and Operations                  79
##  7 Yes          Marketing                               40
##  8 Yes          Sales                                   77
##  9 Yes          Research                                 9
## 10 Yes          Product Development                     50
## 11 Yes          IT and Analytics                        30
## 12 Yes          Finance and Operations                  31
employee_data %>% count(left_company, department, name = 'number_of_employees') %>% 
  arrange(department, left_company)
## # A tibble: 12 x 3
##    left_company department             number_of_employees
##    <fct>        <fct>                                <int>
##  1 No           Marketing                              198
##  2 Yes          Marketing                               40
##  3 No           Sales                                  175
##  4 Yes          Sales                                   77
##  5 No           Research                               284
##  6 Yes          Research                                 9
##  7 No           Product Development                    128
##  8 Yes          Product Development                     50
##  9 No           IT and Analytics                       369
## 10 Yes          IT and Analytics                        30
## 11 No           Finance and Operations                  79
## 12 Yes          Finance and Operations                  31

Câu hỏi tiếp theo đặt ra như sau: Số lượng nhân viên hài lòng với công việc của mình là bao nhiêu, theo từng cấp độ, khoảng cách của họ từ nhà tới công sở?

employee_data %>% group_by(job_satisfaction) %>% 
                  summarise(number_of_employees = n(),
                            avg_miles = mean(miles_from_home))
## # A tibble: 4 x 3
##   job_satisfaction number_of_employees avg_miles
##   <fct>                          <int>     <dbl>
## 1 Low                              289      9.19
## 2 Medium                           280      9.10
## 3 High                             442      9.42
## 4 Very High                        459      9.03
employee_data %>% 
  count(job_satisfaction, name = 'number_of_employees')
## # A tibble: 4 x 2
##   job_satisfaction number_of_employees
##   <fct>                          <int>
## 1 Low                              289
## 2 Medium                           280
## 3 High                             442
## 4 Very High                        459

Một điều cần chú ý khi sử dụng hàm group_by() đó là khi chúng ta muốn thực hiện các phương pháp thống kê hoặc thêm biến theo nhóm, theo các biến phân loại, tuy nhiên khi chúng ta muốn sử dụng các kết quả sau khi thu được cho toàn bộ hàng dữ liệu thì sẽ xảy ra lỗi vì khi đó R sẽ tính toán các giá trị cho từng dữ liệu nhóm phân biệt. Để tránh hiện tượng này xảy ra, thông thường chúng ta cần ungroup() sau khi thực hiện thống kê nhóm. Câu hỏi cụ thể trong trường hợp này là: Hãy tính tỷ lệ % của tổng số nhân viên theo chức vụ cụ thể so với toàn thể nhân viên của công ty.

employee_data %>% group_by(left_company, job_level) %>% 
                  summarise(employees = n()) %>% 
                  ungroup() %>% 
                  mutate(percent_of_total_employees = 100*(employees/sum(employees)))
## `summarise()` has grouped output by 'left_company'. You can override using the
## `.groups` argument.
## # A tibble: 10 x 4
##    left_company job_level      employees percent_of_total_employees
##    <fct>        <fct>              <int>                      <dbl>
##  1 No           Associate            113                       7.69
##  2 No           Manager              251                      17.1 
##  3 No           Senior Manager       447                      30.4 
##  4 No           Director             303                      20.6 
##  5 No           Vice President       119                       8.10
##  6 Yes          Associate             72                       4.90
##  7 Yes          Manager               93                       6.33
##  8 Yes          Senior Manager        29                       1.97
##  9 Yes          Director              28                       1.90
## 10 Yes          Vice President        15                       1.02

Các giá trị ngoại lệ (distinct values)

Sử dụng hàm n_distinct() để đếm ra các giá trị mang tính duy nhất (unique) của biến. Giả sử trong 1470 quan sát phía trên, muốn đếm số lượng phòng ban chức năng phân biệt, làm như sau:

employee_data %>% 
  summarise(number_of_departments = n_distinct(department))
## # A tibble: 1 x 1
##   number_of_departments
##                   <int>
## 1                     6

Kết quả trả về là 6, tức là có 6 bộ phận chức năng riêng biệt.

Xếp hạng dữ liệu với top_n()

Hàm top_n() cho biết kết quả trả về các giá trị cao nhất của quan sát trong tập dữ liệu theo biến được lựa chọn. Ví dụ: Hãy liệt kê ra top 5 người có lương cao nhất trong tập dữ liệu

# Liệt kê ra top 5 người có lương cao nhất
employee_data %>% 
  top_n(5, salary)
## # A tibble: 5 x 13
##   left_company department       job_level    salary weekly_hours business_travel
##   <fct>        <fct>            <fct>         <dbl>        <dbl> <fct>          
## 1 No           IT and Analytics Vice Presid~ 2.09e5           51 Frequently     
## 2 Yes          Finance and Ope~ Vice Presid~ 2.05e5           66 Rarely         
## 3 No           Finance and Ope~ Vice Presid~ 2.12e5           49 Frequently     
## 4 Yes          Marketing        Vice Presid~ 2.07e5           62 Rarely         
## 5 Yes          IT and Analytics Vice Presid~ 2.12e5           60 Frequently     
## # ... with 7 more variables: yrs_at_company <int>, yrs_since_promotion <int>,
## #   previous_companies <dbl>, job_satisfaction <fct>, performance_rating <fct>,
## #   marital_status <fct>, miles_from_home <int>

Sử dụng hàm trượt slice

Hàm trượt slice() cho phép lấy dữ liệu theo index cụ thể của quan sát từng dòng dữ liệu. Ví dụ: Lấy ra thông tin của hàng thứ 25 đến 30

employee_data %>% slice(25:30)
## # A tibble: 6 x 13
##   left_company department job_level       salary weekly_hours business_travel
##   <fct>        <fct>      <fct>            <dbl>        <dbl> <fct>          
## 1 Yes          Marketing  Director       108649.           54 Rarely         
## 2 No           Marketing  Manager         62391.           51 Rarely         
## 3 Yes          Research   Director       119443.           62 Frequently     
## 4 No           Sales      Senior Manager  84021.           41 Rarely         
## 5 No           Research   Director       116974.           49 Rarely         
## 6 No           Marketing  Associate       37230.           51 Rarely         
## # ... with 7 more variables: yrs_at_company <int>, yrs_since_promotion <int>,
## #   previous_companies <dbl>, job_satisfaction <fct>, performance_rating <fct>,
## #   marital_status <fct>, miles_from_home <int>
# Lấy dữ liệu đơn lẻ, dòng 1, dòng 200 và dòng 1002
employee_data %>% 
  slice(1, 200, 1002)
## # A tibble: 3 x 13
##   left_company department          job_level salary weekly_hours business_travel
##   <fct>        <fct>               <fct>      <dbl>        <dbl> <fct>          
## 1 Yes          Sales               Director  1.19e5           56 Rarely         
## 2 No           Finance and Operat~ Director  1.18e5           54 Rarely         
## 3 No           Sales               Director  1.19e5           50 Rarely         
## # ... with 7 more variables: yrs_at_company <int>, yrs_since_promotion <int>,
## #   previous_companies <dbl>, job_satisfaction <fct>, performance_rating <fct>,
## #   marital_status <fct>, miles_from_home <int>

Sử dụng thay thế cho top_n(). Liệt kê ra 3 nhân viên có số thời gian công tác lâu năm nhất

employee_data %>% 
  arrange(desc(yrs_at_company)) %>% 
  slice(1:3)
## # A tibble: 3 x 13
##   left_company department       job_level  salary weekly_hours business_travel
##   <fct>        <fct>            <fct>       <dbl>        <dbl> <fct>          
## 1 Yes          Sales            Associate  37529.           60 Rarely         
## 2 No           Sales            Manager    72984.           46 Rarely         
## 3 No           IT and Analytics Director  117306.           49 Rarely         
## # ... with 7 more variables: yrs_at_company <int>, yrs_since_promotion <int>,
## #   previous_companies <dbl>, job_satisfaction <fct>, performance_rating <fct>,
## #   marital_status <fct>, miles_from_home <int>

Hàm top_n() và hàm slice() khá hữu ích tỏng trường hợp chúng ta muốn tạo tập dữ liệu con để phân tích và đọc kết quả, ví dụ: hãy liệt kê ra theo từng phòng ban chức năng, 2 nhân viên có lương cao nhất.

employee_data %>% 
  group_by(department) %>% 
                  top_n(2, salary)
## # A tibble: 12 x 13
## # Groups:   department [6]
##    left_company department       job_level   salary weekly_hours business_travel
##    <fct>        <fct>            <fct>        <dbl>        <dbl> <fct>          
##  1 No           IT and Analytics Vice Presi~ 2.09e5           51 Frequently     
##  2 No           Marketing        Vice Presi~ 2.03e5           43 None           
##  3 Yes          Finance and Ope~ Vice Presi~ 2.05e5           66 Rarely         
##  4 No           Product Develop~ Vice Presi~ 1.95e5           45 Rarely         
##  5 No           Research         Vice Presi~ 2.02e5           53 Rarely         
##  6 Yes          Product Develop~ Vice Presi~ 1.95e5           57 Rarely         
##  7 Yes          Sales            Vice Presi~ 1.96e5           58 Rarely         
##  8 No           Finance and Ope~ Vice Presi~ 2.12e5           49 Frequently     
##  9 No           Research         Vice Presi~ 2.04e5           49 Rarely         
## 10 No           Sales            Vice Presi~ 1.98e5           52 Rarely         
## 11 Yes          Marketing        Vice Presi~ 2.07e5           62 Rarely         
## 12 Yes          IT and Analytics Vice Presi~ 2.12e5           60 Frequently     
## # ... with 7 more variables: yrs_at_company <int>, yrs_since_promotion <int>,
## #   previous_companies <dbl>, job_satisfaction <fct>, performance_rating <fct>,
## #   marital_status <fct>, miles_from_home <int>

Để hình dung kết quả trên một cách dễ hiểu hơn, sử dụng sắp xếp lại lương

employee_data %>% group_by(department) %>% 
                  top_n(2, salary) %>% 
                  arrange(department, desc(salary))
## # A tibble: 12 x 13
## # Groups:   department [6]
##    left_company department       job_level   salary weekly_hours business_travel
##    <fct>        <fct>            <fct>        <dbl>        <dbl> <fct>          
##  1 Yes          Marketing        Vice Presi~ 2.07e5           62 Rarely         
##  2 No           Marketing        Vice Presi~ 2.03e5           43 None           
##  3 No           Sales            Vice Presi~ 1.98e5           52 Rarely         
##  4 Yes          Sales            Vice Presi~ 1.96e5           58 Rarely         
##  5 No           Research         Vice Presi~ 2.04e5           49 Rarely         
##  6 No           Research         Vice Presi~ 2.02e5           53 Rarely         
##  7 No           Product Develop~ Vice Presi~ 1.95e5           45 Rarely         
##  8 Yes          Product Develop~ Vice Presi~ 1.95e5           57 Rarely         
##  9 Yes          IT and Analytics Vice Presi~ 2.12e5           60 Frequently     
## 10 No           IT and Analytics Vice Presi~ 2.09e5           51 Frequently     
## 11 No           Finance and Ope~ Vice Presi~ 2.12e5           49 Frequently     
## 12 Yes          Finance and Ope~ Vice Presi~ 2.05e5           66 Rarely         
## # ... with 7 more variables: yrs_at_company <int>, yrs_since_promotion <int>,
## #   previous_companies <dbl>, job_satisfaction <fct>, performance_rating <fct>,
## #   marital_status <fct>, miles_from_home <int>

Thiết lập chỉ số logic

Các thuộc tính đặc biệt của vectơ logic

Hãy tưởng tượng chúng ta có dữ liệu từ một cuộc khảo sát mà chúng ta đã thực hiện gần đây, trong đó 7 người đã trả lời và cung cấp thông tin về tuổi của họ. Dữ liệu này được lưu trữ trong vector tuổi bên dưới.

Giẳ sử chúng ta muốn biết số người từ 30 tuổi trở lên và bao nhiêu phần trăm trong tổng số người được hỏi đại diện cho nhóm này? Khi đó chúng ta sử dụng phép so sánh theo index, để trả lời cho câu hỏi trên, sử dụng kết hợp lấy tập dữ liệu con theo véc tơ logic. Hàm sum() trả về kết quả số người trên 30 tuổi, hàm mean() trả về kết quả trung bình số người này chiếm % tổng số người được hỏi.

age <- c(23, 31, 27, 41, 54, 34, 25)
age
## [1] 23 31 27 41 54 34 25
# Kết quả trả về véc tơ logic có điều kiện
age >= 30
## [1] FALSE  TRUE FALSE  TRUE  TRUE  TRUE FALSE
sum(age >=30)
## [1] 4
mean(age >=30)
## [1] 0.5714286

Như vậy là có 4 người có tuổi trên 30 và số người này chiếm 57% số người được hỏi khảo sát.

Áp dụng vào tập dữ liệu về phân bổ nhân viên trong công ty. Câu hỏi đặt ra là: Có bao nhiêu người trong công ty có lương dưới 60.000$ trong từng lĩnh vực phòng ban và số lượng người này chiếm bao nhiêu % trong từng phòng ban đó?

employee_data %>% group_by(department) %>% 
                  summarise(employees = n(),
                            employees_less_60 = sum(salary < 60000),
                            employees_less_60_prop = mean(salary < 60000))
## # A tibble: 6 x 4
##   department             employees employees_less_60 employees_less_60_prop
##   <fct>                      <int>             <int>                  <dbl>
## 1 Marketing                    238                31                 0.130 
## 2 Sales                        252                46                 0.183 
## 3 Research                     293                23                 0.0785
## 4 Product Development          178                28                 0.157 
## 5 IT and Analytics             399                57                 0.143 
## 6 Finance and Operations       110                16                 0.145

Cuối cùng, chúng ta cùng xét một ví dụ đơn giản như sau về một survey khảo sát về thông tin của nhân viên

survey <- tibble(age = c(26, 31, 28, 42, 31, 37, 51, 29),
                 job_function = c('Biotechnology', 'Analytics', 'Machine Learning',
                                  'Marketing','Biotechnology', 'Machine Learning', 
                                  'Analytics', 'Biotechnology'),
                 job_industry = c('Healthcare', 'Healthcare', 'Financial Services',
                                  'Retail', 'Non-Profit', 'Education',
                                  'Retail', 'Healthcare'),
                 job_level = c('Entry', 'Mid', 'Mid', 'Senior', 'Mid', 
                               'Mid', 'Senior', 'Mid'),
                 salary = c(75500, 87600, 97000, 92000, 89000,
                            108500, 121000, 94000))

survey
## # A tibble: 8 x 5
##     age job_function     job_industry       job_level salary
##   <dbl> <chr>            <chr>              <chr>      <dbl>
## 1    26 Biotechnology    Healthcare         Entry      75500
## 2    31 Analytics        Healthcare         Mid        87600
## 3    28 Machine Learning Financial Services Mid        97000
## 4    42 Marketing        Retail             Senior     92000
## 5    31 Biotechnology    Non-Profit         Mid        89000
## 6    37 Machine Learning Education          Mid       108500
## 7    51 Analytics        Retail             Senior    121000
## 8    29 Biotechnology    Healthcare         Mid        94000

Trong nhiều trường hợp, chúng ta cần mã hóa (recoding) lại các biến là biến phân loại theo yêu cầu của bài toán, khi đó chúng ta sử dụng hàm recode().

# Thay thế giá trị Analytics bằng Data Science và
# Machine Learning bằng Data Science
recode(survey$job_function,
       'Analytics' = 'Data Science',
       'Machine Learning' = 'Data Science')
## [1] "Biotechnology" "Data Science"  "Data Science"  "Marketing"    
## [5] "Biotechnology" "Data Science"  "Data Science"  "Biotechnology"
# Sử dụng recode_factor cho biến factor
survey %>% 
  mutate(job_function_chr = recode(job_function,
                                 'Analytics' = 'Data Science',
                                 'Machine Learning' = 'Data Science'),
         job_function_fct = recode_factor(job_function,
                                          'Analytics' = 'Data Science',
                                          'Machine Learning' = 'Data Science'))
## # A tibble: 8 x 7
##     age job_function     job_industry       job_level salary job_function_chr
##   <dbl> <chr>            <chr>              <chr>      <dbl> <chr>           
## 1    26 Biotechnology    Healthcare         Entry      75500 Biotechnology   
## 2    31 Analytics        Healthcare         Mid        87600 Data Science    
## 3    28 Machine Learning Financial Services Mid        97000 Data Science    
## 4    42 Marketing        Retail             Senior     92000 Marketing       
## 5    31 Biotechnology    Non-Profit         Mid        89000 Biotechnology   
## 6    37 Machine Learning Education          Mid       108500 Data Science    
## 7    51 Analytics        Retail             Senior    121000 Data Science    
## 8    29 Biotechnology    Healthcare         Mid        94000 Biotechnology   
## # ... with 1 more variable: job_function_fct <fct>

Tạo biến mới với case_when()

Hàm case_when () trong gói dplyr đặc biệt hữu ích khi chúng ta cần tạo một biến mới dựa trên sự kết hợp phức tạp của các biến hiện có trong tập dữ liệu. Hàm case_when () nhận một chuỗi các công thức hai vế. Bên vế trái xác định giá trị nào phù hợp với trường hợp này và bên vế phải cung cấp giá trị thay thế.

Cú pháp chung là điều kiện logic ~ giá trị thay thế, trong đó điều kiện logic có thể liên quan đến nhiều biến từ một khung dữ liệu. Dãy kết thúc bằng giá trị TRUE ~ cho tất cả các trường hợp khác.

Ví dụ:

# tạo tập dữ liệu survey_updated với biến ds_biotech_30 mới
survey_updated <- survey %>% 
                  mutate(ds_biotech_30 = case_when(age >= 30 & job_function %in% c('Analytics', 'Machine Learning') ~ 'Data Science, 30+', age >= 30 & job_function == "Biotechnology" ~ "Biotechnology, 30+",
TRUE ~ 'Other'))

survey_updated
## # A tibble: 8 x 6
##     age job_function     job_industry       job_level salary ds_biotech_30     
##   <dbl> <chr>            <chr>              <chr>      <dbl> <chr>             
## 1    26 Biotechnology    Healthcare         Entry      75500 Other             
## 2    31 Analytics        Healthcare         Mid        87600 Data Science, 30+ 
## 3    28 Machine Learning Financial Services Mid        97000 Other             
## 4    42 Marketing        Retail             Senior     92000 Other             
## 5    31 Biotechnology    Non-Profit         Mid        89000 Biotechnology, 30+
## 6    37 Machine Learning Education          Mid       108500 Data Science, 30+ 
## 7    51 Analytics        Retail             Senior    121000 Data Science, 30+ 
## 8    29 Biotechnology    Healthcare         Mid        94000 Other

Tạo biến nhóm phân loại (Binning Numeric Variables)

Chia một biến dữ liệu ở dạng số thành các nhóm (biến phân loại) là một dạng điển hình trong tạo biến nhóm phân loại trong thống kê (phương pháp chia khoảng). Trong nội dung này, chúng ta sử dụng hàm cut(). Bài toán đặt ra là có các mức tuổi khác nhau và đưa dữ liệu từ bảng survey trên cho các đối tượng về biến phân loại.

# Dữ liệu biến tuổi ban đầu
survey$age
## [1] 26 31 28 42 31 37 51 29
cut(x = survey$age,
    breaks = c(-Inf, 24, 35, Inf), # Lấy khoảng dữ liệu từ 24-35
    labels = c("Less than 25", "25 - 35", "36 and older"), # Các mức tuổi
    right = TRUE) # Cho phép lấy khoảng đóng bên phải
## [1] 25 - 35      25 - 35      25 - 35      36 and older 25 - 35     
## [6] 36 and older 36 and older 25 - 35     
## Levels: Less than 25 25 - 35 36 and older

Sử dụng pipe và dplyr

survey %>% 
  mutate(age_category = cut(age,
                            breaks = c(-Inf, 24, 35, Inf),
                            labels = c("Less than 25", "25 - 35", "36 and older"),
                            right = TRUE)) %>% 
  select(age, age_category)
## # A tibble: 8 x 2
##     age age_category
##   <dbl> <fct>       
## 1    26 25 - 35     
## 2    31 25 - 35     
## 3    28 25 - 35     
## 4    42 36 and older
## 5    31 25 - 35     
## 6    37 36 and older
## 7    51 36 and older
## 8    29 25 - 35

Chia khoảng tự động - Auto binning

Để chia khoảng tự động, có 3 hàm hữu ích có thể liệt kê, đó là:

  • cut_interval () tạo n nhóm có khoảng bằng nhau

  • cut_number () tạo thành n nhóm có (xấp xỉ) số lượng quan sát bằng nhau

  • cut_width () tạo các nhóm có chiều rộng nhất định

# Dữ liệu gốc
survey$age
## [1] 26 31 28 42 31 37 51 29
# Chia thành 3 khoảng đều nhau
age_interval <- cut_interval(survey$age, n = 3)
age_interval
## [1] [26,34.3]   [26,34.3]   [26,34.3]   (34.3,42.7] [26,34.3]   (34.3,42.7]
## [7] (42.7,51]   [26,34.3]  
## Levels: [26,34.3] (34.3,42.7] (42.7,51]
summary(age_interval)
##   [26,34.3] (34.3,42.7]   (42.7,51] 
##           5           2           1

Sử dụng cut_number() chia dữ liệu thành các khoảng có số quan sát bằng nhau

# cut_number
age_number <- cut_number(survey$age, n = 3)
age_number
## [1] [26,29.7] (29.7,35] [26,29.7] (35,51]   (29.7,35] (35,51]   (35,51]  
## [8] [26,29.7]
## Levels: [26,29.7] (29.7,35] (35,51]
summary(age_number)
## [26,29.7] (29.7,35]   (35,51] 
##         3         2         3

Sử dụng cut_width() để cắt theo chiều dài của dữ liệu

survey$age
## [1] 26 31 28 42 31 37 51 29
age_width <- cut_width(survey$age, width = 10, boundary = 0)
age_width
## [1] [20,30] (30,40] [20,30] (40,50] (30,40] (30,40] (50,60] [20,30]
## Levels: [20,30] (30,40] (40,50] (50,60]
summary(age_width)
## [20,30] (30,40] (40,50] (50,60] 
##       3       3       1       1

Ví dụ thực hành với cut()

Hãy sử dụng hàm cut_width() để hoàn thành quy trình phân tích dữ liệu sau với dplyr

  • Bắt đầu với dữ liệu employ_data

  • Tạo biến mile_category chứa các giá trị mile_from_home theo gia số (increment) 5 dặm

  • Nhóm biểu mẫu theo left_company và mile_category

  • Đếm số lượng nhân viên thuộc mỗi nhóm

employee_data %>% 
  mutate(miles_category = cut_width(miles_from_home, 
         width = 5, boundary = 0)) %>% 
  group_by(left_company, miles_category) %>% 
  summarise(employees = n())
## `summarise()` has grouped output by 'left_company'. You can override using the
## `.groups` argument.
## # A tibble: 12 x 3
## # Groups:   left_company [2]
##    left_company miles_category employees
##    <fct>        <fct>              <int>
##  1 No           [0,5]                545
##  2 No           (5,10]               337
##  3 No           (10,15]               90
##  4 No           (15,20]              102
##  5 No           (20,25]               85
##  6 No           (25,30]               74
##  7 Yes          [0,5]                 87
##  8 Yes          (5,10]                57
##  9 Yes          (10,15]               25
## 10 Yes          (15,20]               23
## 11 Yes          (20,25]               32
## 12 Yes          (25,30]               13

  1. Department of Computer Sciences and Engineering, Faculty of Information Technology, Thai Nguyen University of Information Technology and Communication, ↩︎

  2. Head of Department of Computer Sciences and Engineering, Faculty of Information Technology, Thai Nguyen University of Information Technology and Communication, ↩︎

  3. Advanced class of Information Technology K18, Thai Nguyen University of Information Technology and Communication, ↩︎

  4. Advanced class of Information Technology K18, Thai Nguyen University of Information Technology and Communication, ↩︎