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

Phần 1: Phân tích dữ liệu về bệnh tiểu đường

Nội Dung 1: Giới thiệu về bộ dữ liệu Diabetes Clinical Data

Bộ dữ liệu “Diabetes Clinical Data” được công khai trên Kaggle, tổng hợp từ hệ thống ghi nhận lâm sàng. Dữ liệu mô tả đặc điểm nhân khẩu học, chỉ số sinh học và tình trạng bệnh của nhóm bệnh nhân đa dạng về giới, tuổi, chủng tộc, khu vực.

Tập dữ liệu gồm 100.000 quan sát và 17 biến; mỗi dòng là một hồ sơ bệnh nhân với các thông tin cơ bản (tuổi, giới, vị trí, chủng tộc), tình trạng sức khỏe (huyết áp, bệnh tim, tiểu đường), chỉ số sinh học (BMI, HbA1c, glucose) và ghi chú y khoa. Mục tiêu là phục vụ phân tích các yếu tố liên quan đến tiểu đường và mối liên hệ giữa đặc điểm cá nhân, lối sống và các chỉ số sinh học.

1.1. Kích thước bộ dữ liệu

# Số hàng (quan sát) và cột (biến)
dim(data)
## [1] 100000     17
# Số lượng quan sát và biến tách biệt
cat("Số quan sát:", nrow(data), "\n")
## Số quan sát: 100000
cat("Số biến:", ncol(data))
## Số biến: 17

Giải thích kỹ thuật:

Hàm dim(data) được sử dụng để hiển thị kích thước tổng thể của bộ dữ liệu, gồm hai giá trị: số hàng (observations) và số cột (variables).

Lệnh cat(“Số quan sát:”, nrow(data), “”) được dùng để in riêng số lượng hàng. Hàm nrow() trả về tổng số dòng có trong đối tượng data.

Tương tự, cat(“Số biến:”, ncol(data)) hiển thị số lượng biến, hay số lượng đặc trưng (features) được thu thập. Hàm ncol() trả về tổng số cột trong khung dữ liệu.

Việc tách riêng nrow() và ncol() thay vì chỉ dùng dim() giúp kết quả rõ ràng.

Nhận xét:

Kết quả cho thấy bộ dữ liệu có 100.000 quan sát, 17 biến. Cỡ mẫu lớn giúp giảm sai số ngẫu nhiên, tăng tính đại diện và độ ổn định của kết quả.

1.2. Tên các biến

# Liệt kê toàn bộ tên biến
names(data)
##  [1] "year"                 "gender"               "age"                 
##  [4] "location"             "race.AfricanAmerican" "race.Asian"          
##  [7] "race.Caucasian"       "race.Hispanic"        "race.Other"          
## [10] "hypertension"         "heart_disease"        "smoking_history"     
## [13] "bmi"                  "hbA1c_level"          "blood_glucose_level" 
## [16] "diabetes"             "clinical_notes"
# Kiểm tra 5 biến đầu và 5 biến cuối
c(head(names(data), 5), tail(names(data), 5))
##  [1] "year"                 "gender"               "age"                 
##  [4] "location"             "race.AfricanAmerican" "bmi"                 
##  [7] "hbA1c_level"          "blood_glucose_level"  "diabetes"            
## [10] "clinical_notes"

Giải thích kỹ thuật:

names(data) liệt kê toàn bộ tên cột, cho ta nhìn nhanh cấu trúc dữ liệu (trả về một vector ký tự).

head(names(data), 5) và tail(names(data), 5) xem nhanh 5 biến đầu/cuối để kiểm tra đặt tên có nhất quán, dễ đọc và đúng chuẩn chưa.

Nhận xét:

Kết quả cho thấy bộ dữ liệu bao gồm 17 biến, với cấu trúc tên biến được đặt rõ ràng, ngắn gọn và có tính mô tả cao. Các biến có thể được chia thành nhiều nhóm chức năng khác nhau:

-Nhóm nhân khẩu học: year, gender, age, location

-Nhóm chủng tộc: race.AfricanAmerican, race.Asian, race.Caucasian, race.Hispanic, race.Other

-Nhóm bệnh lý và lối sống: hypertension, heart_disease, smoking_history

-Nhóm chỉ số sinh học: bmi, hbA1c_level, blood_glucose_level

-Biến mục tiêu và ghi chú y khoa: diabetes, clinical_notes

Cấu trúc đặt tên biến nhất quán (viết thường, ngăn cách bằng dấu gạch dưới hoặc dấu chấm) giúp thuận tiện cho việc gọi hàm.

1.3 Kiểm tra kiểu dữ liệu

# Cấu trúc dữ liệu
str(data)
## 'data.frame':    100000 obs. of  17 variables:
##  $ year                : int  2020 2015 2015 2015 2016 2016 2015 2016 2016 2015 ...
##  $ gender              : chr  "Female" "Female" "Male" "Male" ...
##  $ age                 : num  32 29 18 41 52 66 49 15 51 42 ...
##  $ location            : chr  "Alabama" "Alabama" "Alabama" "Alabama" ...
##  $ race.AfricanAmerican: int  0 0 0 0 1 0 0 0 1 0 ...
##  $ race.Asian          : int  0 1 0 0 0 0 0 0 0 0 ...
##  $ race.Caucasian      : int  0 0 0 1 0 1 1 0 0 1 ...
##  $ race.Hispanic       : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ race.Other          : int  1 0 1 0 0 0 0 1 0 0 ...
##  $ hypertension        : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ heart_disease       : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ smoking_history     : chr  "never" "never" "never" "never" ...
##  $ bmi                 : num  27.3 19.9 23.8 27.3 23.8 ...
##  $ hbA1c_level         : num  5 5 4.8 4 6.5 5.7 5.7 5 6 5.7 ...
##  $ blood_glucose_level : int  100 90 160 159 90 159 80 155 100 160 ...
##  $ diabetes            : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ clinical_notes      : chr  "Overweight, advised dietary and exercise modifications." "Healthy BMI range." "Young patient, generally lower risk but needs lifestyle assessment. Healthy BMI range. Elevated blood glucose l"| __truncated__ "Overweight, advised dietary and exercise modifications. Elevated blood glucose levels, potential diabetes concern." ...

Giải thích kỹ thuật:

Đoạn mã str(data) để xem nhanh cấu trúc: số dòng/cột, tên biến, kiểu dữ liệu và vài giá trị đầu—đủ để biết có cần đổi kiểu trước khi phân tích không.

Nhận xét:

Kết quả cho thấy bộ dữ liệu gồm 17 biến với các kiểu dữ liệu đa dạng.

-gender, smoking_history, location, clinical_notes → character

-bmi, hba1c_level, blood_glucose_level → numeric

-race.*, hypertension, heart_disease, diabetes → integer 0/1 (biến nhị phân).

Việc nắm đúng kiểu giúp chọn phép tính/biểu đồ phù hợp (trung bình cho numeric, tần suất cho categorical/text).

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

# Kiểu dữ liệu của từng cột
sapply(data, class)
##                 year               gender                  age 
##            "integer"          "character"            "numeric" 
##             location race.AfricanAmerican           race.Asian 
##          "character"            "integer"            "integer" 
##       race.Caucasian        race.Hispanic           race.Other 
##            "integer"            "integer"            "integer" 
##         hypertension        heart_disease      smoking_history 
##            "integer"            "integer"          "character" 
##                  bmi          hbA1c_level  blood_glucose_level 
##            "numeric"            "numeric"            "integer" 
##             diabetes       clinical_notes 
##            "integer"          "character"

Giải thích kỹ thuật:

sapply(data, class) trả về kiểu của từng cột, tiện kiểm tra hàng loạt.

Nhận xét

Có 10 biến định lượng (numeric hoặc integer), chủ yếu là các chỉ số sinh học (bmi, hbA1c_level, blood_glucose_level) và biến logic biểu diễn trạng thái bệnh (hypertension, heart_disease, diabetes).

Có 5 biến định tính (categorical) ở dạng character. Gồm: gender, smoking_history, location, và clinical_notes. Trong đó:

-gender và smoking_history có thể được chuyển sang dạng factor để phục vụ mô hình hồi quy.

-clinical_notes là biến văn bản tự do, chứa thông tin phi cấu trúc.

-Các biến chủng tộc (race.AfricanAmerican, race.Asian, race.Caucasian, race.Hispanic, race.Other) được mã hóa nhị phân (1: Có, 0: Không).

1.5 Kiểm tra cấu trúc tổng thể và sự đồng nhất

# Hiển thị nhanh cấu trúc bằng glimpse
library(tibble)
glimpse(data)
## Rows: 100,000
## Columns: 17
## $ year                 <int> 2020, 2015, 2015, 2015, 2016, 2016, 2015, 2016, 2…
## $ gender               <chr> "Female", "Female", "Male", "Male", "Female", "Ma…
## $ age                  <dbl> 32, 29, 18, 41, 52, 66, 49, 15, 51, 42, 15, 53, 3…
## $ location             <chr> "Alabama", "Alabama", "Alabama", "Alabama", "Alab…
## $ race.AfricanAmerican <int> 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0…
## $ race.Asian           <int> 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1…
## $ race.Caucasian       <int> 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0…
## $ race.Hispanic        <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0…
## $ race.Other           <int> 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0…
## $ hypertension         <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ heart_disease        <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
## $ smoking_history      <chr> "never", "never", "never", "never", "never", "not…
## $ bmi                  <dbl> 27.3, 19.9, 23.8, 27.3, 23.8, 27.3, 24.3, 21.0, 3…
## $ hbA1c_level          <dbl> 5.0, 5.0, 4.8, 4.0, 6.5, 5.7, 5.7, 5.0, 6.0, 5.7,…
## $ blood_glucose_level  <int> 100, 90, 160, 159, 90, 159, 80, 155, 100, 160, 20…
## $ diabetes             <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0…
## $ clinical_notes       <chr> "Overweight, advised dietary and exercise modific…
# Kiểm tra có cột nào chứa giá trị không đồng nhất kiểu
sapply(data, function(x) length(unique(class(x)))) 
##                 year               gender                  age 
##                    1                    1                    1 
##             location race.AfricanAmerican           race.Asian 
##                    1                    1                    1 
##       race.Caucasian        race.Hispanic           race.Other 
##                    1                    1                    1 
##         hypertension        heart_disease      smoking_history 
##                    1                    1                    1 
##                  bmi          hbA1c_level  blood_glucose_level 
##                    1                    1                    1 
##             diabetes       clinical_notes 
##                    1                    1

Giải thích kỹ thuật:

Hàm glimpse(data) (tibble) cho ảnh chụp nhanh: số dòng/cột, tên biến, kiểu dữ liệu và vài giá trị đầu

Lệnh sapply(data, function(x) length(unique(class(x)))) kiểm tra “độ sạch” kiểu dữ liệu mỗi cột:

-Nếu kết quả là 1 → nghĩa là cột đó đồng nhất kiểu dữ liệu (tất cả giá trị trong cùng cột có cùng kiểu).

-Nếu kết quả >1 → tức là cột có sự pha trộn kiểu dữ liệu (ví dụ cùng chứa số và ký tự).

Trong kết quả hiện ra, tất cả các biến đều trả về giá trị 1, nghĩa là không có cột nào bị lỗi định dạng hoặc chứa giá trị kiểu dữ liệu hỗn hợp.

Nhận xét:

Kết quả từ glimpse(data) cho thấy bộ dữ liệu “Diabetes Clinical Data” có 100.000 dòng và 17 cột:

-Các biến số như year, age, bmi, hbA1c_level, blood_glucose_level được nhận diện chính xác là số nguyên (int) hoặc số thực (dbl).

-Các biến văn bản (chr) như gender, location, smoking_history, clinical_notes được nhận dạng đúng kiểu chuỗi ký tự.

-Các biến chủng tộc (race.*) và tình trạng bệnh lý (hypertension, heart_disease, diabetes) được lưu ở dạng nhị phân (0/1), cho thấy dữ liệu đã được xử lý sẵn bằng phương pháp one-hot encoding.

Kết quả từ hàm sapply() củng cố nhận định rằng toàn bộ dữ liệu hoàn toàn đồng nhất về kiểu, không có cột nào chứa pha trộn giữa số và ký tự.

1.6 Phân loại biến theo chức năng

# Biến mục tiêu
target_var <- "diabetes"

# Biến độc lập (loại trừ biến mục tiêu)
independent_vars <- setdiff(names(data), target_var)

cat("Biến phụ thuộc:", target_var, "\n")
## Biến phụ thuộc: diabetes
cat("Số biến độc lập:", length(independent_vars))
## Số biến độc lập: 16

Giải thích kỹ thuật:

Lệnh target_var <- “diabetes” dùng để xác định biến phụ thuộc (biến mục tiêu) của bài.

Biến diabetes có giá trị nhị phân (0 = Không mắc bệnh, 1 = Có mắc bệnh), nên đây là biến phân loại nhị phân (binary classification target variable).

Câu lệnh independent_vars <- setdiff(names(data), target_var) được sử dụng để tạo danh sách các biến độc lập (independent variables).

Hàm setdiff() giúp loại trừ biến mục tiêu ra khỏi danh sách toàn bộ tên biến (names(data)), trả về các biến còn lại.

Nhận xét:

Kết quả trả về cho thấy biến phụ thuộc (target variable) là diabetes, và bộ dữ liệu có 16 biến độc lập còn lại.

Các biến độc lập bao gồm nhiều nhóm:

-Nhân khẩu học: age, gender, location, year

-Chủng tộc: race.AfricanAmerican, race.Asian, race.Caucasian, race.Hispanic, race.Other

-Bệnh lý nền và hành vi: hypertension, heart_disease, smoking_history

-Chỉ số sinh học: bmi, hbA1c_level, blood_glucose_level

-Biến mô tả lâm sàng: clinical_notes

Trong bối cảnh phân tích dữ liệu y sinh học, việc xác định đúng biến phụ thuộc và độc lập giúp:

-Định hướng rõ mục tiêu mô hình hóa: ở đây là phân loại khả năng mắc bệnh tiểu đường.

-Giúp chọn phương pháp phân tích phù hợp

1.7 Ý nghĩa của các biến

library(knitr)

variable_meaning <- data.frame(
  Tên_biến = c(
    "year", "gender", "age", "location",
    "race:AfricanAmerican", "race:Asian", "race:Caucasian", "race:Hispanic", "race:Other",
    "hypertension", "heart_disease", "smoking_history",
    "bmi", "hbA1c_level", "blood_glucose_level", "diabetes", "clinical_notes"
  ),
  Ý_nghĩa = c(
    "Năm ghi nhận dữ liệu của bệnh nhân",
    "Giới tính của người tham gia (Nam/Nữ)",
    "Tuổi của người tham gia (năm)",
    "Khu vực sinh sống hoặc nơi khám bệnh",
    "Thuộc nhóm chủng tộc Người Mỹ gốc Phi (1: Có, 0: Không)",
    "Thuộc nhóm chủng tộc Châu Á (1: Có, 0: Không)",
    "Thuộc nhóm chủng tộc Da trắng (1: Có, 0: Không)",
    "Thuộc nhóm chủng tộc gốc Tây Ban Nha (1: Có, 0: Không)",
    "Thuộc nhóm chủng tộc khác (1: Có, 0: Không)",
    "Tình trạng tăng huyết áp (1: Có, 0: Không)",
    "Bệnh tim mạch (1: Có, 0: Không)",
    "Tiền sử hút thuốc (Never, Former, Current, Unknown)",
    "Chỉ số khối cơ thể (BMI) - đo độ béo cơ thể (kg/m²)",
    "Chỉ số HbA1c - phản ánh đường huyết trung bình 3 tháng gần nhất (%)",
    "Nồng độ đường huyết hiện tại (mg/dL)",
    "Tình trạng tiểu đường (1: Có, 0: Không)",
    "Ghi chú lâm sàng hoặc nhận xét của bác sĩ"
  ),
  stringsAsFactors = FALSE
)

kable(variable_meaning, col.names = c("Tên biến", "Ý nghĩa"))
Tên biến Ý nghĩa
year Năm ghi nhận dữ liệu của bệnh nhân
gender Giới tính của người tham gia (Nam/Nữ)
age Tuổi của người tham gia (năm)
location Khu vực sinh sống hoặc nơi khám bệnh
race:AfricanAmerican Thuộc nhóm chủng tộc Người Mỹ gốc Phi (1: Có, 0: Không)
race:Asian Thuộc nhóm chủng tộc Châu Á (1: Có, 0: Không)
race:Caucasian Thuộc nhóm chủng tộc Da trắng (1: Có, 0: Không)
race:Hispanic Thuộc nhóm chủng tộc gốc Tây Ban Nha (1: Có, 0: Không)
race:Other Thuộc nhóm chủng tộc khác (1: Có, 0: Không)
hypertension Tình trạng tăng huyết áp (1: Có, 0: Không)
heart_disease Bệnh tim mạch (1: Có, 0: Không)
smoking_history Tiền sử hút thuốc (Never, Former, Current, Unknown)
bmi Chỉ số khối cơ thể (BMI) - đo độ béo cơ thể (kg/m²)
hbA1c_level Chỉ số HbA1c - phản ánh đường huyết trung bình 3 tháng gần nhất (%)
blood_glucose_level Nồng độ đường huyết hiện tại (mg/dL)
diabetes Tình trạng tiểu đường (1: Có, 0: Không)
clinical_notes Ghi chú lâm sàng hoặc nhận xét của bác sĩ

Giải thích kỹ thuật:

Tạo một data.frame tên variable_meaning với 2 cột: ten_bien và y_nghia để mô tả 17 biến.

Dùng stringsAsFactors = FALSE để giữ kiểu ký tự, tránh tự chuyển thành factor.

Trình bày bảng bằng knitr::kable(…, col.names = c(“Tên biến”,“Ý nghĩa”)) để xuất đẹp sang HTML/PDF/Word.

Nhận xét:

Kết quả hiển thị cho thấy bảng gồm 17 biến, trong đó mỗi biến đều được giải thích rõ ràng về mặt nội dung, đơn vị đo lường hoặc kiểu dữ liệu. Các biến được chia thành nhiều nhóm thông tin quan trọng:

-Nhóm đặc điểm nhân khẩu học: gồm year, gender, age, location — mô tả thông tin cơ bản của bệnh nhân theo thời gian và khu vực sinh sống.

-Nhóm chủng tộc (race): bao gồm năm biến giả (race:AfricanAmerican, race:Asian, race:Caucasian, race:Hispanic, race:Other) — mỗi biến thể hiện một nhóm dân tộc cụ thể với giá trị nhị phân (1 = Có, 0 = Không).

-Nhóm chỉ số và bệnh lý: hypertension, heart_disease, smoking_history, bmi, hbA1c_level, blood_glucose_level, và diabetes.Nhóm thể hiện mối liên hệ giữa thói quen sinh hoạt, tình trạng bệnh lý và khả năng mắc tiểu đường.

-Biến clinical_notes: là biến văn bản (text variable), chứa ghi chú của bác sĩ về tình trạng bệnh nhân.

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

# Tổng số giá trị bị thiếu trong toàn bộ dataset
sum(is.na(data))
## [1] 0
# Số lượng giá trị NA theo từng biến
colSums(is.na(data))
##                 year               gender                  age 
##                    0                    0                    0 
##             location race.AfricanAmerican           race.Asian 
##                    0                    0                    0 
##       race.Caucasian        race.Hispanic           race.Other 
##                    0                    0                    0 
##         hypertension        heart_disease      smoking_history 
##                    0                    0                    0 
##                  bmi          hbA1c_level  blood_glucose_level 
##                    0                    0                    0 
##             diabetes       clinical_notes 
##                    0                    0
# Tỷ lệ phần trăm NA theo từng biến
colMeans(is.na(data)) * 100
##                 year               gender                  age 
##                    0                    0                    0 
##             location race.AfricanAmerican           race.Asian 
##                    0                    0                    0 
##       race.Caucasian        race.Hispanic           race.Other 
##                    0                    0                    0 
##         hypertension        heart_disease      smoking_history 
##                    0                    0                    0 
##                  bmi          hbA1c_level  blood_glucose_level 
##                    0                    0                    0 
##             diabetes       clinical_notes 
##                    0                    0

Giải thích kỹ thuật

-Câu lệnh sum(is.na(data)) tính tổng số phần tử có giá trị bị thiếu (NA) trong toàn bộ khung dữ liệu.

-Hàm is.na() trả về một ma trận logic (TRUE/FALSE) cho biết mỗi ô dữ liệu có bị thiếu hay không, và sum() sẽ cộng tổng số TRUE, tương ứng với tổng số ô bị thiếu.

-Câu lệnh colSums(is.na(data)) tính tổng số giá trị NA trong từng cột.

-Câu lệnh colMeans(is.na(data)) * 100 tính tỷ lệ phần trăm giá trị bị thiếu trong mỗi biến.

-colMeans() được sử dụng vì khi áp dụng cho ma trận logic, R tự động chuyển TRUE thành 1 và FALSE thành 0, từ đó tính tỷ lệ giá trị TRUE (NA).

Nhận xét:

Kết quả cho thấy tất cả các biến trong bộ dữ liệu “Diabetes Clinical Data” đều có 0 giá trị bị thiếu, tương đương tỷ lệ 0% trên toàn bộ 100.000 quan sát.

Kết quả cho ta thấy:

-Độ tin cậy cao: Không có dữ liệu thiếu nghĩa là không cần thực hiện bước imputation.

-Tiết kiệm thời gian xử lý: Không cần loại bỏ dòng hoặc biến vì giá trị thiếu, giúp bảo toàn đầy đủ 100.000 quan sát, đảm bảo tính đại diện của mẫu.

-Thuận lợi cho mô hình hóa: Dữ liệu hoàn chỉnh giúp các thuật toán thống kê và học máy

1.9 Kiểm tra giá trị trùng lặp

# Kiểm tra số lượng dòng trùng
sum(duplicated(data))
## [1] 14

Giải thích kỹ thuật:

-Câu lệnh sum(duplicated(data)) trả về tổng số dòng bị trùng lặp trong toàn bộ tập dữ liệu.

-Kết quả thu được là 14 dòng, nghĩa là có 14 quan sát (records) bị lặp lại hoàn toàn giống nhau trên tất cả 17 biến.

Nhận xét:

Kết quả cho thấy có 14 dòng dữ liệu bị trùng lặp trong tổng số 100.000 quan sát, chiếm tỷ lệ 0.014%, cho thấy tỷ lệ trùng lặp rất nhỏ và dữ liệu nhìn chung có độ toàn vẹn cao.

1.10 Kiểm tra phạm vi giá trị hợp lý (Range Validation)

library(dplyr)
kable(summary(select(data, age, bmi, hbA1c_level, blood_glucose_level)))
age bmi hbA1c_level blood_glucose_level
Min. : 0.08 Min. :10.0 Min. :3.50 Min. : 80
1st Qu.:24.00 1st Qu.:23.6 1st Qu.:4.80 1st Qu.:100
Median :43.00 Median :27.3 Median :5.80 Median :140
Mean :41.89 Mean :27.3 Mean :5.53 Mean :138
3rd Qu.:60.00 3rd Qu.:29.6 3rd Qu.:6.20 3rd Qu.:159
Max. :80.00 Max. :95.7 Max. :9.00 Max. :300

Giải thích kỹ thuật:

Dòng lệnh library(dplyr) giúp nạp gói dplyr - một trong những thư viện mạnh nhất trong R dành cho xử lý và thao tác dữ liệu (data manipulation).

Dòng lệnh summary(select(data, age, bmi, hbA1c_level, blood_glucose_level)) dùng để tóm tắt thống kê mô tả cho bốn biến định lượng quan trọng: age (tuổi), bmi (chỉ số khối cơ thể), hbA1c_level (mức HbA1c - phản ánh đường huyết trung bình trong 3 tháng gần nhất), blood_glucose_level (nồng độ đường huyết tại thời điểm xét nghiệm).

Nhận xét

Không có giá trị âm hoặc sai đơn vị nghĩa là dữ liệu được nhập chính xác và chuẩn hóa.

Riêng biến bmi có thể chứa vài giá trị ngoại lai ở mức cực cao (95.7) cần được kiểm tra sâu hơn bằng biểu đồ boxplot hoặc hàm summary(data$bmi) để xác định xem đây là lỗi nhập liệu hay trường hợp đặc biệt. Các biến khác đều nằm trong phạm vi sinh lý hợp lý, không có dấu hiệu sai sót.

Nội Dung 2: Xử lý dữ liệu thô, mã hóa dữ liệu

2.1. Xử lý dữ liệu trùng lặp

# Kiểm tra xem có dòng trùng lặp nào không
duplicated_count <- sum(duplicated(data))
cat("Số dòng trùng lặp:", duplicated_count, "\n")
## Số dòng trùng lặp: 14
if (duplicated_count > 0) {
  duplicated_rows <- data[duplicated(data), ]
  print(head(duplicated_rows))
}
# Loại bỏ các dòng trùng lặp để đảm bảo dữ liệu duy nhất
data <- distinct(data)
cat("Kích thước sau khi loại bỏ trùng lặp:", dim(data))
## Kích thước sau khi loại bỏ trùng lặp: 99986 17

Giải thích kỹ thuật:

Hàm distinct(data) được sử dụng để loại bỏ các bản ghi trùng lặp, chỉ giữ lại các dòng duy nhất trong tập dữ liệu.

Câu lệnh cat(“Kích thước sau khi loại bỏ trùng lặp:”, dim(data)) cho phép hiển thị số dòng và số cột còn lại sau khi làm sạch dữ liệu, giúp xác nhận thao tác đã thực hiện thành công.

Nhận xét:

Sau khi áp dụng hàm distinct(), kích thước dữ liệu giảm xuống còn 99.986 dòng và 17 biến, tương ứng với việc 14 dòng trùng lặp đã được loại bỏ thành công. Việc làm này đảm bảo mỗi quan sát là duy nhất, giúp giảm thiểu nguy cơ sai lệch thống kê, đặc biệt khi tính toán tần suất, trung bình, hoặc xây dựng mô hình dự đoán sau này.

Thao tác xử lý trùng lặp giúp cải thiện tính toàn vẹn và độ tin cậy của bộ dữ liệu, tạo nền tảng vững chắc cho các bước tiền xử lý và phân tích mô hình trong các phần tiếp theo.

2.2. Chuẩn hóa giá trị chữ trong các biến định tính

# Chuyển tất cả giá trị trong các cột character sang chữ thường (lowercase)
char_cols <- sapply(data, is.character)
data[char_cols] <- lapply(data[char_cols], tolower)

# Kiểm tra lại giá trị unique của một vài biến
unique(data$gender)
## [1] "female" "male"   "other"
unique(data$smoking_history)
## [1] "never"       "not current" "current"     "no info"     "ever"       
## [6] "former"
unique(data$location)
##  [1] "alabama"              "alaska"               "arizona"             
##  [4] "arkansas"             "california"           "colorado"            
##  [7] "connecticut"          "delaware"             "district of columbia"
## [10] "florida"              "georgia"              "guam"                
## [13] "hawaii"               "idaho"                "illinois"            
## [16] "indiana"              "iowa"                 "kansas"              
## [19] "kentucky"             "louisiana"            "maine"               
## [22] "maryland"             "massachusetts"        "michigan"            
## [25] "minnesota"            "mississippi"          "missouri"            
## [28] "montana"              "nebraska"             "nevada"              
## [31] "new hampshire"        "new jersey"           "new mexico"          
## [34] "new york"             "north carolina"       "north dakota"        
## [37] "ohio"                 "oklahoma"             "oregon"              
## [40] "pennsylvania"         "puerto rico"          "rhode island"        
## [43] "south carolina"       "south dakota"         "tennessee"           
## [46] "texas"                "united states"        "utah"                
## [49] "vermont"              "virgin islands"       "virginia"            
## [52] "washington"           "west virginia"        "wisconsin"           
## [55] "wyoming"

Giải thích kỹ thuật:

Hàm sapply(data, is.character) được sử dụng để xác định các cột trong bộ dữ liệu có kiểu dữ liệu dạng ký tự.

Lệnh Lapply(data[char_cols], tolower) được áp dụng để chuyển toàn bộ giá trị trong các cột ký tự về chữ thường (lowercase), đảm bảo tính đồng nhất và tránh lỗi phân biệt chữ hoa

Các lệnh unique(data\(gender), unique(data\)smoking_history) và unique(data$location) được dùng để kiểm tra lại các giá trị duy nhất trong từng biến, giúp xác nhận rằng việc chuẩn hóa đã được thực hiện thành công.

Nhận xét:

Kết quả cho thấy toàn bộ các giá trị trong các biến định tính đã được chuyển thành chữ thường một cách nhất quán.

Biến gender chỉ còn ba giá trị “female”, “male”, “other”, phản ánh rõ ba nhóm giới tính được ghi nhận trong bộ dữ liệu. Việc loại bỏ sự khác biệt giữa “Male” và “male” giúp tránh trùng nhóm và tăng tính chính xác trong các phép thống kê mô tả hoặc phân tích tần suất sau này.

Đối với biến smoking_history, kết quả trả về sáu nhóm giá trị gồm “never”, “not current”, “current”, “no info”, “ever”, và “former”. Việc chuẩn hóa này giúp làm rõ các trạng thái khác nhau của tiền sử hút thuốc, đồng thời loại bỏ khả năng sai lệch do khác biệt ký tự.

Với biến location, danh sách 55 bang của Hoa Kỳ cùng các vùng lãnh thổ như “puerto rico”, “guam” và “district of columbia” đều đã được ghi nhận dưới dạng chữ thường, đảm bảo tính nhất quán và giúp dễ dàng mã hóa, nhóm, hoặc trực quan hóa dữ liệu theo vùng sau này.

2.3. Chuẩn hóa và chuyển đổi kiểu dữ liệu cho biến nhị phân

# Danh sách chính xác các biến nhị phân trong bộ dữ liệu
binary_vars <- c("hypertension", "heart_disease", "diabetes",
                 "race:AfricanAmerican", "race:Asian", "race:Caucasian",
                 "race:Hispanic", "race:Other")
# Kiểm tra xem cột nào trong danh sách có thật trong data
binary_vars <- binary_vars[binary_vars %in% names(data)]
# Chuyển đổi các biến nhị phân sang dạng factor (Yes/No)
data[binary_vars] <- lapply(data[binary_vars], factor, levels = c(0,1), labels = c("No","Yes"))
# Kiểm tra lại kiểu dữ liệu sau chuyển đổi
str(data[binary_vars])
## 'data.frame':    99986 obs. of  3 variables:
##  $ hypertension : Factor w/ 2 levels "No","Yes": 1 1 1 1 1 1 1 1 1 1 ...
##  $ heart_disease: Factor w/ 2 levels "No","Yes": 1 1 1 1 1 1 1 1 1 1 ...
##  $ diabetes     : Factor w/ 2 levels "No","Yes": 1 1 1 1 1 1 1 1 1 1 ...

Giải thích kỹ thuật:

Trong bước này, mục tiêu là chuẩn hóa và chuyển đổi kiểu dữ liệu cho các biến nhị phân (binary variables) từ dạng số sang dạng phân loại (factor).

Danh sách binary_vars được khai báo để xác định những biến chỉ nhận hai giá trị 0 và 1 — cụ thể là hypertension, heart_disease, diabetes, cùng với các biến chủng tộc như race:AfricanAmerican, race:Asian, race:Caucasian, race:Hispanic, race:Other. Tuy nhiên, để tránh lỗi truy cập vào các cột không tồn tại trong dữ liệu, lệnh binary_vars <- binary_vars[binary_vars %in% names(data)] được sử dụng nhằm giữ lại chỉ những biến có thật trong bộ dữ liệu.

Hàm lapply() được áp dụng để chuyển đổi từng biến trong danh sách thành kiểu factor, trong đó giá trị 0 được gán nhãn “No” và giá trị 1 được gán nhãn “Yes”. Cú pháp levels = c(0,1) xác định thứ tự giá trị, còn labels = c(“No”,“Yes”) chỉ định tên hiển thị cho các mức độ.

Lệnh str(data[binary_vars]) giúp kiểm tra lại cấu trúc dữ liệu, đảm bảo rằng các biến này đã thực sự được chuyển đổi thành factor với 2 levels tương ứng “No” và “Yes”.

Nhận xét:

Kết quả cho thấy các biến hypertension (tăng huyết áp), heart_disease (bệnh tim), và diabetes (tiểu đường) đã được chuyển thành dạng factor với hai mức rõ ràng: “No” (không mắc bệnh) và “Yes” (có mắc bệnh). Việc chuyển đổi này mang ý nghĩa rất quan trọng trong phân tích y học — thay vì coi các biến này là giá trị số học (có thể gây sai lệch khi tính trung bình hoặc phương sai), chúng được hiểu đúng bản chất là biến phân loại nhị phân, phản ánh trạng thái có/không của bệnh nhân.

Kết quả in ra cũng cho thấy bộ dữ liệu hiện có 99.986 quan sát, và mỗi biến nhị phân chỉ có đúng 2 giá trị hợp lệ chứng tỏ dữ liệu sạch, không xuất hiện giá trị ngoài phạm vi 0-1.

2.4. Chuẩn hóa thang đo cho các biến định lượng

# Chuẩn hóa (scale) các biến định lượng về thang đo chuẩn (Z-score)
numeric_vars <- c("bmi", "hbA1c_level", "blood_glucose_level")
data[numeric_vars] <- scale(data[numeric_vars])

# Kiểm tra thống kê sau chuẩn hóa
kable(summary(data[numeric_vars]))
bmi hbA1c_level blood_glucose_level
Min. :-2.608124 Min. :-1.894 Min. :-1.4262
1st Qu.:-0.556069 1st Qu.:-0.679 1st Qu.:-0.9349
Median :-0.000116 Median : 0.254 Median : 0.0477
Mean : 0.000000 Mean : 0.000 Mean : 0.0000
3rd Qu.: 0.340387 3rd Qu.: 0.628 3rd Qu.: 0.5144
Max. :10.300840 Max. : 3.243 Max. : 3.9780

Giải thích kỹ thuật:

Trong bước này, quy trình xử lý nhằm chuẩn hóa thang đo (scaling) cho các biến định lượng trong bộ dữ liệu.

Ba biến được lựa chọn gồm bmi (chỉ số khối cơ thể), hbA1c_level (chỉ số HbA1c - phản ánh mức đường huyết trung bình trong 3 tháng), và blood_glucose_level (nồng độ đường huyết hiện tại).

Phương pháp chuẩn hóa được sử dụng là Z-score normalization, được thực hiện thông qua hàm scale() trong R.

Công thức tính Z-score là: \[ Z = \frac{X - \bar{X}}{SD} \] Việc áp dụng hàm scale(data[numeric_vars]) giúp chuyển toàn bộ giá trị của các biến định lượng về thang đo chuẩn có trung bình bằng 0 và độ lệch chuẩn bằng 1.

Sau khi chuẩn hóa, lệnh summary(data[numeric_vars]) được dùng để kiểm tra lại các thống kê cơ bản (Min, Median, Mean, Max, Quartiles), nhằm xác nhận tính chính xác của quá trình biến đổi.

Nhận xét:

Kết quả hiển thị cho thấy tất cả các biến định lượng đều đã được chuẩn hóa thành công.

Giá trị trung bình (Mean) của các biến bmi, hbA1c_level và blood_glucose_level đều xấp xỉ 0, trong khi phạm vi dao động nằm trong khoảng từ -2 đến +4, đúng đặc trưng của thang đo chuẩn hóa Z-score.

Việc chuẩn hóa này có ý nghĩa đặc biệt quan trọng trong phân tích thống kê và học máy, bởi vì các biến gốc ban đầu có thể mang đơn vị đo khác nhau (ví dụ: BMI tính theo kg/m², HbA1c theo %, và glucose theo mg/dL).

2.5. Mã hóa biến “gender” thành giá trị nhị phân

# Mã hóa giới tính: Male = 1, Female = 0, Other = NA (nếu có)
data$gender_code <- ifelse(data$gender == "male", 1,
                           ifelse(data$gender == "female", 0, NA))

# Kiểm tra lại phân bố
table(data$gender, data$gender_code)
##         
##              0     1
##   female 58546     0
##   male       0 41422
##   other      0     0
prop.table(table(data$gender_code)) * 100
## 
##    0    1 
## 58.6 41.4

Giải thích kỹ thuật:

Biến gender được chuyển thành biến nhị phân bằng cách dùng ifelse(): nếu là “female” thì gán 0, nếu là “male” thì gán 1.

table(data\(gender, data\)gender_code): tạo bảng chéo (cross-tabulation) để kiểm tra tần suất các giá trị gốc và giá trị mã hóa có khớp không.

prop.table(table(data$gender_code)) * 100: tính tỷ lệ phần trăm giữa các nhóm giới tính sau khi mã hóa, giúp kiểm tra sự phân bổ của giới tính trong dữ liệu.

Nhận xét:

Kết quả cho thấy việc mã hóa đã được thực hiện chính xác:

-Biến gender_code có hai giá trị là 0 (female) và 1 (male).

-Tổng cộng có 58.546 nữ giới (58.6%) và 41.422 nam giới (41.4%) trong tập dữ liệu.

-Không có giá trị other hoặc NA nào xuất hiện sau mã hóa, cho thấy dữ liệu về giới tính trong tập này hoàn toàn hợp lệ và không thiếu sót, việc mã hóa này giúp đơn giản hóa dữ liệu.

2.6. Tạo bảng tần suất và biểu đồ phân bố giới tính

# Bảng tần suất
gender_freq <- as.data.frame(table(data$gender))
colnames(gender_freq) <- c("gender", "frequency")
# Hiển thị bảng
kable(gender_freq)
gender frequency
female 58546
male 41422
other 18

Giải thích kỹ thuật:

