# Clear Workspace
rm(list = ls())
# Required libraries
library(dplyr)
library(tidyverse)
library(stringi)

Bài 1

Trước hết đọc bộ dữ liệu THPT 2018 Quoc gia.csv vào R và đặt tên cho Data Frame này là raw_data

# Load library
library(readr)

read.csv("D:\\DATA_SCIENCE\\R_COURSE_CASED\\data_day12\\THPT 2018 Quoc gia.csv") -> raw_data

1.a

Convert cột biến SoBD (thông tin số báo danh của thí sinh) về character

# Check current structure of SoBD
str(raw_data$SoBD)
##  int [1:744396] 18010226 18010229 18010232 18010242 18010247 18010252 18010259 18010262 18010265 18010268 ...
# Convert into character
raw_data  %>% 
  mutate(SoBD = as.character(SoBD)) -> raw_data

# Check if converting works
str(raw_data$SoBD)
##  chr [1:744396] "18010226" "18010229" "18010232" "18010242" "18010247" ...

1.b

Sử dụng str_count() để tạo ra soluongkt phản ánh thông tin về số lượng kí tự được sử dụng cho Số Báo Danh.

# Load library 
library(stringr)

raw_data  %>% 
  mutate(soluongkt = str_count(SoBD)) -> raw_data

head(raw_data)
##   ID     SoBD Math Viet English Physics Chemistry Biology History Geography
## 1  1 18010226  3.0 3.75     3.0      NA        NA      NA     3.0      6.50
## 2  2 18010229  8.8 7.50     9.0      NA        NA      NA     6.0      9.00
## 3  3 18010232  6.0 5.50     4.0    5.75       5.5    5.00      NA        NA
## 4  4 18010242  3.4 5.75     2.6      NA        NA      NA     3.5      4.75
## 5  5 18010247  3.8 6.75     3.0      NA        NA      NA     3.5      6.25
## 6  6 18010252  5.0 6.50     2.2    2.00       3.5    4.25      NA        NA
##   GDCD  X KhoiA KhoiB KhoiC KhoiD KhoiA1 soluongkt
## 1 8.25 NA    NA    NA 13.25  9.75     NA         8
## 2 8.25 NA    NA    NA 22.50  25.3     NA         8
## 3   NA NA 17.25 16.50    NA  15.5  15.75         8
## 4 7.25 NA    NA    NA 14.00 11.75     NA         8
## 5 8.00 NA    NA    NA 16.50 13.55     NA         8
## 6   NA NA 10.50 12.75    NA  13.7   9.20         8

1.c

Có bao nhiêu số báo danh có 8 kí tự

raw_data  %>% count(soluongkt)
##   soluongkt      n
## 1         7 152357
## 2         8 592039
# Alternatively, using group_by
raw_data  %>% 
    group_by(soluongkt)  %>% 
    count()
## # A tibble: 2 x 2
## # Groups:   soluongkt [2]
##   soluongkt      n
##       <int>  <int>
## 1         7 152357
## 2         8 592039

1.d

Thêm chữ số 0 vào trước nhóm số báo danh chỉ có 7 kí tự để tạo thành cột biến mới có tên là SoBD_new

raw_data  %>%
  mutate(SoBD_new = case_when (
    soluongkt == 7 ~ paste0("0", SoBD),
    TRUE ~ SoBD
  )) -> raw_data

# Alternatively, use function str_c [soluongkt == 7 ~ str_c("0",SoBD)]
raw_data  %>%
  mutate(SoBD_new = case_when (
    soluongkt == 7 ~ str_c("0",SoBD),
    TRUE ~ SoBD
  )) -> raw_data

# Check if all SoBD is now consisting of 8 characters
raw_data$SoBD_new  %>% str_count()  %>% length()
## [1] 744396

1.e

Lấy thông tin về mã tỉnh của kì thi tốt nghiệp THPT 2018

# Load library
library(rvest)

matinh_link <- "https://thuvienphapluat.vn/cong-van/Giao-duc/Cong-van-417-BGDDT-KTKDCLGD-huong-dan-thuc-hien-Quy-che-thi-trung-hoc-pho-thong-quoc-gia-2017-339311.aspx"

matinh_xpath <- '//*[@id="divContentDoc"]/div/div/div/table[7]'

# Get data table from the websource

