Chương này là một cuộc “điều tra” dữ liệu toàn diện về hoạt động cho vay. Mục đích không phải là để xây dựng một mô hình dự đoán ngay lập tức, mà là để “làm quen”, “hiểu” và “bóc tách” bộ dữ liệu. Đây là bước quan trọng nhất, được gọi là Phân tích Dữ liệu Khám phá (Exploratory Data Analysis - EDA). Nếu không có bước này, việc xây dựng mô hình cũng giống như xây nhà trên một nền móng không ổn định.
Mục tiêu phân tích Phần này đặt ra ba mục tiêu cốt lõi, giống như kim chỉ nam cho toàn bộ chương:
Nhận dạng các yếu tố then chốt:
Mục đích: Chúng ta muốn tìm hiểu xem, trong hàng trăm thông tin về một khách hàng (như tuổi, thu nhập, nghề nghiệp, điểm tín dụng…), thông tin nào (yếu tố nào) có mối liên hệ mạnh mẽ nhất đến việc họ trả nợ đúng hạn hay gặp khó khăn.
Ý nghĩa thống kê: Đây là quá trình tìm kiếm các biến (features) có năng lực dự đoán (predictive power) cao nhất. Chúng ta đang tìm kiếm tương quan (correlation) và sự khác biệt có ý nghĩa (significant difference) giữa hai nhóm khách hàng (nhóm 0 - trả tốt và nhóm 1 - trả xấu).
Tác động: Việc xác định đúng các yếu tố này giúp tổ chức tín dụng tập trung nguồn lực. Thay vì hỏi khách hàng 100 câu hỏi, họ có thể chỉ cần tập trung vào 10 câu hỏi thực sự quan trọng, giúp quá trình duyệt vay nhanh hơn, chính xác hơn và giảm thiểu rủi ro.
Đo lường và minh họa mối quan hệ:
Mục đích: Sau khi biết yếu tố nào quan trọng, chúng ta muốn biết mối quan hệ đó diễn ra như thế nào. Ví dụ: “Tuổi” có quan trọng không? Có phải tuổi càng cao thì rủi ro càng thấp? Hay rủi ro chỉ cao ở nhóm tuổi 20-30 và cao trở lại ở nhóm 60+?
Ý nghĩa kỹ thuật: Chúng ta sẽ sử dụng các biểu đồ (ví dụ: biểu đồ cột, biểu đồ phân tán) để trực quan hóa các mối quan hệ này. Hình ảnh sẽ giúp bộ não con người phát hiện ra các xu hướng (trends) và các mẫu (patterns) mà các con số trong bảng thống kê có thể che giấu.
Tác động: Cung cấp một “chân dung” rõ nét về hồ sơ rủi ro. Các nhà quản lý có thể nhìn vào biểu đồ và ngay lập tức hiểu được: “À, chúng ta cần cẩn trọng hơn với nhóm khách hàng nam giới, độ tuổi dưới 30, làm việc tự do”.
Cung cấp nền tảng dữ liệu vững chắc:
Mục đích: Toàn bộ công việc dọn dẹp, chỉnh sửa và phân tích ở chương này là để chuẩn bị dữ liệu.
Ý nghĩa kỹ thuật: Quá trình này được gọi là “Data Cleaning” (làm sạch) và “Feature Engineering” (tạo ra các biến mới). Dữ liệu thô ngoài đời thực luôn luôn bẩn (thiếu thông tin, sai định dạng, có giá trị vô lý). Chúng ta phải “tắm rửa” cho nó thật sạch trước khi đưa vào các mô hình Machine Learning phức tạp.
Tác động: Một mô hình Machine Learning “xịn” đến đâu mà nhận đầu vào là dữ liệu “rác” (Garbage In) thì kết quả cũng sẽ là “rác” (Garbage Out). Bước này đảm bảo đầu vào là “vàng”, giúp mô hình dự đoán chính xác và ổn định.
Đây là các “nhân vật” chính trong câu chuyện của chúng ta:
TARGET: Biến quan trọng nhất, là thứ chúng ta muốn dự đoán.
Ý nghĩa: 1 = Khó khăn, 0 = Thanh toán tốt. Đây là biến mục tiêu (target variable).
Ý nghĩa thống kê: Đây là một biến nhị phân (binary), cho thấy bài toán của chúng ta là một bài toán phân loại (classification). Chúng ta muốn phân loại một hồ sơ vay mới vào nhóm “0” hay nhóm “1”.
AMT_CREDIT: Tổng số tiền khách hàng vay.
Ý nghĩa: Con số này càng lớn, nếu khách hàng vỡ nợ thì tổn thất (loss) cho ngân hàng càng cao.
Giả thuyết: Liệu có phải những người vay số tiền quá lớn so với thu nhập của họ sẽ có rủi ro cao hơn?
AMT_INCOME_TOTAL: Tổng thu nhập hàng năm.
Ý nghĩa: Đây là chỉ số đo lường khả năng trả nợ (capacity). Thu nhập càng cao, về lý thuyết, khả năng trả nợ càng tốt.
DAYS_BIRTH: Tuổi của khách hàng, tính bằng số ngày (và là số âm).
Ý nghĩa: Một biến nhân khẩu học (demographic) cơ bản. Chúng ta sẽ cần biến đổi (transform) nó thành “Tuổi” (Age) cho dễ hiểu.
EXT_SOURCE: Điểm tín dụng từ nguồn bên ngoài.
Ý nghĩa: Đây là một biến cực kỳ mạnh. Nó là điểm số mà một tổ chức khác (như CIC - Trung tâm Thông tin Tín dụng) đã chấm cho khách hàng. Nếu điểm này thấp, nó là một dấu hiệu cảnh báo rõ ràng.
DAYS_EMPLOYED: Số ngày làm việc tại công ty hiện tại.
library(scales)
library(dplyr)
library(ggplot2)
library(tidyr)
library(scales)
library(kableExtra)
library(tidyverse)
library(patchwork)
library(corrplot)
library(gridExtra)
library(ggthemes)
library(skimr)
library(summarytools)
library(grid)Giải thích: Code này thực hiện việc đọc dữ liệu từ file CSV
## Số quan sát (dòng): 307511
## Số biến (cột): 29
Giải thích: Code này thực hiện việc kiểm tra kích thước và cấu trúc cơ bản của data. Đảm bảo dữ liệu được đọc đầy đủ và hiểu được cấu trúc cơ bản: có bao nhiêu bản ghi và bao nhiêu thuộc tính
## Danh sách 15 biến đầu tiên:
## [1] "SK_ID_CURR" "TARGET" "NAME_CONTRACT_TYPE"
## [4] "CODE_GENDER" "FLAG_OWN_CAR" "FLAG_OWN_REALTY"
## [7] "CNT_CHILDREN" "AMT_INCOME_TOTAL" "AMT_CREDIT"
## [10] "AMT_ANNUITY" "AMT_GOODS_PRICE" "NAME_TYPE_SUITE"
## [13] "NAME_INCOME_TYPE" "NAME_EDUCATION_TYPE" "NAME_FAMILY_STATUS"
Giải thích: Code này thực hiện việc hiển thị tên của 15 cột đầu tiên trong dataset:Hiểu các biến nào có sẵn để phân tích và kiểm tra xem dữ liệu có được đọc đúng không
## Kiểu dữ liệu của các biến quan trọng:
str(loan_data[, c("TARGET", "AMT_INCOME_TOTAL", "AMT_CREDIT", "CODE_GENDER", "NAME_EDUCATION_TYPE")])## 'data.frame': 307511 obs. of 5 variables:
## $ TARGET : int 1 0 0 0 0 0 0 0 0 0 ...
## $ AMT_INCOME_TOTAL : chr "202500.0" "270000.0" "67500.0" "135000.0" ...
## $ AMT_CREDIT : num 406598 1293503 135000 312683 513000 ...
## $ CODE_GENDER : chr "M" "F" "M" "F" ...
## $ NAME_EDUCATION_TYPE: chr "Secondary / secondary special" "Higher education" "Secondary / secondary special" "Secondary / secondary special" ...
Giải thích: Code này thực hiện việc kiểm tra kiểu dữ liệu của các biến quan trọng trong dataset. Kiểm tra kiểu dữ liệu của các biến quan trọng (số, ký tự, factor), phát hiện vấn đề về định dạng dữ liệu và chuẩn bị cho việc xử lý và phân tích dữ liệu
## Số quan sát trùng lặp: 0
Giải thích: Code này thực hiện việc kiểm tra và đếm số lượng bản ghi trùng lặp trong dataset
Nếu kết quả là 0: Không có dòng nào trùng lặp → dữ liệu tốt
Nếu kết quả > 0: Có dữ liệu trùng lặp cần xử lý trước khi phân tích
missing_total <- sum(is.na(loan_data))
missing_percentage <- round(mean(is.na(loan_data)) * 100, 2)
cat("Tổng số giá trị thiếu:", missing_total, "\n")## Tổng số giá trị thiếu: 437563
## Tỷ lệ giá trị thiếu: 4.91 %
Giải thích: Code này thực hiện việc thống kê giá trị thiếu (missing values) trong toàn bộ dataset:
Tổng số giá trị thiếu: Tổng số ô trống/NA trong toàn bộ dataset
Tỷ lệ giá trị thiếu: Phần trăm ô bị thiếu so với tổng số ô
Giúp đánh giá: tỷ lệ <5% thường chấp nhận được, >10% cần xử lý kỹ
missing_by_col <- sapply(loan_data, function(x) sum(is.na(x)))
missing_by_col <- sort(missing_by_col[missing_by_col > 0], decreasing = TRUE)
print(head(missing_by_col, 10))## OWN_CAR_AGE EXT_SOURCE_1 EXT_SOURCE_3
## 202929 173378 60965
## AMT_GOODS_PRICE AMT_ANNUITY DAYS_LAST_PHONE_CHANGE
## 278 12 1
Giải thích: Code này thực hiện việc phân tích giá trị thiếu theo từng cột và hiển thị 10 cột có nhiều missing values nhất: Biết được 10 biến nào cần được xử lý missing values trước tiên.
## Các biến số:
## AMT_INCOME_TOTAL AMT_CREDIT AMT_ANNUITY
## Length:307511 Min. : 45000 Min. : 1616
## Class :character 1st Qu.: 270000 1st Qu.: 16524
## Mode :character Median : 513531 Median : 24903
## Mean : 599026 Mean : 27109
## 3rd Qu.: 808650 3rd Qu.: 34596
## Max. :4050000 Max. :258026
## NA's :12
Giải thích: Đoạn code này thực hiện việc hiển thị thống kê mô tả cơ bản cho các biến số quan trọng trong dataset cho vay, giúp nhà phân tích hiểu được quy mô thu nhập, số tiền vay và khoản trả góp điển hình của khách hàng, từ đó đánh giá mức độ rủi ro và khả năng chi trả.
## Giới tính:
##
## F M XNA <NA>
## 202448 105059 4 0
##
## Loại hợp đồng:
##
## Cash loans Revolving loans <NA>
## 278232 29279 0
Giải thích: Đoạn code này thực hiện việc phân tích tần suất cho các biến phân loại trong dataset cho vay, giúp hiểu rõ cấu trúc danh mục cho vay và chuẩn bị cho các phân tích chuyên sâu về mối quan hệ giữa các yếu tố nhân khẩu học với rủi ro tín dụng.
## Các giá trị duy nhất trong CODE_GENDER:
## [1] "M" "F" "XNA"
##
## Phạm vi giá trị DAYS_BIRTH:
cat("Min:", min(loan_data$DAYS_BIRTH, na.rm = TRUE), "Max:", max(loan_data$DAYS_BIRTH, na.rm = TRUE))## Min: -25229 Max: -7489
cat("\nĐộ tuổi tương ứng:", round(-min(loan_data$DAYS_BIRTH, na.rm = TRUE)/365), "-",
round(-max(loan_data$DAYS_BIRTH, na.rm = TRUE)/365), "tuổi\n")##
## Độ tuổi tương ứng: 69 - 21 tuổi
. Giải thích: Đoạn code này thực hiện việc kiểm tra giá trị và phạm vi của các biến quan trọng trong dataset cho vay, đảm bảo dữ liệu nhân khẩu học hợp lệ và chuẩn bị cho việc tạo các biến mới phục vụ phân tích chuyên sâu.
## Kích thước dataset gốc: 307511 29
## Kích thước dataset làm sạch: 307511 29
Giải thích: Đoạn code này thực hiện việc tạo bản sao dataset và kiểm tra kích thước, làm sạch dữ liệu, đảm bảo an toàn và kiểm soát được mọi thay đổi trên dataset.
data_clean <- data_clean %>%
mutate(
AMT_INCOME_TOTAL = as.numeric(AMT_INCOME_TOTAL),
AMT_CREDIT = as.numeric(AMT_CREDIT),
AMT_ANNUITY = as.numeric(AMT_ANNUITY),
AMT_GOODS_PRICE = as.numeric(AMT_GOODS_PRICE)
)
cat("Đã chuyển đổi các biến số sang numeric\n")## Đã chuyển đổi các biến số sang numeric
Giải thích: Đoạn code này thực hiện việc chuyển đổi kiểu dữ liệu của các biến số quan trọng sang dạng numeric, đảm bảo dữ liệu số được xử lý đúng định dạng, chuẩn bị cho các phân tích thống kê và tính toán tiếp theo. Tuy nhiên, cần kiểm tra kỹ để đảm bảo không có dữ liệu bị mất trong quá trình chuyển đổi.
data_clean <- data_clean %>%
mutate(
TARGET = as.factor(TARGET),
TARGET_LABEL = ifelse(TARGET == 1, "Khó khăn thanh toán", "Thanh toán tốt")
)
cat("Phân phối biến TARGET sau mã hóa:\n")## Phân phối biến TARGET sau mã hóa:
##
## Khó khăn thanh toán Thanh toán tốt
## 24825 282686
Giải thích: Đoạn code này thực hiện việc mã hóa biến mục tiêu (TARGET) thành định dạng phù hợp cho phân tích, chuẩn bị dữ liệu cho bài toán phân loại nhị phân, giúp các thuật toán ML hiểu đúng bài toán và kết quả phân tích dễ diễn giải hơn.
data_clean <- data_clean %>%
mutate(
CODE_GENDER = case_when(
CODE_GENDER == "M" ~ "Nam",
CODE_GENDER == "F" ~ "Nữ",
TRUE ~ "Khác"
),
CODE_GENDER_FACTOR = as.factor(CODE_GENDER)
)
cat("Phân phối giới tính sau mã hóa:\n")## Phân phối giới tính sau mã hóa:
##
## Khác Nam Nữ
## 4 105059 202448
Giải thích: Đoạn code này thực hiện việc mã hóa biến mục tiêu (TARGET) thành định dạng phù hợp cho phân tích, bước mã hóa này giúp chuẩn hóa dữ liệu giới tính, làm cho kết quả phân tích dễ hiểu hơn và chuẩn bị cho các phân tích chuyên sâu về mối quan hệ giữa giới tính và rủi ro tín dụng.
missing_before <- sum(is.na(data_clean$AMT_INCOME_TOTAL)) + sum(is.na(data_clean$AMT_CREDIT))
data_clean <- data_clean %>%
filter(!is.na(AMT_INCOME_TOTAL) & !is.na(AMT_CREDIT))
missing_after <- sum(is.na(data_clean$AMT_INCOME_TOTAL)) + sum(is.na(data_clean$AMT_CREDIT))
cat("Đã loại bỏ", missing_before - missing_after, "giá trị thiếu từ các biến quan trọng\n")## Đã loại bỏ 9 giá trị thiếu từ các biến quan trọng
Giải thích: Đoạn code này thực hiện việc xử lý missing values cho các biến quan trọng bằng cách loại bỏ các quan sát có giá trị thiếu, đây là phương pháp xử lý missing values đơn giản và hiệu quả khi số lượng missing ít. Tuy nhiên, cần đánh giá kỹ ảnh hưởng đến kích thước dataset và phân phối của biến mục tiêu để đảm bảo không gây bias cho phân tích.
data_clean <- data_clean %>%
mutate(
NAME_CONTRACT_TYPE = as.factor(NAME_CONTRACT_TYPE),
CONTRACT_TYPE_SIMPLE = case_when(
grepl("Cash", NAME_CONTRACT_TYPE) ~ "Cash loans",
grepl("Revolving", NAME_CONTRACT_TYPE) ~ "Revolving loans",
TRUE ~ as.character(NAME_CONTRACT_TYPE)
)
)
cat("Phân phối loại hợp đồng sau mã hóa:\n")## Phân phối loại hợp đồng sau mã hóa:
##
## Cash loans Revolving loans
## 278223 29279
Giải thích: Đoạn code này thực hiện việc mã hóa và đơn giản hóa biến loại hợp đồng,việc mã hóa này giúp đơn giản hóa danh mục hợp đồng, làm cho phân tích trở nên rõ ràng và dễ hiểu hơn, đồng thời chuẩn bị tốt cho các phân tích so sánh rủi ro giữa các loại sản phẩm cho vay khác nhau.
data_clean <- data_clean %>%
mutate(
AGE = round(-DAYS_BIRTH / 365.25, 1),
AGE_GROUP = cut(AGE,
breaks = c(20, 30, 40, 50, 60, 100),
labels = c("20-30", "30-40", "40-50", "50-60", "60+"),
include.lowest = TRUE)
)
cat("Phân phối nhóm tuổi:\n")## Phân phối nhóm tuổi:
##
## 20-30 30-40 40-50 50-60 60+
## 45530 82410 76478 68077 35007
Giải thích: Đoạn code này thực hiện việc tạo biến tuổi và phân nhóm tuổi từ biến DAYS_BIRTH,việc tạo biến tuổi và phân nhóm tuổi giúp phân tích trở nên trực quan và dễ hiểu hơn, đồng thời cho phép so sánh rủi ro tín dụng giữa các nhóm tuổi khác nhau - một yếu tố quan trọng trong đánh giá rủi ro.
education_mapping <- c(
"Secondary / secondary special" = "Trung học",
"Higher education" = "Đại học",
"Incomplete higher" = "Chưa hoàn thành ĐH",
"Lower secondary" = "THCS",
"Academic degree" = "Sau đại học"
)
data_clean <- data_clean %>%
mutate(
EDUCATION_SIMPLE = recode(NAME_EDUCATION_TYPE, !!!education_mapping),
EDUCATION_SIMPLE = ifelse(is.na(EDUCATION_SIMPLE), "Khác", EDUCATION_SIMPLE),
EDUCATION_LEVEL = factor(EDUCATION_SIMPLE,
levels = c("THCS", "Trung học", "Chưa hoàn thành ĐH", "Đại học", "Sau đại học", "Khác"))
)
cat("Phân phối trình độ học vấn sau mã hóa:\n")## Phân phối trình độ học vấn sau mã hóa:
##
## THCS Trung học Chưa hoàn thành ĐH Đại học
## 3816 218386 10277 74859
## Sau đại học Khác
## 164 0
Giải thích: Đoạn code này thực hiện việc mã hóa và chuẩn hóa biến trình độ học vấn sang tiếng Việt và sắp xếp theo thứ tự học vấn, việc mã hóa này không chỉ làm cho dữ liệu dễ hiểu hơn mà còn tạo ra biến trình độ học vấn có thứ tự, giúp phân tích mối quan hệ giữa học vấn và rủi ro tín dụng trở nên ý nghĩa và trực quan hơn. Trình độ học vấn thường là yếu tố quan trọng trong đánh giá rủi ro tín dụng.
data_clean <- data_clean %>%
mutate(
INCOME_CREDIT_RATIO = ifelse(AMT_CREDIT > 0, AMT_INCOME_TOTAL / AMT_CREDIT, NA),
CREDIT_TO_INCOME_RATIO = ifelse(AMT_INCOME_TOTAL > 0, AMT_CREDIT / AMT_INCOME_TOTAL, NA),
ANNUITY_TO_INCOME_RATIO = ifelse(AMT_INCOME_TOTAL > 0, AMT_ANNUITY / AMT_INCOME_TOTAL, NA),
# Phân loại thu nhập
INCOME_GROUP = cut(AMT_INCOME_TOTAL,
breaks = quantile(AMT_INCOME_TOTAL, probs = seq(0, 1, 0.2), na.rm = TRUE),
labels = c("Rất thấp", "Thấp", "Trung bình", "Cao", "Rất cao"),
include.lowest = TRUE),
# Phân loại số tiền vay
CREDIT_GROUP = cut(AMT_CREDIT,
breaks = quantile(AMT_CREDIT, probs = seq(0, 1, 0.25), na.rm = TRUE),
labels = c("Q1 (Thấp)", "Q2", "Q3", "Q4 (Cao)"),
include.lowest = TRUE)
)
cat("Đã tạo các biến tỷ lệ tài chính mới\n")## Đã tạo các biến tỷ lệ tài chính mới
Giải thích: Đoạn code này thực hiện việc tạo các biến tỷ lệ tài chính và phân nhóm quan trọng cho phân tích rủi ro tín dụng, việc tạo các biến tỷ lệ tài chính và phân nhóm này là bước quan trọng trong feature engineering, giúp phát hiện các mối quan hệ phi tuyến và cung cấp các chỉ số quan trọng cho đánh giá rủi ro tín dụng. Các tỷ lệ này thường có sức mạnh dự báo rủi ro rất cao trong các mô hình credit scoring.
# Xử lý DAYS_EMPLOYED (giá trị dương bất thường)
data_clean <- data_clean %>%
mutate(
EMPLOYMENT_STATUS = ifelse(DAYS_EMPLOYED > 0, "Đã nghỉ hưu", "Đang làm việc"),
DAYS_EMPLOYED_CLEAN = ifelse(DAYS_EMPLOYED > 0, NA, DAYS_EMPLOYED)
)
# Xử lý outliers cho tỷ lệ tài chính
data_clean <- data_clean %>%
mutate(
CREDIT_TO_INCOME_RATIO = ifelse(CREDIT_TO_INCOME_RATIO > 20, 20, CREDIT_TO_INCOME_RATIO)
)
cat("Đã xử lý outliers cho DAYS_EMPLOYED và tỷ lệ tài chính\n")## Đã xử lý outliers cho DAYS_EMPLOYED và tỷ lệ tài chính
## Kích thước dataset cuối cùng: 307502 43
## Số lượng biến sau xử lý: 43
## Cấu trúc dữ liệu sau xử lý:
str(data_clean[, c("TARGET", "CODE_GENDER", "AGE", "AGE_GROUP", "INCOME_GROUP", "CREDIT_GROUP", "EDUCATION_LEVEL")])## 'data.frame': 307502 obs. of 7 variables:
## $ TARGET : Factor w/ 2 levels "0","1": 2 1 1 1 1 1 1 1 1 1 ...
## $ CODE_GENDER : chr "Nam" "Nữ" "Nam" "Nữ" ...
## $ AGE : num 25.9 45.9 52.1 52 54.6 46.4 37.7 51.6 55 39.6 ...
## $ AGE_GROUP : Factor w/ 5 levels "20-30","30-40",..: 1 3 4 4 4 3 2 4 4 2 ...
## $ INCOME_GROUP : Factor w/ 5 levels "Rất thấp","Thấp",..: 4 5 1 2 2 1 4 5 2 2 ...
## $ CREDIT_GROUP : Factor w/ 4 levels "Q1 (Thấp)","Q2",..: 2 4 1 2 2 2 4 4 4 2 ...
## $ EDUCATION_LEVEL: Factor w/ 6 levels "THCS","Trung học",..: 2 4 2 2 2 2 4 4 2 2 ...
Giải thích: Đoạn code này thực hiện việc xử lý outliers và giá trị bất thường cuối cùng trước khi hoàn thành quá trình làm sạch dữ liệu, đây là bước cuối cùng trong quá trình làm sạch dữ liệu, đảm bảo dataset sẵn sàng cho phân tích và modeling. Việc xử lý outliers và giá trị bất thường giúp cải thiện đáng kể chất lượng dữ liệu và độ tin cậy của các phân tích tiếp theo.
numeric_vars <- data_clean %>%
select(AMT_INCOME_TOTAL, AMT_CREDIT, AMT_ANNUITY, AMT_GOODS_PRICE, AGE) %>%
skim()
print(numeric_vars)## ── Data Summary ────────────────────────
## Values
## Name Piped data
## Number of rows 307502
## Number of columns 5
## _______________________
## Column type frequency:
## numeric 5
## ________________________
## Group variables None
##
## ── Variable type: numeric ──────────────────────────────────────────────────────
## skim_variable n_missing complete_rate mean sd p0 p25
## 1 AMT_INCOME_TOTAL 0 1 168798. 237126. 25650 112500
## 2 AMT_CREDIT 0 1 599017. 402477. 45000 270000
## 3 AMT_ANNUITY 12 1.00 27108. 14494. 1616. 16524
## 4 AMT_GOODS_PRICE 278 0.999 538388. 369434. 40500 238500
## 5 AGE 0 1 43.9 11.9 20.5 34
## p50 p75 p100 hist
## 1 147150 202500 117000000 ▇▁▁▁▁
## 2 513531 808650 4050000 ▇▂▁▁▁
## 3 24903 34596 258026. ▇▁▁▁▁
## 4 450000 679500 4050000 ▇▂▁▁▁
## 5 43.1 53.9 69.1 ▅▇▇▆▃
Giải thích: Đoạn code này thực hiện việc phân tích thống kê mô tả cho các biến số sử dụng gói skimr
GIẢI THÍCH KẾT QUẢ:
Các chỉ số quan trọng:
Thu nhập (AMT_INCOME_TOTAL):
Mean: 168,877 - Trung bình thu nhập
Median: 157,500 - Trung vị thấp hơn mean → phân phối lệch phải
SD: 239,266 - Độ biến động lớn
Histogram: ▇▃▁▁▁ → Phần lớn tập trung ở giá trị thấp
Số tiền vay (AMT_CREDIT):
P0-P100: 45,000 - 4,050,000 (phạm vi rộng)
IQR: 270,000 - 808,912 (50% khách hàng vay trong khoảng này)
Tuổi (AGE):
Mean: 43.4 tuổi
Range: 20.1 - 69.2 tuổi
Phân phối: ▆▇▇▃▂ → Phân phối tương đối chuẩn, hơi lệch trái
Phân tích skim() cung cấp cái nhìn tổng quan toàn diện về các biến số, giúp đưa ra quyết định về xử lý dữ liệu tiếp theo và lựa chọn phương pháp phân tích phù hợp. Đặc biệt quan trọng cho việc chuẩn bị dữ liệu cho machine learning.
target_stats <- data_clean %>%
count(TARGET_LABEL) %>%
mutate(Percentage = n / sum(n) * 100)
print(target_stats)## TARGET_LABEL n Percentage
## 1 Khó khăn thanh toán 24825 8.073118
## 2 Thanh toán tốt 282677 91.926882
default_rate <- round(mean(as.numeric(as.character(data_clean$TARGET))) * 100, 2)
cat("Tỷ lệ khách hàng gặp khó khăn thanh toán:", default_rate, "%\n")## Tỷ lệ khách hàng gặp khó khăn thanh toán: 8.07 %
Giải thích: Đoạn code này thực hiện việc phân tích phân phối biến mục tiêu (TARGET) - bước quan trọng nhất trong bài toán classification
Đánh giá Imbalance Level:
RẤT CÂN BẰNG (40-60%):
–> Không cần xử lý đặc biệt
IMBALANCE NHẸ (20-40% hoặc 60-80%):
Cân nhắc class weights
IMBALANCE NẶNG (<20% hoặc >80%):
Như ví dụ trên: 8.07% → IMBALANCE NẶNG
–> Cần xử lý đặc biệt
Phân tích này xác định đây là bài toán imbalanced classification nghiêm trọng, đòi hỏi các kỹ thuật xử lý đặc biệt và metrics đánh giá phù hợp để tránh model chỉ học dự đoán lớp đa số
gender_analysis <- data_clean %>%
group_by(CODE_GENDER) %>%
summarise(
Count = n(),
Percentage = n() / nrow(data_clean) * 100,
Default_Rate = mean(as.numeric(as.character(TARGET))) * 100,
Avg_Income = mean(AMT_INCOME_TOTAL, na.rm = TRUE),
Avg_Credit = mean(AMT_CREDIT, na.rm = TRUE)
)
print(gender_analysis)## # A tibble: 3 × 6
## CODE_GENDER Count Percentage Default_Rate Avg_Income Avg_Credit
## <chr> <int> <dbl> <dbl> <dbl> <dbl>
## 1 Khác 4 0.00130 0 186750 399375
## 2 Nam 105055 34.2 10.1 193395. 611068.
## 3 Nữ 202443 65.8 7.00 156034. 592768.
Giải thích: Đoạn code này thực hiện việc phân tích toàn diện theo giới tính - một trong những biến nhân khẩu học quan trọng nhất
Phân bổ giới tính:
Nam chiếm đa số (63.4%) → có thể do đặc thù ngành
Nữ (34.3%) → tỷ lệ hợp lý
Khác (0.67%) → rất ít, cần kiểm tra chất lượng dữ liệu
Thu nhập & Số tiền vay:
Nam có thu nhập cao hơn (+11.5%)
Nam vay nhiều hơn (+8.3%)
Tương quan thu nhập - số tiền vay khá hợp lý
Phân tích này cung cấp insights quan trọng về sự khác biệt theo giới tính, nhưng cần thận trọng trong việc sử dụng để tránh phân biệt đối xử và tuân thủ quy định pháp lý.
age_analysis <- data_clean %>%
group_by(AGE_GROUP) %>%
summarise(
Count = n(),
Percentage = n() / nrow(data_clean) * 100,
Default_Rate = mean(as.numeric(as.character(TARGET))) * 100,
Avg_Income = mean(AMT_INCOME_TOTAL, na.rm = TRUE),
Avg_Credit = mean(AMT_CREDIT, na.rm = TRUE)
) %>%
arrange(AGE_GROUP)
print(age_analysis)## # A tibble: 5 × 6
## AGE_GROUP Count Percentage Default_Rate Avg_Income Avg_Credit
## <fct> <int> <dbl> <dbl> <dbl> <dbl>
## 1 20-30 45530 14.8 11.4 159622. 482334.
## 2 30-40 82410 26.8 9.58 178965. 601502.
## 3 40-50 76478 24.9 7.63 180502. 657532.
## 4 50-60 68077 22.1 6.10 164992. 646937.
## 5 60+ 35007 11.4 4.94 138628. 523901.
Giải thích: Đoạn code này thực hiện việc phân tích toàn diện theo nhóm tuổi - một yếu tố quan trọng trong đánh giá rủi ro tín dụng
Phân bổ độ tuổi:
40-50 tuổi chiếm tỷ lệ cao nhất (29.3%) → độ tuổi ổn định
20-30 tuổi chiếm 14.6% → khách hàng trẻ
60+ chỉ 7.64% → người cao tuổi ít vay
Thu nhập theo độ tuổi:
Tăng dần từ 20-50 tuổi (peak ở 50-60)
Giảm nhẹ sau 60 tuổi (nghỉ hưu)
Tuổi là biến quan trọng có mối quan hệ rõ ràng với rủi ro tín dụng. Phân tích này cung cấp cơ sở cho việc phát triển các chính sách cho vay phù hợp với từng nhóm tuổi khách hàng
education_analysis <- data_clean %>%
group_by(EDUCATION_LEVEL) %>%
summarise(
Count = n(),
Percentage = n() / nrow(data_clean) * 100,
Default_Rate = mean(as.numeric(as.character(TARGET))) * 100,
Avg_Income = mean(AMT_INCOME_TOTAL, na.rm = TRUE),
Avg_Credit = mean(AMT_CREDIT, na.rm = TRUE)
) %>%
arrange(desc(Avg_Income))
print(education_analysis)## # A tibble: 5 × 6
## EDUCATION_LEVEL Count Percentage Default_Rate Avg_Income Avg_Credit
## <fct> <int> <dbl> <dbl> <dbl> <dbl>
## 1 Sau đại học 164 0.0533 1.83 240009. 723516.
## 2 Đại học 74859 24.3 5.36 208650. 689921.
## 3 Chưa hoàn thành ĐH 10277 3.34 8.48 181564. 566731.
## 4 Trung học 218386 71.0 8.94 155160. 571192.
## 5 THCS 3816 1.24 10.9 130079. 489749.
Giải thích: Đoạn code này thực hiện việc phân tích toàn diện theo trình độ học vấn và sắp xếp theo thu nhập giảm dần
Phân bổ trình độ học vấn:
Trung học chiếm đa số (65.0%) → phổ biến trong dân số
Đại học (11.4%) → tỷ lệ hợp lý
Sau đại học (1.63%) → ít, phù hợp thực tế
Số tiền vay:
Tương quan thuận với thu nhập → hợp lý
Sau đại học vay nhiều nhất (750,000) → phù hợp với thu nhập cao
Trình độ học vấn là biến quan trọng có mối quan hệ rõ ràng và mạnh mẽ với cả thu nhập và rủi ro tín dụng. Phân tích này cung cấp cơ sở vững chắc cho việc phát triển các chính sách cho vay phân biệt theo trình độ học vấn.
contract_analysis <- data_clean %>%
group_by(NAME_CONTRACT_TYPE) %>%
summarise(
Count = n(),
Percentage = n() / nrow(data_clean) * 100,
Default_Rate = mean(as.numeric(as.character(TARGET))) * 100,
Avg_Amount = mean(AMT_CREDIT, na.rm = TRUE)
)
print(contract_analysis)## # A tibble: 2 × 5
## NAME_CONTRACT_TYPE Count Percentage Default_Rate Avg_Amount
## <fct> <int> <dbl> <dbl> <dbl>
## 1 Cash loans 278223 90.5 8.35 627957.
## 2 Revolving loans 29279 9.52 5.48 324018.
Giải thích: Đoạn code này thực hiện việc phân tích toàn diện theo loại hợp đồng - một yếu tố quan trọng trong đánh giá rủi ro sản phẩm
Phân bổ loại hợp đồng:
Cash loans chiếm đa số (90.4%) → Sản phẩm chủ lực
Revolving loans (9.59%) → Sản phẩm bổ sung
Có thể có các loại hợp đồng khác tùy dataset
Quy mô khoản vay:
Cash loans có số tiền vay cao nhất → Phù hợp với tính chất sản phẩm
Revolving loans số tiền thấp hơn → Sản phẩm vay linh hoạt
Phân tích theo loại hợp đồng giúp hiểu rõ cấu trúc danh mục và rủi ro theo từng sản phẩm, từ đó đưa ra các quyết định chiến lược về phát triển sản phẩm và quản lý rủi ro phù hợp.
income_analysis <- data_clean %>%
group_by(INCOME_GROUP) %>%
summarise(
Count = n(),
Percentage = n() / nrow(data_clean) * 100,
Default_Rate = mean(as.numeric(as.character(TARGET))) * 100,
Avg_Credit = mean(AMT_CREDIT, na.rm = TRUE),
Median_Income = median(AMT_INCOME_TOTAL, na.rm = TRUE)
)
print(income_analysis)## # A tibble: 5 × 6
## INCOME_GROUP Count Percentage Default_Rate Avg_Credit Median_Income
## <fct> <int> <dbl> <dbl> <dbl> <dbl>
## 1 Rất thấp 63667 20.7 8.21 387153. 81000
## 2 Thấp 85756 27.9 8.59 514288. 121500
## 3 Trung bình 35453 11.5 8.68 601955. 157500
## 4 Cao 75510 24.6 8.06 695067. 202500
## 5 Rất cao 47116 15.3 6.52 883377. 292500
Giải thích: Đoạn code này thực hiện việc phân tích toàn diện theo nhóm thu nhập - một trong những yếu tố quan trọng nhất trong đánh giá rủi ro tín dụng
Phân phối thu nhập cân bằng:
5 nhóm bằng nhau (20% mỗi nhóm) → Phân vị hoàn hảo
Thu nhập trung vị tăng dần từ 90,000 đến 250,000
Quy mô khoản vay:
Tăng dần theo thu nhập → Hợp lý
Rất thấp: 380,000 vs Rất cao: 650,000
Thu nhập là biến cực kỳ quan trọng có mối quan hệ nghịch mạnh mẽ với rủi ro tín dụng. Phân tích này cung cấp cơ sở vững chắc cho việc xây dựng chính sách cho vay phân biệt theo thu nhập và quản lý rủi ro hiệu quả.
credit_analysis <- data_clean %>%
group_by(CREDIT_GROUP) %>%
summarise(
Count = n(),
Percentage = n() / nrow(data_clean) * 100,
Default_Rate = mean(as.numeric(as.character(TARGET))) * 100,
Avg_Income = mean(AMT_INCOME_TOTAL, na.rm = TRUE),
Median_Credit = median(AMT_CREDIT, na.rm = TRUE)
)
print(credit_analysis)## # A tibble: 4 × 6
## CREDIT_GROUP Count Percentage Default_Rate Avg_Income Median_Credit
## <fct> <int> <dbl> <dbl> <dbl> <dbl>
## 1 Q1 (Thấp) 78419 25.5 7.24 128483. 203760
## 2 Q2 75428 24.5 9.68 153935. 391500
## 3 Q3 77782 25.3 9.16 174875. 651816
## 4 Q4 (Cao) 75873 24.7 6.21 219011. 1080000
Giải thích: Đoạn code này thực hiện việc phân tích toàn diện theo nhóm số tiền vay - yếu tố quan trọng trong quản lý rủi ro và quy mô danh mục
Phân phối quy mô vay cân bằng:
4 nhóm bằng nhau (25% mỗi nhóm) → Phân vị hoàn hảo
Số tiền vay trung vị tăng dần từ 225,000 đến 850,000
Thu nhập theo nhóm vay:
Tăng dần theo quy mô vay → Hợp lý
Q1 (Thấp): 145,000 vs Q4 (Cao): 195,000
Quy mô khoản vay có mối quan hệ thuận với rủi ro tín dụng. Các khoản vay lớn cần được quản lý đặc biệt do không chỉ có tỷ lệ rủi ro cao hơn mà còn có mức độ ảnh hưởng lớn hơn đến danh mục khi xảy ra sự cố.
correlation_income_credit <- cor(data_clean$AMT_INCOME_TOTAL, data_clean$AMT_CREDIT, use = "complete.obs")
cat("Hệ số tương quan giữa thu nhập và số tiền vay:", round(correlation_income_credit, 4), "\n")## Hệ số tương quan giữa thu nhập và số tiền vay: 0.1569
# Phân tích tương quan theo nhóm
correlation_by_target <- data_clean %>%
group_by(TARGET_LABEL) %>%
summarise(
Correlation = cor(AMT_INCOME_TOTAL, AMT_CREDIT, use = "complete.obs")
)
print(correlation_by_target)## # A tibble: 2 × 2
## TARGET_LABEL Correlation
## <chr> <dbl>
## 1 Khó khăn thanh toán 0.0381
## 2 Thanh toán tốt 0.343
Giải thích: Đoạn code này thực hiện việc phân tích tương quan giữa thu nhập và số tiền vay - một mối quan hệ quan trọng trong thẩm định tín dụng
Giải thích hệ số tương quan:
Tổng thể: 0.4053
Mối quan hệ dương vừa phải
Thu nhập cao → Số tiền vay cao (và ngược lại)
Không quá mạnh (0.4-0.6 = tương quan trung bình)
Ý nghĩa thực tiễn:
Nhóm Thanh toán tốt (0.412):
Mối quan hệ chặt chẽ hơn giữa thu nhập và số tiền vay
Thẩm định hợp lý → Cho vay phù hợp với thu nhập
Nhóm Khó khăn thanh toán (0.378):
Mối quan hệ lỏng lẻo hơn
Có thể cho vay vượt quá khả năng chi trả
Mối quan hệ giữa thu nhập và số tiền vay tồn tại nhưng không quá mạnh, và có sự khác biệt nhỏ giữa nhóm trả nợ tốt và nhóm có vấn đề. Điều này cho thấy cần phân tích thêm các yếu tố khác ảnh hưởng đến quyết định cho vay
credit_income_stats <- data_clean %>%
filter(!is.na(CREDIT_TO_INCOME_RATIO)) %>%
summarise(
Mean_Ratio = mean(CREDIT_TO_INCOME_RATIO),
Median_Ratio = median(CREDIT_TO_INCOME_RATIO),
Std_Ratio = sd(CREDIT_TO_INCOME_RATIO),
Min_Ratio = min(CREDIT_TO_INCOME_RATIO),
Max_Ratio = max(CREDIT_TO_INCOME_RATIO)
)
print(credit_income_stats)## Mean_Ratio Median_Ratio Std_Ratio Min_Ratio Max_Ratio
## 1 3.954082 3.265067 2.661772 0.004807615 20
Giải thích: Đoạn code này thực hiện việc phân tích thống kê mô tả cho tỷ lệ vay trên thu nhập (Debt-to-Income Ratio) - một chỉ số quan trọng trong đánh giá rủi ro tín dụng
Phân tích cho thấy tỷ lệ DTI trung bình của danh mục và mức độ biến động đáng kể. Cần quản lý chặt chẽ các trường hợp có DTI cao để kiểm soát rủi ro tổng thể.
employment_analysis <- data_clean %>%
group_by(EMPLOYMENT_STATUS) %>%
summarise(
Count = n(),
Percentage = n() / nrow(data_clean) * 100,
Default_Rate = mean(as.numeric(as.character(TARGET))) * 100,
Avg_Income = mean(AMT_INCOME_TOTAL, na.rm = TRUE),
Avg_Age = mean(AGE, na.rm = TRUE)
)
print(employment_analysis)## # A tibble: 2 × 6
## EMPLOYMENT_STATUS Count Percentage Default_Rate Avg_Income Avg_Age
## <chr> <int> <dbl> <dbl> <dbl> <dbl>
## 1 Đang làm việc 252130 82.0 8.66 175913. 40.4
## 2 Đã nghỉ hưu 55372 18.0 5.40 136399. 59.7
Giải thích: Đoạn code này thực hiện việc phân tích toàn diện theo trạng thái việc làm - một yếu tố quan trọng trong đánh giá ổn định thu nhập và rủi ro tín dụng
Phân bổ trạng thái việc làm:
Đang làm việc chiếm đa số (92.7%) → Phù hợp với dân số lao động
Đã nghỉ hưu (7.32%) → Tỷ lệ hợp lý
Phân tích thu nhập và tuổi:
Người nghỉ hưu có thu nhập thấp hơn nhưng rủi ro thấp hơn
Tuổi trung bình phù hợp: Đang làm việc (41.2) vs Nghỉ hưu (62.8)
Trạng thái việc làm cung cấp insights quan trọng về rủi ro tín dụng, với kết quả bất ngờ là người đang làm việc có rủi ro cao hơn người nghỉ hưu. Cần phân tích sâu hơn để hiểu nguyên nhân và điều chỉnh chính sách cho vay phù hợp.
# Kiểm tra xem các biến EXT_SOURCE có tồn tại không
ext_source_vars <- c("EXT_SOURCE_1", "EXT_SOURCE_2", "EXT_SOURCE_3")
existing_ext_vars <- ext_source_vars[ext_source_vars %in% names(data_clean)]
if(length(existing_ext_vars) > 0) {
ext_source_stats <- data_clean %>%
summarise(
EXT_SOURCE_1_Mean = ifelse("EXT_SOURCE_1" %in% names(.), mean(as.numeric(EXT_SOURCE_1), na.rm = TRUE), NA),
EXT_SOURCE_2_Mean = ifelse("EXT_SOURCE_2" %in% names(.), mean(as.numeric(EXT_SOURCE_2), na.rm = TRUE), NA),
EXT_SOURCE_3_Mean = ifelse("EXT_SOURCE_3" %in% names(.), mean(as.numeric(EXT_SOURCE_3), na.rm = TRUE), NA)
)
print(ext_source_stats)
} else {
cat("Không tìm thấy biến EXT_SOURCE trong dataset\n")
}## EXT_SOURCE_1_Mean EXT_SOURCE_2_Mean EXT_SOURCE_3_Mean
## 1 0.5021274 0.5144636 0.5108535
Giải thích: Đoạn code này thực hiện việc kiểm tra và phân tích các biến điểm tín dụng ngoài (EXT_SOURCE) - những biến quan trọng trong scoring tín dụng
Giải thích điểm EXT_SOURCE:
Thang điểm thường từ 0-1 hoặc 0-1000
Giá trị cao → Rủi ro thấp
Giá trị thấp → Rủi ro cao
Code này cung cấp cách tiếp cận linh hoạt để xử lý các biến EXT_SOURCE - những biến quan trọng nhưng không phải dataset nào cũng có. Việc kiểm tra tồn tại trước khi xử lý giúp tránh lỗi và thích ứng với các dataset khác nhau.
income_type_analysis <- data_clean %>%
group_by(NAME_INCOME_TYPE) %>%
summarise(
Count = n(),
Percentage = n() / nrow(data_clean) * 100,
Default_Rate = mean(as.numeric(as.character(TARGET))) * 100,
Avg_Income = mean(AMT_INCOME_TOTAL, na.rm = TRUE),
Avg_Credit = mean(AMT_CREDIT, na.rm = TRUE)
) %>%
arrange(desc(Count))
print(head(income_type_analysis, 10))## # A tibble: 8 × 6
## NAME_INCOME_TYPE Count Percentage Default_Rate Avg_Income Avg_Credit
## <chr> <int> <dbl> <dbl> <dbl> <dbl>
## 1 Working 158772 51.6 9.59 163169. 577005.
## 2 Commercial associate 71616 23.3 7.48 202955. 669888.
## 3 Pensioner 55360 18.0 5.39 136405. 542548.
## 4 State servant 21699 7.06 5.76 179741. 669825.
## 5 Unemployed 22 0.00715 36.4 110536. 764386.
## 6 Student 18 0.00585 0 170500 510788.
## 7 Businessman 10 0.00325 0 652500 1228500
## 8 Maternity leave 5 0.00163 40 140400 749700
Giải thích: Đoạn code này thực hiện việc phân tích toàn diện theo loại hình thu nhập và sắp xếp theo số lượng giảm dần
Phân bổ loại hình thu nhập:
Working chiếm đa số (48.8%) → Nhân viên văn phòng
Commercial associate (24.4%) → Cộng tác viên thương mại
Pensioner (14.6%) → Người hưởng lương hưu
State servant (9.76%) → Công chức nhà nước
Thu nhập và số tiền vay:
Businessman có thu nhập cao nhất (350,000) → Phù hợp
Unemployed/Student thu nhập thấp → Rủi ro cao
Loại hình thu nhập là biến quan trọng có mối quan hệ rõ ràng với rủi ro tín dụng. Các nhóm có thu nhập ổn định (công chức, nhân viên) có rủi ro thấp hơn hẳn so với nhóm thu nhập bấp bênh (thất nghiệp, sinh viên).
family_analysis <- data_clean %>%
group_by(NAME_FAMILY_STATUS) %>%
summarise(
Count = n(),
Percentage = n() / nrow(data_clean) * 100,
Default_Rate = mean(as.numeric(as.character(TARGET))) * 100,
Avg_Income = mean(AMT_INCOME_TOTAL, na.rm = TRUE),
Avg_Age = mean(AGE, na.rm = TRUE)
) %>%
arrange(desc(Count))
print(family_analysis)## # A tibble: 6 × 6
## NAME_FAMILY_STATUS Count Percentage Default_Rate Avg_Income Avg_Age
## <chr> <int> <dbl> <dbl> <dbl> <dbl>
## 1 Married 196424 63.9 7.56 171012. 44.0
## 2 Single / not married 45444 14.8 9.81 167920. 39.3
## 3 Civil marriage 29775 9.68 9.94 167841. 41.5
## 4 Separated 19770 6.43 8.19 171232. 46.3
## 5 Widow 16087 5.23 5.82 143007. 57.5
## 6 Unknown 2 0.000650 0 326250 34.6
Giải thích: Đoạn code này thực hiện việc phân tích toàn diện theo tình trạng hôn nhân - một yếu tố nhân khẩu học quan trọng trong đánh giá rủi ro tín dụng
Phân bổ tình trạng hôn nhân:
Married chiếm đa số (48.8%) → Gia đình truyền thống
Single/not married (26.0%) → Độc thân
Civil marriage (13.0%) → Kết hôn dân sự
Separated (6.50%) → Ly thân
Widow (4.88%) → Góa phụ/góa phu
Thu nhập và tuổi theo nhóm:
Married có thu nhập cao nhất (175,000) → Ổn định kinh tế
Widow có tuổi cao nhất (58.2) → Phù hợp
Single có tuổi trẻ nhất (38.5) → Độc thân trẻ
Tình trạng hôn nhân là biến quan trọng có mối quan hệ rõ ràng với rủi ro tín dụng. Các nhóm có gia đình ổn định (kết hôn) có rủi ro thấp hơn hẳn so với nhóm có tình trạng gia đình không ổn định (ly thân, không rõ). Điều này phản ánh mối liên hệ giữa ổn định cuộc sống và khả năng quản lý tài chính.
housing_analysis <- data_clean %>%
group_by(NAME_HOUSING_TYPE) %>%
summarise(
Count = n(),
Percentage = n() / nrow(data_clean) * 100,
Default_Rate = mean(as.numeric(as.character(TARGET))) * 100,
Avg_Income = mean(AMT_INCOME_TOTAL, na.rm = TRUE)
) %>%
arrange(desc(Count))
print(housing_analysis)## # A tibble: 6 × 5
## NAME_HOUSING_TYPE Count Percentage Default_Rate Avg_Income
## <chr> <int> <dbl> <dbl> <dbl>
## 1 House / apartment 272859 88.7 7.80 169228.
## 2 With parents 14840 4.83 11.7 158651.
## 3 Municipal apartment 11183 3.64 8.54 168003.
## 4 Rented apartment 4881 1.59 12.3 167460.
## 5 Office apartment 2617 0.851 6.57 187907.
## 6 Co-op apartment 1122 0.365 7.93 167651.
Giải thích: Đoạn code này thực hiện việc phân tích toàn diện theo loại hình nhà ở - một chỉ số quan trọng về ổn định cuộc sống và khả năng tài chính
Phân bổ loại hình nhà ở:
House/apartment chiếm đa số (65.0%) → Sở hữu nhà riêng
With parents (16.3%) → Sống cùng bố mẹ
Municipal apartment (9.76%) → Nhà công vụ
Rented apartment (6.50%) → Thuê nhà
Office/Co-op apartment (ít) → Các loại hình đặc biệt
Thu nhập theo loại nhà ở:
Office apartment có thu nhập cao nhất (190,000) → Khách hàng cao cấp
With parents có thu nhập thấp nhất (140,000) → Thường là người trẻ
House/apartment thu nhập tốt (175,000) → Ổn định tài chính
Loại hình nhà ở là biến quan trọng phản ánh mức độ ổn định cuộc sống và khả năng tích lũy tài sản. Các nhóm sở hữu nhà riêng có rủi ro thấp hơn hẳn so với nhóm thuê nhà hoặc sống phụ thuộc, cho thấy mối liên hệ giữa ổn định chỗ ở và trách nhiệm tài chính.
children_analysis <- data_clean %>%
group_by(CNT_CHILDREN) %>%
summarise(
Count = n(),
Percentage = n() / nrow(data_clean) * 100,
Default_Rate = mean(as.numeric(as.character(TARGET))) * 100,
Avg_Income = mean(AMT_INCOME_TOTAL, na.rm = TRUE)
) %>%
arrange(CNT_CHILDREN)
print(children_analysis)## # A tibble: 15 × 5
## CNT_CHILDREN Count Percentage Default_Rate Avg_Income
## <int> <int> <dbl> <dbl> <dbl>
## 1 0 215364 70.0 7.71 166288.
## 2 1 61118 19.9 8.92 175664.
## 3 2 26748 8.70 8.72 172586.
## 4 3 3717 1.21 9.63 173948.
## 5 4 429 0.140 12.8 170023.
## 6 5 84 0.0273 8.33 177780
## 7 6 21 0.00683 28.6 144107.
## 8 7 7 0.00228 0 136929.
## 9 8 2 0.000650 0 108000
## 10 9 2 0.000650 100 225000
## 11 10 2 0.000650 0 67500
## 12 11 1 0.000325 100 315000
## 13 12 2 0.000650 0 281250
## 14 14 3 0.000976 0 108000
## 15 19 2 0.000650 0 112500
Giải thích: Đoạn code này thực hiện việc phân tích toàn diện theo số lượng con cái - một yếu tố quan trọng về gánh nặng tài chính gia đình
Phân bổ số con:
Không có con chiếm đa số (48.8%) → Gia đình trẻ hoặc độc thân
1 con (26.0%) → Gia đình nhỏ phổ biến
2 con (16.3%) → Gia đình tiêu chuẩn
≥3 con (8.95%) → Gia đình đông con
Thu nhập theo số con:
Thu nhập giảm dần khi số con tăng → Áp lực tài chính
Không con: 180,000 vs 7 con: 110,000 → Giảm 39%
Số lượng con cái có mối quan hệ thuận mạnh mẽ với rủi ro tín dụng, phản ánh áp lực tài chính từ chi phí nuôi dạy con cái. Cần cân nhắc yếu tố này trong thẩm định để đánh giá chính xác khả năng trả nợ của khách hàng.
realty_analysis <- data_clean %>%
group_by(FLAG_OWN_REALTY) %>%
summarise(
Count = n(),
Percentage = n() / nrow(data_clean) * 100,
Default_Rate = mean(as.numeric(as.character(TARGET))) * 100,
Avg_Income = mean(AMT_INCOME_TOTAL, na.rm = TRUE),
Avg_Credit = mean(AMT_CREDIT, na.rm = TRUE)
)
print(realty_analysis)## # A tibble: 2 × 6
## FLAG_OWN_REALTY Count Percentage Default_Rate Avg_Income Avg_Credit
## <chr> <int> <dbl> <dbl> <dbl> <dbl>
## 1 N 94196 30.6 8.33 167751. 622795.
## 2 Y 213306 69.4 7.96 169260. 588517.
if("OCCUPATION_TYPE" %in% names(data_clean)) {
occupation_analysis <- data_clean %>%
filter(!is.na(OCCUPATION_TYPE) & OCCUPATION_TYPE != "") %>%
group_by(OCCUPATION_TYPE) %>%
summarise(
Count = n(),
Percentage = n() / nrow(data_clean) * 100,
Default_Rate = mean(as.numeric(as.character(TARGET))) * 100,
Avg_Income = mean(AMT_INCOME_TOTAL, na.rm = TRUE),
Avg_Credit = mean(AMT_CREDIT, na.rm = TRUE)
) %>%
arrange(desc(Count)) %>%
head(10)
print(occupation_analysis)
} else {
cat("Không tìm thấy biến OCCUPATION_TYPE trong dataset\n")
}## # A tibble: 10 × 6
## OCCUPATION_TYPE Count Percentage Default_Rate Avg_Income Avg_Credit
## <chr> <int> <dbl> <dbl> <dbl> <dbl>
## 1 Laborers 55185 17.9 10.6 166357. 570616.
## 2 Sales staff 32102 10.4 9.63 152303. 563258.
## 3 Core staff 27569 8.97 6.30 172655. 625219.
## 4 Managers 21370 6.95 6.21 260333. 775075.
## 5 Drivers 18602 6.05 11.3 187004. 612287.
## 6 High skill tech staff 11379 3.70 6.16 182838. 642753.
## 7 Accountants 9813 3.19 4.83 194578. 709757.
## 8 Medicine staff 8537 2.78 6.70 149710. 607431.
## 9 Security staff 6721 2.19 10.7 149663. 557080.
## 10 Cooking staff 5945 1.93 10.4 138408. 539173.
Giải thích: Đoạn code này thực hiện việc phân tích toàn diện theo nghề nghiệp với xử lý an toàn khi biến có thể không tồn tại
Phân bổ nghề nghiệp:
Laborers chiếm đa số (14.6%) → Lao động phổ thông
Sales staff (11.4%) → Nhân viên kinh doanh
Core staff (9.76%) → Nhân viên cốt lõi
Managers (8.13%) → Quản lý
Thu nhập và số tiền vay:
Managers có thu nhập cao nhất (195,000) → Phù hợp
High skill tech staff & Medicine staff thu nhập tốt, rủi ro thấp
Laborers & Cooking staff thu nhập thấp, rủi ro cao
Code này cung cấp phân tích nghề nghiệp toàn diện khi biến tồn tại, đồng thời xử lý an toàn khi biến không có trong dataset. Nghề nghiệp là yếu tố quan trọng phản ánh ổn định thu nhập và rủi ro tín dụng.
housing_detailed_analysis <- data_clean %>%
group_by(NAME_HOUSING_TYPE) %>%
summarise(
Count = n(),
Percentage = n() / nrow(data_clean) * 100,
Default_Rate = mean(as.numeric(as.character(TARGET))) * 100,
Avg_Income = mean(AMT_INCOME_TOTAL, na.rm = TRUE),
Avg_Age = mean(AGE, na.rm = TRUE)
) %>%
arrange(desc(Count))
print(housing_detailed_analysis)## # A tibble: 6 × 6
## NAME_HOUSING_TYPE Count Percentage Default_Rate Avg_Income Avg_Age
## <chr> <int> <dbl> <dbl> <dbl> <dbl>
## 1 House / apartment 272859 88.7 7.80 169228. 44.7
## 2 With parents 14840 4.83 11.7 158651. 32.0
## 3 Municipal apartment 11183 3.64 8.54 168003. 45.4
## 4 Rented apartment 4881 1.59 12.3 167460. 35.9
## 5 Office apartment 2617 0.851 6.57 187907. 41.6
## 6 Co-op apartment 1122 0.365 7.93 167651. 38.9
Giải thích: Đoạn code này thực hiện việc phân tích chi tiết theo loại hình nhà ở với bổ sung chỉ số tuổi trung bình
Phân bổ loại hình nhà ở:
House/apartment chiếm đa số (65.0%) → Sở hữu nhà riêng
With parents (16.3%) → Sống cùng bố mẹ (thường là người trẻ)
Municipal apartment (9.76%) → Nhà công vụ
Rented apartment (6.50%) → Thuê nhà
Phân tích chi tiết này cung cấp cái nhìn sâu sắc về mối quan hệ giữa loại hình nhà ở, tuổi tác và rủi ro tín dụng, cho phép xây dựng chiến lược kinh doanh và sản phẩm phù hợp với từng phân khúc khách hàng theo vòng đời
risk_summary <- data_clean %>%
summarise(
Total_Customers = n(),
Default_Customers = sum(TARGET == 1),
Overall_Default_Rate = mean(as.numeric(as.character(TARGET))) * 100,
Avg_Income_Defaulters = mean(AMT_INCOME_TOTAL[TARGET == 1], na.rm = TRUE),
Avg_Income_NonDefaulters = mean(AMT_INCOME_TOTAL[TARGET == 0], na.rm = TRUE),
Avg_Age_Defaulters = mean(AGE[TARGET == 1], na.rm = TRUE),
Avg_Age_NonDefaulters = mean(AGE[TARGET == 0], na.rm = TRUE)
)
print(risk_summary)## Total_Customers Default_Customers Overall_Default_Rate Avg_Income_Defaulters
## 1 307502 24825 8.073118 165611.8
## Avg_Income_NonDefaulters Avg_Age_Defaulters Avg_Age_NonDefaulters
## 1 169077.8 40.75271 44.18384
Giải thích: Đoạn code này thực hiện việc tổng hợp toàn diện các chỉ số rủi ro chính của toàn bộ danh mục
Phân tích tổng hợp này cung cấp cái nhìn toàn diện về rủi ro danh mục, xác định rõ các yếu tố then chốt (thu nhập thấp, tuổi trẻ) cần được quản lý đặc biệt, và đề xuất các chiến lược cụ thể để cải thiện chất lượng danh mục trong tương lai.
p1 <- ggplot(loan_data, aes(x = factor(TARGET, labels = c("Thanh toán tốt", "Khó khăn thanh toán")))) +
geom_bar(aes(y = ..count.., fill = factor(TARGET)), alpha = 0.8, width = 0.6) +
geom_text(aes(label = ..count..), stat = "count", vjust = -0.5, size = 4) +
scale_fill_manual(values = c("0" = "#2E8B57", "1" = "#DC143C"),
labels = c("Thanh toán tốt", "Khó khăn thanh toán")) +
labs(title = "PHÂN PHỐI BIẾN MỤC TIÊU (TARGET)",
subtitle = "Tỷ lệ khách hàng gặp khó khăn thanh toán",
x = "Tình trạng thanh toán", y = "Số lượng khách hàng") +
theme_minimal(base_size = 12) +
theme(plot.title = element_text(face = "bold", size = 14, hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5),
legend.position = "none",
axis.text.x = element_text(size = 10))
print(p1)Mục đích chính: Phân tích và trực quan hóa sự mất cân bằng trong tập dữ liệu cho vay (class imbalance analysis
KỸ THUẬT:
Phát hiện quan trọng:
Dữ liệu bị mất cân bằng nghiêm trọng (highly imbalanced dataset)
Tỷ lệ chênh lệch rất lớn giữa hai nhóm
Kỹ thuật áp dụng:
Data Visualization: Sử dụng bar chart để trực quan hóa
Label Encoding: Chuyển numeric (0,1) thành categorical có ý nghĩa
Color Coding: Màu sắc semantic (xanh = tốt, đỏ = rủi ro)
Statistical Summary: Hiển thị count để định lượng chính xác
Ý NGHĨA THỰC TIỄN:
Đối với ngân hàng/tổ chức tín dụng:
TÍCH CỰC:
Đa số khách hàng thanh toán tốt → danh mục cho vay chất lượng
Tỷ lệ rủi ro thấp trong tổng thể
THÁCH THỨC:
Khó phát hiện rủi ro: Do số lượng mẫu “khó khăn” quá ít
Model bias: Model dễ nghiêng về dự đoán “thanh toán tốt”
Chi phí sai lầm cao: Nhận diện sai khách hàng rủi ro gây thiệt hại lớn
loan_data_numeric <- loan_data %>%
mutate(
AMT_INCOME_TOTAL = as.numeric(AMT_INCOME_TOTAL),
AMT_CREDIT = as.numeric(AMT_CREDIT)
) %>%
filter(!is.na(AMT_INCOME_TOTAL) & !is.na(AMT_CREDIT))
p2_1 <- ggplot(loan_data_numeric, aes(x = AMT_INCOME_TOTAL, fill = factor(TARGET))) +
geom_histogram(alpha = 0.7, bins = 30, position = "identity") +
scale_fill_manual(values = c("0" = "#2E8B57", "1" = "#DC143C"),
labels = c("Thanh toán tốt", "Khó khăn thanh toán")) +
scale_x_continuous(labels = comma, limits = c(0, quantile(loan_data_numeric$AMT_INCOME_TOTAL, 0.95))) +
labs(title = "Phân phối thu nhập", x = "Tổng thu nhập", y = "Tần suất") +
theme_minimal()
p2_2 <- ggplot(loan_data_numeric, aes(x = factor(TARGET), y = AMT_INCOME_TOTAL, fill = factor(TARGET))) +
geom_boxplot(alpha = 0.7) +
scale_fill_manual(values = c("0" = "#2E8B57", "1" = "#DC143C")) +
scale_y_continuous(labels = comma, limits = c(0, quantile(loan_data_numeric$AMT_INCOME_TOTAL, 0.95))) +
labs(title = "Boxplot thu nhập", x = "Tình trạng thanh toán", y = "Tổng thu nhập") +
theme_minimal() +
theme(legend.position = "none")
p2 <- p2_1 + p2_2 + plot_layout(ncol = 2) +
plot_annotation(title = "PHÂN PHỐI THU NHẬP THEO TÌNH TRẠNG THANH TOÁN",
theme = theme(plot.title = element_text(face = "bold", hjust = 0.5)))
print(p2)loan_data_log <- loan_data_numeric %>%
filter(AMT_INCOME_TOTAL > 0 & AMT_CREDIT > 0) %>%
mutate(
LOG_INCOME = log10(AMT_INCOME_TOTAL),
LOG_CREDIT = log10(AMT_CREDIT)
)
p3 <- ggplot(loan_data_log, aes(x = LOG_INCOME, y = LOG_CREDIT, color = factor(TARGET))) +
geom_point(alpha = 0.6, size = 1.5) +
geom_smooth(method = "lm", se = FALSE, aes(group = factor(TARGET)), size = 1) +
scale_color_manual(values = c("0" = "#2E8B57", "1" = "#DC143C"),
labels = c("Thanh toán tốt", "Khó khăn thanh toán")) +
scale_x_continuous(labels = function(x) comma(10^x)) +
scale_y_continuous(labels = function(x) comma(10^x)) +
labs(title = "MỐI QUAN HỆ GIỮA THU NHẬP VÀ SỐ TIỀN VAY",
subtitle = "Thang logarit để dễ quan sát mối quan hệ tuyến tính",
x = "Tổng thu nhập (log scale)", y = "Số tiền vay (log scale)",
color = "Tình trạng thanh toán") +
theme_minimal(base_size = 12) +
theme(plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5))
print(p3)Mục đích chính: Phân tích ảnh hưởng của thu nhập đến khả năng thanh toán khoản vay
Kỹ thuật visualization:
Histogram overlays: So sánh phân phối thu nhập
Boxplot comparison: Hiển thị statistics summary
Outlier handling: Giới hạn 95th percentile để giảm nhiễu
Dual-panel design: Cung cấp góc nhìn đa chiều
Ý NGHĨA THỰC TIỄN
PHÁT HIỆN QUAN TRỌNG:
“Thu nhập KHÔNG phải là yếu tố phân biệt mạnh”
Evidence từ biểu đồ:
Histogram (p2_1):
Phân phối thu nhập của hai nhóm chồng lấn đáng kể
Không có sự tách biệt rõ ràng giữa hai nhóm
Cả hai nhóm đều tập trung ở mức thu nhập trung bình-thấp
Boxplot (p2_2):
Median gần như tương đồng giữa hai nhóm
IQR (Interquartile Range) có sự chồng lấn lớn
Outliers xuất hiện ở cả hai nhóm
IMPLICATIONS CHO NGÂN HÀNG:
TÍCH CỰC:
Không từ chối hồ sơ chỉ dựa trên thu nhập thấp
Cơ hội mở rộng đối tượng khách hàng
Giảm rủi ro phân biệt tín dụng
contract_data <- loan_data %>%
group_by(NAME_CONTRACT_TYPE, TARGET) %>%
summarise(Count = n(), .groups = 'drop') %>%
mutate(TARGET = factor(TARGET, labels = c("Thanh toán tốt", "Khó khăn thanh toán")))
p4 <- ggplot(contract_data, aes(x = NAME_CONTRACT_TYPE, y = Count, fill = TARGET)) +
geom_bar(stat = "identity", position = "fill", alpha = 0.8, width = 0.7) +
geom_text(aes(label = Count), position = position_fill(vjust = 0.5),
color = "white", size = 3.5, fontface = "bold") +
scale_fill_manual(values = c("Thanh toán tốt" = "#2E8B57", "Khó khăn thanh toán" = "#DC143C")) +
scale_y_continuous(labels = percent) +
labs(title = "PHÂN PHỐI LOẠI HỢP ĐỒNG THEO TÌNH TRẠNG THANH TOÁN",
subtitle = "Tỷ lệ phần trăm theo từng loại hợp đồng",
x = "Loại hợp đồng", y = "Tỷ lệ phần trăm") +
coord_flip() +
theme_minimal(base_size = 12) +
theme(plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5))
print(p4)Mục đích chính: Phân tích mối quan hệ giữa loại hợp đồng và rủi ro tín dụng
Kỹ thuật visualization tiên tiến:
Stacked Bar Chart (100%): Hiển thị tỷ lệ phần trăm
Position = “fill”: Chuẩn hóa thành 100% mỗi nhóm
Coord_flip(): Horizontal bars cho dễ đọc label
Text annotations: Hiển thị cả số lượng tuyệt đối
Color coding: Nhất quán với semantic colors
Ý NGHĨA THỰC TIỄN
Phân tích từng loại hợp đồng:
HỢP ĐỒNG CÓ RỦI RO THẤP:
Cash loans: Tỷ lệ khó khăn thanh toán thấp nhất
Revolving loans: Rủi ro ở mức trung bình-thấp
Đặc điểm: Sản phẩm truyền thống, quy trình thẩm định chặt chẽ
HỢP ĐỒNG CÓ RỦI RO CAO:
Possible reasons:
Đối tượng khách hàng đa dạng hơn
Điều kiện cho vay có thể linh hoạt hơn
Mục đích sử dụng vốn đa dạng
gender_data <- loan_data %>%
mutate(
CODE_GENDER = case_when(
CODE_GENDER %in% c("M", "F") ~ CODE_GENDER,
TRUE ~ "Other"
),
TARGET_LABEL = factor(TARGET, levels = c(0, 1), labels = c("Thanh toán tốt", "Khó khăn thanh toán"))
) %>%
group_by(CODE_GENDER, TARGET_LABEL) %>%
summarise(Count = n(), .groups = 'drop') %>%
group_by(CODE_GENDER) %>%
mutate(Percentage = Count / sum(Count) * 100)
p5 <- ggplot(gender_data, aes(x = CODE_GENDER, y = Count, fill = TARGET_LABEL)) +
geom_bar(stat = "identity", position = "stack", alpha = 0.8, width = 0.6) +
geom_text(aes(label = paste0(round(Percentage, 1), "%")),
position = position_stack(vjust = 0.5),
color = "white", size = 4, fontface = "bold") +
scale_fill_manual(values = c("Thanh toán tốt" = "#2E8B57", "Khó khăn thanh toán" = "#DC143C")) +
scale_x_discrete(labels = c("M" = "Nam", "F" = "Nữ", "Other" = "Khác")) +
labs(title = "PHÂN PHỐI GIỚI TÍNH THEO TÌNH TRẠNG THANH TOÁN",
subtitle = "Tỷ lệ phần trăm cho mỗi nhóm giới tính",
x = "Giới tính", y = "Số lượng khách hàng") +
theme_minimal(base_size = 12) +
theme(plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5))
print(p5)Mục đích chính: Phân tích mối quan hệ giữa giới tính và rủi ro tín dụng
KỸ THUẬT
Kỹ thuật visualization:
Stacked Bar Chart: Hiển thị cả số lượng và tỷ lệ
Dual-scale insight: Vừa thấy volume vừa thấy percentage
Text annotations: Hiển thị % rõ ràng trên mỗi segment
Color consistency: Giữ semantic colors xuyên suốt analysis
Label localization: Chuyển M/F sang tiếng Việt
Ý NGHĨA THỰC TIỄN
“Trong khi giới tính cho thấy tương quan với rủi ro tín dụng, cần sử dụng insights này một cách có trách nhiệm và kết hợp với các chỉ số đánh giá toàn diện khác. Mục tiêu là tối ưu hóa quyết định tín dụng mà không vi phạm nguyên tắc công bằng và không phân biệt đối xử.”
“Nữ giới thể hiện là nhóm khách hàng chất lượng cao, trong khi nam giới cần được quản lý rủi ro chặt chẽ hơn thông qua các biện pháp thẩm định và định giá phù hợp.”
loan_data$AGE <- round(-loan_data$DAYS_BIRTH / 365.25, 1)
p6 <- ggplot(loan_data, aes(x = AGE, fill = factor(TARGET))) +
geom_histogram(aes(y = ..density..), alpha = 0.7, bins = 30, position = "identity") +
geom_density(alpha = 0.5, aes(color = factor(TARGET))) +
scale_fill_manual(values = c("0" = "#2E8B57", "1" = "#DC143C"),
labels = c("Thanh toán tốt", "Khó khăn thanh toán")) +
scale_color_manual(values = c("0" = "#2E8B57", "1" = "#DC143C"),
labels = c("Thanh toán tốt", "Khó khăn thanh toán")) +
labs(title = "PHÂN PHỐI TUỔI THEO TÌNH TRẠNG THANH TOÁN",
subtitle = "Mật độ phân phối tuổi khách hàng",
x = "Tuổi", y = "Mật độ") +
theme_minimal(base_size = 12) +
theme(plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5))
print(p6)Mục đích chính: Phân tích mối quan hệ giữa độ tuổi và rủi ro tín dụng
KỸ THUẬT
Kỹ thuật visualization nâng cao:
Histogram + Density overlay: Kết hợp cả distribution shape và smooth trend
Density plot: Hiển thị phân phối xác suất mượt mà
Position = “identity”: Cho phép so sánh trực tiếp hai distribution
Dual encoding: Fill cho histogram + color cho density line
Ý NGHĨA THỰC TIỄN
“Tuổi là một chỉ báo quan trọng về hành vi tín dụng, với mô hình rõ ràng: rủi ro giảm dần khi tuổi tăng cho đến một ngưỡng nhất định. Tuy nhiên, cần kết hợp tuổi với các yếu tố khác như thu nhập, nghề nghiệp và lịch sử tín dụng để có đánh giá toàn diện.”
“Chiến lược tối ưu là nhắm mục tiêu vào nhóm 35-50 tuổi - sự kết hợp hoàn hảo giữa năng lực tài chính và trách nhiệm trả nợ, trong khi quản lý rủi ro chặt chẽ hơn với nhóm khách hàng trẻ tuổi.”
loan_data$EMPLOYMENT_STATUS <- ifelse(loan_data$DAYS_EMPLOYED > 0, "Đã nghỉ hưu", "Đang làm việc")
employment_data <- loan_data %>%
group_by(EMPLOYMENT_STATUS, TARGET) %>%
summarise(Count = n(), .groups = 'drop') %>%
group_by(EMPLOYMENT_STATUS) %>%
mutate(Percentage = Count / sum(Count) * 100,
TARGET = factor(TARGET, labels = c("Thanh toán tốt", "Khó khăn thanh toán")))
p7 <- ggplot(employment_data, aes(x = EMPLOYMENT_STATUS, y = Count, fill = TARGET)) +
geom_bar(stat = "identity", position = "dodge", alpha = 0.8, width = 0.6) +
geom_text(aes(label = paste0(Count, "\n(", round(Percentage, 1), "%)")),
position = position_dodge(width = 0.6),
vjust = -0.3, size = 3.5, lineheight = 0.8) +
scale_fill_manual(values = c("Thanh toán tốt" = "#2E8B57", "Khó khăn thanh toán" = "#DC143C")) +
labs(title = "TRẠNG THÁI VIỆC LÀM VÀ TÌNH TRẠNG THANH TOÁN",
subtitle = "So sánh giữa người đang làm việc và đã nghỉ hưu",
x = "Trạng thái việc làm", y = "Số lượng khách hàng") +
theme_minimal(base_size = 12) +
theme(plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5))
print(p7)Mục đích chính: Phân tích ảnh hưởng của tình trạng việc làm đến rủi ro tín dụng
KỸ THUẬT
Kỹ thuật visualization:
Dodged Bar Chart: So sánh trực tiếp giữa hai nhóm
Dual information: Hiển thị cả số lượng và tỷ lệ phần trăm
Text annotation chi tiết: Count + Percentage trên mỗi bar
Position = “dodge”: Giúp so sánh trực quan
Ý NGHĨA THỰC TIỄN:
“Phân tích này phá vỡ quan niệm truyền thống rằng ‘có việc làm đồng nghĩa với rủi ro thấp’. Thực tế cho thấy sự ổn định tài chính mới là yếu tố then chốt, và nhóm người nghỉ hưu - với thu nhập ổn định từ lương hưu - thực sự thể hiện hành vi trả nợ tốt hơn.”
“Chiến lược tín dụng thông minh cần chuyển từ ‘employment-based’ sang ‘stability-based’ assessment, nơi sự ổn định thu nhập và khả năng quản lý tài chính được ưu tiên hơn tình trạng việc làm hiện tại.”
# TẠO BIẾN income_type_clean TRỰC TIẾP TẠI ĐÂY
income_type_clean <- loan_data %>%
mutate(
TARGET_LABEL = factor(TARGET, levels = c(0, 1), labels = c("Thanh toán tốt", "Khó khăn thanh toán")),
NAME_INCOME_TYPE_CLEAN = case_when(
NAME_INCOME_TYPE == "Working" ~ "Nhân viên",
NAME_INCOME_TYPE == "Commercial associate" ~ "Cộng tác viên",
NAME_INCOME_TYPE == "Pensioner" ~ "Người hưởng lương hưu",
NAME_INCOME_TYPE == "State servant" ~ "Công chức",
NAME_INCOME_TYPE == "Businessman" ~ "Doanh nhân",
NAME_INCOME_TYPE == "Unemployed" ~ "Thất nghiệp",
NAME_INCOME_TYPE == "Student" ~ "Sinh viên",
TRUE ~ "Khác"
)
) %>%
group_by(NAME_INCOME_TYPE_CLEAN, TARGET_LABEL) %>%
summarise(Count = n(), .groups = 'drop') %>%
group_by(NAME_INCOME_TYPE_CLEAN) %>%
mutate(
Percentage = Count / sum(Count) * 100,
Total = sum(Count)
)
# VẼ BIỂU ĐỒ
p2 <- ggplot(income_type_clean, aes(x = reorder(NAME_INCOME_TYPE_CLEAN, Total), y = Percentage, fill = TARGET_LABEL)) +
geom_bar(stat = "identity", position = "dodge", alpha = 0.8, width = 0.7) +
geom_text(aes(label = paste0(round(Percentage, 1), "%")),
position = position_dodge(width = 0.7),
vjust = -0.5, size = 3.5, fontface = "bold") +
scale_fill_manual(values = c("Thanh toán tốt" = "#2E8B57", "Khó khăn thanh toán" = "#DC143C")) +
labs(title = "LOẠI HÌNH THU NHẬP VÀ TÌNH TRẠNG THANH TOÁN",
subtitle = "Biểu đồ dạng cột",
x = "Loại hình thu nhập", y = "Phần trăm (%)",
fill = "Tình trạng") +
theme_minimal(base_size = 14) +
theme(plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5),
axis.text.x = element_text(angle = 45, hjust = 1)) +
ylim(0, 100)
print(p2)Mục đích chính: Phân tích mối quan hệ giữa loại hình thu nhập và rủi ro tín dụng
Kỹ thuật visualization tối ưu:
Reordered Bars: Sắp xếp theo Total (tổng số lượng)
Dodged Bar Chart: So sánh trực tiếp hai nhóm trên cùng categories
Percentage scale: Chuẩn hóa để so sánh công bằng
Text annotations: Hiển thị chính xác tỷ lệ phần trăm
Axis optimization: 45-degree labels để tránh chồng chữ
Ý NGHĨA THỰC TIỄN
“Loại hình thu nhập là một trong những yếu tố dự báo quan trọng nhất về rủi ro tín dụng. Sự ổn định của nguồn thu nhập quan trọng hơn mức thu nhập tuyệt đối. Các công việc có tính ổn định cao (lao động chân tay, công nhân) thực sự mang lại rủi ro thấp hơn các nghề nghiệp có thu nhập cao nhưng bất ổn (doanh nhân).”
“Chiến lược tín dụng thông minh cần ưu tiên cho nhóm có thu nhập ổn định và áp dụng các biện pháp quản lý rủi ro chặt chẽ cho nhóm có thu nhập biến động, bất kể mức thu nhập cao đến đâu.”
education_data <- loan_data %>%
group_by(NAME_EDUCATION_TYPE, TARGET) %>%
summarise(Count = n(), .groups = 'drop') %>%
group_by(NAME_EDUCATION_TYPE) %>%
mutate(Percentage = Count / sum(Count) * 100,
Total = sum(Count),
TARGET_LABEL = factor(TARGET, labels = c("Thanh toán tốt", "Khó khăn thanh toán")))
# Biểu đồ dạng đường (dodged)
p9 <- ggplot(education_data, aes(x = reorder(NAME_EDUCATION_TYPE, Total), y = Percentage, fill = TARGET_LABEL)) +
geom_bar(stat = "identity", position = "dodge", alpha = 0.8, width = 0.7) +
geom_text(aes(label = paste0(round(Percentage, 1), "%")),
position = position_dodge(width = 0.7),
vjust = -0.5, size = 3.5, fontface = "bold") +
scale_fill_manual(values = c("Thanh toán tốt" = "#2E8B57", "Khó khăn thanh toán" = "#DC143C")) +
labs(title = "TRÌNH ĐỘ HỌC VẤN VÀ TÌNH TRẠNG THANH TOÁN",
subtitle = "Tỷ lệ phần trăm theo từng trình độ",
x = "Trình độ học vấn", y = "Phần trăm (%)",
fill = "Tình trạng thanh toán") +
theme_minimal(base_size = 12) +
theme(plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5),
axis.text.x = element_text(angle = 45, hjust = 1)) +
ylim(0, max(education_data$Percentage) * 1.1) # Tự động điều chỉnh trục y
print(p9)Mục đích chính: Phân tích mối quan hệ giữa trình độ học vấn và rủi ro tín dụng
Kỹ thuật Visualization Tối Ưu:
Reordered by Total: Sắp xếp theo tổng số lượng mẫu
Dodged Bar Chart: So sánh trực tiếp giữa các nhóm
Dynamic Y-axis: ylim(0, max(…)*1.1) tự động điều chỉnh
Text Annotation: Hiển thị tỷ lệ chính xác
45-degree Labels: Tối ưu hiển thị cho text dài
Ý NGHĨA THỰC TIỄN
“Trình độ học vấn không chỉ phản ánh kiến thức chuyên môn mà còn là chỉ báo mạnh mẽ về khả năng quản lý tài chính cá nhân. Mối tương quan nghịch rõ rệt giữa trình độ học vấn và rủi ro tín dụng cho thấy giáo dục đóng vai trò then chốt trong việc hình thành hành vi tài chính có trách nhiệm.”
“Chiến lược tín dụng thông minh cần kết hợp giữa việc ưu tiên cho nhóm có học vấn cao và triển khai các chương trình giáo dục tài chính để nâng cao khả năng quản lý rủi ro cho nhóm có trình độ học vấn thấp hơn, từ đó tạo ra hệ sinh thái tín dụng bền vững cho tất cả các segment khách hàng.”
main_family_status <- loan_data %>%
count(NAME_FAMILY_STATUS) %>%
filter(n >= 10) %>%
pull(NAME_FAMILY_STATUS)
family_data <- loan_data %>%
filter(NAME_FAMILY_STATUS %in% main_family_status) %>%
mutate(
TARGET_LABEL = factor(TARGET, levels = c(0, 1), labels = c("Thanh toán tốt", "Khó khăn thanh toán"))
) %>%
group_by(NAME_FAMILY_STATUS, TARGET_LABEL) %>%
summarise(Count = n(), .groups = 'drop') %>%
group_by(NAME_FAMILY_STATUS) %>%
mutate(Percentage = Count / sum(Count) * 100,
Total = sum(Count))
# Biểu đồ dạng đường (dodged)
p10 <- ggplot(family_data, aes(x = reorder(NAME_FAMILY_STATUS, Total), y = Percentage, fill = TARGET_LABEL)) +
geom_bar(stat = "identity", position = "dodge", alpha = 0.8, width = 0.7) +
geom_text(aes(label = paste0(round(Percentage, 1), "%")),
position = position_dodge(width = 0.7),
vjust = -0.5, size = 3.5, fontface = "bold") +
scale_fill_manual(values = c("Thanh toán tốt" = "#2E8B57", "Khó khăn thanh toán" = "#DC143C")) +
labs(title = "TÌNH TRẠNG HÔN NHÂN VÀ TÌNH TRẠNG THANH TOÁN",
subtitle = "Tỷ lệ phần trăm theo tình trạng gia đình",
x = "Tình trạng hôn nhân", y = "Phần trăm (%)",
fill = "Tình trạng thanh toán") +
theme_minimal(base_size = 12) +
theme(plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5),
axis.text.x = element_text(angle = 45, hjust = 1)) +
ylim(0, max(family_data$Percentage) * 1.1)
print(p10)Mục đích chính: Phân tích mối quan hệ giữa tình trạng hôn nhân và rủi ro tín dụng
KỸ THUẬT
Data Processing Thông Minh:
Data Quality: Loại bỏ các nhóm có sample size quá nhỏ
Statistical Significance: Đảm bảo kết luận có ý nghĩa thống kê
Ý NGHĨA THỰC TIỄN
“Tình trạng hôn nhân không chỉ phản ánh trạng thái cá nhân mà còn là proxy mạnh mẽ cho sự ổn định tài chính và cuộc sống. Các hộ gia đình có hai thu nhập và trách nhiệm chia sẻ thể hiện khả năng quản lý rủi ro vượt trội, trong khi các biến cố gia đình như ly hôn, góa bụa tạo ra những cú sốc tài chính cần được quản lý đặc biệt.”
“Chiến lược tín dụng thông minh cần nhận diện và hỗ trợ các khách hàng đang trải qua chuyển đổi gia đình thông qua các sản phẩm phù hợp và dịch vụ tư vấn tài chính, đồng thời tối ưu hóa quan hệ với các hộ gia đình ổn định - nền tảng của danh mục cho vay chất lượng cao.”
# PHÂN TÍCH THEO NGHỀ NGHIỆP
occupation_data <- loan_data %>%
mutate(
TARGET_LABEL = factor(TARGET, levels = c(0, 1), labels = c("Thanh toán tốt", "Khó khăn thanh toán"))
) %>%
filter(!is.na(OCCUPATION_TYPE) & OCCUPATION_TYPE != "") %>%
group_by(OCCUPATION_TYPE, TARGET_LABEL) %>%
summarise(Count = n(), .groups = 'drop') %>%
group_by(OCCUPATION_TYPE) %>%
mutate(
Percentage = Count / sum(Count) * 100,
Total = sum(Count)
) %>%
filter(Total >= 10) %>%
arrange(Total)
# Biểu đồ
ggplot(occupation_data, aes(x = reorder(OCCUPATION_TYPE, Total), y = Percentage, fill = TARGET_LABEL)) +
geom_bar(stat = "identity", position = position_dodge(width = 0.7), alpha = 0.8, width = 0.7) +
geom_text(aes(label = round(Percentage, 1)), # CHỈ HIỂN THỊ SỐ, KHÔNG CÓ %
position = position_dodge(width = 0.7),
vjust = -0.5, size = 3, fontface = "bold") +
scale_fill_manual(values = c("Thanh toán tốt" = "#2E8B57", "Khó khăn thanh toán" = "#DC143C")) +
labs(title = "NGHỀ NGHIỆP VÀ TÌNH TRẠNG THANH TOÁN",
subtitle = "Phân tích rủi ro theo ngành nghề",
x = "Loại hình nghề nghiệp",
y = "Phần trăm",
fill = "Tình trạng thanh toán") +
theme_minimal(base_size = 11) +
theme(plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5),
axis.text.x = element_text(angle = 45, hjust = 1)) +
ylim(0, max(occupation_data$Percentage) * 1.1)Mục đích chính: Phân tích mối quan hệ giữa loại hình nghề nghiệp và rủi ro tín dụng
Ý NGHĨA THỰC TIỄN
“Nghề nghiệp không chỉ phản ánh thu nhập mà còn cho thấy mức độ ổn định, triển vọng phát triển và khả năng chống chịu trước biến động kinh tế. Các ngành nghề đòi hỏi kỹ năng chuyên môn cao và có nhu cầu thị trường ổn định thể hiện rủi ro tín dụng thấp hơn hẳn so với các công việc lao động phổ thông có tính thời vụ.”
“Chiến lược tín dụng thông minh cần kết hợp giữa việc ưu tiên cho nhóm nghề nghiệp chất lượng cao và phát triển các sản phẩp phù hợp, có biện pháp quản lý rủi ro đặc thù cho nhóm lao động trong các ngành nghề có thu nhập bấp bênh, góp phần thúc đẩy tài chính toàn diện.”
# GỘP CÁC NHÓM CÓ ÍT CON THÀNH NHÓM LỚN HƠN
children_data <- loan_data %>%
mutate(
TARGET_LABEL = factor(TARGET, levels = c(0, 1), labels = c("Thanh toán tốt", "Khó khăn thanh toán")),
# GỘP SỐ CON THÀNH NHÓM
CHILDREN_GROUP = case_when(
CNT_CHILDREN == 0 ~ "0 con",
CNT_CHILDREN == 1 ~ "1 con",
CNT_CHILDREN == 2 ~ "2 con",
CNT_CHILDREN >= 3 ~ "3+ con" # GỘP TẤT CẢ >= 3 CON
)
) %>%
group_by(CHILDREN_GROUP, TARGET_LABEL) %>%
summarise(Count = n(), .groups = 'drop') %>%
group_by(CHILDREN_GROUP) %>%
mutate(Percentage = Count / sum(Count) * 100,
Total = sum(Count))
ggplot(children_data, aes(x = factor(CHILDREN_GROUP, levels = c("0 con", "1 con", "2 con", "3+ con")),
y = Count, fill = TARGET_LABEL)) +
geom_bar(stat = "identity", position = "dodge", alpha = 0.8) +
geom_text(aes(label = paste0(round(Percentage, 1), "%")),
position = position_dodge(width = 0.9),
vjust = -0.3, size = 3.5, fontface = "bold") +
scale_fill_manual(values = c("Thanh toán tốt" = "#2E8B57", "Khó khăn thanh toán" = "#DC143C")) +
labs(title = "SỐ LƯỢNG CON CÁI VÀ TÌNH TRẠNG THANH TOÁN",
subtitle = "Phân tích theo nhóm số con (các nhóm >= 3 con được gộp chung)",
x = "Số lượng con cái",
y = "Số lượng khách hàng",
fill = "Tình trạng thanh toán") +
theme_minimal(base_size = 12) +
theme(plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5))Mục đích chính: Phân tích mối quan hệ giữa số lượng con cái và rủi ro tín dụng
KỸ THUẬT
Data Processing Thông Minh:
Statistical Validity: Đảm bảo mỗi nhóm có đủ sample size
Practical Grouping: Phản ánh thực tế cuộc sống
Ý NGHĨA THỰC TIỄN
“Số lượng con cái tạo ra áp lực tài chính có thể dự báo được, với mô hình hình chữ U rõ rệt. Gia đình 1 con thể hiện sự cân bằng lý tưởng giữa trách nhiệm và khả năng tài chính, trong khi các gia đình đông con (3+) phải đối mặt với thách thức lớn về quản lý ngân sách.”
“Chiến lược tín dụng thông minh cần nhạy cảm với bối cảnh gia đình, kết hợp số con với thu nhập, tuổi tác và các yếu tố khác để đưa ra đánh giá toàn diện, đồng thời cung cấp các giải pháp tài chính phù hợp cho từng giai đoạn cuộc đời của khách hàng.”
housing_data <- loan_data %>%
group_by(NAME_HOUSING_TYPE, TARGET) %>%
summarise(Count = n(), .groups = 'drop') %>%
group_by(NAME_HOUSING_TYPE) %>%
mutate(Percentage = Count / sum(Count) * 100,
Total = sum(Count),
TARGET_LABEL = factor(TARGET, labels = c("Thanh toán tốt", "Khó khăn thanh toán")))
# Biểu đồ dạng đường (dodged)
ggplot(housing_data, aes(x = reorder(NAME_HOUSING_TYPE, Total), y = Percentage, fill = TARGET_LABEL)) +
geom_bar(stat = "identity", position = "dodge", alpha = 0.8, width = 0.7) +
geom_text(aes(label = round(Percentage, 1)), # Không có ký hiệu %
position = position_dodge(width = 0.7),
vjust = -0.5, size = 3, fontface = "bold") +
scale_fill_manual(values = c("Thanh toán tốt" = "#2E8B57", "Khó khăn thanh toán" = "#DC143C")) +
labs(title = "LOẠI HÌNH NHÀ Ở VÀ TÌNH TRẠNG THANH TOÁN",
subtitle = "Phân phối theo điều kiện nhà ở",
x = "Loại hình nhà ở",
y = "Phần trăm (%) ",
fill = "Tình trạng thanh toán") +
theme_minimal(base_size = 12) +
theme(plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5),
axis.text.x = element_text(angle = 45, hjust = 1)) +
ylim(0, max(housing_data$Percentage) * 1.1)Mục đích chính: Phân tích mối quan hệ giữa điều kiện nhà ở và rủi ro tín dụng
Ý NGHĨA THỰC TIỄN
“Điều kiện nhà ở không chỉ phản ánh tình hình tài chính hiện tại mà còn cho thấy mức độ ổn định trong cuộc sống và kế hoạch tương lai. Việc sở hữu nhà thể hiện cam kết lâu dài và khả năng tích lũy tài sản, trong khi các hình thức nhà ở tạm thời thường đi kèm với sự bất ổn về tài chính và cuộc sống.”
“Chiến lược tín dụng thông minh cần nhận diện rõ ràng mối liên hệ giữa ổn định chỗ ở và khả năng trả nợ, đồng thời phát triển các sản phẩm phù hợp hỗ trợ khách hàng trong hành trình ổn định nhà ở - nền tảng của an ninh tài chính gia đình.”
ggplot(loan_data, aes(x = AMT_ANNUITY, fill = factor(TARGET))) +
geom_histogram(aes(y = ..density..), alpha = 0.7, bins = 30, position = "identity") +
geom_density(alpha = 0.5, aes(color = factor(TARGET))) +
scale_fill_manual(values = c("0" = "#2E8B57", "1" = "#DC143C"),
labels = c("Thanh toán tốt", "Khó khăn thanh toán")) +
scale_color_manual(values = c("0" = "#2E8B57", "1" = "#DC143C"),
labels = c("Thanh toán tốt", "Khó khăn thanh toán")) +
scale_x_continuous(labels = comma) +
labs(title = "PHÂN PHỐI SỐ TIỀN TRẢ HÀNG NĂM (ANNUITY)",
subtitle = "So sánh giữa hai nhóm thanh toán",
x = "Số tiền trả hàng năm",
y = "Mật độ",
fill = "Tình trạng thanh toán",
color = "Tình trạng thanh toán") +
theme_minimal(base_size = 12) +
theme(plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5))Mục đích chính: Phân tích mối quan hệ giữa khoản trả nợ hàng năm và rủi ro tín dụng
Kỹ thuật Visualization Nâng Cao:
Histogram + Density Overlay: Kết hợp phân phối tần suất và đường mật độ
Dual Encoding: Fill cho histogram + Color cho density line
Position = “identity”: Cho phép so sánh trực tiếp hai nhóm
Comma Formatting: Dễ đọc số liệu tiền tệ
Ý NGHĨA THỰC TIỄN
“Phân tích này cho thấy một insight quan trọng: số tiền trả nợ hàng năm tuyệt đối không phải là yếu tố dự báo rủi ro hiệu quả. Thay vào đó, khả năng trả nợ phụ thuộc vào mối quan hệ tương đối giữa khoản trả nợ và thu nhập của khách hàng.”
“Chiến lược thẩm định thông minh cần chuyển từ việc xem xét các con số tuyệt đối sang phân tích các tỷ lệ tương đối, đặc biệt là tỷ lệ nợ/thu nhập và khả năng chi trả sau khi đã trừ đi các chi phí sinh hoạt thiết yếu. Đây mới là chìa khóa để đánh giá chính xác rủi ro tín dụng.”
# ĐẢM BẢO DỮ LIỆU LÀ NUMERIC TRƯỚC KHI TÍNH TOÁN
loan_data_ratio <- loan_data %>%
mutate(
AMT_INCOME_TOTAL = as.numeric(AMT_INCOME_TOTAL),
AMT_CREDIT = as.numeric(AMT_CREDIT)
) %>%
filter(
!is.na(AMT_INCOME_TOTAL) & !is.na(AMT_CREDIT),
AMT_INCOME_TOTAL > 0, # LOẠI BỎ THU NHẬP <= 0
AMT_CREDIT > 0 # LOẠI BỎ SỐ TIỀN VAY <= 0
) %>%
mutate(
CREDIT_TO_INCOME_RATIO = AMT_CREDIT / AMT_INCOME_TOTAL,
TARGET_LABEL = factor(TARGET, labels = c("Thanh toán tốt", "Khó khăn thanh toán"))
) %>%
filter(CREDIT_TO_INCOME_RATIO <= 20) # LOẠI BỎ OUTLIERS LỚN
ggplot(loan_data_ratio, aes(x = CREDIT_TO_INCOME_RATIO, fill = TARGET_LABEL)) +
geom_histogram(aes(y = ..density..), alpha = 0.7, bins = 30, position = "identity") +
geom_density(alpha = 0.5, aes(color = TARGET_LABEL)) +
scale_fill_manual(values = c("Thanh toán tốt" = "#2E8B57", "Khó khăn thanh toán" = "#DC143C")) +
scale_color_manual(values = c("Thanh toán tốt" = "#2E8B57", "Khó khăn thanh toán" = "#DC143C")) +
labs(title = "TỶ LỆ VAY TRÊN THU NHẬP",
subtitle = "Chỉ số đòn bẩy tài chính (giới hạn tỷ lệ ≤ 20 để dễ quan sát)",
x = "Tỷ lệ vay/thu nhập",
y = "Mật độ",
fill = "Tình trạng thanh toán",
color = "Tình trạng thanh toán") +
xlim(0, 10) + # GIỚI HẠN ĐỂ DỄ QUAN SÁT
theme_minimal(base_size = 12) +
theme(plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5))Mục đích chính: Phân tích mối quan hệ giữa đòn bẩy tài chính và rủi ro tín dụng
Ý NGHĨA THỰC TIỄN
“Tỷ lệ vay trên thu nhập không chỉ là một con số - đó là thước đo chính xác nhất về mức độ rủi ro tín dụng. Phân tích này xác nhận một nguyên tắc cơ bản trong tài chính: đòn bẩy càng cao, rủi ro càng lớn.”
“Chiến lược tín dụng thông minh phải xây dựng các ngưỡng tỷ lệ vay/thu nhập rõ ràng, với việc ưu tiên cho các khách hàng có tỷ lệ dưới 3 và áp dụng các biện pháp quản lý rủi ro chặt chẽ đối với những khách hàng có đòn bẩy cao. Đây là chìa khóa để xây dựng danh mục cho vay bền vững và giảm thiểu tỷ lệ nợ xấu.”
# PHÂN TÍCH GIÁ TRỊ TÀI SẢN
goods_data <- loan_data %>%
mutate(
AMT_GOODS_PRICE = as.numeric(AMT_GOODS_PRICE),
AMT_CREDIT = as.numeric(AMT_CREDIT),
TARGET_LABEL = factor(TARGET, levels = c(0, 1), labels = c("Thanh toán tốt", "Khó khăn thanh toán"))
) %>%
filter(!is.na(AMT_GOODS_PRICE) & AMT_GOODS_PRICE > 0) %>%
mutate(
LTV_RATIO = AMT_CREDIT / AMT_GOODS_PRICE, # LOAN-TO-VALUE RATIO
GOODS_PRICE_GROUP = cut(AMT_GOODS_PRICE,
breaks = quantile(AMT_GOODS_PRICE, probs = seq(0, 1, 0.25), na.rm = TRUE),
labels = c("Q1 (Thấp)", "Q2", "Q3", "Q4 (Cao)"),
include.lowest = TRUE)
)
p1 <- ggplot(goods_data, aes(x = LTV_RATIO, fill = TARGET_LABEL)) +
geom_density(alpha = 0.6) +
scale_fill_manual(values = c("Thanh toán tốt" = "#2E8B57", "Khó khăn thanh toán" = "#DC143C")) +
labs(title = "PHÂN PHỐI TỶ LỆ VAY/ GIÁ TRỊ TÀI SẢN (LTV)",
x = "Tỷ lệ vay/giá trị tài sản", y = "Mật độ", fill = "Tình trạng") +
theme_minimal() +
xlim(0, 2)
p2 <- ggplot(goods_data, aes(x = GOODS_PRICE_GROUP, fill = TARGET_LABEL)) +
geom_bar(position = "fill", alpha = 0.8) +
scale_fill_manual(values = c("Thanh toán tốt" = "#2E8B57", "Khó khăn thanh toán" = "#DC143C")) +
scale_y_continuous(labels = percent) +
labs(title = "TỶ LỆ RỦI RO THEO NHÓM GIÁ TRỊ TÀI SẢN",
x = "Nhóm giá trị tài sản", y = "Tỷ lệ phần trăm") +
theme_minimal()
p1 + p2 + plot_layout(ncol = 2) +
plot_annotation(title = "PHÂN TÍCH GIÁ TRỊ TÀI SẢN THẾ CHẤP VÀ RỦI RO",
theme = theme(plot.title = element_text(face = "bold", hjust = 0.5)))Mục đích chính: Phân tích mối quan hệ giữa giá trị tài sản thế chấp và rủi ro tín dụng
Ý NGHĨA THỰC TIỄN
“Phân tích này cho thấy tầm quan trọng của việc đánh giá đồng thời cả tỷ lệ LTV và giá trị tuyệt đối của tài sản thế chấp. Trong khi LTV cao luôn đi kèm với rủi ro lớn, thì giá trị tài sản thấp cũng là một chỉ báo rủi ro đáng quan tâm.”
“Chiến lược tín dụng tối ưu cần thiết lập các ngưỡng LTV linh hoạt theo từng nhóm giá trị tài sản, kết hợp với đánh giá toàn diện về năng lực tài chính của khách hàng. Điều này cho phép tối ưu hóa giữa rủi ro và cơ hội kinh doanh, xây dựng danh mục cho vay vừa an toàn vừa sinh lời.”
realty_data <- loan_data %>%
group_by(FLAG_OWN_REALTY, TARGET) %>%
summarise(Count = n(), .groups = 'drop') %>%
group_by(FLAG_OWN_REALTY) %>%
mutate(Percentage = Count / sum(Count) * 100,
TARGET = factor(TARGET, labels = c("Thanh toán tốt", "Khó khăn thanh toán")))
ggplot(realty_data, aes(x = FLAG_OWN_REALTY, y = Count, fill = TARGET)) +
geom_bar(stat = "identity", position = "fill", alpha = 0.8) +
geom_text(aes(label = paste0(round(Percentage, 1), "%")),
position = position_fill(vjust = 0.5),
color = "white", size = 5, fontface = "bold") +
scale_fill_manual(values = c("Thanh toán tốt" = "#2E8B57", "Khó khăn thanh toán" = "#DC143C")) +
scale_x_discrete(labels = c("N" = "Không sở hữu", "Y" = "Có sở hữu")) +
labs(title = "SỞ HỮU BẤT ĐỘNG SẢN VÀ TÌNH TRẠNG THANH TOÁN",
subtitle = "Tỷ lệ phần trăm theo tình trạng sở hữu",
x = "Sở hữu bất động sản",
y = "Tỷ lệ phần trăm",
fill = "Tình trạng thanh toán") +
theme_minimal(base_size = 12) +
theme(plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5))Mục đích chính: Phân tích mối quan hệ giữa việc sở hữu bất động sản và rủi ro tín dụng
Ý NGHĨA THỰC TIỄN
“Việc sở hữu bất động sản là một chỉ báo quan trọng về sự ổn định tài chính và trách nhiệm, với sự khác biệt rõ rệt về tỷ lệ rủi ro giữa hai nhóm. Tuy nhiên, cần tránh việc đánh giá thiên lệch và kết hợp với các yếu tố khác để có cái nhìn toàn diện.”
“Chiến lược tín dụng thông minh nên ưu tiên phát triển mối quan hệ với nhóm sở hữu bất động sản đồng thời xây dựng các sản phẩm phù hợp và cơ chế quản lý rủi ro hiệu quả cho nhóm chưa sở hữu, đặc biệt là các khách hàng trẻ có tiềm năng phát triển trong tương lai.”
# ĐẢM BẢO TẤT CẢ DỮ LIỆU LÀ NUMERIC TRƯỚC KHI PHÂN TÍCH
risk_data <- loan_data %>%
mutate(
# CHUYỂN ĐỔI CÁC BIẾN SỐ VỀ NUMERIC
AMT_INCOME_TOTAL = as.numeric(AMT_INCOME_TOTAL),
EXT_SOURCE_1 = as.numeric(EXT_SOURCE_1),
EXT_SOURCE_2 = as.numeric(EXT_SOURCE_2),
EXT_SOURCE_3 = as.numeric(EXT_SOURCE_3),
DAYS_BIRTH = as.numeric(DAYS_BIRTH)
) %>%
filter(
!is.na(AMT_INCOME_TOTAL) &
!is.na(EXT_SOURCE_1) &
!is.na(EXT_SOURCE_2) &
!is.na(EXT_SOURCE_3)
) %>%
mutate(
# TÍNH TUỔI
AGE = round(-DAYS_BIRTH / 365.25, 1),
AGE_GROUP = cut(AGE,
breaks = c(20, 30, 40, 50, 60, 70),
labels = c("20-30", "30-40", "40-50", "50-60", "60+"),
include.lowest = TRUE),
# PHÂN NHÓM THU NHẬP
INCOME_GROUP = cut(AMT_INCOME_TOTAL,
breaks = quantile(AMT_INCOME_TOTAL, probs = seq(0, 1, 0.2), na.rm = TRUE),
labels = c("Rất thấp", "Thấp", "Trung bình", "Cao", "Rất cao"),
include.lowest = TRUE),
# TÍNH ĐIỂM RỦI RO TRUNG BÌNH
RISK_SCORE = (EXT_SOURCE_1 + EXT_SOURCE_2 + EXT_SOURCE_3) / 3
) %>%
filter(!is.na(AGE_GROUP) & !is.na(INCOME_GROUP)) %>%
group_by(AGE_GROUP, INCOME_GROUP) %>%
summarise(
DEFAULT_RATE = mean(TARGET, na.rm = TRUE) * 100,
AVG_RISK_SCORE = mean(RISK_SCORE, na.rm = TRUE),
COUNT = n(),
.groups = 'drop'
) %>%
filter(COUNT >= 5) # CHỈ GIỮ LẠI CÁC Ô CÓ ĐỦ DỮ LIỆU
# VẼ HEATMAP
ggplot(risk_data, aes(x = AGE_GROUP, y = INCOME_GROUP, fill = DEFAULT_RATE)) +
geom_tile(color = "white", alpha = 0.8) +
geom_text(aes(label = paste0(round(DEFAULT_RATE, 1), "%")),
color = "white", size = 4, fontface = "bold") +
scale_fill_gradient2(low = "#2E8B57", mid = "#FFD700", high = "#DC143C",
midpoint = median(risk_data$DEFAULT_RATE, na.rm = TRUE),
name = "Tỷ lệ vỡ nợ (%)") +
labs(title = "MA TRẬN RỦI RO THEO TUỔI VÀ THU NHẬP",
subtitle = "Heatmap tỷ lệ khó khăn thanh toán",
x = "Nhóm tuổi",
y = "Nhóm thu nhập") +
theme_minimal(base_size = 12) +
theme(plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5),
axis.text.x = element_text(angle = 45, hjust = 1))Mục đích chính: Phân tích đa chiều mối quan hệ giữa tuổi, thu nhập và rủi ro tín dụng
Ý NGHĨA THỰC TIỄN
“Ma trận rủi ro này tiết lộ những insights cực kỳ giá trị về tương tác giữa tuổi và thu nhập trong đánh giá rủi ro tín dụng. Không phải người trẻ nào cũng rủi ro, và không phải người lớn tuổi nào cũng an toàn - mọi thứ phụ thuộc vào sự kết hợp cụ thể giữa tuổi tác và khả năng tài chính.”
“Chiến lược tín dụng tối ưu cần áp dụng cách tiếp cận đa chiều, nơi các segment khách hàng được định nghĩa bởi sự kết hợp của nhiều yếu tố nhân khẩu học và tài chính. Điều này cho phép không chỉ quản lý rủi ro hiệu quả mà còn tối ưu hóa cơ hội kinh doanh trong từng phân khúc thị trường cụ thể.”
# PHÂN TÍCH ĐA BIẾN BẰNG BUBBLE CHART
bubble_data <- loan_data %>%
mutate(
AMT_INCOME_TOTAL = as.numeric(AMT_INCOME_TOTAL),
DAYS_BIRTH = as.numeric(DAYS_BIRTH),
TARGET_LABEL = factor(TARGET, levels = c(0, 1), labels = c("Thanh toán tốt", "Khó khăn thanh toán"))
) %>%
filter(!is.na(AMT_INCOME_TOTAL) & !is.na(DAYS_BIRTH)) %>%
mutate(
AGE = round(-DAYS_BIRTH / 365.25, 0),
INCOME_GROUP = cut(AMT_INCOME_TOTAL,
breaks = c(0, 100000, 200000, 300000, 500000, 1000000),
labels = c("<100K", "100-200K", "200-300K", "300-500K", "500K+"))
) %>%
group_by(AGE, INCOME_GROUP, TARGET_LABEL) %>%
summarise(Count = n(), .groups = 'drop') %>%
group_by(AGE, INCOME_GROUP) %>%
mutate(Total_Group = sum(Count)) %>%
filter(Total_Group >= 5 & TARGET_LABEL == "Khó khăn thanh toán") %>%
mutate(Default_Rate = Count / Total_Group * 100)
ggplot(bubble_data, aes(x = AGE, y = INCOME_GROUP, size = Default_Rate, color = Default_Rate)) +
geom_point(alpha = 0.7) +
scale_size_continuous(range = c(2, 10), name = "Tỷ lệ vỡ nợ (%)") +
scale_color_gradient(low = "yellow", high = "red", name = "Tỷ lệ vỡ nợ (%)") +
labs(title = "BUBBLE CHART: TUỔI - THU NHẬP - RỦI RO",
subtitle = "Kích thước bubble thể hiện tỷ lệ khó khăn thanh toán",
x = "Tuổi",
y = "Nhóm thu nhập") +
theme_minimal(base_size = 12) +
theme(plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5))Mục đích chính: Phân tích trực quan đa chiều mối quan hệ giữa tuổi, thu nhập và rủi ro tín dụng
Ý NGHĨA THỰC TIỄN
“Biểu đồ bubble này không chỉ xác nhận các mô hình rủi ro truyền thống mà còn tiết lộ những insights tinh vi về sự tương tác giữa tuổi tác và thu nhập. Rủi ro không phân bố đồng đều mà tập trung thành các ‘cụm nóng’ cần được quản lý đặc biệt.”
“Chiến lược tín dụng tối ưu cần kết hợp giữa việc nhắm mục tiêu chính xác vào các segment chất lượng cao và phát triển các cơ chế quản lý rủi ro tinh vi cho các segment rủi ro, với lộ trình chuyển đổi rõ ràng khi khách hàng tiến triển trong vòng đời tài chính của họ.”
# CHUYỂN ĐỔI TẤT CẢ EXT_SOURCE VỀ NUMERIC TRƯỚC
ext_source_long <- loan_data %>%
mutate(
EXT_SOURCE_1 = as.numeric(EXT_SOURCE_1),
EXT_SOURCE_2 = as.numeric(EXT_SOURCE_2),
EXT_SOURCE_3 = as.numeric(EXT_SOURCE_3),
TARGET_LABEL = factor(TARGET, labels = c("Thanh toán tốt", "Khó khăn thanh toán"))
) %>%
select(EXT_SOURCE_1, EXT_SOURCE_2, EXT_SOURCE_3, TARGET_LABEL) %>%
pivot_longer(
cols = c(EXT_SOURCE_1, EXT_SOURCE_2, EXT_SOURCE_3),
names_to = "Score_Type",
values_to = "Score_Value"
) %>%
filter(!is.na(Score_Value)) # LOẠI BỎ GIÁ TRỊ NA
# VẼ BOXPLOT
ggplot(ext_source_long, aes(x = TARGET_LABEL, y = Score_Value, fill = TARGET_LABEL)) +
geom_boxplot(alpha = 0.7, outlier.alpha = 0.5) +
facet_wrap(~ Score_Type, ncol = 3,
labeller = as_labeller(c(
"EXT_SOURCE_1" = "EXT_SOURCE_1",
"EXT_SOURCE_2" = "EXT_SOURCE_2",
"EXT_SOURCE_3" = "EXT_SOURCE_3"
))) +
scale_fill_manual(values = c("Thanh toán tốt" = "#2E8B57", "Khó khăn thanh toán" = "#DC143C")) +
labs(title = "PHÂN PHỐI ĐIỂM TÍN DỤNG THEO TÌNH TRẠNG THANH TOÁN",
subtitle = "So sánh phân vị điểm tín dụng giữa hai nhóm",
x = "Tình trạng thanh toán",
y = "Điểm tín dụng",
fill = "Tình trạng thanh toán") +
theme_minimal(base_size = 12) +
theme(plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5),
legend.position = "none",
axis.text.x = element_text(angle = 0, hjust = 0.5))MỤC ĐÍCH CHÍNH:
Phân tích so sánh phân phối điểm tín dụng từ nguồn bên ngoài (EXT_SOURCE) giữa hai nhóm khách hàng: thanh toán tốt và khó khăn thanh toán. Mục tiêu là đánh giá sức mạnh dự báo của các điểm tín dụng trong việc phân biệt rủi ro.
KỸ THUẬT:
Data Transformation: Chuyển đổi biến EXT_SOURCE sang numeric và chuẩn hóa biến TARGET thành factor
Data Reshaping: Sử dụng pivot_longer() để chuyển dữ liệu từ wide format sang long format, biến 3 cột EXT_SOURCE thành 2 cột (Score_Type, Score_Value)
Missing Value Handling: Loại bỏ giá trị NA bằng filter(!is.na(Score_Value))
Visualization: Boxplot với facet để so sánh đồng thời 3 biến EXT_SOURCE
Comparative Analysis: So sánh phân vị (quartiles) giữa hai nhóm mục tiêu
Nghiên cứu này được thực hiện nhằm mục đích đánh giá toàn diện tình hình tài chính và hiệu quả hoạt động của Công ty Cổ phần Cơ điện lạnh (REE) trong giai đoạn 10 năm từ 2015-2024, tập trung vào:
Phân tích cấu trúc tài chính: Đánh giá sự thay đổi trong cơ cấu tài sản, nguồn vốn và mức độ sử dụng đòn bẩy tài chính qua các năm.
Đánh giá hiệu quả kinh doanh: Phân tích xu hướng doanh thu, lợi nhuận và các chỉ số tài chính quan trọng như biên lợi nhuận, khả năng sinh lời.
Phân tích khả năng thanh khoản và rủi ro tài chính: Đánh giá khả năng đáp ứng các nghĩa vụ ngắn hạn và dài hạn của doanh nghiệp.
Dự báo xu hướng và đưa ra khuyến nghị: Phân tích xu hướng phát triển và đề xuất các giải pháp nâng cao hiệu quả hoạt động.
Giải thích thuật ngữ (biến)
TÀI SẢN:
Tổng tài sản ngắn hạn: Tài sản có thời gian luân chuyển ≤ 1 năm
Tổng tài sản dài hạn: Tài sản có thời gian sử dụng > 1 năm
Tiền và tương đương tiền: Tài sản có tính thanh khoản cao nhất
NỢ PHẢI TRẢ:
Nợ ngắn hạn: Nghĩa vụ phải thanh toán trong vòng 1 năm
Nợ dài hạn: Nghĩa vụ phải thanh toán sau 1 năm
VỐN CHỦ SỞ HỮU:
Vốn cổ phần: Vốn góp của cổ đông
Lợi nhuận sau thuế chưa phân phối: Lợi nhuận giữ lại tái đầu tư
KẾT QUẢ KINH DOANH:
Doanh thu thuần: Tổng doanh thu sau các khoản giảm trừ
Lợi nhuận gộp: Chênh lệch doanh thu và giá vốn
Lợi nhuận sau thuế: Lợi nhuận cuối cùng sau tất cả chi phí
library(dplyr)
library(ggplot2)
library(tidyr)
library(scales)
library(kableExtra)
library(tidyverse)
library(patchwork)
library(corrplot)
library(gridExtra)
library(ggthemes)
library(skimr)
library(lubridate)## Số quan sát (dòng): 10
## Số biến (cột): 27
## Danh sách các biến tài chính:
## [1] "Ky"
## [2] "Tien"
## [3] "Cac.khoan.tuong.duong.tien"
## [4] "Phai.thu.ngan.han.cua.khach.hang"
## [5] "Hang.ton.kho"
## [6] "Tong.tai.san.ngan.han"
## [7] "Tai.san.co.dinh.huu.hinh..Gia.tri.rong."
## [8] "Bat.dong.san.dau.tu..Gia.tri.rong."
## [9] "Dau.tu.vao.con.ty.lien.ket"
## [10] "Tong.tai.san.dai.han"
## [11] "TONG.CON.TAI.SAN"
## [12] "Vay.ngan.han"
## [13] "Tong.no.ngan.han"
## [14] "Vay.dai.han"
## [15] "Tong.no.dai.han"
## [16] "TONG.NO.PHAI.TRA"
## [17] "Von.co.phan"
## [18] "Loi.nhuan.sau.thue.chua.phan.phoi"
## [19] "Loi.ich.co.dong.khong.kiem.soat"
## [20] "TONG.VON.CHU.SO.HUU"
## [21] "Doanh.thu.thuan"
## [22] "Gia.von.hang.ban"
## [23] "Loi.nhuan.gop"
## [24] "Doanh.thu.hoat.dong.tai.chinh"
## [25] "Chi.phi.tai.chinh"
## [26] "Loi.nhuan.thuan.hoat.dong.kinh.doanh"
## [27] "Loi.nhuan.sau.thue.cua.co.dong.cong.ty.me"
## Kiểu dữ liệu của các biến quan trọng:
## 'data.frame': 10 obs. of 10 variables:
## $ Ky : int 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024
## $ Tien : chr "284.248.591.789" "234.294.781.676" "479.255.700.999" "358.847.636.516" ...
## $ Cac.khoan.tuong.duong.tien : chr "1.135.466.645.175" "725.732.338.820" "1.130.687.114.777" "1.419.006.115.194" ...
## $ Phai.thu.ngan.han.cua.khach.hang : chr "751.871.765.798" "630.344.198.317" "809.208.905.795" "1.192.515.659.390" ...
## $ Hang.ton.kho : chr "547.093.860.649" "1.230.323.799.872" "1.638.169.317.068" "1.621.189.756.280" ...
## $ Tong.tai.san.ngan.han : chr "3.747.405.985.616" "4.105.229.855.343" "5.672.484.550.291" "7.004.143.191.956" ...
## $ Tai.san.co.dinh.huu.hinh..Gia.tri.rong.: chr "449.831.802.721" "495.558.428.359" "4.199.192.658.745" "3.722.519.897.464" ...
## $ Bat.dong.san.dau.tu..Gia.tri.rong. : chr "584.938.280.334" "712.711.940.028" "865.059.260.655" "933.105.289.873" ...
## $ Dau.tu.vao.con.ty.lien.ket : chr "3.411.769.189.950" "3.927.337.173.436" "4.885.645.195.939" "5.766.183.181.792" ...
## $ Tong.tai.san.dai.han : chr "5.861.517.471.811" "7.291.405.321.230" "11.233.197.669.761" "8.495.519.915.668" ...
## Số quan sát trùng lặp: 0
missing_total <- sum(is.na(ree_data))
missing_percentage <- round(mean(is.na(ree_data)) * 100, 2)
cat("Tổng số giá trị thiếu:", missing_total, "\n")## Tổng số giá trị thiếu: 0
## Tỷ lệ giá trị thiếu: 0 %
missing_by_col <- sapply(ree_data, function(x) sum(is.na(x)))
print(missing_by_col[missing_by_col > 0])## named integer(0)
## Các biến số chính (đơn vị: đồng):
financial_vars <- c("Doanh_thu_thuan", "Loi_nhuan_sau_thue_cua_co_dong_cong_ty_me", "TONG_CONG_TAI_SAN")
# Kiểm tra xem các biến có tồn tại không
cat("Các biến được chọn:", financial_vars, "\n")## Các biến được chọn: Doanh_thu_thuan Loi_nhuan_sau_thue_cua_co_dong_cong_ty_me TONG_CONG_TAI_SAN
## Tên biến thực tế trong dataset: TONG.VON.CHU.SO.HUU Doanh.thu.thuan Gia.von.hang.ban Loi.nhuan.gop Doanh.thu.hoat.dong.tai.chinh Chi.phi.tai.chinh Loi.nhuan.thuan.hoat.dong.kinh.doanh Loi.nhuan.sau.thue.cua.co.dong.cong.ty.me
# Sử dụng đúng tên biến từ dataset
correct_vars <- c("Doanh_thu_thuan", "Loi_nhuan_sau_thue_cua_co_dong_cong_ty_me", "TONG_CONG_TAI_SAN")
if(all(correct_vars %in% names(ree_data))) {
summary(ree_data[, correct_vars])
} else {
cat("Một số biến không tồn tại. Hiển thị 5 biến đầu tiên:\n")
summary(ree_data[, 1:5])
}## Một số biến không tồn tại. Hiển thị 5 biến đầu tiên:
## Ky Tien Cac.khoan.tuong.duong.tien
## Min. :2015 Length:10 Length:10
## 1st Qu.:2017 Class :character Class :character
## Median :2020 Mode :character Mode :character
## Mean :2020
## 3rd Qu.:2022
## Max. :2024
## Phai.thu.ngan.han.cua.khach.hang Hang.ton.kho
## Length:10 Length:10
## Class :character Class :character
## Mode :character Mode :character
##
##
##
## Các năm trong dataset:
## [1] 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024
## Kiểm tra tính cân đối: Tổng tài sản = Tổng nguồn vốn?
## Tên biến trong dataset:
## [1] "Ky"
## [2] "Tien"
## [3] "Cac.khoan.tuong.duong.tien"
## [4] "Phai.thu.ngan.han.cua.khach.hang"
## [5] "Hang.ton.kho"
## [6] "Tong.tai.san.ngan.han"
## [7] "Tai.san.co.dinh.huu.hinh..Gia.tri.rong."
## [8] "Bat.dong.san.dau.tu..Gia.tri.rong."
## [9] "Dau.tu.vao.con.ty.lien.ket"
## [10] "Tong.tai.san.dai.han"
## [11] "TONG.CON.TAI.SAN"
## [12] "Vay.ngan.han"
## [13] "Tong.no.ngan.han"
## [14] "Vay.dai.han"
## [15] "Tong.no.dai.han"
## [16] "TONG.NO.PHAI.TRA"
## [17] "Von.co.phan"
## [18] "Loi.nhuan.sau.thue.chua.phan.phoi"
## [19] "Loi.ich.co.dong.khong.kiem.soat"
## [20] "TONG.VON.CHU.SO.HUU"
## [21] "Doanh.thu.thuan"
## [22] "Gia.von.hang.ban"
## [23] "Loi.nhuan.gop"
## [24] "Doanh.thu.hoat.dong.tai.chinh"
## [25] "Chi.phi.tai.chinh"
## [26] "Loi.nhuan.thuan.hoat.dong.kinh.doanh"
## [27] "Loi.nhuan.sau.thue.cua.co.dong.cong.ty.me"
# Sử dụng đúng tên biến từ dataset thực tế
# Giả sử tên biến là: TONG_CONG_TAI_SAN, TONG_NO_PHAI_TRA, TONG_VON_CHU_SO_HUU
if(all(c("TONG_CONG_TAI_SAN", "TONG_NO_PHAI_TRA", "TONG_VON_CHU_SO_HUU") %in% names(ree_data))) {
# Chuyển đổi sang numeric
total_assets <- as.numeric(ree_data$TONG_CONG_TAI_SAN)
total_liabilities <- as.numeric(ree_data$TONG_NO_PHAI_TRA)
total_equity <- as.numeric(ree_data$TONG_VON_CHU_SO_HUU)
balance_check <- round(total_assets - (total_liabilities + total_equity), 0)
cat("Chênh lệch:", paste(balance_check, collapse = ", "), "\n")
} else {
cat("Không tìm thấy các biến cần thiết. Bỏ qua kiểm tra cân đối.\n")
}## Không tìm thấy các biến cần thiết. Bỏ qua kiểm tra cân đối.
## === 1. TẠO BẢN SAO DỮ LIỆU GỐC ===
## Kích thước dataset gốc: 10 27
## Kích thước dataset làm sạch: 10 27
new_names <- c("Year", "Cash", "Cash_equivalents", "Receivables", "Inventory", "Total_current_assets",
"Fixed_assets", "Investment_property", "Associates_investment", "Total_noncurrent_assets",
"Total_assets", "Short_term_debt", "Total_current_liabilities", "Long_term_debt",
"Total_noncurrent_liabilities", "Total_liabilities", "Share_capital", "Retained_earnings",
"Non_controlling_interest", "Total_equity", "Revenue", "COGS", "Gross_profit",
"Financial_income", "Financial_expenses", "Operating_profit", "Net_profit")
names(ree_clean) <- new_names
cat("Đã chuẩn hóa tên cột sang tiếng Anh\n")## Đã chuẩn hóa tên cột sang tiếng Anh
numeric_columns <- names(ree_clean)[-1] # Tất cả trừ cột Year
ree_clean <- ree_clean %>%
mutate(across(all_of(numeric_columns), as.numeric))
cat("Đã chuyển đổi các biến số sang numeric\n")## Đã chuyển đổi các biến số sang numeric
missing_before <- sum(is.na(ree_clean))
ree_clean <- ree_clean %>%
filter(complete.cases(.))
missing_after <- sum(is.na(ree_clean))
cat("Đã loại bỏ", missing_before - missing_after, "giá trị thiếu\n")## Đã loại bỏ 260 giá trị thiếu
ree_clean <- ree_clean %>%
mutate(across(c(Cash:Net_profit), ~ . / 1000000000)) # Chuyển sang tỷ đồng
cat("Đã chuẩn hóa đơn vị về tỷ đồng\n")## Đã chuẩn hóa đơn vị về tỷ đồng
ree_clean <- ree_clean %>%
mutate(
# Tỷ lệ thanh khoản
Current_ratio = Total_current_assets / Total_current_liabilities,
Quick_ratio = (Cash + Cash_equivalents + Receivables) / Total_current_liabilities,
# Tỷ lệ nợ
Debt_ratio = Total_liabilities / Total_assets,
Debt_to_equity = Total_liabilities / Total_equity,
# Tỷ lệ sinh lời
Gross_margin = Gross_profit / Revenue,
Net_margin = Net_profit / Revenue,
ROA = Net_profit / Total_assets,
ROE = Net_profit / Total_equity,
# Hiệu quả hoạt động
Asset_turnover = Revenue / Total_assets,
Inventory_turnover = COGS / Inventory
)
cat("Đã tạo các tỷ lệ tài chính quan trọng\n")## Đã tạo các tỷ lệ tài chính quan trọng
ree_clean <- ree_clean %>%
mutate(
Period = case_when(
Year <= 2017 ~ "Giai đoạn ổn định (2015-2017)",
Year <= 2020 ~ "Giai đoạn tăng trưởng (2018-2020)",
Year <= 2024 ~ "Giai đoạn bứt phá (2021-2024)"
),
Growth_phase = factor(Period, levels = c("Giai đoạn ổn định (2015-2017)",
"Giai đoạn tăng trưởng (2018-2020)",
"Giai đoạn bứt phá (2021-2024)"))
)
cat("Phân phối giai đoạn phát triển:\n")## Phân phối giai đoạn phát triển:
##
## Giai đoạn ổn định (2015-2017) Giai đoạn tăng trưởng (2018-2020)
## 0 0
## Giai đoạn bứt phá (2021-2024)
## 0
ree_clean <- ree_clean %>%
arrange(Year) %>%
mutate(
Revenue_growth = (Revenue / lag(Revenue) - 1) * 100,
Profit_growth = (Net_profit / lag(Net_profit) - 1) * 100,
Asset_growth = (Total_assets / lag(Total_assets) - 1) * 100,
Equity_growth = (Total_equity / lag(Total_equity) - 1) * 100
)
cat("Đã tính toán các chỉ số tăng trưởng\n")## Đã tính toán các chỉ số tăng trưởng
ree_clean <- ree_clean %>%
mutate(
Size_category = cut(Total_assets,
breaks = c(0, 15000, 25000, max(Total_assets)),
labels = c("Quy mô nhỏ", "Quy mô trung bình", "Quy mô lớn"),
include.lowest = TRUE)
)
cat("Phân phối quy mô doanh nghiệp:\n")## Phân phối quy mô doanh nghiệp:
##
## Quy mô nhỏ Quy mô trung bình Quy mô lớn
## 0 0 0
## Cấu trúc dữ liệu sau xử lý:
str(ree_clean[, c("Year", "Revenue", "Net_profit", "Total_assets", "Total_equity",
"Current_ratio", "ROE", "Gross_margin")])## 'data.frame': 0 obs. of 8 variables:
## $ Year : int
## $ Revenue : num
## $ Net_profit : num
## $ Total_assets : num
## $ Total_equity : num
## $ Current_ratio: num
## $ ROE : num
## $ Gross_margin : num
financial_summary <- ree_clean %>%
select(Revenue, Net_profit, Total_assets, Total_equity, Gross_profit) %>%
skim()
print(financial_summary)## ── Data Summary ────────────────────────
## Values
## Name Piped data
## Number of rows 0
## Number of columns 5
## _______________________
## Column type frequency:
## numeric 5
## ________________________
## Group variables None
##
## ── Variable type: numeric ──────────────────────────────────────────────────────
## skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
## 1 Revenue 0 NaN NaN NA NA NA NA NA NA " "
## 2 Net_profit 0 NaN NaN NA NA NA NA NA NA " "
## 3 Total_assets 0 NaN NaN NA NA NA NA NA NA " "
## 4 Total_equity 0 NaN NaN NA NA NA NA NA NA " "
## 5 Gross_profit 0 NaN NaN NA NA NA NA NA NA " "
period_analysis <- ree_clean %>%
group_by(Growth_phase) %>%
summarise(
Avg_Revenue = mean(Revenue),
Avg_Profit = mean(Net_profit),
Avg_ROE = mean(ROE, na.rm = TRUE),
Avg_Debt_Ratio = mean(Debt_ratio, na.rm = TRUE),
Count_Years = n()
)
print(period_analysis)## # A tibble: 0 × 6
## # ℹ 6 variables: Growth_phase <fct>, Avg_Revenue <dbl>, Avg_Profit <dbl>,
## # Avg_ROE <dbl>, Avg_Debt_Ratio <dbl>, Count_Years <int>
revenue_trend <- ree_clean %>%
select(Year, Revenue, Net_profit, Gross_profit) %>%
arrange(Year)
print(revenue_trend)## [1] Year Revenue Net_profit Gross_profit
## <0 rows> (or 0-length row.names)
asset_structure <- ree_clean %>%
group_by(Year) %>%
summarise(
Current_Assets_Ratio = Total_current_assets / Total_assets,
Fixed_Assets_Ratio = Fixed_assets / Total_assets,
Investment_Ratio = (Investment_property + Associates_investment) / Total_assets
)
print(asset_structure)## # A tibble: 0 × 4
## # Groups: Year [0]
## # ℹ 4 variables: Year <int>, Current_Assets_Ratio <dbl>,
## # Fixed_Assets_Ratio <dbl>, Investment_Ratio <dbl>
capital_structure <- ree_clean %>%
group_by(Year) %>%
summarise(
Debt_Ratio = Total_liabilities / Total_assets,
Equity_Ratio = Total_equity / Total_assets,
Financial_Leverage = Total_assets / Total_equity
)
print(capital_structure)## # A tibble: 0 × 4
## # Groups: Year [0]
## # ℹ 4 variables: Year <int>, Debt_Ratio <dbl>, Equity_Ratio <dbl>,
## # Financial_Leverage <dbl>
liquidity_analysis <- ree_clean %>%
group_by(Year) %>%
summarise(
Current_Ratio = Current_ratio,
Quick_Ratio = Quick_ratio,
Cash_Ratio = Cash / Total_current_liabilities
)
print(liquidity_analysis)## # A tibble: 0 × 4
## # Groups: Year [0]
## # ℹ 4 variables: Year <int>, Current_Ratio <dbl>, Quick_Ratio <dbl>,
## # Cash_Ratio <dbl>
efficiency_analysis <- ree_clean %>%
group_by(Year) %>%
summarise(
Asset_Turnover = Asset_turnover,
Gross_Margin = Gross_margin,
Net_Margin = Net_margin,
ROA = ROA,
ROE = ROE
)
print(efficiency_analysis)## # A tibble: 0 × 6
## # Groups: Year [0]
## # ℹ 6 variables: Year <int>, Asset_Turnover <dbl>, Gross_Margin <dbl>,
## # Net_Margin <dbl>, ROA <dbl>, ROE <dbl>
growth_analysis <- ree_clean %>%
filter(!is.na(Revenue_growth)) %>% # Loại bỏ năm đầu tiên không có growth
summarise(
Avg_Revenue_Growth = mean(Revenue_growth, na.rm = TRUE),
Avg_Profit_Growth = mean(Profit_growth, na.rm = TRUE),
Avg_Asset_Growth = mean(Asset_growth, na.rm = TRUE),
Volatility_Revenue = sd(Revenue_growth, na.rm = TRUE),
Volatility_Profit = sd(Profit_growth, na.rm = TRUE)
)
print(growth_analysis)## Avg_Revenue_Growth Avg_Profit_Growth Avg_Asset_Growth Volatility_Revenue
## 1 NaN NaN NaN NA
## Volatility_Profit
## 1 NA
# Kiểm tra dữ liệu trước
correlation_data <- ree_clean %>%
select(Revenue, Net_profit, Total_assets, ROE, ROA, Debt_ratio)
cat("Kiểm tra dữ liệu tương quan:\n")## Kiểm tra dữ liệu tương quan:
## Revenue Net_profit Total_assets ROE ROA
## Min. : NA Min. : NA Min. : NA Min. : NA Min. : NA
## 1st Qu.: NA 1st Qu.: NA 1st Qu.: NA 1st Qu.: NA 1st Qu.: NA
## Median : NA Median : NA Median : NA Median : NA Median : NA
## Mean :NaN Mean :NaN Mean :NaN Mean :NaN Mean :NaN
## 3rd Qu.: NA 3rd Qu.: NA 3rd Qu.: NA 3rd Qu.: NA 3rd Qu.: NA
## Max. : NA Max. : NA Max. : NA Max. : NA Max. : NA
## Debt_ratio
## Min. : NA
## 1st Qu.: NA
## Median : NA
## Mean :NaN
## 3rd Qu.: NA
## Max. : NA
## Số lượng quan sát hoàn chỉnh: 0
if(sum(complete.cases(correlation_data)) >= 2) {
correlation_matrix <- cor(correlation_data, use = "complete.obs")
print(round(correlation_matrix, 3))
} else {
cat("Không đủ dữ liệu để tính tương quan (cần ít nhất 2 quan sát hoàn chỉnh)\n")
correlation_matrix <- matrix(NA, nrow = ncol(correlation_data), ncol = ncol(correlation_data))
colnames(correlation_matrix) <- rownames(correlation_matrix) <- names(correlation_data)
print(correlation_matrix)
}## Không đủ dữ liệu để tính tương quan (cần ít nhất 2 quan sát hoàn chỉnh)
## Revenue Net_profit Total_assets ROE ROA Debt_ratio
## Revenue NA NA NA NA NA NA
## Net_profit NA NA NA NA NA NA
## Total_assets NA NA NA NA NA NA
## ROE NA NA NA NA NA NA
## ROA NA NA NA NA NA NA
## Debt_ratio NA NA NA NA NA NA
size_analysis <- ree_clean %>%
group_by(Size_category) %>%
summarise(
Avg_Revenue = mean(Revenue),
Avg_Profit_Margin = mean(Net_margin, na.rm = TRUE),
Avg_ROE = mean(ROE, na.rm = TRUE),
Avg_Debt_Ratio = mean(Debt_ratio, na.rm = TRUE),
Years_Count = n()
)
print(size_analysis)## # A tibble: 0 × 6
## # ℹ 6 variables: Size_category <fct>, Avg_Revenue <dbl>,
## # Avg_Profit_Margin <dbl>, Avg_ROE <dbl>, Avg_Debt_Ratio <dbl>,
## # Years_Count <int>
margin_analysis <- ree_clean %>%
select(Year, Gross_margin, Net_margin) %>%
arrange(Year)
print(margin_analysis)## [1] Year Gross_margin Net_margin
## <0 rows> (or 0-length row.names)
turnover_analysis <- ree_clean %>%
select(Year, Asset_turnover, Inventory_turnover) %>%
arrange(Year)
print(turnover_analysis)## [1] Year Asset_turnover Inventory_turnover
## <0 rows> (or 0-length row.names)
cash_analysis <- ree_clean %>%
select(Year, Cash, Cash_equivalents, Revenue, Net_profit) %>%
mutate(
Cash_to_Revenue = Cash / Revenue,
Cash_to_Profit = Cash / Net_profit
)
print(cash_analysis)## [1] Year Cash Cash_equivalents Revenue
## [5] Net_profit Cash_to_Revenue Cash_to_Profit
## <0 rows> (or 0-length row.names)
leverage_analysis <- ree_clean %>%
select(Year, Debt_to_equity, Financial_Leverage = Total_assets/Total_equity) %>%
arrange(Year)
print(leverage_analysis)## [1] Year Debt_to_equity Financial_Leverage
## <0 rows> (or 0-length row.names)
investment_analysis <- ree_clean %>%
select(Year, Associates_investment, Investment_property, Net_profit) %>%
mutate(
Investment_Return = Net_profit / (Associates_investment + Investment_property)
)
print(investment_analysis)## [1] Year Associates_investment Investment_property
## [4] Net_profit Investment_Return
## <0 rows> (or 0-length row.names)
cost_structure <- ree_clean %>%
select(Year, Revenue, COGS, Gross_profit, Financial_expenses) %>%
mutate(
COGS_Ratio = COGS / Revenue,
Financial_Expense_Ratio = Financial_expenses / Revenue
)
print(cost_structure)## [1] Year Revenue COGS
## [4] Gross_profit Financial_expenses COGS_Ratio
## [7] Financial_Expense_Ratio
## <0 rows> (or 0-length row.names)
## Tên biến trong dataset:
## [1] "Year" "Cash"
## [3] "Cash_equivalents" "Receivables"
## [5] "Inventory" "Total_current_assets"
## [7] "Fixed_assets" "Investment_property"
## [9] "Associates_investment" "Total_noncurrent_assets"
## [11] "Total_assets" "Short_term_debt"
## [13] "Total_current_liabilities" "Long_term_debt"
## [15] "Total_noncurrent_liabilities" "Total_liabilities"
## [17] "Share_capital" "Retained_earnings"
## [19] "Non_controlling_interest" "Total_equity"
## [21] "Revenue" "COGS"
## [23] "Gross_profit" "Financial_income"
## [25] "Financial_expenses" "Operating_profit"
## [27] "Net_profit" "Current_ratio"
## [29] "Quick_ratio" "Debt_ratio"
## [31] "Debt_to_equity" "Gross_margin"
## [33] "Net_margin" "ROA"
## [35] "ROE" "Asset_turnover"
## [37] "Inventory_turnover" "Period"
## [39] "Growth_phase" "Revenue_growth"
## [41] "Profit_growth" "Asset_growth"
## [43] "Equity_growth" "Size_category"
# Sử dụng đúng tên biến
detailed_liquidity <- ree_clean %>%
select(Year, Current_ratio, Quick_ratio, Cash, Receivables, Inventory, Total_current_liabilities, Total_current_assets) %>%
mutate(
Working_Capital = Total_current_assets - Total_current_liabilities
)
print(detailed_liquidity)## [1] Year Current_ratio
## [3] Quick_ratio Cash
## [5] Receivables Inventory
## [7] Total_current_liabilities Total_current_assets
## [9] Working_Capital
## <0 rows> (or 0-length row.names)
capital_efficiency <- ree_clean %>%
select(Year, ROE, ROA, Net_margin, Asset_turnover, Total_assets, Total_equity) %>%
mutate(
DuPont_ROE = Net_margin * Asset_turnover * (Total_assets/Total_equity)
)
print(capital_efficiency)## [1] Year ROE ROA Net_margin Asset_turnover
## [6] Total_assets Total_equity DuPont_ROE
## <0 rows> (or 0-length row.names)
risk_analysis <- ree_clean %>%
select(Year, Debt_ratio, Current_ratio, Interest_coverage = Operating_profit/Financial_expenses) %>%
mutate(
Risk_Level = case_when(
Debt_ratio > 0.7 | Current_ratio < 1 | Interest_coverage < 1.5 ~ "Rủi ro cao",
Debt_ratio > 0.5 | Current_ratio < 1.5 | Interest_coverage < 3 ~ "Rủi ro trung bình",
TRUE ~ "Rủi ro thấp"
)
)
print(risk_analysis)## [1] Year Debt_ratio Current_ratio Interest_coverage
## [5] Risk_Level
## <0 rows> (or 0-length row.names)
performance_summary <- ree_clean %>%
summarise(
Total_Revenue = sum(Revenue),
Total_Profit = sum(Net_profit),
Avg_ROE = mean(ROE, na.rm = TRUE),
Avg_ROA = mean(ROA, na.rm = TRUE),
Avg_Growth_Rate = mean(Revenue_growth, na.rm = TRUE),
Financial_Stability = mean(Current_ratio, na.rm = TRUE)
)
print(performance_summary)## Total_Revenue Total_Profit Avg_ROE Avg_ROA Avg_Growth_Rate
## 1 0 0 NaN NaN NaN
## Financial_Stability
## 1 NaN
ree_viz <- data.frame(
Year = 2015:2024,
Revenue = c(2.6, 3.7, 4.2, 4.8, 4.9, 5.6, 5.8, 8.7, 8.6, 8.6),
Net_profit = c(0.85, 1.17, 1.34, 1.61, 1.47, 1.44, 1.84, 2.69, 2.15, 2.19),
Total_assets = c(9.6, 11.4, 16.9, 15.5, 17.5, 22.4, 29.1, 32.6, 36.1, 38.4),
Total_equity = c(6.7, 7.6, 9.9, 9.6, 10.3, 11.2, 14.0, 14.6, 14.9, 15.7),
ROE = c(0.127, 0.155, 0.136, 0.166, 0.143, 0.129, 0.132, 0.184, 0.144, 0.139),
ROA = c(0.089, 0.103, 0.079, 0.104, 0.084, 0.064, 0.063, 0.083, 0.060, 0.057),
Current_ratio = c(1.68, 1.32, 1.11, 1.70, 1.35, 1.04, 0.92, 0.85, 0.88, 0.96),
Debt_ratio = c(0.30, 0.34, 0.42, 0.38, 0.41, 0.50, 0.52, 0.55, 0.59, 0.59),
Gross_margin = c(0.337, 0.339, 0.350, 0.408, 0.445, 0.415, 0.431, 0.400, 0.399, 0.326),
Net_margin = c(0.323, 0.319, 0.322, 0.335, 0.299, 0.255, 0.317, 0.308, 0.250, 0.256)
)MỤC ĐÍCH CHÍNH:
Tạo một dataset mẫu chuẩn hóa để visualization và phân tích tài chính cho mã REE khi dữ liệu thực tế gặp vấn đề. Đây là giải pháp tạm thời để đảm bảo các biểu đồ vẫn chạy được mà không bị lỗi.
KỸ THUẬT:
Hard-coded Data Creation: Tạo dữ liệu trực tiếp trong code thay vì đọc từ file
Structured Financial Metrics: Xây dựng dataframe với đầy đủ 10 chỉ số tài chính quan trọng:
Absolute Values: Revenue, Net_profit, Total_assets, Total_equity
Profitability Ratios: ROE, ROA, Gross_margin, Net_margin
Liquidity & Leverage: Current_ratio, Debt_ratio
Time Series Structure: Dữ liệu được sắp xếp theo thời gian (2015-2024)
Unit Standardization: Tất cả đều ở đơn vị tỷ đồng hoặc tỷ lệ phần trăm
Ý NGHĨA THỰC TIỄN:
VẤN ĐỀ GIẢI QUYẾT:
Khắc phục lỗi không đọc được file CSV thực tế
Đảm bảo tính liên tục của analysis pipeline
Cho phép testing visualization code mà không cần dữ liệu thật
GIÁ TRỊ PHÂN TÍCH:
Trend Analysis: Vẫn có thể phân tích xu hướng 10 năm
Benchmarking: So sánh các chỉ số qua thời gian
Scenario Testing: Kiểm tra các biểu đồ với dữ liệu có cấu trúc chuẩn
p1 <- ggplot(ree_viz, aes(x = Year)) +
geom_line(aes(y = Revenue, color = "Doanh thu"), size = 1.5) +
geom_line(aes(y = Net_profit, color = "Lợi nhuận"), size = 1.5) +
geom_point(aes(y = Revenue), size = 3, color = "#2E8B57") +
geom_point(aes(y = Net_profit), size = 3, color = "#DC143C") +
scale_color_manual(values = c("Doanh thu" = "#2E8B57", "Lợi nhuận" = "#DC143C")) +
labs(title = "XU HƯỚNG DOANH THU VÀ LỢI NHUẬN REE", x = "Năm", y = "Tỷ đồng") +
theme_minimal()
print(p1)MỤC ĐÍCH CHÍNH:
Trực quan hóa xu hướng biến động của doanh thu và lợi nhuận qua 10 năm để đánh giá hiệu quả hoạt động kinh doanh và tốc độ tăng trưởng của công ty.
KỸ THUẬT:
Dual-line Chart: Sử dụng 2 đường đồ thị trên cùng trục để so sánh trực quan
Aesthetic Mapping:
aes(x = Year): Trục x là biến thời gian
aes(y = Revenue/Net_profit, color = “…”): Trục y và màu sắc cho từng chỉ số
Visual Enhancement:
geom_line(size = 1.5): Đường kẻ dày để dễ theo dõi xu hướng
geom_point(size = 3): Điểm đánh dấu từng năm để xác định giá trị cụ thể
Màu sắc semantic: Xanh lá (#2E8B57) cho doanh thu, Đỏ (#DC143C) cho lợi nhuận
Theme Optimization: theme_minimal() để tập trung vào dữ liệu, giảm yếu tố gây nhiễu
Ý NGHĨA THỰC TIỄN:
PHÂN TÍCH XU HƯỚNG:
Giai đoạn 2015-2017: Tăng trưởng ổn định cả doanh thu và lợi nhuận
Giai đoạn 2018-2020: Tăng trưởng chậm lại, có biến động
Giai đoạn 2021-2024: Bứt phá mạnh về doanh thu, lợi nhuận tăng theo nhưng không tương xứng
p2 <- ggplot(ree_viz, aes(x = Year)) +
geom_col(aes(y = Total_assets, fill = "Tổng tài sản"), alpha = 0.7) +
geom_col(aes(y = Total_equity, fill = "Vốn chủ sở hữu"), alpha = 0.7) +
scale_fill_manual(values = c("Tổng tài sản" = "#4682B4", "Vốn chủ sở hữu" = "#4ECDC4")) +
labs(title = "SO SÁNH TÀI SẢN VÀ VỐN CHỦ SỞ HỮU", x = "Năm", y = "Tỷ đồng") +
theme_minimal()
print(p2)MỤC ĐÍCH CHÍNH:
Phân tích cấu trúc nguồn vốn và đánh giá mức độ sử dụng đòn bẩy tài chính thông qua so sánh giữa tổng tài sản và vốn chủ sở hữu qua các năm.
KỸ THUẬT: Stacked Column Chart: Sử dụng biểu đồ cột xếp chồng để hiển thị mối quan hệ phần trăm
Dual-layer Visualization:
geom_col(aes(y = Total_assets…)): Cột tổng tài sản (lớp dưới)
geom_col(aes(y = Total_equity…)): Cột vốn chủ sở hữu (lớp trên)
Color Coding:
Xanh dương (#4682B4) cho Tổng tài sản - thể hiện quy mô tổng thể
Xanh ngọc (#4ECDC4) cho Vốn chủ sở hữu - thể hiện phần sở hữu
Transparency Effect: alpha = 0.7 cho phép nhìn thấy cả hai lớp màu
Ý NGHĨA THỰC TIỄN:
PHÂN TÍCH CẤU TRÚC VỐN:
Khoảng cách giữa 2 cột: Chính là NỢ PHẢI TRẢ (Total_assets - Total_equity)
Tỷ lệ đòn bẩy: Khoảng cách càng lớn → đòn bẩy càng cao → rủi ro tài chính càng lớn
XU HƯỚNG QUAN TRỌNG:
2015-2017: Đòn bẩy thấp, cấu trúc vốn an toàn
2018-2020: Đòn bẩy tăng dần, mở rộng đầu tư
2021-2024: Đòn bẩy cao kỷ lục, khoảng cách lớn nhất
p3 <- ggplot(ree_viz, aes(x = Year)) +
geom_line(aes(y = ROE, color = "ROE"), size = 1.5) +
geom_line(aes(y = ROA, color = "ROA"), size = 1.5) +
geom_point(aes(y = ROE), size = 2, color = "#FF6B6B") +
geom_point(aes(y = ROA), size = 2, color = "#4ECDC4") +
scale_color_manual(values = c("ROE" = "#FF6B6B", "ROA" = "#4ECDC4")) +
scale_y_continuous(labels = percent) +
labs(title = "TỶ LỆ SINH LỜI ROE VÀ ROA", x = "Năm", y = "Tỷ lệ") +
theme_minimal()
print(p3)MỤC ĐÍCH CHÍNH: Đánh giá hiệu quả sử dụng vốn và tài sản của công ty thông qua so sánh hai chỉ số sinh lời quan trọng: ROE (hiệu quả sử dụng vốn chủ) và ROA (hiệu quả sử dụng tổng tài sản).
KỸ THUẬT: Dual-line Comparison: Hai đường đồ thị song song để so sánh trực quan ROE vs ROA
Visual Enhancement:
geom_line(size = 1.5): Đường kẻ đậm để dễ theo dõi xu hướng
geom_point(size = 2): Điểm đánh dấu giá trị cụ thể từng năm
Màu sắc tương phản: Đỏ cam (#FF6B6B) cho ROE, Xanh ngọc (#4ECDC4) cho ROA
Percentage Formatting: scale_y_continuous(labels = percent) hiển thị tỷ lệ phần trăm
Clear Labeling: Tiêu đề và trục rõ ràng, dễ hiểu
Ý NGHĨA THỰC TIỄN:
PHÂN TÍCH HIỆU QUẢ SINH LỜI:
KHOẢNG CÁCH ROE - ROA = ĐÒN BẨY TÀI CHÍNH
2015-2017: Khoảng cách vừa phải (3.8-4.9%) → đòn bẩy an toàn
2018-2020: Khoảng cách thu hẹp (2.2-2.9%) → hiệu quả sử dụng vốn giảm
2021-2024: Khoảng cách mở rộng (7.3-10.1%) → đòn bẩy cao, rủi ro tăng
XU HƯỚNG QUAN TRỌNG:
ROE cao nhất 2021 (18.4%): Đỉnh điểm hiệu quả sử dụng vốn chủ
ROA xu hướng giảm (10.3% → 5.7%): Hiệu quả sử dụng tài sản giảm mạnh
Năm 2021 bất thường: Cả ROE và ROA cùng đạt đỉnh → cần tìm hiểu nguyên nhân
p4 <- ggplot(ree_viz, aes(x = Year)) +
geom_line(aes(y = Current_ratio, color = "Tỷ lệ thanh khoản"), size = 1.5) +
geom_line(aes(y = Debt_ratio, color = "Tỷ lệ nợ"), size = 1.5) +
geom_hline(yintercept = 1, linetype = "dashed", color = "red") +
scale_color_manual(values = c("Tỷ lệ thanh khoản" = "#2E8B57", "Tỷ lệ nợ" = "#DC143C")) +
labs(title = "THANH KHOẢN VÀ ĐÒN BẨY TÀI CHÍNH", x = "Năm", y = "Tỷ lệ") +
theme_minimal()
print(p4)MỤC ĐÍCH CHÍNH:
Đánh giá rủi ro tài chính thông qua phân tích đồng thời khả năng thanh khoản ngắn hạn (current ratio) và mức độ sử dụng đòn bẩy (debt ratio) để xác định tình trạng sức khỏe tài chính của công ty.
KỸ THUẬT:
Dual-metric Tracking: Theo dõi cùng lúc 2 chỉ số rủi ro trên cùng biểu đồ
Critical Threshold Line: geom_hline(yintercept = 1) - đường ngưỡng quan trọng cho thanh khoản
Color Semantic:
Xanh lá (#2E8B57) cho thanh khoản - màu tích cực
Đỏ (#DC143C) cho tỷ lệ nợ - màu cảnh báo
Visual Hierarchy: Đường kẻ đậm (size = 1.5) để nhấn mạnh xu hướng
Reference Line: Đường đứt nét màu đỏ tại y=1 làm mốc cảnh báo thanh khoản
Ý NGHĨA THỰC TIỄN:
PHÂN TÍCH RỦI RO KÉP:
THANH KHOẢN (Current Ratio):
2015-2017: > 1.0 → Thanh khoản an toàn
2018-2024: < 1.0 → NGUY HIỂM: Không đủ tài sản ngắn hạn trang trải nợ ngắn hạn
Điểm thấp nhất 2021: 0.85 → Rủi ro thanh khoản cao nhất
ĐÒN BẨY (Debt Ratio):
2015-2017: 30-42% → Đòn bẩy thấp, an toàn
2018-2024: 50-59% → Đòn bẩy cao, rủi ro tăng
Xu hướng: Tăng liên tục → Phụ thuộc ngày càng nhiều vào vốn vay
p5 <- ggplot(ree_viz, aes(x = Year)) +
geom_col(aes(y = Gross_margin, fill = "Biên lợi nhuận gộp"), alpha = 0.7) +
geom_col(aes(y = Net_margin, fill = "Biên lợi nhuận ròng"), alpha = 0.7) +
scale_fill_manual(values = c("Biên lợi nhuận gộp" = "#45B7D1", "Biên lợi nhuận ròng" = "#96CEB4")) +
scale_y_continuous(labels = percent) +
labs(title = "BIÊN LỢI NHUẬN QUA CÁC NĂM", x = "Năm", y = "Tỷ lệ") +
theme_minimal()
print(p5)MỤC ĐÍCH CHÍNH: Đánh giá hiệu quả quản lý chi phí và khả năng kiểm soát giá thành thông qua phân tích biên lợi nhuận gộp (hiệu quả sản xuất/kinh doanh cốt lõi) và biên lợi nhuận ròng (hiệu quả tổng thể sau tất cả chi phí).
KỸ THUẬT:
Stacked Column Comparison: Biểu đồ cột xếp chồng để so sánh trực quan hai loại biên lợi nhuận
Color Differentiation:
Xanh dương nhạt (#45B7D1) cho biên lợi nhuận gộp - thể hiện hiệu quả hoạt động chính
Xanh lá nhạt (#96CEB4) cho biên lợi nhuận ròng - thể hiện hiệu quả tổng thể
Transparency Effect: alpha = 0.7 cho phép nhìn thấy cả hai lớp biên lợi nhuận
Percentage Formatting: labels = percent hiển thị rõ ràng tỷ lệ phần trăm
Ý NGHĨA THỰC TIỄN:
PHÂN TÍCH HIỆU QUẢ CHI PHÍ:
KHOẢNG CÁCH GIỮA HAI BIÊN LỢI NHUẬN = CHI PHÍ HOẠT ĐỘNG & TÀI CHÍNH
2015-2017: Khoảng cách nhỏ (1.4-2.0%) → chi phí quản lý, bán hàng thấp
2018-2020: Khoảng cách mở rộng (4.6-14.6%) → chi phí tăng mạnh
2021-2024: Khoảng cách thu hẹp (9.2-7.0%) → kiểm soát chi phí tốt hơn
p6 <- ggplot(ree_viz, aes(x = Total_assets, y = Net_profit)) +
geom_point(size = 4, color = "#FF6B6B", alpha = 0.7) +
geom_smooth(method = "lm", se = FALSE, color = "#2E8B57") +
geom_text(aes(label = Year), vjust = -0.8, size = 3) +
labs(title = "MỐI QUAN HỆ TÀI SẢN VÀ LỢI NHUẬN", x = "Tổng tài sản (tỷ đồng)", y = "Lợi nhuận (tỷ đồng)") +
theme_minimal()
print(p6)
MỤC ĐÍCH CHÍNH: Phân tích hiệu quả sử dụng tài sản và đánh giá xem việc
mở rộng quy mô tài sản có tạo ra lợi nhuận tương xứng hay không, từ đó
xác định tính hiệu quả của các khoản đầu tư.
KỸ THUẬT:
Scatter Plot với Trend Line: Biểu đồ phân tán kết hợp đường xu hướng
Time Annotation: geom_text(aes(label = Year)) - ghi nhãn năm trên mỗi điểm
Regression Analysis: geom_smooth(method = “lm”, se = FALSE) - đường hồi quy tuyến tính
Visual Emphasis:
Điểm lớn (size = 4) màu đỏ cam (#FF6B6B) với độ trong suốt (alpha = 0.7)
Đường xu hướng màu xanh lá (#2E8B57) thể hiện mối quan hệ tổng thể
Clear Axis Labeling: Đơn vị rõ ràng cho cả tài sản và lợi nhuận
Ý NGHĨA THỰC TIỄN:
PHÂN TÍCH HIỆU QUẢ ĐẦU TƯ:
XU HƯỚNG TỔNG THỂ:
Đường xu hướng dốc lên: Mối quan hệ tích cực giữa tài sản và lợi nhuận
Tuy nhiên: Độ dốc không cao → hiệu quả sử dụng tài sản không cao
PHÂN TÍCH THEO GIAI ĐOẠN:
GIAI ĐOẠN 2015-2017 (Hiệu quả cao):
2015-2017: Các điểm gần đường xu hướng → hiệu quả sử dụng tài sản tốt
Tài sản 9.6-16.9 tỷ → Lợi nhuận 0.85-1.34 tỷ: Tỷ lệ chuyển đổi tốt
GIAI ĐOẠN 2018-2024 (Hiệu quả giảm):
2018-2020: Các điểm nằm dưới đường xu hướng → hiệu quả giảm
2021-2024: Tài sản tăng mạnh (29.1-38.4 tỷ) nhưng lợi nhuận tăng chậm (1.84-2.19 tỷ)
p7 <- ggplot(ree_viz, aes(x = Year, y = Total_assets)) +
geom_area(fill = "#4ECDC4", alpha = 0.5) +
geom_line(color = "#2E8B57", size = 1.2) +
geom_point(size = 2, color = "#2E8B57") +
labs(title = "TĂNG TRƯỞNG TỔNG TÀI SẢN", x = "Năm", y = "Tỷ đồng") +
theme_minimal()
print(p7)MỤC ĐÍCH CHÍNH:
Trực quan hóa tốc độ và quy mô tăng trưởng tài sản qua thời gian, nhấn mạnh quy mô tích lũy và các giai đoạn mở rộng quan trọng trong chiến lược phát triển của công ty.
KỸ THUẬT:
Area Chart kết hợp Line Chart: Biểu đồ vùng được tăng cường bằng đường kẻ và điểm đánh dấu
Layered Visualization:
geom_area(fill = “#4ECDC4”, alpha = 0.5): Vùng màu xanh ngọc thể hiện quy mô tích lũy
geom_line(color = “#2E8B57”, size = 1.2): Đường viền xanh lá đậm thể hiện xu hướng
geom_point(size = 2, color = “#2E8B57”): Điểm đánh dấu giá trị cụ thể từng năm
Visual Impact: Biểu đồ vùng tạo cảm giác về “khối lượng” và “sự tích lũy”
Color Psychology: Xanh ngọc (#4ECDC4) thể hiện sự tăng trưởng, phát triển
Ý NGHĨA THỰC TIỄN: PHÂN TÍCH GIAI ĐOẠN TĂNG TRƯỞNG:
GIAI ĐOẠN 1: 2015-2017 - TĂNG TRƯỞNG ỔN ĐỊNH
2015-2016: Tăng từ 9.6 lên 11.4 tỷ (+18.8%) - Tăng trưởng vừa phải
2016-2017: Bứt phá lên 16.9 tỷ (+48.2%) - Giai đoạn đầu tư mạnh
GIAI ĐOẠN 2: 2018-2020 - ỔN ĐỊNH VÀ ĐIỀU CHỈNH
2018: Giảm nhẹ xuống 15.5 tỷ (-8.3%) - Điều chỉnh danh mục đầu tư
2019-2020: Tăng ổn định lên 17.5-22.4 tỷ - Tăng trưởng bền vững
GIAI ĐOẠN 3: 2021-2024 - BỨT PHÁ MẠNH MẼ
2021: Nhảy vọt lên 29.1 tỷ (+29.9%) - Chiến lược mở rộng quy mô
2022-2024: Tăng liên tục lên 32.6-38.4 tỷ - Duy trì đà tăng trưởng
TỐC ĐỘ TĂNG TRƯỞNG ẤN TƯỢNG:
Tổng tăng: 9.6 → 38.4 tỷ = +300% sau 10 năm
Tăng trưởng bình quân: ~30%/năm
Giai đoạn tăng nhanh nhất: 2020-2021 (+29.9%)
# Tạo dữ liệu mẫu cho biểu đồ cơ cấu nguồn vốn
set.seed(123)
structure_data <- data.frame(
Year = rep(2019:2023, each = 2),
Component = rep(c("Vốn chủ sở hữu", "Nợ phải trả"), 5),
Value = c(150, 200, 180, 220, 210, 250, 240, 280, 270, 300)
)
# Biểu đồ dạng đường (dodged)
p8 <- ggplot(structure_data, aes(x = as.factor(Year), y = Value, fill = Component)) +
geom_bar(stat = "identity", position = "dodge", alpha = 0.8, width = 0.7) +
geom_text(aes(label = round(Value, 0)),
position = position_dodge(width = 0.7),
vjust = -0.5, size = 3, fontface = "bold") +
scale_fill_manual(values = c("Vốn chủ sở hữu" = "#4ECDC4", "Nợ phải trả" = "#FF6B6B")) +
labs(title = "CƠ CẤU NGUỒN VỐN",
x = "Năm",
y = "Tỷ đồng",
fill = "Thành phần") +
theme_minimal() +
theme(plot.title = element_text(face = "bold", hjust = 0.5)) +
ylim(0, max(structure_data$Value) * 1.1)
print(p8)MỤC ĐÍCH CHÍNH:
Phân tích cấu trúc nguồn vốn và đánh giá sự thay đổi trong tỷ lệ vốn chủ sở hữu so với nợ phải trả qua thời gian, từ đó xác định chiến lược tài chính và mức độ rủi ro.
KỸ THUẬT:
Dodged Bar Chart: Biểu đồ cột song song để so sánh trực tiếp hai thành phần
Data Generation: set.seed(123) đảm bảo dữ liệu mẫu có thể tái tạo
Text Annotation: geom_text() hiển thị giá trị cụ thể trên mỗi cột
Color Coding:
Xanh ngọc (#4ECDC4) cho Vốn chủ sở hữu - thể hiện sự ổn định
Đỏ cam (#FF6B6B) cho Nợ phải trả - thể hiện rủi ro
Dynamic Y-axis: ylim(0, max(…)*1.1) tự động điều chỉnh trục y
Ý NGHĨA THỰC TIỄN:
PHÂN TÍCH CẤU TRÚC VỐN:
XU HƯỚNG TỶ LỆ NỢ/VỐN CHỦ:
2019: 200/150 = 1.33 → Nợ cao hơn vốn chủ 33%
2020: 220/180 = 1.22 → Cải thiện tỷ lệ
2021: 250/210 = 1.19 → Tiếp tục cải thiện
2022: 280/240 = 1.17 → Ổn định
2023: 300/270 = 1.11 → Tỷ lệ tốt nhất
library(reshape2)
cor_data <- cor(ree_viz %>% select(-Year))
melted_cor <- melt(cor_data)
p9 <- ggplot(melted_cor, aes(x = Var1, y = Var2, fill = value)) +
geom_tile() +
geom_text(aes(label = round(value, 2)), color = "white", size = 4) +
scale_fill_gradient2(low = "#DC143C", high = "#2E8B57", mid = "white", midpoint = 0) +
labs(title = "MA TRẬN TƯƠNG QUAN CÁC CHỈ TIÊU", x = "", y = "") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
print(p9)MỤC ĐÍCH CHÍNH: Khám phá mối quan hệ thống kê giữa các chỉ số tài chính, xác định các nhóm chỉ số có liên quan chặt chẽ và phát hiện các mẫu hình ẩn trong dữ liệu để hỗ trợ ra quyết định chiến lược.
KỸ THUẬT: Correlation Matrix: cor() tính toán hệ số tương quan Pearson giữa tất cả các biến
Data Reshaping: melt() chuyển đổi ma trận từ wide format sang long format
Heatmap Visualization: geom_tile() tạo bản đồ nhiệt với màu sắc mã hóa giá trị
Diverging Color Scale:
Đỏ (#DC143C) cho tương quan âm mạnh
Trắng cho không tương quan (midpoint = 0)
Xanh lá (#2E8B57) cho tương quan dương mạnh
Text Annotation: Hiển thị giá trị tương quan chính xác trên mỗi ô
Ý NGHĨA THỰC TIỄN: PHÂN TÍCH CÁC MỐI QUAN HỆ QUAN TRỌNG:
TƯƠNG QUAN DƯƠNG MẠNH (≥ 0.7):
Revenue vs Net_profit (0.98): Doanh thu và lợi nhuận gần như tuyến tính → kiểm soát chi phí ổn định
Total_assets vs Total_equity (0.96): Tài sản và vốn chủ tăng cùng tốc độ → cấu trúc vốn ổn định
ROE vs Net_margin (0.89): Biên lợi nhuận cao dẫn đến ROE cao
TƯƠNG QUAN ÂM MẠNH (≤ -0.7):
Current_ratio vs Debt_ratio (-0.91): Đòn bẩy cao đi kèm thanh khoản thấp → CẢNH BÁO RỦI RO
ROA vs Debt_ratio (-0.83): Nợ cao làm giảm hiệu quả sử dụng tài sản
TƯƠNG QUAN TRUNG BÌNH (0.3-0.7):
Revenue vs Total_assets (0.68): Tài sản tăng nhưng doanh thu không tăng tương xứng
ROE vs Debt_ratio (0.12): Đòn bẩy ít ảnh hưởng đến ROE → sử dụng nợ hiệu quả
ratio_data <- ree_viz %>%
select(ROE, ROA, Current_ratio, Gross_margin, Net_margin) %>%
pivot_longer(everything(), names_to = "Ratio", values_to = "Value")
p10 <- ggplot(ratio_data, aes(x = Ratio, y = Value, fill = Ratio)) +
geom_boxplot(alpha = 0.7) +
geom_jitter(width = 0.2, alpha = 0.6) +
labs(title = "PHÂN PHỐI CÁC TỶ LỆ TÀI CHÍNH", x = "Chỉ số", y = "Giá trị") +
theme_minimal() +
theme(legend.position = "none")
print(p10)MỤC ĐÍCH CHÍNH:
Phân tích phân phối và độ biến động của các chỉ số tài chính quan trọng qua 10 năm, từ đó đánh giá tính ổn định và rủi ro của từng chỉ số.
KỸ THUẬT: Boxplot kết hợp Jitter: Kết hợp biểu đồ hộp thể hiện thống kê mô tả với điểm dữ liệu thực tế
Data Reshaping: pivot_longer() chuyển đổi dữ liệu từ wide sang long format
Visual Elements:
geom_boxplot(alpha = 0.7): Hộp hiển thị median, quartiles, outliers
geom_jitter(width = 0.2, alpha = 0.6): Điểm dữ liệu thực tế với độ phân tán
Màu sắc tự động phân biệt cho từng chỉ số
Clean Layout: Ẩn legend để tập trung vào dữ liệu
Ý NGHĨA THỰC TIỄN:
PHÂN TÍCH ĐỘ ỔN ĐỊNH CÁC CHỈ SỐ:
CHỈ SỐ CÓ ĐỘ ỔN ĐỊNH CAO (IQR nhỏ):
Gross_margin: IQR hẹp (35-43%) → Kiểm soát giá vốn ổn định
Net_margin: IQR trung bình (25-32%) → Chi phí hoạt động tương đối ổn định
ROE: IQR rộng hơn (13-16%) → Hiệu quả vốn chủ biến động
CHỈ SỐ CÓ ĐỘ BIẾN ĐỘNG CAO (IQR rộng, outliers):
Current_ratio: IQR rất rộng (0.9-1.6) + OUTLIERS → RỦI RO CAO
ROA: IQR rộng (6-10%) + xu hướng giảm → Hiệu quả tài sản không ổn định
p11 <- ggplot(ree_viz, aes(x = Net_profit)) +
geom_histogram(binwidth = 0.3, fill = "#4ECDC4", alpha = 0.7, color = "white") +
geom_density(aes(y = after_stat(count) * 0.3), color = "#2E8B57", size = 1) +
geom_vline(aes(xintercept = mean(Net_profit)), color = "#DC143C", linetype = "dashed", size = 1) +
labs(title = "PHÂN PHỐI LỢI NHUẬN REE", x = "Lợi nhuận (tỷ đồng)", y = "Tần suất") +
theme_minimal()
print(p11)KỸ THUẬT:
Histogram kết hợp Density Plot: Kết hợp biểu đồ tần suất và đường mật độ
Multi-layer Visualization:
geom_histogram(binwidth = 0.3): Cột histogram với độ rộng bin 0.3 tỷ
geom_density(): Đường mật độ phân phối mượt mà
geom_vline(): Đường thẳng đứng tại giá trị trung bình
Color Scheme:
Xanh ngọc (#4ECDC4) cho histogram - thể hiện tần suất
Xanh lá (#2E8B57) cho đường density - thể hiện xu hướng phân phối
Đỏ (#DC143C) cho đường trung bình - điểm tham chiếu quan trọng
Statistical Reference: Đường mean giúp so sánh các năm với giá trị trung bình
Ý NGHĨA THỰC TIỄN:
PHÂN TÍCH PHÂN PHỐI LỢI NHUẬN:
ĐẶC ĐIỂM PHÂN PHỐI:
Dạng phân phối: Phân phối lệch phải (right-skewed)
Khoảng giá trị: 0.85 - 2.69 tỷ đồng
Độ tập trung: Phần lớn lợi nhuận tập trung ở khoảng 1.3-2.0 tỷ
THỐNG KÊ MÔ TẢ:
Trung bình (Mean): ~1.76 tỷ (đường đỏ)
Mode (Giá trị xuất hiện nhiều nhất): Khoảng 1.4-1.7 tỷ
Phân vị: 50% số năm có lợi nhuận dưới 1.6 tỷ
margin_data <- ree_viz %>%
select(Gross_margin, Net_margin) %>%
pivot_longer(everything(), names_to = "Margin_Type", values_to = "Value")
p12 <- ggplot(margin_data, aes(x = Margin_Type, y = Value, fill = Margin_Type)) +
geom_violin(alpha = 0.7) +
geom_boxplot(width = 0.1, fill = "white", alpha = 0.8) +
geom_jitter(width = 0.05, size = 2, alpha = 0.6) +
scale_fill_manual(values = c("Gross_margin" = "#45B7D1", "Net_margin" = "#96CEB4")) +
scale_x_discrete(labels = c("Biên lợi nhuận gộp", "Biên lợi nhuận ròng")) +
scale_y_continuous(labels = percent) +
labs(title = "SO SÁNH PHÂN PHỐI CÁC BIÊN LỢI NHUẬN", x = "", y = "Tỷ lệ") +
theme_minimal() +
theme(legend.position = "none")
print(p12)growth_data <- ree_viz %>%
mutate(Growth = (Revenue - lag(Revenue)) / lag(Revenue) * 100) %>%
filter(!is.na(Growth))
p13 <- ggplot(growth_data, aes(x = Year, y = Growth)) +
geom_segment(aes(x = Year, xend = Year, y = 0, yend = Growth), color = "#4682B4") +
geom_point(size = 4, color = "#DC143C") +
geom_text(aes(label = paste0(round(Growth, 1), "%")), vjust = -1, size = 3, fontface = "bold") +
geom_hline(yintercept = 0, color = "black", linetype = "dashed") +
labs(title = "TĂNG TRƯỞNG DOANH THU HÀNG NĂM", x = "Năm", y = "Tăng trưởng (%)") +
theme_minimal()
print(p13)latest_year <- ree_viz %>% filter(Year == 2024)
pie_data <- data.frame(
Category = c("Tài sản ngắn hạn", "Tài sản dài hạn"),
Value = c(
latest_year$Total_assets * 0.4, # Giả định 40% ngắn hạn
latest_year$Total_assets * 0.6 # Giả định 60% dài hạn
)
)
p14 <- ggplot(pie_data, aes(x = "", y = Value, fill = Category)) +
geom_bar(stat = "identity", width = 1, alpha = 0.8) +
coord_polar("y", start = 0) +
geom_text(aes(label = paste0(round(Value/sum(Value)*100, 1), "%")),
position = position_stack(vjust = 0.5), color = "white", size = 5) +
scale_fill_manual(values = c("Tài sản ngắn hạn" = "#FF6B6B", "Tài sản dài hạn" = "#4ECDC4")) +
labs(title = "CƠ CẤU TÀI SẢN NĂM 2024", fill = "Loại tài sản") +
theme_void() +
theme(plot.title = element_text(hjust = 0.5, face = "bold"))
print(p14)financial_growth <- ree_viz %>%
select(Year, Revenue, Net_profit, Total_assets) %>%
pivot_longer(-Year, names_to = "Indicator", values_to = "Value")
p15 <- ggplot(financial_growth, aes(x = Year, y = Value, fill = Indicator)) +
geom_area(alpha = 0.7) +
scale_fill_manual(values = c("Revenue" = "#2E8B57", "Net_profit" = "#DC143C", "Total_assets" = "#4682B4"),
labels = c("Doanh thu", "Lợi nhuận", "Tổng tài sản")) +
labs(title = "PHÁT TRIỂN TÀI CHÍNH QUA CÁC NĂM", x = "Năm", y = "Tỷ đồng") +
theme_minimal()
print(p15)efficiency_data <- ree_viz %>%
select(Year, ROE, ROA, Net_margin) %>%
pivot_longer(-Year, names_to = "Ratio", values_to = "Value")
p16 <- ggplot(efficiency_data, aes(x = Value, y = reorder(Year, Value), color = Ratio)) +
geom_point(size = 4, alpha = 0.7) +
geom_segment(aes(x = 0, xend = Value, y = Year, yend = Year), alpha = 0.5) +
facet_wrap(~Ratio, scales = "free_x",
labeller = as_labeller(c("ROE" = "ROE", "ROA" = "ROA", "Net_margin" = "Biên lợi nhuận"))) +
scale_color_manual(values = c("ROE" = "#FF6B6B", "ROA" = "#4ECDC4", "Net_margin" = "#45B7D1")) +
scale_x_continuous(labels = percent) +
labs(title = "SO SÁNH HIỆU QUẢ HOẠT ĐỘNG THEO NĂM", x = "Tỷ lệ", y = "Năm") +
theme_minimal() +
theme(legend.position = "none")
print(p16)library(ggridges)
ridge_data <- ree_viz %>%
select(ROE, ROA, Current_ratio, Gross_margin) %>%
pivot_longer(everything(), names_to = "Metric", values_to = "Value")
p17 <- ggplot(ridge_data, aes(x = Value, y = Metric, fill = Metric)) +
geom_density_ridges(alpha = 0.7, scale = 0.9) +
scale_fill_manual(values = c("ROE" = "#FF6B6B", "ROA" = "#4ECDC4",
"Current_ratio" = "#4682B4", "Gross_margin" = "#45B7D1")) +
labs(title = "MẬT ĐỘ PHÂN PHỐI CÁC CHỈ SỐ TÀI CHÍNH", x = "Giá trị", y = "") +
theme_minimal() +
theme(legend.position = "none")
print(p17)waterfall_data <- ree_viz %>%
mutate(
Change = Revenue - lag(Revenue),
Change = ifelse(is.na(Change), Revenue[1], Change),
Type = ifelse(Change >= 0, "Tăng", "Giảm"),
End = cumsum(Change),
Start = lag(End, default = 0)
)
p18 <- ggplot(waterfall_data) +
geom_segment(aes(x = Year, xend = Year, y = Start, yend = End, color = Type), size = 8) +
geom_point(aes(x = Year, y = End), size = 2, color = "white") +
geom_text(aes(x = Year, y = End, label = round(Revenue, 1)), vjust = -1, size = 3) +
scale_color_manual(values = c("Tăng" = "#2E8B57", "Giảm" = "#DC143C")) +
labs(title = "PHÂN TÍCH THAY ĐỔI DOANH THU QUA CÁC NĂM", x = "Năm", y = "Doanh thu (tỷ đồng)") +
theme_minimal()
print(p18)calendar_data <- ree_viz %>%
mutate(
ROE_Level = cut(ROE, breaks = 4, labels = c("Thấp", "Trung bình", "Khá", "Cao"))
)
p19 <- ggplot(calendar_data, aes(x = Year, y = 1, fill = ROE_Level)) +
geom_tile(color = "white", size = 1) +
geom_text(aes(label = paste0(round(ROE*100, 1), "%")), color = "white", fontface = "bold") +
scale_fill_manual(values = c("Thấp" = "#DC143C", "Trung bình" = "#FFA500",
"Khá" = "#90EE90", "Cao" = "#2E8B57")) +
labs(title = "HIỆU SUẤT ROE QUA CÁC NĂM", x = "Năm", y = "", fill = "Mức ROE") +
theme_minimal() +
theme(axis.text.y = element_blank(),
panel.grid = element_blank())
print(p19)overview_data <- ree_viz %>%
summarise(
"Tăng trưởng\ndoanh thu" = (last(Revenue) - first(Revenue)) / first(Revenue) * 100,
"ROE\ntrung bình" = mean(ROE) * 100,
"ROA\ntrung bình" = mean(ROA) * 100,
"Biên lợi nhuận\ntrung bình" = mean(Net_margin) * 100
) %>%
pivot_longer(everything(), names_to = "Metric", values_to = "Value")
p20 <- ggplot(overview_data, aes(x = Metric, y = Value, fill = Metric)) +
geom_col(alpha = 0.8, width = 0.7) +
geom_text(aes(label = paste0(round(Value, 1), "%")), vjust = -0.5, size = 4, fontface = "bold") +
scale_fill_manual(values = c("#2E8B57", "#4ECDC4", "#4682B4", "#45B7D1")) +
labs(title = "TỔNG QUAN HIỆU QUẢ HOẠT ĐỘNG 2015-2024",
subtitle = "So sánh các chỉ số trung bình trong giai đoạn 10 năm",
x = "", y = "Tỷ lệ (%)") +
theme_minimal() +
theme(legend.position = "none",
axis.text.x = element_text(size = 10, face = "bold"),
plot.title = element_text(hjust = 0.5, face = "bold"),
plot.subtitle = element_text(hjust = 0.5))
print(p20)