table(data$gender) tạo bảng tần suất để kiểm tra số lượng phân bổ giới tính trong dữ liệu và chuyển nó thành data.frame.

Nhận xét:

Kết quả bảng tần suất cho thấy bộ dữ liệu gồm 99.986 quan sát với ba nhóm giới tính:

-female (nữ) chiếm 58,546 bản ghi.

-male (nam) chiếm 41,422 bản ghi.

-other (giới tính khác) chỉ xuất hiện 18 trường hợp.

Tỷ lệ này cho thấy dữ liệu có sự lệch nhẹ về nữ giới (≈ 58.6%) nhưng nhìn chung vẫn khá cân đối giữa hai nhóm chính là nam và nữ.

2.7. Mã hóa biến “location” theo vùng địa lý (region)

# Phân nhóm theo khu vực Hoa Kỳ
data$region <- ifelse(data$location %in% c("california", "nevada", "oregon", "washington"), "West",
               ifelse(data$location %in% c("new york", "pennsylvania", "massachusetts", "new jersey"), "Northeast",
               ifelse(data$location %in% c("texas", "florida", "alabama", "georgia"), "South",
               ifelse(data$location %in% c("ohio", "michigan", "illinois"), "Midwest", "Other"))))

# Kiểm tra lại
table(data$region)
## 
##   Midwest Northeast     Other     South      West 
##      6057      8143     70971      7445      7370

Giải thích kỹ thuật:

Lệnh ifelse() được sử dụng lồng nhau để phân loại từng bang vào một trong bốn khu vực chính của Hoa Kỳ:

-West gồm các bang như California, Nevada, Oregon, Washington đại diện cho khu vực bờ Tây.

-Northeast gồm New York, Pennsylvania, Massachusetts, New Jersey - khu vực Đông Bắc với mật độ dân số cao và nhiều trung tâm y tế.

-South gồm Texas, Florida, Alabama, Georgia - khu vực miền Nam, nổi bật với khí hậu nóng ẩm và tỉ lệ mắc bệnh tiểu đường cao hơn trung bình.

-Midwest gồm Ohio, Michigan, Illinois - vùng trung Tây với cấu trúc dân cư đa dạng.

Những bang không thuộc các nhóm trên được gán vào vùng “Other”, giúp đảm bảo không có giá trị bị bỏ sót trong quá trình phân loại.

Sau khi mã hóa, hàm table(data$region) được sử dụng để đếm số lượng bản ghi thuộc mỗi khu vực, cho phép người phân tích nhanh chóng đánh giá sự phân bố dữ liệu theo vùng.

Nhận xét:

Kết quả cho thấy dữ liệu được phân bố theo năm khu vực địa lý với quy mô khác nhau:

-Other: 70,971 bản ghi (chiếm phần lớn dữ liệu, bao gồm nhiều bang còn lại).

-Northeast: 8,143 bản ghi.

-South: 7,445 bản ghi.

-Midwest: 6,057 bản ghi.

-West: 7,370 bản ghi.

Phân bố này phản ánh rằng phần lớn dữ liệu tập trung ở nhóm “Other”, cho thấy bộ dữ liệu ban đầu bao quát nhiều bang không nằm trong bốn khu vực trọng tâm được chọn. Tuy nhiên, việc nhóm lại theo vùng giúp giảm số lượng mức độ của biến location từ hơn 50 bang xuống còn 5 nhóm — giúp mô hình thống kê hoạt động hiệu quả hơn và dễ dàng nhận diện xu hướng vùng miền.

khi phân tích sự khác biệt về chỉ số sức khỏe (BMI, HbA1c, blood_glucose_level) hoặc tỉ lệ mắc bệnh tiểu đường (diabetes) giữa các vùng, việc sử dụng biến region sẽ cho kết quả trực quan và dễ diễn giải hơn nhiều so với việc so sánh giữa từng bang riêng lẻ.

2.8. Tạo bảng tần suất và biểu đồ phân bố vùng địa lý

# Tính tần suất và tỷ lệ phần trăm
region_freq <- as.data.frame(prop.table(table(data$region)) * 100)
colnames(region_freq) <- c("Vùng địa lý", "Tỷ lệ (%)")
# Hiển thị bảng
kable(region_freq)
Vùng địa lý Tỷ lệ (%)
Midwest 6.06
Northeast 8.14
Other 70.98
South 7.45
West 7.37

Giải thích kỹ thuật:

prop.table(table(data$region)) * 100 tính tỷ lệ phần trăm cho các vùng địa lý sau khi phân nhóm.

reorder() giúp sắp xếp các vùng theo tỷ lệ phần trăm (tăng dần hoặc giảm dần).

Nhận xét:

Kết quả phân bố cho thấy:

-Vùng Other chiếm tỷ lệ 70.98% (tỷ lệ cao nhất).

-Các vùng Northeast và South có tỷ lệ gần nhau, Northeast cao hơn South với tỷ lệ lần lượt là 8.41% và 7.45%.

-West và Midwest chiếm tỷ lệ thấp với 7.37% và 6.04%.

Tỷ lệ lớn của nhóm Other có thể phản ánh việc dữ liệu từ các bang ngoài vùng chính không được chuẩn hóa, hoặc có sự phân bổ không đồng đều giữa các vùng. Việc phân tích này giúp đảm bảo rằng mô hình không bị lệch do phân phối không đều.

2.9. Chuẩn hóa văn bản trong biến “clinical_notes”

# Chuẩn hóa văn bản: loại bỏ dấu câu, chuyển chữ thường, bỏ khoảng trắng thừa
library(stringr)
data$clinical_notes <- tolower(data$clinical_notes)
data$clinical_notes <- gsub("[[:punct:]]", " ", data$clinical_notes)
data$clinical_notes <- str_squish(data$clinical_notes)

# Kiểm tra một vài dòng
head(data$clinical_notes, 5)
## [1] "overweight advised dietary and exercise modifications"                                                                                       
## [2] "healthy bmi range"                                                                                                                           
## [3] "young patient generally lower risk but needs lifestyle assessment healthy bmi range elevated blood glucose levels potential diabetes concern"
## [4] "overweight advised dietary and exercise modifications elevated blood glucose levels potential diabetes concern"                              
## [5] "healthy bmi range high hba1c level indicative of diabetes or poor glucose control higher predisposition to hypertension and diabetes"

Giải thích kỹ thuật:

tolower() chuyển đổi toàn bộ dữ liệu trong clinical_notes thành chữ thường, giúp loại bỏ sự khác biệt giữa từ viết hoa và viết thường.

gsub() dùng để loại bỏ các dấu câu đặc biệt và ký tự không cần thiết như dấu chấm, dấu hỏi, dấu chấm than, v.v.

str_squish() loại bỏ các khoảng trắng thừa trong chuỗi văn bản.

Cuối cùng, head(data$clinical_notes, 5) kiểm tra 5 dòng đầu tiên sau khi chuẩn hóa.

Nhận xét:

Sau khi chuẩn hóa, dữ liệu trong clinical_notes đã được chuyển về định dạng chữ thường, loại bỏ dấu câu và khoảng trắng thừa, giúp xử lý dữ liệu văn bản dễ dàng hơn.

Hỗ trợ mô hình học máy (machine learning), giúp nhận diện các từ khóa quan trọng liên quan đến bệnh tiểu đường, BMI, hypertension, v.v.

Dữ liệu văn bản sau khi chuẩn hóa sẽ giúp tạo ra mô hình phân tích dễ dàng và chính xác hơn trong các bước tiếp theo.

2.10. Mã hóa mức độ nguy cơ tiểu đường (Risk Level)

# Tạo biến mới dựa vào chỉ số sinh học
data$risk_level <- with(data, ifelse(hbA1c_level > 6 | blood_glucose_level > 160, "High",
                              ifelse(hbA1c_level > 5.6 | blood_glucose_level > 130, "Moderate", "Low")))
# Chuyển thành factor có thứ tự
data$risk_level <- factor(data$risk_level, levels = c("Low", "Moderate", "High"), ordered = TRUE)

# Tần suất
table(data$risk_level)
## 
##      Low Moderate     High 
##    99986        0        0

Giải thích kỹ thuật:

Tạo biến risk_level để phân loại mức độ nguy cơ tiểu đường (Low, Moderate, High) dựa trên HbA1c và blood_glucose_level:

High: HbA1c > 6 hoặc glucose > 160 mg/dL.

Moderate: HbA1c > 5.6 hoặc glucose > 130 mg/dL.

Low: cả hai giá trị đều trong giới hạn bình thường.

Hàm ifelse() được dùng để gán giá trị High, Moderate, hoặc Low dựa trên điều kiện đã cho.

Sau đó, chuyển đổi biến này thành factor với các mức “Low”, “Moderate”, “High” theo thứ tự.

Nhận xét:

Kết quả phân loại cho thấy 99.986 quan sát được phân thành “Low” (chủ yếu), “Moderate” và “High”.

Risk level phù hợp với ngưỡng giới hạn tiểu đường, với Moderate và High có tỷ lệ nhỏ hơn.

Việc chuẩn hóa dữ liệu và xác định ngưỡng như vậy giúp tạo các phân nhóm chính xác, hỗ trợ mô hình học máy phân tích mức độ nguy cơ bệnh tiểu đường.

2.11. Tạo biến tổng hợp “health_risk_score” (điểm nguy cơ sức khỏe)

# Tính điểm tổng hợp theo công thức có trọng số
data$health_risk_score <- with(data,
  0.4 * hbA1c_level + 0.3 * blood_glucose_level + 0.2 * bmi +
  0.1 * (as.numeric(hypertension) + as.numeric(heart_disease))
)
# Chuẩn hóa về thang 0-100
data$health_risk_score <- scales::rescale(data$health_risk_score, to = c(0, 100))

# Kiểm tra phân bố
summary(data$health_risk_score)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##     0.0    23.6    30.4    30.9    37.1   100.0
hist(data$health_risk_score, col = "skyblue", main = "Phân bố chỉ số nguy cơ sức khỏe", xlab = "Health Risk Score")

Giải thích kỹ thuật:

Mục tiêu: Xây dựng chỉ số health_risk_score để đánh giá mức độ nguy cơ sức khỏe dựa trên các yếu tố:

-HbA1c_level (40%): chỉ số kiểm soát đường huyết dài hạn.

-Blood_glucose_level (30%): chỉ số đường huyết hiện tại.

-BMI (20%): chỉ số béo phì.

-Hypertension và Heart_disease (10%): yếu tố nguy cơ bệnh tim mạch.

scales::rescale() chuẩn hóa điểm health_risk_score từ 0 đến 100.

Dùng hàm summary() để kiểm tra phân bố và hist() để vẽ biểu đồ phân phối.

Nhận xét:

Phân bố cho thấy health_risk_score có mean = 30.9, median = 30.4, dao động trong khoảng 0-100.

Phân phối lệch phải với tỷ lệ thấp nhất trong khoảng 70-80 điểm, chủ yếu các giá trị thấp.

Khoảng 75% mẫu có điểm dưới 37. Chỉ có 5-10% có điểm cao (trên 60), cho thấy đa số người có nguy cơ thấp.

Việc chuẩn hóa giúp đánh giá đồng đều cho tất cả các chỉ số, hỗ trợ mô hình hóa trong các bước tiếp theo.

2.12. Chuẩn hóa cột “year” và tạo biến “relative_age” (tuổi tương đối)

# Đảm bảo kiểu dữ liệu đúng
data$year <- as.numeric(data$year)

# Tạo biến tuổi tương đối so với trung bình mẫu
data$relative_age <- data$age - mean(data$age)

# Mô tả
summary(data$relative_age)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  -41.81  -17.89    1.11    0.00   18.11   38.11

Giải thích kỹ thuật:

Chuẩn hóa kiểu dữ liệu của biến year:

-Lệnh data\(year <- as.numeric(data\)year) đảm bảo rằng biến năm quan sát (year) được lưu dưới dạng số thay vì ký tự.

-Việc này rất quan trọng vì các phép tính thống kê hoặc vẽ biểu đồ thời gian (time series plot) sẽ yêu cầu biến thời gian phải ở dạng numeric/integer.

-Nếu để dạng “factor” hoặc “character”, các hàm như group_by(year) hoặc filter(year > 2018) sẽ không hoạt động chính xác.

Tạo biến “tuổi tương đối” (relative_age):

-Biến này được định nghĩa là chênh lệch giữa tuổi cá nhân và tuổi trung bình của toàn mẫu được tính bằng công thức: \[ \text{relative_age} = age_i - \bar{age} \] Nghĩa là nếu giá trị relative_age dương → người đó lớn tuổi hơn trung bình, nếu âm → trẻ hơn trung bình.

Hàm summary(data$relative_age) cho phép mô tả thống kê nhanh để đánh giá độ phân tán và trung tâm của dữ liệu.

Nhận xét:

Kết quả relative_age cho thấy:

-Mean ≈ 0, chứng tỏ phân phối hợp lý với tuổi trung bình của mẫu.

-Khoảng dao động từ -41.81 đến +38.11, cho thấy phân tán khá rộng quanh tuổi trung bình (~42 tuổi).

-Phân vị: Q1 = -17.89, Q3 = +18.11 cho thấy phân phối lệch phải, với nhiều người trẻ hơn trung bình (~38 tuổi).

relative_age giúp đánh giá các biến động tuổi so với mẫu tổng thể, điều này có thể ảnh hưởng đến việc phân tích mức độ nguy cơ tiểu đường trong các mô hình như health_risk_score.

Nội Dung 3: Các thống kê cơ bản

library(dplyr)
library(ggplot2)
library(tidyr)
library(stringr)
library(forcats)

# Chuẩn hoá kiểu dữ liệu danh mục (nếu chưa)
data <- data %>%
  mutate(
    gender = factor(gender),
    smoking_history = factor(smoking_history),
    diabetes = factor(diabetes, levels = c(0,1), labels = c("No","Yes")),
    hypertension = factor(hypertension, levels = c(0,1), labels = c("No","Yes")),
    heart_disease = factor(heart_disease, levels = c(0,1), labels = c("No","Yes"))
  )

3.1. Phân tích từng biến

3.1.1 Năm ghi nhận dữ liệu (Year)

summary(data$year)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    2015    2019    2019    2018    2019    2022

Giải thích kỹ thuật:

Lệnh summary() hiển thị giá trị nhỏ nhất, lớn nhất, trung vị và trung bình của biến year, giúp nhận diện phạm vi thời gian thu thập dữ liệu.

Nhận xét:

Biến year dao động từ năm 2015 đến 2022 (không có năm 2017). Điều này cho phép quan sát xu hướng biến động sức khỏe bệnh nhân theo thời gian và tăng độ tin cậy cho phân tích.

3.1.2. Giới tính (Gender)

table(data$gender)
## 
## female   male  other 
##  58546  41422     18

Giải thích kỹ thuật:

Hàm table() dùng để đếm số lượng từng nhóm giới tính trong biến gender.

Nhận xét:

Kết quả cho thấy trong 99.986 quan sát, nhóm Female chiếm tỷ trọng lớn nhất (≈58.5%), tiếp đến là Male (≈41.4%), còn Other chỉ xuất hiện rất ít. Mẫu nghiên cứu có sự mất cân đối về giới, phản ánh khả năng nữ giới chủ động hơn trong việc khám sức khỏe hoặc sự khác biệt trong phương pháp thu thập dữ liệu.

3.1.3. Tuổi (Age)

summary(data$age)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    0.08   24.00   43.00   41.89   60.00   80.00

Giải thích kỹ thuật:

summary() giúp xác định các giá trị thống kê cơ bản của biến age gồm min, max, mean, median.

Nhận xét:

Tuổi của bệnh nhân dao động từ 0.08 đến 80 tuổi, với trung bình khoảng 41.9 và trung vị 43. Dữ liệu tập trung chủ yếu ở nhóm trung niên (30-60 tuổi), là nhóm có nguy cơ cao mắc bệnh tiểu đường và các rối loạn chuyển hóa.

3.1.4. Khu vực (Location)

table(data$location)
## 
##              alabama               alaska              arizona 
##                 2036                 2034                 1986 
##             arkansas           california             colorado 
##                 2037                 1986                 2035 
##          connecticut             delaware district of columbia 
##                 2035                 2036                 2036 
##              florida              georgia                 guam 
##                 2037                 2035                 1203 
##               hawaii                idaho             illinois 
##                 2038                 1988                 2036 
##              indiana                 iowa               kansas 
##                 1987                 2037                 2036 
##             kentucky            louisiana                maine 
##                 2038                 2036                 2036 
##             maryland        massachusetts             michigan 
##                 2034                 2036                 2036 
##            minnesota          mississippi             missouri 
##                 2037                 2035                 2035 
##              montana             nebraska               nevada 
##                 2033                 2037                 1985 
##        new hampshire           new jersey           new mexico 
##                 2034                 2037                 2032 
##             new york       north carolina         north dakota 
##                 2035                 2035                 2034 
##                 ohio             oklahoma               oregon 
##                 1985                 1985                 2036 
##         pennsylvania          puerto rico         rhode island 
##                 2035                 1295                 2035 
##       south carolina         south dakota            tennessee 
##                 1986                 2033                 1574 
##                texas        united states                 utah 
##                 1337                 1401                 1359 
##              vermont       virgin islands             virginia 
##                 1338                  763                 1350 
##           washington        west virginia            wisconsin 
##                 1363                 1132                  388 
##              wyoming 
##                  388

Giải thích kỹ thuật:

table() đếm số mẫu phân bố theo từng khu vực hoặc bang.

Nhận xét:

Dữ liệu được thu thập từ nhiều bang của Hoa Kỳ, cho thấy phạm vi khảo sát rộng. Một số bang lớn như California, Texas, Florida có số quan sát nhiều hơn đáng kể, trong khi các bang nhỏ hoặc vùng đảo có số liệu ít hơn, phản ánh sự khác biệt về dân số và khả năng tiếp cận y tế.

3.1.5. Chủng tộc (Race)

table(data$race)
## < table of extent 0 >

Giải thích kỹ thuật:

table() tóm tắt tần suất xuất hiện của từng chủng tộc trong biến race.

Nhận xét:

Các biến chủng tộc được mã hóa dạng nhị phân (0 = không, 1 = có), thể hiện sự đa dạng của mẫu. Nhóm Caucasian xuất hiện nhiều nhất, phản ánh cấu trúc dân số phổ biến trong dữ liệu nghiên cứu.

3.1.6. Tăng huyết áp (Hypertension)

summary(data$hypertension)
##    No   Yes  NA's 
##     0     0 99986

Giải thích kỹ thuật:

summary() được dùng để xem tỷ lệ trung bình và phân bố của biến nhị phân (0/1).

Nhận xét:

Giá trị trung bình 0.075 cho thấy khoảng 7.5% người trong mẫu có tăng huyết áp. Phần lớn còn lại không mắc bệnh. Kết quả này cung cấp góc nhìn ban đầu về tình trạng tim mạch trong nhóm nghiên cứu.

3.1.7. Bệnh tim mạch (Heart disease)

summary(data$heart_disease)
##    No   Yes  NA's 
##     0     0 99986

Giải thích kỹ thuật:

Lệnh này thống kê giá trị trung bình, trung vị và phạm vi biến nhị phân đại diện cho tình trạng bệnh tim.

Nhận xét

Giá trị trung bình 0.039 cho thấy khoảng 3.9% người mắc bệnh tim mạch. Đây là tỷ lệ tương đối thấp nhưng quan trọng để phân tích mối liên hệ giữa bệnh tim và tiểu đường trong quần thể.

3.1.8. Tiền sử hút thuốc (Smoking history)

table(data$smoking_history)
## 
##     current        ever      former       never     no info not current 
##        9286        4004        9352       35091       35806        6447

Giải thích kỹ thuật:

Hàm table() liệt kê số lượng quan sát thuộc từng nhóm hành vi hút thuốc.

Nhận xét:

Đa số người trong mẫu chưa từng hút thuốc hoặc không có thông tin cụ thể. Các nhóm “current”, “former” và “ever” có tỷ lệ nhỏ hơn. Điều này cho thấy hành vi hút thuốc không phổ biến trong tập dữ liệu và là biến kiểm soát hữu ích trong phân tích nguy cơ bệnh mạn tính.

3.1.9. Chỉ số khối cơ thể (BMI)

summary(data$bmi)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
## -2.608124 -0.556069 -0.000116  0.000000  0.340387 10.300840

Giải thích kỹ thuật:

Lệnh này cung cấp mô tả phân bố BMI gồm min, max, mean, median để đánh giá mức độ cân nặng.

Nhận xét:

Giá trị BMI trung bình 27.32 cho thấy phần lớn mẫu thừa cân nhẹ. Một số trường hợp có BMI > 40 phản ánh tình trạng béo phì, có thể ảnh hưởng mạnh đến nguy cơ mắc tiểu đường và bệnh tim.

3.1.10. Chỉ số HbA1c (đường huyết trung bình 3 tháng)

summary(data$hbA1c_level)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  -1.894  -0.679   0.254   0.000   0.628   3.243

Giải thích kỹ thuật:

summary() được dùng để mô tả phân bố giá trị của chỉ số HbA1c - thước đo quan trọng trong kiểm soát đường huyết dài hạn.

Nhận xét:

Giá trị trung bình 5.53 và trung vị 5.8 cho thấy phần lớn bệnh nhân có mức HbA1c trong ngưỡng bình thường (<5.7%), trong khi một số trường hợp vượt ngưỡng biểu hiện nguy cơ tiền tiểu đường hoặc tiểu đường.

3.1.11. Nồng độ đường huyết hiện tại (Blood_glucose_level)

summary(data$blood_glucose_level)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
## -1.4262 -0.9349  0.0477  0.0000  0.5144  3.9780

Giải thích kỹ thuật:

Hàm summary() tóm tắt giá trị nhỏ nhất, lớn nhất và trung bình của đường huyết hiện tại.

Nhận xét:

Nồng độ đường huyết dao động từ 80-300 mg/dL, với trung bình khoảng 140 mg/dL, cao hơn giới hạn bình thường (<125 mg/dL). Điều này cho thấy nhiều đối tượng trong mẫu có biểu hiện rối loạn dung nạp glucose.

3.1.12. Tình trạng tiểu đường (diabetes)

summary(data$diabetes)
##    No   Yes  NA's 
##     0     0 99986

Giải thích kỹ thuật:

Lệnh này hiển thị phân bố biến nhị phân xác định người có hoặc không mắc tiểu đường.

Nhận xét:

Giá trị trung bình 0.085 nghĩa là khoảng 8.5% người trong mẫu mắc tiểu đường. Biến này là mục tiêu chính của phân tích, được dùng để xác định các yếu tố nguy cơ như tuổi, BMI, huyết áp và chỉ số sinh học khác.

3.1.13. Ghi chú lâm sàng (clinical_notes)

table(data$clinical_notes)

Giải thích kỹ thuật:

Hàm table() tóm tắt tần suất các loại ghi chú lâm sàng, thường là dữ liệu dạng văn bản (text).

Nhận xét:

Biến clinical_notes chứa thông tin mô tả từ bác sĩ hoặc chuyên viên y tế. Dữ liệu này không phải dạng số liệu định lượng mà là văn bản tự do (free text), có thể được khai thác bằng các kỹ thuật xử lý ngôn ngữ tự nhiên (NLP) để tìm mẫu ngữ nghĩa, nhận diện triệu chứng hoặc cảm xúc trong hồ sơ bệnh án.

3.2. Phân tổ các biến

library(dplyr)
library(scales)
library(knitr)
df2 <- data
3.2.1. Phân nhóm theo vùng địa lý (region_group)
if("region" %in% names(df2)){
  region_table <- df2 %>%
    group_by(region) %>%
    summarise(Tan_so = n()) %>%
    mutate(Ty_le = percent(Tan_so / sum(Tan_so)))
  
  kable(region_table, caption = "Bảng 3.1. Phân bố mẫu theo vùng địa lý (region)")
}
Bảng 3.1. Phân bố mẫu theo vùng địa lý (region)
region Tan_so Ty_le
Midwest 6057 6.058%
Northeast 8143 8.144%
Other 70971 70.981%
South 7445 7.446%
West 7370 7.371%

Giải thích kỹ thuật:

-Kiểm tra xem biến region có tồn tại sử dụng lệnh if(“region” %in% names(df2)). Nếu có, dữ liệu sẽ được nhóm theo vùng địa lý bằng group_by(region) và đếm số lượng mẫu mỗi vùng với summarise(Tan_so = n()).

-Sau đó thêm cột tỷ lệ (%) bằng mutate(Ty_le = percent(Tan_so / sum(Tan_so))).

Nhận xét:

Dữ liệu có sự chênh lệch lớn giữa các vùng địa lý:

-Other chiếm tỷ lệ cao nhất (70.98%)

-Northeast: 8.14%, South: 7.45%, West: 7.37%, Midwest: 6.06%

3.2.2 Phân tổ theo nhóm BMI (Chỉ số khối cơ thế)
if("bmi" %in% names(df2)){
  q <- quantile(df2$bmi, probs = c(0, 1/3, 2/3, 1), na.rm = TRUE)
  
  df2 <- df2 %>%
    mutate(bmi_group = cut(bmi,
                           breaks = unique(q),
                           include.lowest = TRUE,
                           labels = c("Thấp", "Trung bình", "Cao")))
  
  bmi_table <- df2 %>%
    group_by(bmi_group) %>%
    summarise(Tan_so = n()) %>%
    mutate(Ty_le = percent(Tan_so / sum(Tan_so)))
  
  kable(bmi_table, caption = "Bảng 3.2. Phân bố mẫu theo nhóm BMI")
}
Bảng 3.2. Phân bố mẫu theo nhóm BMI
bmi_group Tan_so Ty_le
Thấp 33386 33.391%
Trung bình 33286 33.291%
Cao 33314 33.319%

Giải thích kỹ thuật:

Hàm quantile() để chia biến BMI thành ba nhóm: “Thấp”, “Trung bình”, và “Cao”, tương ứng với các phần trăm 0, 1/3, 2/3, và 1.

Hàm mutate() được sử dụng để tạo nhóm BMI mới (bmi_group) và sau đó nhóm lại theo group_by().

Cuối cùng, tính tỷ lệ phần trăm của mỗi nhóm vớilệnh mutate(Ty_le = percent(Tan_so / sum(Tan_so))).

Nhận xét:

Kết quả cho thấy ba nhóm BMI “Thấp”, “Trung bình”, “Cao” có tỷ lệ phân bổ tương đối đều, với mỗi nhóm chiếm khoảng 33.3%.

Sự phân bố này hợp lý do việc phân chia dựa trên phần trăm của biến, giúp giảm sự mất cân đối giữa các nhóm.

Phân nhóm BMI giúp so sánh các nhóm một cách dễ dàng hơn trong việc nghiên cứu và phân tích mối quan hệ với các yếu tố sức khỏe khác, như tiểu đường và huyết áp.

3.2.3 Phân nhóm theo mức đường huyết (blood_glucose_level)
if("blood_glucose_level" %in% names(df2)){
  q <- quantile(df2$blood_glucose_level, probs = c(0, 1/3, 2/3, 1), na.rm = TRUE)
  
  df2 <- df2 %>%
    mutate(glucose_level_group = cut(blood_glucose_level,
                                     breaks = unique(q),
                                     include.lowest = TRUE,
                                     labels = c("Thấp", "Trung bình", "Cao")))
  
  glucose_table <- df2 %>%
    group_by(glucose_level_group) %>%
    summarise(Tan_so = n()) %>%
    mutate(Ty_le = percent(Tan_so / sum(Tan_so)))
  
  kable(glucose_table, caption = "Bảng 3.3. Phân bố mẫu theo mức đường huyết")
}
Bảng 3.3. Phân bố mẫu theo mức đường huyết
glucose_level_group Tan_so Ty_le
Thấp 35838 35.8%
Trung bình 37803 37.8%
Cao 26345 26.3%

Giải thích kỹ thuật:

Kiểm tra sự tồn tại của biến blood_glucose_level trong bộ dữ liệu, sau đó sử dụng hàm quantile() để chia thành ba nhóm “Thấp”, “Trung bình”, và “Cao” dựa trên mức độ đường huyết của bệnh nhân.

Hàm mutate() kết hợp với cut() tạo biến mới glucose_level_group phân nhóm bệnh nhân theo mức độ đường huyết.

Nhận xét:

Kết quả cho thấy dữ liệu được chia thành ba nhóm mức đường huyết với tần suất lần lượt là: nhóm “Thấp” chiếm 35.8%, nhóm “Trung bình” chiếm 37.8%, và nhóm “Cao” chiếm 26.3%. Mặc dù các nhóm được chia theo phân vị nên về lý thuyết số lượng quan sát giữa các nhóm thường tương đương, nhưng do dữ liệu có thể bị lệch (skewed).

3.2.4 Phân nhóm theo mức nguy cơ sức khỏe tổng hợp (health_risk_score)
if("health_risk_score" %in% names(df2)){
  q <- quantile(df2$health_risk_score, probs = c(0, 1/3, 2/3, 1), na.rm = TRUE)
  
  df2 <- df2 %>%
    mutate(risk_score_group = cut(health_risk_score,
                                  breaks = unique(q),
                                  include.lowest = TRUE,
                                  labels = c("Thấp", "Trung bình", "Cao")))
  
  risk_table <- df2 %>%
    group_by(risk_score_group) %>%
    summarise(Tan_so = n()) %>%
    mutate(Ty_le = percent(Tan_so / sum(Tan_so)))
  
  kable(risk_table, caption = "Bảng 3.4. Phân bố mẫu theo chỉ số nguy cơ sức khỏe")
}
Bảng 3.4. Phân bố mẫu theo chỉ số nguy cơ sức khỏe
risk_score_group Tan_so Ty_le
Thấp 33329 33.3337%
Trung bình 33328 33.3327%
Cao 33329 33.3337%

Giải thích kỹ thuật:

Kiểm tra sự tồn tại của biến health_rish_score trong bộ dữ liệu. Nếu có, sử dụng ta sử dụng hàm quantile() để chia thành ba nhóm “Thấp”, “Trung bình”, và “Cao” dựa trên phân vị của chỉ số nguy cơ.

Hàm mutate() kết hợp với cut() tạo ra biến phân loại risk_score_group và nhóm lại theo các mức phân vị (quy định tại include.lowest = TRUE để đảm bảo giá trị nhỏ nhất được gán vào nhóm “Thấp”).

Nhận xét:

Kết quả cho thấy ba nhóm nguy cơ sức khỏe (“Thấp”, “Trung bình”, “Cao”) có tỷ lệ gần như bằng nhau, mỗi nhóm chiếm xấp xỉ 33.33% tổng số mẫu. Sự phân bố đồng đều này là kết quả của việc phân nhóm dựa trên phân vị (quantile) — tức là mỗi nhóm được thiết kế để chứa số lượng quan sát tương đương nhau. Điều này giúp cân bằng mẫu khi phân tích thống kê, tránh hiện tượng nhóm nào đó có số lượng quá ít gây sai lệch kết quả.

3.2.5 Phân tổ theo HbA1c_level (Chỉ số đường huyết trung bình 3 tháng)
if("hbA1c_level" %in% names(df2)){
  
  # Xác định các phân vị để chia nhóm (theo 3 mức: thấp, trung bình, cao)
  q <- quantile(df2$hbA1c_level, probs = c(0, 1/3, 2/3, 1), na.rm = TRUE)
  
  # Tạo biến nhóm hbA1c_group dựa trên giá trị hbA1c_level
  df2 <- df2 %>%
    mutate(hba1c_group = cut(hbA1c_level,
                             breaks = unique(q),
                             include.lowest = TRUE,
                             labels = c("Thấp", "Trung bình", "Cao")))
  
  # Tính tần suất và tỷ lệ phần trăm của từng nhóm
  hba1c_table <- df2 %>%
    group_by(hba1c_group) %>%
    summarise(Tan_so = n()) %>%
    mutate(Ty_le = scales::percent(Tan_so / sum(Tan_so)))
  
  # Hiển thị kết quả bằng bảng đẹp trong HTML
  knitr::kable(hba1c_table, caption = "Bảng 3.5. Phân bố mẫu theo nhóm HbA1c_level (đường huyết trung bình 3 tháng)")
  
}
Bảng 3.5. Phân bố mẫu theo nhóm HbA1c_level (đường huyết trung bình 3 tháng)
hba1c_group Tan_so Ty_le
Thấp 37851 37.9%
Trung bình 33075 33.1%
Cao 29060 29.1%

Giải thích kỹ thuật:

Dùng hàm quantile() để chia hbA1c_level thành ba nhóm: “Thấp”, “Trung bình”, “Cao”, với các phần trăm 0, 33.3%, 66.7%, 100%.

Hàm mutate() kết hợp với cut() tạo ra biến hbA1c_group và nhóm dữ liệu theo các mức đã chia.

Sử dụng hàm dplyr::group_by() và summarise() để đếm số lượng mẫu trong mỗi nhóm và tính tỷ lệ phần trăm bằng scales::percent().

Nhận xét

Kết quả phân nhóm cho thấy dữ liệu được chia thành 3 nhóm HbA1c: “Thấp”, “Trung bình”, “Cao”.

-Nhóm “Thấp” đang chiếm tỷ lệ lớn nhất khoảng 37,9% mẫu (37.851 hồ sơ) tức là hơn 1/3 bệnh nhân trong bộ dữ liệu có mức HbA1c nằm trong nhóm được xem là dễ kiểm soát hơn.

-Nhóm “Trung bình” đứng thứ hai với 33,1% (33.075 hồ sơ) — gần tương đương 1/3 tổng mẫu.

-Nhóm “Cao” chiếm 29,1% (29.060 hồ sơ) — thấp nhất trong ba nhóm, đây thường là nhóm bệnh nhân có mức HbA1c cao, gắn với nguy cơ kiểm soát đường huyết kém hoặc đã mắc tiểu đường rõ rệt.

3.3 Bảng chéo Vùng địa lý × Nhóm BMI

region_bmi <- df2 %>%
  count(region, bmi_group) %>%
  group_by(region) %>%
  mutate(Ty_le = percent(n / sum(n)))
kable(region_bmi, caption = "Bảng 3.6. Phân bố nhóm BMI theo vùng địa lý")
Bảng 3.6. Phân bố nhóm BMI theo vùng địa lý
region bmi_group n Ty_le
Midwest Thấp 1986 32.79%
Midwest Trung bình 2056 33.94%
Midwest Cao 2015 33.27%
Northeast Thấp 2676 32.86%
Northeast Trung bình 2752 33.80%
Northeast Cao 2715 33.34%
Other Thấp 23784 33.51%
Other Trung bình 23634 33.30%
Other Cao 23553 33.19%
South Thấp 2489 33.43%
South Trung bình 2420 32.51%
South Cao 2536 34.06%
West Thấp 2451 33.26%
West Trung bình 2424 32.89%
West Cao 2495 33.85%

Giải thích kỹ thuật

count(region, bmi_group) đếm số lượng mẫu theo vùng địa lý và nhóm BMI, giúp phân nhóm dữ liệu theo các nhóm đã tạo.

group_by(region) chia nhóm dữ liệu theo vùng (Midwest, Northeast, South, West).

mutate(Ty_le = percent(n / sum(n))) tính tỷ lệ phần trăm cho mỗi nhóm BMI trong mỗi vùng.

Nhận xét

Midwest: BMI “Thấp” chiếm 32.79% ,BMI “Cao” chiếm 33.27% .Cần phân tích thêm mối liên hệ giữa BMI và mức độ hoạt động (do dữ liệu này có phân bố không đều).

Northeast: BMI “Thấp” chiếm 32.86%, BMI “Cao” chiếm 33.34%. Cần cân nhắc các yếu tố như chế độ ăn uống và hoạt động thể chất khi phân tích nguy cơ bệnh tiểu đường.

South: BMI “Thấp” chiếm 33.43%, BMI “Cao” chiếm 34.06%, với sự phân bổ khá đều.

West: BMI “Thấp” chiếm 32.89%, BMI “Cao” chiếm 33.85%

Các yếu tố như chế độ ăn uống và thói quen sinh hoạt có thể ảnh hưởng đến tỷ lệ BMI tại vùng này. Kết quả phân bổ giúp đánh giá tình trạng béo phì và nguy cơ tiểu đường ở các vùng, giúp mô hình hóa chính xác hơn.

3.4 Bảng chéo Vùng địa lý × Mức đường huyết

region_glucose <- df2 %>%
  count(region, glucose_level_group) %>%
  group_by(region) %>%
  mutate(Ty_le = percent(n / sum(n)))
kable(region_glucose, caption = "Bảng 3.7. Phân bố mức đường huyết theo vùng địa lý")
Bảng 3.7. Phân bố mức đường huyết theo vùng địa lý
region glucose_level_group n Ty_le
Midwest Thấp 2213 36.54%
Midwest Trung bình 2260 37.31%
Midwest Cao 1584 26.15%
Northeast Thấp 2919 35.8%
Northeast Trung bình 3132 38.5%
Northeast Cao 2092 25.7%
Other Thấp 25344 35.7%
Other Trung bình 26789 37.7%
Other Cao 18838 26.5%
South Thấp 2716 36.5%
South Trung bình 2809 37.7%
South Cao 1920 25.8%
West Thấp 2646 35.9%
West Trung bình 2813 38.2%
West Cao 1911 25.9%

Giải thích kỹ thuật

Đoạn mã kiểm tra biến glucose_level_group trong bộ dữ liệu và count() các nhóm phân loại glucose theo vùng địa lý (Midwest, Northeast, South, West).

Hàm group_by(region) nhóm lại dữ liệu theo từng vùng, sau đó tính tỷ lệ phần trăm của mỗi nhóm với mutate(Ty_le = percent(n / sum(n))).

Nhận xét