matinh_link  %>% 
  read_html()  %>% 
  html_nodes(xpath = matinh_xpath)  %>% 
  html_table  %>% 
  .[[1]]  %>% 
  mutate_all(function(x) {stri_trans_general(x, "Latin-ASCII")}) %>% # Convert font
  slice(-1) -> df_matinh                                             # First row is redundant

# Rename columns and select only the first two because they are resemble to the last two
# Store this data in df_matinh

df_matinh  %>% 
  mutate(code = X1, province = substring(X2,9))  %>% 
  select(code, province) -> df_matinh    
         
head(df_matinh)
##   code        province
## 1   01          Ha Noi
## 2   02 TP. Ho Chi Minh
## 3   03       Hai Phong
## 4   04         Da Nang
## 5   05        Ha Giang
## 6   06        Cao Bang

1.f

Tạo ra một cột biến mới có tên province cho raw_data rồi báo cáo con số về số lượng thí sinh tham dự kì thi theo chiều giảm dần

# Extract province's code from SoBD

raw_data  %>% 
  mutate(code = substr(SoBD_new, 1, 2))  -> raw_data

# Join raw_data with df_matinh by province's code

full_join(raw_data, df_matinh, by = "code") -> raw_data

# Count number of students in each province and sort in descending order
# Store this result in df_soluong_theotinh
raw_data  %>% 
  group_by(province)  %>% 
  count()  %>% 
  arrange(-n) -> df_soluong_theotinh

df_soluong_theotinh
## # A tibble: 64 x 2
## # Groups:   province [64]
##    province            n
##    <chr>           <int>
##  1 TP. Ho Chi Minh 78321
##  2 Ha Noi          38099
##  3 Dong Nai        28651
##  4 Dak Lak         22035
##  5 Thai Binh       21435
##  6 Hai Duong       19973
##  7 Bac Giang       19612
##  8 Binh Dinh       17785
##  9 Quang Nam       17532
## 10 Ha Tinh         16330
## # ... with 54 more rows
# Comment: Some provinces reported only 1. Indeed these are provinces whose data is missing. 

check <- is.na(raw_data$SoBD)
sum(check)
## [1] 5
raw_data$province[check]
## [1] "Son La"                 "Quang Ninh"             "Nghe An"               
## [4] "An Giang"               "truong - Bo Quoc phong"

1.g

Chỉ xét những thí sinh thi khối A của kì thi này. Tính toán tỉ lệ thí sinh có tổng điểm thi ba môn lớn hơn hoặc bằng 25 cho các tỉnh rồi sắp xếp tỉ lệ này theo chiều giảm dần. Ở đây định nghĩa tỉ lệ thí sinh có tổng điểm thi ba môn lớn hơn hoặc bằng 25” bằng số lượng thí sinh có tổng điểm ba môn lớn hơn hoặc bằng 25 chia cho tổng số thi sinh tham dự kì thi khối A của từng tỉnh.

# Filter students taking KhoiA only
raw_data  %>% 
  filter(!is.na(KhoiA))  %>% 
  mutate(over25 = case_when(KhoiA >= 25 ~ 1, TRUE ~ 0)) -> df_khoiA

# Count numbers of over25 and under25 in each province
df_khoiA  %>% 
  group_by(province, over25)  %>% 
  count() -> df_over25

# Split those under25 in separate dataframe
df_over25  %>% 
  filter(over25 == 0)  %>% 
  rename(n_under = n) -> df_under25

# Split those over25 in another dataframe
df_over25  %>% 
  filter(over25 == 1) -> df_over25

# Join these two dataframes back by province

full_join(df_over25, df_under25, by = "province") -> df_rate

# Compute the rate of over25 for each province and sort in descending order

df_rate  %>% 
  mutate(rate = n/(n + n_under)*100)  %>% 
  arrange(-rate) -> df_rate

# Report results
df_rate
## # A tibble: 59 x 6
## # Groups:   province [59]
##    province  over25.x     n over25.y n_under  rate
##    <chr>        <dbl> <int>    <dbl>   <int> <dbl>
##  1 Ha Giang         1    27        0     554 4.65 
##  2 Bac Ninh         1    47        0    5174 0.900
##  3 Thai Binh        1    91        0   10058 0.897
##  4 Ninh Binh        1    31        0    3483 0.882
##  5 Hoa Binh         1    20        0    2336 0.849
##  6 Thanh Hoa        1    44        0    5453 0.800
##  7 Vinh Phuc        1    30        0    4040 0.737
##  8 Hai Phong        1    14        0    1947 0.714
##  9 Nam Dinh         1    18        0    2697 0.663
## 10 Bac Giang        1    33        0    5031 0.652
## # ... with 49 more rows

