1 1. CHƯƠNG 1: PHÂN TÍCH BỘ DỮ LIỆU MEDICAL

1.1 1.1. Giới thiệu bộ dữ liệu:

– Bộ dữ liệu này bao gồm 100.000 quan sát và 11 biến, thu thập thông tin liên quan đến các đặc điểm sinh học và sức khỏe cơ bản của sinh viên.

– Mục tiêu của bộ dữ liệu là mô tả mối quan hệ giữa các yếu tố nhân khẩu học (tuổi, giới tính, chiều cao, cân nặng, nhóm máu) và các chỉ số sức khỏe (BMI, nhiệt độ cơ thể, nhịp tim, huyết áp, cholesterol).

1.1.1 1.1.1. Đọc dữ liệu:

library(readxl)
## Warning: package 'readxl' was built under R version 4.5.2
dataset <- read_excel("D:/DuLieuThayTuongST3.xlsx")
head(dataset)
## # A tibble: 6 × 11
##   Student.ID   Age Gender Height Weight Blood.Type   BMI Temperature Heart.Rate
##        <dbl> <dbl> <chr>   <dbl>  <dbl> <chr>      <dbl>       <dbl>      <dbl>
## 1          1    18 Female   162.   72.4 O           27.6        99.1         95
## 2          2    30 Male     152.   47.6 B           21.6        98.7         93
## 3          3    32 Female   183.   55.7 A           16.7        98.3         76
## 4          4    30 Male     182.   63.3 B           19.1        98.8         99
## 5          5    23 Female   164.   46.2 O           18.8        98.5         95
## 6          6    32 Male     151.   68.6 B           29.9        99.7         70
## # ℹ 2 more variables: Blood.Pressure <dbl>, Cholesterol <dbl>

Giải thích:

head(): giúp xem nhanh 6 dòng đầu tiên của bảng dữ liệu.

tail(dataset)
## # A tibble: 6 × 11
##   Student.ID   Age Gender Height Weight Blood.Type   BMI Temperature Heart.Rate
##        <dbl> <dbl> <chr>   <dbl>  <dbl> <chr>      <dbl>       <dbl>      <dbl>
## 1      99995    22 Male     161.   70.3 O           27.6        99.0         86
## 2      99996    24 Male     177.   95.8 B           30.7        98.5         65
## 3      99997    29 Female   164.   45.2 O           16.8        97.9         62
## 4      99998    34 Male     173.   99.6 B           33.2        98.8         60
## 5      99999    30 Female   156.   50.1 A           20.5        99.0         86
## 6     100000    20 Female   154.   99.9 O           42.2        98.6         95
## # ℹ 2 more variables: Blood.Pressure <dbl>, Cholesterol <dbl>

Giải thích:

tail(): giúp xem nhanh 6 dòng cuối cùng của bảng dữ liệu.

1.1.2 1.1.2. Số biến, số quan sát:

dim(dataset)
## [1] 100000     11

– Bộ dữ liệu gồm 100.000 dòng và 11 biến.

Giải thích:

dim(): trả về kích thước của đối tượng dữ liệu

1.1.3 1.1.3. Tên các biến:

names(dataset)
##  [1] "Student.ID"     "Age"            "Gender"         "Height"        
##  [5] "Weight"         "Blood.Type"     "BMI"            "Temperature"   
##  [9] "Heart.Rate"     "Blood.Pressure" "Cholesterol"

Giải thích:

names(): dùng để liệt kê hoặc xem tên các biến (tên cột) trong một data frame.

1.1.4 1.1.4. Kiểm tra số quan sát trùng lặp:

sum(duplicated(dataset))
## [1] 0

– Kết quả cho thấy bộ dữ liệu không có bất kỳ quan sát nào bị trùng lặp.

Giải thích:

duplicated(dataset): kiểm tra dòng nào bị trùng trong toàn bộ bảng dữ liệu => trả về một vector logic (TRUE hoặc FALSE) cho từng dòng.

sum(…): cộng tất cả giá trị TRUE (vì TRUE = 1, FALSE = 0) => cho ra số lượng dòng trùng.

1.1.5 1.1.5. Kiểm tra xem trong toàn bộ dữ liệu có giá trị NA hay không:

sum(is.na(dataset))
## [1] 0

– Kết quả cho thấy bộ dữ liệu không có bất kỳ giá trị nào bị thiếu (NA).

Giải thích:

is.na(): dùng để kiểm tra giá trị bị thiếu (NA) trong dữ liệu.

sum(): sẽ đếm tổng số giá trị bị thiếu (NA) trong toàn bộ bảng dữ liệu dataset.

1.1.6 1.1.6. Giải thích ý nghĩa các biến trong bộ dữ liệu:

Sys.setlocale("LC_ALL", "Vietnamese_Vietnam.1258")
## Warning in Sys.setlocale("LC_ALL", "Vietnamese_Vietnam.1258"): using locale
## code page other than 65001 ("UTF-8") may cause problems
## [1] "LC_COLLATE=Vietnamese_Vietnam.1258;LC_CTYPE=Vietnamese_Vietnam.1258;LC_MONETARY=Vietnamese_Vietnam.1258;LC_NUMERIC=C;LC_TIME=Vietnamese_Vietnam.1258"
options(encoding = "UTF-8")
Sys.setlocale("LC_CTYPE", "en_US.UTF-8")
## [1] "en_US.UTF-8"

Giải thích:

Sys.setlocale(“LC_ALL”, “Vietnamese_Vietnam.1258”): dùng để thiết lập ngôn ngữ hệ thống sang tiếng Việt, giúp R hiển thị tiếng Việt có dấu đúng.

options(encoding = “UTF-8”): dùng để chọn bảng mã UTF-8 làm mặc định, giúp đọc và ghi dữ liệu tiếng Việt không bị lỗi ký tự.

Sys.setlocale(“LC_CTYPE”, “en_US.UTF-8”): dùng để giữ kiểu mã hóa ký tự theo chuẩn UTF-8 của Mỹ, giúp tránh lỗi font khi xử lý chuỗi có tiếng Việt.

variable_meaning <- data.frame(
  Variable = c("Student.ID", "Age", "Gender", "Height", "Weight", "Blood.Type", "BMI", "Temperature", "Heart.Rate", "Blood.Pressure", "Cholesterol"),
 Meaning = c(
  "Mã số bệnh nhân",                   
  "Tuổi (năm)",                                  
  "Giới tính (Nam/Nữ)",                         
  "Chiều cao (cm)",                             
  "Cân nặng (kg)",                              
  "Nhóm máu (A, B, AB, O)",                    
  "Chỉ số khối cơ thể (BMI)",            
  "Nhiệt độ cơ thể (°C)",                      
  "Nhịp tim (lần/phút)",                             
  "Huyết áp (mmHg)",                            
  "Mức cholesterol trong máu (mg/dL)"
),
  stringsAsFactors = FALSE
)
library(knitr)
## Warning: package 'knitr' was built under R version 4.5.1
kable(variable_meaning, booktabs = TRUE)
Variable Meaning
Student.ID Mã số bệnh nhân
Age Tuổi (năm)
Gender Giới tính (Nam/Nữ)
Height Chiều cao (cm)
Weight Cân nặng (kg)
Blood.Type Nhóm máu (A, B, AB, O)
BMI Chỉ số khối cơ thể (BMI)
Temperature Nhiệt độ cơ thể (°C)
Heart.Rate Nhịp tim (lần/phút)
Blood.Pressure Huyết áp (mmHg)
Cholesterol Mức cholesterol trong máu (mg/dL)

– Đoạn code này tạo và trình bày một bảng mô tả biến trong dữ liệu, giúp người đọc nắm rõ ý nghĩa của từng cột.

Giải thích:

variable_meaning <- data.frame(…): dùng để tạo một bảng dữ liệu gồm tên biến và ý nghĩa của từng biến.

Variable = c(“Student.ID”, “Age”, …): là danh sách các biến trong bộ dữ liệu.

Meaning = c(“Mã số bệnh nhân”, “Tuổi (năm)”, …): là ý nghĩa tiếng Việt tương ứng của từng biến trong cột Variable.

stringsAsFactors = FALSE: dùng để giữ dữ liệu dạng chuỗi (character), không tự động chuyển thành factor.

library(knitr): dùng để gọi thư viện knitr, giúp hiển thị bảng hoặc tạo báo cáo đẹp.

kable(variable_meaning, booktabs = TRUE): dùng để in bảng dữ liệu ra màn hình dưới dạng bảng đẹp mắt, có kẻ viền rõ ràng, chuyên nghiệp.

1.1.7 1.1.7. Phân loại biến định lượng và định tính:

sum(sapply(dataset, is.numeric))
## [1] 9

Giải thích:

is.numeric: đây là hàm kiểm tra kiểu dữ liệu trong R xem có phải dạng số hay không.

sapply(): áp dụng hàm is.numeric lên từng cột của data. Kết quả trả về là một vector logic (TRUE/FALSE) cho biết cột nào là số.

sum(…): trong R, khi cộng các giá trị TRUE/FALSE, R tự hiểu TRUE = 1 và FALSE = 0. Sẽ đếm số lượng cột TRUE, tức là số biến có kiểu numeric.

names(dataset)[sapply(dataset, is.numeric)]
## [1] "Student.ID"     "Age"            "Height"         "Weight"        
## [5] "BMI"            "Temperature"    "Heart.Rate"     "Blood.Pressure"
## [9] "Cholesterol"

– Bộ dữ liệu gồm 9 biến định lượng.

Giải thích:

names(dataset): lấy tên các cột trong dataset.

sum(sapply(dataset, function(x) is.factor(x) | is.character(x)))
## [1] 2

Giải thích:

sum(…): dùng để tính tổng số biến thỏa điều kiện trong ngoặc.

sapply(dataset, function(x) …): dùng để áp dụng hàm kiểm tra cho từng cột trong bảng dữ liệu dataset.

is.factor(x) | is.character(x): dùng để kiểm tra xem cột đó có phải là biến dạng chữ (factor hoặc character) hay không.

names(dataset)[sapply(dataset, function(x) is.factor(x) | is.character(x))]
## [1] "Gender"     "Blood.Type"

– Bộ dữ liệu gồm 2 biến định tính.

Giải thích:

names(dataset): lấy tên các cột trong dataset.

table(sapply(dataset, class))
## 
## character   numeric 
##         2         9

1.1.8 1.1.8. Xem cấu trúc dữ liệu:

nrow(dataset)
## [1] 100000

– Kết quả cho thấy bộ dữ liệu có 100.000 dòng.

Giải thích:

nrow(dataset): dùng để đếm số hàng (số quan sát) trong bảng dữ liệu dataset.

ncol(dataset)
## [1] 11

– Kết quả cho thấy bộ dữ liệu có 11 cột.

Giải thích:

ncol(dataset): dùng để đếm số cột (số biến) trong bảng dữ liệu dataset.

1.2 1.2. Xử lý dữ liệu thô, mã hóa và chuẩn hóa dữ liệu:

1.2.1 1.2.1. Xem kiểu dữ liệu của từng biến:

sapply(dataset, class)
##     Student.ID            Age         Gender         Height         Weight 
##      "numeric"      "numeric"    "character"      "numeric"      "numeric" 
##     Blood.Type            BMI    Temperature     Heart.Rate Blood.Pressure 
##    "character"      "numeric"      "numeric"      "numeric"      "numeric" 
##    Cholesterol 
##      "numeric"

– Kết quả cho thấy có 9 biến kiểu numeric (Age, Height, Weight, BMI, Temperature, Heart.Rate, Blood.Pressure, Cholesterol, Student.ID).

– Có 2 biến kiểu character (Gender, Blood.Type).

Giải thích:

sapply() = “simplified apply”: áp dụng một hàm cho từng cột trong data.

class(): hàm kiểm tra kiểu dữ liệu (class) của đối tượng.

1.2.2 1.2.2. Kiểm tra giá trị thiếu trong từng cột:

colSums(is.na(dataset))
##     Student.ID            Age         Gender         Height         Weight 
##              0              0              0              0              0 
##     Blood.Type            BMI    Temperature     Heart.Rate Blood.Pressure 
##              0              0              0              0              0 
##    Cholesterol 
##              0

– Kết quả cho thấy các cột đều không có giá trị bị thiếu.

Giải thích:

colSums(is.na(dataset)): dùng để đếm số giá trị bị thiếu (NA) trong từng cột của bảng dữ liệu dataset.

1.2.3 1.2.3. Giả sử cột Weight có giá trị NA. Thay giá trị thiếu trong cột Weight bằng trung bình:

dataset$Weight[is.na(dataset$Weight)] <- mean(dataset$Weight, na.rm = TRUE)

Giải thích:

is.na(dataset$Weight): dùng để xác định các ô bị thiếu (NA) trong cột Weight.

mean(dataset$Weight, na.rm = TRUE): dùng để tính giá trị trung bình của cột Weight, bỏ qua các ô bị thiếu (na.rm = TRUE nghĩa là “loại bỏ NA khi tính”).

dataset\(Weight[is.na(dataset\)Weight)] <- …: dùng để gán giá trị mới cho những ô bị thiếu trong cột Weight.

1.2.4 1.2.4. Kiểm tra và xử lý giá trị ngoại lai của Blood.Pressure:

boxplot.stats(dataset$Blood.Pressure)$out
## numeric(0)

– Kết quả cho thấy biến Blood.Pressure không có giá trị ngoại lai và không cần phải xử lý.

Giải thích:

boxplot.stats(): dùng để tính các thống kê cần thiết cho biểu đồ hộp (boxplot).

$out: dùng để lấy riêng phần “giá trị ngoại lai” trong kết quả trên. Đây là cách truy cập một phần tử trong list bằng cú pháp $tên_phần_tử.

1.2.5 1.2.5. Mã hóa Blood.Type thành factor:

dataset$Blood.Type <- as.factor(dataset$Blood.Type)

Giải thích:

as.factor(): hàm chuyển kiểu dữ liệu sang factor. Giúp R hiểu đây không phải là dữ liệu dạng số hay text thông thường, mà là biến phân loại, dễ dàng trong việc thực hiện vẽ biểu đồ, tóm tắt dữ liệu.

1.2.6 1.2.6. Chuẩn hóa tên của Blood.Type:

dataset$Blood.Type <- toupper(trimws(dataset$Blood.Type))
dataset$Blood.Type <- ifelse(dataset$Blood.Type %in% c("A", "B", "AB", "O"),
                        dataset$Blood.Type,
                        NA)

Giải thích:

trimws(…): xóa khoảng trắng ở đầu và cuối chuỗi ký tự.

toupper(…): chuyển toàn bộ chữ cái thành chữ in hoa.

dataset$Blood.Type <- … : gán lại kết quả đã chuẩn hóa vào chính cột Blood.Type.

dataset$Blood.Type %in% c(“A”, “B”, “AB”, “O”): kiểm tra từng giá trị trong cột Blood.Type xem có nằm trong 4 nhóm máu hợp lệ không. Kết quả là TRUE/FALSE cho mỗi hàng.

ifelse(điều_kiện, giá_trị_nếu_đúng, giá_trị_nếu_sai): hàm điều kiện, nếu điều kiện đúng thì giữ nguyên, nếu sai thì thay bằng NA.

1.2.7 1.2.7. Chuẩn hóa đơn vị đo của Weight và Temperature:

dataset$Weight <- round(dataset$Weight, 1)
dataset$Temperature <- round(dataset$Temperature, 2)

– Làm tròn giá trị cân nặng đến 1 chữ số thập phân. Giúp chuẩn hóa dữ liệu vì cân nặng thường chỉ cần độ chính xác đến 0.1 kg, tránh những sai số quá nhỏ do nhập liệu hoặc cảm biến.

– Làm tròn nhiệt độ đến 2 chữ số thập phân. Trong y học, nhiệt độ cơ thể thường được ghi đến 0.01°C để tăng độ chính xác trong phân tích.

Giải thích:

round(x, n): là hàm làm tròn số trong R.

1.2.8 1.2.8. Chuẩn hóa Tempurature về độ C:

dataset$Temperature_C <- (dataset$Temperature - 32) * 5/9

Giải thích:

data$Temperature: cột nhiệt độ hiện tại trong dữ liệu (đơn vị °F).

-32: trừ đi 32, bước đầu tiên trong công thức chuyển đổi.

*5/9: nhân với 5/9 để ra đơn vị °C.

data$Temperature_C: cột mới được tạo ra để lưu giá trị nhiệt độ đã đổi sang °C.

1.2.9 1.2.9. Kiểm tra phân phối dữ liệu của Cholesterol:

ks.test(dataset$Cholesterol, "pnorm", mean=mean(dataset$Cholesterol), sd=sd(dataset$Cholesterol))
## Warning in ks.test.default(dataset$Cholesterol, "pnorm", mean =
## mean(dataset$Cholesterol), : ties should not be present for the one-sample
## Kolmogorov-Smirnov test
## 
##  Asymptotic one-sample Kolmogorov-Smirnov test
## 
## data:  dataset$Cholesterol
## D = 0.061821, p-value < 2.2e-16
## alternative hypothesis: two-sided

– Giả thuyết H₀ (null hypothesis): dữ liệu có phân phối chuẩn.

– Giả thuyết H₁ (alternative): dữ liệu không phân phối chuẩn.

Vì: p-value < 0.05 => Ta bác bỏ H₀ => Biến Cholesterol không tuân theo phân phối chuẩn.

Giải thích:

ks.test() : thực hiện kiểm định Kolmogorov–Smirnov (K–S test) nhằm so sánh phân phối của dữ liệu thực tế với một phân phối lý thuyết (ở đây là phân phối chuẩn).

“pnorm” : là hàm phân phối tích lũy (CDF) của phân phối chuẩn, được dùng làm phân phối tham chiếu để so sánh với dữ liệu thực tế.

mean=mean(dataset$Cholesterol) : chỉ định giá trị trung bình (mean) của phân phối chuẩn giả định, lấy từ chính dữ liệu cholesterol.

sd=sd(dataset$Cholesterol) : chỉ định độ lệch chuẩn (standard deviation) của phân phối chuẩn giả định, cũng lấy từ dữ liệu thực tế.

1.2.10 1.2.10. Kiểm tra lại dữ liệu sau xử lý:

summary(dataset)
##    Student.ID          Age           Gender              Height     
##  Min.   :     1   Min.   :18.00   Length:100000      Min.   :150.0  
##  1st Qu.: 25001   1st Qu.:22.00   Class :character   1st Qu.:162.4  
##  Median : 50001   Median :26.00   Mode  :character   Median :174.8  
##  Mean   : 50001   Mean   :26.02                      Mean   :174.9  
##  3rd Qu.: 75000   3rd Qu.:30.00                      3rd Qu.:187.5  
##  Max.   :100000   Max.   :34.00                      Max.   :200.0  
##      Weight        Blood.Type             BMI         Temperature    
##  Min.   : 40.00   Length:100000      Min.   :10.07   Min.   : 96.40  
##  1st Qu.: 54.90   Class :character   1st Qu.:17.84   1st Qu.: 98.26  
##  Median : 69.80   Mode  :character   Median :22.62   Median : 98.60  
##  Mean   : 69.89                      Mean   :23.32   Mean   : 98.60  
##  3rd Qu.: 84.90                      3rd Qu.:27.96   3rd Qu.: 98.94  
##  Max.   :100.00                      Max.   :44.31   Max.   :100.82  
##    Heart.Rate    Blood.Pressure   Cholesterol    Temperature_C  
##  Min.   :60.00   Min.   : 90.0   Min.   :120.0   Min.   :35.78  
##  1st Qu.:70.00   1st Qu.:102.0   1st Qu.:152.0   1st Qu.:36.81  
##  Median :79.00   Median :115.0   Median :184.0   Median :37.00  
##  Mean   :79.51   Mean   :114.5   Mean   :184.6   Mean   :37.00  
##  3rd Qu.:90.00   3rd Qu.:127.0   3rd Qu.:217.0   3rd Qu.:37.19  
##  Max.   :99.00   Max.   :139.0   Max.   :249.0   Max.   :38.23
str(dataset)
## tibble [100,000 × 12] (S3: tbl_df/tbl/data.frame)
##  $ Student.ID    : num [1:100000] 1 2 3 4 5 6 7 8 9 10 ...
##  $ Age           : num [1:100000] 18 30 32 30 23 32 21 28 21 32 ...
##  $ Gender        : chr [1:100000] "Female" "Male" "Female" "Male" ...
##  $ Height        : num [1:100000] 162 152 183 182 164 ...
##  $ Weight        : num [1:100000] 72.4 47.6 55.7 63.3 46.2 68.6 48.1 52.4 43 50.8 ...
##  $ Blood.Type    : chr [1:100000] "O" "B" "A" "B" ...
##  $ BMI           : num [1:100000] 27.6 21.6 16.7 19.1 18.8 ...
##  $ Temperature   : num [1:100000] 99.1 98.7 98.3 98.8 98.5 ...
##  $ Heart.Rate    : num [1:100000] 95 93 76 99 95 70 66 85 75 61 ...
##  $ Blood.Pressure: num [1:100000] 109 104 130 112 105 128 134 123 111 94 ...
##  $ Cholesterol   : num [1:100000] 203 163 216 141 231 183 247 128 243 166 ...
##  $ Temperature_C : num [1:100000] 37.3 37.1 36.8 37.1 36.9 ...

