3  Làm sạch (p1)

Trước buổi học
  • Tải data thực hành

  • Cái đặt các package sau (chỉ cần làm 1 lần):
install.packages(c("tidyverse", "readxl", "writexl", "stringi", "janitor"))
  • Và load các package:
library(tidyverse)
library(writexl)
library(readxl)
library(stringi)
library(janitor)
Mục tiêu
  1. Hiểu các thuật ngữ trong R
  2. Hiểu các datatype trong R
  3. Học các thao tác cơ bản khi làm việc với data.frame
  4. Học về các package hỗ trợ khi làm việc với các datatype phức tạp hơn (string, Date)

3.1 Tại sao làm sạch số liệu bằng R?

Sử dụng R để làm sạch số liệu có nhiều ưu điểm hơn dùng excel:

  • R có nhiều chức năng tìm và sàng lọc vấn đề cần làm sạch: Có thể viết thành 1 quy trình kiểm tra dữ liệu tự động, kết hợp thống kê mô tả và trực quan hóa để phát hiện vấn đề cần làm sạch. Có nhiều chức năng đã được các tác giả khác viết thành những package thuận tiện cho việc sử dụng.

  • Tất cả các công đoạn làm sạch đều được lưu lại: Chúng ta có thể lưu thành file R code hoặc Rmd, trong đó mỗi câu lệnh là 1 hay nhiều bước làm sạch. Trong tương lai, chỉ cần đọc file R code hoặc Rmd để dễ dàng tra cứu lại những thay đổi giữa file số liệu được phân tích và file gốc. Nếu dự án nghiên cứu có thành viên mới tham gia, quá trình chuyển giao công việc sẽ thuận lợi hơn vì mọi thứ đều được lưu trong file R code hoặc Rmd nên sẽ không sợ bị sót hay quên thông tin.

  • Tiết kiệm thời gian làm sạch số liệu mới: Nếu tiếp tục các đợt thu thập số liệu mới, có thể tái sử dụng file R code hoặc Rmd để làm sạch các vấn đề tương tự đã gặp trong số liệu cũ đã xử lý.

3.2 Đối tượng trong R

Mọi thứ được lưu trữ trong R - bao gồm bộ dữ liệu, biến số, danh sách tên cột, kết quả đầu ra như biểu đồ - đều là các đối tượng, được gán têncó thể được tham chiếu trong các lệnh sau đó.

Để tạo đối tượng trong R, ta dùng cú pháp sau

tên_đối_tượng <- giá_trị_của_đối_tượng 

VD: Đọc file vaccine_data.xlsx để sử dụng trong bài học

# tạo đối tượng file_path để lưu trữ đường dẫn đến file excel
data_path <- "data/vaccine_data.xlsx"

# tạo đối tượng df lưu trữ bảng dữ liệu đọc từ file excel
df <- read_excel(path = data_path)

Sau khi tạo các đối tượng, ta có thể thử xem giá trị được lưu trữ

data_path
[1] "data/vaccine_data.xlsx"
# sử dụng lệnh head để xem 6 hàng đầu thay vì toàn bộ bảng
head(df) 
# A tibble: 6 × 21
     id gioitinh ngaysinh   huyen    xa    tinh  `VGB <24` `VGB >24` VGB_1 VGB_2
  <dbl> <chr>    <chr>      <chr>    <chr> <chr> <chr>     <chr>     <chr> <chr>
1     1 nam      27/02/2024 Bình Ch… Phườ… Thàn… 28/02/20… 29/02/20… 10/0… 20/0…
2     2 nữ       06/03/2024 Huyện T… Phườ… Thàn… 07/03/20… 08/03/20… 23/0… 27/0…
3     3 nữ       27/01/2024 Thành p… Phườ… Thàn… 28/01/20… 29/01/20… 08/0… 18/0…
4     4 nữ       12/02/2024 Hóc Môn  Xã Đ… Thàn… 13/02/20… 14/02/20… 17/0… 27/0…
5     5 nữ       21/02/2024 Bình Ch… Xã B… Thàn… 21/02/20… 20/02/20… 23/0… 09/0…
6     6 nữ       07/03/2024 Quận 7   Phú … Thàn… 07/03/20… 08/03/20… 23/0… 02/0…
# ℹ 11 more variables: VGB_3 <chr>, `VGB_4+` <chr>, HG_1 <chr>, HG_2 <chr>,
#   HG_3 <chr>, `HG_4+` <chr>, UV_1 <chr>, UV_2 <chr>, UV_3 <chr>,
#   `UV_4+` <chr>, tinhtrang <chr>
Thông tin về vaccine_data

Bộ dữ liệu được tạo ra dựa trên format của dữ liệu tiêm chủng. Giá trị của bộ dữ liệu là giả, được tạo một cách ngẫu nhiên và có thể không khớp với lịch tiêm chủng thực tế.

Nhiều giá trị được làm sai (dữ liệu thiếu, mũi 2 trước mũi 1, địa chỉ không phù hợp, …) có chủ đích cho bài học làm sạch phần 2

Từ điển biến số