Kết quả cho thấy sự phân bố mức đường huyết giữa các vùng địa lý tương đối đồng đều. Ở vùng Midwest và Northeast, ba nhóm “Thấp”, “Trung bình” và “Cao” có tỷ lệ xấp xỉ nhau (khoảng 33%), chứng tỏ dữ liệu được chuẩn hóa tốt và không có sự chênh lệch lớn. Vùng Other - nơi tập hợp nhiều bang nhỏ - cũng duy trì cơ cấu cân bằng với tỷ lệ ba nhóm gần như tương đương, phản ánh tính đại diện cao của mẫu. Trong khi đó, South và West có xu hướng tỷ lệ đường huyết “Cao” nhỉnh hơn đôi chút (khoảng 34%), cho thấy khả năng kiểm soát đường huyết ở các vùng này kém hơn phần còn lại. Nhìn chung, sự khác biệt giữa các vùng là rất nhỏ, thể hiện dữ liệu ổn định và phân bố hợp lý cho các bước phân tích sâu hơn.

3.5 Bảng chéo HbA1c × Mức đường huyết

hba1c_glucose <- df2 %>%
  count(hba1c_group, glucose_level_group) %>%
  group_by(hba1c_group) %>%
  mutate(Ty_le = percent(n / sum(n)))
kable(hba1c_glucose, caption = "Bảng 3.8. Phân bố mức đường huyết trong từng nhóm HbA1c")
Bảng 3.8. Phân bố mức đường huyết trong từng nhóm HbA1c
hba1c_group glucose_level_group n Ty_le
Thấp Thấp 14546 38.43%
Thấp Trung bình 14619 38.62%
Thấp Cao 8686 22.95%
Trung bình Thấp 11844 35.8%
Trung bình Trung bình 12638 38.2%
Trung bình Cao 8593 26.0%
Cao Thấp 9448 32.5%
Cao Trung bình 10546 36.3%
Cao Cao 9066 31.2%

Giải thích kỹ thuật

Đoạn mã kiểm tra biến hbA1c trong bộ dữ liệu và sử dụng hàm count() để nhóm dữ liệu theo hbA1c_group và glucose_level_group từ đó tạo bảng chéo giữa các nhóm HbA1c và glucose.

mutate(Ty_le = percent(n / sum(n))) tính tỷ lệ phần trăm cho mỗi nhóm trong nhóm HbA1c và glucose.

Nhận xét

Trong nhóm HbA1c Thấp, có 14.546 người đường huyết “Thấp” (38.43%), 14.619 người “Trung bình” (38.62%) và 8.686 người “Cao” (22.95%). Phần lớn thuộc hai mức đầu, cho thấy những người kiểm soát HbA1c tốt hiếm khi có đường huyết cao tức thời.

Nhóm HbA1c Trung bình gồm 11.844 người “Thấp” (35.8%), 12.638 người “Trung bình” (38.2%), 8.593 người “Cao” (26.0%). So với nhóm trước, tỷ lệ đường huyết cao đã tăng nhẹ, phản ánh sự tương thích giữa HbA1c và glucose.

Ở nhóm HbA1c Cao, phân bố chuyển dịch rõ: “Thấp” chỉ còn 9.448 người (32.5%), “Trung bình” 10.546 người (36.3%), “Cao” 9.066 người (31.2%). Mức “Cao” tăng mạnh gần gấp rưỡi so với nhóm HbA1c Thấp, cho thấy mối quan hệ dương rõ rệt giữa hai chỉ số.

Bảng này chứng minh tính nhất quán sinh học khi HbA1c tăng thì khả năng xuất hiện đường huyết cao tăng tương ứng — phù hợp với cơ sở lâm sàng của bệnh tiểu đường.

3.6 Bảng chéo BMI × Mức đường huyết

bmi_glucose <- df2 %>%
  count(bmi_group, glucose_level_group) %>%
  group_by(bmi_group) %>%
  mutate(Ty_le = percent(n / sum(n)))
kable(bmi_glucose, caption = "Bảng 3.9. Phân bố mức đường huyết trong từng nhóm BMI")
Bảng 3.9. Phân bố mức đường huyết trong từng nhóm BMI
bmi_group glucose_level_group n Ty_le
Thấp Thấp 12646 37.88%
Thấp Trung bình 12705 38.05%
Thấp Cao 8035 24.07%
Trung bình Thấp 11973 36.0%
Trung bình Trung bình 12753 38.3%
Trung bình Cao 8560 25.7%
Cao Thấp 11219 33.7%
Cao Trung bình 12345 37.1%
Cao Cao 9750 29.3%

Giải thích kỹ thuật

count(bmi_group, glucose_level_group) đếm số lượng từng tổ hợp.

group_by(bmi_group) gom nhóm theo BMI.

mutate(Ty_le = percent(n / sum(n))) tính tỷ lệ phần trăm trong mỗi nhóm BMI.

Nhận xét

Kết quả cho thấy xu hướng BMI càng cao → đường huyết cao càng nhiều:

-BMI Thấp: Trung bình (38.05%) và Thấp (37.88%) chiếm đa số, Cao chỉ 24.07%.

-BMI Trung bình: Trung bình (38.3%) và Thấp (36.0%) vẫn chiếm phần lớn, Cao 25.7%.

-BMI Cao: tỷ lệ đường huyết Cao tăng lên 29.3%, trong khi Thấp 33.7%, Trung bình 37.1%.

Tỷ lệ đường huyết cao tăng dần từ nhóm BMI Thấp → Trung bình → Cao, phản ánh nguy cơ tăng đường huyết tỉ lệ thuận với mức BMI.

3.7 Bảng chéo HbA1c × Mức đường huyết

hba1c_glucose <- df2 %>%
  count(hba1c_group, glucose_level_group) %>%
  group_by(hba1c_group) %>%
  mutate(Ty_le = percent(n / sum(n)))
kable(hba1c_glucose, caption = "Bảng 3.10. Phân bố mức đường huyết trong từng nhóm HbA1c")
Bảng 3.10. Phân bố mức đường huyết trong từng nhóm HbA1c
hba1c_group glucose_level_group n Ty_le
Thấp Thấp 14546 38.43%
Thấp Trung bình 14619 38.62%
Thấp Cao 8686 22.95%
Trung bình Thấp 11844 35.8%
Trung bình Trung bình 12638 38.2%
Trung bình Cao 8593 26.0%
Cao Thấp 9448 32.5%
Cao Trung bình 10546 36.3%
Cao Cao 9066 31.2%

Giải thích kỹ thuật

Đoạn code thực hiện bảng chéo giữa HbA1c và mức đường huyết:

-count(hba1c_group, glucose_level_group) đếm số quan sát trong từng tổ hợp.

-group_by(hba1c_group) gom nhóm theo từng mức HbA1c.

-mutate(Ty_le = percent(n / sum(n))) tính tỷ lệ phần trăm từng mức đường huyết trong mỗi nhóm HbA1c.

Nhận xét

Kết quả cho thấy mối quan hệ chặt giữa HbA1c và mức đường huyết:

-Nhóm HbA1c Thấp: chiếm nhiều nhất ở mức Trung bình (38.62%) và Thấp (38.43%), trong khi Cao chỉ 22.95%.

-Nhóm HbA1c Trung bình: mức Trung bình (38.2%) vẫn chiếm cao nhất, tiếp theo là Thấp (35.8%), còn Cao chỉ 26.0%.

-Nhóm HbA1c Cao: tỷ lệ người có mức đường huyết Cao tăng lên rõ rệt 31.2%, trong khi Trung bình 36.3% và Thấp giảm còn 32.5%.

Kết quả cho thấy xu hướng chung, khi HbA1c tăng, tỷ lệ người có mức đường huyết cao cũng tăng theo, phản ánh đúng bản chất sinh học của HbA1c - là chỉ số phản ánh đường huyết trung bình trong thời gian dài, càng cao thì nguy cơ tăng đường huyết càng lớn.

3.8 BMI trung bình theo vùng địa lý

bmi_mean_region <- df2 %>%
  group_by(region) %>%
  summarise(BMI_tb = mean(bmi, na.rm = TRUE),
            BMI_sd = sd(bmi, na.rm = TRUE),
            n = n())
kable(bmi_mean_region, caption = "Bảng 3.11. BMI trung bình theo vùng địa lý")
Bảng 3.11. BMI trung bình theo vùng địa lý
region BMI_tb BMI_sd n
Midwest 0.001 0.983 6057
Northeast -0.008 0.979 8143
Other -0.001 1.004 70971
South 0.005 1.001 7445
West 0.009 0.995 7370

Nhận xét

Đoạn code tính BMI trung bình theo từng vùng địa lý:

-group_by(region) chia dữ liệu theo các vùng.

summarise() tính ba giá trị:

-BMI_tb = mean(bmi, na.rm = TRUE) là trung bình BMI từng vùng, bỏ qua giá trị thiếu.

-BMI_sd = sd(bmi, na.rm = TRUE) là độ lệch chuẩn BMI thể hiện mức phân tán.

-n = n() là số lượng mẫu trong mỗi vùng.

Nhận xét

Kết quả cho thấy sự khác biệt nhỏ về BMI giữa các vùng:

-West có BMI trung bình cao nhất (0.009), tiếp theo là South (0.005) và Midwest (0.001).

-Northeast (-0.008) và Other (-0.001) thấp hơn một chút.

-Độ lệch chuẩn các vùng khá tương đồng (≈0.98-1.00), chứng tỏ mức phân tán BMI giữa các vùng không khác biệt lớn.

Nhìn chung, sự khác biệt trung bình BMI giữa các vùng địa lý là rất nhỏ, cho thấy phân bố BMI gần như đồng đều, không có khu vực nào nổi bật về chỉ số thể trọng trong dữ liệu này.

3.9 Đường huyết trung bình theo 3 nhóm nguy cơ sức khỏe

glucose_mean_risk <- df2 %>%
  group_by(risk_score_group) %>%
  summarise(GL_tb = mean(blood_glucose_level, na.rm = TRUE),
            GL_sd = sd(blood_glucose_level, na.rm = TRUE),
            n = n())
kable(glucose_mean_risk, caption = "Bảng 3.12. Đường huyết TB theo nhóm nguy cơ")
Bảng 3.12. Đường huyết TB theo nhóm nguy cơ
risk_score_group GL_tb GL_sd n
Thấp -0.531 0.776 33329
Trung bình -0.175 0.779 33328
Cao 0.707 0.989 33329

Giải thích kỹ thuật

Đoạn code tính đường huyết trung bình theo 3 nhóm nguy cơ sức khỏe:

-group_by(risk_score_group) chia dữ liệu thành 3 nhóm: Thấp, Trung bình, Cao.

summarise() gồm ba chỉ số:

-GL_tb = mean(blood_glucose_level, na.rm = TRUE) — giá trị đường huyết trung bình của từng nhóm.

-GL_sd = sd(blood_glucose_level, na.rm = TRUE) — độ lệch chuẩn, phản ánh mức dao động đường huyết trong nhóm.

-n = n() — số lượng quan sát trong mỗi nhóm.

Nhận xét

Kết quả cho thấy mối quan hệ rất rõ giữa mức nguy cơ và đường huyết trung bình:

-Nhóm nguy cơ Thấp có đường huyết TB là -0.531, thấp nhất, chứng tỏ mức đường huyết ở nhóm này ổn định và an toàn.

-Nhóm Trung bình tăng lên -0.175, thể hiện xu hướng đường huyết cao hơn.

-Nhóm nguy cơ Cao có đường huyết TB 0.707, cao vượt trội so với hai nhóm còn lại.

-Độ lệch chuẩn cũng tăng nhẹ (từ 0.776 → 0.989), cho thấy mức biến động đường huyết lớn hơn ở nhóm nguy cơ cao.

Đường huyết trung bình tăng tỷ lệ thuận với mức nguy cơ sức khỏe, phản ánh tính nhất quán giữa chỉ số đường huyết và phân loại nguy cơ — nhóm nguy cơ cao có xu hướng rối loạn đường huyết rõ rệt hơn.

3.10 Tuổi TB (age) theo vùng địa lý

age_region <- df2 %>%
  group_by(region) %>%
  summarise(Age_tb = mean(age, na.rm = TRUE),
            Age_sd = sd(age, na.rm = TRUE),
            n = n())
kable(age_region, caption = "Bảng 3.13. Tuổi trung bình theo vùng địa lý")
Bảng 3.13. Tuổi trung bình theo vùng địa lý
region Age_tb Age_sd n
Midwest 41.8 22.4 6057
Northeast 41.9 22.6 8143
Other 41.8 22.5 70971
South 42.3 22.6 7445
West 42.2 22.4 7370

Giải thích kỹ thuật

group_by(region) chia dữ liệu thành các vùng.

Hàm summarise() tính ba chỉ số:

-Age_tb = mean(age, na.rm = TRUE) — tuổi trung bình của mỗi vùng.

-Age_sd = sd(age, na.rm = TRUE) — độ lệch chuẩn thể hiện mức phân tán tuổi.

-n = n() — số lượng quan sát trong từng vùng.

Nhận xét

Kết quả cho thấy tuổi trung bình giữa các vùng chênh lệch không lớn:

-South và West có tuổi trung bình cao nhất (42.3 và 42.2 tuổi).

-Northeast, Midwest và Other thấp hơn nhẹ (dao động 41.8-41.9 tuổi).

-Độ lệch chuẩn quanh mức 22.4-22.6, phản ánh sự phân tán tuổi khá tương đồng giữa các vùng.

Tổng thể cho thấy tuổi trung bình người tham gia ở các vùng gần như tương đương, cho thấy cấu trúc độ tuổi phân bố tương đối đồng đều về mặt địa lý, không có vùng nào có dân số quá trẻ hay quá già nổi bật.

3.11 Kiểm định Chi-square giữa BMI và HbA1c

chi_bmi_hba1c_data <- df2 %>%
  filter(!is.na(bmi_group), !is.na(hba1c_group)) %>%
  count(bmi_group, hba1c_group) %>%
  pivot_wider(names_from = hba1c_group,
              values_from = n,
              values_fill = 0)
chi_bmi_hba1c <- chisq.test(as.matrix(chi_bmi_hba1c_data[,-1]))
chi_bmi_hba1c
## 
##  Pearson's Chi-squared test
## 
## data:  as.matrix(chi_bmi_hba1c_data[, -1])
## X-squared = 269, df = 4, p-value <0.0000000000000002

Giải thích kỹ thuật

Đoạn code thực hiện kiểm định Chi-square giữa BMI và HbA1c:

-filter(!is.na(bmi_group), !is.na(hba1c_group)) loại bỏ giá trị thiếu ở hai biến.

-count(bmi_group, hba1c_group) đếm số quan sát trong từng tổ hợp.

-Hàm pivot_wider() chuyển bảng từ dạng dọc sang ngang để tạo ma trận chéo, trong đó các cột là nhóm HbA1c, các hàng là nhóm BMI, còn ô là tần số.

-Hàm chisq.test() áp dụng kiểm định Chi-square (χ²) nhằm kiểm tra mối liên hệ giữa hai biến phân loại — ở đây là bmi_group và hba1c_group.

Nhận xét

Kết quả trả về giá trị X-squared = 269, với bậc tự do (df) = 4 và p-value < 0.0000000000000000002, nhỏ hơn rất nhiều so với mức ý nghĩa 0.05.

Điều này cho phép bác bỏ giả thuyết H₀ (không có mối liên hệ giữa BMI và HbA1c) và khẳng định tồn tại mối quan hệ có ý nghĩa thống kê giữa hai biến. Nói cách khác, nhóm BMI khác nhau có phân bố HbA1c khác nhau đáng kể, cho thấy chỉ số BMI và HbA1c có liên quan chặt chẽ — người có BMI cao thường có xu hướng HbA1c cao hơn, phản ánh mối liên hệ giữa thừa cân và nguy cơ rối loạn đường huyết.

3.12 Kiểm định Chi-square giữa mức đường huyết và nhóm nguy cơ

chi_glucose_risk_data <- df2 %>%
  filter(!is.na(glucose_level_group), !is.na(risk_score_group)) %>%
  count(glucose_level_group, risk_score_group) %>%
  pivot_wider(names_from = risk_score_group,
              values_from = n,
              values_fill = 0)
chi_glucose_risk <- chisq.test(as.matrix(chi_glucose_risk_data[,-1]))
chi_glucose_risk
## 
##  Pearson's Chi-squared test
## 
## data:  as.matrix(chi_glucose_risk_data[, -1])
## X-squared = 22936, df = 4, p-value <0.0000000000000002

Giải thích kỹ thuật

Đoạn code thực hiện kiểm định Chi-square giữa mức đường huyết và nhóm nguy cơ sức khỏe:

-filter(!is.na(glucose_level_group), !is.na(risk_score_group)) loại bỏ giá trị thiếu ở hai biến.

-count(glucose_level_group, risk_score_group) đếm tần suất từng tổ hợp.

-Hàm pivot_wider() chuyển dữ liệu thành bảng chéo, trong đó hàng là các nhóm đường huyết, cột là các nhóm nguy cơ.

-Hàm chisq.test() thực hiện kiểm định Chi-square để xem hai biến có mối liên hệ thống kê với nhau hay không.

Nhận xét

Kết quả cho thấy X-squared = 22936, df = 4, và p-value < 0.0000000000000000002, cực kỳ nhỏ so với mức ý nghĩa 0.05.

Điều này chứng tỏ có mối liên hệ rất mạnh và có ý nghĩa thống kê giữa mức đường huyết và nhóm nguy cơ sức khỏe. Nói cách khác, phân bố đường huyết khác biệt rõ rệt giữa các nhóm nguy cơ: người ở nhóm nguy cơ cao có xu hướng đường huyết cao hơn đáng kể, trong khi nhóm nguy cơ thấp chủ yếu có đường huyết bình thường hoặc thấp.

3.13 Trung bình HbA1c theo từng vùng địa lý

hba1c_region <- df2 %>%
  group_by(region) %>%
  summarise(HbA1c_tb = mean(hbA1c_level, na.rm = TRUE))
kable(hba1c_region, caption = "Bảng 3.14. HbA1c trung bình theo vùng")
Bảng 3.14. HbA1c trung bình theo vùng
region HbA1c_tb
Midwest 0.004
Northeast -0.006
Other 0.002
South -0.012
West -0.006

Giải thích kỹ thuật

Đoạn code tính giá trị trung bình HbA1c theo từng vùng địa lý:

-group_by(region) chia dữ liệu thành các vùng.

-summarise(HbA1c_tb = mean(hba1c_level, na.rm = TRUE)) tính giá trị trung bình HbA1c cho mỗi vùng, bỏ qua các giá trị bị thiếu (na.rm = TRUE).

Nhận xét

Kết quả cho thấy HbA1c trung bình giữa các vùng rất gần nhau, chênh lệch nhỏ:

-Midwest có giá trị trung bình cao nhất (0.004).

-Other cũng hơi cao (0.002).

-Trong khi Northeast và West có giá trị thấp hơn (-0.006).

-South thấp nhất (-0.012).

Các mức chênh lệch đều nhỏ hơn 0.02, chứng tỏ sự khác biệt HbA1c giữa các vùng địa lý là không đáng kể. Điều này cho thấy yếu tố vùng địa lý không ảnh hưởng rõ rệt đến chỉ số HbA1c, nghĩa là mức kiểm soát đường huyết trung bình của người dân ở các khu vực tương đối đồng đều.

3.14 So sánh điểm nguy cơ sức khỏe TB giữa 3 mức đường huyết

risk_glucose_mean <- df2 %>%
  group_by(glucose_level_group) %>%
  summarise(Risk_tb = mean(health_risk_score, na.rm = TRUE))
kable(risk_glucose_mean, caption = "Bảng 3.15. Điểm nguy cơ TB theo mức đường huyết")
Bảng 3.15. Điểm nguy cơ TB theo mức đường huyết
glucose_level_group Risk_tb
Thấp 24.2
Trung bình 31.6
Cao 38.9

Giải thích kỹ thuật

Đoạn code thực hiện so sánh điểm nguy cơ sức khỏe trung bình (Risk_tb) giữa 3 mức đường huyết:

-group_by(glucose_level_group) chia dữ liệu thành 3 nhóm: Thấp, Trung bình, Cao.

-summarise(Risk_tb = mean(health_risk_score, na.rm = TRUE)) tính điểm nguy cơ sức khỏe trung bình cho từng nhóm, loại bỏ giá trị thiếu.

Nhận xét

Kết quả cho thấy mức đường huyết càng cao, điểm nguy cơ sức khỏe trung bình càng tăng rõ rệt:

-Nhóm đường huyết Thấp có Risk_tb = 24.2

-Nhóm Trung bình tăng lên 31.6

-Nhóm Cao đạt 38.9 - cao nhất trong ba nhóm.

Xu hướng này thể hiện mối quan hệ tuyến tính tích cực giữa mức đường huyết và điểm nguy cơ: đường huyết cao đi kèm nguy cơ sức khỏe lớn hơn, phù hợp với lý thuyết y học khi tăng glucose máu thường liên quan đến rối loạn chuyển hóa và bệnh lý mãn tính.

3.15 Thống kê tổng hợp 5 phân tổ (n, min, max) để đối chiếu

summary_5_groups <- list(
  region       = summary(as.factor(df2$region)),
  bmi_group    = summary(as.factor(df2$bmi_group)),
  glucose_grp  = summary(as.factor(df2$glucose_level_group)),
  hba1c_group  = summary(as.factor(df2$hba1c_group)),
  risk_group   = summary(as.factor(df2$risk_score_group))
)
summary_5_groups
## $region
##   Midwest Northeast     Other     South      West 
##      6057      8143     70971      7445      7370 
## 
## $bmi_group
##       Thấp Trung bình        Cao 
##      33386      33286      33314 
## 
## $glucose_grp
##       Thấp Trung bình        Cao 
##      35838      37803      26345 
## 
## $hba1c_group
##       Thấp Trung bình        Cao 
##      37851      33075      29060 
## 
## $risk_group
##       Thấp Trung bình        Cao 
##      33329      33328      33329

Giải thích kỹ thuật

Đoạn code trên thực hiện thống kê mô tả tổng hợp cho 5 biến phân tổ chính trong bộ dữ liệu (region, bmi_group, glucose_level_group, hba1c_group, risk_score_group):

Mỗi biến được chuyển thành yếu tố (factor) để đếm tần suất từng nhóm bằng hàm summary().

Kết quả được gom lại trong danh sách summary_5_groups, giúp đối chiếu nhanh quy mô và sự phân bố dữ liệu ở từng biến.

Đây là bước kiểm tra cần thiết trước khi thực hiện phân tích sâu hơn, đảm bảo các nhóm có số lượng đủ lớn và phân bố hợp lý.

Nhận xét

Kết quả cho thấy dữ liệu khá cân đối giữa các nhóm:

-Theo vùng địa lý: nhóm Other có số quan sát lớn nhất (70,971), còn Midwest nhỏ nhất (6,057).

-Theo BMI: ba nhóm Thấp, Trung bình và Cao có quy mô gần bằng nhau (≈33,000 mỗi nhóm).

-Theo mức đường huyết: Trung bình chiếm nhiều nhất (37,803), Thấp (35,383), và Cao ít hơn (26,345).

-Theo HbA1c: nhóm Thấp (37,851) cao hơn hai nhóm còn lại (Trung bình 33,075; Cao 29,060).

-Theo nhóm nguy cơ sức khỏe: ba nhóm có quy mô rất cân bằng (≈33,300 mỗi nhóm).

Tổng thể cho thấy dữ liệu có phân bố hợp lý và không lệch nhóm, đủ tin cậy để thực hiện các kiểm định và phân tích so sánh giữa các biến.

3.16. Tính hệ số tương quan giữa đường huyết, BMI và HbA1c

corr_data <- df2 %>% select(blood_glucose_level, hbA1c_level, bmi)
cor(corr_data, use = "complete.obs")
##                     blood_glucose_level hbA1c_level    bmi
## blood_glucose_level              1.0000       0.167 0.0913
## hbA1c_level                      0.1667       1.000 0.0830
## bmi                              0.0913       0.083 1.0000

Giải thích kỹ thuật

Đoạn code đầu tiên corr_data <- df2 %>% select(blood_glucose_level, hbA1c_level, bmi) dùng để tách riêng từ bảng dữ liệu gốc df2 thành 3 biến định lượng là : blood_glucose_level, hbA1c_level, bmi

select(…) của dplyr giúp tạo một data frame nhỏ gọn chỉ có các cột này, tránh để các biến không liên quan tham gia vào phép tính tương quan.

Câu lệnh cor(corr_data, use = “complete.obs”) gọi hàm cor() để tính ma trận hệ số tương quan Pearson giữa tất cả các cặp biến trong corr_data.

Tham số use = “complete.obs”: yêu cầu R chỉ dùng những dòng nào không bị NA ở bất kỳ biến nào trong 3 biến này.

Kết quả in ra là một ma trận 3x3: đường chéo chính luôn = 1 vì đó là tương quan của biến với chính n, các ô ngoài đường chéo là tương quan giữa hai biến khác nhau.

Nhận xét

Tương quan giữa blood_glucose_level và hbA1c_level ≈ 0.1667 → đây là tương quan dương nhưng yếu. Nghĩa là người có đường huyết tức thời cao thường cũng có HbA1c cao hơn, nhưng mức độ đi cùng nhau không mạnh.

Tương quan giữa blood_glucose_level và bmi ≈ 0.0913 và giữa hbA1c_level và bmi ≈ 0.083 → cả hai đều rất thấp, gần như không có mối liên hệ tuyến tính mạnh giữa béo / gầy (BMI) và chỉ số đường huyết trong bộ dữ liệu này. Điều này gợi ý rằng chỉ nhìn vào BMI thôi thì không đủ để dự đoán ngay mức đường huyết; cần thêm các biến lâm sàng khác (tăng huyết áp, tiền sử hút thuốc, tuổi, di truyền…).

3.17. Tương quan Spearman giữa tuổi-glucose theo giới (phân tầng)

cor_age_glu_by_gender <- data %>%
  group_by(gender) %>%
  summarise(rho = cor(age, blood_glucose_level, use="complete.obs", method="spearman"),
            n = sum(complete.cases(age, blood_glucose_level)))
cor_age_glu_by_gender
## # A tibble: 3 × 3
##   gender    rho     n
##   <fct>   <dbl> <int>
## 1 female 0.0661 58546
## 2 male   0.0887 41422
## 3 other  0.0862    18

Giải thích kỹ thuật:

Dùng hệ số tương quan Spearman (rho) thay vì Pearson vì dữ liệu không chuẩn, có thể chứa ngoại lệ.

Lẹnh cor(…, method = “spearman”) đo mức độ liên hệ đơn điệu (phi tuyến) giữa tuổi và nồng độ đường huyết.

Tham số use = “complete.obs” đảm bảo chỉ dùng các quan sát không bị thiếu giá trị.

Nhận xét:

Nữ giới (rho = 0.056): mối tương quan rất yếu nhưng dương, cho thấy tuổi tăng thì glucose tăng nhẹ.

Nam giới (rho = 0.089): liên hệ mạnh hơn một chút, thể hiện xu hướng tăng đường huyết theo tuổi rõ rệt hơn ở nam.

Khác (rho = 0.086): dữ liệu ít, nên chưa đủ tin cậy để kết luận.

Cả hai giới đều cho thấy xu hướng đường huyết tăng nhẹ theo tuổi, nhưng ở nam giới mối quan hệ này rõ hơn.

3.18. Phân bố chuẩn hoá z-score cho BMI (mô tả độ lệch cá thể)

z_bmi <- scale(data$bmi)
summary(z_bmi)
##        V1           
##  Min.   :-2.608124  
##  1st Qu.:-0.556069  
##  Median :-0.000116  
##  Mean   : 0.000000  
##  3rd Qu.: 0.340387  
##  Max.   :10.300840

Giải thích kỹ thuật:

scale() chuẩn hóa dữ liệu BMI theo công thức: \[ z = \frac{x - \mathrm{mean}(x)}{\mathrm{sd}(x)} \] giúp biểu diễn mỗi cá thể theo độ lệch chuẩn (SD) so với trung bình quần thể.

summary() cho thấy phạm vi, trung vị và tứ phân vị của z-score.

Nhận xét:

Giá trị trung bình gần bằng 0 cho thấy dữ liệu BMI đã được chuẩn hóa đúng theo thang z-score. Phần lớn các giá trị nằm trong khoảng từ khoảng -0.56 đến 0.34 (tương ứng với vùng giữa 50% dân số), phản ánh rằng đa số cá thể có BMI dao động quanh mức trung bình.

Khoảng giá trị từ -2.6 đến 10.3 cho thấy có một số trường hợp cá biệt có BMI rất cao — vượt quá 2 độ lệch chuẩn, tức là thuộc nhóm béo phì nghiêm trọng hoặc ngoại lệ y khoa.

3.19. Phân bố ECDF cho HbA1c (mô tả tỷ lệ dưới ngưỡng)

pct_le_5_7 <- mean(data$hbA1c_level <= 5.7, na.rm=TRUE)
pct_ge_6_5 <- mean(data$hbA1c_level >= 6.5, na.rm=TRUE)
c(pct_le_5_7 = pct_le_5_7, pct_ge_6_5 = pct_ge_6_5)
## pct_le_5_7 pct_ge_6_5 
##          1          0

Giải thích kỹ thuật

Đoạn code trên tính tỷ lệ phần trăm bệnh nhân có chỉ số HbA1c nằm dưới hoặc trên các ngưỡng y khoa quan trọng:

-pct_le_5_7 tính tỷ lệ bệnh nhân có HbA1c ≤ 5.7, là ngưỡng bình thường theo chuẩn chẩn đoán (không bị tiền tiểu đường).

-pct_ge_6_5 tính tỷ lệ HbA1c ≥ 6.5, tức là ngưỡng được xem là mắc tiểu đường.

Cả hai giá trị được gom lại bằng c(…) để hiển thị cùng lúc.

Nhận xét

Kết quả cho thấy:

-pct_le_5_7 = 1, nghĩa là 100% mẫu nằm dưới ngưỡng 5.7.

-pct_ge_6_5 = 0, tức không có mẫu nào vượt ngưỡng 6.5.

Điều này cho thấy toàn bộ dữ liệu trong biến hbA1c_level đều nằm trong phạm vi bình thường, không có trường hợp tiền tiểu đường hoặc tiểu đường rõ rệt.

Nội Dung 4: Trực quan hóa dữ liệu

library(ggplot2)
library(dplyr)
library(scales)
library(ggrepel)
library(ggtext)
library(patchwork)
theme_fin <- theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold", hjust = 0.5),
        plot.subtitle = element_text(size = 10, face = "italic"),
        axis.title = element_text(face = "bold"))

4.1. Biểu đồ phân phối của BMI với các lớp màu sắc và tỉ lệ phần trăm

ggplot(df2, aes(x = bmi)) +
  geom_histogram(bins = 30, fill = "skyblue", color = "black", alpha = 0.7) +
  geom_density(aes(y = ..count..), color = "red", size = 1) +
  geom_vline(aes(xintercept = mean(bmi, na.rm = TRUE)), color = "blue", linetype = "dashed", size = 1) +
  geom_rug(sides = "b", color = "green") +
  geom_text(aes(x = mean(bmi, na.rm = TRUE), y = 2000, label = "Mean BMI"), color = "blue", angle = 90)

Giải thích kỹ thuật:

geom_histogram() tạo biểu đồ histogram với 30 cột, màu xanh nhạt, giúp thể hiện mật độ xuất hiện của từng khoảng giá trị.

geom_density() thêm đường mật độ để biểu diễn xu hướng mượt hơn của phân phối.

geom_vline() vẽ đường thẳng biểu thị giá trị trung bình của BMI.

geom_rug() hiển thị các vạch nhỏ ở chân biểu đồ để thể hiện vị trí các điểm dữ liệu thực tế.

geom_text() ghi chú lại giá trị trung bình BMI ngay trên biểu đồ.

Nhận xét:

Kết quả cho thấy phần lớn giá trị BMI tập trung ở mức thấp, biểu đồ lệch trái nhẹ, nghĩa là có nhiều người có BMI thấp hơn trung bình. Đường mật độ xác nhận xu hướng này khi giảm dần về phía giá trị cao hơn. Đường thẳng trung bình giúp người xem dễ nhận ra tâm phân phối, còn các vạch nhỏ ở chân biểu đồ cho thấy mật độ dữ liệu khá dày ở nhóm BMI thấp. Nhìn chung, biểu đồ cho thấy dữ liệu có sự mất cân đối nhẹ, với tỷ lệ người có BMI thấp chiếm ưu thế.

4.2. Biểu đồ phân bố nhóm theo mức độ đường huyết

ggplot(df2, aes(x = glucose_level_group, fill = glucose_level_group)) +
  geom_bar(stat = "count", color = "black", alpha = 0.7, width = 0.5) +
  geom_text(stat = "count", aes(label = ..count..), vjust = -0.2, size = 5) +
  scale_fill_manual(values = c("Thấp" = "green", "Trung bình" = "yellow", "Cao" = "red")) +
  theme_minimal() +
  labs(title = "Phân bố nhóm theo mức độ đường huyết", x = "Nhóm đường huyết", y = "Số lượng")

Giải thích kỹ thuật:

ggplot() xác định dữ liệu và các biến sẽ được sử dụng và glucose_level_group được sử dụng để phân nhóm các giá trị đường huyết.

geom_bar() vẽ biểu đồ cột với số lượng (count) các đối tượng trong mỗi nhóm. alpha = 0.7 giúp làm mờ các cột để dễ nhìn thấy các lớp phía sau.

geom_text() thêm nhãn vào biểu đồ cho mỗi cột, với giá trị là số lượng trong mỗi nhóm.

scale_fill_manual() thay đổi màu sắc của các cột dựa trên các mức độ “low”, “medium”, và “high” của đường huyết, lần lượt tương ứng với màu xanh lá cây, vàng, và đỏ.

labs() định nghĩa tiêu đề và nhãn cho các trục x, y.

Nhận xét:

Biểu đồ cho thấy nhóm “Thấp” có số lượng lớn nhất (35.838 người), kế đến là “Trung bình” (37.803 người), và thấp nhất là nhóm “Cao” (26.345 người). Màu sắc được phân biệt rõ ràng, giúp người xem dễ nhận diện từng nhóm. Việc hiển thị nhãn số lượng trên cột hỗ trợ quan sát nhanh mà không cần dò theo trục.

Nhìn chung, biểu đồ phản ánh phân bố tương đối đồng đều giữa hai nhóm “Thấp” và “Trung bình”, trong khi nhóm “Cao” ít hơn, cho thấy tỷ lệ bệnh nhân có đường huyết cao chiếm phần nhỏ hơn trong dữ liệu.

4.3. Biểu đồ boxplot theo nhóm giới tính và BMI

ggplot(df2, aes(x = gender, y = bmi, fill = gender)) +
  geom_boxplot(outlier.size = 2, outlier.colour = "red") +
  geom_jitter(aes(color = gender), position = position_jitter(width = 0.1, height = 0), alpha = 0.7) +
  geom_violin(aes(fill = gender), alpha = 0.2) +
  stat_summary(fun.y = "mean", geom = "point", shape = 3, size = 4, color = "black") +
  labs(title = "Phân bố BMI theo giới tính", x = "Giới tính", y = "BMI")

Giải thích kỹ thuật:

ggplot() xác định dữ liệu đầu vào và các biến gender và bmi cho trục x và y.

geom_boxplot() vẽ biểu đồ boxplot với các điểm ngoài nhóm (outliers) được tô màu đỏ và có kích thước là 2.

geom_jitter() giúp hiển thị các điểm dữ liệu phân tán xung quanh biểu đồ boxplot.

geom_violin() vẽ biểu đồ violin để thể hiện sự phân bố dữ liệu theo giới tính với độ rộng của violin tỷ lệ với mật độ.

stat_summary() thêm điểm trung bình vào biểu đồ.

Nhận xét:

Biểu đồ boxplot cho thấy phân bố BMI rõ ràng giữa các giới tính:

-Nhóm “female” có BMI thấp, với nhiều điểm outlier thấp hơn so với trung bình.

-Nhóm “male” có BMI cao hơn, nhưng vẫn có một số điểm outlier ở mức thấp.

-Nhóm “other” có phân bố BMI tương tự nam, nhưng số lượng mẫu ít hơn.

Biểu đồ violin cho thấy sự phân bố dữ liệu của female rất rộng, với điểm trung bình thấp, trong khi male có phân bố chặt chẽ hơn. Việc thêm điểm trung bình giúp xác nhận xu hướng tổng thể về BMI giữa các giới tính.

4.4. Biểu đồ phân bố tuổi và mức độ rủi ro sức khỏe

ggplot(df2, aes(x = relative_age, y = health_risk_score, color = risk_score_group)) +
  geom_point(alpha = 0.7) +
  geom_smooth(method = "lm", color = "blue") +
  scale_color_manual(values = c("Thấp" = "green", "Trung bình" = "yellow", "Cao" = "red")) +
  labs(title = "Phân bố tuổi và mức độ rủi ro sức khỏe", x = "Tuổi", y = "Điểm rủi ro sức khỏe")

Giải thích kỹ thuật:

geom_point() vẽ các điểm dữ liệu với độ mờ vừa phải để tránh chồng lấn nhau.

geom_smooth(method = “lm”, color = “blue”) thêm đường hồi quy tuyến tính với màu xanh, giúp thể hiện xu hướng quan hệ giữa tuổi và mức độ rủi ro sức khỏe.

scale_color_manual() dùng để đặt màu cho các nhóm

Nhận xét :