Bài 3

Viết một hàm có tên xep_loai nhận input, kí hiệu x, là một số thực không âm nằm trong đoạn từ 0 đến 10 và trả về kết quả theo định nghĩa sau:

  • x >= 9 -> “GroupA”.
  • 8 <= x < 9 -> “GroupB”.
  • 7 <= x < 8 -> “GroupC”.
  • 6 <= x < 7 -> “GroupD”.
  • 5 <= x < 6 -> “GroupE”.
  • x < 5 -> “GroupF”.
# Write "xep_loai" function 

xep_loai <- function(x) {
  
  y <- case_when(x >= 9 ~ "Group A",
                 x >= 8 & x < 9 ~ "Group B",
                 x >= 7 & x < 8 ~ "Group C",
                 x >= 6 & x < 7 ~ "Group D",
                 x >= 5 & x < 6 ~ "Group E",
                 TRUE ~ "Group F"
                 )
  return(y)
}

3.a

Sử dụng hàm này để: a. Chỉ xét các thí sinh thi khối A. Tính toán tỉ lệ thí sinh có điểm thi môn Toán thuộc nhóm GroupA cho các tỉnh.

# Count number of groupA/not GroupA students in Math in each province

df_khoiA  %>% 
  mutate(rank_math = xep_loai(Math))  %>% 
  mutate(group_A = case_when(rank_math == "Group A" ~ "Group_A", TRUE ~ "Not_Group_A"))  %>% 
  group_by(province, group_A)  %>% 
  count() -> df_groupA

# Convert df_groupA into wide form 
library(tidyr)

df_groupA  %>%  
  pivot_wider(names_from = group_A, values_from = n) -> df_groupA_wide 

# Calculate the rate and sort in descending order

df_groupA_wide  %>% 
  mutate(Group_A = replace_na(Group_A,0))  %>% 
  mutate(rate_groupA = Group_A/(Group_A + Not_Group_A)*100)  %>% 
  arrange(-rate_groupA) -> df_groupA_wide

# Report results
df_groupA_wide
## # A tibble: 59 x 4
## # Groups:   province [59]
##    province  Group_A Not_Group_A rate_groupA
##    <chr>       <dbl>       <int>       <dbl>
##  1 Ha Giang       26         555       4.48 
##  2 Hoa Binh       20        2336       0.849
##  3 Phu Tho        22        3140       0.696
##  4 Ha Noi         63       15800       0.397
##  5 Hai Phong       6        1955       0.306
##  6 Thanh Hoa      16        5481       0.291
##  7 Hung Yen       15        5257       0.285
##  8 Dien Bien       2         767       0.260
##  9 Ninh Binh       9        3505       0.256
## 10 Lang Son        3        1180       0.254
## # ... with 49 more rows

3.b

  1. Chỉ xét các thí sinh thi khối B. Tính toán tỉ lệ thí sinh có điểm thi môn Sinh thuộc nhóm GroupA cho các tỉnh
# Filter Khoi B students only

raw_data  %>%
  filter(!is.na(KhoiB))  %>% 
  filter(!is.na(Biology)) -> df_khoiB

# Compute number of student in Group A for Biology in each province 
# Store this result in df_bio

df_khoiB  %>% 
  mutate(rank_bio = xep_loai(Biology))  %>% 
  mutate(groupA_bio = case_when(rank_bio == "Group A" ~ "Group_A_Bio", 
                                TRUE ~ "Not_Group_A_Bio"))  %>% 
  group_by(province, groupA_bio)  %>% 
  count() -> df_bio
  
# Transpose into wide form 
df_bio  %>% 
  pivot_wider(names_from = groupA_bio, values_from = n) -> df_groupA_bio

# Compute the rate of groupA students for biology in each province
df_groupA_bio  %>% 
  mutate(Group_A_Bio = replace_na(Group_A_Bio))  %>% 
  mutate(rate_groupA_bio = Group_A_Bio/(Group_A_Bio + Not_Group_A_Bio)*100)  %>% 
  arrange(-rate_groupA_bio) -> df_groupA_bio