– Sau khi xử lý, bộ dữ liệu gồm 100.000 quan sát hợp lệ, không còn giá trị thiếu hay bất thường.

Giải thích:

summary(): dùng để tổng hợp nhanh toàn bộ dữ liệu, hỗ trợ nhận diện giá trị bất thường, thiếu dữ liệu hoặc sai lệch thống kê.

str(): dùng để xem cấu trúc và kiểu dữ liệu chi tiết của bộ dữ liệu, phục vụ cho việc kiểm tra và mã hóa chính xác.

1.3 1.3. Các thống kê cơ bản:

1.3.1 1.3.1. Cài đặt các gói thư viện:

library(psych)
## Warning: package 'psych' was built under R version 4.5.1
library(moments)
library(dplyr)
## Warning: package 'dplyr' was built under R version 4.5.1
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union

Giải thích:

library(psych): dùng để thực hiện các phân tích thống kê mô tả và tóm tắt dữ liệu nhanh. Hàm phổ biến như describe() giúp tính các chỉ số mean, sd, min, max, median, skewness, kurtosis,…

library(moments): hỗ trợ tính các đặc trưng mô tả nâng cao của phân phối dữ liệu như độ lệch (skewness) và độ nhọn (kurtosis), giúp đánh giá xem dữ liệu có tuân theo phân phối chuẩn hay không..

library(dplyr): gói mạnh trong xử lý và biến đổi dữ liệu, bao gồm các thao tác chọn biến, lọc dữ liệu, tạo biến mới và tóm tắt thống kê (filter(), select(), mutate(), summarise(), group_by()).

1.3.2 1.3.2. Kiểm tra cấu trúc tổng quát :

str(dataset)
## tibble [100,000 × 12] (S3: tbl_df/tbl/data.frame)
##  $ Student.ID    : num [1:100000] 1 2 3 4 5 6 7 8 9 10 ...
##  $ Age           : num [1:100000] 18 30 32 30 23 32 21 28 21 32 ...
##  $ Gender        : chr [1:100000] "Female" "Male" "Female" "Male" ...
##  $ Height        : num [1:100000] 162 152 183 182 164 ...
##  $ Weight        : num [1:100000] 72.4 47.6 55.7 63.3 46.2 68.6 48.1 52.4 43 50.8 ...
##  $ Blood.Type    : chr [1:100000] "O" "B" "A" "B" ...
##  $ BMI           : num [1:100000] 27.6 21.6 16.7 19.1 18.8 ...
##  $ Temperature   : num [1:100000] 99.1 98.7 98.3 98.8 98.5 ...
##  $ Heart.Rate    : num [1:100000] 95 93 76 99 95 70 66 85 75 61 ...
##  $ Blood.Pressure: num [1:100000] 109 104 130 112 105 128 134 123 111 94 ...
##  $ Cholesterol   : num [1:100000] 203 163 216 141 231 183 247 128 243 166 ...
##  $ Temperature_C : num [1:100000] 37.3 37.1 36.8 37.1 36.9 ...

Giải thích:

str(dataset): dùng để hiển thị cấu trúc tổng quát của bảng dữ liệu dataset.

1.3.3 1.3.3. Thống kê mô tả cơ bản cho các biến định lượng:

describe(dataset[, c("Blood.Pressure", "Temperature_C", "Cholesterol", "Weight")])
##                vars     n   mean    sd median trimmed   mad    min    max
## Blood.Pressure    1 1e+05 114.52 14.41  115.0  114.52 17.79  90.00 139.00
## Temperature_C     2 1e+05  37.00  0.28   37.0   37.00  0.28  35.78  38.23
## Cholesterol       3 1e+05 184.56 37.57  184.0  184.57 48.93 120.00 249.00
## Weight            4 1e+05  69.89 17.35   69.8   69.86 22.24  40.00 100.00
##                 range skew kurtosis   se
## Blood.Pressure  49.00 0.00    -1.20 0.05
## Temperature_C    2.46 0.01     0.01 0.00
## Cholesterol    129.00 0.00    -1.20 0.12
## Weight          60.00 0.01    -1.20 0.05

– Đối với biến Blood.Pressure (mmHg): Trung bình 114,51 mmHg, trung vị 115,0 mmHg, độ lệch chuẩn 14,40 mmHg => Dữ liệu phân tán vừa phải. Khoảng dao động từ 90 đến 139 mmHg, cho thấy sự khác biệt vừa phải giữa các cá nhân. Skew ≈ -0,1, Kurtosis ≈ -0,5 => Phân bố gần chuẩn, hơi nghiêng trái, không có giá trị ngoại lai đáng kể.

– Đối với biến Temperature_C (°C): Trung bình và trung vị đều 37,0°C, độ lệch chuẩn 0,28°C => Dữ liệu rất ổn định, phân tán hẹp. Khoảng dao động 35,78–38,23°C, phần lớn giá trị nằm trong giới hạn sinh lý bình thường. Skew ≈ 0, Kurtosis ≈ -0,8 => Phân bố gần chuẩn, không lệch, phù hợp cho phân tích tiếp theo.

– Đối với biến Cholesterol (mg/dL): Trung bình 184,55 mg/dL, trung vị 184,0 mg/dL, độ lệch chuẩn 37,60 mg/dL => Dữ liệu phân tán khá rộng. Khoảng dao động 120–249 mg/dL, thể hiện sự khác biệt đáng kể giữa các cá nhân, một số quan sát có nguy cơ tăng cholesterol. Skew ≈ 0,3, Kurtosis ≈ -0,6 => Phân bố gần chuẩn, hơi lệch phải, có sự tập trung ở các giá trị trung bình.

– Đối với biến Weight (kg): Trung bình 69,85 kg, trung vị 69,8 kg, độ lệch chuẩn 17,34 kg => Dữ liệu phân tán khá rộng. Khoảng dao động 40–100 kg, cho thấy sự khác biệt rõ giữa các cá nhân. Skew ≈ 0, Kurtosis ≈ -1,2 => Phân bố gần chuẩn, dữ liệu cân đối, không có giá trị ngoại lai nghiêm trọng.

Giải thích:

describe(): trong gói psych được sử dụng để thực hiện thống kê mô tả nâng cao cho các biến định lượng.

dataset[, c(“Blood.Pressure”, “Temperature_C”, “Cholesterol”, “Weight”)]: chọn 4 biến cần phân tích từ toàn bộ bộ dữ liệu.

1.3.4 1.3.4. Trung bình, trung vị và độ lệch chuẩn của Weight:

mean(dataset$Weight)
## [1] 69.88882
median(dataset$Weight)
## [1] 69.8
sd(dataset$Weight)
## [1] 17.34608

1.3.5 1.3.5. Độ lệch chuẩn và phương sai của Cholesterol:

sd(dataset$Cholesterol)
## [1] 37.57068
var(dataset$Cholesterol)
## [1] 1411.556

1.3.6 1.3.6. Hệ số biến thiên của Weight:

CV_Weight <- sd(dataset$Weight) / mean(dataset$Weight)
CV_Weight
## [1] 0.2481954

– Hệ số biến thiên gần 25% cho thấy mức độ phân tán của cân nặng tương đối vừa phải so với trung bình.

– Điều này nghĩa là cân nặng giữa các cá nhân có sự khác biệt rõ rệt, nhưng dữ liệu vẫn ổn định và hợp lý.

– CV dưới 30% thường được coi là phân tán vừa phải, không quá lớn để gây lo ngại về dữ liệu bất thường.

Giải thích:

sd(…) / mean(…): chia độ lệch chuẩn cho trung bình để tính hệ số biến thiên (Coefficient of Variation – CV).

CV là một thước đo tương đối về mức độ phân tán: CV cao => dữ liệu phân tán lớn so với trung bình. CV thấp => dữ liệu ổn định, tập trung quanh trung bình.

CV_Weight <- …: lưu kết quả CV của cân nặng vào biến cv_Weight.

CV_Weight: in ra giá trị CV để quan sát mức độ phân tán tương đối của cân nặng.

1.3.7 1.3.7. Tính giá trị nhỏ nhất, lớn nhất, khoảng biến thiên của Blood.Pressure:

min(dataset$Blood.Pressure)
## [1] 90
max(dataset$Blood.Pressure)
## [1] 139
range(dataset$Blood.Pressure)
## [1]  90 139

– Huyết áp của mẫu tất cả nằm trong giới hạn sinh lý bình thường (không quá thấp hoặc quá cao).

– Khoảng dao động tương đối hẹp, cho thấy dữ liệu phân tán vừa phải, không có giá trị ngoại lai nghiêm trọng.

1.3.8 1.3.8. Tạo bảng tần số của Weight_Group:

dataset$Weight_Group <- cut(dataset$Weight,
                            breaks = 5,
                            labels = c("Nhóm 1", "Nhóm 2", "Nhóm 3", "Nhóm 4", "Nhóm 5"))
table(dataset$Weight_Group)
## 
## Nhóm 1 Nhóm 2 Nhóm 3 Nhóm 4 Nhóm 5 
##  20272  20220  19851  19799  19858

Giải thích:

cut(dataset$Weight, breaks = 5, labels = …): chia dữ liệu Weight thành 5 khoảng bằng nhau (theo giá trị). labels: đặt tên cho từng nhóm.

table(dataset$Weight_Group): đếm số lượng quan sát trong từng nhóm cân nặng.

1.3.9 1.3.9. Độ lệch chuẩn và phương sai của Weight_Group:

aggregate(Weight ~ Weight_Group, data = dataset, FUN = sd)
##   Weight_Group   Weight
## 1       Nhóm 1 3.504366
## 2       Nhóm 2 3.467652
## 3       Nhóm 3 3.448899
## 4       Nhóm 4 3.456697
## 5       Nhóm 5 3.447356
aggregate(Weight ~ Weight_Group, data = dataset, FUN = var)
##   Weight_Group   Weight
## 1       Nhóm 1 12.28058
## 2       Nhóm 2 12.02461
## 3       Nhóm 3 11.89491
## 4       Nhóm 4 11.94875
## 5       Nhóm 5 11.88427

Giải thích:

aggregate(…): dùng để tính toán thống kê theo nhóm trong dữ liệu.

Weight ~ Weight_Group: nghĩa là biến phụ thuộc là Weight và biến nhóm là Weight_Group.

data = dataset: chỉ định nguồn dữ liệu là bảng dataset.

FUN = sd: dùng để tính độ lệch chuẩn (standard deviation) của Weight trong từng nhóm.

FUN = var: dùng để tính phương sai (variance) của Weight trong từng nhóm.

1.3.10 1.3.10. Tứ phân vị của Cholesterol:

quantile(dataset$Cholesterol)
##   0%  25%  50%  75% 100% 
##  120  152  184  217  249

– Kết quả mặc định: trả về các phân vị 0%, 25%, 50%, 75%, 100%:

  • 0% => Giá trị nhỏ nhất (Min).

  • 25% => Q1 (tứ phân vị thứ nhất).

  • 50% => Median (trung vị).

  • 75% => Q3 (tứ phân vị thứ ba).

  • 100% => Giá trị lớn nhất (Max).

Giải thích:

quantile(): là hàm trong R dùng để tính các phân vị (percentiles/quantiles) của một biến. Phân vị giúp đánh giá phân bố dữ liệu, tập trung ở đâu, có giá trị ngoại lai không.

1.3.11 1.3.11. Kiểm tra có bao nhiêu giá trị khác biệt (unique):

sapply(dataset, function(x) length(unique(x)))
##     Student.ID            Age         Gender         Height         Weight 
##         100000             17              2          89924            601 
##     Blood.Type            BMI    Temperature     Heart.Rate Blood.Pressure 
##              4          89952            376             40             50 
##    Cholesterol  Temperature_C   Weight_Group 
##            130            376              5

– Blood.Type: 4 giá trị khác nhau. Nhóm máu A, B, AB, O => biến định tính.

– Weight: 601 giá trị khác nhau. Cân nặng có nhiều mức khác nhau => biến định lượng, phân tán vừa phải.

– Temperature_C: 376 giá trị khác nhau. Chuyển từ °F sang °C vẫn giữ dạng liên tục => biến định lượng.

– Cholesterol: 130 giá trị khác nhau. Cholesterol nhiều mức khác nhau => biến định lượng, phân tán rộng.

– Blood.Pressure: 50 giá trị khác nhau. Huyết áp đo bằng mmHg => biến định lượng, phân tán vừa phải.

Giải thích:

sapply(): hàm trong R dùng để áp dụng một hàm cho từng cột (hoặc phần tử) của một dữ liệu, và trả về kết quả dạng vector hoặc ma trận.

function(x) length(unique(x)): tạo một hàm ẩn danh (anonymous function) nhận đầu vào x (mỗi cột).

unique(x): trả về các giá trị khác nhau trong cột.

length(unique(x)): đếm số lượng giá trị khác nhau trong cột.

1.3.12 1.3.12. Trung bình Cholesterol theo Blood.Type:

aggregate(Cholesterol ~ Blood.Type, data = dataset, FUN = mean)
##   Blood.Type Cholesterol
## 1          A    184.3609
## 2         AB    184.3577
## 3          B    184.8111
## 4          O    184.6606

Giải thích:

aggregate(): là hàm trong R dùng để tóm tắt dữ liệu theo nhóm. Cho phép áp dụng một hàm thống kê (ví dụ mean, sd, sum) trên một biến theo từng nhóm của biến khác.

Cholesterol ~ Blood.Type: cú pháp Biến_cần_tính ~ Biến_nhóm: Cholesterol => biến định lượng cần tính trung bình. Blood.Type => biến phân loại dùng để nhóm dữ liệu.

data = dataset: chọn dataset làm nguồn dữ liệu để phân tích.

FUN = mean: áp dụng hàm trung bình (mean) để tính giá trị Cholesterol trung bình cho mỗi nhóm Blood.Type.

1.3.13 1.3.13. Trung bình và độ lệch chuẩn của Blood.Pressure theo Blood.Type:

dataset %>%
  group_by(Blood.Type) %>%
  summarise(
    Mean_BP = mean(Blood.Pressure, na.rm = TRUE),
    SD_BP = sd(Blood.Pressure, na.rm = TRUE)
  )
## # A tibble: 4 × 3
##   Blood.Type Mean_BP SD_BP
##   <chr>        <dbl> <dbl>
## 1 A             115.  14.5
## 2 AB            114.  14.4
## 3 B             114.  14.4
## 4 O             115.  14.4

– Trung bình huyết áp (Blood.Pressure) theo nhóm máu gần nhau, dao động từ 114,40 đến 114,67 mmHg. Độ lệch chuẩn các nhóm cũng tương tự (~14,36–14,44 mmHg), cho thấy huyết áp phân bố đồng đều giữa các nhóm máu, không có sự khác biệt rõ rệt.

Giải thích:

dataset %>%: dùng toán tử pipe (%>%) trong dplyr để truyền dữ liệu từ bước trước sang bước sau.

group_by(Blood.Type): nhóm dữ liệu theo Blood.Type (nhóm máu). Mỗi nhóm sẽ được tính toán riêng biệt.

summarise(…): tạo bảng tổng hợp với các chỉ số thống kê cho từng nhóm: Mean_BP = mean(Blood.Pressure, na.rm = TRUE) => tính trung bình Huyết áp của mỗi nhóm máu. SD_BP = sd(Blood.Pressure, na.rm = TRUE) => tính độ lệch chuẩn Huyết áp của mỗi nhóm.

na.rm = TRUE: loại bỏ các giá trị NA (nếu có) để tránh lỗi khi tính toán.

1.4 1.4. Trực quan hóa dữ liệu:

1.4.1 1.4.1. Biểu đồ cột của nhóm máu:

library(ggplot2)
## Warning: package 'ggplot2' was built under R version 4.5.1
## 
## Attaching package: 'ggplot2'
## The following objects are masked from 'package:psych':
## 
##     %+%, alpha
library(dplyr)
library(viridis)
## Warning: package 'viridis' was built under R version 4.5.1
## Loading required package: viridisLite
tanso1 <- table(dataset$Blood.Type)
bang_tanso1 <- data.frame(NhomMau = names(tanso1),  TanSo = as.numeric(tanso1), TanSoTichLuy = cumsum(as.numeric(tanso1)))
print(bang_tanso1)
##   NhomMau TanSo TanSoTichLuy
## 1       A 22236        22236
## 2      AB 22183        44419
## 3       B 22781        67200
## 4       O 32800       100000
library(ggplot2)
library(RColorBrewer)
my_colors <- brewer.pal(n = 6, name = "Set2")  
ggplot(bang_tanso1, aes(x = NhomMau, y = TanSo, fill = NhomMau)) +
  geom_col() +
  geom_text(aes(label = TanSo), vjust = -0.5, size = 4) +
  scale_fill_manual(values = my_colors) +
  labs(title = "Biểu đồ cột nhóm máu",
       x = "Nhóm máu",
       y = "Tần số") +
  theme_minimal() +
  theme(legend.position = "none")

Nhận xét:

– Nhóm máu O có tần số cao nhất với 32.800 bệnh nhân, chiếm ưu thế rõ rệt so với các nhóm còn lại.

– Các nhóm máu A, AB và B có tần số tương đối gần nhau, lần lượt là 22.236, 22.183 và 22.781, với nhóm B hơi cao hơn một chút.

Giải thích:

library(RColorBrewer): nạp gói RColorBrewer để lấy các bảng màu trực quan, đẹp mắt.

my_colors <- brewer.pal(n = 6, name = “Set2”): tạo một vector 6 màu từ bảng màu “Set2” để tô màu cho các cột biểu đồ.

ggplot(bang_tanso1, aes(x = NhomMau, y = TanSo, fill = NhomMau)): tạo khung biểu đồ.

geom_col(): vẽ cột dựa trên giá trị tần số đã có.

geom_text(aes(label = TanSo), vjust = -0.5, size = 4): thêm nhãn tần số lên đỉnh cột, vjust = -0.5 đẩy nhãn cao hơn một chút, size = 4 chỉnh kích thước chữ.

scale_fill_manual(values = my_colors): dùng màu đã định nghĩa cho các cột.

labs(title = “Biểu đồ cột tần số theo nhóm máu”, x = “Nhóm máu”, y = “Tần số”:) đặt tiêu đề biểu đồ và nhãn trục X, Y.

theme_minimal(): sử dụng giao diện tối giản, loại bỏ các chi tiết rườm rà.

theme(legend.position = “none”): ẩn legend vì màu cột đã biểu thị rõ nhóm máu.

1.4.2 1.4.2. Đường tích lũy của nhóm máu:

options(scipen = 999)

library(ggplot2)

ggplot(bang_tanso1, aes(x = NhomMau, y = TanSoTichLuy, group = 1)) +
  geom_line(color = "#0072B2", size = 1.2) +                        
  geom_point(size = 3, color = "#E69F00") +                        
  geom_text(aes(label = format(TanSoTichLuy, big.mark = ",")),       
            vjust = -0.7, size = 4, color = "#333333") +
  scale_y_continuous(labels = function(x) format(x, big.mark = ",")) +  
  labs(title = "Đường tích lũy nhóm máu",
       x = "Nhóm máu",
       y = "Tần số tích lũy",) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5, color = "#333333"),
    axis.title.x = element_text(face = "bold"),
    axis.title.y = element_text(face = "bold")
  )
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Nhận xét

– Giá trị tần số cộng dồn tăng dần từ nhóm máu A đến O, cho thấy tổng số bệnh nhân được cộng dồn theo thứ tự nhóm máu.

– Nhóm máu O đứng ở vị trí cuối với tổng tần số cộng dồn là 100.000, tương ứng tổng số mẫu trong dữ liệu.

– Các điểm dữ liệu và các nhãn số trên biểu đồ rõ ràng giúp dễ theo dõi giá trị tích lũy cho từng nhóm máu.

– Biểu đồ cho thấy sự phân bố của dữ liệu theo nhóm máu và mức độ đóng góp của từng nhóm vào tổng số mẫu.

=> Đây là kiểu biểu đồ hữu ích để thấy sự tích lũy và tỷ lệ phần trăm của từng nhóm máu trong toàn bộ dữ liệu.

Giải thích:

options(scipen = 999): tắt hiển thị dạng số khoa học trong R, để các giá trị số được hiển thị bình thường, dễ đọc.

library(ggplot2): nạp gói ggplot2 để vẽ biểu đồ.

ggplot(bang_tanso1, aes(x = NhomMau, y = TanSoTichLuy, group = 1)): tạo khung biểu đồ với trục X là các nhóm máu, trục Y là tần số tích lũy, group = 1 để nối tất cả các điểm thành một đường duy nhất.