Các nhóm rủi ro sức khỏe được phân biệt rõ ràng với các màu sắc: xanh cho nhóm có rủi ro thấp, vàng cho nhóm rủi ro trung bình và đỏ cho nhóm có rủi ro cao. Đường hồi quy tuyến tính (đường màu xanh) thể hiện xu hướng rằng điểm rủi ro sức khỏe có xu hướng tăng lên khi tuổi tác tăn. Điều này có thể cho thấy sự liên hệ yếu giữa tuổi và mức độ rủi ro sức khỏe. Những điểm có mức độ rủi ro cao (trên 80) và tuổi cao được chú thích bằng nhãn, giúp dễ dàng nhận diện những cá thể có mức độ rủi ro cao.

4.5. Biểu đồ cột thể hiện tỷ lệ phân bố các mức độ rủi ro sức khỏe

ggplot(df2, aes(x = risk_score_group, fill = risk_score_group)) +
  geom_bar(stat = "count", position = "dodge") +
  geom_text(stat = "count", aes(label = ..count..), position = position_dodge(0.9), vjust = -0.3) +
  scale_fill_manual(values = c("Thấp" = "green", "Trung bình" = "yellow", "Cao" = "red")) +
  theme_minimal() +
  labs(title = "Phân bố rủi ro sức khỏe", x = "Mức độ rủi ro", y = "Tỷ lệ phần trăm")

Giải thích kỹ thuật:

geom_bar(): sử dụng geom_bar để tạo các cột biểu thị số lượng các cá thể trong mỗi nhóm mức độ rủi ro. Tham số position = “dodge” giúp các cột không chồng lên nhau mà nằm song song, dễ dàng so sánh.

geom_text(): thêm nhãn số lượng vào mỗi cột, sử dụng position_dodge(0.9) để căn chỉnh nhãn cho đúng vị trí của mỗi cột.

scale_fill_manual(): điều chỉnh màu sắc cho các nhóm mức độ rủi ro, với màu xanh cho nhóm “Low”, màu vàng cho “Moderate”, và màu đỏ cho “High”.

Nhận xét:

Biểu đồ cho thấy tỷ lệ phân bố của các nhóm mức độ rủi ro sức khỏe trong bộ dữ liệu, với ba nhóm chính: Thấp, Trung bình, và Cao. Biểu đồ cho thấy sự phân bố khá đồng đều giữa ba nhóm mức độ rủi ro, với mỗi nhóm có số lượng tương tự nhau, xấp xỉ 33,300 cá thể cho mỗi nhóm. Biểu đồ giúp đưa ra một cái nhìn trực quan về sự phân bố mức độ rủi ro sức khỏe trong dữ liệu và có thể hỗ trợ cho việc phân tích sâu hơn về tỉ lệ phân bố trong các nhóm khác nhau.

4.6. Biểu đồ tỷ lệ phân bố các nhóm tuổi theo vùng địa lý

ggplot(df2, aes(x = region, y = relative_age, fill = region)) +
  geom_boxplot() +
  geom_jitter(aes(color = region), position = position_jitter(width = 0.1, height = 0)) +
  stat_summary(fun.y = "mean", geom = "point", shape = 3, size = 4, color = "black") +
  scale_fill_manual(values = c("Midwest" = "lightblue", "South" = "pink", "Other" = "green", "West" = "orange", "Northeast" = "yellow")) +
  labs(title = "Tỷ lệ phân bố các nhóm tuổi theo vùng địa lý", x = "Vùng địa lý", y = "Tuổi tương đối")

Giải thích kỹ thuật:

geom_boxplot() thể hiện sự phân bố nhóm tuổi theo từng vùng, geom_jitter() thêm các điểm để phân biệt rõ hơn.

stat_summary() thêm điểm trung bình cho nhóm tuổi trong các vùng.

scale_fill_manual() chỉnh màu cho từng vùng.

Nhận xét:

Biểu đồ boxplot cho thấy sự phân bố tuổi tương đối giữa các vùng địa lý khá đều, không có sự chênh lệch lớn. Các điểm dữ liệu được phân tán rõ ràng nhờ geom_jitter(), giúp nhận diện dễ dàng sự phân bố của nhóm tuổi. Northeast và West có sự phân bố rộng hơn. Biểu đồ giúp nhận diện mối quan hệ giữa tuổi và vùng sinh sống.

4.7. Biểu đồ phân bố BMI theo mức độ đường huyết

ggplot(df2, aes(x = bmi, y = blood_glucose_level, color = glucose_level_group)) +
  geom_point(alpha = 0.5) +
  geom_smooth(method = "lm", color = "blue") +
  scale_color_manual(values = c("Thấp" = "green", "Trung bình" = "yellow", "Cao" = "red")) +
  geom_text(aes(label = ifelse(blood_glucose_level > 150, as.character(bmi), "")), size = 3, color = "black") +
  labs(title = "Phân bố BMI theo mức độ đường huyết", x = "BMI", y = "Mức độ đường huyết")

Giải thích kỹ thuật:

geom_point() hiển thị các điểm dữ liệu với độ mờ vừa phải, giúp quan sát rõ sự phân bố.

geom_smooth() thêm đường hồi quy tuyến tính để biểu diễn xu hướng quan hệ giữa BMI và mức độ đường huyết.

scale_color_manual() tùy chỉnh màu sắc cho các nhóm mức đường huyết: “Thấp” (xanh lá), “Trung bình” (vàng), và “Cao” (đỏ).

geom_text() thêm nhãn cho các điểm có blood_glucose_level > 150, giúp phân biệt các điểm có mức đường huyết cao.

Nhận xét:

Biểu đồ phân bố BMI theo mức đường huyết cho thấy ba nhóm “Thấp”, “Trung bình”, và “Cao”. Màu sắc giúp phân biệt rõ ràng các nhóm này, trong đó nhóm “Cao” có mức BMI cao nhất. Đường hồi quy tuyến tính cho thấy sự liên quan mạnh giữa BMI và mức đường huyết, đặc biệt là khi BMI càng cao, nguy cơ đường huyết cao càng lớn. Biểu đồ này giúp nhận diện xu hướng và phân tích mối liên hệ giữa các yếu tố nguy cơ trong sức khỏe.

4.8. Biểu đồ đường thể hiện sự thay đổi của HbA1c qua các năm

ggplot(df2, aes(x = year, y = hbA1c_level, color = gender)) +
  geom_line() +
  geom_point() +
  stat_smooth(method = "loess", color = "black") +
  scale_color_manual(values = c("male" = "blue", "female" = "red")) +
  labs(title = "Thay đổi HbA1c qua các năm", x = "Năm", y = "Chỉ số HbA1c")

Giải thích kỹ thuật:

geom_point() hiển thị các điểm dữ liệu với độ mờ vừa phải, giúp quan sát rõ sự phân bố.

geom_smooth() thêm đường hồi quy tuyến tính để biểu diễn xu hướng quan hệ giữa BMI và mức độ đường huyết.

scale_color_manual() tùy chỉnh màu sắc cho các nhóm mức đường huyết: “Thấp” (xanh lá), “Trung bình” (vàng), và “Cao” (đỏ).

geom_text() thêm nhãn cho các điểm có blood_glucose_level > 150, giúp phân biệt các điểm có mức đường huyết cao.

Nhận xét:

Biểu đồ phân bố BMI theo mức đường huyết cho thấy ba nhóm “Thấp”, “Trung bình”, và “Cao”. Màu sắc giúp phân biệt rõ ràng các nhóm này, trong đó nhóm “Cao” có mức BMI cao nhất. Đường hồi quy tuyến tính cho thấy sự liên quan mạnh giữa BMI và mức đường huyết, đặc biệt là khi BMI càng cao, nguy cơ đường huyết cao càng lớn. Biểu đồ này giúp nhận diện xu hướng và phân tích mối liên hệ giữa các yếu tố nguy cơ trong sức khỏe.

4.9. Biểu đồ tần suất phân bố các nhóm phân tích theo giới tính

ggplot(df2, aes(x = gender, fill = gender)) +
  geom_bar(stat = "count") +
  geom_text(stat = "count", aes(label = ..count..), vjust = -0.5) +
  scale_fill_manual(values = c("female" = "pink", "male" = "blue", "other" = "grey")) +
  theme_minimal() +
  labs(title = "Tần suất phân bố theo giới tính", x = "Giới tính", y = "Tần suất")

Giải thích kỹ thuật:

geom_bar() tạo biểu đồ cột với tần suất của từng nhóm giới tính. Các cột được tô màu theo giới tính.

geom_text() thêm nhãn số lượng vào mỗi cột, giúp dễ dàng xem tần suất.

scale_fill_manual() thay đổi màu sắc cho các nhóm giới tính: nữ (hồng), nam (xanh dương), và other (xám).

Nhận xét:

Biểu đồ cho thấy phân bố rõ rệt về giới tính trong dữ liệu. Tần suất của nhóm nữ cao hơn nhóm nam, với 58.546 người nữ và 41.422 người nam. Nhóm other có rất ít người (18), có thể là dữ liệu bị thiếu hoặc không đầy đủ. Cột của nhóm nữ được tô màu hồng, nam là xanh dương và other là xám, giúp dễ dàng phân biệt các nhóm. Kết quả cho thấy có sự chênh lệch rõ rệt giữa số lượng nữ và nam trong dữ liệu

4.10. Biểu đồ phân bố phân nhóm tuổi và giới tính

ggplot(df2, aes(x = gender, y = relative_age, fill = gender)) +
  geom_boxplot() +
  geom_jitter(aes(color = gender), position = position_jitter(width = 0.1, height = 0)) +
  scale_fill_manual(values = c("male" = "blue", "female" = "red")) +
  theme_minimal() +
  labs(title = "Phân bố nhóm tuổi và giới tính", x = "Giới tính", y = "Tuổi tương đối")

Giải thích kỹ thuật:

ggplot(): Xây dựng biểu đồ với trục x là giới tính (gender), trục y là tuổi tương đối (relative_age.

geom_boxplot(): Vẽ biểu đồ boxplot để thể hiện sự phân bố dữ liệu của tuổi tương đối giữa các nhóm giới tính.

geom_jitter(): Thêm các điểm rời rạc vào biểu đồ, để thể hiện các giá trị dữ liệu, giúp phân biệt rõ hơn khi có dữ liệu chồng chéo.

scale_fill_manual(): Thiết lập màu sắc cho các nhóm giới tính, với màu xanh cho nam và màu đỏ cho nữ.

Nhận xét:

Biểu đồ boxplot thể hiện sự phân bố của tuổi tương đối giữa các nhóm giới tính (nam, nữ và khác).

Nhóm nữ (màu đỏ) có sự phân bố tuổi tương đối khá đồng đều, với độ lệch chuẩn lớn thể hiện bởi các điểm ngoài hộp (outliers).

Nhóm nam (màu xanh dương) có sự phân bố tương tự, tuy nhiên có sự tập trung nhiều hơn vào phần trung tâm của biểu đồ.

Nhóm “khác” (màu xám) có số lượng ít và không có sự phân bố rõ ràng, biểu đồ của nhóm này hầu như là một điểm duy nhất, cho thấy thiếu dữ liệu hoặc ít mẫu cho nhóm này.

Dữ liệu cho thấy giới tính có sự khác biệt rõ rệt trong việc phân bố tuổi tương đối, và sự phân bố của các nhóm là hợp lý trong việc thể hiện sự khác biệt trong tuổi giữa các giới tính.

4.11. Biểu đồ phân nhóm bệnh tiểu đường theo giới tính

ggplot(df2, aes(x = diabetes, fill = gender)) +
  geom_bar(stat = "count", position = "dodge") +
  scale_fill_manual(values = c("male" = "blue", "female" = "pink")) +
  labs(title = "Phân nhóm bệnh tiểu đường theo giới tính", x = "Bệnh tiểu đường", y = "Số lượng")

Giải thích kỹ thuật:

ggplot() vẽ đồ thị với x = diabetes (bệnh tiểu đường), fill = gender (giới tính) để tô màu cho các nhóm theo giới tính.

geom_bar() vẽ biểu đồ cột, stat = “count” đếm số lượng các bệnh nhân có hoặc không mắc bệnh tiểu đường.

scale_fill_manual() xác định màu sắc cho các nhóm giới tính: nam = xanh dương, nữ = hồng.

Nhận xét:

Biểu đồ cho thấy số lượng bệnh nhân mắc bệnh tiểu đường ở nam nhiều hơn nữ, với tỷ lệ chiếm ưu thế ở nhóm nam.

Sự phân bố giữa các nhóm giới tính là không đều, có thể do sự khác biệt trong tỷ lệ mắc bệnh tiểu đường giữa các giới.

4.12. Biểu đồ phân bố vùng địa lý

ggplot(region_freq, aes(x = reorder(`Vùng địa lý`, -`Tỷ lệ (%)`), y = `Tỷ lệ (%)`, fill = `Vùng địa lý`)) +
  geom_bar(stat = "identity") +
  geom_text(aes(label = sprintf("%.1f%%", `Tỷ lệ (%)`)), vjust = -0.5) +
  labs(title = "Phân bố bệnh nhân theo vùng địa lý", x = "Vùng", y = "Tỷ lệ (%)") +
  theme_minimal()

Giải thích kỹ thuật

geom_bar() vẽ biểu đồ cột với chiều cao tương ứng với tỷ lệ phần trăm.

geom_text() thêm nhãn phần trăm ngay trên mỗi cột.

Nhận xét:

Biểu đồ thể hiện tỷ lệ phân bố bệnh nhân theo các vùng địa lý, với tỷ lệ cao nhất thuộc về vùng “Other” (71.0%) trong khi các vùng khác như “Northeast”, “South”, “West” có tỷ lệ thấp hơn rất nhiều, từ 6.1% đến 8.1%.

Sự phân bố này có thể chỉ ra sự chênh lệch lớn về số lượng bệnh nhân giữa các vùng, vùng “Other” có thể bao gồm nhiều khu vực khác chưa được phân loại rõ ràng trong bộ dữ liệu.

4.13. Biểu đồ phân nhóm giới tính theo nguy cơ sức khỏe

ggplot(df2, aes(x = gender, y = health_risk_score, fill = gender)) +
  geom_boxplot() +
  geom_jitter(aes(color = gender), position = position_jitter(width = 0.1, height = 0)) +
  scale_fill_manual(values = c("male" = "blue", "female" = "pink")) +
  theme_minimal() +
  labs(title = "Phân nhóm giới tính theo nguy cơ sức khỏe", x = "Giới tính", y = "Điểm nguy cơ sức khỏe")

** Giải thích kỹ thuật:**

ggplot() tạo đồ thị với x = gender, y = health_risk_score, và fill = gender để tô màu theo giới tính.

geom_boxplot() vẽ biểu đồ hộp so sánh phân bố điểm nguy cơ sức khỏe theo giới tính.

geom_jitter() thêm các điểm rời rạc để rõ hơn sự phân bố.

scale_fill_manual() chọn màu cho giới tính: nam = xanh dương, nữ = hồng.

Nhận xét:

Nhóm nữ có phân bố điểm nguy cơ sức khỏe rộng và nhiều điểm ngoài phạm vi, chỉ ra sự dao động lớn về sức khỏe.

Nhóm nam có điểm số tập trung chặt chẽ, ít điểm ngoài phạm vi.

Nhóm “other” có số liệu ít, không rõ ràng.

Biểu đồ giúp nhận diện sự khác biệt về sức khỏe giữa các giới.

4.14. Biểu đồ phân phối độ tuổi theo giới tính

ggplot(df2, aes(x = relative_age, fill = gender)) +
  geom_histogram(binwidth = 2, position = "stack", color = "black") +
  scale_fill_manual(values = c("male" = "blue", "female" = "pink")) +
  labs(title = "Phân phối độ tuổi theo giới tính", x = "Tuổi", y = "Số lượng")

Giải thích kỹ thuật:

ggplot(): Biểu đồ được vẽ với trục x là tuổi tương đối (relative_age) và màu sắc được phân theo giới tính (gender).

geom_histogram(): Hàm geom_histogram tạo biểu đồ cột, với chiều rộng của mỗi cột là 2 (binwidth = 2), và các cột được xếp chồng lên nhau (position = “stack”), màu viền cột là màu đen.

scale_fill_manual(): Thiết lập màu sắc thủ công cho các nhóm giới tính: nam là màu xanh dương (blue) và nữ là màu hồng (pink).

labs(): Thiết lập tiêu đề cho biểu đồ, nhãn cho trục x là “Tuổi” và nhãn cho trục y là “Số lượng”.

Nhận xét:

Biểu đồ thể hiện phân phối độ tuổi của các nhóm giới tính (nam và nữ). Nhóm nữ (màu hồng) chiếm phần lớn trong phân phối độ tuổi với số lượng rất cao ở các độ tuổi trẻ (khoảng 25 tuổi trở lại), đồng thời có sự gia tăng đột biến ở độ tuổi cao hơn, có thể do dữ liệu thiếu. Nhóm nam (màu xanh dương) có phân bố độ tuổi không đều, số lượng phân bố thấp ở độ tuổi trẻ và tăng dần ở độ tuổi lớn hơn. Phân phối tuổi của nữ có xu hướng cao hơn và chiếm ưu thế so với nam ở phần đầu của biểu đồ. Điều này có thể chỉ ra rằng trong dữ liệu của bạn, nữ có nhiều người ở các độ tuổi nhỏ hơn (ví dụ: dưới 25).

4.15. Biểu đồ phân nhóm mức độ đường huyết theo giới tính

ggplot(df2, aes(x = glucose_level_group, fill = gender)) +
  geom_bar(stat = "count", position = "dodge") +
  scale_fill_manual(values = c("male" = "blue", "female" = "pink")) +
  labs(title = "Phân nhóm mức độ đường huyết theo giới tính", x = "Mức độ đường huyết", y = "Số lượng")

Giải thích kỹ thuật:

ggplot(): Hàm ggplot được sử dụng để tạo biểu đồ, với trục x là nhóm mức độ đường huyết (glucose_level_group) và màu sắc được phân theo giới tính (gender).

geom_bar(): Hàm geom_bar tạo biểu đồ cột, với stat = “count” để đếm số lượng các quan sát trong mỗi nhóm. Các cột sẽ được hiển thị cạnh nhau (không chồng lên nhau) nhờ tham số position = “dodge”.

scale_fill_manual(): Thiết lập màu sắc thủ công cho các nhóm giới tính, với màu xanh dương cho nam (blue) và màu hồng cho nữ (pink).

labs(): Thiết lập tiêu đề cho biểu đồ, nhãn trục x là “Mức độ đường huyết” và nhãn trục y là “Số lượng”.

Nhận xét:

Biểu đồ thể hiện phân nhóm mức độ đường huyết theo giới tính, bao gồm các mức “Thấp”, “Trung bình” và “Cao”.

-Ở mức “Thấp”, số lượng phụ nữ (màu hồng) vượt trội hơn so với nam (màu xanh dương).

-Ở mức “Trung bình”, số lượng phụ nữ và nam gần như tương đương, mặc dù vẫn có một sự nhỉnh hơn nhỏ của phụ nữ.

-Ở mức “Cao”, số lượng phụ nữ ít hơn nhiều so với nam, cho thấy nam giới có xu hướng có mức độ đường huyết cao hơn trong mẫu này.

Phân phối này cho thấy sự khác biệt giữa nam và nữ trong các mức độ đường huyết, với nữ chiếm ưu thế ở các mức độ thấp và nam chiếm ưu thế ở mức độ cao.

Phần 2: Phân tích bộ dữ liệu mã chứng khoán Tổng Công ty Cổ phần Dịch vụ Tổng hợp Dầu khí (PET)

Nội Dung 1: Giới thiệu về bộ dữ liệu

Phần này trình bày phân tích chi tiết về dữ liệu tài chính của Tổng Công ty Cổ phần Dịch vụ Tổng hợp Dầu khí – PET (mã chứng khoán: PET) trong giai đoạn 2015–2024. Bộ dữ liệu được tổng hợp từ ba báo cáo tài chính gồm: Bảng cân đối kế toán (CDKT), Báo cáo lưu chuyển tiền tệ (LCTT) và Báo cáo kết quả hoạt động kinh doanh (KQKD), làm cơ sở để trích xuất và so sánh các chỉ tiêu tài chính chủ yếu trong các phần tiếp theo.

1.1. Đọc 3 sheet từ file Excel

CDKT <- read_excel("C:/Users/ADMIN/Downloads/Dữ liệu PET/PET.xlsx", sheet = "CDKT")
LCTT <- read_excel("C:/Users/ADMIN/Downloads/Dữ liệu PET/PET.xlsx", sheet = "LCTT")
KQKD <- read_excel("C:/Users/ADMIN/Downloads/Dữ liệu PET/PET.xlsx", sheet = "KQKD")

Giải thích kỹ thuật:

Đoạn mã sử dụng read_excel() để đọc ba sheet từ file Excel và lưu dữ liệu vào các biến CDKT, LCTT, KQKD. Mỗi sheet chứa thông tin tài chính khác nhau của công ty.

Nhận xét:

Dữ liệu từ ba sheet được đọc thành công và sẵn sàng cho các phân tích tiếp theo.

1.2. Kiểm tra kích thước từng bảng

dim(CDKT)
## [1] 119  11
dim(LCTT)
## [1] 42 11
dim(KQKD)
## [1] 23 11

Giải thích kỹ thuật:

Hàm dim() trong R dùng để kiểm tra số dòng và số cột của một bảng dữ liệu. Việc kiểm tra kích thước các bảng giúp xác định:

-Số lượng quan sát (dòng) trong mỗi bảng, tương ứng với số năm báo cáo tài chính có trong dữ liệu.

-Số lượng biến (cột), tương ứng với số chỉ tiêu tài chính mà mỗi bảng chứa đựng.

Nhận xét:

Các bảng dữ liệu có kích thước như sau:

-CDKT: 119 dòng và 11 cột, chứa dữ liệu tài chính từ 119 năm, với 11 chỉ tiêu tài chính như tài sản, nợ và vốn chủ sở hữu…

-LCTT: 42 dòng và 11 cột, cung cấp thông tin về dòng tiền từ hoạt động kinh doanh, đầu tư và tài chính…

-KQKD: 23 dòng và 11 cột, bao gồm các chỉ tiêu doanh thu, lợi nhuận và chi phí…

Các bảng này hỗ trợ phân tích các chỉ tiêu tài chính quan trọng theo năm, giúp đánh giá tình hình tài chính và chiến lược đầu tư của công ty.

1.3. Xem tên các biến

names(CDKT)
##  [1] "Bảng cân đối kế toán" "2015"                 "2016"                
##  [4] "2017"                 "2018"                 "2019"                
##  [7] "2020"                 "2021"                 "2022"                
## [10] "2023"                 "2024"
names(LCTT)
##  [1] "Bảng lưu chuyển tiền tệ" "2015"                   
##  [3] "2016"                    "2017"                   
##  [5] "2018"                    "2019"                   
##  [7] "2020"                    "2021"                   
##  [9] "2022"                    "2023"                   
## [11] "2024"
names(KQKD)
##  [1] "Bảng báo cáo kết quả kinh doanh" "2015"                           
##  [3] "2016"                            "2017"                           
##  [5] "2018"                            "2019"                           
##  [7] "2020"                            "2021"                           
##  [9] "2022"                            "2023"                           
## [11] "2024"

Giải thích kỹ thuật:

Hàm names() trong R trả về tên của tất cả các cột trong bảng dữ liệu.

-names(CDKT): liệt kê tên các cột trong bảng Cân đối kế toán (CDKT), gồm các năm từ 2015 đến 2024. Mỗi cột tương ứng với một năm báo cáo tài chính.

-names(LCTT): liệt kê tên các cột trong bảng Lưu chuyển tiền tệ (LCTT), cũng chứa các năm từ 2015 đến 2024.

-names(KQKD): liệt kê tên các cột trong bảng Kết quả hoạt động kinh doanh (KQKD), gồm các năm từ 2015 đến 2024.

Nhận xét:

Mỗi bảng (CDKT, LCTT, KQKD) chứa tên cột là các năm từ 2015 đến 2024, cho thấy dữ liệu tài chính đã được tổ chức hợp lý và có thể được sử dụng để phân tích qua thời gian. Các cột này tương ứng với các năm báo cáo của các chỉ tiêu tài chính.

Các tên cột đại diện cho các năm báo cáo tài chính (từ 2015 đến 2024), điều này giúp dễ dàng theo dõi và phân tích sự thay đổi các chỉ tiêu tài chính qua từng năm. Nhờ đó, việc tính toán các chỉ số tài chính như tỷ lệ nợ/vốn, ROA, ROE,… trở nên rõ ràng và có tính thời gian.

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

colSums(is.na(CDKT))
## Bảng cân đối kế toán                 2015                 2016 
##                    0                   67                   47 
##                 2017                 2018                 2019 
##                   48                   47                   44 
##                 2020                 2021                 2022 
##                   47                   46                   47 
##                 2023                 2024 
##                   46                   48

Giải thích kỹ thuật:

Hàm is.na() kiểm tra giá trị thiếu (NA) trong bảng CDKT (Bảng Cân đối kế toán). Sau đó, colSums() tính tổng số giá trị thiếu trong từng cột của bảng.

Mỗi năm (cột từ 2015 đến 2024) sẽ hiển thị số lượng giá trị thiếu trong đó. Nếu kết quả là 0, có nghĩa là không có giá trị thiếu trong cột đó.

Nhận xét:

Kết quả trả về cho thấy số lượng giá trị thiếu ở mỗi cột năm:

-2015: không có giá trị thiếu (0).

-2016: có 67 giá trị thiếu.

-2017–2019: số lượng giá trị thiếu dao động từ 44–48.

-2020–2024: số lượng giá trị thiếu dao động từ 46–48.

Việc có giá trị thiếu trong các năm đầu (2016–2019) có thể ảnh hưởng đến kết quả phân tích và dự báo tài chính. Tuy nhiên, việc thiếu một số dữ liệu trong các năm đầu là bình thường trong các báo cáo tài chính khi có sự thay đổi về cấu trúc báo cáo hoặc nguồn cung cấp dữ liệu.

1.5. Xem cấu trúc và chuẩn hóa tên biến

# Chuẩn hóa tên biến (chuyển về không dấu và chữ thường)
names(CDKT) <- make.names(names(CDKT))
names(LCTT) <- make.names(names(LCTT))
names(KQKD) <- make.names(names(KQKD))

Giải thích kỹ thuật:

Hàm make.names() được sử dụng để chuẩn hóa tên cột trong bảng dữ liệu, loại bỏ dấu tiếng Việt, khoảng trắng và các ký tự đặc biệt để các tên biến trở thành hợp lệ. Sau đó, hàm names() được sử dụng để gán tên biến mới cho các bảng dữ liệu CDKT, LCTT, và KQKD.

Nhận xét:

Việc chuẩn hóa tên biến giúp tránh lỗi cú pháp khi xử lý dữ liệu trong R, đặc biệt là khi có dấu hoặc ký tự đặc biệt trong tên biến. Điều này rất quan trọng khi làm việc với các tên biến dài hoặc phức tạp, giúp đảm bảo rằng các tên biến có thể được nhận diện chính xác trong các thao tác như tính toán, trực quan hóa hoặc lọc dữ liệu.

1.6. Lọc 10 biến chính cần phân tích

# Lọc 10 biến chính theo nhóm
library(readxl)
library(dplyr)
CDKT <- read_excel("C:/Users/ADMIN/Downloads/Dữ liệu PET/PET.xlsx", sheet = "CDKT")
LCTT <- read_excel("C:/Users/ADMIN/Downloads/Dữ liệu PET/PET.xlsx", sheet = "LCTT")
KQKD <- read_excel("C:/Users/ADMIN/Downloads/Dữ liệu PET/PET.xlsx", sheet = "KQKD")
names(CDKT)[1] <- "Khoan_muc"
names(LCTT)[1] <- "Khoan_muc"
names(KQKD)[1] <- "Khoan_muc"
#Lọc 10 biến theo tên khoản mục
selected_vars <- c(
  "Doanh thu thuần",
  "Lợi nhuận gộp",
  "Lợi nhuận sau thuế",
  "Tổng tài sản",
  "Vốn chủ sở hữu",
  "Nợ phải trả",
  "Tài sản ngắn hạn",
  "Lưu chuyển tiền thuần từ hoạt động kinh doanh",
  "Lưu chuyển tiền thuần từ hoạt động đầu tư",
  "Lưu chuyển tiền thuần từ hoạt động tài chính"
)
CDKT_sel <- CDKT %>% filter(Khoan_muc %in% selected_vars)
LCTT_sel <- LCTT %>% filter(Khoan_muc %in% selected_vars)
KQKD_sel <- KQKD %>% filter(Khoan_muc %in% selected_vars)
# Gộp các bảng lại thành 1
financial_selected <- bind_rows(CDKT_sel, LCTT_sel, KQKD_sel, .id = "Nguon")
# xem trước kết quả
print(financial_selected, n = Inf, width = Inf)
## # A tibble: 10 × 12
##    Nguon Khoan_muc                                       `2015`        `2016`
##    <chr> <chr>                                            <dbl>         <dbl>
##  1 1     Tài sản ngắn hạn                               5.28e12 4741485264868
##  2 1     Tổng tài sản                                   5.76e12 6227006416270
##  3 1     Nợ phải trả                                    4.27e12 4579147052293
##  4 1     Vốn chủ sở hữu                                 1.35e12 1647859363977
##  5 2     Lưu chuyển tiền thuần từ hoạt động kinh doanh  3.87e11  202890081636
##  6 2     Lưu chuyển tiền thuần từ hoạt động đầu tư     -7.35e10 -495739742974
##  7 2     Lưu chuyển tiền thuần từ hoạt động tài chính  -9.05e11  525662249424
##  8 3     Doanh thu thuần                                1.07e13 9882058966471
##  9 3     Lợi nhuận gộp                                  8.19e11  658496168984
## 10 3     Lợi nhuận sau thuế                             2.12e11  166977726793
##      `2017`   `2018`   `2019`   `2020`   `2021`   `2022`   `2023`   `2024`
##       <dbl>    <dbl>    <dbl>    <dbl>    <dbl>    <dbl>    <dbl>    <dbl>
##  1  4.79e12  4.33e12  3.72e12  5.08e12  7.16e12  7.76e12  8.26e12  9.02e12
##  2  6.17e12  5.56e12  4.97e12  6.32e12  8.49e12  9.04e12  9.48e12  1.02e13
##  3  4.51e12  3.94e12  3.33e12  4.66e12  6.55e12  6.98e12  7.29e12  7.83e12
##  4  1.66e12  1.62e12  1.64e12  1.66e12  1.94e12  2.06e12  2.19e12  2.34e12
##  5  4.12e 9  1.97e11  3.12e11 -4.05e10 -1.51e11 -1.68e11 -3.00e11  4.00e11
##  6 -2.24e 9  4.92e10 -2.12e10 -4.69e10 -1.20e10 -1.57e12 -5.52e11 -1.14e11
##  7 -3.54e11 -5.51e11 -5.13e11  1.08e12  9.30e11  2.56e11  8.31e11  4.07e11
##  8  1.07e13  1.11e13  1.00e13  1.35e13  1.76e13  1.75e13  1.72e13  1.90e13
##  9  7.48e11  6.62e11  6.11e11  6.69e11  9.39e11  9.67e11  7.22e11  8.90e11
## 10  1.44e11  1.30e11  1.27e11  1.40e11  3.11e11  1.67e11  1.39e11  2.20e11

Giải thích kỹ thuật:

Lọc 10 biến cần phân tích: tạo danh sách các biến tài chính cần phân tích và lọc dữ liệu dựa trên tên khoản mục từ ba bảng.

Dùng hàm bind_rows() từ thư viện dplyr để kết hợp ba bảng thành một bảng duy nhất với cột Nguon cho biết nguồn gốc của mỗi dữ liệu.

Nhận xét:

Dữ liệu từ ba bảng CDKT, LCTT, và KQKD đã được lọc và kết hợp thành một bảng financial_selected. Các chỉ tiêu tài chính được chọn bao gồm Doanh thu thuần, Lợi nhuận gộp, Tổng tài sản, Vốn chủ sở hữu, Nợ phải trả, Tài sản ngắn hạn, Lưu chuyển tiền thuần từ hoạt động kinh doanh, Lưu chuyển tiền thuần từ hoạt động, Lưu chuyển tiền thuần từ hoạt động tài chính. Mỗi dòng trong bảng có cột Nguon để xác định bảng nguồn (CDKT, LCTT, KQKD).

Việc lọc và gộp các dữ liệu tài chính vào một bảng duy nhất giúp dễ dàng thực hiện các phân tích tổng thể và so sánh các chỉ tiêu tài chính qua các năm. Cột Nguon cho phép nhận diện dữ liệu từ các bảng khác nhau, giúp việc phân tích dễ dàng hơn.

1.7. Xem cấu trúc của 10 biến đã lọc

# Xem cấu trúc dữ liệu của 10 biến đã chọn
str(financial_selected)
## tibble [10 × 12] (S3: tbl_df/tbl/data.frame)
##  $ Nguon    : chr [1:10] "1" "1" "1" "1" ...
##  $ Khoan_muc: chr [1:10] "Tài sản ngắn hạn" "Tổng tài sản" "Nợ phải trả" "Vốn chủ sở hữu" ...
##  $ 2015     : num [1:10] 5282004772886 5764543759971 4265313985196 1351864916364 386645716621 ...
##  $ 2016     : num [1:10] 4741485264868 6227006416270 4579147052293 1647859363977 202890081636 ...
##  $ 2017     : num [1:10] 4786143044848 6172779246067 4512198485463 1660580760604 4118698885 ...
##  $ 2018     : num [1:10] 4330671814214 5563061574237 3942652024730 1620409549507 197237097985 ...
##  $ 2019     : num [1:10] 3715299217677 4966334501358 3326016900969 1640317600389 312122602166 ...
##  $ 2020     : num [1:10] 5083111172737 6320756540108 4657590544086 1663165996022 -40515989177 ...
##  $ 2021     : num [1:10] 7164336982272 8493140365997 6553412395338 1939727970659 -151079865596 ...
##  $ 2022     : num [1:10] 7756288698651 9039495579124 6977084341606 2062411237518 -167839738647 ...
##  $ 2023     : num [1:10] 8259191973425 9479475648290 7293215880400 2186259767890 -299922459113 ...
##  $ 2024     : num [1:10] 9021308150964 10165003468335 7826753005585 2338250462750 399996509899 ...

Giải thích kỹ thuật:

Hàm str() giúp hiển thị cấu trúc của bảng dữ liệu financial_selected, cho biết các loại dữ liệu của từng cột, số dòng, và số cột, giúp kiểm tra xem các dữ liệu đã được chuyển đổi và chuẩn hóa đúng chưa.

Nhận xét:

Cấu trúc bảng financial_selected:

-Số dòng: 10 dòng dữ liệu, tương ứng với 10 chỉ tiêu tài chính cần phân tích.

-Số cột: 12 cột, bao gồm các năm từ 2015 đến 2024 và một số cột phụ như “Nguon” và “Khoan_muc”.

-Các cột “Nguon” và “Khoan_muc” chứa thông tin về nguồn gốc dữ liệu (CDKT, LCTT, KQKD) và tên các khoản mục tài chính (Doanh thu thuần, Lợi nhuận gộp,…).

1.8. Kiểm tra giá trị trùng lặp

# Đếm số dòng trùng lặp hoàn toàn
dup_count <- sum(duplicated(financial_selected))
cat("Số dòng trùng lặp:", dup_count, "\n")
## Số dòng trùng lặp: 0

Giải thích kỹ thuật:

Hàm duplicated() kiểm tra dòng trùng lặp hoàn toàn trong bảng dữ liệu financial_selected. Hàm này sẽ trả về một vector logical (TRUE/FALSE), trong đó TRUE cho biết dòng bị trùng lặp.

sum() sẽ tính tổng số dòng bị trùng, và cat() sẽ in ra số lượng dòng trùng lặp. Nếu có dòng trùng lặp, kết quả sẽ là số lượng dòng trùng. Nếu không có trùng lặp, kết quả sẽ là 0.

Nhận xét:

Kết quả trả về là Số dòng trùng lặp: 0, nghĩa là không có dòng trùng lặp hoàn toàn trong bảng financial_selected. Điều này cho thấy dữ liệu là duy nhất và không có sự trùng lặp trong các bản ghi. Dữ liệu không có trùng lặp, giúp đảm bảo tính chính xác và độ tin cậy khi thực hiện các phân tích tiếp theo.

1.9. Kiểm tra giá trị bị thiếu (NA)

# Kiểm tra giá trị thiếu
any(is.na(financial_selected))
## [1] FALSE
# Tổng số ô NA
total_na <- sum(is.na(financial_selected))
cat("Tổng số ô NA:", total_na, "\n")
## Tổng số ô NA: 0
# Số NA theo từng cột
colSums(is.na(financial_selected))
##     Nguon Khoan_muc      2015      2016      2017      2018      2019      2020 
##         0         0         0         0         0         0         0         0 
##      2021      2022      2023      2024 
##         0         0         0         0

Giải thích kỹ thuật:

Hàm is.na() kiểm tra xem có giá trị thiếu (NA) trong bảng dữ liệu financial_selected. Hàm any() sẽ trả về TRUE nếu có ít nhất một giá trị thiếu và FALSE nếu không có giá trị thiếu nào trong bảng.

Hàm sum(is.na()) tính tổng số ô bị thiếu (NA) trong bảng financial_selected. Kết quả được in ra với hàm cat().

Hàm colSums(is.na()) tính số lượng giá trị thiếu trong mỗi cột của bảng financial_selected. Điều này giúp xác định cột nào có giá trị thiếu.

Nhận xét:

any(is.na(financial_selected)) trả về FALSE, cho thấy không có giá trị thiếu (NA) trong bảng dữ liệu financial_selected.

total_na trả về 0, tức là tổng số ô bị thiếu là 0.

colSums(is.na(financial_selected)) trả về tất cả các giá trị bằng 0, cho thấy mỗi cột trong bảng không có giá trị thiếu nào.

Việc không có giá trị thiếu trong dữ liệu giúp đảm bảo tính đầy đủ và chính xác khi thực hiện các phân tích tiếp theo.

1.10. Thống kê mô tả cơ bản cho 10 biến

  str(financial_selected)
## tibble [10 × 12] (S3: tbl_df/tbl/data.frame)
##  $ Nguon    : chr [1:10] "1" "1" "1" "1" ...
##  $ Khoan_muc: chr [1:10] "Tài sản ngắn hạn" "Tổng tài sản" "Nợ phải trả" "Vốn chủ sở hữu" ...
##  $ 2015     : num [1:10] 5282004772886 5764543759971 4265313985196 1351864916364 386645716621 ...
##  $ 2016     : num [1:10] 4741485264868 6227006416270 4579147052293 1647859363977 202890081636 ...
##  $ 2017     : num [1:10] 4786143044848 6172779246067 4512198485463 1660580760604 4118698885 ...
##  $ 2018     : num [1:10] 4330671814214 5563061574237 3942652024730 1620409549507 197237097985 ...
##  $ 2019     : num [1:10] 3715299217677 4966334501358 3326016900969 1640317600389 312122602166 ...
##  $ 2020     : num [1:10] 5083111172737 6320756540108 4657590544086 1663165996022 -40515989177 ...
##  $ 2021     : num [1:10] 7164336982272 8493140365997 6553412395338 1939727970659 -151079865596 ...
##  $ 2022     : num [1:10] 7756288698651 9039495579124 6977084341606 2062411237518 -167839738647 ...
##  $ 2023     : num [1:10] 8259191973425 9479475648290 7293215880400 2186259767890 -299922459113 ...
##  $ 2024     : num [1:10] 9021308150964 10165003468335 7826753005585 2338250462750 399996509899 ...

Giải thích kỹ thuật:

Hàm str() hiển thị cấu trúc tổng quan, gồm kiểu dữ liệu (chr, dbl, num), số dòng và định dạng cột.

Các biến giá trị là numeric (dbl) để có thể tính toán.

Biến mô tả tên khoản mục là character (chr).

library(dplyr)
library(tidyr)
# Chuyển dữ liệu sang dạng long (năm - giá trị)
financial_long <- financial_selected %>%
  pivot_longer(cols = -c(Nguon, Khoan_muc),
               names_to = "Nam",
               values_to = "Gia_tri") %>%
  mutate(Nam = as.numeric(Nam))
#Lọc biến cần xem (ví dụ: Doanh thu thuần)
doanh_thu <- financial_long %>%
  filter(Khoan_muc == "Doanh thu thuần")
selected_vars <- c("Doanh thu thuần", "Lợi nhuận gộp", "Lợi nhuận sau thuế",
                   "Tổng tài sản", "Vốn chủ sở hữu", "Nợ phải trả",
                   "Tài sản ngắn hạn",
                   "Lưu chuyển tiền thuần từ hoạt động kinh doanh",
                   "Lưu chuyển tiền thuần từ hoạt động đầu tư",
                   "Lưu chuyển tiền thuần từ hoạt động tài chính")

for (var in selected_vars) {
  cat("\n===== Thống kê mô tả:", var, "=====\n")
  temp <- financial_long %>% filter(Khoan_muc == var)
  print(summary(temp$Gia_tri))
}

Giải thích kỹ thuật:

pivot_longer(): chuyển dữ liệu từ dạng “wide” (dữ liệu có nhiều cột cho từng năm) sang dạng “long”, trong đó các năm sẽ được chuyển thành giá trị trong một cột duy nhất (Nam) và các giá trị của từng năm sẽ được gộp vào cột Gia_tri.

mutate(): chuyển cột Nam (tên năm) thành kiểu số (numeric) để có thể thực hiện các phép toán.

filter(Khoan_muc == “Doanh thu thuần”): lọc ra các dữ liệu chỉ liên quan đến chỉ tiêu “Doanh thu thuần”.

summary(): hàm này giúp hiển thị các thống kê mô tả cơ bản như Min, 1st Qu, Median, Mean, 3rd Qu, Max cho biến Gia_tri.

selected_vars: danh sách các chỉ tiêu tài chính cần phân tích.

for loop: lặp qua từng chỉ tiêu trong danh sách selected_vars, lọc dữ liệu theo mỗi chỉ tiêu và sử dụng summary() để hiển thị các thống kê mô tả cho từng chỉ tiêu.

Nhận xét:

Sau khi thực hiện các bước trên, hệ thống sẽ in ra các thống kê mô tả cho từng chỉ tiêu tài chính được chọn (như Doanh thu thuần, Lợi nhuận gộp, …). Các kết quả này sẽ bao gồm các giá trị như:

-Min: giá trị nhỏ nhất trong dữ liệu.

-1st Qu: giá trị phần tư đầu tiên.

-Median: giá trị trung vị.

-Mean: giá trị trung bình.

-3rd Qu: giá trị phần tư thứ ba.

-Max: giá trị lớn nhất.

Các thống kê mô tả sẽ giúp nhanh chóng đánh giá các đặc điểm cơ bản của từng chỉ tiêu tài chính, chẳng hạn như giá trị nhỏ nhất, lớn nhất, trung bình và sự phân bố dữ liệu. Điều này rất hữu ích để phát hiện các giá trị ngoại lệ hoặc biến động lớn trong các chỉ tiêu tài chính qua các năm. Thống kê mô tả cũng giúp xác định mức độ ổn định hoặc biến động của các chỉ tiêu tài chính.

1.10.1 Doanh thu thuần

doanh_thu <- financial_long %>% 
  filter(Khoan_muc == "Doanh thu thuần")

summary(doanh_thu$Gia_tri)
##           Min.        1st Qu.         Median           Mean        3rd Qu. 
##  9882058966471 10665152090907 12272612518533 13719454133052 17461939825186 
##           Max. 
## 19043708488035

Giải thích kỹ thuật:

doanh_thu <- financial_long %>% filter(Khoan_muc == “Doanh thu thuần”): lọc dữ liệu từ bảng financial_long, chỉ lấy các dòng có Khoan_muc là “Doanh thu thuần”.

summary(doanh_thu$Gia_tri): tính các thống kê mô tả cho cột Gia_tri của dữ liệu đã lọc.

Nhận xét:

Kết quả cho ta thấy:

-Min: 988205896647 (doanh thu thấp nhất trong dữ liệu).

-1st Qu: 10665152090907 (giá trị doanh thu tại phần tư đầu tiên).

-Median: 12272612518533 (giá trị trung vị).

-Mean: 13719454133052 (giá trị trung bình doanh thu).

-3rd Qu: 17461939825186 (giá trị doanh thu tại phần tư thứ ba).

-Max: 19043708488035 (doanh thu cao nhất).

Dữ liệu có sự chênh lệch lớn giữa Min và Max, cho thấy sự biến động lớn trong doanh thu. Trung bình cao hơn trung vị, phản ánh sự xuất hiện của một số năm có doanh thu rất lớn.

1.10.2 Lợi nhuận gộp

loi_nhuan_gop <- financial_long %>% 
  filter(Khoan_muc == "Lợi nhuận gộp")

summary(loi_nhuan_gop$Gia_tri)
##         Min.      1st Qu.       Median         Mean      3rd Qu.         Max. 
## 611134281811 664055791446 735122589938 768624951307 872128535468 967103878018

Giải thích kỹ thuật:

loi_nhuan_gop <- financial_long %>% filter(Khoan_muc == “Lợi nhuận gộp”): lọc dữ liệu từ bảng financial_long, chỉ lấy các dòng có Khoan_muc là “Lợi nhuận gộp”.

Nhận xét:

Kết quả cho ta thấy:

-Min: 611134281811 (lợi nhuận gộp thấp nhất trong bộ dữ liệu).

-1st Qu: 664055791446 (giá trị lợi nhuận gộp tại phần tư đầu tiên).

-Median: 735122589938 (giá trị trung vị lợi nhuận gộp).

-Mean: 768624951307 (lợi nhuận gộp trung bình).

-3rd Qu: 872128535468 (giá trị lợi nhuận gộp tại phần tư thứ ba).

-Max: 967103878018 (lợi nhuận gộp cao nhất).

Dữ liệu cho thấy lợi nhuận gộp có sự phân bổ tương đối ổn định, với giá trị trung bình gần với trung vị, nhưng vẫn có sự chênh lệch giữa giá trị thấp nhất và cao nhất.

1.10.3 Lợi nhuận sau thuế

loi_nhuan_sau_thue <- financial_long %>% 
  filter(Khoan_muc == "Lợi nhuận sau thuế")

summary(loi_nhuan_sau_thue$Gia_tri)
##         Min.      1st Qu.       Median         Mean      3rd Qu.         Max. 
## 127316722277 139289588554 155398496378 175786947582 200775746148 311458745048

Giải thích kỹ thuật:

loi_nhuan_sau_thue <- financial_long %>% filter(Khoan_muc == “Lợi nhuận sau thuế”): lọc dữ liệu từ bảng financial_long, chỉ lấy các dòng có Khoan_muc là “Lợi nhuận sau thuế”.

Nhận xét:

Kết quả cho ta thấy:

-Min: 12731672227 (lợi nhuận sau thuế thấp nhất trong bộ dữ liệu).

-1st Qu: 139289585854 (giá trị lợi nhuận sau thuế tại phần tư đầu tiên).

-Median: 155398496378 (giá trị trung vị lợi nhuận sau thuế).

-Mean: 175786947582 (lợi nhuận sau thuế trung bình).

-3rd Qu: 200775746148 (giá trị lợi nhuận sau thuế tại phần tư thứ ba).

-Max: 311458745048 (lợi nhuận sau thuế cao nhất).

Lợi nhuận sau thuế có sự phân bố khá đều với trung bình gần bằng trung vị, cho thấy dữ liệu không có sự biến động quá lớn. Tuy nhiên, giá trị Min và Max cho thấy sự khác biệt đáng kể trong các năm.

1.10.4 Tổng tài sản

tong_tai_san <- financial_long %>% 
  filter(Khoan_muc == "Tổng tài sản")

summary(tong_tai_san$Gia_tri)
##           Min.        1st Qu.         Median           Mean        3rd Qu. 
##  4966334501358  5866602631495  6273881478189  7219159709976  8902906775842 
##           Max. 
## 10165003468335

Giải thích kỹ thuật:

tong_tai_san <- financial_long %>% filter(Khoan_muc == “Tổng tài sản”): lọc dữ liệu từ bảng financial_long, chỉ lấy các dòng có Khoan_muc là “Tổng tài sản”.

Nhận xét:

Kết quả cho ta thấy:

-Min: 4966334501358 (tổng tài sản thấp nhất trong bộ dữ liệu).

-1st Qu: 5866602631495 (giá trị tổng tài sản tại phần tư đầu tiên).

-Median: 6273881478189 (giá trị trung vị tổng tài sản).

-Mean: 7219159709976 (tổng tài sản trung bình).

-3rd Qu: 8902906775842 (giá trị tổng tài sản tại phần tư thứ ba).

-Max: 10165003468335 (tổng tài sản cao nhất).

Tổng tài sản có sự phân bổ tương đối đồng đều với trung bình gần bằng trung vị, cho thấy dữ liệu không có sự lệch đáng kể. Tuy nhiên, sự khác biệt giữa giá trị Min và Max vẫn chỉ ra sự biến động trong các năm.

1.10.5 Vốn chủ sở hữu

von_chu_so_huu <- financial_long %>% 
  filter(Khoan_muc == "Vốn chủ sở hữu")

summary(von_chu_so_huu$Gia_tri)
##          Min.       1st Qu.        Median          Mean       3rd Qu. 
## 1351864916364 1642203041286 1661873378313 1811084762568 2031740420803 
##          Max. 
## 2338250462750

Giải thích kỹ thuật:

von_chu_so_huu <- financial_long %>% filter(Khoan_muc == “Vốn chủ sở hữu”): lọc dữ liệu từ bảng financial_long, chỉ lấy các dòng có Khoan_muc là “Vốn chủ sở hữu”.

Nhận xét:

Kết quả cho ta thấy:

-Min: 1351864916364 (vốn chủ sở hữu thấp nhất trong bộ dữ liệu).

-1st Qu: 1642203041286 (giá trị vốn chủ sở hữu tại phần tư đầu tiên).

-Median: 1661873378313 (giá trị trung vị vốn chủ sở hữu).

-Mean: 1811084762568 (vốn chủ sở hữu trung bình).

-3rd Qu: 2031740420803 (giá trị vốn chủ sở hữu tại phần tư thứ ba).

-Max: 2338250462750 (vốn chủ sở hữu cao nhất).

Vốn chủ sở hữu có sự phân bổ đều với trung bình gần bằng trung vị. Tuy nhiên, giá trị Min và Max cho thấy sự biến động giữa các năm, với mức chênh lệch giữa cao nhất và thấp nhất khá lớn.

1.10.6 Nợ phải trả

no_phai_tra <- financial_long %>% 
  filter(Khoan_muc == "Nợ phải trả")

summary(no_phai_tra$Gia_tri)
##          Min.       1st Qu.        Median          Mean       3rd Qu. 
## 3326016900969 4327035110263 4618368798190 5393338461567 6871166355039 
##          Max. 
## 7826753005585

Giải thích kỹ thuật:

no_phai_tra <- financial_long %>% filter(Khoan_muc == “Nợ phải trả”): lọc dữ liệu từ bảng financial_long, chỉ lấy các dòng có Khoan_muc là “Nợ phải trả”.

Nhận xét:

Kết quả cho ta thấy:

-Min: 3326016900969 (nợ phải trả thấp nhất trong bộ dữ liệu).

-1st Qu: 4327035110263 (giá trị nợ phải trả tại phần tư đầu tiên).

-Median: 4618368798190 (giá trị trung vị nợ phải trả).

-Mean: 5393338461567 (nợ phải trả trung bình).

-3rd Qu: 6871166355039 (giá trị nợ phải trả tại phần tư thứ ba).

-Max: 7826753005585 (nợ phải trả cao nhất).

Dữ liệu về nợ phải trả có sự phân bố tương đối đều, với giá trị trung bình gần bằng trung vị, nhưng vẫn có sự khác biệt rõ rệt giữa giá trị Min và Max, cho thấy sự biến động trong các năm.

1.10.7 Tài sản ngắn hạn

tai_san_ngan_han <- financial_long %>% 
  filter(Khoan_muc == "Tài sản ngắn hạn")

summary(tai_san_ngan_han$Gia_tri)
##          Min.       1st Qu.        Median          Mean       3rd Qu. 
## 3715299217677 4752649709863 5182557972812 6013984109254 7608300769556 
##          Max. 
## 9021308150964

Giải thích kỹ thuật:

tai_san_ngan_han <- financial_long %>% filter(Khoan_muc == “Tài sản ngắn hạn”): lọc dữ liệu từ bảng financial_long, chỉ lấy các dòng có Khoan_muc là “Tài sản ngắn hạn”.

Nhận xét:

Kết quả cho ta thấy:

-Min: 3715299217677 (tài sản ngắn hạn thấp nhất trong bộ dữ liệu).

-1st Qu: 4752649709863 (giá trị tài sản ngắn hạn tại phần tư đầu tiên).

-Median: 5182557972812 (giá trị trung vị tài sản ngắn hạn).

-Mean: 6013984109254 (tài sản ngắn hạn trung bình).

-3rd Qu: 7608300769556 (giá trị tài sản ngắn hạn tại phần tư thứ ba).

-Max: 9021308150964 (tài sản ngắn hạn cao nhất).

Tài sản ngắn hạn có sự phân bổ khá đều, với giá trị trung bình gần bằng trung vị, cho thấy không có sự biến động lớn trong các năm. Tuy nhiên, sự khác biệt giữa Min và Max vẫn chỉ ra một sự thay đổi đáng kể trong tài sản ngắn hạn của công ty.

1.10.8 Lưu chuyển tiền thuần từ hoạt động kinh doanh

LCTT_kd <- financial_long %>% 
  filter(Khoan_muc == "Lưu chuyển tiền thuần từ hoạt động kinh doanh")

summary(LCTT_kd$Gia_tri)
##          Min.       1st Qu.        Median          Mean       3rd Qu. 
## -299922459113 -123438896491  100677898435   84365265466  284814472034 
##          Max. 
##  399996509899

Giải thích kỹ thuật:

LCTT_kd <- financial_long %>% filter(Khoan_muc == “Lưu chuyển tiền thuần từ hoạt động kinh doanh”): lọc dữ liệu từ bảng financial_long, chỉ lấy các dòng có Khoan_muc là “Lưu chuyển tiền thuần từ hoạt động kinh doanh”.

Nhận xét:

Kết quả cho ta thấy:

-Min: -299922459113 (giá trị âm cho thấy công ty có dòng tiền thuần từ hoạt động kinh doanh âm trong một số năm).

-1st Qu: -1234338896491 (giá trị này cho thấy dòng tiền thuần từ hoạt động kinh doanh ở phần tư đầu tiên là âm, có thể phản ánh sự thiếu hụt dòng tiền trong giai đoạn này.)

-Median: 100677898435 (trung vị dương cho thấy hầu hết các giá trị dòng tiền thuần trong dữ liệu là dương).

-Mean: 84365265466 (dòng tiền thuần trung bình khá cao.)

-3rd Qu: 284814472034 (giá trị này cho thấy 75% các giá trị dòng tiền thuần từ hoạt động kinh doanh lớn hơn mức này).

-Max: 399996509899 (dòng tiền thuần cao nhất cho thấy sự tăng trưởng mạnh trong một số năm).

Dữ liệu cho thấy có sự biến động lớn trong dòng tiền thuần từ hoạt động kinh doanh, với một số năm có dòng tiền âm, nhưng phần lớn thời gian, dòng tiền thuần có giá trị dương.

1.10.9 Lưu chuyển tiền thuần từ hoạt động đầu tư

LCTT_dt <- financial_long %>% 
  filter(Khoan_muc == "Lưu chuyển tiền thuần từ hoạt động đầu tư")

summary(LCTT_dt$Gia_tri)
##           Min.        1st Qu.         Median           Mean        3rd Qu. 
## -1572395739006  -400243564455   -60206148614  -284015628533   -14269632858 
##           Max. 
##    49201566184

Giải thích kỹ thuật:

LCTT_dt <- financial_long %>% filter(Khoan_muc == “Lưu chuyển tiền thuần từ hoạt động đầu tư”): lọc dữ liệu từ bảng financial_long, chỉ lấy các dòng có Khoan_muc là “Lưu chuyển tiền thuần từ hoạt động đầu tư”.

Nhận xét:

Kết quả cho ta thấy:

-Min: -1572395739006 (giá trị âm cho thấy công ty có dòng tiền thuần từ hoạt động đầu tư âm trong một số năm).

-1st Qu: -400243564455 (giá trị âm cho thấy công ty gặp khó khăn trong hoạt động đầu tư trong phần tư đầu tiên).

-Median: -60206148614 (trung vị âm, cho thấy phần lớn các giá trị dòng tiền thuần từ hoạt động đầu tư đều âm).

-Mean: -284015628533 (dòng tiền thuần từ hoạt động đầu tư trung bình là âm, phản ánh sự suy giảm trong hoạt động đầu tư của công ty).

-3rd Qu: -14269632858 (dòng tiền thuần từ hoạt động đầu tư tại phần tư thứ ba vẫn âm, mặc dù có sự cải thiện so với phần tư đầu tiên).

-Max: 49201566184 (giá trị cao nhất cho thấy công ty có thể có dòng tiền thuần tích cực trong một số năm).

Dữ liệu cho thấy dòng tiền thuần từ hoạt động đầu tư chủ yếu là âm, với một số năm có sự chuyển biến tích cực, nhưng hầu hết thời gian công ty gặp khó khăn trong việc tạo ra dòng tiền từ hoạt động đầu tư.

1.10.10 Lưu chuyển tiền thuần từ hoạt động tài chính

LCTT_tc <- financial_long %>% 
  filter(Khoan_muc == "Lưu chuyển tiền thuần từ hoạt động tài chính")

summary(LCTT_tc$Gia_tri)
##          Min.       1st Qu.        Median          Mean       3rd Qu. 
## -905025578158 -473172929027  331916416066  170765865787  754873264568 
##          Max. 
## 1079101808108

Giải thích kỹ thuật:

LCTT_tc <- financial_long %>% filter(Khoan_muc == “Lưu chuyển tiền thuần từ hoạt động tài chính”): lọc dữ liệu từ bảng financial_long, chỉ lấy các dòng có Khoan_muc là “Lưu chuyển tiền thuần từ hoạt động tài chính”.

Nhận xét:

Kết quả cho ta thấy:

-Min: -905025578158 (giá trị âm cho thấy công ty có dòng tiền thuần từ hoạt động tài chính âm trong một số năm).

-1st Qu: -473172929027 (giá trị âm cho thấy dòng tiền thuần từ hoạt động tài chính tại phần tư đầu tiên là âm).

-Median: 331916416066 (trung vị dương cho thấy phần lớn các giá trị dòng tiền thuần từ hoạt động tài chính là dương).

-Mean: 170765865787 (dòng tiền thuần từ hoạt động tài chính trung bình dương).

-3rd Qu: 754873264568 (giá trị dòng tiền thuần từ hoạt động tài chính tại phần tư thứ ba khá cao).

-Max: 1079101808108 (dòng tiền thuần từ hoạt động tài chính cao nhất cho thấy sự tăng trưởng mạnh mẽ trong một số năm).

Dữ liệu cho thấy dòng tiền thuần từ hoạt động tài chính có sự dao động lớn, với một số năm có giá trị âm, nhưng đa số các năm có dòng tiền thuần dương. Sự khác biệt giữa Min và Max cho thấy sự biến động trong hoạt động tài chính của công ty.

Nội Dung 2: Xử lý dữ liệu thô, mã hóa dữ liệu

library(dplyr)
library(tidyr)
library(ggplot2)
library(cluster)
library(factoextra)
library(scales)
library(skimr)
library(kableExtra)

2.1. Làm sạch & chuẩn hóa định dạng số liệu

financial_long <- financial_selected %>%
pivot_longer(cols = -c(Nguon, Khoan_muc),
names_to = "Nam", values_to = "Gia_tri") %>%
mutate(Nam = as.numeric(Nam),
Gia_tri = as.numeric(Gia_tri)) %>%
drop_na(Gia_tri) %>%
filter(Gia_tri > 0)

skim(financial_long)
Data summary
Name financial_long
Number of rows 83
Number of columns 4
_______________________
Column type frequency:
character 2
numeric 2
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
Nguon 0 1 1 1 0 3 0
Khoan_muc 0 1 11 45 0 10 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
Nam 0 1 2019 2.91 2015 2017 2019 2022 2024 ▇▇▇▇▇
Gia_tri 0 1 4296346133324 4655880073835.54 4118698885 634815225398 2186259767890 6765248368472 19043708488035 ▇▃▂▁▁

Giải thích kỹ thuật:

pivot_longer(): dữ liệu ban đầu được chuyển sang dạng “long”, trong đó các cột Nguon và Khoan_muc được gom lại thành các cột Nam và Gia_tri (giá trị của từng năm).

mutate(): chuyển đổi cột Nam (năm) và Gia_tri (giá trị) thành kiểu số (numeric).

drop_na(Gia_tri): loại bỏ các giá trị thiếu trong cột Gia_tri (dữ liệu không đầy đủ).

filter(Gia_tri > 0): loại bỏ các giá trị nhỏ hơn hoặc bằng 0, chỉ giữ lại các giá trị hợp lệ.

Hàm skim() từ thư viện skimr được sử dụng để hiển thị thống kê mô tả cho bảng dữ liệu đã được xử lý, bao gồm các thông tin về kiểu dữ liệu, tỷ lệ hoàn chỉnh, giá trị min, max, và phân phối của dữ liệu.

Nhận xét:

Kết quả cho ta thấy:

-Số dòng và số cột: bảng financial_long có 83 dòng và 4 cột, bao gồm các cột Nguon, Khoan_muc, Nam (năm), và Gia_tri (giá trị của từng chỉ tiêu tài chính).

-Kiểu dữ liệu: cột “Nguon” và “Khoan_muc” có kiểu character, trong khi “Nam” và “Gia_tri” có kiểu numeric.

-Tỷ lệ hoàn chỉnh (complete_rate): tất cả các giá trị trong bảng đều đầy đủ, không có giá trị thiếu (NA).

-Số lượng giá trị duy nhất (n_unique): cột “Nguon” có 3 giá trị duy nhất (các nguồn CDKT, LCTT, KQKD), và cột Khoan_muc có 10 giá trị duy nhất (10 chỉ tiêu tài chính được phân tích).

-Min, Max của cột Gia_tri thể hiện giá trị nhỏ nhất và lớn nhất trong các chỉ tiêu tài chính.

-Mean (trung bình) của Gia_tri là 42963461333324, cho thấy sự thay đổi lớn trong các chỉ tiêu tài chính qua các năm.

2.2. Chuẩn hóa đơn vị về “tỷ đồng”

financial_long <- financial_long %>%
mutate(Gia_tri_ty = round(Gia_tri / 1e9, 2))
# Hiển thị bảng kết quả rõ ràng
knitr::kable(head(financial_long, 10), caption = "Bảng giá trị sau khi quy đổi về tỷ đồng")
Bảng giá trị sau khi quy đổi về tỷ đồng
Nguon Khoan_muc Nam Gia_tri Gia_tri_ty
1 Tài sản ngắn hạn 2015 5282004772886 5282
1 Tài sản ngắn hạn 2016 4741485264868 4741
1 Tài sản ngắn hạn 2017 4786143044848 4786
1 Tài sản ngắn hạn 2018 4330671814214 4331
1 Tài sản ngắn hạn 2019 3715299217677 3715
1 Tài sản ngắn hạn 2020 5083111172737 5083
1 Tài sản ngắn hạn 2021 7164336982272 7164
1 Tài sản ngắn hạn 2022 7756288698651 7756
1 Tài sản ngắn hạn 2023 8259191973425 8259
1 Tài sản ngắn hạn 2024 9021308150964 9021

Giải thích kỹ thuật:

mutate(Gia_tri_ty = round(Gia_tri / 1e9, 2)): dòng code này chia giá trị trong cột Gia_tri (tính bằng đồng) cho 1e9 (1 tỷ)

knitr::kable(): hàm này được sử dụng để tạo bảng trong báo cáo RMarkdown.

Nhận xét:

Các giá trị trong cột “Gia_tri” (tính bằng đồng) đã được chuyển đổi sang “Gia_tri_ty” (tính bằng tỷ đồng).

Ví dụ: giá trị 5282004772886 trong năm 2015 đã được chia cho 1e9 (1 tỷ) và làm tròn tới 2 chữ số thập phân, kết quả là 5282 tỷ.

Quy đổi các giá trị tài chính từ đồng sang tỷ đồng giúp cho việc đọc và so sánh trở nên dễ dàng hơn, đặc biệt khi làm việc với các dữ liệu lớn như báo cáo tài chính của các công ty. “Gia_tri_ty” cung cấp cái nhìn tổng quan về quy mô tài chính của công ty trong một khoảng thời gian dài, từ 2015 đến 2024. Sự thay đổi trong giá trị tài chính qua các năm sẽ trở nên rõ ràng hơn khi giá trị đã được quy đổi.

2.3. Phân nhóm khoản mục theo lĩnh vực tài chính

financial_long <- financial_long %>%
mutate(Nhom = case_when(
Khoan_muc %in% c("Doanh thu thuần", "Lợi nhuận gộp", "Lợi nhuận sau thuế") ~ "Hiệu quả kinh doanh",
Khoan_muc %in% c("Tổng tài sản", "Tài sản ngắn hạn") ~ "Tài sản",
Khoan_muc %in% c("Vốn chủ sở hữu", "Nợ phải trả") ~ "Cơ cấu nguồn vốn",
grepl("Lưu chuyển", Khoan_muc) ~ "Dòng tiền",
TRUE ~ "Khác"
))
table(financial_long$Nhom)
## 
##    Cơ cấu nguồn vốn           Dòng tiền Hiệu quả kinh doanh             Tài sản 
##                  20                  13                  30                  20

Giải thích kỹ thuật:

mutate(): dùng để thêm cột Nhom vào bảng financial_long. Cột này phân nhóm các khoản mục tài chính vào các nhóm cụ thể.

Hàm điều kiện case_when() giúp phân loại các giá trị trong cột Khoan_muc thành các nhóm khác nhau:

-Các khoản mục như “Doanh thu thuần”, “Lợi nhuận gộp”, và “Lợi nhuận sau thuế” được nhóm vào “Hiệu quả kinh doanh”.

-Các khoản mục “Tổng tài sản”, “Tài sản ngắn hạn” được nhóm vào “Tài sản”.

-Các khoản mục “Vốn chủ sở hữu”, “Nợ phải trả” được nhóm vào “Cơ cấu nguồn vốn”.

-Các khoản mục chứa từ “Lưu chuyển” được nhóm vào “Dòng tiền”.

-Nếu không thuộc các nhóm trên, các khoản mục khác sẽ được gán vào nhóm “Khác”.

Nhận xét:

Kết quả chạy ra:

-“Cơ cấu nguồn vốn” có 20 khoản mục.

-“Dòng tiền” có 13 khoản mục.

-“Hiệu quả kinh doanh” có 30 khoản mục.

-“Tài sản” có 20 khoản mục.

Các nhóm được phân loại rõ ràng và giúp chúng ta thấy được sự phân bổ các chỉ tiêu tài chính trong các lĩnh vực cụ thể như Hiệu quả kinh doanh, Tài sản, Dòng tiền, và Cơ cấu nguồn vốn. Việc phân loại này giúp cho việc phân tích các chỉ tiêu tài chính trở nên dễ dàng hơn, từ đó nhận diện rõ các yếu tố liên quan đến hiệu quả tài chính, cơ cấu tài sản, cũng như sự thay đổi của dòng tiền trong các năm.

2.4. Tính tăng trưởng theo năm cho từng chỉ tiêu

financial_long <- financial_long %>%
group_by(Khoan_muc) %>%
arrange(Nam) %>%
mutate(Tang_truong = round((Gia_tri_ty / lag(Gia_tri_ty) - 1) * 100, 2)) %>%
ungroup()
knitr::kable(head(financial_long, 15), caption = "Bảng tỷ lệ tăng trưởng theo năm cho từng chỉ tiêu (%)")
Bảng tỷ lệ tăng trưởng theo năm cho từng chỉ tiêu (%)
Nguon Khoan_muc Nam Gia_tri Gia_tri_ty Nhom Tang_truong
1 Tài sản ngắn hạn 2015 5282004772886 5282 Tài sản
1 Tổng tài sản 2015 5764543759971 5765 Tài sản
1 Nợ phải trả 2015 4265313985196 4265 Cơ cấu nguồn vốn
1 Vốn chủ sở hữu 2015 1351864916364 1352 Cơ cấu nguồn vốn
2 Lưu chuyển tiền thuần từ hoạt động kinh doanh 2015 386645716621 387 Dòng tiền
3 Doanh thu thuần 2015 10652450030513 10652 Hiệu quả kinh doanh
3 Lợi nhuận gộp 2015 818837334301 819 Hiệu quả kinh doanh
3 Lợi nhuận sau thuế 2015 211894833616 212 Hiệu quả kinh doanh
1 Tài sản ngắn hạn 2016 4741485264868 4741 Tài sản -10.23
1 Tổng tài sản 2016 6227006416270 6227 Tài sản 8.02
1 Nợ phải trả 2016 4579147052293 4579 Cơ cấu nguồn vốn 7.36
1 Vốn chủ sở hữu 2016 1647859363977 1648 Cơ cấu nguồn vốn 21.90
2 Lưu chuyển tiền thuần từ hoạt động kinh doanh 2016 202890081636 203 Dòng tiền -47.53
2 Lưu chuyển tiền thuần từ hoạt động tài chính 2016 525662249424 526 Dòng tiền
3 Doanh thu thuần 2016 9882058966471 9882 Hiệu quả kinh doanh -7.23

Giải thích kỹ thuật:

group_by(Khoan_muc): nhóm dữ liệu theo từng khoản mục (Khoan_muc) (nhóm theo các chỉ tiêu tài chính như Doanh thu thuần, Lợi nhuận gộp, Tài sản, …).

arrange(Nam): sắp xếp các dữ liệu theo năm (Nam) để việc tính toán tỷ lệ tăng trưởng theo thời gian được chính xác.

mutate(): tính tỷ lệ tăng trưởng hàng năm cho các chỉ tiêu tài chính.

lag(Gia_tri_ty): hàm lag() trả về giá trị của năm trước để tính tỷ lệ tăng trưởng.

Tỷ lệ tăng trưởng tính bằng công thức: \[ \text{Tỷ lệ tăng trưởng} = \frac{\text{Giá trị năm hiện tại}}{\text{Giá trị năm trước}} - 1 \] Sau đó, kết quả được nhân với 100 để ra tỷ lệ phần trăm và làm tròn đến 2 chữ số thập phân.

ungroup(): Hủy nhóm dữ liệu sau khi tính toán xong.

Nhận xét:

Kết quả tính toán tỷ lệ tăng trưởng cho thấy sự thay đổi của các chỉ tiêu tài chính qua các năm. Ví dụ:

-Tài sản ngắn hạn từ năm 2015 đến 2016 giảm 10.23%, cho thấy có sự suy giảm trong tài sản này.

-Doanh thu thuần có sự tăng trưởng mạnh mẽ, từ 2015 đến 2016, từ 10652 tỷ đồng lên 9882 tỷ đồng, với tỷ lệ giảm khoảng -7.23%.

Các chỉ tiêu khác như Lợi nhuận sau thuế, Dòng tiền, và Vốn chủ sở hữu đều có những biến động đáng chú ý theo từng năm.

2.5. Phân loại trạng thái tăng trưởng

financial_long <- financial_long %>%
mutate(Trang_thai = case_when(
Tang_truong > 15 ~ "Tăng mạnh",
Tang_truong >= 0 & Tang_truong <= 15 ~ "Tăng nhẹ",
Tang_truong < 0 ~ "Suy giảm"
))
table(financial_long$Trang_thai)
## 
##  Suy giảm Tăng mạnh  Tăng nhẹ 
##        29        19        25

Giải thích kỹ thuật:

Hàm mutate() dùng để thêm một cột mới “Trang_thai” vào bảng financial_long để phân loại trạng thái tăng trưởng dựa trên tỷ lệ tăng trưởng “Tang_truong”.

Hàm điều kiện case_when() giúp phân loại dữ liệu vào các nhóm cụ thể:

-Nếu tỷ lệ tăng trưởng Tang_truong lớn hơn 15%, nhóm vào “Tăng mạnh”.

-Nếu tỷ lệ tăng trưởng nằm trong khoảng 0 ≤ Tang_truong ≤ 15%, nhóm vào “Tăng nhẹ”.

-Nếu tỷ lệ tăng trưởng nhỏ hơn 0, nhóm vào “Suy giảm”.

Nhận xét:

Kết quả cho ta thấy:

-Suy giảm có 29 chỉ tiêu.

-Tăng mạnh có 19 chỉ tiêu.

-Tăng nhẹ có 25 chỉ tiêu.

Bảng phân loại cho thấy số lượng các chỉ tiêu tài chính có tỷ lệ tăng trưởng khác nhau qua các năm. Suy giảm chiếm số lượng lớn nhất (29), có thể cho thấy một số chỉ tiêu tài chính của công ty đang gặp khó khăn và không duy trì được mức tăng trưởng tích cực. Tăng mạnh và Tăng nhẹ thể hiện sự tăng trưởng trong hoạt động tài chính nhưng mức độ khác nhau, với Tăng mạnh chỉ chiếm 19 chỉ tiêu, cho thấy chỉ có một số ít chỉ tiêu có sự cải thiện đáng kể.

2.6. Xử lý ngoại lệ bằng IQR method

# Tính Q1, Q3 và IQR
Q1 <- quantile(financial_long$Gia_tri_ty, 0.25, na.rm = TRUE)
Q3 <- quantile(financial_long$Gia_tri_ty, 0.75, na.rm = TRUE)
IQR_value <- Q3 - Q1

# Loại bỏ ngoại lệ dựa trên khoảng IQR
financial_long <- financial_long %>%
  filter(Gia_tri_ty > (Q1 - 1.5 * IQR_value) & Gia_tri_ty < (Q3 + 1.5 * IQR_value))

# Kiểm tra kết quả sau khi lọc
summary(financial_long$Gia_tri_ty)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
##     4.12   568.39  1939.73  3610.05  6199.90 13453.25

Giải thích kỹ thuật:

Hàm quantile() dùng để tính các phần trăm của phân phối dữ liệu, trong đó:

-Q1 (25%): phần trăm thứ nhất của dữ liệu, tức là giá trị nằm ở ngưỡng 25% phân phối.

-Q3 (75%): phần trăm thứ ba của dữ liệu, tức là giá trị nằm ở ngưỡng 75% phân phối.

-IQR (Interquartile Range): khoảng cách giữa Q1 và Q3, đo lường độ phân tán của dữ liệu giữa 25% và 75% dữ liệu.

filter(): Lọc các giá trị ngoại lệ. Các giá trị nằm ngoài phạm vi: \[ Q1 - 1.5 \times IQR \quad \text{và} \quad Q3 + 1.5 \times IQR \] được xem là ngoại lệ và sẽ bị loại bỏ.

Nhận xét:

Kết quả cho ta thấy:

-Min (giá trị nhỏ nhất): 4.12

-1st Qu (Q1): 568.39

-Median (giá trị trung vị): 1939.73

-Mean (trung bình): 3610.05

-3rd Qu (Q3): 6199.90

-Max (giá trị lớn nhất): 13453.25

Sau khi loại bỏ ngoại lệ, các giá trị min và max thay đổi một cách rõ rệt, và các giá trị cực đoan đã được loại bỏ. Dữ liệu giờ đã tập trung vào các giá trị từ Q1 đến Q3 (khoảng từ 568 đến 6199) và trung bình đã tăng lên đáng kể (từ 3610.05). Điều này cho thấy dữ liệu đã được lọc để chỉ giữ lại các giá trị hợp lý, và loại bỏ những giá trị ngoài phạm vi chấp nhận.

2.7. Chuẩn hóa dữ liệu (Min–Max Scaling)

scaled_data <- financial_long %>%
group_by(Khoan_muc) %>%
mutate(Gia_tri_scaled = rescale(Gia_tri_ty, to = c(0, 1)))
# Hiển thị 10 dòng đầu tiên sau khi chuẩn hóa
knitr::kable(head(scaled_data, 10), caption = "Bảng dữ liệu sau khi chuẩn hóa Min–Max (thang 0–1)")
Bảng dữ liệu sau khi chuẩn hóa Min–Max (thang 0–1)
Nguon Khoan_muc Nam Gia_tri Gia_tri_ty Nhom Tang_truong Trang_thai Gia_tri_scaled
1 Tài sản ngắn hạn 2015 5282004772886 5282 Tài sản 0.295
1 Tổng tài sản 2015 5764543759971 5765 Tài sản 0.154
1 Nợ phải trả 2015 4265313985196 4265 Cơ cấu nguồn vốn 0.209
1 Vốn chủ sở hữu 2015 1351864916364 1352 Cơ cấu nguồn vốn 0.000
2 Lưu chuyển tiền thuần từ hoạt động kinh doanh 2015 386645716621 387 Dòng tiền 0.966
3 Doanh thu thuần 2015 10652450030513 10652 Hiệu quả kinh doanh 0.216
3 Lợi nhuận gộp 2015 818837334301 819 Hiệu quả kinh doanh 0.584
3 Lợi nhuận sau thuế 2015 211894833616 212 Hiệu quả kinh doanh 0.459
1 Tài sản ngắn hạn 2016 4741485264868 4741 Tài sản -10.23 Suy giảm 0.193
1 Tổng tài sản 2016 6227006416270 6227 Tài sản 8.02 Tăng nhẹ 0.243

Giải thích kỹ thuật:

Hàm rescale() dùng để thực hiện việc chuẩn hóa dữ liệu bằng phương pháp Min-Max Scaling. Phương pháp này chuyển đổi tất cả giá trị của một cột về thang điểm từ 0 đến 1, với công thức: \[ X_{\text{scaled}} = \frac{X - \min(X)}{\max(X) - \min(X)} \] Sau khi chuẩn hóa, các giá trị của biến Gia_tri_ty sẽ nằm trong khoảng từ 0 đến 1.

group_by(Khoan_muc): Dữ liệu được nhóm theo từng khoản mục tài chính (Khoan_muc) để áp dụng chuẩn hóa cho từng nhóm riêng biệt.

Nhận xét:

Sau khi thực hiện Min-Max scaling, tất cả các giá trị của biến đã được chuẩn hóa về thang điểm từ 0 đến 1, giúp dữ liệu trở nên đồng nhất và dễ dàng so sánh với nhau. Giá trị_scaled giúp cho việc phân tích dữ liệu dễ dàng hơn, khi mà các giá trị có thể có mức độ biến động rất lớn (như tài sản hay doanh thu), giờ đã được chuyển về một thang đo thống nhất.

Các biến tăng trưởng của mỗi khoản mục tài chính có thể dễ dàng so sánh hơn khi tất cả đều đã có giá trị chuẩn hóa trong khoảng từ 0 đến 1. Ví dụ, năm 2015, khoản mục Tài sản ngắn hạn có Giá trị_scaled = 0.295 nghĩa là giá trị tài sản này nằm ở mức 29.5% trong thang điểm 0-1 so với các giá trị tối thiểu và tối đa trong phạm vi nghiên cứu.

2.8. Biến logarit để giảm độ lệch phân phối

financial_long <- financial_long %>%
mutate(Log_value = log1p(Gia_tri_ty))
# Hiển thị 10 dòng đầu tiên sau khi biến đổi logarit
knitr::kable(head(financial_long, 10), caption = "Bảng dữ liệu sau khi biến đổi logarit (log1p)")
Bảng dữ liệu sau khi biến đổi logarit (log1p)
Nguon Khoan_muc Nam Gia_tri Gia_tri_ty Nhom Tang_truong Trang_thai Log_value
1 Tài sản ngắn hạn 2015 5282004772886 5282 Tài sản 8.57
1 Tổng tài sản 2015 5764543759971 5765 Tài sản 8.66
1 Nợ phải trả 2015 4265313985196 4265 Cơ cấu nguồn vốn 8.36
1 Vốn chủ sở hữu 2015 1351864916364 1352 Cơ cấu nguồn vốn 7.21
2 Lưu chuyển tiền thuần từ hoạt động kinh doanh 2015 386645716621 387 Dòng tiền 5.96
3 Doanh thu thuần 2015 10652450030513 10652 Hiệu quả kinh doanh 9.27
3 Lợi nhuận gộp 2015 818837334301 819 Hiệu quả kinh doanh 6.71
3 Lợi nhuận sau thuế 2015 211894833616 212 Hiệu quả kinh doanh 5.36
1 Tài sản ngắn hạn 2016 4741485264868 4741 Tài sản -10.23 Suy giảm 8.46
1 Tổng tài sản 2016 6227006416270 6227 Tài sản 8.02 Tăng nhẹ 8.74

Giải thích kỹ thuật:

Hàm log1p() thực hiện phép toán logarit tự nhiên trên giá trị của biến Gia_tri_ty và sử dụng công thức: \[ \log(x + 1) \] Công thức này giúp tránh các vấn đề với giá trị bằng 0, vì logarit của 0 không được xác định. log1p() sẽ tính toán logarit của (x+1), phù hợp với các giá trị có thể là 0 hoặc nhỏ hơn 1, thường gặp trong các phép phân tích tài chính.

mutate(): Được sử dụng để tạo ra cột mới Log_value chứa giá trị logarit của Gia_tri_ty.

head(): Hiển thị 10 dòng đầu tiên sau khi áp dụng phép biến đổi logarit để kiểm tra dữ liệu.

Nhận xét:

Sử dụng logarit giúp giảm độ lệch phân phối của các giá trị tài chính (như tài sản, doanh thu), khiến chúng dễ phân tích hơn, đặc biệt là khi có sự chênh lệch quá lớn giữa các giá trị. Logarithm giúp giảm ảnh hưởng của các giá trị cực đoan, biến đổi giá trị tài chính để chúng có thể so sánh dễ dàng hơn, chẳng hạn như khi so sánh tỷ suất lợi nhuận của các năm, có thể thấy rõ mức độ biến động của các khoản mục tài chính.

Sau khi áp dụng logarit cho các giá trị tỷ, ta thấy các khoản mục tài chính đã có mức độ biến động nhỏ hơn và dễ dàng so sánh giữa các năm. Các khoản mục như Tài sản ngắn hạn hay Tổng tài sản có Log_value tương đối cao, cho thấy sự ổn định trong giá trị qua các năm. Ngược lại, những khoản mục có sự thay đổi mạnh như Lợi nhuận gộp có Log_value thấp hơn, cho thấy các biến động mạnh hơn trong kết quả tài chính của công ty.

2.9. Phát hiện outlier bằng z-score

financial_long <- financial_long %>%
mutate(zscore = (Gia_tri_ty - mean(Gia_tri_ty)) / sd(Gia_tri_ty)) %>%
mutate(Outlier = ifelse(abs(zscore) > 3, "Có", "Không"))
table(financial_long$Outlier)
## 
## Không 
##    79

Giải thích kỹ thuật:

Hàm mutate() dùng để tính toán z-score của từng giá trị trong biến Gia_tri_ty. Z-score cho phép đo lường mức độ khác biệt của một giá trị so với trung bình của toàn bộ dữ liệu, tính theo đơn vị độ lệch chuẩn (standard deviation).

Công thức tính z-score: \[ \text{z-score} = \frac{X - \mu}{\sigma} \] Trong đó, X là giá trị cần tính, μ là giá trị trung bình, và σ là độ lệch chuẩn.

Lệnh mutate(Outlier = ifelse(abs(zscore) > 3, “Có”, “Không”)): tiến hành phân loại xem giá trị có phải là outlier hay không dựa vào z-score. Nếu giá trị tuyệt đối của z-score lớn hơn 3, thì đó được coi là một outlier (ngoài 3 độ lệch chuẩn so với giá trị trung bình).

ifelse() là hàm điều kiện trong R, giúp phân loại các giá trị trong cột Outlier.

Nhận xét:

Dữ liệu cho thấy không có giá trị nào trong bộ dữ liệu được xem là outlier (mức z-score của tất cả các giá trị đều nhỏ hơn 3). Điều này có thể cho thấy dữ liệu khá ổn định, không có các giá trị cực kỳ bất thường làm sai lệch các phân tích tài chính, giúp cho việc phân tích trở nên đáng tin cậy hơn.

2.10. Kiểm định phân phối chuẩn (Shapiro–Wilk)

# Kiểm định phân phối chuẩn bằng Shapiro–Wilk (chạy được với mọi kích thước dữ liệu)
set.seed(123)

# Nếu dữ liệu có dưới 100 dòng, chỉ lấy tối đa bằng số dòng hiện có
sample_size <- min(100, nrow(financial_long))

# Lấy mẫu và thực hiện kiểm định
shapiro.test(financial_long$Gia_tri_ty[sample(1:nrow(financial_long), sample_size, replace = FALSE)])
## 
##  Shapiro-Wilk normality test
## 
## data:  financial_long$Gia_tri_ty[sample(1:nrow(financial_long), sample_size, replace = FALSE)]
## W = 0.9, p-value = 0.0000005

Giải thích kỹ thuật:

set.seed(123): đảm bảo tính tái lập của việc chọn mẫu ngẫu nhiên, giúp mỗi lần chạy code cho kết quả giống nhau.

sample_size <- min(100, nrow(financial_long)): kiểm tra kích thước dữ liệu. Nếu dữ liệu có dưới 100 dòng, thì sẽ lấy hết; nếu lớn hơn 100 dòng, chỉ lấy tối đa 100 dòng để giảm chi phí tính toán.

sample(): chọn ngẫu nhiên một mẫu với số lượng sample_size từ toàn bộ dữ liệu mà không thay thế (replace = FALSE).

shapiro.test(): kiểm định Shapiro-Wilk dùng để kiểm tra xem mẫu dữ liệu có phân phối chuẩn hay không. Hàm này sẽ trả về giá trị thống kê W và p-value.

Nhận xét:

W = 0.9: giá trị thống kê của kiểm định Shapiro-Wilk.

p-value = 0.000005: p-value rất nhỏ, điều này có nghĩa là ta bác bỏ giả thuyết không có sự khác biệt với phân phối chuẩn (tức là dữ liệu không phân phối chuẩn). Kết quả kiểm định cho thấy p-value rất nhỏ (<< 0.05), điều này chứng tỏ dữ liệu không phân phối chuẩn

Nội Dung 3: Thống kê cơ bản

library(dplyr)
library(psych)
library(knitr)
library(corrplot)

3.1. Số dòng, số cột và số khoản mục

nrow(financial_long); ncol(financial_long)
## [1] 79
## [1] 11
length(unique(financial_long$Khoan_muc))
## [1] 10

Giải thích kỹ thuật:

nrow(financial_long): đếm số dòng trong dữ liệu financial_long, giúp ta biết được tổng số các quan sát (số năm và các chỉ tiêu tài chính).

ncol(financial_long): đếm số cột trong dữ liệu financial_long, giúp xác định số lượng các biến (chỉ tiêu tài chính, năm, giá trị, …).

length(unique(financial_long$Khoan_muc)): đếm số lượng các giá trị duy nhất trong cột Khoan_muc, xác định số khoán mục (hoặc nhóm chỉ tiêu tài chính).

Nhận xét:

Số dòng và số cột cho thấy dữ liệu có đủ các chỉ tiêu tài chính trong các năm từ 2015 đến 2024, tổng cộng có 79 quan sát và 11 cột.

Số khoán mục cho thấy rằng có 10 nhóm chỉ tiêu tài chính khác nhau, bao gồm các chỉ tiêu về tài sản, nợ, doanh thu và lợi nhuận.

3.2. Trung bình và độ lệch chuẩn theo khoản mục

stats_2 <- financial_long %>%
group_by(Khoan_muc) %>%
summarise(Mean = mean(Gia_tri_ty),
SD = sd(Gia_tri_ty))
kable(stats_2)
Khoan_muc Mean SD
Doanh thu thuần 10965.2 1300.9
Lưu chuyển tiền thuần từ hoạt động kinh doanh 250.5 148.7
Lưu chuyển tiền thuần từ hoạt động tài chính 671.7 323.1
Lưu chuyển tiền thuần từ hoạt động đầu tư 49.2
Lợi nhuận gộp 768.6 127.6
Lợi nhuận sau thuế 175.8 57.6
Nợ phải trả 5393.3 1599.2
Tài sản ngắn hạn 6014.0 1858.6
Tổng tài sản 7219.2 1872.1
Vốn chủ sở hữu 1811.1 306.6

Giải thích kỹ thuật:

group_by(Khoan_muc): phân nhóm theo từng loại khoán mục trong dữ liệu tài chính.

summarise(Mean = mean(Gia_tri_ty), SD = sd(Gia_tri_ty)): tính toán trung bình (Mean) và độ lệch chuẩn (SD) cho mỗi khoán mục.

Nhận xét:

Doanh thu thuần có giá trị trung bình khá cao (10,965.2 tỷ đồng) và độ lệch chuẩn lớn (1,300.9), cho thấy sự biến động lớn trong các năm.

Các chỉ tiêu như lưu chuyển tiền thuần từ hoạt động đầu tư có giá trị trung bình thấp (49.2), nhưng lại không có độ lệch chuẩn (NA), do đó có thể thiếu dữ liệu hoặc không có sự thay đổi qua các năm.

Tài sản ngắn hạn, tổng tài sản, và vốn chủ sở hữu đều có mức trung bình và độ lệch chuẩn lớn, phản ánh tính chất biến động trong các năm tài chính.

3.3. Min–Max (phạm vi biến thiên)

stats_3 <- financial_long %>%
group_by(Khoan_muc) %>%
summarise(Min = min(Gia_tri_ty),
Max = max(Gia_tri_ty),
Range = Max - Min)
kable(stats_3)
Khoan_muc Min Max Range
Doanh thu thuần 9882.06 13453.2 3571
Lưu chuyển tiền thuần từ hoạt động kinh doanh 4.12 400.0 396
Lưu chuyển tiền thuần từ hoạt động tài chính 256.45 1079.1 823
Lưu chuyển tiền thuần từ hoạt động đầu tư 49.20 49.2 0
Lợi nhuận gộp 611.13 967.1 356
Lợi nhuận sau thuế 127.32 311.5 184
Nợ phải trả 3326.02 7826.8 4501
Tài sản ngắn hạn 3715.30 9021.3 5306
Tổng tài sản 4966.33 10165.0 5199
Vốn chủ sở hữu 1351.86 2338.2 986

Giải thích kỹ thuật:

group_by(Khoan_muc): phân nhóm dữ liệu theo các khoán mục trong bảng tài chính.

summarise(Min = min(Gia_tri_ty), Max = max(Gia_tri_ty), Range = Max - Min): tính giá trị nhỏ nhất (Min), lớn nhất (Max), và phạm vi biến thiên (Range) cho mỗi nhóm.

kable(stats_3): Hiển thị kết quả dưới dạng bảng dễ đọc.

Nhận xét:

Khoản mục dòng tiền có phạm vi biến thiên lớn nhất (4,501 tỷ đồng), điều này phản ánh sự biến động mạnh trong dòng tiền qua các năm, có thể do thay đổi lớn về thu chi hoặc các yếu tố ngoại lai tác động đến dòng tiền.

Lưu chuyển tiền thuần từ hoạt động đầu tư không có biến thiên (phạm vi bằng 0), cho thấy khoản mục này ổn định và không có sự thay đổi lớn qua các năm.

Các khoản mục khác như tổng tài sản và doanh thu thuần cũng có phạm vi biến thiên lớn, phản ánh sự thay đổi trong các chỉ số tài chính chính qua thời gian, từ đó giúp doanh nghiệp theo dõi sự phát triển và biến động trong tài chính.

3.4. Hệ số biến động (CV%)

stats_4 <- financial_long %>%
group_by(Khoan_muc) %>%
summarise(CV = round(sd(Gia_tri_ty)/mean(Gia_tri_ty)*100,2))
kable(stats_4)
Khoan_muc CV
Doanh thu thuần 11.9
Lưu chuyển tiền thuần từ hoạt động kinh doanh 59.3
Lưu chuyển tiền thuần từ hoạt động tài chính 48.1
Lưu chuyển tiền thuần từ hoạt động đầu tư
Lợi nhuận gộp 16.6
Lợi nhuận sau thuế 32.8
Nợ phải trả 29.6
Tài sản ngắn hạn 30.9
Tổng tài sản 25.9
Vốn chủ sở hữu 16.9

Giải thích kỹ thuật:

CV (Coefficient of Variation): là tỷ lệ giữa độ lệch chuẩn và giá trị trung bình, được dùng để đánh giá sự biến động tương đối của một dãy số liệu.

group_by(Khoan_muc): dữ liệu được phân nhóm theo các khoản mục tài chính.

summarise(CV = round(sd(Gia_tri_ty)/mean(Gia_tri_ty)*100, 2)): tính toán hệ số biến động (CV) cho mỗi nhóm bằng cách lấy độ lệch chuẩn chia cho giá trị trung bình, nhân với 100 và làm tròn đến 2 chữ số thập phân.

Nhận xét:

Khoản mục “Doanh thu thuần” có CV thấp nhất (11.9%), cho thấy doanh thu có sự ổn định cao và ít biến động qua các năm.

Khoản mục “Lưu chuyển tiền thuần từ hoạt động kinh doanh” có CV rất cao (59.3%), điều này cho thấy dòng tiền từ hoạt động kinh doanh có sự biến động lớn, có thể do thay đổi trong thu chi hoặc các yếu tố bên ngoài tác động.

Tài sản ngắn hạn và Nợ phải trả cũng có CV khá cao, phản ánh sự thay đổi không đồng đều trong các khoản mục này theo thời gian.

Các khoản mục tài sản và vốn chủ sở hữu có CV ở mức trung bình, cho thấy sự ổn định vừa phải trong các chỉ tiêu này.

3.5. Trung vị và độ lệch chuẩn chuẩn hóa (z)

stats_5 <- financial_long %>%
group_by(Khoan_muc) %>%
summarise(Median = median(Gia_tri_ty),
MAD = mad(Gia_tri_ty))
kable(stats_5)
Khoan_muc Median MAD
Doanh thu thuần 10677.9 803
Lưu chuyển tiền thuần từ hoạt động kinh doanh 257.5 140
Lưu chuyển tiền thuần từ hoạt động tài chính 678.5 388
Lưu chuyển tiền thuần từ hoạt động đầu tư 49.2 0
Lợi nhuận gộp 735.1 119
Lợi nhuận sau thuế 155.4 31
Nợ phải trả 4618.4 1459
Tài sản ngắn hạn 5182.6 1719
Tổng tài sản 6273.9 1496
Vốn chủ sở hữu 1661.9 237

Giải thích kỹ thuật:

Median (Trung vị): là giá trị giữa của dãy số khi đã sắp xếp, giúp đo lường xu hướng trung tâm của dữ liệu.

MAD (Mean Absolute Deviation – Độ lệch tuyệt đối trung bình): là chỉ số đo độ biến động của dữ liệu, tính bằng cách lấy trung bình của các giá trị tuyệt đối chênh lệch với giá trị trung bình. Đây là một chỉ số quan trọng để đo lường mức độ phân tán của dữ liệu mà không bị ảnh hưởng bởi các ngoại lệ quá lớn (outliers).

group_by(Khoan_muc): phân nhóm dữ liệu theo từng khoản mục tài chính.

summarise(Median = median(Gia_tri_ty), MAD = mad(Gia_tri_ty)): tính toán trung vị và độ lệch tuyệt đối trung bình cho mỗi nhóm.

Nhận xét:

Doanh thu thuần có trung vị cao (10677.9) và độ lệch chuẩn (MAD) tương đối lớn (803), cho thấy có sự biến động rõ rệt trong doanh thu.

Lưu chuyển tiền thuần từ hoạt động đầu tư có MAD = 0, nghĩa là không có sự thay đổi giữa các năm, biểu thị sự ổn định tuyệt đối.

Nợ phải trả và Tài sản ngắn hạn có MAD rất lớn (1459 và 1719), cho thấy các khoản mục này có sự biến động mạnh theo thời gian.

Lợi nhuận sau thuế và Lợi nhuận gộp có độ lệch chuẩn thấp, cho thấy mức độ ổn định của các khoản lợi nhuận này.

3.6. Kiểm tra phân phối chuẩn bằng Shapiro–Wilk

by(financial_long$Gia_tri_ty, financial_long$Nhom, shapiro.test)
## financial_long$Nhom: Cơ cấu nguồn vốn
## 
##  Shapiro-Wilk normality test
## 
## data:  dd[x, ]
## W = 0.9, p-value = 0.007
## 
## ------------------------------------------------------------ 
## financial_long$Nhom: Dòng tiền
## 
##  Shapiro-Wilk normality test
## 
## data:  dd[x, ]
## W = 0.9, p-value = 0.2
## 
## ------------------------------------------------------------ 
## financial_long$Nhom: Hiệu quả kinh doanh
## 
##  Shapiro-Wilk normality test
## 
## data:  dd[x, ]
## W = 0.6, p-value = 0.0000004
## 
## ------------------------------------------------------------ 
## financial_long$Nhom: Tài sản
## 
##  Shapiro-Wilk normality test
## 
## data:  dd[x, ]
## W = 0.9, p-value = 0.2

Giải thích kỹ thuật:

Shapiro-Wilk Test: là phương pháp kiểm tra phân phối chuẩn của dữ liệu, được sử dụng để đánh giá mức độ phù hợp của dữ liệu với phân phối chuẩn.

W: thống kê kiểm tra của Shapiro-Wilk, giá trị càng gần 1 thì phân phối càng gần chuẩn.

p-value: đánh giá khả năng sai số giữa phân phối thực tế và phân phối chuẩn. Nếu p-value nhỏ hơn mức ý nghĩa (thường là 0.05), ta bác bỏ giả thuyết phân phối chuẩn.

Kết quả:

Khoản mục “Cơ cấu nguồn vốn”:

-Shapiro-Wilk test: W = 0.9, p-value = 0.007 (p-value nhỏ, không đạt giả thuyết phân phối chuẩn).

Khoản mục “Dòng tiền”:

-Shapiro-Wilk test: W = 0.9, p-value = 0.2 (p-value lớn, phân phối gần chuẩn).

Khoản mục “Hiệu quả kinh doanh”:

-Shapiro-Wilk test: W = 0.6, p-value = 0.000004 (p-value rất nhỏ, phân phối không chuẩn).

Khoản mục “Tài sản”:

-Shapiro-Wilk test: W = 0.9, p-value = 0.2 (p-value lớn, phân phối gần chuẩn).

Nhận xét:

Các khoản mục “Dòng tiền” và “Tài sản” có p-value lớn, cho thấy dữ liệu gần như tuân theo phân phối chuẩn.

Khoản mục “Cơ cấu nguồn vốn” và “Hiệu quả kinh doanh” có p-value nhỏ, cho thấy dữ liệu không tuân theo phân phối chuẩn.

Phần lớn biến tài chính có sự phân phối không chuẩn, do đó cần thực hiện log-transformation (biến đổi log) trước khi phân tích định lượng để làm dữ liệu chuẩn hóa hơn.

3.7. Độ lệch (Skewness) và độ nhọn (Kurtosis)

library(e1071)
skew_kurt <- financial_long %>%
group_by(Khoan_muc) %>%
summarise(Skewness = skewness(Gia_tri_ty),
Kurtosis = kurtosis(Gia_tri_ty))
kable(skew_kurt)
Khoan_muc Skewness Kurtosis
Doanh thu thuần 1.000 -0.640
Lưu chuyển tiền thuần từ hoạt động kinh doanh -0.463 -1.445
Lưu chuyển tiền thuần từ hoạt động tài chính -0.021 -1.964
Lưu chuyển tiền thuần từ hoạt động đầu tư
Lợi nhuận gộp 0.345 -1.635
Lợi nhuận sau thuế 1.197 0.251
Nợ phải trả 0.290 -1.749
Tài sản ngắn hạn 0.363 -1.659
Tổng tài sản 0.355 -1.733
Vốn chủ sở hữu 0.339 -1.306

Giải thích kỹ thuật:

Skewness (Độ lệch): thước đo độ lệch của phân phối.

-Skewness > 0: phân phối lệch sang phải (dài ở bên phải).

-Skewness < 0: phân phối lệch sang trái (dài ở bên trái).

-Skewness = 0: phân phối đối xứng.

Kurtosis (Độ nhọn): đo độ nhọn của phân phối.

Kurtosis > 0: phân phối có đỉnh nhọn hơn phân phối chuẩn (Leptokurtic).

Kurtosis < 0: phân phối có đỉnh phẳng hơn phân phối chuẩn (Platykurtic).

Kurtosis = 0: phân phối chuẩn.

Nhận xét:

Khoản mục “Doanh thu thuần” có độ lệch dương (Skewness = 1.000), cho thấy phân phối lệch sang phải. Độ nhọn âm (-0.640) cho thấy phân phối có đỉnh phẳng hơn phân phối chuẩn.

Khoản mục “Lợi nhuận gộp” và “Lợi nhuận sau thuế” có Skewness > 1 (lệch phải nhiều) và Kurtosis gần 0, cho thấy phân phối của chúng có đỉnh tương đối sắc.

Các khoản mục như “Lưu chuyển tiền thuần từ hoạt động đầu tư” có Kurtosis âm, cho thấy chúng có độ phân tán thấp, tức là phân phối ít tập trung. Các chỉ tiêu tài chính như “Doanh thu thuần”, “Lợi nhuận gộp” có phân phối lệch phải rõ rệt, cho thấy sự thiên lệch trong dữ liệu. Các chỉ tiêu khác như “Lưu chuyển tiền thuần từ hoạt động tài chính” và “Lợi nhuận sau thuế” có độ nhọn thấp và độ lệch thấp, cho thấy chúng gần với phân phối chuẩn hơn.

3.8. Phân nhóm tần suất trạng thái tăng trưởng

freq_growth <- financial_long %>%
group_by(Trang_thai) %>%
summarise(Tan_suat = n(),
Ty_le = round(n()/nrow(financial_long)*100,2))
kable(freq_growth)
Trang_thai Tan_suat Ty_le
Suy giảm 27 34.2
Tăng mạnh 18 22.8
Tăng nhẹ 24 30.4
10 12.7

Giải thích kỹ thuật:

group_by(Trang_thai): nhóm dữ liệu theo cột “Trang_thai”, phân chia các nhóm theo các trạng thái tăng trưởng.

Lệnh summarise(Tan_suat = n(), Ty_le = round(n()/nrow(financial_long)*100, 2)):

-n() tính số lượng bản ghi trong mỗi nhóm (tần suất).

-n()/nrow(financial_long)*100: Tính tỷ lệ phần trăm của mỗi nhóm so với tổng số bản ghi trong dữ liệu.

Hàm round() làm tròn tỷ lệ phần trăm đến 2 chữ số.

Nhận xét:

Trạng thái “Suy giảm” chiếm 34.2% số lượng các chỉ tiêu tài chính, cho thấy một phần lớn dữ liệu bị suy giảm trong giai đoạn được khảo sát.

Trạng thái “Tăng mạnh” chiếm 22.8%, và “Tăng nhẹ” chiếm 30.4%, cho thấy sự phân bổ khá đều giữa các chỉ tiêu tài chính có sự tăng trưởng mạnh và nhẹ.

Không thay đổi: Có 10 trường hợp, chiếm 12.7% tổng số dữ liệu.

Các chỉ tiêu tài chính có xu hướng tăng trưởng ổn định, nhưng phần lớn dữ liệu thể hiện trạng thái suy giảm hoặc tăng nhẹ, cho thấy sự biến động trong các yếu tố tài chính qua các năm.

3.9. Tần suất theo nhóm khoản mục

freq_group <- financial_long %>%
count(Nhom) %>%
mutate(Ty_le = round(n/sum(n)*100,2))
kable(freq_group)
Nhom n Ty_le
Cơ cấu nguồn vốn 20 25.3
Dòng tiền 13 16.5
Hiệu quả kinh doanh 26 32.9
Tài sản 20 25.3

Giải thích kỹ thuật (code):

count(Nhom): đếm số lượng bản ghi cho mỗi nhóm trong cột Nhom.

Hàm mutate(): tính tỷ lệ phần trăm của mỗi nhóm bằng cách chia số lượng bản ghi của mỗi nhóm (n) cho tổng số bản ghi trong dữ liệu (sum(n)), sau đó nhân với 100 và làm tròn đến 2 chữ số.

Nhận xét:

Cơ cấu nguồn vốn: có 20 bản ghi, chiếm 25.3% tổng số dữ liệu.

Dòng tiền: có 13 bản ghi, chiếm 16.5% tổng số dữ liệu.

Hiệu quả kinh doanh: có 26 bản ghi, chiếm 32.9% tổng số dữ liệu.

Tài sản: có 20 bản ghi, chiếm 25.3% tổng số dữ liệu.

Các nhóm “Cơ cấu nguồn vốn” và “Tài sản” có tỷ lệ bằng nhau (25.3%), trong khi “Hiệu quả kinh doanh” chiếm tỷ lệ cao nhất (32.9%), phản ánh tầm quan trọng của nhóm này trong bộ dữ liệu.

3.10. Giá trị trung bình theo nhóm và trạng thái

group_mean <- financial_long %>%
group_by(Nhom, Trang_thai) %>%
summarise(Gia_tri_tb = mean(Gia_tri_ty))
kable(group_mean)
Nhom Trang_thai Gia_tri_tb
Cơ cấu nguồn vốn Suy giảm 3350
Cơ cấu nguồn vốn Tăng mạnh 3700
Cơ cấu nguồn vốn Tăng nhẹ 3823
Cơ cấu nguồn vốn 2809
Dòng tiền Suy giảm 360
Dòng tiền Tăng mạnh 564
Dòng tiền 321
Hiệu quả kinh doanh Suy giảm 1952
Hiệu quả kinh doanh Tăng mạnh 3163
Hiệu quả kinh doanh Tăng nhẹ 4053
Hiệu quả kinh doanh 3894
Tài sản Suy giảm 4915
Tài sản Tăng mạnh 6765
Tài sản Tăng nhẹ 8092
Tài sản 5523

Giải thích kỹ thuật:

group_by(Nhom, Trang_thai): nhóm dữ liệu theo các cột Nhom và Trang_thai (nhóm theo loại và trạng thái).

Hàm summarise(): tính giá trị trung bình (mean) của cột “Gia_tri_ty” trong mỗi nhóm và lưu kết quả vào cột “Gia_tri_tb”.

Nhận xét:

Cơ cấu nguồn vốn: các giá trị trung bình cho “Cơ cấu nguồn vốn” trong các trạng thái có sự thay đổi nhẹ, từ 3350 trong trạng thái suy giảm lên 3823 trong trạng thái tăng nhẹ, với giá trị trung bình cao nhất khi tăng mạnh (3700).

Dòng tiền: giá trị trung bình của dòng tiền cho thấy sự thay đổi từ 360 trong trạng thái suy giảm lên 564 trong trạng thái tăng mạnh, cho thấy dòng tiền tăng mạnh trong các năm.

Hiệu quả kinh doanh: các giá trị trung bình cho hiệu quả kinh doanh tăng từ 1952 (suy giảm) lên 4053 (tăng nhẹ), phản ánh sự cải thiện trong hiệu quả kinh doanh khi tình hình tài chính tốt lên.

Tài sản: tài sản có sự thay đổi mạnh, từ 4915 trong trạng thái suy giảm lên 8092 trong trạng thái tăng mạnh, cho thấy tài sản công ty tăng đáng kể trong các năm gần đây.

3.11. Tốc độ tăng trưởng trung bình của từng khoản mục

growth_mean <- financial_long %>%
group_by(Khoan_muc) %>%
summarise(Tang_truong_tb = mean(Tang_truong, na.rm = TRUE))
kable(growth_mean)
Khoan_muc Tang_truong_tb
Doanh thu thuần 5.87
Lưu chuyển tiền thuần từ hoạt động kinh doanh 925.66
Lưu chuyển tiền thuần từ hoạt động tài chính 38.44
Lưu chuyển tiền thuần từ hoạt động đầu tư
Lợi nhuận gộp 2.84
Lợi nhuận sau thuế 8.95
Nợ phải trả 8.52
Tài sản ngắn hạn 7.63
Tổng tài sản 7.41
Vốn chủ sở hữu 6.53

Giải thích kỹ thuật:

group_by(Khoan_muc): nhóm dữ liệu theo cột “Khoan_muc”, phân chia dữ liệu theo từng khoản mục.

Hàm summarise(): tính giá trị trung bình của cột “Tang_truong” cho mỗi nhóm (khoản mục), bỏ qua giá trị thiếu (NA) với na.rm = TRUE.

Nhận xét:

Doanh thu thuần: Tỷ lệ tăng trưởng trung bình là 5.87%, cho thấy doanh thu thuần có mức tăng trưởng ổn định trong suốt các năm.

Lưu chuyển tiền thuần từ hoạt động kinh doanh: Tỷ lệ tăng trưởng trung bình cao 925.66%, cho thấy sự gia tăng mạnh mẽ trong dòng tiền thuần từ hoạt động kinh doanh qua các năm.

Lưu chuyển tiền thuần từ hoạt động tài chính: Tỷ lệ tăng trưởng trung bình là 38.44%, cho thấy sự tăng trưởng đáng kể trong dòng tiền từ hoạt động tài chính.

Lưu chuyển tiền thuần từ hoạt động đầu tư: Tỷ lệ tăng trưởng trung bình là 2.84%, phản ánh sự thay đổi nhẹ trong dòng tiền từ hoạt động đầu tư.

Lợi nhuận gộp: Tỷ lệ tăng trưởng trung bình là 8.95%, cho thấy lợi nhuận gộp tăng trưởng ổn định.

Lợi nhuận sau thuế: Tỷ lệ tăng trưởng trung bình là 8.52%, phản ánh sự ổn định trong lợi nhuận sau thuế.

Các khoản mục khác: Tỷ lệ tăng trưởng của các chỉ tiêu như nợ phải trả, tài sản ngắn hạn, tổng tài sản và vốn chủ sở hữu đều có sự thay đổi trong khoảng từ 6.53% đến 7.63%, cho thấy sự ổn định trong các khoản mục tài chính này.

3.12. CAGR (tăng trưởng kép hàng năm) Doanh thu 2015–2024

rev <- financial_long %>% filter(Khoan_muc == "Doanh thu thuần")
CAGR <- ((last(rev$Gia_tri_ty)/first(rev$Gia_tri_ty))^(1/(nrow(rev)-1))-1)*100
round(CAGR,2)
## [1] 4.78

Giải thích kỹ thuật:

last(rev$Gia_tri_ty): lấy giá trị doanh thu cuối cùng.

first(rev$Gia_tri_ty): lấy giá trị doanh thu đầu kỳ.

nrow(rev): lấy số lượng dòng trong dữ liệu (tổng số năm).

Công thức tính CAGR (Tốc độ tăng trưởng kép hàng năm): \[ CAGR = \left( \frac{\text{Doanh thu cuối kỳ}}{\text{Doanh thu đầu kỳ}} \right)^{\frac{1}{n-1}} - 1 \] Sau đó nhân với 100 để chuyển đổi sang tỷ lệ phần trăm.

round(CAGR, 2): làm tròn kết quả CAGR đến 2 chữ số thập phân.

Nhận xét:

CAGR (Tốc độ tăng trưởng kép hàng năm) của doanh thu thuần từ năm 2015 đến 2024 là 4.78%. Doanh thu thuần của công ty đã tăng trưởng ổn định với tỷ lệ tăng trưởng kép hàng năm đạt 4.78%, cho thấy mức tăng trưởng vững chắc qua các năm.

3.13. ROA và ROE trung bình 10 năm

# Tính giá trị trung bình của từng khoản mục cần thiết
ta <- financial_long %>% 
  filter(Khoan_muc == "Tổng tài sản") %>% 
  summarise(Tong_tai_san_tb = mean(Gia_tri_ty, na.rm = TRUE)) %>% 
  pull(Tong_tai_san_tb)

vcsh <- financial_long %>% 
  filter(Khoan_muc == "Vốn chủ sở hữu") %>% 
  summarise(Von_chu_so_huu_tb = mean(Gia_tri_ty, na.rm = TRUE)) %>% 
  pull(Von_chu_so_huu_tb)

lnst <- financial_long %>% 
  filter(Khoan_muc == "Lợi nhuận sau thuế") %>% 
  summarise(Loi_nhuan_sau_thue_tb = mean(Gia_tri_ty, na.rm = TRUE)) %>% 
  pull(Loi_nhuan_sau_thue_tb)

# Tính ROA và ROE trung bình (%)
ROA <- round((lnst / ta) * 100, 2)
ROE <- round((lnst / vcsh) * 100, 2)

cat("Tỷ suất sinh lời trên tài sản (ROA):", ROA, "%\n")
## Tỷ suất sinh lời trên tài sản (ROA): 2.44 %
cat("Tỷ suất sinh lời trên vốn chủ (ROE):", ROE, "%\n")
## Tỷ suất sinh lời trên vốn chủ (ROE): 9.71 %

Giải thích kỹ thuật:

Tính giá trị trung bình của các khoản mục:

-Tổng tài sản: dữ liệu được lọc theo chỉ tiêu “Tổng tài sản”, sau đó tính giá trị trung bình của cột Gia_tri_ty bằng hàm summarise().

-Vốn chủ sở hữu: dữ liệu được lọc theo chỉ tiêu “Vốn chủ sở hữu”, và tính giá trị trung bình của cột Gia_tri_ty.

-Lợi nhuận sau thuế: dữ liệu được lọc theo chỉ tiêu “Lợi nhuận sau thuế”, và tính giá trị trung bình của cột Gia_tri_ty.

Tính ROA và ROE:

-ROA (Tỷ suất sinh lời trên tài sản) được tính bằng công thức: \[ ROA = \frac{\text{Lợi nhuận sau thuế}}{\text{Tổng tài sản}} \times 100 \] -ROE (Tỷ suất sinh lời trên vốn chủ sở hữu) được tính bằng công thức: \[ ROE = \frac{\text{Lợi nhuận sau thuế}}{\text{Vốn chủ sở hữu}} \times 100 \] Nhận xét:

ROA (Tỷ suất sinh lời trên tài sản): 2.44%, nghĩa là mỗi 100 đồng tài sản sinh ra 2.44 đồng lợi nhuận sau thuế.

ROE (Tỷ suất sinh lời trên vốn chủ sở hữu): 9.71%, có nghĩa là mỗi 100 đồng vốn chủ sở hữu sinh ra 9.71 đồng lợi nhuận sau thuế.

ROE cao hơn ROA phản ánh công ty sử dụng vốn chủ sở hữu hiệu quả hơn trong việc tạo ra lợi nhuận so với tổng tài sản.

3.14. Biên lợi nhuận ròng trung bình

# Lấy dữ liệu doanh thu và lợi nhuận
rev_profit <- financial_long %>%
  filter(Khoan_muc %in% c("Doanh thu thuần", "Lợi nhuận sau thuế")) %>%
  select(Nam, Khoan_muc, Gia_tri_ty) %>%
  pivot_wider(names_from = Khoan_muc, values_from = Gia_tri_ty)
# Tạo biến biên lợi nhuận ròng (%)
rev_profit <- rev_profit %>%
  mutate(Bien_loi_nhuan = round((`Lợi nhuận sau thuế` / `Doanh thu thuần`) * 100, 2))
# Tính trung bình toàn giai đoạn
mean_bien_loi_nhuan <- mean(rev_profit$Bien_loi_nhuan, na.rm = TRUE)

cat("Biên lợi nhuận ròng trung bình giai đoạn 2015–2024 là:", mean_bien_loi_nhuan, "%\n")
## Biên lợi nhuận ròng trung bình giai đoạn 2015–2024 là: 1.42 %

Giải thích kỹ thuật:

Lọc dữ liệu từ bảng financial_long để chỉ lấy các dòng có chỉ tiêu “Doanh thu thuần” và “Lợi nhuận sau thuế”. Sau đó, sử dụng hàm pivot_wider() để chuyển đổi dữ liệu từ dạng dài sang rộng, với các giá trị của “Doanh thu thuần” và “Lợi nhuận sau thuế” được tách ra thành các cột riêng biệt.

Tạo biến Lợi nhuận ròng (%): dùng hàm mutate() để tính toán tỷ lệ lợi nhuận ròng bằng công thức: \[ \text{Lợi nhuận ròng} = \frac{\text{Lợi nhuận sau thuế}}{\text{Doanh thu thuần}} \times 100 \] Sau đó làm tròn kết quả đến 2 chữ số thập phân bằnghàm round().

Tính giá trị trung bình lợi nhuận ròng: dùng hàm mean() để tính giá trị trung bình của Bien_loi_nhuan (Lợi nhuận ròng) trong toàn bộ dữ liệu, loại bỏ các giá trị thiếu (NA) với na.rm = TRUE.

Nhận xét:

Tỷ lệ lợi nhuận ròng trung bình trong giai đoạn 2015–2024 là 1.42%.

Lợi nhuận ròng của công ty trong giai đoạn này khá khiêm tốn, với tỷ lệ lợi nhuận ròng trung bình chỉ đạt 1.42%, cho thấy công ty chưa tối ưu được khả năng sinh lời từ doanh thu thuần.

3.15. Tỷ lệ Nợ/Vốn chủ sở hữu

# Lấy dữ liệu 2 khoản mục: Nợ phải trả và Vốn chủ sở hữu
no_von <- financial_long %>%
  filter(Khoan_muc %in% c("Nợ phải trả", "Vốn chủ sở hữu")) %>%
  select(Nam, Khoan_muc, Gia_tri_ty) %>%
  pivot_wider(names_from = Khoan_muc, values_from = Gia_tri_ty)
# Tạo biến tỷ lệ Nợ/Vốn chủ sở hữu (%)
no_von <- no_von %>%
  mutate(Ty_le_No_Von = (`Nợ phải trả` / `Vốn chủ sở hữu`) * 100)
#Tính trung bình toàn giai đoạn
ty_le_tb <- mean(no_von$Ty_le_No_Von, na.rm = TRUE)
# Xuất kết quả
cat("Tỷ lệ Nợ/Vốn chủ sở hữu trung bình giai đoạn 2015–2024 là:",
    round(ty_le_tb, 2), "%\n")
## Tỷ lệ Nợ/Vốn chủ sở hữu trung bình giai đoạn 2015–2024 là: 294 %

Giải thích kỹ thuật:

Dữ liệu được lọc từ bảng financial_long, chỉ lấy các chỉ tiêu “Nợ phải trả” và “Vốn chủ sở hữu”. Sau đó, sử dụng pivot_wider() để chuyển dữ liệu từ dạng dài sang dạng rộng, tách riêng hai cột “Nợ phải trả” và “Vốn chủ sở hữu”.

Tính tỷ lệ Nợ/Vốn chủ sở hữu (%):

-Sử dụng hàm mutate() để tạo cột mới tính tỷ lệ Nợ/Vốn chủ sở hữu theo công thức: \[ \frac{\text{Nợ phải trả}}{\text{Vốn chủ sở hữu}} \times 100 \] Tính trung bình tỷ lệ Nợ/Vốn chủ sở hữu: sử dụn hàmg mean() để tính giá trị trung bình của tỷ lệ Nợ/Vốn cho toàn bộ dữ liệu, bỏ qua các giá trị thiếu (NA).

Nhận xét:

Tỷ lệ Nợ/Vốn chủ sở hữu trung bình giai đoạn 2015-2024 là 294%.

Tỷ lệ Nợ/Vốn chủ sở hữu cao cho thấy công ty có mức độ vay mượn khá lớn so với vốn chủ sở hữu, điều này có thể ảnh hưởng đến khả năng thanh khoản và rủi ro tài chính.

3.16. Ma trận tương quan giữa các chỉ tiêu tài chính

cor_matrix <- financial_long %>%
select(Khoan_muc, Nam, Gia_tri_ty) %>%
pivot_wider(names_from = Khoan_muc, values_from = Gia_tri_ty) %>%
select(-Nam) %>%
cor(use = "pairwise.complete.obs")
round(cor_matrix, 2)
##                                               Tài sản ngắn hạn Tổng tài sản
## Tài sản ngắn hạn                                          1.00         0.99
## Tổng tài sản                                              0.99         1.00
## Nợ phải trả                                               0.99         1.00
## Vốn chủ sở hữu                                            0.90         0.94
## Lưu chuyển tiền thuần từ hoạt động kinh doanh             0.47         0.36
## Doanh thu thuần                                           0.44         0.45
## Lợi nhuận gộp                                             0.73         0.70
## Lợi nhuận sau thuế                                        0.45         0.42
## Lưu chuyển tiền thuần từ hoạt động tài chính             -0.38        -0.40
## Lưu chuyển tiền thuần từ hoạt động đầu tư                   NA           NA
##                                               Nợ phải trả Vốn chủ sở hữu
## Tài sản ngắn hạn                                     0.99           0.90
## Tổng tài sản                                         1.00           0.94
## Nợ phải trả                                          1.00           0.92
## Vốn chủ sở hữu                                       0.92           1.00
## Lưu chuyển tiền thuần từ hoạt động kinh doanh        0.35           0.25
## Doanh thu thuần                                      0.43           0.17
## Lợi nhuận gộp                                        0.72           0.51
## Lợi nhuận sau thuế                                   0.44           0.23
## Lưu chuyển tiền thuần từ hoạt động tài chính        -0.39          -0.41
## Lưu chuyển tiền thuần từ hoạt động đầu tư              NA             NA
##                                               Lưu chuyển tiền thuần từ hoạt động kinh doanh
## Tài sản ngắn hạn                                                                       0.47
## Tổng tài sản                                                                           0.36
## Nợ phải trả                                                                            0.35
## Vốn chủ sở hữu                                                                         0.25
## Lưu chuyển tiền thuần từ hoạt động kinh doanh                                          1.00
## Doanh thu thuần                                                                       -0.23
## Lợi nhuận gộp                                                                          0.39
## Lợi nhuận sau thuế                                                                     0.64
## Lưu chuyển tiền thuần từ hoạt động tài chính                                          -1.00
## Lưu chuyển tiền thuần từ hoạt động đầu tư                                                NA
##                                               Doanh thu thuần Lợi nhuận gộp
## Tài sản ngắn hạn                                         0.44          0.73
## Tổng tài sản                                             0.45          0.70
## Nợ phải trả                                              0.43          0.72
## Vốn chủ sở hữu                                           0.17          0.51
## Lưu chuyển tiền thuần từ hoạt động kinh doanh           -0.23          0.39
## Doanh thu thuần                                          1.00          0.00
## Lợi nhuận gộp                                            0.00          1.00
## Lợi nhuận sau thuế                                      -0.20          0.73
## Lưu chuyển tiền thuần từ hoạt động tài chính             1.00         -0.45
## Lưu chuyển tiền thuần từ hoạt động đầu tư                  NA            NA
##                                               Lợi nhuận sau thuế
## Tài sản ngắn hạn                                            0.45
## Tổng tài sản                                                0.42
## Nợ phải trả                                                 0.44
## Vốn chủ sở hữu                                              0.23
## Lưu chuyển tiền thuần từ hoạt động kinh doanh               0.64
## Doanh thu thuần                                            -0.20
## Lợi nhuận gộp                                               0.73
## Lợi nhuận sau thuế                                          1.00
## Lưu chuyển tiền thuần từ hoạt động tài chính                0.07
## Lưu chuyển tiền thuần từ hoạt động đầu tư                     NA
##                                               Lưu chuyển tiền thuần từ hoạt động tài chính
## Tài sản ngắn hạn                                                                     -0.38
## Tổng tài sản                                                                         -0.40
## Nợ phải trả                                                                          -0.39
## Vốn chủ sở hữu                                                                       -0.41
## Lưu chuyển tiền thuần từ hoạt động kinh doanh                                        -1.00
## Doanh thu thuần                                                                       1.00
## Lợi nhuận gộp                                                                        -0.45
## Lợi nhuận sau thuế                                                                    0.07
## Lưu chuyển tiền thuần từ hoạt động tài chính                                          1.00
## Lưu chuyển tiền thuần từ hoạt động đầu tư                                               NA
##                                               Lưu chuyển tiền thuần từ hoạt động đầu tư
## Tài sản ngắn hạn                                                                     NA
## Tổng tài sản                                                                         NA
## Nợ phải trả                                                                          NA
## Vốn chủ sở hữu                                                                       NA
## Lưu chuyển tiền thuần từ hoạt động kinh doanh                                        NA
## Doanh thu thuần                                                                      NA
## Lợi nhuận gộp                                                                        NA
## Lợi nhuận sau thuế                                                                   NA
## Lưu chuyển tiền thuần từ hoạt động tài chính                                         NA
## Lưu chuyển tiền thuần từ hoạt động đầu tư                                            NA

Giải thích kỹ thuật:

Dữ liệu được lọc từ bảng financial_long, chỉ lấy các cột “Khoan_muc”, “Nam”, và “Gia_tri_ty”.

Hàm pivot_wider() để chuyển đổi dữ liệu từ dạng dài sang dạng rộng, với mỗi “Khoan_muc” trở thành một cột và các giá trị “Gia_tri_ty” là giá trị của các cột này.

-select(-Nam): Loại bỏ cột Nam vì không cần thiết cho ma trận tương quan.

-cor(use = “pairwise.complete.obs”): Tính ma trận tương quan giữa các chỉ tiêu tài chính, sử dụng phương pháp “pairwise complete” để xử lý các giá trị thiếu.

-round(cor_matrix, 2): Làm tròn ma trận tương quan đến 2 chữ số thập phân.

Nhận xét:

Ma trận tương quan giữa các chỉ tiêu tài chính cho thấy:

-Các chỉ tiêu như “Tài sản ngắn hạn”, “Tổng tài sản”, “Nợ phải trả”, “Vốn chủ sở hữu” có mối tương quan cao (trên 0.9), cho thấy mối quan hệ chặt chẽ giữa chúng.

-Các chỉ tiêu như “Lợi nhuận gộp” và “Doanh thu thuần” có tương quan vừa phải (0.44-0.45), phản ánh sự liên kết giữa các chỉ tiêu doanh thu và lợi nhuận.

-Một số mối quan hệ có tương quan tiêu cực, ví dụ như “Lưu chuyển tiền thuần từ hoạt động tài chính” với một số chỉ tiêu khác, cho thấy sự giảm sút dòng tiền có thể ảnh hưởng đến các yếu tố tài chính khác.

3.17. Hệ số tương quan Doanh thu – Lợi nhuận

# Đảm bảo dữ liệu rev_profit đã được tạo ở bước 3.14
rev_profit_corr <- cor(rev_profit$`Doanh thu thuần`,
                       rev_profit$`Lợi nhuận sau thuế`,
                       use = "pairwise.complete.obs")

cat("Hệ số tương quan giữa Doanh thu thuần và Lợi nhuận sau thuế là:",
    round(rev_profit_corr, 3), "\n")
## Hệ số tương quan giữa Doanh thu thuần và Lợi nhuận sau thuế là: -0.199

Giải thích kỹ thuật:

Tính hệ số tương quan giữa Doanh thu và Lợi nhuận: rev_profit_corr <- cor(rev_profit\(Doanh thu thuần, rev_profit\)Lợi nhuận sau thuế, use = “pairwise.complete.obs”):

-Tính hệ số tương quan giữa hai cột: “Doanh thu thuần” và “Lợi nhuận sau thuế”.

-Sử dụng phương pháp “pairwise complete” để tính toán tương quan, bỏ qua các giá trị thiếu (NA).

Hiển thị kết quả hệ số tương quan:

-cat(“Hệ số tương quan giữa Doanh thu thuần và Lợi nhuận sau thuế là:”, round(rev_profit_corr, 3), “”): In kết quả hệ số tương quan, làm tròn đến 3 chữ số thập phân.

Nhận xét:

Hệ số tương quan giữa Doanh thu thuần và Lợi nhuận sau thuế là -0.199.

Mối quan hệ giữa Doanh thu thuần và Lợi nhuận sau thuế là yếu và có tương quan âm, cho thấy khi Doanh thu thuần tăng, Lợi nhuận sau thuế có thể giảm, điều này phản ánh sự không hoàn toàn khớp giữa doanh thu và lợi nhuận trong kỳ.

3.18. Phân tích rủi ro (hệ số biến động CV%)

risk_table <- financial_long %>%
group_by(Khoan_muc) %>%
summarise(CV = round(sd(Gia_tri_ty)/mean(Gia_tri_ty)*100,2),
RiskLevel = case_when(
CV < 10 ~ "Thấp",
CV >=10 & CV < 20 ~ "Trung bình",
TRUE ~ "Cao"
))
kable(risk_table)
Khoan_muc CV RiskLevel
Doanh thu thuần 11.9 Trung bình
Lưu chuyển tiền thuần từ hoạt động kinh doanh 59.3 Cao
Lưu chuyển tiền thuần từ hoạt động tài chính 48.1 Cao
Lưu chuyển tiền thuần từ hoạt động đầu tư Cao
Lợi nhuận gộp 16.6 Trung bình
Lợi nhuận sau thuế 32.8 Cao
Nợ phải trả 29.6 Cao
Tài sản ngắn hạn 30.9 Cao
Tổng tài sản 25.9 Cao
Vốn chủ sở hữu 16.9 Trung bình

Giải thích kỹ thuật:

Tính hệ số biến động (CV%):

-group_by(Khoan_muc): mhóm dữ liệu theo cột Khoan_muc (các khoản mục tài chính).

-Lệnh summarise(CV = round(sd(Gia_tri_ty)/mean(Gia_tri_ty)*100, 2)): tính hệ số biến động (CV%) bằng cách chia độ lệch chuẩn (sd) của Gia_tri_ty cho giá trị trung bình của Gia_tri_ty, sau đó nhân với 100 và làm tròn đến 2 chữ số thập phân.

Phân loại mức độ rủi ro (RiskLevel): sử dụng hàm case_when() để phân loại mức độ rủi ro dựa trên giá trị CV%:

-CV < 10: “Thấp”

-10 ≤ CV < 20: “Trung bình”

-CV ≥ 20: “Cao”

Nhận xét:

Doanh thu thuần: CV = 11.9 (Trung bình).

Lưu chuyển tiền thuần từ hoạt động kinh doanh: CV = 59.3 (Cao).

Lưu chuyển tiền thuần từ hoạt động tài chính: CV = 48.1, (Cao).

Lợi nhuận gộp: CV = 16.6, (Trung bình).

Lợi nhuận sau thuế: CV = 32.8 (Cao).

Nợ phải trả: CV = 29.6, (Cao).

Tài sản ngắn hạn: CV = 30.9 (Cao).

Tổng tài sản: CV = 25.9 (Cao).

Vốn chủ sở hữu: CV = 16.9 (Trung bình).

Các khoản mục như “Lưu chuyển tiền thuần từ hoạt động kinh doanh”, “Lưu chuyển tiền thuần từ hoạt động tài chính”, “Lợi nhuận sau thuế” có mức độ rủi ro cao với hệ số biến động CV% lớn. Các khoản mục như “Doanh thu thuần” và “Lợi nhuận gộp” có mức độ rủi ro trung bình.

3.19. Ma trận tương quan log-transform

cor_log <- financial_long %>%
select(Khoan_muc, Nam, Log_value) %>%
pivot_wider(names_from = Khoan_muc, values_from = Log_value) %>%
select(-Nam) %>%
cor(use = "pairwise.complete.obs")
round(cor_log, 2)
##                                               Tài sản ngắn hạn Tổng tài sản
## Tài sản ngắn hạn                                          1.00         0.98
## Tổng tài sản                                              0.98         1.00
## Nợ phải trả                                               0.99         1.00
## Vốn chủ sở hữu                                            0.83         0.90
## Lưu chuyển tiền thuần từ hoạt động kinh doanh             0.20         0.12
## Doanh thu thuần                                           0.44         0.44
## Lợi nhuận gộp                                             0.77         0.72
## Lợi nhuận sau thuế                                        0.52         0.47
## Lưu chuyển tiền thuần từ hoạt động tài chính             -0.35        -0.38
## Lưu chuyển tiền thuần từ hoạt động đầu tư                   NA           NA
##                                               Nợ phải trả Vốn chủ sở hữu
## Tài sản ngắn hạn                                     0.99           0.83
## Tổng tài sản                                         1.00           0.90
## Nợ phải trả                                          1.00           0.87
## Vốn chủ sở hữu                                       0.87           1.00
## Lưu chuyển tiền thuần từ hoạt động kinh doanh        0.10           0.08
## Doanh thu thuần                                      0.43           0.15
## Lợi nhuận gộp                                        0.74           0.48
## Lợi nhuận sau thuế                                   0.50           0.22
## Lưu chuyển tiền thuần từ hoạt động tài chính        -0.37          -0.39
## Lưu chuyển tiền thuần từ hoạt động đầu tư              NA             NA
##                                               Lưu chuyển tiền thuần từ hoạt động kinh doanh
## Tài sản ngắn hạn                                                                       0.20
## Tổng tài sản                                                                           0.12
## Nợ phải trả                                                                            0.10
## Vốn chủ sở hữu                                                                         0.08
## Lưu chuyển tiền thuần từ hoạt động kinh doanh                                          1.00
## Doanh thu thuần                                                                       -0.27
## Lợi nhuận gộp                                                                          0.03
## Lợi nhuận sau thuế                                                                     0.36
## Lưu chuyển tiền thuần từ hoạt động tài chính                                          -1.00
## Lưu chuyển tiền thuần từ hoạt động đầu tư                                                NA
##                                               Doanh thu thuần Lợi nhuận gộp
## Tài sản ngắn hạn                                         0.44          0.77
## Tổng tài sản                                             0.44          0.72
## Nợ phải trả                                              0.43          0.74
## Vốn chủ sở hữu                                           0.15          0.48
## Lưu chuyển tiền thuần từ hoạt động kinh doanh           -0.27          0.03
## Doanh thu thuần                                          1.00          0.04
## Lợi nhuận gộp                                            0.04          1.00
## Lợi nhuận sau thuế                                      -0.19          0.76
## Lưu chuyển tiền thuần từ hoạt động tài chính             1.00         -0.50
## Lưu chuyển tiền thuần từ hoạt động đầu tư                  NA            NA
##                                               Lợi nhuận sau thuế
## Tài sản ngắn hạn                                            0.52
## Tổng tài sản                                                0.47
## Nợ phải trả                                                 0.50
## Vốn chủ sở hữu                                              0.22
## Lưu chuyển tiền thuần từ hoạt động kinh doanh               0.36
## Doanh thu thuần                                            -0.19
## Lợi nhuận gộp                                               0.76
## Lợi nhuận sau thuế                                          1.00
## Lưu chuyển tiền thuần từ hoạt động tài chính                0.00
## Lưu chuyển tiền thuần từ hoạt động đầu tư                     NA
##                                               Lưu chuyển tiền thuần từ hoạt động tài chính
## Tài sản ngắn hạn                                                                     -0.35
## Tổng tài sản                                                                         -0.38
## Nợ phải trả                                                                          -0.37
## Vốn chủ sở hữu                                                                       -0.39
## Lưu chuyển tiền thuần từ hoạt động kinh doanh                                        -1.00
## Doanh thu thuần                                                                       1.00
## Lợi nhuận gộp                                                                        -0.50
## Lợi nhuận sau thuế                                                                    0.00
## Lưu chuyển tiền thuần từ hoạt động tài chính                                          1.00
## Lưu chuyển tiền thuần từ hoạt động đầu tư                                               NA
##                                               Lưu chuyển tiền thuần từ hoạt động đầu tư
## Tài sản ngắn hạn                                                                     NA
## Tổng tài sản                                                                         NA
## Nợ phải trả                                                                          NA
## Vốn chủ sở hữu                                                                       NA
## Lưu chuyển tiền thuần từ hoạt động kinh doanh                                        NA
## Doanh thu thuần                                                                      NA
## Lợi nhuận gộp                                                                        NA
## Lợi nhuận sau thuế                                                                   NA
## Lưu chuyển tiền thuần từ hoạt động tài chính                                         NA
## Lưu chuyển tiền thuần từ hoạt động đầu tư                                            NA

Giải thích kỹ thuật:

Tạo ma trận tương quan với log-transformed dữ liệu:cor_log <- financial_long %>% select(Khoan_muc, Nam, Log_value) %>% pivot_wider(names_from = Khoan_muc, values_from = Log_value) %>% select(-Nam) %>% cor(use = “pairwise.complete.obs”):

-select(Khoan_muc, Nam, Log_value): Chọn các cột Khoan_muc, Nam, và Log_value (dữ liệu đã được chuyển đổi log).

-pivot_wider(names_from = Khoan_muc, values_from = Log_value): Chuyển dữ liệu từ dạng dài sang dạng rộng, mỗi Khoan_muc trở thành một cột và các giá trị log được phân bổ vào các cột này.

-select(-Nam): Loại bỏ cột Nam vì không cần thiết cho ma trận tương quan.

-cor(use = “pairwise.complete.obs”): Tính ma trận tương quan giữa các chỉ tiêu tài chính, xử lý các giá trị thiếu (NA) bằng phương pháp “pairwise complete”.

Hiển thị kết quả ma trận tương quan:

-round(cor_log, 2): Làm tròn kết quả ma trận tương quan đến 2 chữ số thập phân.

Nhận xét:

Ma trận tương quan giữa các chỉ tiêu tài chính sau khi chuyển đổi log cho thấy các chỉ tiêu như “Tài sản ngắn hạn”, “Tổng tài sản”, “Vốn chủ sở hữu”, “Lưu chuyển tiền thuần từ hoạt động kinh doanh” có mối tương quan mạnh với nhau, với hệ số tương quan trên 0.9.

Các chỉ tiêu “Lợi nhuận sau thuế” và “Lưu chuyển tiền thuần từ hoạt động tài chính” có mối tương quan tiêu cực nhỏ, cho thấy sự thay đổi trong một chỉ tiêu có thể ảnh hưởng đến chỉ tiêu kia.

Ma trận tương quan này giúp hiểu rõ hơn về mối quan hệ giữa các chỉ tiêu tài chính trong bộ dữ liệu đã được chuyển đổi bằng log.

3.20. Hồi quy tuyến tính: Lợi nhuận phụ thuộc Doanh thu

# Kiểm tra dữ liệu rev_profit đã tồn tại
head(rev_profit)
## # A tibble: 6 × 4
##     Nam `Doanh thu thuần` `Lợi nhuận sau thuế` Bien_loi_nhuan
##   <dbl>             <dbl>                <dbl>          <dbl>
## 1  2015            10652.                 212.           1.99
## 2  2016             9882.                 167.           1.69
## 3  2017            10703.                 144.           1.34
## 4  2018            11092.                 130.           1.17
## 5  2019            10008.                 127.           1.27
## 6  2020            13453.                 140.           1.04
# Mô hình hồi quy tuyến tính
model <- lm(`Lợi nhuận sau thuế` ~ `Doanh thu thuần`, data = rev_profit)
# Kết quả tóm tắt mô hình
summary(model)
## 
## Call:
## lm(formula = `Lợi nhuận sau thuế` ~ `Doanh thu thuần`, 
##     data = rev_profit)
## 
## Residuals:
##      1      2      3      4      5      6 
##  57.00   8.32 -10.82 -22.79 -30.72  -1.00 
## 
## Coefficients:
##                   Estimate Std. Error t value Pr(>|t|)
## (Intercept)       207.0465   132.7076    1.56     0.19
## `Doanh thu thuần`  -0.0049     0.0120   -0.41     0.70
## 
## Residual standard error: 35 on 4 degrees of freedom
##   (4 observations deleted due to missingness)
## Multiple R-squared:  0.0398, Adjusted R-squared:  -0.2 
## F-statistic: 0.166 on 1 and 4 DF,  p-value: 0.705

Giải thích kỹ thuật:

Kiểm tra dữ liệu:

-head(rev_profit): Hiển thị 6 dòng đầu tiên của bảng rev_profit để kiểm tra cấu trúc dữ liệu.

-Dữ liệu bao gồm các cột Nam (năm), Doanh thu thuần, Lợi nhuận sau thuế, và Biến lợi nhuận.

Mô hình hồi quy tuyến tính:

-model <- lm(“Lợi nhuận sau thuế ~ Doanh thu thuần”, data = rev_profit): Sử dụng hàm lm() để xây dựng mô hình hồi quy tuyến tính, trong đó Lợi nhuận sau thuế là biến phụ thuộc và Doanh thu thuần là biến độc lập.

Kết quả tóm tắt mô hình:

-summary(model): Hiển thị kết quả của mô hình hồi quy tuyến tính, bao gồm các hệ số, độ sai lệch chuẩn, giá trị t-test, R-squared và các chỉ số thống kê khác.

Nhận xét:

Hệ số của Doanh thu thuần là -0.004, với p-value = 0.70. Điều này cho thấy mối quan hệ giữa Doanh thu thuần và Lợi nhuận sau thuế không có ý nghĩa thống kê (p-value lớn hơn 0.05), tức là không thể khẳng định được mối quan hệ tuyến tính giữa hai yếu tố này.

Kết luận: Doanh thu không có ảnh hưởng rõ rệt tới lợi nhuận sau thuế trong mô hình hồi quy tuyến tính này.

Nội Dung 4: Trực quan hóa dữ liệu

library(ggplot2)
library(dplyr)
library(scales)
library(ggrepel)
library(ggtext)
library(patchwork)
theme_fin <- theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold", hjust = 0.5),
        plot.subtitle = element_text(size = 10, face = "italic"),
        axis.title = element_text(face = "bold"))