Tên biến Ý nghĩa Khoảng giá trị
id số thứ tự trẻ
gioitinh giới tính nam, nữ
ngaysinh ngày sinh của trẻ [01/01/2014, 01/04/2024]
huyen địa chỉ đăng ký tiêm chủng
xa địa chỉ đăng ký tiêm chủng
tinh địa chỉ đăng ký tiêm chủng
VGB <24 Ngày tiêm mũi vắc xin Viêm gan B sơ sinh trước 24h
VGB >24 Ngày tiêm mũi vắc xin Viêm gan B sơ sinh sau 24h (tiêm trong vòng 28 ngày đầu sau sinh)
VGB_1 Ngày tiêm mũi tiêm chứa kháng nguyên Viêm gan B mũi 1
VGB_2 Ngày tiêm mũi tiêm chứa kháng nguyên Viêm gan B mũi 2
VGB_3 Ngày tiêm mũi tiêm chứa kháng nguyên Viêm gan B mũi 3
VGB_4+ Ngày tiêm mũi tiêm chứa kháng nguyên Viêm gan B mũi 4 trở lên (mũi gần nhất)
HG_1 Ngày tiêm mũi tiêm chứa kháng nguyên ho gà mũi 1
HG_2 Ngày tiêm mũi tiêm chứa kháng nguyên ho gà mũi 2
HG_3 Ngày tiêm mũi tiêm chứa kháng nguyên ho gà mũi 3
HG_4+ Ngày tiêm mũi tiêm chứa kháng nguyên ho gà mũi 4 trở lên (mũi gần nhất)
UV_1 Ngày tiêm mũi tiêm chứa kháng nguyên uốn ván mũi 1
UV_2 Ngày tiêm mũi tiêm chứa kháng nguyên uốn ván mũi 2
UV_3 Ngày tiêm mũi tiêm chứa kháng nguyên uốn ván mũi 3
UV_4+ Ngày tiêm mũi tiêm chứa kháng nguyên uốn ván mũi 4 trở lên (mũi gần nhất)
tinhtrang Trạng thái theo dõi “ngừng theo dõi”, “theo dõi”
Lưu ý

Giá trị của một đối tượng có thể bị ghi đè bất kỳ lúc nào bằng cách chạy lệnh gán để định nghĩa lại giá trị của nó. Do đó, thứ tự của các lệnh được chạy rất quan trọng.

Lệnh sau sẽ định nghĩa lại giá trị cho đối tượng data_path

data_path <- "data/new_path.xlsx"

# kiểm tra giá trị 
data_path
[1] "data/new_path.xlsx"

3.3 Câu văn trong R

Câu lệnh (command) hoặc hàm (function) chính là những câu văn trong R để giao tiếp với máy tính. Chúng ta phải học ngữ pháp của 1 câu lệnh và ý nghĩa của nó để có thể sử dụng R để ra lệnh cho máy tính. Một câu lệnh trong R thường có dạng như sau:

tên-câu-lệnh(tên-tham-số = data-đầu-vào hoặc lựa-chọn)
  • Tên câu lệnh: nằm trước dấu (), thể hiện ý nghĩa câu lệnh dùng để làm gì.
  • Tên tham số (argument): nằm trong dấu () là những tham số của câu lệnh đó. Tham số là những yêu cầu nhỏ và cụ thể để máy tính hiểu rõ hơn yêu cầu của người dùng.
  • Dấu =: dùng để gán 1 giá trị cho tham số. Giá trị gán cho tham số có thể là một data đầu vào, hoặc một lựa chọn của người dùng (đúng/sai).

VD:

max(x = df$id, na.rm = TRUE)
[1] 1000
  • Tên câu lệnh là max: chúng ta đang yêu cầu máy tính hãy tìm giá trị lớn nhất của 1 biến.
  • Các tham số là xna.rm:
    • x: là data đầu vào, ở đây chúng ta viết x = df$id để cho máy tính biết data đầu vào là cột id trong bảng data df.
    • na.rm: là yêu cầu cụ thể cách giải quyết khi gặp missing value (NA), ở đây chúng ta lựa chọn na.rm = TRUE nghĩa là nếu cột id có NA thì bỏ những giá trị NA đi trước khi tìm giá trị lớn nhất.
Tip

Để đọc hướng dẫn sử dụng về câu lệnh, gõ ?tên-câu-lệnh.

3.4 Loại dữ liệu (datatype) trong R

Trong R, mỗi đối tượng được quy định phải theo 1 loại dữ liệu (datatype) nhất định

3.4.1 Các datatype trong R

Datatype Ý nghĩa Ví dụ
numeric dữ liệu dạng số -1, -2, 0, 1.5, …
logical dữ liệu dạng đúng/sai [TRUE, FALSE] hoặc [1, 0]
character dữ liệu dạng ký tự (hay còn gọi là string), được đặt trong dấu ngoặc kép "".
đối tượng dạng ký tự thì không thể tính toán
Date dữ liệu ngày tháng 2024-04-01, 2024-04-02, …
factor dữ liệu dạng phân loại (categorical) giới tính: nam, nữ
tỉnh thành
dân tộc
data.frame dữ liệu dạng bảng
tibble dữ liệu dạng bảng tương tự như data.frame, sự khác biệt chính là tibble in đẹp hơn trong R console
Tip

Để kiểm tra datatype của 1 đối tượng, sử dụng lệnh class(tên_đối_tượng)

Để kiểm tra datatype của các cột trong data.frame, ta có thể sử dụng lệnh str hoặc kiểm tra tại mục Data của RStudio

VD:

# kiểm tra datatype của df
class(df)
[1] "tbl_df"     "tbl"        "data.frame"
# kiểm tra datatype của các cột trong df
str(df)
tibble [1,000 × 21] (S3: tbl_df/tbl/data.frame)
 $ id       : num [1:1000] 1 2 3 4 5 6 7 8 9 10 ...
 $ gioitinh : chr [1:1000] "nam" "nữ" "nữ" "nữ" ...
 $ ngaysinh : chr [1:1000] "27/02/2024" "06/03/2024" "27/01/2024" "12/02/2024" ...
 $ huyen    : chr [1:1000] "Bình Chánh" "Huyện Thạnh Phú" "Thành phố Vĩnh Long" "Hóc Môn" ...
 $ xa       : chr [1:1000] "Phường Bình Hưng Hòa" "Phường 01" "Phường Phú Thuận" "Xã Đông Thạnh" ...
 $ tinh     : chr [1:1000] "Thành phố Hồ Chí Minh" "Thành phố Hồ Chí Minh" "Thành phố Hồ Chí Minh" "Thành phố Hồ Chí Minh" ...
 $ VGB <24  : chr [1:1000] "28/02/2024" "07/03/2024" "28/01/2024" "13/02/2024" ...
 $ VGB >24  : chr [1:1000] "29/02/2024" "08/03/2024" "29/01/2024" "14/02/2024" ...
 $ VGB_1    : chr [1:1000] "10/03/2024" "23/03/2024" "08/02/2024" "17/02/2024" ...
 $ VGB_2    : chr [1:1000] "20/03/2024" "27/03/2024" "18/02/2024" "27/02/2024" ...
 $ VGB_3    : chr [1:1000] "20/03/2024" "11/04/2024" "19/02/2024" "28/02/2024" ...
 $ VGB_4+   : chr [1:1000] "04/04/2024" "26/04/2024" "19/02/2024" "28/02/2024" ...
 $ HG_1     : chr [1:1000] "28/02/2024" "07/03/2024" "28/01/2024" "12/02/2024" ...
 $ HG_2     : chr [1:1000] "02/03/2024" "22/03/2024" "07/02/2024" "16/02/2024" ...
 $ HG_3     : chr [1:1000] "12/03/2024" "06/04/2024" "17/02/2024" "02/03/2024" ...
 $ HG_4+    : chr [1:1000] "12/03/2024" "16/04/2024" "27/02/2024" "12/03/2024" ...
 $ UV_1     : chr [1:1000] "27/02/2024" "07/03/2024" "27/01/2024" "12/02/2024" ...
 $ UV_2     : chr [1:1000] "28/02/2024" "22/03/2024" "06/02/2024" "22/02/2024" ...
 $ UV_3     : chr [1:1000] "09/03/2024" "06/04/2024" "21/02/2024" "08/03/2024" ...
 $ UV_4+    : chr [1:1000] "19/03/2024" "21/04/2024" "02/03/2024" "23/03/2024" ...
 $ tinhtrang: chr [1:1000] "theo dõi" "ngừng theo dõi" "ngừng theo dõi" "theo dõi" ...

3.4.2 Chuyển đổi datatype

Cú pháp chung cho thay đổi datatype là as.datatype()

Ví dụ như:

  • as.numeric() chuyển thành định dạng số.

  • as.character() chuyển thành định dạng văn bản.

  • as.factor() chuyển thành định dạng factor.

  • as.Date() chuyển thành định dạng ngày.

Ngoài ra, còn có các lệnh khác để phục vụ cho các mục đích cụ thể hơn, bao gồm:

  • ifelse() chuyển thành định dạng logical theo điều kiện nhất định

  • is.na() chuyển thành định dạng logical, các dữ liệu trống (NA) có giá trị TRUE và ngược lại.

  • dmy(), ymd(), mdy(), … chuyển đổi string theo các format khác nhau thành định dạng ngày

3.5 Các thao tác thông dụng với data.frame

3.5.1 Làm sạch tên cột

Trong R, tên cột là giá trị trên đỉnh của một cột.