geom_line(color = “#0072B2”, size = 1.2): vẽ đường nối các giá trị tần số tích lũy, màu xanh dương, độ dày 1.2.

geom_point(size = 3, color = “#E69F00”): đánh dấu các điểm dữ liệu trên đường bằng chấm màu cam, kích thước 3.

geom_text(aes(label = format(TanSoTichLuy, big.mark = “,”)), vjust = -0.7, size = 4, color = “#333333”): hiển thị nhãn tần số tích lũy trên từng điểm, định dạng có dấu phẩy, nhãn hơi lệch lên trên điểm, chữ màu xám đậm, cỡ chữ 4.

scale_y_continuous(labels = function(x) format(x, big.mark = “,”)): định dạng trục Y để hiển thị số có dấu phẩy, dễ đọc.

labs(title = “Đường Biểu Diễn Tần Số Tích Lũy Nhóm Máu”, x = “Nhóm Máu”, y = “Tần số tích lũy”): đặt tiêu đề biểu đồ và nhãn trục X, Y.

theme_minimal(base_size = 14): dùng giao diện tối giản, chữ cơ bản cỡ 14.

theme(plot.title = element_text(face = “bold”, hjust = 0.5, color = “#333333”), axis.title.x = element_text(face = “bold”), axis.title.y = element_text(face = “bold”)): tùy chỉnh chi tiết giao diện, tiêu đề in đậm, căn giữa, màu xám đậm; nhãn trục X, Y in đậm.

1.4.3 1.4.3. Biểu đồ ngang theo nhóm máu:

ggplot(bang_tanso1, aes(x = NhomMau, y = TanSo, fill = NhomMau)) +
  geom_col(color = "black") +
  geom_text(aes(label = TanSo), hjust = -0.5, size = 4) +
  coord_flip() +
  labs(title = "Biểu đồ ngang nhóm máu", 
       x = "Nhóm máu", 
       y = "Tần số") +
  scale_fill_brewer(palette = "Set2") +
  theme_minimal() +
  theme(plot.title = element_text(face = "bold", hjust = 0.3))

Nhận xét:

– Nhóm máu O có tần số vượt trội hoàn toàn so với các nhóm máu còn lại (ở mức 32.800).

– Sự khác biệt này tạo nên một phân bố không đồng đều, cho thấy Nhóm máu O là nhóm máu phổ biến nhất trong tập dữ liệu này

– Ba nhóm máu còn lại (A, B, AB) có tần số tương đương nhau và tạo thành một cụm ở mức thấp hơn đáng kể.

– Nhóm máu AB là nhóm máu ít phổ biến nhất với tần số thấp nhất là 22.183.

– Nhóm AB chỉ chiếm một phần rất nhỏ so với nhóm O.

Giải thích:

ggplot(bang_tanso1, aes(x = NhomMau, y = TanSo, fill = NhomMau)): tạo khung biểu đồ với trục X là nhóm máu, trục Y là tần số, mỗi cột được tô màu theo nhóm máu.

geom_col(color = “black”): vẽ các cột, chiều cao dựa trên tần số, có đường viền màu đen.

geom_text(aes(label = TanSo), hjust = -0.5, size = 4): thêm nhãn giá trị tần số lên từng cột, nhãn hơi lệch ra ngoài, chữ cỡ 4.

coord_flip(): xoay trục X và Y, biểu đồ trở thành cột ngang để dễ đọc tên nhóm máu.

labs(title = “Biểu đồ ngang tần số theo nhóm máu”, x = “Nhóm máu”, y = “Tần số”): đặt tiêu đề biểu đồ và nhãn trục X, Y.

scale_fill_brewer(palette = “Set2”): sử dụng bảng màu Set2 từ RColorBrewer, màu pastel hài hòa và dễ phân biệt.

theme_minimal(): giao diện tối giản, loại bỏ các chi tiết rườm rà.

theme(plot.title = element_text(face = “bold”, hjust = 0.5)): tùy chỉnh tiêu đề in đậm và căn giữa.

1.4.4 1.4.4. Biểu đồ tròn theo nhóm máu:

tansuat1 <- prop.table(tanso1) * 100
bang_tansuat1 <- data.frame(NhomMau = names(tansuat1), TanSuat = round(as.numeric(tansuat1), 2), TanSuatTichLuy = round(cumsum(as.numeric(tansuat1)), 2))
print(bang_tansuat1)
##   NhomMau TanSuat TanSuatTichLuy
## 1       A   22.24          22.24
## 2      AB   22.18          44.42
## 3       B   22.78          67.20
## 4       O   32.80         100.00
Sys.setlocale("LC_ALL", "Vietnamese")
## Warning in Sys.setlocale("LC_ALL", "Vietnamese"): using locale code page other
## than 65001 ("UTF-8") may cause problems
## [1] "LC_COLLATE=Vietnamese_Vietnam.1258;LC_CTYPE=Vietnamese_Vietnam.1258;LC_MONETARY=Vietnamese_Vietnam.1258;LC_NUMERIC=C;LC_TIME=Vietnamese_Vietnam.1258"
library(ggplot2)
ggplot(bang_tansuat1, aes(x = "", y = TanSuat, fill = NhomMau)) +
  geom_bar(width = 1, stat = "identity", color = "white", linewidth = 0.5) +
  coord_polar("y", start = 0) +
  geom_text(aes(label = paste0(TanSuat, "%")),
            position = position_stack(vjust = 0.55),
            color = "gray10", size = 4, fontface = "bold") +
  scale_fill_manual(values = c(
    "#A8DADC",
    "#F6BD60",
    "#F28482",
    "#84A59D",
    "#B9FBC0",
    "#FFD6A5"
  )) +
  labs(
    title = "Biểu đồ tròn nhóm máu",
    fill = "Nhóm máu"
  ) +
  theme_void() +
  theme(
    plot.title = element_text(size = 15, face = "bold", hjust = 0.5, color = "#333333"),
    legend.title = element_text(size = 12, face = "bold"),
    legend.text = element_text(size = 11),
    legend.position = "right"
  )

Nhận xét:

– Nhóm máu phổ biến nhất là nhóm máu O chiếm tỷ lệ cao nhất với 32.8%.

– Các nhóm máu còn lại (A, B, AB) có tỷ lệ rất sát nhau và đều xấp xỉ 22%.

  • Nhóm máu B chiếm 22.78%

  • Nhóm máu A chiếm 22.24%

  • Nhóm máu AB chiếm 22.18%

– Nhóm máu ít phổ biến nhất trong 4 nhóm, nhóm máu AB có tỷ lệ thấp nhất (22.18%), nhưng chỉ chênh lệch một chút so với nhóm A và B.

– Nhóm máu O chiếm gần 1/3 tổng số, trong khi ba nhóm máu còn lại (A, B, AB) phân bố gần như đồng đều.

Giải thích:

Sys.setlocale(“LC_ALL”, “Vietnamese”): cài đặt môi trường hệ thống sang tiếng Việt để hiển thị đúng các ký tự tiếng Việt.

library(ggplot2) : tải thư viện ggplot2, công cụ đồ họa chính để vẽ biểu đồ.

ggplot(bang_tansuat1, aes(x = ““, y = TanSuat, fill = NhomMau)): khởi tạo đồ thị, ánh xạ trục \(x\) cố định, trục \(y\) là TanSuat (tần suất) và màu sắc (fill) theo NhomMau.

geom_bar(width = 1, stat = “identity”, color = “white”, linewidth = 0.5): vẽ các thanh cột, stat = “identity” sử dụng trực tiếp giá trị TanSuat làm chiều cao, thêm viền trắng mỏng để phân tách các lát cắt.

coord_polar(“y”, start = 0): chuyển đổi hệ tọa độ từ biểu đồ cột sang biểu đồ tròn, biến trục \(y\) (tần suất) thành bán kính.

geom_text(aes(label = paste0(TanSuat, “%”)), position = position_stack(vjust = 0.55), color = “gray10”, size = 4, fontface = “bold”): thêm nhãn phần trăm vào giữa các lát cắt, position_stack(vjust = 0.55) canh giữa nhãn chính xác.

scale_fill_manual(values = c(“#A8DADC”, “#F6BD60”, “#F28482”, “#84A59D”, “#B9FBC0”, “#FFD6A5”)): thiết lập các mã màu tùy chỉnh (Hexadecimal) cho từng nhóm máu.

labs(title = “Biểu đồ tròn theo nhóm máu”, fill = “Nhóm máu”): đặt tiêu đề chính của đồ thị và tiêu đề của chú giải màu (fill).

theme_void(): áp dụng giao diện tối giản, xóa tất cả các thành phần không cần thiết (trục, đường lưới, nền).

theme(plot.title = element_text(size = 15, face = “bold”, hjust = 0.5, color = “#333333”), legend.title = element_text(size = 12, face = “bold”), legend.text = element_text(size = 11), legend.position = “right”): định dạng tiêu đề đồ thị (căn giữa, in đậm), định dạng chú giải, đặt chú giải ở bên phải.

1.4.5 1.4.5. Biểu đồ cột của Weight:

Sys.setlocale("LC_ALL", "Vietnamese_Vietnam.1258")
## [1] "LC_COLLATE=Vietnamese_Vietnam.1258;LC_CTYPE=Vietnamese_Vietnam.1258;LC_MONETARY=Vietnamese_Vietnam.1258;LC_NUMERIC=C;LC_TIME=Vietnamese_Vietnam.1258"
options(encoding = "UTF-8")
Sys.setlocale("LC_CTYPE", "en_US.UTF-8")
## [1] "en_US.UTF-8"
library(ggplot2)

ggplot(dataset, aes(x = Weight)) +
  geom_histogram(
    bins = 15,
    fill = "#69b3a2",  
    color = "white"
  ) +
  geom_text(
    stat = "bin",
    bins = 15,
    aes(label = ..count..),
    vjust = -0.3,
    size = 3,
    color = "black"
  ) +
  scale_x_continuous(
    breaks = seq(40, max(dataset$Weight, na.rm = TRUE), by = 5)
  ) +
  labs(
    title = "Biểu đồ cột cân nặng",
    x = "Cân nặng (kg)",
    y = "Tần số"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold", size = 13),
    axis.text = element_text(size = 11),
    axis.title = element_text(size = 11)
  )
## Warning: The dot-dot notation (`..count..`) was deprecated in ggplot2 3.4.0.
## ℹ Please use `after_stat(count)` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Nhận xét:

– Tần số tập trung cao: Đa số các cá nhân có cân nặng từ 45 kg đến 95 kg, với tần số duy trì ổn định ở mức rất cao, quanh 7.000 người.

– Giá trị thấp nhất (hai đầu): Hai nhóm cân nặng ở hai đầu mút (40 kg và 100 kg) có tần số thấp hơn đáng kể (khoảng 3.600 - 3.700 người) so với nhóm ở giữa.

– Hình dạng phân bố: Phân bố có hình dạng gần như đối xứng, cho thấy các cân nặng trung bình là phổ biến nhất.

Giải thích:

ggplot(dataset, aes(x = Weight)): tạo khung biểu đồ với trục X là cột cân nặng của dữ liệu.

geom_histogram(bins = 15, fill = “#69b3a2”, color = “white”): vẽ biểu đồ cột (histogram) với 15 cột, cột tô màu xanh (#69b3a2) và đường viền màu trắng.

geom_text(stat = “bin”, bins = 15, aes(label = ..count..), vjust = -0.3, size = 3, color = “black”): thêm nhãn hiển thị số lượng quan sát trong mỗi cột, nhãn hơi lệch lên trên, cỡ chữ 3, màu chữ đen.

scale_x_continuous(breaks = seq(40, max(dataset$Weight, na.rm = TRUE), by = 5)): định dạng trục X với các giá trị hiển thị từ 40 đến giá trị cân nặng lớn nhất, cách nhau 5 kg.

labs(title = “Biểu đồ cột tần số theo cân nặng”, x = “Cân nặng (kg)”, y = “Tần số”): đặt tiêu đề biểu đồ và nhãn trục X, Y.

theme_minimal(): sử dụng giao diện tối giản, loại bỏ các chi tiết rườm rà.

theme(plot.title = element_text(hjust = 0.5, face = “bold”, size = 13), axis.text = element_text(size = 11), axis.title = element_text(size = 12)): tùy chỉnh giao diện chi tiết, tiêu đề căn giữa và in đậm, kích thước chữ tiêu đề và nhãn trục được chỉnh phù hợp, chữ trục số liệu cỡ 11.

1.4.6 1.4.6. Biểu đồ đường mật độ của Weight:

library(ggplot2)

ggplot(dataset, aes(x = Weight)) +
  geom_density(color = "#1f78b4", size = 1.2) +
  labs(
    title = "Biểu đồ đường mật độ cân nặng",
    x = "Cân nặng (kg)",
    y = "Mật độ"
  ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5)
  )

Nhận xét:

– Phân bố đồng đều: Mật độ phân bố gần như rất đồng đều trong khoảng cân nặng rộng, từ khoảng 45 kg đến 95 kg. Đường mật độ trong khu vực này duy trì ổn định quanh mức 0.016.

– Mật độ thấp ở hai đầu: Mật độ giảm mạnh, tạo thành hai đuôi dốc đứng ở hai đầu mút của biểu đồ, tương ứng với mức cân nặng 40 kg và 100 kg. Điều này cho thấy số lượng cá thể có cân nặng cực đoan (rất nhẹ hoặc rất nặng) là ít hơn hẳn.

– Không có đỉnh rõ rệt: Biểu đồ không có một đỉnh duy nhất nổi bật, mà có nhiều dao động nhỏ trên đỉnh phẳng, củng cố nhận định rằng hầu hết các cân nặng trong khoảng 45 kg - 95 kg đều có mật độ xuất hiện tương đương nhau.

Giải thích:

ggplot(dataset, aes(x = Weight)): tạo khung biểu đồ với trục X là cột cân nặng của dữ liệu.

geom_density(color = “#1f78b4”, size = 1.2): vẽ đường mật độ (density) cho dữ liệu cân nặng, đường màu xanh dương (#1f78b4) và dày 1.2.

labs(title = “Biểu đồ đường mật độ của cân nặng”, x = “Weight (kg)”, y = “Mật độ”): đặt tiêu đề biểu đồ và nhãn trục X, Y.

theme_minimal(base_size = 14): sử dụng giao diện tối giản với cỡ chữ cơ bản 14.

theme(plot.title = element_text(face = “bold”, hjust = 0.5)): tùy chỉnh tiêu đề in đậm và căn giữa.

1.4.7 1.4.7. Boxplot của Weight:

library(ggplot2)

ggplot(dataset, aes(y = Weight)) +
  geom_boxplot(fill = "lightcoral", color = "black") +
  labs(
    title = "Boxplot cân nặng",
    y = "Cân nặng (kg)"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(size = 16, face = "bold", hjust = 0.5),
    axis.title.y = element_text(size = 13),
    axis.text.y = element_text(size = 11)
  )

Nhận xét:

– Phân bố đối xứng: Dữ liệu cân nặng có sự phân bố rất đối xứng, không có giá trị ngoại lai (outlier).

– Trung vị và phạm vi chính: Cân nặng trung vị (Q2) là 70 kg.

– Tập trung dữ liệu: 50% số người được khảo sát có cân nặng nằm trong khoảng 55 kg đến 85 kg.

– Phạm vi tổng thể: Toàn bộ dữ liệu nằm trong khoảng 40 kg đến 100 kg.

Giải thích:

ggplot(dataset, aes(y = Weight)): tạo khung biểu đồ với trục Y là cột cân nặng của dữ liệu.

geom_boxplot(fill = “lightcoral”, color = “black”): vẽ biểu đồ Boxplot, bên trong cột tô màu hồng nhạt (lightcoral), viền cột màu đen.

labs(title = “Biểu đồ Boxplot theo cân nặng”, y = “Cân nặng (kg)”): đặt tiêu đề biểu đồ và nhãn trục Y.

theme_minimal(): sử dụng giao diện tối giản, loại bỏ các chi tiết rườm rà.

theme(plot.title = element_text(size = 16, face = “bold”, hjust = 0.5), axis.title.y = element_text(size = 13), axis.text.y = element_text(size = 11)): tùy chỉnh giao diện chi tiết, tiêu đề in đậm, cỡ chữ 16, căn giữa; nhãn trục Y cỡ chữ 13; chữ trục số liệu cỡ 11.

1.4.8 1.4.8. Biểu đồ cột nhóm cân nặng theo nhóm máu:

dataset$Weight_Group <- cut(dataset$Weight,
                            breaks = 3, 
                            include.lowest = TRUE)

tanso2 <- table(dataset$Weight_Group)
bang_tanso2 <- data.frame(
  Weight_Group = names(tanso2),
  TanSo = as.numeric(tanso2),
  TanSoTichLuy = cumsum(as.numeric(tanso2)))

print(bang_tanso2)
##   Weight_Group TanSo TanSoTichLuy
## 1    [39.9,60] 33716        33716
## 2      (60,80] 33264        66980
## 3     (80,100] 33020       100000
library(ggplot2)

ggplot(dataset, aes(x = Blood.Type, fill = Weight_Group)) +
  geom_bar(position = "dodge", color = "black") +
  geom_text(
    stat = "count",
    aes(label = ..count..),
    position = position_dodge(width = 0.9),
    vjust = -0.3,
    size = 3.5
  ) +
  scale_fill_brewer(palette = "Set3", name = "Nhóm cân nặng") +
  labs(
    title = "Nhóm cân nặng và nhóm máu",
    x = "Nhóm máu",
    y = "Tần số"
  ) +
  theme_minimal() +
  theme(
    legend.position = "top",
    plot.title = element_text(size = 16, face = "bold", hjust = 0.5),
    axis.text.x = element_text(size = 12),
    axis.title = element_text(size = 13)
  )

Nhận xét:

– Sự khác biệt rõ rệt theo nhóm máu: Tần số giữa các nhóm máu có sự khác biệt rất lớn. Nhóm máu O có tần số cao hơn đáng kể so với ba nhóm máu còn lại.

  • Nhóm O: Tổng tần số xấp xỉ 32.000 (cao nhất).

  • Nhóm A, AB, B: Tổng tần số của mỗi nhóm chỉ dao động quanh 22.000 - 23.000.

– Phân bố Cân nặng theo Nhóm máu: Trong mỗi nhóm máu, tần số của các nhóm cân nặng rất đồng đều và gần như tương đương nhau.

– Nhóm A, AB, B: Tần số của ba nhóm cân nặng ([39, 60], (60, 80], (80, 100]) đều nằm trong khoảng hẹp 7.292 đến 7.743.

– Nhóm O: Tần số của ba nhóm cân nặng cũng rất đồng đều, xấp xỉ 10.000 người ([39, 60] là 11.132; (60, 80] là 10.968; (80, 100] là 10.700).

Giải thích:

ggplot(dataset, aes(x = Blood.Type, fill = Weight_Group)): tạo khung biểu đồ với trục X là nhóm máu và các cột được tô màu theo nhóm cân nặng.

geom_bar(position = “dodge”, color = “black”): vẽ biểu đồ cột phân tổ, các cột nhóm cân nặng đặt cạnh nhau (dodge), viền cột màu đen.

geom_text(stat = “count”, aes(label = ..count..), position = position_dodge(width = 0.9), vjust = -0.3, size = 3.5): thêm nhãn số lượng trên mỗi cột, nhãn căn đúng với từng nhóm cột, hơi lệch lên trên, chữ cỡ 3.5.

scale_fill_brewer(palette = “Set3”, name = “Nhóm cân nặng”): sử dụng bảng màu Set3 từ RColorBrewer và đặt tên cho legend là “Nhóm cân nặng”.

labs(title = “Biểu đồ cột phân tổ nhóm cân nặng theo nhóm máu”, x = “Nhóm máu”, y = “Tần số”): đặt tiêu đề biểu đồ và nhãn trục X, Y.

theme_minimal(): sử dụng giao diện tối giản, loại bỏ các chi tiết rườm rà.

theme(legend.position = “top”, plot.title = element_text(size = 16, face = “bold”, hjust = 0.5), axis.text.x = element_text(size = 12), axis.title = element_text(size = 13)): tùy chỉnh giao diện chi tiết, đặt legend lên trên, tiêu đề in đậm, căn giữa, chữ tiêu đề cỡ 16; chữ trục X cỡ 12; nhãn trục cỡ 13.

1.4.9 1.4.9. Biểu đồ tròn nhóm cân nặng theo nhóm máu:

bang_tansuat2 <- dataset %>%
  group_by(Blood.Type, Weight_Group) %>%
  summarise(Count = n(), .groups = "drop") %>%
  group_by(Blood.Type) %>%
  mutate(TanSuat = round(Count / sum(Count) * 100, 1))
print(bang_tansuat2)
## # A tibble: 12 × 4
## # Groups:   Blood.Type [4]
##    Blood.Type Weight_Group Count TanSuat
##    <chr>      <fct>        <int>   <dbl>
##  1 A          [39.9,60]     7359    33.1
##  2 A          (60,80]       7431    33.4
##  3 A          (80,100]      7446    33.5
##  4 AB         [39.9,60]     7482    33.7
##  5 AB         (60,80]       7292    32.9
##  6 AB         (80,100]      7409    33.4
##  7 B          [39.9,60]     7743    34  
##  8 B          (60,80]       7573    33.2
##  9 B          (80,100]      7465    32.8
## 10 O          [39.9,60]    11132    33.9
## 11 O          (60,80]      10968    33.4
## 12 O          (80,100]     10700    32.6
library(ggplot2)

ggplot(bang_tansuat2, aes(x = "", y = TanSuat, fill = Weight_Group)) +
  geom_bar(width = 1, stat = "identity", color = "white", linewidth = 0.5) +
  coord_polar("y", start = 0) +
  facet_wrap(~ Blood.Type) +
  geom_text(aes(label = paste0(TanSuat, "%")),
            position = position_stack(vjust = 0.5),
            size = 3.5, fontface = "bold") +
  scale_fill_brewer(palette = "Set3", name = "Nhóm cân nặng") +
  labs(
    title = "Nhóm cân nặng và nhóm máu"
  ) +
  theme_void() +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold", size = 16),
    legend.position = "bottom",
    legend.title = element_text(face = "bold"),
    strip.text = element_text(face = "bold", size = 12)
  )

Nhận xét:

– Phân bố cân nặng đồng đều: Tỷ lệ phần trăm của ba nhóm cân nặng ([39, 60], (60, 80], (80, 100]) là rất đồng nhất trong tất cả bốn nhóm máu (A, AB, B, O).

– Tỷ lệ xấp xỉ 1/3: Trong mỗi nhóm máu, ba nhóm cân nặng đều chiếm tỷ lệ xấp xỉ 33% (dao động trong khoảng 32.6% đến 34%). Điều này có nghĩa là mỗi nhóm cân nặng chiếm khoảng một phần ba tổng số người trong nhóm máu đó.

– Không có mối liên hệ rõ rệt: Việc tỷ lệ phần trăm các nhóm cân nặng gần như bằng nhau giữa các nhóm máu (A, AB, B, O) cho thấy không có mối liên hệ đáng kể giữa nhóm máu và sự phân bố cân nặng.

Giải thích:

ggplot(bang_tansuat2, aes(x = ““, y = TanSuat, fill = Weight_Group)): tạo khung biểu đồ với trục Y là tần suất nhóm cân nặng, trục X để trống, các phần được tô màu theo nhóm cân nặng.

geom_bar(width = 1, stat = “identity”, color = “white”, linewidth = 0.5): vẽ cột với chiều cao tương ứng giá trị tần suất, viền cột màu trắng, độ dày viền 0.5, bề rộng cột đầy đủ (width = 1).

coord_polar(“y”, start = 0): chuyển đổi biểu đồ cột thành biểu đồ tròn (pie chart) dựa trên trục Y.

facet_wrap(~ Blood.Type): tạo các biểu đồ tròn riêng biệt cho từng nhóm máu.

geom_text(aes(label = paste0(TanSuat, “%”)), position = position_stack(vjust = 0.5), size = 3.5, fontface = “bold”): thêm nhãn phần trăm lên từng phần trong biểu đồ, căn giữa từng phần, chữ cỡ 3.5, in đậm.

scale_fill_brewer(palette = “Set3”, name = “Nhóm cân nặng”): sử dụng bảng màu Set3 từ RColorBrewer, đặt tên legend là “Nhóm cân nặng”.

labs(title = “Biểu đồ tròn phân tổ nhóm cân nặng theo nhóm máu”): đặt tiêu đề cho biểu đồ.

theme_void(): sử dụng giao diện trống, loại bỏ các trục và lưới để tập trung vào biểu đồ tròn.

theme(plot.title = element_text(hjust = 0.5, face = “bold”, size = 16), legend.position = “bottom”, legend.title = element_text(face = “bold”), strip.text = element_text(face = “bold”, size = 12)): tùy chỉnh giao diện chi tiết, tiêu đề in đậm và căn giữa, legend đặt dưới biểu đồ, tiêu đề legend in đậm, nhãn facet (tên nhóm máu) in đậm và cỡ chữ 12.

1.4.10 1.4.10. Boxplot của Cholesterol:

library(ggplot2)

ggplot(dataset, aes(y = Cholesterol)) +
  geom_boxplot(fill = "#F28482", color = "black", width = 0.4) +
  labs(
    title = "Boxplot Cholesterol",
    y = "Cholesterol (mg/dL)"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5, size = 14),
    axis.text.x = element_text(size = 12),
    axis.title.x = element_text(size = 12),
    axis.title.y = element_text(size = 12)
  )

Nhận xét:

  • Giá trị trung vị (Median): Đường kẻ đậm chia hộp nằm chính xác tại 185 mg/dL. Điều này cho thấy 50% các cá thể có mức Cholesterol dưới 185 mg/dL.

  • Trung vị: Mức Cholesterol trung vị (Q2) là 185 mg/dL.

  • Tập trung dữ liệu: 50% số người được khảo sát có mức Cholesterol tập trung trong khoảng 155 mg/dL đến 215 mg/dL.

Giải thích:

ggplot(dataset, aes(y = Cholesterol)): tạo khung biểu đồ với trục Y là cột Cholesterol của dữ liệu.

geom_boxplot(fill = “#F28482”, color = “black”, width = 0.4): vẽ biểu đồ Boxplot, bên trong cột tô màu hồng nhạt (#F28482), viền cột màu đen, chiều rộng cột 0.4.

labs(title = “Biểu đồ Boxplot của Cholesterol”, y = “Cholesterol”): đặt tiêu đề biểu đồ và nhãn trục Y.

theme_minimal(): sử dụng giao diện tối giản, loại bỏ các chi tiết rườm rà.

theme(plot.title = element_text(face = “bold”, hjust = 0.5, size = 14), axis.text.x = element_text(size = 12), axis.title.x = element_text(size = 12), axis.title.y = element_text(size = 12)): tùy chỉnh giao diện chi tiết, tiêu đề in đậm, căn giữa, cỡ chữ 14; chữ trục và nhãn trục cỡ 12.

1.4.11 1.4.11. Biểu đồ phân phối mật độ của Cholesterol:

library(ggplot2)

ggplot(dataset, aes(x = Cholesterol)) +
  geom_density(fill = "#9b59b6", alpha = 0.6) +
  labs(
    title = "Biểu đồ mật độ Cholesterol",
    x = "Cholesterol (mg/dL)",
    y = "Mật độ"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5)
  )

Nhận xét:

– Phân bố đồng đều và phẳng: Mật độ phân phối Cholesterol rất đồng đều và gần như phẳng trong phạm vi rộng, từ khoảng 140 mg/dL đến 240 mg/dL. Đường mật độ duy trì ổn định xấp xỉ mức 0.0078.

– Mật độ giảm mạnh ở hai đầu: Mật độ giảm nhanh chóng, tạo thành các đuôi dốc đứng ở hai đầu (từ 120 đến 140 mg/dL và từ 240 đến 250 mg/dL).

– Không có đỉnh nổi bật: Biểu đồ không có một đỉnh (mode) rõ ràng, mà chỉ có các dao động nhỏ trên đỉnh phẳng. Điều này khẳng định rằng hầu hết các mức Cholesterol trong phạm vi chính đều có mật độ xuất hiện gần như nhau.

Giải thích:

ggplot(dataset, aes(x = Cholesterol)): tạo khung biểu đồ với trục X là cột Cholesterol của dữ liệu.

geom_density(fill = “#9b59b6”, alpha = 0.6): vẽ đường mật độ (density) cho dữ liệu Cholesterol, tô màu tím (#9b59b6) với độ trong mờ 0.6.

labs(title = “Biểu đồ mật độ phân phối của Cholesterol”, x = “Cholesterol”, y = “Mật độ”): đặt tiêu đề biểu đồ và nhãn trục X, Y.

theme_minimal(): sử dụng giao diện tối giản, loại bỏ các chi tiết rườm rà.

theme(plot.title = element_text(face = “bold”, hjust = 0.5)): tùy chỉnh tiêu đề in đậm và căn giữa.

1.4.12 1.4.12. Biểu đồ cột theo nhóm Cholesterol:

dataset$Cholesterol_Group <- cut(dataset$Cholesterol,
                                 breaks = seq(120, 260, by = 22),
                                 right = FALSE,
                                 labels = paste(seq(120, 238, by = 22),
                                                seq(141, 260, by = 22), sep = "-"))

tanso3 <- table(dataset$Cholesterol_Group)

bang_tanso3 <- data.frame(
  Cholesterol_Group = names(tanso3),           
  TanSo = as.numeric(tanso3),
  TanSoTichLuy = cumsum(as.numeric(tanso3))
)

print(bang_tanso3)
##   Cholesterol_Group TanSo TanSoTichLuy
## 1           120-141 16855        16855
## 2           142-163 17023        33878
## 3           164-185 16940        50818
## 4           186-207 16701        67519
## 5           208-229 16921        84440
## 6           230-251 15560       100000
library(ggplot2)

ggplot(bang_tanso3, aes(x = Cholesterol_Group, y = TanSo, fill = TanSo)) +
  geom_col(color = "black", width = 0.7) +
  geom_text(aes(label = TanSo), vjust = -0.5, size = 3) +
  geom_hline(yintercept = mean(bang_tanso3$TanSo), linetype = "dashed", color = "red") +
  scale_fill_gradient(low = "#a6cee3", high = "#1f78b4") +
  labs(
    title = "Biểu đồ cột nhóm Cholesterol",
    x = "Nhóm Cholesterol (mg/dL)",
    y = "Tần số"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    axis.text.x = element_text(angle = 45, hjust = 1)
  )

Nhận xét:

– Phân bố đồng đều: Tần số của hầu hết các nhóm Cholesterol rất đồng đều, dao động quanh mức 16,900 người (được minh họa bằng đường gạch ngang màu đỏ).

– Bốn nhóm giữa ([142-163], [164-185], [186-207], [208-229]) và nhóm [120-141] đều có tần số xấp xỉ 16,900.

– Nhóm Cholesterol cao nhất thấp hơn: Nhóm có mức Cholesterol cao nhất [230-251] có tần số thấp hơn đáng kể (15,560 người) so với các nhóm còn lại.

Giải thích:

ggplot(bang_tanso3, aes(x = Cholesterol_Group, y = TanSo, fill = TanSo)): tạo khung biểu đồ với trục X là nhóm Cholesterol, trục Y là tần số, các cột được tô màu theo giá trị tần số.

geom_col(color = “black”, width = 0.7): vẽ cột với viền màu đen, chiều rộng cột 0.7.

geom_text(aes(label = TanSo), vjust = -0.5, size = 3): thêm nhãn hiển thị giá trị tần số trên đỉnh cột, nhãn hơi lệch lên trên, chữ cỡ 3.

geom_hline(yintercept = mean(bang_tanso3$TanSo), linetype = “dashed”, color = “red”): vẽ đường ngang tại giá trị trung bình của tần số, nét đứt, màu đỏ để tham chiếu.

scale_fill_gradient(low = “#a6cee3”, high = “#1f78b4”): sử dụng gradient màu từ xanh nhạt đến xanh đậm cho các cột theo giá trị tần số.

labs(title = “Biểu đồ cột tần số theo nhóm Cholesterol”, x = “Nhóm Cholesterol”, y = “Tần số”): đặt tiêu đề biểu đồ và nhãn trục X, Y.

theme_minimal(): sử dụng giao diện tối giản, loại bỏ các chi tiết rườm rà.

theme(plot.title = element_text(face = “bold”, hjust = 0.5), axis.text.x = element_text(angle = 45, hjust = 1)): tùy chỉnh giao diện, tiêu đề in đậm và căn giữa; chữ trục X xoay 45 độ để dễ đọc.

1.4.13 1.4.13. Biểu đồ cột nhóm Cholesterol theo nhóm máu:

library(ggplot2)
library(dplyr)

bang_tanso4 <- dataset %>%
  group_by(Blood.Type, Cholesterol_Group) %>%
  summarise(Count = n(), .groups = "drop")

ggplot(bang_tanso4, aes(x = Blood.Type, y = Count, fill = Cholesterol_Group)) +
  geom_col(position = position_dodge(width = 0.8), width = 0.7) +
  geom_text(aes(label = Count), 
            position = position_dodge(width = 0.8),
            vjust = -0.5, size = 1.7) +
  scale_fill_brewer(palette = "Set3", name = "Nhóm Cholesterol") +
  labs(
    title = "Nhóm cholesterol và nhóm máu",
    x = "Nhóm máu",
    y = "Tần số"
  ) +
  theme_minimal() +
  theme(
    legend.position = "top",
    plot.title = element_text(size = 16, face = "bold", hjust = 0.5),
    axis.text.x = element_text(size = 12),
    axis.title = element_text(size = 13)
  )

Nhận xét:

– Sự khác biệt rõ rệt theo Nhóm máu: Tần số ở nhóm máu O cao hơn hẳn so với ba nhóm còn lại (A, AB, B).

  • Nhóm O: Mọi nhóm Cholesterol đều có tần số xấp xỉ 5,000 đến 5,500 người.

  • Nhóm A, AB, B: Mọi nhóm Cholesterol đều có tần số xấp xỉ 3,400 đến 3,900 người.

– Phân bố Cholesterol đồng đều trong từng nhóm: Trong mỗi nhóm máu (A, AB, B, O), tần số của các nhóm Cholesterol khác nhau là rất đồng nhất, dao động trong phạm vi hẹp. Điều này khẳng định nhận xét từ biểu đồ tỷ lệ trước đó: sự phân bố Cholesterol không bị ảnh hưởng bởi nhóm máu.

– Nhóm [230-251] có tần số thấp nhất (chủ yếu): Mặc dù sự khác biệt nhỏ, nhóm Cholesterol cao nhất [230-251] (màu cam) có xu hướng có tần số thấp nhất hoặc gần thấp nhất trong hầu hết các nhóm máu (A, AB, B, O).

Giải thích:

library(ggplot2): nạp gói ggplot2 để vẽ biểu đồ.

library(dplyr): nạp gói dplyr để xử lý dữ liệu.

bang_tanso4 <- dataset %>% group_by(Blood.Type, Cholesterol_Group) %>% summarise(Count = n(), .groups = “drop”): nhóm dữ liệu theo nhóm máu và nhóm Cholesterol, đếm số quan sát trong mỗi nhóm và tạo bảng tần số mới.

ggplot(bang_tanso4, aes(x = Blood.Type, y = Count, fill = Cholesterol_Group)): tạo khung biểu đồ với trục X là nhóm máu, trục Y là tần số, các cột được tô màu theo nhóm Cholesterol.

geom_col(position = position_dodge(width = 0.8), width = 0.7): vẽ cột cho từng nhóm Cholesterol, đặt các cột cạnh nhau (dodge) với chiều rộng 0.7.

geom_text(aes(label = Count), position = position_dodge(width = 0.8), vjust = -0.5, size = 2.25): thêm nhãn số lượng trên đỉnh từng cột, nhãn căn đúng với cột, hơi lệch lên trên, chữ cỡ 2.25.

scale_fill_brewer(palette = “Set3”, name = “Nhóm Cholesterol”): sử dụng bảng màu Set3 từ RColorBrewer và đặt tên legend là “Nhóm Cholesterol”.

labs(title = “Biểu đồ cột tần số phân tổ nhóm Cholesterol theo nhóm máu”, x = “Nhóm máu”, y = “Tần số”): đặt tiêu đề biểu đồ và nhãn trục X, Y.

theme_minimal(): sử dụng giao diện tối giản, loại bỏ các chi tiết rườm rà.

theme(legend.position = “top”, plot.title = element_text(size = 16, face = “bold”, hjust = 0.5), axis.text.x = element_text(size = 12), axis.title = element_text(size = 13)): tùy chỉnh giao diện, đặt legend lên trên, tiêu đề in đậm, căn giữa, chữ tiêu đề cỡ 16; chữ trục X cỡ 12; nhãn trục cỡ 13.

1.4.14 1.4.14. Boxplot của Cholesterol theo nhóm Weight:

library(ggplot2)

ggplot(dataset, aes(x = Weight_Group, y = Cholesterol, fill = Weight_Group)) +
  geom_boxplot() +
  labs(
    title = "Boxplot cholesterol và nhóm cân nặng",
    x = "Nhóm cân nặng",
    y = "Cholesterol (mg/dL)"
  ) +
  theme_minimal() +
  scale_fill_brewer(palette = "Pastel1") +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold")
  )

Nhận xét:

– Trung vị (Median) Cholesterol đồng đều: Giá trị trung vị (đường kẻ ngang đậm trong hộp) của Cholesterol gần như bằng nhau giữa cả ba nhóm cân nặng: [39, 60], (60, 80], và (80, 100]. Đường trung vị đều nằm xấp xỉ mức 185 mg/dL.

– Phạm vi phân vị (IQR) đồng đều: Khoảng phân vị giữa (IQR), được biểu thị bằng chiều cao của hộp (box), là giống nhau cho cả ba nhóm cân nặng. Điều này cho thấy 50% dữ liệu Cholesterol giữa \(Q_1\)\(Q_3\) có độ phân tán tương đương nhau, không bị ảnh hưởng bởi nhóm cân nặng.

– Phạm vi tổng thể (Range) đồng đều: Phạm vi dữ liệu Cholesterol tổng thể (từ râu dưới đến râu trên) là như nhau cho cả ba nhóm cân nặng, từ mức thấp nhất xấp xỉ 120 mg/dL đến mức cao nhất xấp xỉ 250 mg/dL.

Giải thích:

ggplot(dataset, aes(x = Weight_Group, y = Cholesterol, fill = Weight_Group)): tạo khung biểu đồ với trục X là nhóm cân nặng, trục Y là Cholesterol, các cột Boxplot được tô màu theo nhóm cân nặng.

geom_boxplot(): vẽ biểu đồ Boxplot cho từng nhóm cân nặng.

labs(title = “Biểu đồ Boxplot của Cholesterol theo nhóm Weight”, x = “Weight Group”, y = “Cholesterol”): đặt tiêu đề biểu đồ và nhãn trục X, Y.

theme_minimal(): sử dụng giao diện tối giản, loại bỏ các chi tiết rườm rà.

scale_fill_brewer(palette = “Pastel1”): sử dụng bảng màu Pastel1 từ RColorBrewer để tô màu các Boxplot.

theme(plot.title = element_text(hjust = 0.5, face = “bold”)): tùy chỉnh tiêu đề in đậm và căn giữa.

1.4.15 1.4.15. Tỷ lệ nhóm Cholesterol theo nhóm máu:

library(dplyr)
library(ggplot2)

bang_tansuat3 <- dataset %>%
  group_by(Blood.Type, Cholesterol_Group) %>%
  summarise(Count = n(), .groups = "drop") %>%
  group_by(Blood.Type) %>%
  mutate(Percent = round(Count / sum(Count) * 100, 2))

ggplot(bang_tansuat3, aes(x = Blood.Type, y = Percent, fill = Cholesterol_Group)) +
  geom_col(position = "stack", color = "black") +
  geom_text(aes(label = paste0(Percent, "%")), 
            position = position_stack(vjust = 0.5), 
            size = 3) +
  labs(
    title = "Tỷ lệ nhóm cholesterol và nhóm máu",
    x = "Nhóm máu",
    y = "Tỷ lệ (%)",
    fill = "Nhóm Cholesterol"
  ) +
  theme_minimal() +
  scale_fill_brewer(palette = "Set3") +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold")
  )

Nhận xét:

– Phân bố Cholesterol cực kỳ đồng đều: Tỷ lệ phần trăm của sáu nhóm Cholesterol khác nhau là rất nhất quán giữa cả bốn nhóm máu (A, AB, B, O).

– Tỷ lệ xấp xỉ 1/6: Trong mỗi nhóm máu, mỗi nhóm Cholesterol chiếm tỷ lệ gần như bằng nhau, dao động quanh mức 16.5% đến 17.2% (tức là khoảng 1/6).

– Nhóm [230-251] thấp hơn một chút: Nhóm Cholesterol cao nhất, [230-251], có xu hướng chiếm tỷ lệ thấp hơn một chút (khoảng 15.4% - 15.8%) so với các nhóm khác (thường trên 16.5%).

Giải thích:

library(dplyr): nạp gói dplyr để xử lý dữ liệu.

bang_tansuat3 <- dataset %>% group_by(Blood.Type, Cholesterol_Group) %>% summarise(Count = n(), .groups = “drop”) %>% group_by(Blood.Type) %>% mutate(Percent = round(Count/sum(Count)*100,2)): tính bảng tần suất và tỷ lệ phần trăm của từng nhóm Cholesterol trong mỗi nhóm máu.

ggplot(bang_tansuat3, aes(x = Blood.Type, y = Percent, fill = Cholesterol_Group)): tạo khung biểu đồ với trục X là nhóm máu, trục Y là tỷ lệ %, các phần được tô màu theo nhóm Cholesterol.

geom_col(position = “stack”, color = “black”): vẽ cột xếp chồng (stacked bar) với viền cột màu đen.

geom_text(aes(label = paste0(Percent,“%”)), position = position_stack(vjust = 0.5), size = 3): thêm nhãn phần trăm lên từng phần của cột, căn giữa từng phần, chữ cỡ 3.

labs(title = “Biểu đồ tỷ lệ nhóm Cholesterol theo nhóm máu”, x = “Nhóm máu”, y = “Tỷ lệ %”): đặt tiêu đề biểu đồ và nhãn trục X, Y.

theme_minimal(): sử dụng giao diện tối giản, loại bỏ các chi tiết rườm rà.

scale_fill_brewer(palette = “Set3”): sử dụng bảng màu Set3 từ RColorBrewer để tô màu các phần của cột.

theme(plot.title = element_text(hjust = 0.5, face = “bold”)): tùy chỉnh tiêu đề in đậm và căn giữa.

2 2. CHƯƠNG 2: PHÂN TÍCH CÁC YẾU TỐ TÀI CHÍNH CỦA CÔNG TY GMD GIAI ĐOẠN 2015-2024

2.1 2.1. Giới thiệu bộ dữ liệu:

2.1.1 2.1.1. Đọc dữ liệu:

library(dplyr)
library(tidyr)
library(stringi)
library(ggplot2)
library(psych)
library(janitor)
## Warning: package 'janitor' was built under R version 4.5.2
## 
## Attaching package: 'janitor'
## The following objects are masked from 'package:stats':
## 
##     chisq.test, fisher.test

Giải thích:

library(dplyr): dùng để thao tác, biến đổi và tóm tắt dữ liệu. Nó cho phép lọc (filter), chọn cột (select), sắp xếp (arrange), tạo biến mới (mutate) và nhóm dữ liệu (group_by, summarise) một cách ngắn gọn, dễ đọc và hiệu quả.

library(tidyr): dùng để làm gọn cấu trúc dữ liệu, đặc biệt là chuyển đổi giữa dạng rộng (wide) và dài (long) bằng các hàm như pivot_longer() hoặc pivot_wider(). Nó giúp dữ liệu phù hợp với các công cụ trực quan hóa hoặc mô hình thống kê.

library(stringi): dùng để xử lý chuỗi ký tự, đặc biệt hữu ích khi làm việc với dữ liệu tiếng Việt. Gói này cho phép chuyển đổi chữ có dấu sang không dấu (stri_trans_general), đổi chữ hoa/thường, xóa ký tự đặc biệt và chuẩn hóa tên biến.

library(ggplot2): là gói chuyên dùng để vẽ đồ thị thống kê theo nguyên tắc “Grammar of Graphics”. Nó giúp trực quan hóa dữ liệu bằng nhiều loại biểu đồ như cột, đường, hộp, phân tán… và dễ dàng tùy chỉnh màu sắc, nhãn, chủ đề.

library(psych): được sử dụng để thực hiện các phân tích thống kê mô tả và kiểm định cơ bản. Hàm phổ biến nhất là describe(), cung cấp nhanh các chỉ số như trung bình, độ lệch chuẩn, giá trị nhỏ nhất, lớn nhất, độ lệch, độ nhọn.

library(janitor): giúp làm sạch dữ liệu và tên biến. Các hàm như clean_names() chuyển tên cột về dạng chuẩn (snake_case), tabyl() tính tần suất các giá trị, và remove_empty() loại bỏ hàng hoặc cột trống. Gói này thường được dùng ở bước tiền xử lý để dữ liệu sạch và dễ thao tác hơn.

2.1.2 2.1.2. Đọc từng sheet vào các dataframe riêng biệt và gán vào từng object mới:

library(readxl)
cdkt <- read_excel("D:/DuLieuThayTuongST3chuong2.xlsx", sheet = "CDKT")
kqkd <- read_excel("D:/DuLieuThayTuongST3chuong2.xlsx", sheet = "HDKD")
lctt <- read_excel("D:/DuLieuThayTuongST3chuong2.xlsx", sheet = "LCTT")

2.1.3 2.1.3. Chuẩn hóa cột đầu tiên:

names(cdkt)[1] <- "Khoan_muc"
names(lctt)[1] <- "Khoan_muc"
names(kqkd)[1] <- "Khoan_muc"

– Chuẩn hóa cấu trúc dữ liệu để dễ dàng gộp, nối và xử lý dữ liệu giữa các bảng.

Giải thích:

names(cdkt): lấy ra danh sách tên các cột của bảng cdkt.

names(cdkt)[1] <- “Khoan_muc”: truy cập vào phần tử thứ nhất trong danh sách tên cột (tức là cột đầu tiên) và gán lại tên của cột đó thành “Khoan_muc”.

names(lctt)[1] <- “Khoan_muc” và names(kqkd)[1] <- “Khoan_muc”: đổi tên cột đầu tiên trong hai bảng lctt và kqkd thành “Khoan_muc”.

2.1.4 2.1.4. Danh sách biến cần lọc:

Sys.setlocale("LC_ALL", "Vietnamese_Vietnam.1258")
## Warning in Sys.setlocale("LC_ALL", "Vietnamese_Vietnam.1258"): using locale
## code page other than 65001 ("UTF-8") may cause problems
## [1] "LC_COLLATE=Vietnamese_Vietnam.1258;LC_CTYPE=Vietnamese_Vietnam.1258;LC_MONETARY=Vietnamese_Vietnam.1258;LC_NUMERIC=C;LC_TIME=Vietnamese_Vietnam.1258"
options(encoding = "UTF-8")
Sys.setlocale("LC_CTYPE", "en_US.UTF-8")
## [1] "en_US.UTF-8"
vars_to_select <- c(
  "TỔNG CỘNG TÀI SẢN",
  "I. Nợ ngắn hạn",
  "II. Nợ dài hạn",
  "7. Chi phí tài chính",
  "18. Lợi nhuận sau thuế thu nhập doanh nghiệp(60=50-51-52)",
  "Lưu chuyển tiền thuần từ hoạt động kinh doanh",
  "I. Tiền và các khoản tương đương tiền"
)

Giải thích:

vars_to_select <- c(…): dấu <- dùng để gán giá trị cho biến. Hàm c() (combine) được dùng để tạo một vector gồm nhiều phần tử ký tự (chuỗi).

Cụ thể, vector này gồm 7 mục:

“TỔNG CỘNG TÀI SẢN”: tổng giá trị tài sản của doanh nghiệp.

“I. Nợ ngắn hạn”: tổng các khoản nợ phải trả trong thời gian ngắn hạn (dưới 1 năm).

“II. Nợ dài hạn”: tổng các khoản nợ dài hạn (trên 1 năm).

“7. Chi phí tài chính”: chi phí liên quan đến hoạt động tài chính.

“18. Lợi nhuận sau thuế thu nhập doanh nghiệp(60=50-51-52)”: lợi nhuận ròng sau khi trừ thuế thu nhập doanh nghiệp.

“Lưu chuyển tiền thuần từ hoạt động kinh doanh”: lượng tiền thực thu ròng từ hoạt động kinh doanh chính.

“I. Tiền và các khoản tương đương tiền”: tiền mặt và các tài sản dễ chuyển đổi thành tiền.

2.1.5 2.1.5. Hàm chuẩn hóa tên để so khớp:

clean_name <- function(x) {
  toupper(stringi::stri_trans_general(x, "Latin-ASCII")) %>%
    gsub("[^A-Z0-9]", "", .)
}
vars_clean <- clean_name(vars_to_select)

– Loại bỏ dấu và ký tự đặc biệt để dễ xử lý, so sánh và ghép dữ liệu.

Giải thích:

clean_name <- function(x) { … }: tạo một hàm chuẩn hóa tên biến.

stringi::stri_trans_general(x, “Latin-ASCII”): loại bỏ dấu tiếng Việt, chuyển ký tự có dấu về chữ latin không dấu.

toupper(…): chuyển toàn bộ chuỗi sang chữ in hoa.

%>%: truyền kết quả từ bước trước sang bước sau trong chuỗi thao tác.

gsub(“[^A-Z0-9]”, ““, .): loại bỏ mọi ký tự không phải chữ cái a–z hoặc chữ số 0–9, chỉ giữ ký tự và số.

vars_clean <- clean_name(vars_to_select): áp dụng hàm cho vector vars_to_select và lưu kết quả vào vars_clean.

vars_clean: chứa các tên đã được chuẩn hóa, viết hoa, không dấu, không khoảng trắng và không ký tự đặc biệt.

2.1.6 2.1.6. Hàm lọc biến theo tên:

filter_df <- function(df, nguon) {
  df %>%
    mutate(Khoan_muc_clean = clean_name(Khoan_muc)) %>%
    filter(Khoan_muc_clean %in% vars_clean) %>%
    mutate(Nguon = nguon) %>%
    select(-Khoan_muc_clean)
}

– Hàm này trả về bảng dữ liệu đã được lọc theo các khoản mục cần thiết và gắn thêm thông tin về nguồn dữ liệu.

Giải thích:

filter_df <- function(df, nguon) { … }: tạo một hàm tên là filter_df, nhận hai đối số là df (bảng dữ liệu) và nguon (tên nguồn dữ liệu).

df %>%: lấy dữ liệu đầu vào và thực hiện các thao tác tuần tự.

mutate(Khoan_muc_clean = clean_name(Khoan_muc)): tạo cột mới Khoan_muc_clean bằng cách chuẩn hóa tên trong cột Khoan_muc bằng hàm clean_name.

filter(Khoan_muc_clean %in% vars_clean): lọc các dòng mà giá trị trong Khoan_muc_clean nằm trong danh sách vars_clean.

mutate(Nguon = nguon): thêm một cột mới tên là Nguon, chứa tên nguồn dữ liệu được truyền vào hàm.

select(-Khoan_muc_clean): xóa cột Khoan_muc_clean khỏi bảng dữ liệu sau khi đã dùng xong.

2.1.7 2.1.7. Lọc dữ liệu từ 3 bảng:

cdkt_sel <- filter_df(cdkt, "CDKT")
lctt_sel <- filter_df(lctt, "LCTT")
kqkd_sel <- filter_df(kqkd, "KQKD")

– Lọc các khoản mục cần thiết trong từng bảng.

2.1.8 2.1.8. Gộp 3 bảng:

financial_selected <- bind_rows(cdkt_sel, lctt_sel, kqkd_sel)

– Tạo ra bảng tổng hợp financial_selected, chứa toàn bộ dữ liệu đã lọc từ 3 nguồn.

2.1.9 2.1.9. Xác định cột năm:

cols_years <- names(financial_selected)[!names(financial_selected) %in% c("Nguon", "Khoan_muc")]

– Chọn tất cả các cột năm.

2.1.10 2.1.10. Pivot sang dạng long:

library(dplyr)
library(tidyr)

financial_long <- financial_selected %>%
  pivot_longer(
    cols = -c(Nguon, Khoan_muc),
    names_to = "Nam",
    values_to = "Gia_tri"
  ) %>%
  mutate(
    Nam = as.numeric(gsub("[^0-9]", "", Nam)),
    Khoan_muc_VN = case_when(
      grepl("TỔNG CỘNG TÀI SẢN", Khoan_muc, ignore.case = TRUE) ~ "TCTS",
      grepl("Nợ ngắn hạn", Khoan_muc, ignore.case = TRUE) ~ "NNH",
      grepl("Nợ dài hạn", Khoan_muc, ignore.case = TRUE) ~ "NDH",
      grepl("Chi phí tài chính", Khoan_muc, ignore.case = TRUE) ~ "CPTC",
      grepl("Lợi nhuận sau thuế thu nhập doanh nghiệp", Khoan_muc, ignore.case = TRUE) ~ "LNST",
      grepl("Lưu chuyển tiền thuần từ hoạt động kinh doanh", Khoan_muc, ignore.case = TRUE) ~ "LCTT",
      grepl("Tiền và các khoản tương đương tiền", Khoan_muc, ignore.case = TRUE) ~ "TVTDT",
      TRUE ~ Khoan_muc
    )
  )

– Tạo bảng financial_long ở dạng dữ liệu dài, có cột năm, giá trị, nguồn và mã hóa tên các khoản mục ngắn gọn.

Giải thích:

financial_long <- financial_selected %>%: lấy dữ liệu từ bảng financial_selected và thực hiện các bước xử lý tiếp theo.

pivot_longer(…): chuyển dữ liệu từ dạng rộng (wide) sang dạng dài (long) để dễ phân tích.

cols = -c(Nguon, Khoan_muc): giữ nguyên hai cột “Nguon” và “Khoan_muc”, còn lại chuyển thành cột “Nam” và “Gia_tri”.

names_to = “Nam”: đặt tên cho cột chứa các tên năm là “Nam”.

values_to = “Gia_tri”: đặt tên cho cột chứa giá trị tương ứng là “Gia_tri”.

mutate(…): tạo hoặc biến đổi thêm các cột mới.

Nam = as.numeric(gsub(“[^0-9]”, ““, Nam)): trích số năm từ tên cột và chuyển thành kiểu số.

Khoan_muc_VN = case_when(…): tạo cột mới tên là “Khoan_muc_VN”, mã hóa các khoản mục bằng ký hiệu ngắn gọn.

grepl(“TỔNG CỘNG TÀI SẢN”, Khoan_muc, ignore.case = TRUE) ~ “TCTS”: nếu “Khoan_muc” chứa cụm từ “TỔNG CỘNG TÀI SẢN” thì gán mã là “TCTS”.

grepl(“Nợ ngắn hạn”, Khoan_muc, ignore.case = TRUE) ~ “NNH”: nếu chứa “Nợ ngắn hạn” thì gán mã là “NNH”.

grepl(“Nợ dài hạn”, Khoan_muc, ignore.case = TRUE) ~ “NDH”: nếu chứa “Nợ dài hạn” thì gán mã là “NDH”.

grepl(“Chi phí tài chính”, Khoan_muc, ignore.case = TRUE) ~ “CPTC”: nếu chứa “Chi phí tài chính” thì gán mã là “CPTC”.

grepl(“Lợi nhuận sau thuế thu nhập doanh nghiệp”, Khoan_muc, ignore.case = TRUE) ~ “LNST”: nếu chứa cụm này thì gán mã là “LNST”.

grepl(“Lưu chuyển tiền thuần từ hoạt động kinh doanh”, Khoan_muc, ignore.case = TRUE) ~ “LCTT”: nếu chứa cụm này thì gán mã là “LCTT”.

grepl(“Tiền và các khoản tương đương tiền”, Khoan_muc, ignore.case = TRUE) ~ “TVTDT”: nếu chứa cụm này thì gán mã là “TVTDT”.

TRUE ~ Khoan_muc: nếu không khớp với điều kiện nào ở trên thì giữ nguyên giá trị gốc.

2.1.11 2.1.11. Loại bỏ trùng lặp:

financial_long_unique <- financial_long %>%
  group_by(Nam, Khoan_muc_VN) %>%
  summarise(Gia_tri = first(Gia_tri), .groups = "drop")

– Loại bỏ các bản ghi trùng lặp trong nhóm (Nam, Khoan_muc_VN) và giữ giá trị đầu tiên.

2.1.12 2.1.12. Pivot sang dạng rộng:

financial_wide <- financial_long_unique %>%
  pivot_wider(
    names_from = Khoan_muc_VN,
    values_from = Gia_tri
  ) %>%
  arrange(Nam)

Giải thích:

pivot_wider: biến các giá trị trong một cột (Khoan_muc_VN) thành cột mới.

values_from = Gia_tri: điền dữ liệu vào các cột mới này.

arrange(Nam): sắp xếp bảng theo thứ tự tăng dần của năm.

2.1.13 2.1.13. Kiểm tra kết quả:

cat("## Kết quả: Tên cột trong financial_wide\n")
## ## Kết quả: Tên cột trong financial_wide
print(names(financial_wide))
## [1] "Nam"   "CPTC"  "LCTT"  "LNST"  "NDH"   "NNH"   "TCTS"  "TVTDT"

2.1.14 2.1.14. Xem cấu trúc dữ liệu:

str(financial_wide)
## tibble [10 × 8] (S3: tbl_df/tbl/data.frame)
##  $ Nam  : num [1:10] 2015 2016 2017 2018 2019 ...
##  $ CPTC : num [1:10] 112637632042 150966297263 145904768287 43698716269 146510028288 ...
##  $ LCTT : num [1:10] 353815618542 761518412131 632876665393 545285498122 1057439882016 ...
##  $ LNST : num [1:10] 564932238864 443734791018 581436276874 1900250425402 613569051999 ...
##  $ NDH  : num [1:10] 1791384488732 1290150502229 1520448359028 1890915837664 1724167022149 ...
##  $ NNH  : num [1:10] 1169335416918 2961152475919 2676231766155 1564164959918 1828483009231 ...
##  $ TCTS : num [1:10] 8179782482929 10117918996180 11291217207272 9984063244119 10119906897002 ...
##  $ TVTDT: num [1:10] 943317929162 724469956298 779802200597 172567048493 185545788383 ...

Giải thích:

str(): dùng để xem cấu trúc và kiểu dữ liệu chi tiết của bộ dữ liệu, phục vụ cho việc kiểm tra và mã hóa chính xác.

2.1.15 2.1.15. Kiểm tra số lượng dòng và cột:

dim(financial_wide)
## [1] 10  8

– Bộ dữ liệu gồm có 10 dòng và 8 cột.

2.1.16 2.1.16. Xem 6 dòng đầu tiên:

head(financial_wide)
## # A tibble: 6 × 8
##     Nam         CPTC          LCTT          LNST     NDH     NNH    TCTS   TVTDT
##   <dbl>        <dbl>         <dbl>         <dbl>   <dbl>   <dbl>   <dbl>   <dbl>
## 1  2015 112637632042  353815618542  564932238864 1.79e12 1.17e12 8.18e12 9.43e11
## 2  2016 150966297263  761518412131  443734791018 1.29e12 2.96e12 1.01e13 7.24e11
## 3  2017 145904768287  632876665393  581436276874 1.52e12 2.68e12 1.13e13 7.80e11
## 4  2018  43698716269  545285498122 1900250425402 1.89e12 1.56e12 9.98e12 1.73e11
## 5  2019 146510028288 1057439882016  613569051999 1.72e12 1.83e12 1.01e13 1.86e11
## 6  2020 159264353024  655364412679  440475754374 1.49e12 1.75e12 9.83e12 4.28e11

2.1.17 2.1.17. Tóm tắt thống kê tổng quát:

summary(financial_wide)
##       Nam            CPTC                   LCTT              
##  Min.   :2015   Min.   : 43698716269   Min.   :  -2870318244  
##  1st Qu.:2017   1st Qu.:120954416103   1st Qu.: 567183289940  
##  Median :2020   Median :148401242505   Median : 708441412405  
##  Mean   :2020   Mean   :133764595442   Mean   : 879430966658  
##  3rd Qu.:2022   3rd Qu.:153762834318   3rd Qu.:1034284190540  
##  Max.   :2024   Max.   :165690839670   Max.   :2299239828020  
##       LNST                    NDH                     NNH               
##  Min.   : 440475754374   Min.   :1290150502230   Min.   :1169335416920  
##  1st Qu.: 569058248366   1st Qu.:1500914466190   1st Qu.:1766029587820  
##  Median : 667065434087   Median :1757775755440   Median :2008047806480  
##  Mean   :1088377188160   Mean   :1703114238270   Mean   :2143397451210  
##  3rd Qu.:1715511268310   3rd Qu.:1886266430770   3rd Qu.:2572854002320  
##  Max.   :2533934246420   Max.   :2125168858460   Max.   :3210578779510  
##       TCTS                    TVTDT              
##  Min.   : 8179782482930   Min.   : 172567048493  
##  1st Qu.:10017527182100   1st Qu.: 480094457596  
##  Median :10425558980100   Median : 752136078448  
##  Mean   :11483317558900   Mean   :1067107065570  
##  3rd Qu.:12595794059000   3rd Qu.:1259091661410  
##  Max.   :17997853312700   Max.   :3964316764610

2.1.18 2.1.18. Kiểm tra kiểu dữ liệu của từng cột:

sapply(financial_wide, class)
##       Nam      CPTC      LCTT      LNST       NDH       NNH      TCTS     TVTDT 
## "numeric" "numeric" "numeric" "numeric" "numeric" "numeric" "numeric" "numeric"

– Tất cả các cột trong tập dữ liệu đều đang được lưu trữ dưới dạng số.

2.2 2.2. Xử lý dữ liệu thô, mã hóa dữ liệu:

2.2.1 2.2.1. Chuẩn hóa tên cột:

financial_wide <- financial_wide %>% clean_names()

– financial_wide sau khi chạy sẽ vẫn giữ các giá trị trong bảng, nhưng tên cột được chuẩn hóa.

Giải thích:

financial_wide <- financial_wide %>% clean_names(): dùng để chuẩn hóa tên cột của bảng dữ liệu financial_wide.

clean_names(): chuyển các tên cột về dạng dễ đọc và nhất quán hơn, loại bỏ dấu cách, ký tự đặc biệt và đảm bảo các tên là duy nhất, giúp việc xử lý dữ liệu về sau thuận tiện hơn.

2.2.2 2.2.2. Xem lại tên cột sau clean:

colnames(financial_wide)
## [1] "nam"   "cptc"  "lctt"  "lnst"  "ndh"   "nnh"   "tcts"  "tvtdt"

2.2.3 2.2.3. Kiểm tra giá trị bị thiếu:

anyNA(financial_wide)
## [1] FALSE

– Bộ dữ liệu không có giá trị bị thiếu.

2.2.4 2.2.4. Mã hóa biến định tính:

financial_wide$nam <- as.numeric(financial_wide$nam)

– Một thao tác cơ bản trong xử lý dữ liệu với R nhằm chuyển đổi kiểu dữ liệu của một cột.

2.2.5 2.2.5. Xây dựng các biến tài chính:

financial_wide <- financial_wide %>%
  mutate(
    tsr = tcts - nnh - ndh,
    tn = nnh + ndh,
    kntt = tvtdt / nnh,
    roa = lnst / tcts,
    lctt_tts = lctt / tcts,
    cptc_tn = cptc / (nnh + ndh),
    ln_lctt = lnst / lctt
  )

– tsr = tcts - nnh - ndh: biến tsr là tổng tài sản ròng, được tính bằng tổng tài sản (tcts) trừ nợ ngắn hạn (nnh) và nợ dài hạn (ndh).

– tn = nnh + ndh: biến tn là tổng nợ, được tính bằng nợ ngắn hạn cộng nợ dài hạn.

– kntt = tvtdt / nnh: biến kntt là khả năng thanh toán, được tính bằng tài sản ngắn hạn (tvtdt) chia nợ ngắn hạn (nnh).

– roa = lnst / tcts: biến roa là tỷ suất sinh lời trên tài sản.

– lctt_tts = lctt / tcts: biến lctt_tts là tỷ lệ lưu chuyển tiền thuần trên tổng tài sản.

– cptc_tn = cptc / (nnh + ndh): biến cptc_tn là tỷ lệ chi phí tài chính trên tổng nợ, được tính bằng chi phí tài chính (cptc) chia tổng nợ (nnh + ndh).

– ln_lctt = lnst / lctt: biến ln_lctt là tỷ lệ lợi nhuận trên lưu chuyển tiền thuần, được tính bằng lợi nhuận sau thuế (lnst) chia lưu chuyển tiền thuần (lctt).

2.2.6 2.2.6. Chuẩn hóa đơn vị tính:

financial_wide <- financial_wide %>%
  mutate(across(c(cptc, lctt, lnst, ndh, nnh, tcts, tvtdt),
                ~ . / 1e12)) %>%
  mutate(
    tsr = tcts - nnh - ndh,
    tn = nnh + ndh,
    kntt = tvtdt / nnh,
    roa = lnst / tcts,
    lctt_tts = lctt / tcts,
    cptc_tn = cptc / (nnh + ndh),
    ln_lctt = lnst / lctt
  )

– Chuyển đổi đơn vị các biến tài chính từ đồng sang nghìn tỷ đồng, giúp dữ liệu gọn, rõ, dễ phân tích và trực quan hóa hơn.

Giải thích:

. / 1e12: đây là hàm ẩn danh (anonymous function) áp dụng cho từng cột.

Dấu . đại diện cho giá trị gốc của cột.

1e12 nghĩa là 1.000.000.000.000 => Tức là chia toàn bộ giá trị trong các cột này cho 1 nghìn tỷ.

2.2.7 2.2.7. Kiểm tra lại NA lần cuối:

colSums(is.na(financial_wide))
##      nam     cptc     lctt     lnst      ndh      nnh     tcts    tvtdt 
##        0        0        0        0        0        0        0        0 
##      tsr       tn     kntt      roa lctt_tts  cptc_tn  ln_lctt 
##        0        0        0        0        0        0        0

– Không có giá trị nào bị thiếu sau khi kiểm tra.

2.2.8 2.2.8. Loại bỏ dòng trùng lặp:

financial_wide <- financial_wide %>% distinct()

Mục đích:

– Loại bỏ dữ liệu trùng lặp.

– Giữ lại mỗi quan sát duy nhất một lần.

– Đảm bảo độ chính xác và tính gọn gàng của dữ liệu.

Giải thích:

financial_wide: là tên dataframe (bảng dữ liệu) đang làm việc.

%>%: là toán tử pipe của dplyr, giúp chuyển kết quả từ bước trước sang bước sau.

distinct(): là hàm loại bỏ các dòng trùng lặp trong bảng dữ liệu.

2.2.9 2.2.9. Kiểm tra tóm tắt cuối cùng:

summary(financial_wide)
##       nam            cptc             lctt               lnst       
##  Min.   :2015   Min.   :0.0437   Min.   :-0.00287   Min.   :0.4405  
##  1st Qu.:2017   1st Qu.:0.1210   1st Qu.: 0.56718   1st Qu.:0.5691  
##  Median :2020   Median :0.1484   Median : 0.70844   Median :0.6671  
##  Mean   :2020   Mean   :0.1338   Mean   : 0.87943   Mean   :1.0884  
##  3rd Qu.:2022   3rd Qu.:0.1538   3rd Qu.: 1.03428   3rd Qu.:1.7155  
##  Max.   :2024   Max.   :0.1657   Max.   : 2.29924   Max.   :2.5339  
##       ndh             nnh             tcts           tvtdt       
##  Min.   :1.290   Min.   :1.169   Min.   : 8.18   Min.   :0.1726  
##  1st Qu.:1.501   1st Qu.:1.766   1st Qu.:10.02   1st Qu.:0.4801  
##  Median :1.758   Median :2.008   Median :10.43   Median :0.7521  
##  Mean   :1.703   Mean   :2.143   Mean   :11.48   Mean   :1.0671  
##  3rd Qu.:1.886   3rd Qu.:2.573   3rd Qu.:12.60   3rd Qu.:1.2591  
##  Max.   :2.125   Max.   :3.211   Max.   :18.00   Max.   :3.9643  
##       tsr               tn             kntt             roa         
##  Min.   : 5.219   Min.   :2.961   Min.   :0.1015   Min.   :0.04386  
##  1st Qu.: 6.539   1st Qu.:3.479   1st Qu.:0.2448   1st Qu.:0.05378  
##  Median : 6.820   Median :3.750   Median :0.2865   Median :0.06811  
##  Mean   : 7.637   Mean   :3.847   Mean   :0.5162   Mean   :0.09104  
##  3rd Qu.: 7.734   3rd Qu.:4.219   3rd Qu.:0.6825   3rd Qu.:0.10244  
##  Max.   :13.772   Max.   :5.083   Max.   :1.8871   Max.   :0.19033  
##     lctt_tts             cptc_tn           ln_lctt         
##  Min.   :-0.0002119   Min.   :0.01265   Min.   :-882.8060  
##  1st Qu.: 0.0549743   1st Qu.:0.03314   1st Qu.:   0.5808  
##  Median : 0.0709517   Median :0.03554   Median :   0.7095  
##  Mean   : 0.0751293   Mean   :0.03494   Mean   : -87.2459  
##  3rd Qu.: 0.0886391   3rd Qu.:0.03993   3rd Qu.:   1.1746  
##  Max.   : 0.1764486   Max.   :0.04916   Max.   :   3.4849

– Tóm tắt nhanh toàn bộ nội dung của bảng dữ liệu.

2.3 2.3. Các thống kê cơ bản:

2.3.1 2.3.1. Kiểm tra kiểu dữ liệu và cấu trúc:

library(skimr)
## Warning: package 'skimr' was built under R version 4.5.2
str(financial_wide)
## tibble [10 × 15] (S3: tbl_df/tbl/data.frame)
##  $ nam     : num [1:10] 2015 2016 2017 2018 2019 ...
##  $ cptc    : num [1:10] 0.1126 0.151 0.1459 0.0437 0.1465 ...
##  $ lctt    : num [1:10] 0.354 0.762 0.633 0.545 1.057 ...
##  $ lnst    : num [1:10] 0.565 0.444 0.581 1.9 0.614 ...
##  $ ndh     : num [1:10] 1.79 1.29 1.52 1.89 1.72 ...
##  $ nnh     : num [1:10] 1.17 2.96 2.68 1.56 1.83 ...
##  $ tcts    : num [1:10] 8.18 10.12 11.29 9.98 10.12 ...
##  $ tvtdt   : num [1:10] 0.943 0.724 0.78 0.173 0.186 ...
##  $ tsr     : num [1:10] 5.22 5.87 7.09 6.53 6.57 ...
##  $ tn      : num [1:10] 2.96 4.25 4.2 3.46 3.55 ...
##  $ kntt    : num [1:10] 0.807 0.245 0.291 0.11 0.101 ...
##  $ roa     : num [1:10] 0.0691 0.0439 0.0515 0.1903 0.0606 ...
##  $ lctt_tts: num [1:10] 0.0433 0.0753 0.0561 0.0546 0.1045 ...
##  $ cptc_tn : num [1:10] 0.038 0.0355 0.0348 0.0126 0.0412 ...
##  $ ln_lctt : num [1:10] 1.597 0.583 0.919 3.485 0.58 ...

Giải thích:

skim(): cho ta số dòng, trung bình, độ lệch chuẩn, min, max, tỉ lệ NA,… Dùng để đánh giá chất lượng dữ liệu sau xử lý.

2.3.2 2.3.2. Kiếm tra số lượng giá trị bị thiếu:

colSums(is.na(financial_wide))  
##      nam     cptc     lctt     lnst      ndh      nnh     tcts    tvtdt 
##        0        0        0        0        0        0        0        0 
##      tsr       tn     kntt      roa lctt_tts  cptc_tn  ln_lctt 
##        0        0        0        0        0        0        0

– Bộ dữ liệu không có giá trị nào bị thiếu.

2.3.3 2.3.3. Thống kê mô tả cho tất cả các biến:

summary(financial_wide)
##       nam            cptc             lctt               lnst       
##  Min.   :2015   Min.   :0.0437   Min.   :-0.00287   Min.   :0.4405  
##  1st Qu.:2017   1st Qu.:0.1210   1st Qu.: 0.56718   1st Qu.:0.5691  
##  Median :2020   Median :0.1484   Median : 0.70844   Median :0.6671  
##  Mean   :2020   Mean   :0.1338   Mean   : 0.87943   Mean   :1.0884  
##  3rd Qu.:2022   3rd Qu.:0.1538   3rd Qu.: 1.03428   3rd Qu.:1.7155  
##  Max.   :2024   Max.   :0.1657   Max.   : 2.29924   Max.   :2.5339  
##       ndh             nnh             tcts           tvtdt       
##  Min.   :1.290   Min.   :1.169   Min.   : 8.18   Min.   :0.1726  
##  1st Qu.:1.501   1st Qu.:1.766   1st Qu.:10.02   1st Qu.:0.4801  
##  Median :1.758   Median :2.008   Median :10.43   Median :0.7521  
##  Mean   :1.703   Mean   :2.143   Mean   :11.48   Mean   :1.0671  
##  3rd Qu.:1.886   3rd Qu.:2.573   3rd Qu.:12.60   3rd Qu.:1.2591  
##  Max.   :2.125   Max.   :3.211   Max.   :18.00   Max.   :3.9643  
##       tsr               tn             kntt             roa         
##  Min.   : 5.219   Min.   :2.961   Min.   :0.1015   Min.   :0.04386  
##  1st Qu.: 6.539   1st Qu.:3.479   1st Qu.:0.2448   1st Qu.:0.05378  
##  Median : 6.820   Median :3.750   Median :0.2865   Median :0.06811  
##  Mean   : 7.637   Mean   :3.847   Mean   :0.5162   Mean   :0.09104  
##  3rd Qu.: 7.734   3rd Qu.:4.219   3rd Qu.:0.6825   3rd Qu.:0.10244  
##  Max.   :13.772   Max.   :5.083   Max.   :1.8871   Max.   :0.19033  
##     lctt_tts             cptc_tn           ln_lctt         
##  Min.   :-0.0002119   Min.   :0.01265   Min.   :-882.8060  
##  1st Qu.: 0.0549743   1st Qu.:0.03314   1st Qu.:   0.5808  
##  Median : 0.0709517   Median :0.03554   Median :   0.7095  
##  Mean   : 0.0751293   Mean   :0.03494   Mean   : -87.2459  
##  3rd Qu.: 0.0886391   3rd Qu.:0.03993   3rd Qu.:   1.1746  
##  Max.   : 0.1764486   Max.   :0.04916   Max.   :   3.4849

2.3.4 2.3.4. Thống kê riêng biến tổng cộng tài sản:

summary(financial_wide$tcts)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    8.18   10.02   10.43   11.48   12.60   18.00
sd(financial_wide$tcts)
## [1] 2.774758

Nhận xét:

– Giá trị trung bình (Mean) và trung vị (Median) lệch nhau: Giá trị trung bình (Mean = 11.48) lớn hơn đáng kể so với trung vị (Median = 10.43). Điều này cho thấy phân bố của biến tổng cộng tài sản bị lệch phải, tức là có một số giá trị tài sản rất lớn đang kéo giá trị trung bình lên cao.

– Độ phân tán lớn: Độ lệch chuẩn (SD = 2.774758) tương đối lớn so với giá trị trung bình (11.48), chỉ ra rằng dữ liệu có sự phân tán rộng quanh giá trị trung bình.

– Phạm vi rộng: Phạm vi dữ liệu (Range = Max - Min) là 9.82 (18.00 - 8.18).

– Sự tập trung của dữ liệu: 50% các quan sát tập trung trong khoảng từ tứ phân vị thứ nhất (Q1 = 10.02) đến tứ phân vị thứ ba (Q3 = 12.60).

Giải thích:

summary(): tạo bản tóm tắt thống kê cho một biến số.

sd(): tính độ lệch chuẩn (standard deviation) của biến.

2.3.5 2.3.5. Thống kê riêng biến lợi nhuận sau thuế:

mean(financial_wide$lnst)
## [1] 1.088377
median(financial_wide$lnst)
## [1] 0.6670654
range(financial_wide$lnst)
## [1] 0.4404758 2.5339342

Nhận xét:

– Giá trị trung bình (1.09) lớn hơn trung vị (0.67) một cách đáng kể => Điều này cho thấy phân bố của biến lợi nhuận sau thuế bị lệch phải rất mạnh. Có một số ít đơn vị có mức lợi nhuận sau thuế rất cao, kéo giá trị trung bình lên xa so với trung vị.

– Phạm vi (Range): Biến lợi nhuận sau thuế có phạm vi rộng, từ mức tối thiểu 0.44 đến tối đa 2.53 => Phần lớn các đơn vị có lợi nhuận tập trung quanh mức trung vị thấp (0.67), trong khi một số ít đơn vị đạt được lợi nhuận cao hơn nhiều, dẫn đến sự chênh lệch lớn giữa Mean và Median.

Giải thích:

mean(): trung bình

sd(): tính độ lệch chuẩn

var(): tính phương sai

2.3.6 2.3.6. Kiểm tra độ phân tán biến chi phí tài chính:

var(financial_wide$cptc)
## [1] 0.001356584
sd(financial_wide$cptc)
## [1] 0.03683184

Nhận xét:

– Cả phương sai (rất gần 0) và độ lệch chuẩn (0.037) đều là những giá trị rất nhỏ.

=> Điều này chỉ ra rằng các giá trị của biến chi phí tài chính (cptc) phân tán rất ít quanh giá trị trung bình của nó.

2.3.7 2.3.7. Thống kê mô tả biến lưu chuyển tiền thuần:

summary(financial_wide$lctt)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
## -0.00287  0.56718  0.70844  0.87943  1.03428  2.29924

Nhận xét:

– Giá trị Trung bình (Mean = 0.879) lớn hơn Trung vị (Median = 0.708) => Điều này cho thấy phân bố của biến lưu chuyển tiền thuần (lctt) bị lệch phải. Có một số đơn vị có mức lưu chuyển tiền thuần rất cao, kéo giá trị trung bình lên cao hơn so với trung vị của dữ liệu.

– Giá trị tối thiểu (Min) là -0.00287 => Sự tồn tại của giá trị âm cho thấy có một số đơn vị có lưu chuyển tiền thuần âm (dòng tiền ra lớn hơn dòng tiền vào), mặc dù giá trị này rất gần 0.

– Biến lưu chuyển tiền thuần có xu hướng lệch phải, với đa số các đơn vị có dòng tiền dương nhưng một số ít đơn vị đạt được mức dòng tiền rất cao.

2.3.8 2.3.8. Thống kê mô tả 2 biến nợ ngắn hạn và nợ dài hạn:

summary(financial_wide[, c("nnh", "ndh")])
##       nnh             ndh       
##  Min.   :1.169   Min.   :1.290  
##  1st Qu.:1.766   1st Qu.:1.501  
##  Median :2.008   Median :1.758  
##  Mean   :2.143   Mean   :1.703  
##  3rd Qu.:2.573   3rd Qu.:1.886  
##  Max.   :3.211   Max.   :2.125

Nhận xét:

– Nợ ngắn hạn (nnh) cao hơn: Các giá trị thống kê của nnh đều cao hơn so với ndh ở mọi chỉ số (Median, Mean, Max).

– Trung bình (nnh Mean = 2.143) cao hơn đáng kể so với trung bình (ndh Mean = 1.703).

– Giá trị tối đa (nnh Max = 3.211) cao hơn giá trị tối đa (ndh Max = 2.125).

=> Điều này cho thấy các đơn vị được khảo sát có xu hướng gánh nợ ngắn hạn nhiều hơn nợ dài hạn.

– Đối với nnh: Mean (2.143) > Median (2.008).

– Đối với ndh: Mean (1.703) < Median (1.758).

=> Biến nợ ngắn hạn (nnh) bị lệch phải, cho thấy có một số ít đơn vị có mức nợ ngắn hạn rất cao. Biến nợ dài hạn (ndh) gần như đối xứng hoặc hơi lệch trái nhẹ (do Median > Mean), nhưng sự khác biệt không đáng kể.

– Nợ ngắn hạn phân tán rộng hơn: Khoảng phân vị giữa (IQR = Q3 - Q1) của nnh (2.573 - 1.766 = 0.807) lớn hơn của ndh (1.886 - 1.501 = 0.385) .

=> Điều này chỉ ra rằng mức nợ ngắn hạn có sự khác biệt lớn hơn giữa các đơn vị so với mức nợ dài hạn.

2.3.9 2.3.9. Tính giá trị trung bình của từng biến:

sapply(financial_wide[, c("cptc", "lctt", "lnst", "ndh", "nnh", "tcts", "tvtdt")], mean)
##       cptc       lctt       lnst        ndh        nnh       tcts      tvtdt 
##  0.1337646  0.8794310  1.0883772  1.7031142  2.1433975 11.4833176  1.0671071

Nhận xét:

– tcts (Tổng cộng tài sản): 11.4833176 là giá trị trung bình cao nhất, phản ánh quy mô tài sản lớn nhất.

– Giá trị trung bình ở mức trung bình:

  • nnh (Nợ ngắn hạn): 2.1433975

  • ndh (Nợ dài hạn): 1.7031142

  • lnst (Lợi nhuận sau thuế): 1.088377

  • tvtdt (Tài sản vô hình và tài sản dài hạn): 1.0671071

  • lctt (Lưu chuyển tiền thuần): 0.8794310

– cptc (Chi phí tài chính): 0.1337646 là giá trị trung bình thấp nhất, phù hợp với nhận xét trước đó về độ phân tán rất nhỏ của biến này.

– Mức nợ ngắn hạn (nnh = 2.14) trung bình cao hơn đáng kể so với mức nợ dài hạn (ndh = 1.70).

– Trung bình lợi nhuận sau thuế (lnst = 1.09) cao hơn trung bình lưu chuyển tiền thuần (lctt = 0.88).

2.3.10 2.3.10. Tính hệ số biến thiên (CV = sd/mean) để đo mức dao động:

sapply(financial_wide[, c("cptc", "lctt", "lnst", "ndh", "nnh", "tcts", "tvtdt")],
       function(x) sd(x)/mean(x))
##      cptc      lctt      lnst       ndh       nnh      tcts     tvtdt 
## 0.2753482 0.7354831 0.6969639 0.1533387 0.2992067 0.2416338 1.0375251

Nhận xét:

– Kết quả cho thấy biến động tiền và tương đương tiền (tvtdt), lưu chuyển tiền tệ (lctt) và lợi nhuận sau thuế (lnst) là những chỉ số bất ổn nhất, trong khi cấu trúc nợ (đặc biệt là nợ dài hạn) là ổn định nhất trong bộ dữ liệu.

Giải thích:

sapply(): áp dụng một hàm con (ở đây là function(x) sd(x)/mean(x)) lần lượt cho từng cột trong các cột đã chọn.

sd(x): tính độ lệch chuẩn của cột x, cho biết mức độ biến động dữ liệu.

2.3.11 2.3.11. Thống kê mô tả theo từng năm:

financial_wide %>%
  group_by(nam) %>%
  summarise(across(c(cptc, lctt, lnst, ndh, nnh, tcts, tvtdt), mean))
## # A tibble: 10 × 8
##      nam   cptc     lctt  lnst   ndh   nnh  tcts tvtdt
##    <dbl>  <dbl>    <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
##  1  2015 0.113   0.354   0.565  1.79  1.17  8.18 0.943
##  2  2016 0.151   0.762   0.444  1.29  2.96 10.1  0.724
##  3  2017 0.146   0.633   0.581  1.52  2.68 11.3  0.780
##  4  2018 0.0437  0.545   1.90   1.89  1.56  9.98 0.173
##  5  2019 0.147   1.06    0.614  1.72  1.83 10.1  0.186
##  6  2020 0.159   0.655   0.440  1.49  1.75  9.83 0.428
##  7  2021 0.108   0.965   0.721  1.42  2.26 10.7  0.637
##  8  2022 0.166   2.30    1.16   1.87  3.21 13.0  1.36 
##  9  2023 0.155  -0.00287 2.53   1.90  1.92 13.5  1.47 
## 10  2024 0.150   1.53    1.92   2.13  2.10 18.0  3.96

2.3.12 2.3.12. Tính hệ số tương quan giữa các biến tài chính:

cor(financial_wide[, c("cptc", "lctt", "lnst", "ndh", "nnh", "tcts", "tvtdt")])
##             cptc        lctt        lnst        ndh        nnh      tcts
## cptc   1.0000000  0.31759350 -0.18297496 -0.1096537  0.4921861 0.3687987
## lctt   0.3175935  1.00000000 -0.08992542  0.2318695  0.5919432 0.4397241
## lnst  -0.1829750 -0.08992542  1.00000000  0.7516008 -0.1556587 0.6343854
## ndh   -0.1096537  0.23186952  0.75160078  1.0000000 -0.3108658 0.6079920
## nnh    0.4921861  0.59194320 -0.15565872 -0.3108658  1.0000000 0.2976216
## tcts   0.3687987  0.43972415  0.63438540  0.6079920  0.2976216 1.0000000
## tvtdt  0.3356372  0.36703870  0.49245788  0.6156292  0.1232809 0.9049609
##           tvtdt
## cptc  0.3356372
## lctt  0.3670387
## lnst  0.4924579
## ndh   0.6156292
## nnh   0.1232809
## tcts  0.9049609
## tvtdt 1.0000000

– Ma trận tương quan cho thấy các biến liên quan đến quy mô tài sản (tcts, tvtdt) và cấu trúc nợ (ndh) có mối quan hệ tuyến tính mạnh mẽ với nhau và với hiệu suất (lnst). Ngược lại, mối quan hệ giữa dòng tiền (lctt) và lợi nhuận (lnst) là rất yếu.

2.3.13 2.3.13. Tổng từng biến qua các năm:

financial_wide %>%
  summarise(across(c(cptc, lctt, lnst, ndh, nnh, tcts, tvtdt), sum))
## # A tibble: 1 × 7
##    cptc  lctt  lnst   ndh   nnh  tcts tvtdt
##   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1  1.34  8.79  10.9  17.0  21.4  115.  10.7

2.3.14 2.3.14. Năm có tổng cộng tài sản thấp nhất:

financial_wide %>%
  filter(tcts == min(tcts))
## # A tibble: 1 × 15
##     nam  cptc  lctt  lnst   ndh   nnh  tcts tvtdt   tsr    tn  kntt    roa
##   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>  <dbl>
## 1  2015 0.113 0.354 0.565  1.79  1.17  8.18 0.943  5.22  2.96 0.807 0.0691
## # ℹ 3 more variables: lctt_tts <dbl>, cptc_tn <dbl>, ln_lctt <dbl>

– Năm có tổng cộng tài sản thấp nhất là năm 2015, đạt khoảng 8.179782 tỷ đồng.

Giải thích:

tcts == min(tcts): điều kiện chọn dòng có tổng cộng tài sản (tcts) bằng giá trị tổn cộng tài sản lớn nhất trong toàn bộ bảng.

2.3.15 2.3.15. So sánh trung bình lợi nhuận sau thuế và lưu chuyển tiền tệ:

mean(financial_wide$lnst)
## [1] 1.088377
mean(financial_wide$lctt)
## [1] 0.879431

Nhận xét:

– Giá trị trung bình của lợi nhuận sau thuế (1.09) cao hơn đáng kể so với giá trị trung bình của lưu chuyển tiền tệ (0.88) => Sự chênh lệch này cho thấy trong kỳ kế toán, các đơn vị có xu hướng ghi nhận lợi nhuận cao hơn số tiền mặt thực tế họ luân chuyển hoặc thu được.

2.3.16 2.3.16. Tính tỷ lệ lợi nhuận trên tổng cộng tài sản (ROA):

financial_wide$roa <- financial_wide$lnst / financial_wide$tcts
summary(financial_wide$roa)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
## 0.04386 0.05378 0.06811 0.09104 0.10244 0.19033

Nhận xét:

– ROA trung bình (Mean) là 9.10%. Điều này cho thấy, trung bình, cứ mỗi 100 đơn vị tài sản được sử dụng sẽ tạo ra 9.10 đơn vị lợi nhuận sau thuế.

– Giá trị trung bình (Mean = 0.091) lớn hơn đáng kể so với trung vị (Median = 0.068) => Điều này cho thấy phân bố của ROA bị lệch phải. Có một số ít đơn vị đạt được tỷ suất sinh lời trên tài sản rất cao (kéo giá trị trung bình lên), thể hiện qua giá trị Tối đa là 19.03%.

– 50% dữ liệu ROA tập trung trong khoảng từ tứ phân vị thứ nhất (Q1 = 5.38%) đến tứ phân vị thứ ba (Q3 = 10.24%). Đây là phạm vi sinh lời phổ biến nhất.

Giải thích:

financial_wide$roa: được tính bằng lợi nhuận sau thuế (lnst) chia cho tổng tài sản (tcts). Đây là chỉ tiêu ROA (Return on Assets) - dùng để đo lường hiệu quả sử dụng tài sản của doanh nghiệp.

2.3.17 2.3.17. Kiểm tra phân phối chuẩn của ROA:

shapiro.test(financial_wide$roa)
## 
##  Shapiro-Wilk normality test
## 
## data:  financial_wide$roa
## W = 0.78554, p-value = 0.009671

Nhận xét:

– Giả thuyết H₀ (null hypothesis): dữ liệu có phân phối chuẩn.

– Giả thuyết H₁ (alternative): dữ liệu không phân phối chuẩn.

Vì: p-value < 0.05 => Ta bác bỏ H₀ => Biến ROA không tuân theo phân phối chuẩn.

Giải thích:

shapiro.test(): dùng để kiểm định tính phân phối chuẩn (normality test) trong R.

financial_wide$roa: là biến cần kiểm định.

2.3.18 2.3.18. Tính tài sản ròng và thống kê mô tả cho biến đó:

financial_wide$tsr <- financial_wide$tcts - financial_wide$nnh - financial_wide$ndh
summary(financial_wide$tsr)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   5.219   6.539   6.820   7.637   7.734  13.772

Nhận xét:

– Giá trị trung bình (Mean = 7.637) lớn hơn đáng kể so với trung vị (Median = 6.820) => Điều này cho thấy phân bố của biến tài sản ròng (tsr) bị lệch phải. Sự tồn tại của các đơn vị có mức tài sản ròng rất cao (được thể hiện qua giá trị Max = 13.772) đã kéo giá trị trung bình lên cao hơn trung vị.

– Giá trị tối đa (13.772) cao gấp hơn hai lần giá trị trung vị (6.820), cho thấy có sự khác biệt lớn về quy mô tài sản ròng giữa các đơn vị.

– 50% các đơn vị có tài sản ròng tập trung trong khoảng từ tứ phân vị thứ nhất (Q1 = 6.539) đến tứ phân vị thứ ba (Q3 = 7.734), cho thấy phần lớn dữ liệu tập trung ở mức thấp và trung bình.

2.3.19 2.3.19. Kiểm định phân phối chuẩn cho biến lợi nhuận sau thuế:

shapiro.test(financial_wide$lnst)
## 
##  Shapiro-Wilk normality test
## 
## data:  financial_wide$lnst
## W = 0.81117, p-value = 0.01981

Nhận xét:

– Giả thuyết H₀ (null hypothesis): dữ liệu có phân phối chuẩn.

– Giả thuyết H₁ (alternative): dữ liệu không phân phối chuẩn.

Vì: p-value < 0.05 => Ta bác bỏ H₀ => Biến LNST không tuân theo phân phối chuẩn.

2.3.20 2.3.20. Thống kê mô tả tổng hợp cho tất cả các biến:

library(skimr)
skim(financial_wide[, c("cptc", "lctt", "lnst", "ndh", "nnh", "tcts", "tvtdt")])
Data summary
Name …[]
Number of rows 10
Number of columns 7
_______________________
Column type frequency:
numeric 7
________________________
Group variables None

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
cptc 0 1 0.13 0.04 0.04 0.12 0.15 0.15 0.17 ▁▁▂▁▇
lctt 0 1 0.88 0.65 0.00 0.57 0.71 1.03 2.30 ▃▇▃▂▂
lnst 0 1 1.09 0.76 0.44 0.57 0.67 1.72 2.53 ▇▁▁▂▁
ndh 0 1 1.70 0.26 1.29 1.50 1.76 1.89 2.13 ▃▃▂▇▂
nnh 0 1 2.14 0.64 1.17 1.77 2.01 2.57 3.21 ▅▇▅▂▅
tcts 0 1 11.48 2.77 8.18 10.02 10.43 12.60 18.00 ▇▃▃▁▂
tvtdt 0 1 1.07 1.11 0.17 0.48 0.75 1.26 3.96 ▇▃▁▁▁

Mục đích:

– So sánh mức độ biến động giữa các chỉ tiêu tài chính.

– Phát hiện dữ liệu bị thiếu (NA).

– Quan sát sơ bộ hình dạng phân phối của từng biến.

2.4 2.4. Trực quan hóa dữ liệu:

2.4.1 2.4.1. Biểu đồ cột LNST qua các năm:

library(ggplot2)
ggplot(financial_wide, aes(x = nam, y = lnst)) +
  geom_col(fill = "pink", alpha = 1) +
  geom_text(aes(label = round(lnst, 1)), vjust = -0.3, size = 3) +
  geom_smooth(aes(x = as.numeric(nam), y = lnst), method = "lm", se = FALSE, color = "red") +
  labs(title = "Biểu đồ cột LNST qua các năm", 
       x = "Năm", 
       y = "LNST (tỷ đồng)") +
  theme_minimal() +
  theme(plot.title = element_text(face = "bold", color = "darkgreen"))
## `geom_smooth()` using formula = 'y ~ x'

Nhận xét:

– Đường xu hướng màu đỏ cho thấy LNST có xu hướng tăng dần qua các năm, đặc biệt trong giai đoạn cuối của chuỗi thời gian. Điều này phản ánh hiệu quả kinh doanh nhìn chung được cải thiện.

– Mức LNST dao động rất mạnh giữa các năm, cho thấy sự bất ổn lớn trong kết quả kinh doanh:

  • Mức thấp: LNST giảm mạnh trong các năm 2016 (0.4 tỷ) và 2020 (0.4 tỷ).

  • Mức cao đột biến: Có hai đỉnh lợi nhuận cao nhất vào năm 2018 (1.9 tỷ) và đặc biệt là năm 2023 (2.5 tỷ), cho thấy sự tăng trưởng đột phá trong hai năm này.

– Sự tăng trưởng LNST trở nên rõ rệt và mạnh mẽ hơn trong giai đoạn từ năm 2021 đến 2023 (từ 0.7 tỷ lên 2.5 tỷ), trước khi giảm nhẹ vào năm 2024 (1.9 tỷ).

=> Lợi nhuận sau thuế có tính chu kỳ hoặc biến động mạnh nhưng duy trì một xu hướng tăng trưởng tích cực trong dài hạn.

Giải thích:

ggplot(financial_wide, aes(x = nam, y = lnst)): khởi tạo biểu đồ từ bộ dữ liệu financial_wide. Dùng aes(x = nam, y = lnst) để quy định trục hoành là năm (nam) và trục tung là lợi nhuận sau thuế (lnst).

geom_col(fill = “pink”, alpha = 1): vẽ biểu đồ cột (column chart) thể hiện giá trị LNST qua từng năm. fill = “pink”: tô màu cột là hồng. alpha = 1: độ trong suốt bằng 1 → hiển thị màu rõ nét hoàn toàn.

geom_text(aes(label = round(lnst, 1)), vjust = -0.3, size = 3): thêm nhãn số liệu trên đầu mỗi cột. label = round(lnst, 1): hiển thị giá trị LNST, làm tròn 1 chữ số thập phân. vjust = -0.3: điều chỉnh vị trí nhãn nằm trên đỉnh cột. size = 3: cỡ chữ của nhãn.

geom_smooth(aes(x = as.numeric(nam), y = lnst), method = “lm”, se = FALSE, color = “red”): thêm đường xu hướng (trend line) dựa trên mô hình hồi quy tuyến tính (lm). se = FALSE: không hiển thị khoảng tin cậy. color = “red”: đường xu hướng có màu đỏ. as.numeric(nam): đảm bảo biến năm được hiểu là số, giúp hồi quy chính xác.

2.4.2 2.4.2. Biểu đồ đường của LCTT từ hoạt động kinh doanh:

library(ggplot2)
ggplot(financial_wide, aes(x = nam, y = lctt)) +
  geom_area(fill = "skyblue", alpha = 0.4) +
  geom_line(color = "blue", size = 1) +
  geom_point(size = 3, color = "darkblue") +
  geom_text(aes(label = round(lctt, 1)), vjust = -0.4, size = 3) +
  labs(title = "LCTT từ hoạt động kinh doanh", x = "Năm", y = "LCTT (tỷ đồng)") +
  theme_minimal()

Nhận xét:

– Lưu chuyển tiền tệ có tính chu kỳ và dao động cực kỳ mạnh qua các năm, với nhiều đỉnh và đáy rõ rệt. Mức độ bất ổn của dòng tiền cao.

– Nhìn chung, không có xu hướng tăng trưởng rõ ràng trong dài hạn, mà là sự dao động mạnh quanh mức trung bình thấp.

– Biến động:

  • Đỉnh cao nhất (2022): LCTT đạt mức cao nhất đột biến là 2.3 tỷ đồng vào khoảng năm 2022.

  • Đáy thấp nhất (2023): LCTT giảm sâu nhất và chạm mức gần 0 (hoặc rất gần 0) vào khoảng năm 2023. Sự sụt giảm đột ngột từ đỉnh 2.3 tỷ xuống gần 0 cho thấy một vấn đề nghiêm trọng hoặc thay đổi lớn trong quản lý dòng tiền tại thời điểm đó.

  • Phục hồi (2024): LCTT phục hồi mạnh mẽ trở lại mức 1.5 tỷ đồng vào khoảng năm 2024.

=> LCTT từ hoạt động kinh doanh cho thấy sự không ổn định đáng kể, với mức độ rủi ro dòng tiền cao do sự khác biệt lớn giữa đỉnh và đáy trong thời gian ngắn.

Giải thích:

ggplot(financial_wide, aes(x = nam, y = lctt)): khởi tạo biểu đồ từ bộ dữ liệu financial_wide. aes(x = nam, y = lctt) quy định: Trục hoành (x): năm (nam). Trục tung (y): lưu chuyển tiền tệ từ hoạt động kinh doanh (lctt).

geom_area(fill = “skyblue”, alpha = 0.4): vẽ biểu đồ vùng (area chart) để thể hiện mức độ biến động theo thời gian. fill = “skyblue”: tô vùng màu xanh da trời nhạt. alpha = 0.4: làm vùng trong mờ 40%, giúp dễ nhìn hơn.

geom_line(color = “blue”, size = 1): thêm đường nối các điểm dữ liệu để thấy rõ xu hướng. color = “blue”: màu đường là xanh dương. size = 1: độ dày đường.

geom_point(size = 3, color = “darkblue”): vẽ các điểm dữ liệu (marker) tại từng năm. size = 3: kích thước điểm. color = “darkblue”: màu xanh đậm.

geom_text(aes(label = round(lctt, 1)), vjust = -0.4, size = 3): thêm nhãn giá trị trên từng điểm dữ liệu. label = round(lctt, 1): hiển thị giá trị LCTT, làm tròn 1 chữ số thập phân. vjust = -0.4: điều chỉnh vị trí chữ cao hơn một chút so với điểm. size = 3: cỡ chữ.

2.4.3 2.4.3. Biểu đồ đường so sánh NHH và NDH:

library(ggplot2)

ggplot(financial_wide, aes(x = nam)) +
  geom_line(aes(y = ndh, color = "Nợ dài hạn"), size = 1.2) +
  geom_line(aes(y = nnh, color = "Nợ ngắn hạn"), size = 1.2) +
  geom_point(aes(y = ndh, color = "Nợ dài hạn"), size = 3) +
  geom_point(aes(y = nnh, color = "Nợ ngắn hạn"), size = 3) +
  labs(
    title = "So sánh NDH và NNH",
    x = "Năm",
    y = "Giá trị (tỷ đồng)",
    color = "Loại nợ"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", color = "darkblue", size = 13),
    legend.position = "top"
  )

Nhận xét:

– Nợ ngắn Hạn (NNH) biến động mạnh: đường nợ ngắn hạn (màu xanh ngọc) có mức độ dao động (rủi ro) cao hơn hẳn so với nợ dài hạn.

  • NNH có sự tăng trưởng và sụt giảm đột ngột (từ 2.0 tỷ lên đỉnh 3.2 tỷ vào khoảng năm 2022, sau đó giảm về 2.0 tỷ).

  • Mức nợ ngắn hạn luôn cao hơn nợ dài hạn trong hầu hết các năm (trừ giai đoạn 2017-2019).

– Nợ dài Hạn (NDH) Ổn định hơn: Đường Nợ dài hạn (màu đỏ) có xu hướng ổn định hơn và ít có các đỉnh đột biến. NDH duy trì ở mức trung bình quanh 1.5 - 1.9 tỷ, với sự sụt giảm nhẹ vào khoảng năm 2016 và mức phục hồi ổn định sau đó.

– Trong giai đoạn này, có lúc NDH có giá trị cao hơn NNH (khoảng năm 2018).

– Nợ ngắn hạn (NNH) đạt đỉnh cao nhất, sau đó cả hai loại nợ đều có xu hướng tăng dần và gặp nhau ở mức xấp xỉ 2.1 tỷ vào cuối chuỗi thời gian, cho thấy sự cân bằng trở lại trong cấu trúc nợ.

=> Cấu trúc nợ cho thấy sự ưu tiên và bất ổn cao ở Nợ ngắn hạn, mặc dù Nợ dài hạn duy trì sự ổn định hơn.

Giải thích:

geom_line(aes(y = ndh, color = “Nợ dài hạn”), size = 1.2): vẽ đường biểu diễn nợ dài hạn (ndh) qua các năm. y = ndh: trục tung thể hiện giá trị nợ dài hạn. color = “Nợ dài hạn”: gán màu riêng cho đường này và tạo chú giải (legend). size = 1.2: độ dày của đường kẻ.

geom_line(aes(y = nnh, color = “Nợ ngắn hạn”), size = 1.2): vẽ đường biểu diễn nợ ngắn hạn (nnh) qua các năm. color = “Nợ ngắn hạn” giúp tự động thêm màu khác trong chú giải để phân biệt với NDH.

geom_point(aes(y = ndh, color = “Nợ dài hạn”), size = 3) + geom_point(aes(y = nnh, color = “Nợ ngắn hạn”), size = 3): thêm các điểm dữ liệu (marker) cho từng năm trên hai đường, giúp người xem dễ xác định giá trị tại từng thời điểm. size = 3: kích thước điểm vừa phải, cân đối với đường.

2.4.4 2.4.4. Ma trận tương quan của các chỉ số tài chính:

library(dplyr)
library(corrplot)
## Warning: package 'corrplot' was built under R version 4.5.2
## corrplot 0.95 loaded
cor_matrix <- financial_wide %>%
  select(cptc, lctt, lnst, ndh, nnh, tcts, tvtdt) %>%
  cor(use = "complete.obs")

corrplot(
  cor_matrix,
  method = "color",
  col = colorRampPalette(c("#6D9EC1", "white", "#E46726"))(200),
  addCoef.col = "black",
  tl.col = "black",
  tl.srt = 45,
  number.cex = 0.9,
  title = "Ma trận tương quan",
  mar = c(0, 0, 2, 0),
  cl.cex = 0.8,
  diag = TRUE
)

Nhận xét:

– Kết quả cho thấy các biến quy mô tài sản và nợ dài hạn có quan hệ tuyến tính mạnh mẽ với hiệu suất (lnst), trong khi dòng tiền (lctt) gần như độc lập với lợi nhuận sau thuế.

Giải thích:

cor_matrix: dữ liệu đầu vào là ma trận tương quan vừa tính.

method = “color”: dùng ô màu để biểu diễn mức độ tương quan.

Màu đỏ/cam: tương quan dương mạnh. Màu xanh: tương quan âm mạnh. Màu trắng: tương quan yếu.

col = colorRampPalette(c(“#6D9EC1”, “white”, “#E46726”))(200): tạo thang màu chuyển dần từ xanh => trắng => cam, với 200 cấp độ màu. Giúp biểu đồ nhìn mượt và dễ phân biệt hơn.

addCoef.col = “black”: hiển thị giá trị hệ số tương quan (r) ngay trong từng ô bằng màu đen.

tl.col = “black”: đặt màu nhãn tên biến là màu đen.

tl.srt = 45: xoay nhãn tên biến 45 độ cho dễ đọc khi chúng dài.

number.cex = 0.9: điều chỉnh kích cỡ chữ của số tương quan.

title = “Ma trận tương quan”: tiêu đề của biểu đồ.

mar = c(0, 0, 2, 0): tùy chỉnh khoảng cách lề quanh biểu đồ (trên, dưới, trái, phải).

cl.cex = 0.8: điều chỉnh kích cỡ chữ của thang màu (color legend) bên phải.

diag = TRUE: giữ lại đường chéo chính, nơi tương quan giữa biến với chính nó bằng 1.

2.4.5 2.4.5. Biểu đồ phân phối mật độ của LNST:

library(ggplot2)

ggplot(financial_wide, aes(x = lnst)) +
  geom_density(fill = "skyblue", alpha = 0.6, color = "darkblue") +
  geom_vline(aes(xintercept = mean(lnst, na.rm = TRUE)),
             color = "red", linetype = "dashed", size = 1) +
  labs(
    title = "Phân phối mật độ LNST",
    x = "LNST (tỷ đồng)",
    y = "Mật độ"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", color = "darkblue", size = 13)
  )

Nhận xét:

– Biểu đồ thể hiện sự lệch phải rất rõ rệt. Phần lớn mật độ tập trung ở phía bên trái (mức LNST thấp) và đường cong kéo dài dần về phía bên phải (mức LNST cao).

  • Mật độ cao nhất (đỉnh) nằm ở khoảng 0.6 tỷ đồng. Đây là mức lợi nhuận sau thuế phổ biến nhất trong tập dữ liệu.

  • Mật độ giảm dần từ đỉnh 0.6 tỷ đồng và kéo dài đến khoảng 2.5 tỷ đồng. Tuy nhiên, sau mốc khoảng 1.0 tỷ đồng (đường gạch ngang đỏ), mật độ giảm xuống rất nhiều, cho thấy số lượng đơn vị đạt lợi nhuận trên 1.0 tỷ đồng là tương đối ít.

=> Phân phối LNST bị lệch phải mạnh, cho thấy sự phân hóa lớn về lợi nhuận, với đa số các đơn vị đạt lợi nhuận thấp và một số ít đạt lợi nhuận vượt trội.

Giải thích:

ggplot(financial_wide, aes(x = lnst)): khởi tạo biểu đồ với bộ dữ liệu financial_wide. Trục hoành (x) là biến lnst (lợi nhuận sau thuế).

geom_density(fill = “skyblue”, alpha = 0.6, color = “darkblue”): vẽ biểu đồ mật độ (density plot) của biến lợi nhuận sau thuế. fill = “skyblue”: tô màu vùng bên dưới đường mật độ bằng màu xanh nhạt. alpha = 0.6: độ trong suốt 60%, giúp biểu đồ nhìn nhẹ nhàng hơn. color = “darkblue”: viền của đường mật độ có màu xanh đậm.

geom_vline(aes(xintercept = mean(lnst, na.rm = TRUE)), color = “red”, linetype = “dashed”, size = 1): vẽ đường thẳng đứng tại vị trí giá trị trung bình của LNST. xintercept = mean(lnst, na.rm = TRUE): lấy trung bình của LNST (bỏ qua giá trị thiếu). color = “red”: đường màu đỏ để dễ nhận biết. linetype = “dashed”: kiểu gạch đứt. size = 1: độ dày đường.

labs(title = “Phân phối mật độ LNST”, x = “LNST (tỷ đồng)”, y = “Mật độ”): đặt tiêu đề và nhãn cho biểu đồ: title: tên biểu đồ. x: tên trục hoành. y: tên trục tung.

theme_minimal(): áp dụng giao diện tối giản, loại bỏ đường nền, giúp biểu đồ sáng sủa và chuyên nghiệp hơn.

theme(plot.title = element_text(face = “bold”, color = “darkblue”, size = 13)): tùy chỉnh tiêu đề biểu đồ. face = “bold”: chữ in đậm. color = “darkblue”: màu xanh đậm. size = 13: cỡ chữ 13.

2.4.6 2.4.6. Biểu đồ cột so sánh NHH và NDH theo năm:

financial_long1 <- financial_wide %>%
  select(nam, nnh, ndh) %>%
  pivot_longer(cols = c(nnh, ndh), names_to = "Loai_no", values_to = "Gia_tri")

library(ggplot2)

ggplot(financial_long1, aes(x = nam, y = Gia_tri, fill = Loai_no)) +
  geom_col(position = "dodge", alpha = 0.8) +
  geom_text(aes(label = round(Gia_tri, 1)),
            position = position_dodge(width = 0.9), vjust = -0.3, size = 3) +
  geom_line(aes(y = Gia_tri, color = Loai_no, group = Loai_no),
            size = 1, linetype = "dotted") +
  labs(title = "So sánh NNH và NDH theo năm",
       x = "Năm",
       y = "Giá trị (tỷ đồng)",
       fill = "Loại nợ",
       color = "Loại nợ") +
  theme_minimal() +
  theme(plot.title = element_text(face = "bold", color = "darkblue", size = 13))

Nhận xét:

– Nợ ngắn hạn (NNH) có mức độ biến động cao hơn hẳn nợ dài hạn (NDH), thể hiện qua các đỉnh và đáy rõ rệt (đạt đỉnh 3.2 tỷ vào khoảng 2022.5). Trong hầu hết các năm, NNH có giá trị cao hơn NDH, đặc biệt là trong giai đoạn 2016-2017 và sau năm 2021.

– Nợ dài hạn (NDH) duy trì sự ổn định tương đối, chủ yếu dao động trong khoảng 1.5 tỷ đến 1.9 tỷ qua các năm, với mức thay đổi qua từng năm nhỏ hơn NNH.

– Các Giai đoạn cân bằng:

  • 2019-2020: Hai loại nợ có giá trị gần như tương đương nhau (khoảng 1.7 - 1.8 tỷ).

  • 2023-2024: Hai loại nợ có xu hướng cân bằng lại ở mức khoảng 2.1 tỷ, cho thấy sự điều chỉnh trong cấu trúc nợ sau giai đoạn NNH đạt đỉnh.

Giải thích:

select(nam, nnh, ndh): chọn ba cột gồm năm, nợ ngắn hạn (nnh), và nợ dài hạn (ndh) từ dữ liệu financial_wide.

pivot_longer(…): chuyển dữ liệu từ dạng rộng (wide) sang dạng dài (long) để ggplot dễ vẽ hơn.

cols = c(nnh, ndh): chọn hai cột cần gom lại.

names_to = “Loai_no”: tên mới của cột chứa loại nợ (NNH hoặc NDH).

values_to = “Gia_tri”: tên mới của cột chứa giá trị của từng loại nợ.

=> Cấu trúc nợ của các đơn vị cho thấy sự ưu tiên và bất ổn cao hơn ở Nợ ngắn hạn, trong khi nợ dài hạn đóng vai trò ổn định trong cơ cấu vốn.

2.4.7 2.4.7. Biểu đồ đường của TCTS và TN qua các năm:

library(ggplot2)

financial_wide$tn <- financial_wide$nnh + financial_wide$ndh

ggplot(financial_wide, aes(x = nam)) +
  geom_line(aes(y = tcts, color = "Tổng cộng tài sản"), size = 1.2) +
  geom_line(aes(y = tn, color = "Tổng nợ"), size = 1.2, linetype = "dashed") +
  geom_point(aes(y = tcts, color = "Tổng cộng tài sản"), size = 3) +
  geom_point(aes(y = tn, color = "Tổng nợ"), size = 3) +
  labs(title = "TCTS và TN qua các năm",
       x = "Năm", y = "Giá trị (tỷ đồng)", color = "Chỉ tiêu") +
  theme_minimal() +
  theme(plot.title = element_text(face = "bold", color = "darkred", size = 13))

Nhận xét:

– Tổng cộng tài sản (TCTS - đường đỏ) luôn có giá trị cao hơn đáng kể so với tổng nợ trong suốt chuỗi thời gian.

– TCTS thể hiện xu hướng tăng trưởng ổn định và mạnh mẽ trong dài hạn, đặc biệt là trong giai đoạn từ năm 2021 đến cuối chu kỳ, đạt mức cao nhất (trên 15 tỷ đồng).

– Tổng nợ (TN - đường xanh lam) duy trì ở mức ổn định thấp, chủ yếu dao động trong khoảng 3 tỷ đến 5 tỷ đồng.

– TN có một đợt tăng nhẹ vào năm 2022, nhưng sau đó giảm xuống và duy trì ổn định.

=> Sự chênh lệch lớn và liên tục giữa TCTS và TN cho thấy các đơn vị duy trì tỷ lệ vốn chủ sở hữu cao (Tài sản Ròng lớn). Việc TCTS tăng trưởng mạnh mẽ trong khi TN chỉ biến động nhẹ cho thấy sự tăng trưởng quy mô tài sản trong những năm gần đây chủ yếu được tài trợ bằng vốn chủ sở hữu hơn là nợ vay.

Giải thích:

financial_wide\(tn <- financial_wide\)nnh + financial_wide$ndh: tạo biến tổng nợ (TN) bằng cách cộng nợ ngắn hạn và dài hạn.

ggplot(financial_wide, aes(x = nam)): khởi tạo biểu đồ với trục hoành là năm.

geom_line(… tcts …): vẽ đường biểu diễn tổng cộng tài sản (TCTS).

geom_line(… tn …): vẽ đường biểu diễn tổng nợ (TN) dạng gạch đứt để phân biệt.

geom_point(): thêm các điểm tròn đánh dấu giá trị từng năm.

labs(): thêm tiêu đề, nhãn trục và chú thích.

theme_minimal() + theme(): tạo giao diện tối giản, tiêu đề đỏ đậm, in đậm.

2.4.8 2.4.8. Biểu đồ đường của ROA:

financial_wide$roa <- financial_wide$lnst / financial_wide$tcts

ggplot(financial_wide, aes(x = nam, y = roa)) +
  geom_line(color = "darkgreen", size = 1.2) +
  geom_point(color = "forestgreen", size = 3) +
  geom_text(aes(label = round(roa, 3)), vjust = -0.5, size = 3) +
  labs(title = "Tỷ suất sinh lời trên tài sản",
       x = "Năm", y = "ROA") +
  theme_minimal() +
  theme(plot.title = element_text(face = "bold", color = "darkgreen", size = 13))

Nhận xét:

– ROA thể hiện sự biến động cực kỳ mạnh mẽ, tạo thành các đỉnh và đáy sắc nét, cho thấy hiệu quả sử dụng tài sản rất bất ổn qua các năm.

  • Năm 2018 (khoảng 2017.5) đạt 0.19 (19%) là mức cao nhất.

  • Năm 2023 (khoảng 2022.5) đạt 0.187 (18.7%) là mức cao gần nhất.

– Hai đỉnh này cho thấy đơn vị đã có hai giai đoạn sử dụng tài sản đặc biệt hiệu quả.

– Sau đỉnh cao năm 2023, ROA giảm mạnh xuống 0.107 (10.7%) vào năm 2024. Mặc dù có giảm, mức 10.7% này vẫn cao hơn đáng kể so với mức ROA trung bình trong giai đoạn 2015-2022.

=> ROA có tính chu kỳ và bất ổn cao, với hiệu quả sử dụng tài sản đạt đỉnh vào năm 2018 và 2023, nhưng có xu hướng giảm sau mỗi đỉnh cao.

Giải thích:

aes(x = nam, y = roa): trục hoành là năm, trục tung là ROA (tỷ suất sinh lời trên tài sản).

geom_line(): vẽ đường xu hướng ROA.

geom_point(): thêm điểm tròn đánh dấu ROA từng năm.

geom_text(): hiển thị giá trị ROA trên từng điểm.

labs(): đặt tiêu đề và tên trục.

theme_minimal() + theme(): giao diện gọn, tiêu đề in đậm và màu xanh.

2.4.9 2.4.9. Biểu đồ đường của TVTDT:

ggplot(financial_wide, aes(x = nam, y = tvtdt)) +
  geom_area(fill = "lightblue", alpha = 0.5) +
  geom_line(color = "blue", size = 1.2) +
  geom_point(color = "darkblue", size = 3) +
  labs(
    title = "Tiền và tương đương tiền",
    x = "Năm",
    y = "TVTDT (tỷ đồng)"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", color = "navy", size = 13)
  )

Nhận xét:

– Chỉ tiêu này có một xu hướng tăng trưởng rất rõ ràng và mạnh mẽ trong nửa sau của chuỗi thời gian.

  • Giai đoạn giảm (đầu chu kỳ): Giá trị giảm dần từ khoảng 1.0 tỷ đồng xuống mức đáy (dưới 0.5 tỷ đồng) trong giai đoạn đầu.

  • Giai đoạn tăng trưởng (cuối chu kỳ): Sau khi đạt đáy, giá trị tăng trưởng nhanh chóng và liên tục, vượt qua mốc 1.0 tỷ đồng và kết thúc ở đỉnh cao nhất, đạt 4.0 tỷ đồng.

– Tăng trưởng đột phá gần đây: Sự tăng trưởng từ mức 1.5 tỷ đồng lên 4.0 tỷ đồng là sự đột biến, cho thấy khả năng tích lũy tiền mặt hoặc các khoản tương đương tiền đã tăng vọt trong những năm gần đây.

=> Chỉ tiêu tiền và tương đương tiền thể hiện sự phục hồi mạnh mẽ và tăng trưởng đột biến trong giai đoạn cuối của chu kỳ, cho thấy sự cải thiện đáng kể về khả năng thanh khoản và quản lý vốn lưu động.

Giải thích:

aes(x = nam, y = tvtdt): trục hoành là năm, trục tung là tiền và tương đương tiền (TVTDT).

geom_area(): tô màu vùng dưới đường để nhấn mạnh tổng lượng tiền theo năm.

geom_line(): vẽ đường xu hướng TVTDT.

geom_point(): đánh dấu giá trị từng năm bằng điểm tròn.

labs(): thêm tiêu đề và nhãn trục.

theme_minimal() + theme(): giao diện gọn, tiêu đề in đậm màu xanh đậm.

2.4.10 2.4.10. Biểu đồ tròn của LNST, TCTS và TVTDT năm 2023:

library(dplyr)
library(ggplot2)

financial_2023 <- financial_wide %>%
  filter(nam == 2023) %>%
  select(tvtdt, lnst, tcts) %>%
  pivot_longer(cols = everything(), names_to = "Chi_tieu", values_to = "Gia_tri")
financial_2023 <- financial_2023 %>%
  mutate(Ty_le = Gia_tri / sum(Gia_tri) * 100)
ggplot(financial_2023, aes(x = "", y = Ty_le, fill = Chi_tieu)) +
  geom_col(width = 1, color = "white") +
  coord_polar(theta = "y") +
  geom_text(aes(label = paste0(round(Ty_le, 1), "%")),
            position = position_stack(vjust = 0.5), color = "white", size = 4) +
  scale_fill_manual(values = c("#66c2a5", "#fc8d62", "#8da0cb")) +
  labs(
    title = "TVTDT, LNST và TCTS năm 2023",
    fill = "Chỉ tiêu"
  ) +
  theme_void() +
  theme(
    plot.title = element_text(face = "bold", color = "darkblue", size = 13, hjust = 0.5),
    legend.title = element_text(face = "bold")
  )

Nhận xét:

– Tổng cộng tài sản (tcts) chiếm ưu thế tuyệt đối: Tổng cộng tài sản (tcts) chiếm tỷ trọng lớn nhất và áp đảo, đạt 77.2% (gần 4/5 tổng giá trị). Điều này là hợp lý vì tổng tài sản thường là chỉ tiêu có quy mô lớn nhất trong một doanh nghiệp.

– Lợi nhuận sau thuế (lnst) xếp thứ hai: Lợi nhuận sau thuế (lnst) chiếm tỷ trọng thứ hai với 14.4%.

– Tiền và tương đương tiền (tvtdt) chiếm tỷ trọng thấp nhất: Tiền và tương đương tiền (tvtdt) chiếm tỷ trọng nhỏ nhất, chỉ 8.4%.

Giải thích:

filter(nam == 2023): chọn dữ liệu năm 2023.

select(tvtdt, lnst, tcts): lấy 3 chỉ tiêu tiền và tương đương tiền, lợi nhuận sau thuế, tổng tài sản.

pivot_longer(): chuyển dữ liệu sang dạng dài (Chi_tieu và Gia_tri) để vẽ pie chart.

mutate(Ty_le = Gia_tri / sum(Gia_tri) * 100): tính tỷ lệ % của từng chỉ tiêu.

geom_col() + coord_polar(theta = “y”): tạo biểu đồ tròn.

geom_text(): hiển thị phần trăm trên từng lát cắt.

scale_fill_manual(): đặt màu sắc đẹp, phân biệt từng chỉ tiêu.

theme_void() + theme(): giao diện gọn, tiêu đề in đậm và màu xanh, chú thích rõ ràng.

2.4.11 2.4.11. Biểu đồ đường thể hiện sự thay đổi của LNST, LCTT và TVTDT:

financial_long2 <- financial_wide %>%
  select(nam, lnst, lctt, tvtdt) %>%
  pivot_longer(cols = c(lnst, lctt, tvtdt), names_to = "Chi_tieu", values_to = "Gia_tri")

ggplot(financial_long2, aes(x = nam, y = Gia_tri, color = Chi_tieu)) +
  geom_line(size = 1.2) +
  geom_point(size = 3) +
  labs(
    title = "LNST, LCTT và TVTDT qua các năm",
    x = "Năm", y = "Giá trị (tỷ đồng)",
    color = "Chỉ tiêu"
  ) +
  theme_minimal() +
  theme(plot.title = element_text(face = "bold", color = "darkblue", size = 13))

Nhận xét:

– Cả ba chỉ tiêu đều thể hiện sự biến động mạnh qua các năm, với nhiều đỉnh và đáy đột ngột, phản ánh tính bất ổn trong kết quả hoạt động và cấu trúc tài sản.

– tvtdt (Đường xanh dương): Thể hiện xu hướng tăng trưởng mạnh nhất trong dài hạn, đặc biệt là vào cuối chu kỳ, đạt mức cao nhất (gần 4.0 tỷ đồng).

– lnst và lctt: Hai chỉ tiêu này cũng có xu hướng tăng trưởng, nhưng với mức độ ổn định thấp hơn và không mạnh mẽ bằng tvtdt.

– Trong nhiều năm, lnst và lctt di chuyển cùng pha (cùng tăng/cùng giảm), nhưng có những giai đoạn chúng phân kỳ (lnst đạt đỉnh 2.5 tỷ thì LCTT ở mức thấp).

– tvtdt có mối quan hệ ít rõ ràng hơn với hai chỉ tiêu kia trong ngắn hạn, nhưng cho thấy sự tăng trưởng mạnh mẽ trong các năm gần đây.

=> Chỉ tiêu tvtdt (Tiền và tương đương tiền) có mức tăng trưởng ổn định và mạnh nhất. Các chỉ tiêu hiệu quả hoạt động (lnst, lctt) thể hiện sự bất ổn lớn, đặc biệt là sự sụt giảm đột ngột của dòng tiền (lctt) vào cuối chu kỳ trước.

Giải thích:

select(nam, lnst, lctt, tvtdt): lấy năm và 3 chỉ tiêu tài chính.

pivot_longer(): chuyển dữ liệu sang dạng dài để vẽ nhiều đường trên cùng biểu đồ.

geom_line(): vẽ đường xu hướng từng chỉ tiêu qua các năm.

geom_point(): đánh dấu giá trị từng năm bằng điểm tròn.

labs(): thêm tiêu đề, nhãn trục và chú thích.

theme_minimal() + theme(): giao diện gọn, tiêu đề in đậm, màu xanh.