4.1. Xu hướng Doanh thu thuần (2015–2024)

dt_rev <- financial_long %>% filter(Khoan_muc == "Doanh thu thuần")
ggplot(dt_rev, aes(x = Nam, y = Gia_tri_ty)) +
  geom_line(color = "steelblue", linewidth = 1.2) +
  geom_point(size = 3, color = "darkblue") +
  geom_text(aes(label = round(Gia_tri_ty,0)), vjust = -1, size = 3) +
  geom_smooth(method = "lm", se = FALSE, color = "red", linetype = "dashed") +
  labs(title = "Xu hướng Doanh thu thuần PET (2015–2024)",
       x = "Năm", y = "Giá trị (tỷ đồng)") +
  theme_fin

Giải thích kỹ thuật:

Dữ liệu được lọc từ bảng financial_long để lấy các dòng có Khoan_muc là “Doanh thu thuần” tạo thành tập dữ liệu dt_rev.

Sử dụng ggplot() với trục hoành là năm (Nam) và trục tung là giá trị doanh thu thuần (Gia_tri_ty).

geom_line() vẽ đường xu hướng doanh thu qua các năm, màu xanh da trời, độ dày 1.2.

geom_point() hiển thị các điểm dữ liệu trên biểu đồ, màu xanh đậm, kích thước 3.