# Report results
df_groupA_bio
## # A tibble: 59 x 4
## # Groups:   province [59]
##    province  Group_A_Bio Not_Group_A_Bio rate_groupA_bio
##    <chr>           <int>           <int>           <dbl>
##  1 Ha Giang            3             567           0.526
##  2 Dien Bien           4             781           0.510
##  3 Kon Tum             6            1592           0.375
##  4 Ha Tinh            15            5052           0.296
##  5 Bac Kan             1             346           0.288
##  6 Hai Duong          14            7367           0.190
##  7 Phu Tho             5            3150           0.158
##  8 Lai Chau            1             653           0.153
##  9 Ha Noi             23           15692           0.146
## 10 Tra Vinh            4            2805           0.142
## # ... with 49 more rows
# Clear workspace
rm(list = ls())

Bài 2

Xem Bar Plot trình bày ở trang 26 của báo cáo PCI2018 tại http://pci2018.pcivietnam.vn/uploads/2019/ho-so-63-tinh-vie.pdf rồi trả lời các câu hỏi sau:

2.a

Bar Plot còn thiếu sót gì?

  • Trục x không nhất thiết phải thể hiện số vì đã gán số liệu cụ thể cho từng tỉnh.
  • Các tỉnh/thành phố quan trọng VD Hà Nội, HCM nên được làm nổi bật hơn.

2.b

Sử dụng dữ liệu du-lieu-pci-2018.xlsx cung cấp bởi http://pci2018.pcivietnam.vn/ để tạo Bar Plot theo bố cục và cách trình bày tương tự như Bar Plot thuộc trang 26 của báo cáo này

library(readxl)

# Load data 

read_excel("D:\\DATA_SCIENCE\\R_COURSE_CASED\\Lecture_2\\du_lieu_pci_2018.xlsx", 
           range = "A2:C64", 
           col_names = FALSE)-> df_pci

# Rename columns 

df_pci  %>% 
  rename(province = ...1, 
         rank = ...2,
         pci_score = ...3) -> df_pci

# Processing data 
# Categorize pci_score into groups using standard deviation from the mean

df_pci  %>% 
  mutate(mean = mean(pci_score))  %>% 
  mutate(sd = sd(pci_score))  %>% 
  mutate(category = case_when(pci_score >= mean + 2*sd ~ "Rất tốt",
                              pci_score < mean + 2*sd & pci_score >= mean + sd ~ "Tốt",
                              pci_score < mean + sd & pci_score >= mean ~ "Khá",
                              pci_score < mean & pci_score >= mean - sd ~ "Trung bình",
                              pci_score < mean - sd & pci_score >= mean - 2*sd ~ "Tương đối thấp",
                              TRUE ~ "Thấp"))   %>% 
  mutate(pci_score = round(pci_score, digits = 2)) -> df_pci 

# Arrange pci_score in descending order and factorize province

df_pci  %>% 
  arrange(pci_score)  %>% 
  mutate(province_ft = factor(province, levels = province))  %>% 
  mutate(color = case_when(category == "Rất tốt" ~ "#7D3C98",
                           category == "Tốt" ~ "#034EA2",
                           category == "Khá" ~ "#4792CF",
                           category == "Trung bình" ~ "#8ED8F8",
                           category == "Tương đối thấp" ~ "#ABEBC6",
                           category == "Thấp" ~ "#FADBD8"
                           )) -> df_pci

# Draw bar chart

library(dplyr)
library(ggplot2)

# Color code 
# https://html-color-codes.info/colors-from-image/

df_pci  %>% 
  ggplot(aes(x = pci_score, y = province_ft)) +
  geom_col(fill = df_pci$color, color = "white", width = 0.6) +
  labs(title = "Figure 1: Vietnam Provincial Competitiveness Index (CPI) 2018", 
       x = NULL, y = NULL,
       caption = "Source: http://pci2018.pcivietnam.vn/") +
  theme(axis.text.y = element_text(size = 8)) + 
  geom_text(aes(label = pci_score), size = 2, hjust = -0.3) 

Comments on improvements of this chart:

  • Adjust size
  • Add rank next to province’s name
  • Remove - next to province’s name
  • Add group explaination/table of legend