Các quy tắc cho tên cột thường bao gồm:

  • Tên ngắn

  • Không có khoảng trắng (thay thế bằng dấu gạch dưới _ ).

  • Không có ký tự đặc biệt (&, #, <, >, …) hoặc dấu.

  • Không bắt đầu bằng số.

Lệnh clean_names() của package janitor được sử dụng để tự động hoá quá trình chuẩn hoá tên cột. Ngoài ra, lệnh rename() của package dplyr được sử dụng để thay đổi tên cột một cách thủ công.

df %>% colnames()
 [1] "id"        "gioitinh"  "ngaysinh"  "huyen"     "xa"        "tinh"     
 [7] "VGB <24"   "VGB >24"   "VGB_1"     "VGB_2"     "VGB_3"     "VGB_4+"   
[13] "HG_1"      "HG_2"      "HG_3"      "HG_4+"     "UV_1"      "UV_2"     
[19] "UV_3"      "UV_4+"     "tinhtrang"
df %>% clean_names() %>% colnames()
 [1] "id"        "gioitinh"  "ngaysinh"  "huyen"     "xa"        "tinh"     
 [7] "vgb_24"    "vgb_24_2"  "vgb_1"     "vgb_2"     "vgb_3"     "vgb_4"    
[13] "hg_1"      "hg_2"      "hg_3"      "hg_4"      "uv_1"      "uv_2"     
[19] "uv_3"      "uv_4"      "tinhtrang"
df <- df %>% 
  clean_names() %>% # dùng clean names để tự động làm sạch tên cột
  rename( # dùng rename để chỉnh sửa thêm
    vgb_truoc_24 = vgb_24,
    vgb_sau_24 = vgb_24_2
  )
Lưu ý
  • Dấu <- được sử dụng ở VD trên để gán giá trị mới vào cột Country (tương đương việc UPDATE cột)

  • Nếu không sử dụng <- thì R chỉ trả về kết quả của các câu lệnh nhưng bảng df sẽ không có thay đổi

Xem tên các cột

Lệnh colnames được sử dụng để xem danh sách tên các cột trong bảng

Dấu %>% (pipe)

%>% được dùng để chuyển đối tượng vào 1 function (trong VD đầu, %>% chuyển df vào function colnames)

%>% còn thường được dùng để chuyển các kết quả trung gian sang function tiếp theo, từ đó có thể dùng để nối 1 chuỗi các function để thao tác trên 1 đối tượng

3.5.2 Lấy dữ liệu theo hàng/cột

  • Cú pháp: tên_bảng_dữ_liệu[hàng, cột]. Trong đó cột có thể là số thứ tự của cột hoặc tên cột.

  • Để lấy toàn bộ hàng thì để trống cột và ngược lại

  • Để lấy nhiều cột hay hàng thì cho tên cột hay hàng theo dạng list (chuỗi)

# lấy dữ liệu tại hàng thứ 5, cột thứ 2
df[5 ,2]

# lấy dữ liệu tại hàng thứ 5, cột thứ 2
df[5 , "vgb_truoc_24"]

# lấy hàng thứ 5 trong bảng df
df[5, ]

#lấy các cột id, ngaysinh, vgb_truoc_24
df[, c("id", "ngaysinh", "vgb_truoc_24")] 

#lấy 10 hàng đầu tiên của các cột Country, Year, GDP
#1:10 là cú pháp nhanh để tạo chuỗi từ 1 đến 10
df[1:10, c("id", "ngaysinh", "vgb_truoc_24")] 
Note

Đối với cột, còn có cách trích xuất thông dụng khác bằng cách sử dụng dấu $. Tuy nhiên, cách này sẽ chỉ trích xuất giá trị trong 1 cột.
VD: df$col1 sẽ trả về các giá trị trong cột col1 của bảng df

3.5.3 Chuyển đổi datatype cho cột

Khi đọc dữ liệu vào R, các cột sẽ được tự động chuyển thành datatype hợp lý nhất

Trong df, cột id được tự động chuyển thành dạng numeric

Tuy nhiên, có nhiều cột cần người dùng chuyển một cách thủ công, bao gồm:

  • Các cột ngày tháng (ngaysinh và các cột ngày tiêm): R không thể tự động chuyển sang Date vì format này không khớp với format thông dụng (YYYY-MM-dd , YYYY/MM/dd, …)

  • Cột gioitinh nên được đổi thành factor để hỗ trợ trong quá trình phân tích về sau

  • Cột tinhtrang nên được đổi thành logical để thể hiện đúng ý nghĩa là biến đúng/sai

Để thay đổi datatype của cột, ta có thể áp dụng các function từ phần trước.

VD:

date_cols <- c("ngaysinh", "vgb_truoc_24","vgb_sau_24","vgb_1","vgb_2","vgb_3","vgb_4","hg_1","hg_2","hg_3","hg_4","uv_1","uv_2","uv_3","uv_4")

df <- df %>% 
  mutate(
    gioitinh = as.factor(gioitinh),
    tinhtrang = ifelse(tinhtrang == "theo dõi", TRUE, FALSE)
  ) %>% 
  mutate_at(
    # áp dụng lệnh dmy lên tất cả các cột ngày
    date_cols, 
    dmy
  ) %>% 
  rename(
    theodoi = tinhtrang
  )

str(df)
tibble [1,000 × 21] (S3: tbl_df/tbl/data.frame)
 $ id          : num [1:1000] 1 2 3 4 5 6 7 8 9 10 ...
 $ gioitinh    : Factor w/ 2 levels "nam","nữ": 1 2 2 2 2 2 1 1 1 1 ...
 $ ngaysinh    : Date[1:1000], format: "2024-02-27" "2024-03-06" ...
 $ huyen       : chr [1:1000] "Bình Chánh" "Huyện Thạnh Phú" "Thành phố Vĩnh Long" "Hóc Môn" ...
 $ xa          : chr [1:1000] "Phường Bình Hưng Hòa" "Phường 01" "Phường Phú Thuận" "Xã Đông Thạnh" ...
 $ tinh        : chr [1:1000] "Thành phố Hồ Chí Minh" "Thành phố Hồ Chí Minh" "Thành phố Hồ Chí Minh" "Thành phố Hồ Chí Minh" ...
 $ vgb_truoc_24: Date[1:1000], format: "2024-02-28" "2024-03-07" ...
 $ vgb_sau_24  : Date[1:1000], format: "2024-02-29" "2024-03-08" ...
 $ vgb_1       : Date[1:1000], format: "2024-03-10" "2024-03-23" ...
 $ vgb_2       : Date[1:1000], format: "2024-03-20" "2024-03-27" ...
 $ vgb_3       : Date[1:1000], format: "2024-03-20" "2024-04-11" ...
 $ vgb_4       : Date[1:1000], format: "2024-04-04" "2024-04-26" ...
 $ hg_1        : Date[1:1000], format: "2024-02-28" "2024-03-07" ...
 $ hg_2        : Date[1:1000], format: "2024-03-02" "2024-03-22" ...
 $ hg_3        : Date[1:1000], format: "2024-03-12" "2024-04-06" ...
 $ hg_4        : Date[1:1000], format: "2024-03-12" "2024-04-16" ...
 $ uv_1        : Date[1:1000], format: "2024-02-27" "2024-03-07" ...
 $ uv_2        : Date[1:1000], format: "2024-02-28" "2024-03-22" ...
 $ uv_3        : Date[1:1000], format: "2024-03-09" "2024-04-06" ...
 $ uv_4        : Date[1:1000], format: "2024-03-19" "2024-04-21" ...
 $ theodoi     : logi [1:1000] TRUE FALSE FALSE TRUE TRUE TRUE ...
Lệnh mutatemutate_at

mutate được sử dụng để chỉnh sửa giá trị của cột hoặc tạo cột mới

mutate_at được sử dụng khi cần chỉnh sửa nhiều cột cùng lúc

3.5.4 Lọc dữ liệu

Function filter() được sử dụng cho việc lọc dữ liệu theo điều kiện
Một số ký tự thường được sử dụng để quy định điều kiện bao gồm:

Ký tự Ý nghĩa
x == y x bằng y
x != y x không bằng y
y < x x nhỏ hơn y
y <= x x nhỏ hơn hoặc bằng y
is.na(x) x trống
A & B A và B
A | B A hoặc B
! Không là (NOT)
# Lọc các dữ liệu của quận 2
df %>% filter(huyen == "Quận 2") %>% select(id, huyen)

# Lọc các trẻ có ngày tiêm vgb_1 trước ngày 20/2/2024 
df %>% filter(vgb_1 < as.Date("2024-02-20")) %>% select(id, vgb_1)

# Lọc các trẻ ở quận 2 hoặc trước ngày 20/02/2024
df %>% filter(huyen == "Quận 2" | vgb_1 < as.Date("2024-02-20")) %>% select(id, huyen, vgb_1)

# Lọc các trẻ ở quận 2 và trước ngày 20/02/2024
df %>% filter(huyen == "Quận 2" & vgb_1 < as.Date("2024-02-20")) %>% select(id, huyen, vgb_1)

3.6 Dữ liệu string

Gói stringr (1 trong các package trong tidyverse) thường được sử dụng để xử lý các dữ liệu dạng string.

Ngoài ra, R còn có package stringi dành cho các mục đích cụ thể khác (VD như xử lý dấu, cách mã hoá văn bản, … )

Function Package Công dụng
tolower base biến thành chữ thường
toupper base biến thành chữ hoa
str_to_title stringr in hoa chữ cái đầu tiên của từng từ
stri_trans_general stringi loại bỏ dấu

VD: xoá dấu trong cột tinh huyen xa

df %>% 
  mutate(
    tinh_mod = stri_trans_general(tinh, "latin-ascii"),
    huyen_mod = stri_trans_general(huyen, "latin-ascii"),
    xa_mod = stri_trans_general(xa, "latin-ascii")
  ) %>% 
  select(id, tinh, tinh_mod, huyen, huyen_mod, xa, xa_mod)
# A tibble: 1,000 × 7
      id tinh                  tinh_mod             huyen huyen_mod xa    xa_mod
   <dbl> <chr>                 <chr>                <chr> <chr>     <chr> <chr> 
 1     1 Thành phố Hồ Chí Minh Thanh pho Ho Chi Mi… Bình… Binh Cha… Phườ… Phuon…
 2     2 Thành phố Hồ Chí Minh Thanh pho Ho Chi Mi… Huyệ… Huyen Th… Phườ… Phuon…
 3     3 Thành phố Hồ Chí Minh Thanh pho Ho Chi Mi… Thàn… Thanh ph… Phườ… Phuon…
 4     4 Thành phố Hồ Chí Minh Thanh pho Ho Chi Mi… Hóc … Hoc Mon   Xã Đ… Xa Do…
 5     5 Thành phố Hồ Chí Minh Thanh pho Ho Chi Mi… Bình… Binh Cha… Xã B… Xa Bi…
 6     6 Thành phố Hồ Chí Minh Thanh pho Ho Chi Mi… Quận… Quan 7    Phú … Phu T…
 7     7 Thành phố Hồ Chí Minh Thanh pho Ho Chi Mi… Bình… Binh Tha… 22    22    
 8     8 Thành phố Hồ Chí Minh Thanh pho Ho Chi Mi… Quận… Quan 8    10    10    
 9     9 Thành phố Hồ Chí Minh Thanh pho Ho Chi Mi… Củ C… Cu Chi    Xã A… Xa An…
10    10 Thành phố Hồ Chí Minh Thanh pho Ho Chi Mi… Cần … Can Gio   Lý N… Ly Nh…
# ℹ 990 more rows

3.6.1 Các hàm thông dụng của stringr

Function Công dụng
str_starts lọc hàng bắt đầu bằng chuỗi ký tự khớp với pattern
str_ends lọc hàng kết thúc bằng bằng chuỗi ký tự khớp với pattern
str_detect lọc hàng có chứa chuỗi ký tự khớp với pattern
str_interp chèn giá trị vào string (string interpolation)
str_extract trích 1 chuỗi con (substring) đầu tiên khớp với pattern
str_extract_all trích tất cả các chuỗi con (substring) khớp với pattern
str_remove xoá 1 chuỗi con (substring) đầu tiên khớp với pattern
str_remove_all xoá tất cả các chuỗi con (substring) khớp với pattern
str_replace thay thế 1 chuỗi con (substring) đầu tiên khớp với pattern bằng 1 chuỗi mới
str_replace_all thay thế tất cả chuỗi con (substring) khớp với pattern bằng 1 chuỗi mới

3.6.2 Regex

Trong R, regex là 1 ngôn ngữ ngắn gọn để mô tả các patterns trong string

Có bốn công cụ cơ bản mà người dùng có thể sử dụng để tạo một biểu thức chính quy cơ bản:

  • Nhóm
  • Bộ ký tự
  • Siêu ký tự
  • Bộ định lượng

Nhóm chỉ định các chuỗi cần khớp. Thường được viết trong() và phân cách bởi dấu |

VD: "(Quận|Huyện)" - quy định cần khớp chữ "Quận" hay "Huyện".

Bộ ký tự string được đánh giá khớp tìm thấy bất kỳ ký tự nào trong dấu ngoặc [ ] trong chuỗi. Vd, để tìm string chứa các nguyên âm, người ta có thể sử dụng bộ ký tự này: "[aeiou]".

Các bộ ký tự đặc biệt trong R

Bộ ký tự Ý nghĩa
"[A-Z]" bất kỳ chữ cái viết hoa đơn lẻ nào
"[a-z]" bất kỳ chữ cái viết thường đơn lẻ nào
"[0-9]" bất kỳ số nào
"[:alnum:]" bất kỳ chữ cái và số nào
"[:digit:]" bất kỳ chữ số nào
"[:alpha:]" bất kỳ ký tự nào (viết hoa hoặc viết thường)
"[:upper:]" bất kỳ ký tự viết hoa nào
"[:lower:]" bất kỳ ký tự viết thường nào

Bộ định lượng chỉ định độ dài của pattern Bộ định lượng là các số được viết trong dấu ngoặc nhọn { } sau pattern mà chúng đang định lượng. Ngoài ra có 2 ký tự đặc biệt khác cho bộ định lượng, bao gồm:

  • Dấu * : khớp khi có 0 hoặc nhiều hơn pattern được định lượng.

  • Dấu + : khớp khi có 1 hoặc nhiều hơn pattern được định lượng.

VD:

  • "A{2}" quy định khớp khi có hai chữ cái A viết hoa.

  • "[:digit:]{2}" quy định khớp khi có hai chữ số.

  • "[:alpha:]+" quy định khớp khi có ít nhất 1 chữ cái.

Siêu ký tự là phiên bản rút ngắn của 1 số bộ ký tự

Siêu ký tự Ý nghĩa
"\\s" khoảng trắng đơn
"\\w" tương đương “[:alnum:]”
"\\d" tương đương “[:digit:]”

3.6.3 Một số ví dụ sử dụng hàm của stringr cùng regex

VD1: Tìm các hàng có xã số

  • Chọn câu lệnh: cần tìm theo tên –> cần kiểm tra chữ cuối trong string ->dùng lệnh str_detect
  • Quy định pattern bằng regex: cần string chứa số-> áp dụng Nhóm
df %>% 
  filter(
    str_detect(xa, "[:digit:]")
  ) 
# A tibble: 390 × 21
      id gioitinh ngaysinh   huyen           xa    tinh  vgb_truoc_24 vgb_sau_24
   <dbl> <fct>    <date>     <chr>           <chr> <chr> <date>       <date>    
 1     2 nữ       2024-03-06 Huyện Thạnh Phú Phườ… Thàn… 2024-03-07   2024-03-08
 2     7 nam      2024-02-03 Bình Thạnh      22    Thàn… 2024-02-03   2024-02-04
 3     8 nam      2024-01-24 Quận 8          10    Thàn… 2024-01-24   2024-01-25
 4    15 nam      2024-02-26 Quận 11         Phườ… Thàn… 2024-02-26   2024-02-27
 5    19 nữ       2024-01-30 Phú Nhuận       Phườ… Thàn… 2024-01-30   2024-01-31
 6    23 nam      2024-02-07 Thành phố Long… Phườ… Thàn… 2024-02-08   2024-02-09
 7    26 nam      2024-01-03 Quận 10         Phườ… Thàn… 2024-01-04   2024-01-05
 8    27 nữ       2024-01-03 Gò vấp          Phườ… Thàn… 2024-01-04   2024-01-05
 9    32 nữ       2024-03-19 NA              12    Thàn… 2024-03-19   2024-03-20
10    34 nam      2024-02-20 Thị xã Bình Lo… Phườ… Thàn… 2024-02-21   2024-02-22
# ℹ 380 more rows
# ℹ 13 more variables: vgb_1 <date>, vgb_2 <date>, vgb_3 <date>, vgb_4 <date>,
#   hg_1 <date>, hg_2 <date>, hg_3 <date>, hg_4 <date>, uv_1 <date>,
#   uv_2 <date>, uv_3 <date>, uv_4 <date>, theodoi <lgl>

VD 2: cần tìm các trẻ ở quận Phú Nhuận hoặc Bình Thạnh

Chọn câu lệnh: cần lọc các hàng có huyen chứa dòng chữ “Phú Nhuận” hoặc “Bình Thạnh” –> dùng lệnh str_detect

df %>% 
  filter(
    str_detect(
      huyen, 
      "(Phú Nhuận|Bình Thạnh)"
    )
  )
# A tibble: 75 × 21
      id gioitinh ngaysinh   huyen      xa        tinh   vgb_truoc_24 vgb_sau_24
   <dbl> <fct>    <date>     <chr>      <chr>     <chr>  <date>       <date>    
 1     7 nam      2024-02-03 Bình Thạnh 22        Thành… 2024-02-03   2024-02-04
 2    19 nữ       2024-01-30 Phú Nhuận  Phường 15 Thành… 2024-01-30   2024-01-31
 3    48 nữ       2024-02-28 Phú Nhuận  Phường 15 Thành… 2024-02-28   2024-02-29
 4    73 nữ       2024-01-03 Bình Thạnh 17        Thành… 2024-01-03   2024-01-04
 5    97 nam      2024-02-25 Bình Thạnh 11        Thành… 2024-02-26   2024-02-27
 6    98 nam      2024-02-21 Bình Thạnh Phường 17 Thành… 2024-02-22   2024-02-23
 7   104 nam      2024-01-11 Bình Thạnh Phường 11 Thành… 2024-01-12   2024-01-13
 8   108 nam      2024-01-03 Bình Thạnh Phường 03 Thành… 2024-01-04   2024-01-05
 9   110 nam      2024-01-22 Phú Nhuận  Phường 08 Thành… 2024-01-22   2024-01-23
10   126 nữ       2024-02-16 Phú Nhuận  Phường 15 Thành… 2024-02-16   2024-02-17
# ℹ 65 more rows
# ℹ 13 more variables: vgb_1 <date>, vgb_2 <date>, vgb_3 <date>, vgb_4 <date>,
#   hg_1 <date>, hg_2 <date>, hg_3 <date>, hg_4 <date>, uv_1 <date>,
#   uv_2 <date>, uv_3 <date>, uv_4 <date>, theodoi <lgl>

3.7 Dữ liệu ngày tháng

Đối với dữ liệu dạng ngày tháng, package thông dụng nhất là lubridate (cũng là 1 phần của tidyverse).

3.7.1 Các lệnh liên quan đến ngày tháng

Function Công dụng Package
difftime tính khoảng cách giữa hai mốc thời gian base
month/year lấy tháng/ năm lubridate
quarter chuyển ngày sang quý lubridate
today lấy ngày hiện tại lubridate
dmy, ymd, mdy chuyển string theo nhiều format khác nhau sang ngày lubridate

VD: tìm các trẻ được tiêm vgb_2 trong 1 tháng kể từ ngày sinh

  • Cần tính khoảng thời gian từ lúc cấp chứng chỉ đến hiện tại -> dùng lệnh difftime() với unit là days (difftime không có unit month) sau đó chia cho 365
  • hoặc dùng lệnh month để lấy tháng của 2 mốc thời gian rồi trừ nhau
# --- Cách 1
df %>% 
  filter(
    # gọi difftime để tính khoảng cách thời gian
    difftime(vgb_2, ngaysinh, units = "days")/30 <= 2
  )
# A tibble: 990 × 21
      id gioitinh ngaysinh   huyen           xa    tinh  vgb_truoc_24 vgb_sau_24
   <dbl> <fct>    <date>     <chr>           <chr> <chr> <date>       <date>    
 1     1 nam      2024-02-27 Bình Chánh      Phườ… Thàn… 2024-02-28   2024-02-29
 2     2 nữ       2024-03-06 Huyện Thạnh Phú Phườ… Thàn… 2024-03-07   2024-03-08
 3     3 nữ       2024-01-27 Thành phố Vĩnh… Phườ… Thàn… 2024-01-28   2024-01-29
 4     4 nữ       2024-02-12 Hóc Môn         Xã Đ… Thàn… 2024-02-13   2024-02-14
 5     5 nữ       2024-02-21 Bình Chánh      Xã B… Thàn… 2024-02-21   2024-02-20
 6     6 nữ       2024-03-07 Quận 7          Phú … Thàn… 2024-03-07   2024-03-08
 7     7 nam      2024-02-03 Bình Thạnh      22    Thàn… 2024-02-03   2024-02-04
 8     8 nam      2024-01-24 Quận 8          10    Thàn… 2024-01-24   2024-01-25
 9     9 nam      2024-01-14 Củ Chi          Xã A… Thàn… 2024-01-15   2024-01-16
10    10 nam      2024-01-14 Cần Giờ         Lý N… Thàn… 2024-01-15   2024-01-16
# ℹ 980 more rows
# ℹ 13 more variables: vgb_1 <date>, vgb_2 <date>, vgb_3 <date>, vgb_4 <date>,
#   hg_1 <date>, hg_2 <date>, hg_3 <date>, hg_4 <date>, uv_1 <date>,
#   uv_2 <date>, uv_3 <date>, uv_4 <date>, theodoi <lgl>
# --- Cách 2
df %>% 
  filter(
    # Gọi month để lấy tháng sau đó trừ nhau
    month(vgb_2) - month(ngaysinh)  <= 2
  )
# A tibble: 990 × 21
      id gioitinh ngaysinh   huyen           xa    tinh  vgb_truoc_24 vgb_sau_24
   <dbl> <fct>    <date>     <chr>           <chr> <chr> <date>       <date>    
 1     1 nam      2024-02-27 Bình Chánh      Phườ… Thàn… 2024-02-28   2024-02-29
 2     2 nữ       2024-03-06 Huyện Thạnh Phú Phườ… Thàn… 2024-03-07   2024-03-08
 3     3 nữ       2024-01-27 Thành phố Vĩnh… Phườ… Thàn… 2024-01-28   2024-01-29
 4     4 nữ       2024-02-12 Hóc Môn         Xã Đ… Thàn… 2024-02-13   2024-02-14
 5     5 nữ       2024-02-21 Bình Chánh      Xã B… Thàn… 2024-02-21   2024-02-20
 6     6 nữ       2024-03-07 Quận 7          Phú … Thàn… 2024-03-07   2024-03-08
 7     7 nam      2024-02-03 Bình Thạnh      22    Thàn… 2024-02-03   2024-02-04
 8     8 nam      2024-01-24 Quận 8          10    Thàn… 2024-01-24   2024-01-25
 9     9 nam      2024-01-14 Củ Chi          Xã A… Thàn… 2024-01-15   2024-01-16
10    10 nam      2024-01-14 Cần Giờ         Lý N… Thàn… 2024-01-15   2024-01-16
# ℹ 980 more rows
# ℹ 13 more variables: vgb_1 <date>, vgb_2 <date>, vgb_3 <date>, vgb_4 <date>,
#   hg_1 <date>, hg_2 <date>, hg_3 <date>, hg_4 <date>, uv_1 <date>,
#   uv_2 <date>, uv_3 <date>, uv_4 <date>, theodoi <lgl>

Trong 2 ví dụ trên, cách 1 sẽ chính xác hơn vì ta tính cả ngày khi sử dụng difftime còn cách 2 chỉ lấy 2 tháng trừ nhau

3.8 Lưu bảng dữ liệu sau khi chỉnh sửa

R cho phép lưu lại dữ liệu với nhiều format khác nhau, bao gồm:

  • RData - định dạng dữ liệu của R để lưu trữ nhiều đối tượng

  • RDS - định dạng dữ liệu của R để lưu trữ 1 đối tượng

  • excel

Lưu ý

Tốc độ đọc file RData và RDS của R nhanh hơn rất nhiều so với việc đọc file excel.
Vì vậy, đối với các dữ liệu có số lượng lớn hoặc cần xử lý thêm trong R, nên lưu trữ dưới dạng RDS/Rdata.

VD: lưu dữ liệu

# định nghĩa đường dẫn đến folder chứa data
data_path <- file.path(getwd(), "data")

# lưu dưới dạng Rdata
save(df, file = file.path(data_path, "rda_data.Rda"))

# lưu dưới dạng RDS
saveRDS(df, file = file.path(data_path, "rds_data.Rds"))

# lưu dưới dạng file excel
write_xlsx(df, path = file.path(data_path, "excel_data.xlsx"))

Đọc dữ liệu được lưu

# đọc file Rdata
load(file.path(data_path, "rda_data.Rda"))

# đọc file RDS
readRDS(file = file.path(data_path, "rds_data.Rds"))

# đọc file excel
read_excel(df, path = file.path(data_path, "excel_data.xlsx"))

3.9 Thực hành

3.9.1 Đọc dữ liệu

Đọc 2 bộ dữ liệu sau

  • life-exp.csv

  • practising_cert.csv

Và làm sạch tên cột

Code
life_exp_path <- "data/life-exp.csv"
practising_cert_path <- "data/practising_cert.csv"

# tạo đối tượng để lưu trữ bảng dữ liệu đọc từ file csv
life_exp_df <- read_csv(file = life_exp_path, show_col_types = FALSE)
practising_cert_df <- read_csv(file = practising_cert_path, show_col_types = FALSE)

# làm sạch tên cột 
life_exp_df <- clean_names(life_exp_df)
practising_cert_df <- clean_names(practising_cert_df)

3.9.2 Lọc dữ liệu đơn giản

Từ bảng dữ liệu life-exp

  • Lọc các dữ liệu của Thái Lan

  • Lọc các hàng có GDP năm 2015 hơn 2000

  • Lọc các hàng có country là Thailand hoặc GDP > 2000

  • Lọc các hàng có country là Thailand GDP > 2000

Note: không cần lưu những dữ liệu được lọc

Code
# Lọc các dữ liệu của Thái Lan
life_exp_df %>% filter(country == "Thailand")

# Lọc các hàng có GDP năm 2015 hơn 2000
life_exp_df %>% filter(year == 2015 & gdp > 2000)

# Lọc các hàng có Country là Thailand hoặc GDP > 2000
life_exp_df %>% filter(country == "Thailand" | (gdp > 2000))

# Lọc các hàng có Country là Thailand và GDP > 2000
life_exp_df %>% filter(country == "Thailand" & (gdp > 2000))

3.9.3 Lọc dữ liệu phức tạp

Các bài thực hành sau sẽ được áp dụng trên dữ liệu practising_cert.csv

3.9.3.1 Dữ liệu string

  1. Tìm kiếm thông tin cho những ai có tên là Thịnh/Hằng/Diễm
Gợi ý
Gợi ý
  • Chọn câu lệnh: cần tìm theo tên –> cần kiểm tra chữ cuối trong string ->dùng lệnh str_ends

  • Quy định pattern bằng regex: cần khớp với 1 trong các tên nêu trên -> áp dụng Nhóm

Code
practising_cert_df %>%
  filter( # gọi filter để lọc
    str_ends( # gọi lệnh str_ends để kiểm tra các ký tự cuối của string
      ho_ten, # cần kiểm tra cột HoTen
      "(Thịnh|Hằng|Diễm)" # quy định pattern cần khớp
      )
  )
  1. Tìm thông tin cho những ai được cấp chứng chỉ chữa bệnh chuyên khoa răng hàm mặt hoặc mắt
Gợi ý
Gợi ý

Chọn câu lệnh: cần lọc các hàng có Phạm Vi Hoạt động chứa dòng chữ “răng hàm mặt” hoặc “mắt” –> dùng lệnh str_detect

Code
practising_cert_df %>%
  mutate(
    # chuyển thành in thường để tránh các tình trạng không khớp vì khác case
    ten_pham_vi_hoat_dong = tolower(ten_pham_vi_hoat_dong)
  ) %>%
  filter(
    str_detect(
      ten_pham_vi_hoat_dong,
      "(răng hàm mặt|mắt)"
    )
  )

3.9.3.2 Dữ liệu ngày tháng

Tìm những ai được cấp chứng chỉ hành nghề trong vòng 5 năm gần đây

Gợi ý
Gợi ý
  • Cần lấy ngày hiện tại để tính toán –> dùng lệnh today()

  • Cần tính khoảng thời gian từ lúc cấp chứng chỉ đến hiện tại -> dùng lệnh difftime() với unit là days (difftime không có unit year) sau đó chia cho 365

    hoặc dùng lệnh year để lấy năm của 2 mốc thời gian rồi trừ nhau

Code
# --- Cách 1
practising_cert_df %>%
  filter(
    # gọi difftime để tính khoảng cách thời gian
    difftime(today(), ngay_cap_chung_chi, units = "days")/365 <= 5
  )

# --- Cách 2
practising_cert_df %>%
  filter(
    # Gọi year để lấy năm sau đó trừ nhau
    year(today()) - year(ngay_cap_chung_chi)  <= 5
  )