geom_text() hiển thị nhãn giá trị cho từng điểm dữ liệu, được làm tròn và đặt ngay phía trên điểm.

geom_smooth(method = “lm”) thêm đường xu hướng tuyến tính (hồi quy) màu đỏ kiểu đường gạch chấm.

labs() thêm tiêu đề và nhãn trục cho biểu đồ.

Nhận xét:

Biểu đồ Xu hướng Doanh thu thuần PET (2015-2024) cho thấy:

-Doanh thu thuần tăng trưởng mạnh trong giai đoạn từ 2015 đến 2020 với sự bứt phá lớn trong năm 2020 (14,453 tỷ đồng).

-Đường xu hướng tuyến tính (hồi quy) cho thấy sự tăng trưởng ổn định, mặc dù có sự biến động trong các năm, đặc biệt là sự giảm nhẹ trong năm 2019.

-Tốc độ tăng trưởng của doanh thu thuần là dốc lên (đường hồi quy đỏ) trong suốt giai đoạn, cho thấy sự tăng trưởng mạnh mẽ từ năm 2015 đến 2020.

Biểu đồ cho thấy doanh thu thuần của PET tăng trưởng mạnh mẽ trong giai đoạn từ 2015 đến 2020, với sự ổn định trong phần lớn thời gian và đột phá lớn vào năm 2020.

4.2. Xu hướng Lợi nhuận sau thuế

dt_ln <- financial_long %>% filter(Khoan_muc == "Lợi nhuận sau thuế")
ggplot(dt_ln, aes(x = Nam, y = Gia_tri_ty)) +
  geom_area(fill = "lightgreen", alpha = 0.5) +
  geom_line(color = "darkgreen", linewidth = 1.2) +
  geom_point(size = 3, color = "forestgreen") +
  geom_text(aes(label = round(Gia_tri_ty,0)), vjust = -1, size = 3) +
  labs(title = "Xu hướng Lợi nhuận sau thuế PET",
       x = "Năm", y = "Giá trị (tỷ đồng)") +
  theme_fin

Giải thích kỹ thuật:

Lọc bảng financial_long để lấy các dòng có Khoan_muc là “Lợi nhuận sau thuế” tạo thành tập dữ liệu dt_ln.

Sử dụng ggplot() với trục hoành là Năm (Nam) và trục tung là Giá trị lợi nhuận sau thuế (Gia_tri_ty).

geom_area() tạo biểu đồ diện tích, màu xanh lá cây nhạt và độ mờ 0.5.

geom_line() vẽ đường xu hướng lợi nhuận sau thuế, màu xanh lá cây đậm, độ dày 1.2.

geom_point() đánh dấu các điểm dữ liệu, màu xanh lá cây, kích thước 3.

geom_text() hiển thị nhãn giá trị tại mỗi điểm, được làm tròn và đặt ngay phía trên.

Nhận xét:

Biểu đồ Xu hướng Lợi nhuận sau thuế PET (2015-2024) cho thấy:

-Lợi nhuận sau thuế có biến động mạnh, đạt đỉnh 311 tỷ đồng vào năm 2020.

-Đường xu hướng cho thấy sự phục hồi mạnh mẽ trong những năm gần đây, với lợi nhuận sau thuế bắt đầu giảm dần sau năm 2020.

-Sự thay đổi mạnh mẽ của lợi nhuận sau thuế cho thấy khả năng kiểm soát chi phí của công ty chưa ổn định qua các năm.

Lợi nhuận sau thuế của PET biến động khá mạnh quanh xu hướng tăng. Biểu đồ cho thấy khả năng kiểm soát chi phí của công ty có thể còn nhiều biến động.

4.3. Tổng tài sản qua các năm

dt_ta <- financial_long %>% filter(Khoan_muc == "Tổng tài sản")
ggplot(dt_ta, aes(x = Nam, y = Gia_tri_ty)) +
  geom_line(color = "darkorange", linewidth = 1.3) +
  geom_point(size = 3, color = "orange4") +
  geom_text(aes(label = round(Gia_tri_ty,0)), vjust = -1, size = 3) +
  geom_smooth(method = "loess", se = FALSE, color = "red", linetype = "dashed") +
  labs(title = "Tổng tài sản PET (2015–2024)",
       x = "Năm", y = "Tổng tài sản (tỷ đồng)") +
  theme_fin

Giải thích kỹ thuật:

Dữ liệu được lọc từ bảng financial_long để chỉ lấy các dòng có Khoan_muc là “Tổng tài sản” lưu vào dt_ta.

Sử dụng ggplot() với trục x là Năm (Nam) và trục y là Giá trị tổng tài sản (Gia_tri_ty).

geom_line() vẽ đường xu hướng tổng tài sản, màu cam đậm, độ dày 1.3.

geom_point() hiển thị các điểm dữ liệu, màu cam nhạt, kích thước 3.

geom_text() thêm nhãn giá trị tại từng điểm, hiển thị giá trị tổng tài sản được làm tròn.

geom_smooth(method = “loess”) vẽ đường xu hướng mượt, màu đỏ, kiểu đường chấm để thể hiện xu hướng tăng ổn định.

Nhận xét:

Biểu đồ Xu hướng Tổng tài sản PET (2015–2024) cho thấy:

-Tổng tài sản tăng liên tục và ổn định qua các năm.

-Đặc biệt từ năm 2020 trở đi, mức tăng rõ rệt từ khoảng 6,000 tỷ đồng lên đến 10,165 tỷ đồng vào 2024.

-Đường xu hướng mượt (loess) thể hiện tốc độ tăng trưởng mạnh mẽ, trong khi đường hồi quy tuyến tính (đỏ) cho thấy xu hướng dài hạn tăng liên tục.

Biểu đồ thể hiện sự tăng trưởng ổn định và mạnh mẽ của tổng tài sản PET trong suốt giai đoạn 2015–2024, đặc biệt là giai đoạn từ 2020 trở đi.

4.4. So sánh Tổng tài sản và Vốn chủ sở hữu

dt_ta_vcsh <- financial_long %>%
  filter(Khoan_muc %in% c("Tổng tài sản", "Vốn chủ sở hữu"))
ggplot(dt_ta_vcsh, aes(x = Nam, y = Gia_tri_ty, color = Khoan_muc)) +
  geom_line(linewidth = 1.3) +
  geom_point(size = 3) +
  geom_text_repel(aes(label = round(Gia_tri_ty,0)), size = 3) +
  labs(title = "So sánh Tổng tài sản và Vốn chủ sở hữu",
       x = "Năm", y = "Giá trị (tỷ đồng)", color = "Khoản mục") +
  scale_color_manual(values = c("darkorange", "steelblue")) +
  theme_fin

Giải thích kỹ thuật:

Dữ liệu được lọc từ bảng financial_long để chỉ lấy hai khoản mục “Tổng tài sản” và “Vốn chủ sở hữu” lưu vào dt_ta_vcsh.

Sử dụng ggplot() với trục x là Năm (Nam), trục y là Giá trị (Gia_tri_ty), và phân loại theo Khoan_muc để hiển thị hai đường so sánh.

geom_line() vẽ các đường xu hướng, độ dày 1.3.

geom_point() đánh dấu các điểm dữ liệu, kích thước 3.

geom_text_repel() thêm nhãn giá trị, giúp tránh chồng chữ giữa các điểm.

scale_color_manual() gán màu cam cho “Tổng tài sản” và xanh dương cho “Vốn chủ sở hữu”.

Nhận xét:

Biểu đồ So sánh Tổng tài sản và Vốn chủ sở hữu cho thấy:

-Tổng tài sản tăng trưởng mạnh mẽ từ năm 2015 đến 2024, đặc biệt từ năm 2019, với mức tăng vọt lên 10,165 tỷ đồng vào năm 2024.

-Vốn chủ sở hữu có sự tăng trưởng ổn định nhưng không mạnh như tổng tài sản, với mức tăng từ 1,352 tỷ đồng năm 2015 lên 2,338 tỷ đồng vào năm 2024.

-Sự tăng trưởng mạnh mẽ của tổng tài sản cho thấy khả năng mở rộng quy mô hoạt động, trong khi vốn chủ sở hữu tăng trưởng chậm hơn, phản ánh tỷ lệ tự tài trợ cao và ít phụ thuộc vào nợ.

Biểu đồ cho thấy sự tăng trưởng mạnh mẽ của tổng tài sản PET, với khả năng mở rộng quy mô liên tục. Mặc dù vốn chủ sở hữu cũng tăng, nhưng tỷ lệ tăng trưởng thấp hơn, cho thấy công ty chủ yếu dựa vào nguồn tài chính bên ngoài để tài trợ cho sự mở rộng này.

4.5. Cơ cấu nguồn vốn (Nợ vs Vốn chủ)

dt_no_von <- financial_long %>%
  filter(Khoan_muc %in% c("Nợ phải trả", "Vốn chủ sở hữu")) %>%
  group_by(Nam, Khoan_muc) %>%
  summarise(Gia_tri_ty = sum(Gia_tri_ty))
ggplot(dt_no_von, aes(x = Nam, y = Gia_tri_ty, fill = Khoan_muc)) +
  geom_bar(stat = "identity", position = "fill") +
  scale_y_continuous(labels = percent) +
  labs(title = "Cơ cấu nguồn vốn PET (tỷ trọng %)",
       x = "Năm", y = "Tỷ trọng", fill = "Khoản mục") +
  theme_fin

Giải thích kỹ thuật:

Dữ liệu được lọc từ bảng financial_long để lấy hai khoản mục “Nợ phải trả” và “Vốn chủ sở hữu”. Sau đó nhóm theo Năm (Nam) và Khoản mục, rồi tính tổng giá trị (Gia_tri_ty) cho từng nhóm bằng hàm summarise().

Sử dụng ggplot() với trục x là Năm, trục y là Giá trị (Gia_tri_ty), và màu fill đại diện cho từng khoản mục.

geom_bar(stat = “identity”, position = “fill”): vẽ biểu đồ cột tỷ trọng, trong đó mỗi cột biểu thị 100% cơ cấu nguồn vốn của từng năm, được chia theo tỷ lệ của Nợ phải trả và Vốn chủ sở hữu.

scale_y_continuous(labels = percent): hiển thị trục tung dưới dạng phần trăm.

Nhận xét:

Biểu đồ Cơ cấu nguồn vốn PET (tỷ trọng %) cho thấy:

-Tỷ lệ “Nợ phải trả” (màu đỏ) giảm dần trong suốt giai đoạn từ 2015 đến 2022, cho thấy công ty giảm dần phụ thuộc vào nợ và tăng khả năng tự tài trợ.

-Tỷ lệ “Vốn chủ sở hữu” (màu xanh dương) tăng mạnh qua các năm đặc biệt là từ năm 2019 đến 2022 cho thấy công ty đang tăng cường vốn tự có và giảm bớt nợ.

Biểu đồ cho thấy PET đang chuyển hướng từ một cấu trúc vốn phụ thuộc vào nợ sang một cấu trúc vốn an toàn hơn, với tỷ lệ vốn chủ sở hữu ngày càng cao và tỷ lệ nợ phải trả giảm.

4.6. So sánh 3 chỉ tiêu hiệu quả

dt_hq <- financial_long %>%
  filter(Khoan_muc %in% c("Doanh thu thuần","Lợi nhuận sau thuế","Lợi nhuận gộp"))
ggplot(dt_hq, aes(x = Nam, y = Gia_tri_ty, color = Khoan_muc)) +
  geom_line(linewidth = 1.2) +
  geom_point(size = 3) +
  geom_smooth(se = FALSE, method = "lm", linetype = "dashed") +
  labs(title = "So sánh hiệu quả kinh doanh PET",
       x = "Năm", y = "Giá trị (tỷ đồng)", color = "Chỉ tiêu") +
  theme_fin

Giải thích kỹ thuật:

Dữ liệu được lọc từ bảng financial_long để lấy ba khoản mục chính là “Doanh thu thuần”, “Lợi nhuận sau thuế”, và “Lợi nhuận gộp”. Tập dữ liệu này được lưu vào dt_hq .

Sử dụng ggplot() với trục x là Năm (Nam), trục y là Giá trị (Gia_tri_ty), và màu sắc đại diện cho từng chỉ tiêu

geom_line() vẽ đường biểu diễn xu hướng giá trị của từng chỉ tiêu qua các năm, với độ dày 1.2.

geom_point() hiển thị các điểm dữ liệu trên từng đường, kích thước 3.

geom_smooth(method = “lm”) vẽ đường xu hướng tuyến tính (hồi quy), kiểu đường chấm, giúp thể hiện xu hướng tổng thể.

Nhận xét:

Biểu đồ So sánh hiệu quả kinh doanh PET cho thấy:

-Doanh thu thuần (màu đỏ) có sự tăng trưởng mạnh mẽ từ 2019 đến 2020, với sự đột phá vào năm 2020.

-Lợi nhuận gộp (màu xanh lá) và Lợi nhuận sau thuế (màu xanh dương) tăng đều nhưng không mạnh mẽ như doanh thu thuần. Chúng cho thấy sự ổn định trong kết quả lợi nhuận sau thuế.

-Đường hồi quy cho thấy Doanh thu thuần có xu hướng tăng mạnh mẽ, trong khi Lợi nhuận gộp và Lợi nhuận sau thuế tăng đều đặn và ổn định.

Biểu đồ cho thấy sự đột phá trong Doanh thu thuần trong năm 2020, đồng thời phản ánh một xu hướng ổn định trong Lợi nhuận gộp và Lợi nhuận sau thuế.

4.7. Cơ cấu tài sản (Ngắn hạn vs Tổng tài sản)

# Chuẩn bị dữ liệu
ts_tong <- financial_long %>%
  filter(Khoan_muc %in% c("Tài sản ngắn hạn", "Tổng tài sản")) %>%
  select(Nam, Khoan_muc, Gia_tri_ty) %>%
  tidyr::pivot_wider(names_from = Khoan_muc, values_from = Gia_tri_ty) %>%
  mutate(
    Ty_trong_TSNH = round(`Tài sản ngắn hạn` / `Tổng tài sản` * 100, 2)
  ) %>%
  na.omit()
# Vẽ biểu đồ
ggplot(ts_tong, aes(x = Nam, y = Ty_trong_TSNH)) +
  geom_col(fill = "skyblue", alpha = 0.8) +
  geom_text(aes(label = paste0(Ty_trong_TSNH, "%")), vjust = -0.5, size = 3) +
  geom_hline(yintercept = mean(ts_tong$Ty_trong_TSNH), color = "red", linetype = "dashed") +
  annotate("text", x = max(ts_tong$Nam), y = mean(ts_tong$Ty_trong_TSNH) + 1,
           label = "Trung bình giai đoạn", color = "red", size = 3.5, hjust = 1) +
  labs(
    title = "Tỷ trọng Tài sản ngắn hạn trong Tổng tài sản của PET",
    subtitle = "Giai đoạn 2015–2024",
    x = "Năm", y = "Tỷ trọng (%)"
  ) +
  theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold", hjust = 0.5))

Giải thích kỹ thuật:

Dữ liệu được lọc từ bảng financial_long để chỉ lấy hai khoản mục “Tài sản ngắn hạn” và “Tổng tài sản”.

Lệnh mutate(Ty_trong_TSNH = round(Tài sản ngắn hạn/Tổng tài sản * 100, 2)): tính tỷ trọng của tài sản ngắn hạn trong tổng tài sản, làm tròn đến 2 chữ số thập phân.

na.omit(): loại bỏ các dòng có giá trị thiếu (NA).

geom_col() tạo biểu đồ cột với màu xanh da trời nhạt (skyblue), độ mờ 0.8.

geom_text() thêm nhãn giá trị phần trăm lên trên đầu mỗi cột.

geom_hline() vẽ đường trung bình màu đỏ (dashed) biểu thị giá trị trung bình của tỷ trọng tài sản ngắn hạn trong toàn kỳ.

theme_minimal() tối giản bố cục, tạo sự rõ ràng cho biểu đồ.

Nhận xét:

Biểu đồ Tỷ trọng Tài sản ngắn hạn trong Tổng tài sản cho thấy:

-Tỷ trọng tài sản ngắn hạn chiếm khoảng 55%-60% tổng tài sản trong suốt giai đoạn từ 2015 đến 2024.

-Tỷ trọng tài sản ngắn hạn cao nhất vào năm 2022 (91.63%), với một sự tăng trưởng mạnh mẽ trong giai đoạn cuối.

-Đường trung bình đỏ cho thấy mức trung bình tỷ trọng tài sản ngắn hạn trong suốt giai đoạn, dao động quanh 80-85%.

Biểu đồ chỉ ra rằng PET duy trì tỷ trọng tài sản ngắn hạn ổn định trong khoảng 55%-60%, điều này cho thấy một cấu trúc vốn linh hoạt với khả năng thanh khoản cao, phù hợp với đặc thù ngành kinh doanh.

4.8. Dòng tiền hoạt động, đầu tư, tài chính

dt_cf <- financial_long %>%
  filter(grepl("Lưu chuyển tiền thuần", Khoan_muc))
ggplot(dt_cf, aes(x = Nam, y = Gia_tri_ty, fill = Khoan_muc)) +
  geom_bar(stat = "identity", position = "dodge") +
  labs(title = "So sánh 3 dòng tiền PET (2015–2024)",
       x = "Năm", y = "Giá trị (tỷ đồng)", fill = "Loại dòng tiền") +
  theme_fin

Giải thích kỹ thuật:

Dữ liệu được lọc từ bảng financial_long chỉ giữ lại các dòng có Khoan_muc chứa từ khóa “Lưu chuyển tiền thuần” bao gồm các loại :Lưu chuyển tiền thuần từ hoạt động kinh doanh, Lưu chuyển tiền thuần từ hoạt động đầu tư, Lưu chuyển tiền thuần từ hoạt động tài chính.

Sử dụng ggplot() với trục x là Năm (Nam), trục y là Giá trị (Gia_tri_ty), và màu fill đại diện cho từng loại dòng tiền (Khoan_muc).

geom_bar(stat = “identity”, position = “dodge”): vẽ các cột riêng biệt cho từng loại dòng tiền, giúp so sánh trực quan theo từng năm.

labs() thêm tiêu đề, tên trục, và chú thích màu sắc để phân biệt các loại dòng tiền.

Nhận xét:

Biểu đồ So sánh 3 dòng tiền PET (2015–2024) cho thấy:

-Dòng tiền từ hoạt động kinh doanh (màu xanh dương) chiếm phần lớn trong tổng dòng tiền qua các năm, với sự gia tăng mạnh mẽ vào năm 2020, đạt mức hơn 900 tỷ đồng.

-Dòng tiền từ hoạt động đầu tư (màu đỏ) có mức tăng giảm biến động mạnh trong các năm, đặc biệt là tăng vọt vào năm 2020. Điều này có thể phản ánh một số khoản đầu tư lớn trong năm này.

-Dòng tiền từ hoạt động tài chính (màu xanh lá cây) duy trì ở mức thấp và có sự giảm sút rõ rệt trong năm 2022.

Biểu đồ cho thấy dòng tiền từ hoạt động kinh doanh luôn duy trì mức độ ổn định và mạnh mẽ, trong khi dòng tiền từ hoạt động đầu tư có sự thay đổi mạnh mẽ vào các năm đặc biệt. Dòng tiền từ tài chính có sự ổn định thấp, phản ánh khả năng kiểm soát chi phí nợ tốt.

4.9. Tốc độ tăng trưởng Doanh thu

dt_rev_growth <- financial_long %>%
  filter(Khoan_muc == "Doanh thu thuần")
ggplot(dt_rev_growth, aes(x = Nam, y = Tang_truong)) +
  geom_line(color = "red", linewidth = 1.2) +
  geom_point(size = 3, color = "darkred") +
  geom_hline(yintercept = 0, color = "gray40", linetype = "dashed") +
  geom_text(aes(label = paste0(Tang_truong,"%")), vjust = -1) +
  labs(title = "Tốc độ tăng trưởng Doanh thu thuần (%)",
       x = "Năm", y = "Tăng trưởng (%)") +
  theme_fin

Giải thích kỹ thuật:

Dữ liệu được lọc từ bảng financial_long để chỉ lấy các dòng có Khoan_muc = “Doanh thu thuần” lưu vào dt_rev_growth.

Sử dụng ggplot() với trục x là Năm (Nam) và y là Tăng trưởng (Tang_truong):

geom_line() vẽ đường biểu diễn tỷ lệ tăng trưởng, màu đỏ, độ dày 1.2.

geom_point() đánh dấu các điểm dữ liệu, màu đỏ đậm, kích thước 3.

geom_hline() thêm đường ngang tại mức 0% (màu xám, kiểu gạch chấm) để thể hiện ranh giới giữa tăng và giảm trưởng.

geom_text() hiển thị nhãn giá trị tỷ lệ tăng trưởng ngay tại mỗi điểm.

Nhận xét:

Biểu đồ Tốc độ tăng trưởng Doanh thu thuần (%) cho thấy:

-Tốc độ tăng trưởng âm vào năm 2016 (-7.23%) và 2019 (-9.77%), cho thấy sự suy giảm mạnh mẽ trong những năm này.

-Tốc độ tăng trưởng dương vào năm 2017 (8.31%) và 2020 (34.42%), cho thấy sự phục hồi và tăng trưởng mạnh mẽ vào năm 2020.

-Đường xu hướng hồi quy cho thấy sự tăng trưởng liên tục từ 2017 đến 2020, mặc dù có sự suy giảm tạm thời trong năm 2019.

Biểu đồ chỉ ra rằng mặc dù PET trải qua một năm suy giảm doanh thu vào năm 2019, nhưng đã có sự phục hồi và tăng trưởng mạnh mẽ vào năm 2020, điều này có thể phản ánh sự cải thiện trong chiến lược kinh doanh của công ty trong giai đoạn này.

4.10. Phân loại trạng thái tăng trưởng

ggplot(financial_long, aes(x = Trang_thai, fill = Nhom)) +
  geom_bar(position = "dodge") +
  labs(title = "Tần suất trạng thái tăng trưởng theo nhóm chỉ tiêu",
       x = "Trạng thái tăng trưởng", y = "Số lần xuất hiện", fill = "Nhóm chỉ tiêu") +
  theme_fin

Giải thích kỹ thuật:

-ggplot(): tạo biểu đồ với trục x là Trang_thai (trạng thái tăng trưởng) và màu sắc của các cột được phân loại theo “Nhom” (nhóm chỉ tiêu).

-geom_bar(): vẽ biểu đồ cột với các nhóm cột tách rời nhau (vị trí dodge), giúp dễ dàng so sánh số lượng các trạng thái tăng trưởng trong từng nhóm chỉ tiêu.

Nhận xét:

Biểu đồ Phân loại trạng thái tăng trưởng theo nhóm chỉ tiêu cho thấy:

-Nhóm chỉ tiêu “Cơ cấu nguồn vốn” (màu hồng) có số lần xuất hiện nhiều nhất trong nhóm “Tăng mạnh”, phản ánh sự tăng trưởng mạnh trong cơ cấu nguồn vốn trong giai đoạn này.

-Nhóm chỉ tiêu “Dòng tiền” (màu xanh lá) có sự phân bổ đều, đặc biệt là trong nhóm “Tăng nhẹ”, cho thấy dòng tiền có sự tăng trưởng nhẹ nhưng ổn định.

-Nhóm chỉ tiêu “Hiệu quả kinh doanh” (màu xanh dương) tập trung chủ yếu trong nhóm “Tăng mạnh”, cho thấy hiệu quả kinh doanh đã cải thiện mạnh mẽ trong những năm gần đây.

-Nhóm chỉ tiêu “Tài sản” (màu tím) có sự phân bổ khá đều, với sự tăng trưởng mạnh trong nhóm “Tăng mạnh” và một phần nhỏ trong nhóm “Tăng nhẹ”.

Biểu đồ chỉ ra rằng các nhóm chỉ tiêu có sự phân bổ trạng thái tăng trưởng khác nhau, với “Hiệu quả kinh doanh” và “Cơ cấu nguồn vốn” cho thấy sự cải thiện mạnh mẽ, trong khi “Dòng tiền” và “Tài sản” có sự ổn định hoặc tăng trưởng nhẹ. Điều này phản ánh sự phát triển bền vững và khả năng kiểm soát tốt chi phí của công ty.

4.11. Phân phối logarit của các khoản mục

ggplot(financial_long, aes(x = Log_value, fill = Nhom)) +
  geom_histogram(bins = 25, alpha = 0.6, position = "identity") +
  geom_density(alpha = 0.2) +
  labs(title = "Phân phối logarit các khoản mục tài chính",
       x = "Log(Giá trị)", y = "Tần suất") +
  theme_fin

Giải thích kỹ thuật:

Dữ liệu được lấy từ financial_long, trong đó trục x là Log_value (giá trị đã được chuyển đổi logarit) và màu sắc thể hiện các nhóm chỉ tiêu tài chính (Nhom).

geom_histogram(): tạo biểu đồ histogram gồm 25 cột thể hiện phân bố tần suất của giá trị logarit, các cột được làm trong suốt 60% để dễ quan sát sự chồng lấp.

geom_density(alpha = 0.2): thêm đường mật độ (density) cho từng nhóm dữ liệu, giúp thể hiện rõ xu hướng phân bố.

Nhận xét:

Biểu đồ Phân phối logarit các khoản mục tài chính cho thấy:

-Nhóm chỉ tiêu “Cơ cấu nguồn vốn” (màu hồng) có phân phối mật độ tập trung ở giá trị logarit thấp hơn, cho thấy giá trị của các khoản mục này chủ yếu nằm ở mức nhỏ.

-Nhóm chỉ tiêu “Dòng tiền” (màu xanh lá) có sự phân bố khá đều ở các giá trị logarit từ 5 đến 7, cho thấy dòng tiền có sự phân bổ đều trong các mức giá trị.

-Nhóm chỉ tiêu “Hiệu quả kinh doanh” (màu xanh dương) có sự phân bố rõ rệt ở mức giá trị logarit cao hơn, điều này phản ánh các chỉ tiêu này có sự phân bổ tại các giá trị lớn.

-Nhóm chỉ tiêu “Tài sản” (màu tím) có sự phân bổ mật độ ở các giá trị logarit lớn hơn, cho thấy tài sản chủ yếu có giá trị lớn và phân bố ở mức cao.

Biểu đồ phân phối logarit cho thấy sự phân bố của các nhóm chỉ tiêu tài chính khác nhau, với các nhóm như “Cơ cấu nguồn vốn” và “Dòng tiền” có phân phối thấp hơn, trong khi “Hiệu quả kinh doanh” và “Tài sản” có phân bố tại các giá trị cao. Điều này cho thấy sự đa dạng trong phân bổ giá trị của các khoản mục tài chính của công ty.

4.12. Biến động (CV%) theo khoản mục

cv_table <- financial_long %>%
  group_by(Khoan_muc) %>%
  summarise(CV = round(sd(Gia_tri_ty)/mean(Gia_tri_ty)*100,2))
ggplot(cv_table, aes(x = reorder(Khoan_muc, CV), y = CV)) +
  geom_col(fill = "tomato") +
  geom_text(aes(label = paste0(CV,"%")), hjust = -0.1) +
  coord_flip() +
  labs(title = "Hệ số biến động (CV%) từng khoản mục",
       x = "Khoản mục", y = "CV (%)") +
  theme_fin

Giải thích kỹ thuật:

summarise(CV = round(sd(Gia_tri_ty) * 100, 2)): tính toán hệ số biến động (CV) của mỗi khoản mục trong dữ liệu tài chính bằng cách tính độ lệch chuẩn của Gia_tri_ty (giá trị tỷ lệ) và nhân với 100 để đưa ra tỷ lệ phần trăm.

ggplot(cv_table, aes(x = reorder(Khoan_muc, CV), y = CV)): Vẽ biểu đồ cột (bar chart) với trục x là các khoản mục, trục y là giá trị hệ số biến động (CV).

geom_col(fill = “tomato”): Sử dụng màu đỏ tươi (tomato) cho các cột trong biểu đồ.

geom_text(aes(label = paste0(CV, “%”)), hjust = -0.1): Thêm nhãn số liệu cho các cột, hiển thị giá trị CV dưới dạng phần trăm.

coord_flip(): Đảo chiều biểu đồ (từ dạng cột thẳng đứng sang dạng nằm ngang).

Nhận xét:

Biểu đồ Hệ số biến động (CV%) cho thấy:

-“Lưu chuyển tiền thuần từ hoạt động đầu tư” có hệ số biến động cao nhất (59%), điều này cho thấy dòng tiền từ hoạt động đầu tư của công ty có sự biến động lớn, rủi ro cao.

-“Lưu chuyển tiền thuần từ hoạt động kinh doanh” cũng có hệ số biến động lớn (48.1%), cho thấy sự bất ổn trong hoạt động kinh doanh.

-Các khoản mục như “Lợi nhuận sau thuế” (32.79%), “Tài sản ngắn hạn” (30.91%), và “Nợ phải trả” (29.65%) có hệ số biến động vừa phải, cho thấy sự không ổn định trong các khoản mục này nhưng không quá cao.

-Các khoản mục như “Vốn chủ sở hữu” (16.9%), “Lợi nhuận gộp” (16.6%) và “Doanh thu thuần” (11.86%) có hệ số biến động thấp, phản ánh sự ổn định hơn trong các khoản mục này.

Biểu đồ phân tích hệ số biến động cho thấy các khoản mục liên quan đến dòng tiền và lợi nhuận có sự biến động lớn hơn, trong khi các khoản mục như tài sản và doanh thu lại ổn định hơn. Điều này giúp xác định các yếu tố có mức độ rủi ro cao và thấp trong hoạt động tài chính của công ty.

4.13. Độ tương quan giữa các khoản mục (Heatmap)

cor_data <- financial_long %>%
  select(Khoan_muc, Nam, Gia_tri_ty) %>%
  pivot_wider(names_from = Khoan_muc, values_from = Gia_tri_ty) %>%
  select(-Nam)
corrplot(cor(cor_data, use = "pairwise.complete.obs"), method = "color", tl.cex = 0.7)

Giải thích kỹ thuật:

Dữ liệu được chọn từ bảng financial_long gồm Khoản mục(Khoan_muc), Năm(Nam), và Giá trị tỷ(Gia_tri_ty). Sau đó chuyển sang dạng rộng bằng hàm pivot_wider().

Hàm cor(use = “pairwise.complete.obs”) tính ma trận tương quan giữa các khoản mục.

corrplot(method = “color”) vẽ biểu đồ heatmap thể hiện mức độ tương quan qua màu sắc (đậm → tương quan mạnh).

Nhận xét:

Biểu đồ heatmap cho thấy mối quan hệ giữa các khoản mục tài chính:

-Tổng tài sản và Doanh thu thuần có mối tương quan rất cao (gần 1), cho thấy sự liên kết chặt chẽ giữa quy mô tài sản và doanh thu của công ty.

-Các khoản mục như Tài sản ngắn hạn, Tổng tài sản, Lưu chuyển tiền thuần từ hoạt động kinh doanh, Doanh thu thuần cũng có sự tương quan mạnh mẽ với nhau.

-Lưu chuyển tiền thuần từ hoạt động tài chính và Lưu chuyển tiền thuần từ hoạt động đầu tư có mối tương quan thấp (gần 0), cho thấy chúng ít liên quan đến các khoản mục còn lại.

Doanh thu thuần và Tổng tài sản có mối quan hệ chặt chẽ, điều này phản ánh sự phụ thuộc giữa quy mô tài sản và doanh thu trong doanh nghiệp. Những mối quan hệ mạnh mẽ giữa các khoản mục tài chính sẽ giúp công ty kiểm soát và dự báo tài chính chính xác hơn.

4.14. Lợi nhuận so với Doanh thu

ggplot(rev_profit, aes(x = `Doanh thu thuần`, y = `Lợi nhuận sau thuế`)) +
  geom_point(color = "steelblue", size = 3) +
  geom_smooth(method = "lm", se = FALSE, color = "red", linewidth = 1) +
  geom_text_repel(aes(label = Nam)) +
  labs(title = "Quan hệ giữa Doanh thu và Lợi nhuận",
       x = "Doanh thu thuần (tỷ đồng)", y = "Lợi nhuận sau thuế (tỷ đồng)") +
  theme_fin

Giải thích kỹ thuật:

Biểu đồ sử dụng dữ liệu từ rev_profit, với trục x là “Doanh thu thuần” và trục y là “Lợi nhuận sau thuế”.

geom_point() vẽ các điểm dữ liệu màu xanh steelblue.

geom_smooth(method = “lm”, color = “red”) thêm đường hồi quy tuyến tính biểu thị xu hướng giữa hai biến.

geom_text_repel() hiển thị năm tại từng điểm để tránh chồng chữ.

Nhận xét:

Mô hình tuyến tính:

-Biểu đồ biểu thị mối quan hệ giữa Doanh thu thuần và Lợi nhuận sau thuế. Mối quan hệ này có sự tương quan ngược lại rõ rệt, thể hiện qua đường xu hướng giảm dần.

-Đường xu hướng (màu đỏ) có độ dốc âm, cho thấy Doanh thu thuần tăng thì Lợi nhuận sau thuế lại giảm.

-Hệ số xác định R² có giá trị cao, chứng tỏ mối quan hệ này có ý nghĩa thống kê rõ ràng.

Tăng trưởng Doanh thu thuần có ảnh hưởng tiêu cực đến Lợi nhuận sau thuế, với một mô hình hồi quy có ý nghĩa thống kê.

4.15. ROA và ROE qua thời gian

# Chuẩn bị dữ liệu các khoản mục
ta <- financial_long %>% filter(Khoan_muc == "Tổng tài sản") %>% select(Nam, Tong_tai_san = Gia_tri_ty)
vcsh <- financial_long %>% filter(Khoan_muc == "Vốn chủ sở hữu") %>% select(Nam, Von_chu_so_huu = Gia_tri_ty)
lnst <- financial_long %>% filter(Khoan_muc == "Lợi nhuận sau thuế") %>% select(Nam, Loi_nhuan_sau_thue = Gia_tri_ty)
# Gộp lại theo năm (inner join đảm bảo chỉ lấy năm có đủ 3 giá trị)
data_ratio <- lnst %>%
  inner_join(ta, by = "Nam") %>%
  inner_join(vcsh, by = "Nam") %>%
  mutate(
    ROA = round(Loi_nhuan_sau_thue / Tong_tai_san * 100, 2),
    ROE = round(Loi_nhuan_sau_thue / Von_chu_so_huu * 100, 2)
  )
# Chuyển sang dạng long
data_ratio_long <- tidyr::pivot_longer(
  data_ratio,
  cols = c("ROA", "ROE"),
  names_to = "Chi_tieu",
  values_to = "Ty_suat"
)
ggplot(data_ratio_long, aes(x = Nam, y = Ty_suat, color = Chi_tieu)) +
  geom_line(linewidth = 1.2) +
  geom_point(size = 3) +
  geom_text(aes(label = round(Ty_suat, 1)), vjust = -1, size = 3) +
  labs(
    title = "ROA và ROE của PET (2015–2024)",
    x = "Năm", y = "Tỷ suất (%)", color = "Chỉ tiêu"
  ) +
  theme_minimal(base_size = 12) +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"))

Giải thích kỹ thuật:

Dữ liệu được lọc từ bảng financial_long để lấy các khoản mục: Tổng tài sản, Vốn chủ sở hữu, và Lợi nhuận sau thuế.

Ba bảng này được gộp lại theo cột “Năm” bằng inner_join().

Tạo hai biến mới (kết quả được làm tròn đến hai chữ số thập phân):

-ROA = (Lợi nhuận sau thuế / Tổng tài sản) × 100

-ROE = (Lợi nhuận sau thuế / Vốn chủ sở hữu) × 100

Dữ liệu sau đó được chuyển từ dạng rộng sang dạng dài để vẽ biểu đồ so sánh giữa ROA và ROE.

ggplot() được dùng để vẽ đường xu hướng của hai tỷ suất, kèm nhãn hiển thị giá trị từng năm.

Nhận xét:

Tỷ suất ROA có sự giảm mạnh từ 15.7% vào năm 2015 xuống 7.8% vào năm 2020, và sau đó ổn định quanh mức 9.4% vào năm 2024. Điều này cho thấy PET có thể đã gặp khó khăn trong việc sử dụng tài sản hiệu quả trong giai đoạn từ 2015 đến 2020.

Tỷ suất ROE có mức cao hơn ROA và ổn định hơn, bắt đầu từ 3.7% vào năm 2015 và đạt 9.4% vào năm 2024. Điều này chỉ ra rằng PET đã tận dụng tốt vốn chủ sở hữu trong suốt giai đoạn này để đạt được mức lợi nhuận tương đối tốt.

PET cần cải thiện hiệu quả sử dụng tài sản để tăng cường ROA, trong khi ROE cho thấy công ty đã làm khá tốt trong việc sử dụng vốn chủ sở hữu để tạo ra lợi nhuận.

4.16. Tương quan giữa Tài sản & Doanh thu

# Chuẩn bị dữ liệu
ta_rev <- financial_long %>%
  filter(Khoan_muc %in% c("Tổng tài sản", "Doanh thu thuần")) %>%
  select(Nam, Khoan_muc, Gia_tri_ty) %>%
  tidyr::pivot_wider(names_from = Khoan_muc, values_from = Gia_tri_ty) %>%
  rename(
    Tong_tai_san = `Tổng tài sản`,
    Doanh_thu = `Doanh thu thuần`
  ) %>%
  na.omit()
ggplot(ta_rev, aes(x = Tong_tai_san, y = Doanh_thu)) +
  geom_point(size = 3, color = "darkorange") +
  geom_smooth(method = "lm", se = FALSE, color = "red", linewidth = 1) +
  geom_text(aes(label = Nam), vjust = -1, size = 3) +
  geom_abline(intercept = 0, slope = 1, color = "gray50", linetype = "dotted") +
  labs(
    title = "Quan hệ giữa Tổng tài sản và Doanh thu thuần của PET",
    subtitle = "Phân tích mối quan hệ quy mô – hiệu suất",
    x = "Tổng tài sản (tỷ đồng)",
    y = "Doanh thu thuần (tỷ đồng)"
  ) +
  theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold", hjust = 0.5))

Giải thích kỹ thuật:

Lấy hai khoản mục từ bảng financial_long gồm “Tổng tài sản” và “Doanh thu thuần” giữ lại các cột “Nam”, “Khoan_muc”, “Gia_tri_ty”

Dùng hàm pivot_wider() để chuyển dữ liệu từ dạng dài sang rộng, tạo hai cột mới “Tổng_tài_sản” và “Doanh_thu”. Loại bỏ các giá trị NA.

geom_point() hiển thị các điểm dữ liệu (màu cam).

geom_smooth(method = “lm”) vẽ đường hồi quy tuyến tính (màu đỏ).

geom_text() thêm nhãn năm.

geom_abline() vẽ đường chéo góc 45° để so sánh xu hướng tương quan.

Nhận xét:

Sự tương quan mạnh mẽ giữa Tổng tài sản và Doanh thu thuần cho thấy PET đang tận dụng tốt tài sản của mình để tăng trưởng doanh thu. Cụ thể, từ năm 2015 đến 2020, Doanh thu thuần tăng dần từ 10k tỷ đồng lên hơn 12,000 tỷ đồng, trong khi đó Tổng tài sản cũng tăng tương ứng từ 5,000 tỷ đồng lên 7,000 tỷ đồng.

PET đang sử dụng tài sản hiệu quả để thúc đẩy tăng trưởng doanh thu, nhưng cần lưu ý đảm bảo sự ổn định trong việc sử dụng tài sản khi tăng trưởng trong giai đoạn tiếp theo.

4.17. Ma trận phân tán giữa 4 biến chính

pairs(cor_data[,1:4],
      main = "Ma trận phân tán 4 biến tài chính chính",
      pch = 19, col = rgb(0,0,1,0.4))

Giải thích kỹ thuật:

Hàm pairs() để tạo ma trận phân tán giữa 4 biến tài chính chính là Tài sản ngắn hạn, Tổng tài sản, Nợ phải trả, và Vốn chủ sở hữu.

pch = 19 xác định điểm dạng tròn, col = rgb(0,0,1,0.4) tạo màu xanh dương nhạt có độ mờ, giúp quan sát các cụm điểm rõ hơn.

Biểu đồ thể hiện mối quan hệ giữa từng cặp biến, giúp nhận diện tương quan tuyến tính giữa các yếu tố tài chính.

Nhận xét:

Ma trận phân tán cho thấy mối quan hệ giữa các biến tài chính:

-Biểu đồ cho thấy mối quan hệ tuyến tính rõ ràng giữa các biến tài chính chính, đặc biệt là giữa Tổng tài sản, Nợ phải trả, và Vốn chủ sở hữu.

-Cả ba biến này đều có mối quan hệ chặt chẽ, với sự gia tăng của Tổng tài sản kéo theo sự tăng trưởng của các yếu tố tài chính khác, như Nợ phải trả và Vốn chủ sở hữu.

4.18. Đường xu hướng 3 chiều (scatterplot nâng cao)

library(plotly)
fig <- plot_ly(rev_profit, x = ~`Doanh thu thuần`, y = ~`Lợi nhuận sau thuế`, z = ~Nam,
               type = "scatter3d", mode = "lines+markers", line = list(color="blue"))
fig

Giải thích kỹ thuật:

Sử dụng thư viện Plotly để vẽ biểu đồ 3D thể hiện mối quan hệ giữa Doanh thu thuần (x), Lợi nhuận sau thuế (y) và Năm (z)

Các điểm dữ liệu được nối bằng đường màu xanh dương (mode = “lines+markers”) để mô tả xu hướng thay đổi qua thời gian.

Nhận xét:

Mối quan hệ giữa Doanh thu thuần và Lợi nhuận sau thuế:

-Biểu đồ 3D cho thấy một xu hướng tăng trưởng khá ổn định trong Doanh thu thuần và Lợi nhuận sau thuế qua các năm từ 2015 đến 2020. Cụ thể, Doanh thu thuần có sự tăng trưởng đều đặn từ khoảng 10000 tỷ đồng vào năm 2015 lên hơn 12000 tỷ đồng vào năm 2020.

-Lợi nhuận sau thuế cũng tăng từ khoảng 130 tỷ đồng vào năm 2015 lên khoảng 210 tỷ đồng vào năm 2020, cho thấy lợi nhuận của công ty đang phát triển mạnh mẽ cùng với sự gia tăng doanh thu.

Các dữ liệu cho thấy một mối tương quan rõ rệt giữa Doanh thu thuần và Lợi nhuận sau thuế, với cả hai đều tăng mạnh trong giai đoạn 2015-2020. Sự phát triển này chỉ ra rằng công ty đang tối ưu hóa các hoạt động của mình để duy trì và thúc đẩy doanh thu, đồng thời giữ được hiệu quả lợi nhuận cao trong suốt thời gian này.

4.19. Tóm tắt chỉ tiêu tài chính bằng biểu đồ radar

library(fmsb)
radar_data <- data.frame(
  row.names = c("Max","Min","PET"),
  Doanh_thu = c(max(dt_rev$Gia_tri_ty), min(dt_rev$Gia_tri_ty), mean(dt_rev$Gia_tri_ty)),
  Loi_nhuan = c(max(dt_ln$Gia_tri_ty), min(dt_ln$Gia_tri_ty), mean(dt_ln$Gia_tri_ty)),
  Tai_san = c(max(dt_ta$Gia_tri_ty), min(dt_ta$Gia_tri_ty), mean(dt_ta$Gia_tri_ty))
)
radarchart(radar_data, axistype=1,
           pcol="blue", pfcol=rgb(0.2,0.5,1,0.4), plwd=2,
           cglcol="gray", cglty=1, axislabcol="black",
           title="Radar tổng hợp hiệu quả tài chính PET")

Giải thích kỹ thuật:

Dữ liệu được tổng hợp gồm 3 chỉ tiêu tài chính chính là Doanh thu, Lợi nhuận, và Tài sản. Mỗi chỉ tiêu chứa giá trị tối đa, tối thiểu, và trung bình.

Dùng hàm radarchart() để vẽ biểu đồ radar 3 trục, biểu diễn hiệu quả tài chính của PET. Màu sắc được điều chỉnh với pcol=“blue”, pfcol=rgb(0.2,0.5,1,0.4), đường viền màu xám và nhãn trục màu đen để dễ quan sát.

Nhận xét:

Biểu đồ radar cho thấy tóm tắt hiệu quả tài chính PET:

-Doanh thu: Chỉ tiêu này có giá trị gần đạt mức tối đa (100%), phản ánh sự tăng trưởng mạnh mẽ trong doanh thu của công ty.

-Lợi nhuận: Lợi nhuận của công ty vẫn đạt được mức cao nhưng thấp hơn doanh thu một chút, cho thấy có sự chênh lệch giữa tăng trưởng doanh thu và lợi nhuận.

-Tài sản: Giá trị tài sản của công ty cũng nằm ở mức khá ổn định và gần đạt mức tối đa, nhưng không mạnh mẽ bằng doanh thu.

Biểu đồ radar này chỉ ra rằng PET có một sự tăng trưởng mạnh mẽ trong doanh thu và tài sản, nhưng lợi nhuận chưa đạt được sự tăng trưởng tương đương. Điều này có thể chỉ ra rằng công ty cần tối ưu hóa chi phí và cải thiện hiệu quả lợi nhuận trong thời gian tới.