# Load các thư viện cần thiết
library(dplyr)
library(ggplot2)
library(lubridate)
library(stringr)
library(tidyr)
library(scales)
library(knitr)
library(patchwork)
library(ggExtra)
library(ggpattern)df <- read.csv("Orders_Sales_Data.csv", sep = ";")
knitr::kable(head(df, 4 ), caption = "4 dòng đầu của dataset")| Order_ID | Customer_ID | Customer_Type | Product | Category | Unit_Price | Quantity | Discount | Total_Price | Region | Order_Date |
|---|---|---|---|---|---|---|---|---|---|---|
| ORD1 | CUS1496 | B2B | Vio Wasser | Water | 1.66 | 53 | 0.10 | 79.18 | Baden-Württemberg | 23/08/2023 |
| ORD1 | CUS1496 | B2B | Evian | Water | 1.56 | 90 | 0.10 | 126.36 | Baden-Württemberg | 23/08/2023 |
| ORD1 | CUS1496 | B2B | Sprite | Soft Drinks | 1.17 | 73 | 0.05 | 81.14 | Baden-Württemberg | 23/08/2023 |
| ORD1 | CUS1496 | B2B | Rauch Multivitamin | Juices | 3.22 | 59 | 0.10 | 170.98 | Baden-Württemberg | 23/08/2023 |
🧩 Giải thích code R
Mục đích: Đoạn code này dùng để đọc một tệp dữ liệu CSV và hiển thị 4 dòng đầu tiên của tệp đó dưới dạng bảng có định dạng rõ ràng.
Cách thực hiện:
read.csv(): Tải dữ liệu từ file “Orders_Sales_Data.csv”. Tham số sep = “;” chỉ định rằng các cột trong file được ngăn cách bởi dấu chấm phẩy.
knitr::kable(head(df, 4), …): Lấy 4 dòng đầu tiên của dữ liệu (head(df, 4)) và dùng hàm kable để định dạng chúng thành một bảng HTML đẹp mắt, kèm theo tiêu đề (caption).
📊 Phân tích Kết quả
dim_df <- data.frame(
"Số dòng" = nrow(df),
"Số cột" = ncol(df)
)
knitr::kable(dim_df, caption = "Kích thước dataset")| Số.dòng | Số.cột |
|---|---|
| 1048575 | 11 |
🧩 Giải thích Code R (Kích thước dataset)
Mục đích: Tạo một bảng tóm tắt kích thước (số dòng và số cột) của một bộ dữ liệu có tên là df.
Cách thực hiện:
📊 Phân tích Kết quả (Bảng kích thước)
| Tên.cột |
|---|
| Order_ID |
| Customer_ID |
| Customer_Type |
| Product |
| Category |
| Unit_Price |
| Quantity |
| Discount |
| Total_Price |
| Region |
| Order_Date |
🧩 Phân tích Code R
📊 Phân tích Kết quả
str_df <- data.frame(
"Cột" = names(df),
"Kiểu dữ liệu" = sapply(df, class)
)
knitr::kable(str_df, caption = "Cấu trúc dataset - Kiểu dữ liệu")| Cột | Kiểu.dữ.liệu | |
|---|---|---|
| Order_ID | Order_ID | character |
| Customer_ID | Customer_ID | character |
| Customer_Type | Customer_Type | character |
| Product | Product | character |
| Category | Category | character |
| Unit_Price | Unit_Price | numeric |
| Quantity | Quantity | integer |
| Discount | Discount | numeric |
| Total_Price | Total_Price | numeric |
| Region | Region | character |
| Order_Date | Order_Date | character |
🧩 Giải thích Code R
Mục đích: Đoạn code này dùng để kiểm tra cấu trúc dữ liệu của một dataframe tên là df.
Cách thực hiện:
Tạo một dataframe mới (str_df).
Lấy tên của tất cả các cột trong df (dùng names()) và gán vào cột “Cột”.
Lấy kiểu dữ liệu (dùng class) của từng cột (dùng sapply()) và gán vào cột “Kiểu dữ liệu”.
Sử dụng knitr::kable() để định dạng dataframe str_df thành một bảng HTML gọn gàng với tiêu đề “Cấu trúc dataset - Kiểu dữ liệu”.
📊 Phân tích Kết quả
# Lấy thống kê tổng quan cho các cột số
summary_numeric <- summary(df %>% select(where(is.numeric)))
knitr::kable(summary_numeric, caption = "Thống kê tổng quan các cột số")| Unit_Price | Quantity | Discount | Total_Price | |
|---|---|---|---|---|
| Min. : 0.320 | Min. : 1.00 | Min. :0.00000 | Min. : 0.30 | |
| 1st Qu.: 1.050 | 1st Qu.: 6.00 | 1st Qu.:0.00000 | 1st Qu.: 8.40 | |
| Median : 1.750 | Median : 11.00 | Median :0.00000 | Median : 21.14 | |
| Mean : 5.847 | Mean : 23.14 | Mean :0.02973 | Mean : 130.98 | |
| 3rd Qu.: 3.210 | 3rd Qu.: 30.00 | 3rd Qu.:0.05000 | 3rd Qu.: 69.77 | |
| Max. :160.440 | Max. :100.00 | Max. :0.15000 | Max. :12682.78 |
🧩 Phân tích Code R
Mục đích : Cung cấp tóm tắt thống kê 5 số (Min, Q1, Median, Q3, Max) và giá trị Trung bình (Mean) cho tất cả các cột có kiểu dữ liệu là số (numeric).
Giải thích Khối:
df %>% select(where(is.numeric)): Bắt đầu với df, chỉ chọn (select) các cột có kiểu dữ liệu là số (is.numeric).
summary(…): Áp dụng hàm thống kê tóm tắt lên các cột vừa được chọn.
📊 Phân tích Kết quả
missing_data <- data.frame(
"Cột" = names(df),
"Số giá trị missing" = colSums(is.na(df))
)
knitr::kable(missing_data, caption = "Dữ liệu missing")| Cột | Số.giá.trị.missing | |
|---|---|---|
| Order_ID | Order_ID | 0 |
| Customer_ID | Customer_ID | 0 |
| Customer_Type | Customer_Type | 0 |
| Product | Product | 0 |
| Category | Category | 0 |
| Unit_Price | Unit_Price | 0 |
| Quantity | Quantity | 0 |
| Discount | Discount | 0 |
| Total_Price | Total_Price | 0 |
| Region | Region | 0 |
| Order_Date | Order_Date | 0 |
🧩 Giải thích Code R
Mục đích: Tạo một bảng thống kê số lượng giá trị bị thiếu (missing/NA) cho từng cột trong bộ dữ liệu df.
Cách thực hiện:
is.na(df): Kiểm tra xem mỗi ô trong df có phải là NA không.
colSums(): Đếm tổng số giá trị NA theo từng cột.
data.frame(): Tạo một bảng dữ liệu mới gồm 2 cột: “Cột” và “Số giá trị missing” .
knitr::kable(): Định dạng và hiển thị bảng missing_data dưới dạng HTML với tiêu đề “Dữ liệu missing”.
📊 Phân tích Kết quả
duplicate_info <- data.frame(
"Số dòng trùng lặp" = sum(duplicated(df))
)
knitr::kable(duplicate_info, caption = "Kiểm tra dữ liệu trùng lặp")| Số.dòng.trùng.lặp |
|---|
| 0 |
🧩 Giải thích Code R
Mục đích: Đếm tổng số dòng bị trùng lặp hoàn toàn (duplicate rows) trong bộ dữ liệu df.
Cách thực hiện: Code sử dụng hàm duplicated(df) để tìm các dòng lặp lại, sau đó dùng sum() để đếm tổng số lượng dòng đó.
📊 Phân tích Kết quả
unique_counts <- data.frame(
"Đối tượng" = c("Order_ID", "Customer_ID", "Product", "Category"),
"Số lượng duy nhất" = c(
n_distinct(df$Order_ID),
n_distinct(df$Customer_ID),
n_distinct(df$Product),
n_distinct(df$Category)
)
)
knitr::kable(unique_counts, caption = "Số lượng duy nhất")| Đối.tượng | Số.lượng.duy.nhất |
|---|---|
| Order_ID | 349193 |
| Customer_ID | 10000 |
| Product | 47 |
| Category | 4 |
🧩 Phân tích Code R
Mục đích: Đoạn code này dùng để đếm số lượng giá trị duy nhất (không trùng lặp) cho 4 cột cụ thể trong data frame df và trình bày kết quả dưới dạng bảng.
Cách thực hiện:
Sử dụng hàm n_distinct() (thường từ gói dplyr) để đếm số lượng giá trị duy nhất cho mỗi cột: Order_ID, Customer_ID, Product, và Category.
Tạo một data.frame mới tên là unique_counts để lưu trữ kết quả, với một cột là tên “Đối tượng” và cột kia là “Số lượng duy nhất”.
Sử dụng knitr::kable() để định dạng data.frame này thành một bảng HTML, đặt tiêu đề cho bảng là “Số lượng duy nhất”.
📊 Phân tích Kết quả
# Thống kê mô tả cho các cột số
numeric_summary <- df %>%
select(where(is.numeric)) %>%
gather() %>%
group_by(key) %>%
summarise(
Min = min(value, na.rm = TRUE),
Q1 = quantile(value, 0.25, na.rm = TRUE),
Median = median(value, na.rm = TRUE),
Mean = mean(value, na.rm = TRUE),
Q3 = quantile(value, 0.75, na.rm = TRUE),
Max = max(value, na.rm = TRUE),
SD = sd(value, na.rm = TRUE)
)
knitr::kable(numeric_summary, caption = "Phân tích chi tiết các cột số")| key | Min | Q1 | Median | Mean | Q3 | Max | SD |
|---|---|---|---|---|---|---|---|
| Discount | 0.00 | 0.00 | 0.00 | 0.0297332 | 0.05 | 0.15 | 0.0447929 |
| Quantity | 1.00 | 6.00 | 11.00 | 23.1439935 | 30.00 | 100.00 | 26.8839927 |
| Total_Price | 0.30 | 8.40 | 21.14 | 130.9783482 | 69.77 | 12682.78 | 510.9053793 |
| Unit_Price | 0.32 | 1.05 | 1.75 | 5.8467548 | 3.21 | 160.44 | 14.7834629 |
🧩 Phân tích Code R
Mục đích: Tính toán các chỉ số thống kê mô tả cơ bản (như min, max, trung bình, trung vị) cho tất cả các cột dữ liệu dạng số (numeric) trong dataframe df.
Cách thực hiện:
select(where(is.numeric)): Tự động chọn tất cả các cột có kiểu dữ liệu là số.
gather(): Chuyển đổi (pivot) cấu trúc dữ liệu từ dạng “rộng” (nhiều cột) sang dạng “dài” (hai cột key và value), giúp dễ dàng tính toán theo nhóm. key là tên cột gốc (ví dụ: “Quantity”), value là giá trị của cột đó.
group_by(key) và summarise(…): Nhóm dữ liệu theo tên cột gốc (key) và sau đó tính toán 7 chỉ số thống kê mô tả (Min, Q1, Median, Mean, Q3, Max, SD) cho mỗi nhóm.
📊 Phân tích Kết quả
set.seed(123)
random_rows <- df %>% sample_n(4)
knitr::kable(random_rows, caption = "4 dòng dữ liệu ngẫu nhiên")| Order_ID | Customer_ID | Customer_Type | Product | Category | Unit_Price | Quantity | Discount | Total_Price | Region | Order_Date |
|---|---|---|---|---|---|---|---|---|---|---|
| ORD322780 | CUS8894 | B2C | San Pellegrino | Water | 0.91 | 15 | 0.00 | 13.65 | Niedersachsen | 30/08/2023 |
| ORD62889 | CUS6575 | B2C | Tomato Juice | Juices | 3.33 | 2 | 0.00 | 6.66 | Thüringen | 03/05/2022 |
| ORD44667 | CUS5904 | B2B | San Pellegrino | Water | 0.64 | 3 | 0.05 | 1.82 | Saarland | 10/09/2021 |
| ORD41342 | CUS4945 | B2C | Fritz-Kola | Soft Drinks | 2.12 | 5 | 0.00 | 10.60 | Baden-Württemberg | 15/05/2023 |
🧩 Phân tích Code R
Mục đích: Trích xuất và hiển thị ngẫu nhiên 4 dòng dữ liệu từ bộ dữ liệu chính (df) để kiểm tra nhanh cấu trúc và nội dung.
Cách thực hiện:
set.seed(123): Đảm bảo rằng việc chọn ngẫu nhiên luôn cho ra cùng 4 dòng giống nhau mỗi khi chạy lại code (để kết quả có thể tái lặp).
sample_n(4): Sử dụng hàm (từ dplyr) để lấy ra 4 hàng ngẫu nhiên từ df.
knitr::kable(): Sử dụng hàm (từ knitr) để định dạng 4 hàng vừa chọn thành một bảng HTML rõ ràng, dễ đọc với tiêu đề được chỉ định.
📊 Phân tích Kết quả
# 1. Chuyển đổi định dạng ngày tháng
df$Order_Date <- as.Date(df$Order_Date, format = "%d/%m/%Y")
date_conversion <- data.frame(
"Cột" = "Order_Date",
"Kiểu dữ liệu mới" = class(df$Order_Date),
"Giá trị đầu tiên" = as.character(head(df$Order_Date, 1))
)
kable(date_conversion, caption = "Chuyển đổi định dạng ngày tháng")| Cột | Kiểu.dữ.liệu.mới | Giá.trị.đầu.tiên |
|---|---|---|
| Order_Date | Date | 2023-08-23 |
🧩 Giải thích Code R
Mục đích: Chuyển đổi cột Order_Date sang định dạng Ngày chuẩn để R có thể hiểu và thực hiện các phép toán liên quan đến thời gian.
Cách thực hiện:
as.Date(…, format = “%d/%m/%Y”): Hàm này đọc cột Order_Date và thông báo cho R rằng định dạng gốc của ngày tháng là “Ngày/Tháng/Năm” (ví dụ: “23/08/2023”).
data.frame(…): Tạo một bảng tóm tắt nhỏ để kiểm tra. Bảng này ghi lại tên cột vừa đổi, kiểu dữ liệu mới (class()) và giá trị đầu tiên (head()) sau khi chuyển đổi.
kable(…): Hiển thị bảng tóm tắt đó dưới dạng HTML đẹp mắt.
📊 Phân tích Kết quả (Bảng chuyển đổi)
# 2. Trích xuất thông tin thời gian
df$Order_Year <- year(df$Order_Date)
df$Order_Month <- month(df$Order_Date)
df$Order_Quarter <- quarter(df$Order_Date)
df$Order_Day <- day(df$Order_Date)
df$Order_Weekday <- weekdays(df$Order_Date)
time_extraction <- data.frame(
"Order_Date" = head(df$Order_Date, 5),
"Order_Year" = head(df$Order_Year, 5),
"Order_Month" = head(df$Order_Month, 5),
"Order_Quarter" = head(df$Order_Quarter, 5),
"Order_Day" = head(df$Order_Day, 5),
"Order_Weekday" = head(df$Order_Weekday, 5)
)
kable(time_extraction, caption = "Trích xuất thông tin thời gian (5 dòng đầu)")| Order_Date | Order_Year | Order_Month | Order_Quarter | Order_Day | Order_Weekday |
|---|---|---|---|---|---|
| 2023-08-23 | 2023 | 8 | 3 | 23 | Thứ Tư |
| 2023-08-23 | 2023 | 8 | 3 | 23 | Thứ Tư |
| 2023-08-23 | 2023 | 8 | 3 | 23 | Thứ Tư |
| 2023-08-23 | 2023 | 8 | 3 | 23 | Thứ Tư |
| 2023-08-23 | 2023 | 8 | 3 | 23 | Thứ Tư |
🧩 Giải thích Code R
Mục đích: Trích xuất các thành phần thời gian chi tiết (như năm, tháng, ngày, thứ…) từ cột Order_Date và hiển thị 5 dòng đầu tiên của kết quả để kiểm tra.
Cách thực hiện:
Sử dụng các hàm year(), month(), quarter(), day(), và weekdays() (thường từ thư viện lubridate hoặc base R) để “bóc tách” thông tin từ cột df$Order_Date.
Lưu kết quả vào 5 cột mới trong dataframe df.
Tạo một dataframe tạm thời chỉ chứa 5 dòng đầu tiên của các cột này.
Sử dụng kable() để trình bày 5 dòng mẫu này dưới dạng bảng HTML có tiêu đề.
📊 Phân tích Kết quả (Bảng trích xuất)
# 3. Tạo cột doanh thu tính toán
df$Calculated_Revenue <- df$Unit_Price * df$Quantity
revenue_calculation <- data.frame(
"Unit_Price" = head(df$Unit_Price, 5),
"Quantity" = head(df$Quantity, 5),
"Calculated_Revenue" = head(df$Calculated_Revenue, 5)
)
kable(revenue_calculation, caption = "Tạo cột doanh thu tính toán (5 dòng đầu)")| Unit_Price | Quantity | Calculated_Revenue |
|---|---|---|
| 1.66 | 53 | 87.98 |
| 1.56 | 90 | 140.40 |
| 1.17 | 73 | 85.41 |
| 3.22 | 59 | 189.98 |
| 0.87 | 35 | 30.45 |
🧩 Phân tích Code R
Mục đích: Thêm một cột mới tên là Calculated_Revenue (Doanh thu tính toán) vào bộ dữ liệu df và hiển thị 5 dòng đầu tiên để kiểm tra kết quả.
Cách thực hiện:
Tạo cột mới Calculated_Revenue bằng cách nhân hai cột có sẵn: df\(Unit_Price (Đơn giá) và df\)Quantity (Số lượng).
Tạo một data frame tạm thời (revenue_calculation) chỉ chứa 3 cột (Unit_Price, Quantity, và Calculated_Revenue mới) từ 5 dòng đầu tiên (head) của df.
kable(…): Định dạng data frame tạm thời này thành một bảng HTML để xem trước, với tiêu đề “Tạo cột doanh thu tính toán”.
📊 Phân tích Kết quả
# 4. Kiểm tra giá trị âm
negative_check <- data.frame(
"Kiểu giá trị" = c("Số lượng", "Đơn giá", "Doanh thu tính toán"),
"Số lượng giá trị âm" = c(
sum(df$Quantity < 0),
sum(df$Unit_Price < 0),
sum(df$Calculated_Revenue < 0)
)
)
kable(negative_check, caption = "Kiểm tra giá trị âm")| Kiểu.giá.trị | Số.lượng.giá.trị.âm |
|---|---|
| Số lượng | 0 |
| Đơn giá | 0 |
| Doanh thu tính toán | 0 |
🧩 Phân tích Code R
Mục đích: Đoạn code này được dùng để kiểm tra tính hợp lệ của dữ liệu, cụ thể là tìm kiếm các giá trị âm (nhỏ hơn 0) trong ba cột số liệu quan trọng: Quantity (Số lượng), Unit_Price (Đơn giá), và Calculated_Revenue (Doanh thu tính toán).
Cách thực hiện:
Tạo một data frame mới tên là negative_check.
Sử dụng hàm sum() kết hợp với điều kiện logic (ví dụ: df$Quantity < 0) để đếm số lượng bản ghi (rows) có giá trị âm trong từng cột.
Dùng kable() để định dạng kết quả thành bảng HTML với tiêu đề “Kiểm tra giá trị âm”
📊 Phân tích Kết quả
# 5. Chuẩn hóa dữ liệu văn bản
df$Product <- str_to_upper(str_trim(df$Product))
df$Category <- str_to_title(str_trim(df$Category))
df$Region <- str_to_title(str_trim(df$Region))
text_standardization <- data.frame(
"Product" = head(df$Product, 5),
"Category" = head(df$Category, 5),
"Region" = head(df$Region, 5)
)
kable(text_standardization, caption = "Chuẩn hóa dữ liệu văn bản (5 dòng đầu)")| Product | Category | Region |
|---|---|---|
| VIO WASSER | Water | Baden-Württemberg |
| EVIAN | Water | Baden-Württemberg |
| SPRITE | Soft Drinks | Baden-Württemberg |
| RAUCH MULTIVITAMIN | Juices | Baden-Württemberg |
| GEROLSTEINER | Water | Baden-Württemberg |
🧩 Phân tích Code R
Mục đích: Làm sạch và đồng nhất (chuẩn hóa) định dạng dữ liệu văn bản ở ba cột (Product, Category, Region) để đảm bảo tính nhất quán, sau đó hiển thị 5 dòng đầu tiên để kiểm tra.
Cách thực hiện:
str_trim(): Loại bỏ các khoảng trắng (spaces) thừa ở đầu và cuối chuỗi cho cả ba cột.
str_to_upper(): Chuyển đổi tất cả văn bản trong cột Product thành VIẾT HOA.
str_to_title(): Chuyển đổi văn bản trong cột Category và Region sang dạng Viết Hoa Chữ Cái Đầu .
head(…, 5) và kable(): Tạo một data frame mới chỉ chứa 5 dòng đầu tiên của dữ liệu đã làm sạch và trình bày nó dưới dạng bảng HTML có tiêu đề.
📊 Phân tích Kết quả
# 6. Phân loại mùa
df$Season <- case_when(
df$Order_Month %in% c(12, 1, 2) ~ "Winter",
df$Order_Month %in% c(3, 4, 5) ~ "Spring",
df$Order_Month %in% c(6, 7, 8) ~ "Summer",
df$Order_Month %in% c(9, 10, 11) ~ "Autumn"
)
season_classification <- data.frame(
"Order_Month" = head(df$Order_Month, 5),
"Season" = head(df$Season, 5)
)
kable(season_classification, caption = "Phân loại mùa (5 dòng đầu)")| Order_Month | Season |
|---|---|
| 8 | Summer |
| 8 | Summer |
| 8 | Summer |
| 8 | Summer |
| 8 | Summer |
🧩 Phân tích Code R
Mục đích: Phân loại các đơn hàng vào 4 mùa (Winter, Spring, Summer, Autumn) dựa trên tháng đặt hàng (Order_Month) và hiển thị 5 ví dụ phân loại đầu tiên.
Cách thực hiện:
case_when(): Tạo một cột mới tên là Season trong df. Code này gán giá trị “Winter” (Đông) cho tháng 12, 1, 2; “Spring” (Xuân) cho 3, 4, 5; “Summer” (Hè) cho 6, 7, 8; và “Autumn” (Thu) cho 9, 10, 11.
data.frame() + head(): Tạo một bảng dữ liệu (season_classification) mới, chỉ lấy 5 dòng đầu tiên của cột Order_Month và cột Season vừa tạo để xem trước.
kable(): Hiển thị bảng 5 dòng này dưới dạng HTML với tiêu đề “Phân loại mùa (5 dòng đầu)”.
📊 Phân tích Kết quả
# 7. Phân loại giá sản phẩm
df$Price_Category <- cut(df$Unit_Price,
breaks = c(0, 2, 5, 20, Inf),
labels = c("Low Price", "Medium Price", "High Price", "Premium Price"),
right = FALSE)
price_category <- data.frame(
"Unit_Price" = head(df$Unit_Price, 5),
"Price_Category" = head(df$Price_Category, 5)
)
kable(price_category, caption = "Phân loại giá sản phẩm (5 dòng đầu)")| Unit_Price | Price_Category |
|---|---|
| 1.66 | Low Price |
| 1.56 | Low Price |
| 1.17 | Low Price |
| 3.22 | Medium Price |
| 0.87 | Low Price |
🧩 Phân tích Code R
Mục đích: Phân loại các sản phẩm vào 4 nhóm giá khác nhau (“Low”, “Medium”, “High”, “Premium”) dựa trên cột Unit_Price.
Cách thực hiện: Code sử dụng hàm cut để tạo một cột mới là Price_Category. Nó chia giá trị Unit_Price theo các khoảng:
[0, 2): “Low Price”
[2, 5): “Medium Price”
[5, 20): “High Price”
[20, Inf): “Premium Price”
📊 Phân tích Kết quả
# 8. Tạo ID đơn giản
df$Customer_Simple_ID <- as.numeric(factor(df$Customer_ID))
df$Product_Simple_ID <- as.numeric(factor(df$Product))
simple_id <- data.frame(
"Customer_ID" = head(df$Customer_ID, 5),
"Customer_Simple_ID" = head(df$Customer_Simple_ID, 5),
"Product" = head(df$Product, 5),
"Product_Simple_ID" = head(df$Product_Simple_ID, 5)
)
kable(simple_id, caption = "Tạo ID đơn giản (5 dòng đầu)")| Customer_ID | Customer_Simple_ID | Product | Product_Simple_ID |
|---|---|---|---|
| CUS1496 | 554 | VIO WASSER | 43 |
| CUS1496 | 554 | EVIAN | 10 |
| CUS1496 | 554 | SPRITE | 39 |
| CUS1496 | 554 | RAUCH MULTIVITAMIN | 30 |
| CUS1496 | 554 | GEROLSTEINER | 13 |
🧩 Phân tích Code R
Mục đích: Chuyển đổi các cột ID dạng chữ (text/string) như Customer_ID và Product sang dạng ID số (numeric) đơn giản. ID số thường hiệu quả hơn cho các thuật toán ).
Cách thực hiện:
Sử dụng as.numeric(factor(…)) để tạo ra một con số nguyên duy nhất cho mỗi giá trị text duy nhất. Ví dụ: “CUS1496” trở thành 554, “VIO WASSER” trở thành 43.
Lưu các ID số mới này vào hai cột mới là Customer_Simple_ID và Product_Simple_ID.
Tạo một data.frame (simple_id) chỉ chứa 5 dòng đầu tiên để hiển thị song song ID cũ và ID mới.
📊 Phân tích Kết quả
Diễn giải kết quả:Diễn giải: Bảng kết quả cho thấy 5 dòng đầu tiên của dữ liệu. Ta thấy:
Khách hàng “CUS1496” luôn được ánh xạ (map) sang ID số là 554.
Mỗi sản phẩm khác nhau (VIO WASSER, EVIAN, SPRITE…) được ánh xạ sang một ID số khác nhau (43, 10, 39…).
# 9. Phân tích kích thước đơn hàng
order_size <- df %>%
group_by(Order_ID) %>%
summarise(Order_Size = sum(Quantity))
df <- df %>%
left_join(order_size, by = "Order_ID")
df$Order_Size_Category <- cut(df$Order_Size,
breaks = c(0, 10, 50, Inf),
labels = c("Small", "Medium", "Large"),
right = FALSE)
order_size_example <- data.frame(
"Order_ID" = head(df$Order_ID, 5),
"Order_Size" = head(df$Order_Size, 5),
"Order_Size_Category" = head(df$Order_Size_Category, 5)
)
kable(order_size_example, caption = "Phân tích kích thước đơn hàng (5 dòng đầu)")| Order_ID | Order_Size | Order_Size_Category |
|---|---|---|
| ORD1 | 310 | Large |
| ORD1 | 310 | Large |
| ORD1 | 310 | Large |
| ORD1 | 310 | Large |
| ORD1 | 310 | Large |
🧩 Phân tích Code R
Mục đích: Tính toán tổng số lượng sản phẩm cho mỗi đơn hàng (kích thước đơn hàng), sau đó phân loại các đơn hàng thành các nhóm “Small”, “Medium”, “Large” dựa trên tổng số lượng đó.
Cách thực hiện:
Tính toán: Sử dụng group_by(Order_ID) và summarise(Order_Size = sum(Quantity)) để tạo một bảng order_size chứa tổng số lượng (Order_Size) cho mỗi Order_ID duy nhất.
Gia nhập (Join): Dùng left_join để gán giá trị Order_Size (tổng kích thước) tương ứng vào lại mỗi dòng trong bảng df gốc dựa trên Order_ID.
Phân loại: Dùng hàm cut để tạo cột mới Order_Size_Category. Các đơn hàng được gán nhãn:
Small: 0–9 sản phẩm
Medium: 10–49 sản phẩm
Large: 50 sản phẩm trở lên
📊 Phân tích Kết quả
Diễn giải kết quả: Bảng kết quả hiển thị 5 dòng đầu tiên trong bộ dữ liệu, không phải 5 đơn hàng khác nhau. Tất cả 5 dòng này đều thuộc cùng một đơn hàng là ORD1.Hàm left_join đã hoạt động chính xác:
Đơn hàng ORD1 có tổng số lượng sản phẩm (Order_Size) là 310.
Vì 310 lớn hơn 50, đơn hàng này được phân loại chính xác là “Large”.
processing_summary <- data.frame(
"Thao tác xử lý" = c(
"Chuyển đổi ngày tháng",
"Trích xuất thời gian",
"Tạo cột doanh thu",
"Chuẩn hóa văn bản",
"Phân loại mùa",
"Phân loại giá",
"Tạo ID đơn giản",
"Phân tích kích thước đơn"
),
"Trạng thái" = "Hoàn thành",
"Số cột mới tạo" = c(1, 5, 1, 0, 1, 1, 2, 2)
)
kable(processing_summary, caption = "Tổng quan kết quả xử lý dữ liệu")| Thao.tác.xử.lý | Trạng.thái | Số.cột.mới.tạo |
|---|---|---|
| Chuyển đổi ngày tháng | Hoàn thành | 1 |
| Trích xuất thời gian | Hoàn thành | 5 |
| Tạo cột doanh thu | Hoàn thành | 1 |
| Chuẩn hóa văn bản | Hoàn thành | 0 |
| Phân loại mùa | Hoàn thành | 1 |
| Phân loại giá | Hoàn thành | 1 |
| Tạo ID đơn giản | Hoàn thành | 2 |
| Phân tích kích thước đơn | Hoàn thành | 2 |
🧩 Phân tích Code R
Mục đích: Tạo một bảng tóm tắt (data frame) theo dõi trạng thái và kết quả của các bước tiền xử lý dữ liệu.
Cách thực hiện:
data.frame(): Xây dựng một bảng dữ liệu (data frame) thủ công.
Tạo 3 cột: Thao tác xử lý, Trạng thái (gán cứng giá trị “Hoàn thành” cho tất cả), và Số cột mới tạo (liệt kê số cột được thêm vào tương ứng với mỗi thao tác).
📊 Phân tích Kết quả
## 1. TỔNG QUAN DOANH THU VÀ SỐ LƯỢNG:
total_stats <- df %>%
summarise(
Total_Revenue = sum(Calculated_Revenue),
Total_Quantity = sum(Quantity),
Avg_Unit_Price = mean(Unit_Price),
Total_Orders = n_distinct(Order_ID),
Avg_Items_Per_Order = mean(Order_Size)
)
print(total_stats)## Total_Revenue Total_Quantity Avg_Unit_Price Total_Orders Avg_Items_Per_Order
## 1 148580063 24268213 5.846755 349193 84.91937
🧩 Phân tích Code R
Mục đích: Tính toán các chỉ số hiệu suất kinh doanh (KPI) chính, tóm tắt toàn bộ bộ dữ liệu df thành một hàng duy nhất.
Cách thực hiện:
Sử dụng cú pháp %>% (pipe) và hàm summarise() (của thư viện dplyr) để thực hiện các phép tính gộp.
Tính 5 chỉ số: Tổng doanh thu (sum), Tổng số lượng bán ra (sum), Đơn giá trung bình của sản phẩm (mean), Tổng số đơn hàng duy nhất (n_distinct), và Số lượng mặt hàng trung bình trên mỗi đơn (mean của cột Order_Size đã tính trước đó).
📊 Phân tích Kết quả
##
## 2. DOANH THU THEO NĂM:
revenue_by_year <- df %>%
group_by(Order_Year) %>%
summarise(
Revenue = sum(Calculated_Revenue),
Orders = n_distinct(Order_ID),
Avg_Order_Value = Revenue / Orders
) %>%
arrange(Order_Year)
print(revenue_by_year)## # A tibble: 3 × 4
## Order_Year Revenue Orders Avg_Order_Value
## <dbl> <dbl> <int> <dbl>
## 1 2021 47922604. 116043 413.
## 2 2022 49821461. 116863 426.
## 3 2023 50835997. 116287 437.
🧩 Phân tích Code R
Mục đích: Tính toán và tóm tắt các chỉ số kinh doanh chính (Doanh thu, số lượng đơn hàng, giá trị đơn hàng trung bình) theo từng năm.
Cách thực hiện:
Sử dụng dplyr (thông qua %>%), code bắt đầu bằng cách nhóm (group_by) toàn bộ dữ liệu theo cột Order_Year.
Dùng summarise() để tính toán các giá trị tổng hợp cho mỗi năm:Revenue: Tính tổng doanh thu bằng cách sum() cột Calculated_Revenue, Orders: Đếm số lượng đơn hàng duy nhất bằng n_distinct() của Order_ID, Avg_Order_Value: Tính giá trị trung bình đơn bằng cách lấy Revenue chia cho Orders.
Cuối cùng, arrange() sắp xếp kết quả theo thứ tự năm tăng dần.
📊 Phân tích Kết quả
##
## 3. TOP 10 SẢN PHẨM DOANH THU CAO NHẤT:
top_products_revenue <- df %>%
group_by(Product, Category) %>%
summarise(
Total_Revenue = sum(Calculated_Revenue),
Total_Quantity = sum(Quantity),
Avg_Price = mean(Unit_Price)
) %>%
arrange(desc(Total_Revenue)) %>%
head(10)
print(top_products_revenue)## # A tibble: 10 × 5
## # Groups: Product [10]
## Product Category Total_Revenue Total_Quantity Avg_Price
## <chr> <chr> <dbl> <int> <dbl>
## 1 VEUVE CLICQUOT Alcoholic Beverages 26010133. 316620 94.1
## 2 MOËT & CHANDON Alcoholic Beverages 21981229. 308477 62.4
## 3 JOHNNIE WALKER Alcoholic Beverages 12089790. 305202 36.8
## 4 JACK DANIELS Alcoholic Beverages 11979346. 310730 43.9
## 5 TANQUERAY Alcoholic Beverages 10724692. 304860 33.8
## 6 BACARDI Alcoholic Beverages 7457171. 307767 17.7
## 7 HAVANA CLUB Alcoholic Beverages 7382110. 309875 17.0
## 8 SAUVIGNON BLANC Alcoholic Beverages 2997684. 314981 8.78
## 9 RIESLING Alcoholic Beverages 2970089. 342425 5.95
## 10 CRANBERRY JUICE Juices 2905395. 862501 3.41
🧩 Phân tích Code R
Mục đích: Xác định và liệt kê 10 sản phẩm hàng đầu dựa trên tổng doanh thu (Total_Revenue).
Cách thực hiện:
group_by(Product, Category): Phân nhóm toàn bộ dữ liệu theo từng Product và Category riêng biệt.
summarise(…): Tính toán ba chỉ số tóm tắt cho mỗi nhóm: tổng doanh thu (Total_Revenue), tổng số lượng bán (Total_Quantity), và giá bán trung bình (Avg_Price).
arrange(desc(Total_Revenue)): Sắp xếp bảng kết quả tóm tắt theo tổng doanh thu từ cao xuống thấp.
head(10): Chỉ giữ lại 10 dòng đầu tiên (top 10) trong bảng đã sắp xếp.
📊 Phân tích Kết quả
##
## 4. TOP 10 SẢN PHẨM BÁN CHẠY NHẤT:
top_products_quantity <- df %>%
group_by(Product, Category) %>%
summarise(Total_Quantity = sum(Quantity)) %>%
arrange(desc(Total_Quantity)) %>%
head(10)
print(top_products_quantity)## # A tibble: 10 × 3
## # Groups: Product [10]
## Product Category Total_Quantity
## <chr> <chr> <int>
## 1 HOHES C ORANGE Juices 907035
## 2 TOMATO JUICE Juices 865112
## 3 RAUCH MULTIVITAMIN Juices 863109
## 4 CRANBERRY JUICE Juices 862501
## 5 MANGO JUICE Juices 857302
## 6 PASSION FRUIT JUICE Juices 857299
## 7 GRANINI APPLE Juices 854001
## 8 SAN PELLEGRINO Water 688660
## 9 VITTEL Water 682874
## 10 SELTERS Water 674364
🧩 Phân tích Code R
Mục đích: Lọc và xác định 10 sản phẩm bán chạy nhất dựa trên tổng số lượng đã bán từ toàn bộ dataset.
Cách thực hiện:
Sử dụng thư viện dplyr (thông qua toán tử %>%).
Nhóm (group_by) dữ liệu theo từng Product (Sản phẩm) và Category (Danh mục) của sản phẩm đó.
Tính tổng số lượng (summarise và sum(Quantity)) cho mỗi nhóm sản phẩm.
Sắp xếp (arrange) kết quả theo tổng số lượng giảm dần (desc).
📊 Phân tích Kết quả
##
## 5. PHÂN TÍCH THEO DANH MỤC:
category_analysis <- df %>%
group_by(Category) %>%
summarise(
Total_Revenue = sum(Calculated_Revenue),
Total_Quantity = sum(Quantity),
Avg_Price = mean(Unit_Price),
Product_Count = n_distinct(Product),
Percentage_Revenue = Total_Revenue / sum(df$Calculated_Revenue) * 100
) %>%
arrange(desc(Total_Revenue))
print(category_analysis)## # A tibble: 4 × 6
## Category Total_Revenue Total_Quantity Avg_Price Product_Count
## <chr> <dbl> <int> <dbl> <int>
## 1 Alcoholic Beverages 115165007. 6073628 18.1 19
## 2 Juices 16787829. 6066359 2.67 7
## 3 Soft Drinks 10494035. 6090053 1.62 12
## 4 Water 6133192. 6038173 0.965 9
## # ℹ 1 more variable: Percentage_Revenue <dbl>
🧩 Phân tích Code R
Mục đích: Tổng hợp và phân tích hiệu suất kinh doanh theo từng danh mục sản phẩm.
Cách thực hiện:
group_by(Category): Nhóm toàn bộ dữ liệu theo từng danh mục sản phẩm.
summarise(): Tính toán các chỉ số tổng hợp cho mỗi nhóm, bao gồm: Tổng Doanh thu (Total_Revenue), Tổng Số lượng (Total_Quantity), Giá trung bình (Avg_Price), và Số lượng sản phẩm riêng biệt (Product_Count).
Percentage_Revenue: Tính tỷ trọng doanh thu của từng danh mục so với tổng doanh thu chung của toàn bộ dữ liệu.
arrange(desc(Total_Revenue)): Sắp xếp bảng kết quả theo Doanh thu từ cao xuống thấp.
📊 Phân tích Kết quả
##
## 6. PHÂN TÍCH THEO MÙA:
season_analysis <- df %>%
group_by(Season) %>%
summarise(
Total_Revenue = sum(Calculated_Revenue),
Total_Orders = n_distinct(Order_ID),
Avg_Order_Value = Total_Revenue / Total_Orders
) %>%
arrange(desc(Total_Revenue))
print(season_analysis)## # A tibble: 4 × 4
## Season Total_Revenue Total_Orders Avg_Order_Value
## <chr> <dbl> <int> <dbl>
## 1 Summer 37815510. 87849 430.
## 2 Autumn 36926577. 86926 425.
## 3 Winter 36926059. 86063 429.
## 4 Spring 36911917. 88355 418.
🧩 Phân tích Code R
Mục đích: Tính toán và tóm tắt các chỉ số kinh doanh chính được nhóm theo từng mùa.
Cách thực hiện:
group_by(Season): Phân nhóm bộ dữ liệu df theo 4 mùa (đã tạo ở bước trước).
summarise(): Tính toán các giá trị tổng hợp cho mỗi mùa:
Total_Revenue: Tổng doanh thu (từ cột Calculated_Revenue).
Total_Orders: Đếm số lượng Order_ID duy nhất (không trùng lặp).
Avg_Order_Value: Tính giá trị trung bình đơn hàng (lấy Tổng doanh thu chia Tổng số đơn).
arrange(desc(Total_Revenue)): Sắp xếp bảng kết quả theo doanh thu từ cao xuống thấp.
print(): In bảng tóm tắt season_analysis ra màn hình.
📊 Phân tích Kết quả
##
## 7. DOANH THU THEO THÁNG:
monthly_analysis <- df %>%
group_by(Order_Year, Order_Month) %>%
summarise(
Revenue = sum(Calculated_Revenue),
Orders = n_distinct(Order_ID)
) %>%
arrange(Order_Year, Order_Month)
print(monthly_analysis)## # A tibble: 36 × 4
## # Groups: Order_Year [3]
## Order_Year Order_Month Revenue Orders
## <dbl> <dbl> <dbl> <int>
## 1 2021 1 4126548. 10025
## 2 2021 2 3717107. 8840
## 3 2021 3 3940412. 9774
## 4 2021 4 3951113. 9556
## 5 2021 5 4024292. 9902
## 6 2021 6 4039464. 9595
## 7 2021 7 3991542. 9745
## 8 2021 8 4115523. 9930
## 9 2021 9 3878263. 9597
## 10 2021 10 4057696. 9770
## # ℹ 26 more rows
🧩 Phân tích Code R
Mục đích: Tính toán và tổng hợp tổng doanh thu và tổng số đơn hàng cho mỗi tháng của mỗi năm từ bộ dữ liệu df.
Cách thực hiện: Code sử dụng group_by để nhóm dữ liệu theo Order_Year (năm) và Order_Month (tháng). Sau đó, dùng summarise để thực hiện hai phép tính:
Revenue = sum(Calculated_Revenue): Tính tổng doanh thu của mỗi tháng.
Orders = n_distinct(Order_ID): Đếm số lượng đơn hàng duy nhất của mỗi tháng.
📊 Phân tích Kết quả
##
## 9. PHÂN TÍCH THEO PHÂN KHÚC GIÁ:
price_category_analysis <- df %>%
group_by(Price_Category) %>%
summarise(
Total_Revenue = sum(Calculated_Revenue),
Total_Quantity = sum(Quantity),
Product_Count = n_distinct(Product),
Avg_Price = mean(Unit_Price)
) %>%
arrange(desc(Total_Revenue))
print(price_category_analysis)## # A tibble: 4 × 5
## Price_Category Total_Revenue Total_Quantity Product_Count Avg_Price
## <fct> <dbl> <int> <int> <dbl>
## 1 Premium Price 93350947. 1876811 8 52.9
## 2 Medium Price 23933914. 8133507 29 2.96
## 3 High Price 16474252. 1558362 10 10.5
## 4 Low Price 14820950. 12699533 32 1.15
🧩 Phân tích Code R
Mục đích: Tổng hợp và so sánh hiệu suất bán hàng dựa trên các phân khúc giá đã được định nghĩa trước đó.
Cách thực hiện:
group_by(Price_Category): Nhóm tất cả các dòng dữ liệu lại theo từng phân khúc giá.
summarise(…): Tính toán 4 chỉ số tổng hợp cho mỗi nhóm:
Total_Revenue: Tổng doanh thu.
Total_Quantity: Tổng số lượng sản phẩm bán ra.
Product_Count: Số lượng sản phẩm duy nhất (n_distinct) trong nhóm đó.
Avg_Price: Mức giá trung bình của các sản phẩm trong nhóm.
📊 Phân tích Kết quả
Diễn giải kết quả:
Phân khúc “Premium Price” (Giá cao cấp) thống trị về doanh thu (9.33 triệu) và số lượng bán (1.87 triệu), vượt trội hơn hẳn các nhóm còn lại.
Điều đáng chú ý là doanh thu khổng lồ này chỉ đến từ 8 sản phẩm duy nhất có mức giá trung bình rất cao (52.9).
Các phân khúc “Medium” và “Low Price” có danh mục sản phẩm đa dạng nhất (lần lượt 29 và 32 sản phẩm) nhưng đóng góp doanh thu thấp hơn nhiều.
##
## 10. PHÂN TÍCH KÍCH THƯỚC ĐƠN HÀNG:
order_size_analysis <- df %>%
group_by(Order_Size_Category) %>%
summarise(
Total_Revenue = sum(Calculated_Revenue),
Order_Count = n_distinct(Order_ID),
Avg_Order_Value = Total_Revenue / Order_Count,
Percentage_Orders = Order_Count / n_distinct(df$Order_ID) * 100
)
print(order_size_analysis)## # A tibble: 3 × 5
## Order_Size_Category Total_Revenue Order_Count Avg_Order_Value
## <fct> <dbl> <int> <dbl>
## 1 Small 1222197. 37595 32.5
## 2 Medium 30241490. 194255 156.
## 3 Large 117116376. 117343 998.
## # ℹ 1 more variable: Percentage_Orders <dbl>
🧩 Phân tích Code R
Mục đích: Tính toán và tóm tắt các chỉ số hiệu suất kinh doanh (doanh thu, số lượng đơn) dựa trên các danh mục kích thước đơn hàng (Order_Size_Category).
Cách thực hiện:
group_by(Order_Size_Category): Phân nhóm toàn bộ dữ liệu (df) dựa trên cột phân loại kích thước đơn hàng (ví dụ: “Small”, “Medium”, “Large”).
summarise(): Tính toán các chỉ số tổng hợp cho từng nhóm: Total_Revenue: Tổng doanh thu, Order_Count: Số lượng đơn hàng duy nhất, Avg_Order_Value: Giá trị trung bình của mỗi đơn hàng (lấy Doanh thu / Số đơn), Percentage_Orders: Tỷ lệ phần trăm số đơn hàng của nhóm đó so với tổng số đơn hàng.
📊 Phân tích Kết quả
##
## 11. PHÂN TÍCH CHI TIẾT DOANH THU THEO DANH MỤC:
category_detailed <- df %>%
group_by(Category) %>%
summarise(
Total_Revenue = sum(Calculated_Revenue),
Percentage = Total_Revenue / sum(df$Calculated_Revenue) * 100,
Avg_Price = mean(Unit_Price),
Total_Products = n_distinct(Product),
Total_Quantity = sum(Quantity)
) %>%
arrange(desc(Total_Revenue))
print(category_detailed)## # A tibble: 4 × 6
## Category Total_Revenue Percentage Avg_Price Total_Products Total_Quantity
## <chr> <dbl> <dbl> <dbl> <int> <int>
## 1 Alcoholic Be… 115165007. 77.5 18.1 19 6073628
## 2 Juices 16787829. 11.3 2.67 7 6066359
## 3 Soft Drinks 10494035. 7.06 1.62 12 6090053
## 4 Water 6133192. 4.13 0.965 9 6038173
🧩 Phân tích Code R
Mục đích: Tổng hợp hiệu suất kinh doanh chi tiết theo từng danh mục sản phẩm.
Cách thực hiện:
group_by(Category): Nhóm dữ liệu trong df theo cột Category.
summarise(…): Tính toán các chỉ số tổng hợp cho mỗi nhóm, bao gồm: Tổng doanh thu (Total_Revenue), Tỷ trọng doanh thu (Percentage) so với tổng thể, Giá bán trung bình (Avg_Price), Số lượng sản phẩm riêng biệt (Total_Products), và Tổng số lượng bán ra (Total_Quantity).
arrange(desc(Total_Revenue)): Sắp xếp bảng kết quả theo doanh thu từ cao xuống thấp.
📊 Phân tích Kết quả
##
## 12. PHÂN TÍCH XU HƯỚNG DOANH THU THEO NĂM:
yearly_trend <- df %>%
group_by(Order_Year) %>%
summarise(
Revenue = sum(Calculated_Revenue),
Orders = n_distinct(Order_ID),
Customers = n_distinct(Customer_ID),
Avg_Order_Value = Revenue / Orders
) %>%
arrange(Order_Year)
print(yearly_trend)## # A tibble: 3 × 5
## Order_Year Revenue Orders Customers Avg_Order_Value
## <dbl> <dbl> <int> <int> <dbl>
## 1 2021 47922604. 116043 9999 413.
## 2 2022 49821461. 116863 10000 426.
## 3 2023 50835997. 116287 10000 437.
🧩 Phân tích Code R
Mục đích: Tính toán và tóm tắt các chỉ số kinh doanh cốt lõi (Doanh thu, Đơn hàng, Khách hàng) theo từng năm để phân tích xu hướng tăng trưởng.
Cách thực hiện:
group_by(Order_Year): Phân nhóm toàn bộ dữ liệu dựa trên cột Order_Year.
summarise(…): Tính toán các giá trị tổng hợp cho từng nhóm năm: tổng Revenue, số lượng Orders duy nhất, số lượng Customers duy nhất.
Avg_Order_Value = Revenue / Orders: Tạo một cột mới để tính giá trị trung bình của mỗi đơn hàng trong năm đó.
📊 Phân tích Kết quả
##
## 13. PHÂN TÍCH SÂU TOP SẢN PHẨM:
top_products_analysis <- df %>%
group_by(Product, Category) %>%
summarise(
Total_Revenue = sum(Calculated_Revenue),
Total_Quantity = sum(Quantity),
Avg_Price = mean(Unit_Price),
Times_Ordered = n_distinct(Order_ID),
Revenue_Percentage = Total_Revenue / sum(df$Calculated_Revenue) * 100
) %>%
arrange(desc(Total_Revenue)) %>%
head(15)
print(top_products_analysis)## # A tibble: 15 × 7
## # Groups: Product [15]
## Product Category Total_Revenue Total_Quantity Avg_Price Times_Ordered
## <chr> <chr> <dbl> <int> <dbl> <int>
## 1 VEUVE CLICQUOT Alcohol… 26010133. 316620 94.1 13619
## 2 MOËT & CHANDON Alcohol… 21981229. 308477 62.4 13484
## 3 JOHNNIE WALKER Alcohol… 12089790. 305202 36.8 13327
## 4 JACK DANIELS Alcohol… 11979346. 310730 43.9 13578
## 5 TANQUERAY Alcohol… 10724692. 304860 33.8 13397
## 6 BACARDI Alcohol… 7457171. 307767 17.7 13337
## 7 HAVANA CLUB Alcohol… 7382110. 309875 17.0 13407
## 8 SAUVIGNON BLANC Alcohol… 2997684. 314981 8.78 13397
## 9 RIESLING Alcohol… 2970089. 342425 5.95 14640
## 10 CRANBERRY JUICE Juices 2905395. 862501 3.41 37203
## 11 MANGO JUICE Juices 2817626. 857302 3.18 37000
## 12 PASSION FRUIT … Juices 2784186. 857299 3.06 36887
## 13 CHARDONNAY Alcohol… 2635664. 308655 6.22 13324
## 14 MERLOT Alcohol… 2565938. 305385 5.86 13262
## 15 ROTKÄPPCHEN SE… Alcohol… 2505132. 343417 7.63 14803
## # ℹ 1 more variable: Revenue_Percentage <dbl>
🧩 Phân tích Code R
Mục đích: Thực hiện một phân tích sâu về các sản phẩm, xác định 15 sản phẩm mang lại doanh thu cao nhất và tính toán các chỉ số hiệu suất chính cho chúng.
Cách thực hiện:
Nhóm (group_by) dữ liệu theo cả Product và Category.
Tóm tắt (summarise) mỗi nhóm để tính: Total_Revenue, Total_Quantity, Avg_Price, Times_Ordered, Revenue_Percentage .
📊 Phân tích Kết quả
##
## Phân bố đơn giá theo nhóm:
price_distribution <- df %>%
group_by(Price_Category) %>%
summarise(
Count = n(),
Percentage = n() / nrow(df) * 100,
Avg_Price = mean(Unit_Price)
) %>%
arrange(desc(Count))
print(price_distribution)## # A tibble: 4 × 4
## Price_Category Count Percentage Avg_Price
## <fct> <int> <dbl> <dbl>
## 1 Low Price 582906 55.6 1.15
## 2 Medium Price 329962 31.5 2.96
## 3 Premium Price 72284 6.89 52.9
## 4 High Price 63423 6.05 10.5
🧩 Phân tích Code R
Mục đích: Thống kê và tóm tắt dữ liệu dựa trên các nhóm giá (Price_Category) đã được phân loại trước đó.
Cách thực hiện:
group_by(Price_Category): Phân nhóm toàn bộ dữ liệu theo từng loại giá (ví dụ: Low Price, Medium Price, v.v.).
summarise(…): Tính toán các chỉ số tóm tắt cho mỗi nhóm, bao gồm: Count (tổng số lượng đơn/sản phẩm trong nhóm), Percentage (tỷ lệ phần trăm của nhóm đó so với toàn bộ dữ liệu) và Avg_Price (đơn giá trung bình của các sản phẩm trong nhóm).
📊 Phân tích Kết quả
##
## 15. PHÂN TÍCH CHI TIẾT THEO MÙA:
season_detailed <- df %>%
group_by(Season) %>%
summarise(
Total_Revenue = sum(Calculated_Revenue),
Total_Orders = n_distinct(Order_ID),
Total_Customers = n_distinct(Customer_ID),
Avg_Order_Value = Total_Revenue / Total_Orders,
Revenue_Percentage = Total_Revenue / sum(df$Calculated_Revenue) * 100,
Most_Popular_Category = names(which.max(table(Category))),
Most_Popular_Product = names(which.max(table(Product)))
) %>%
arrange(desc(Total_Revenue))
print(season_detailed)## # A tibble: 4 × 8
## Season Total_Revenue Total_Orders Total_Customers Avg_Order_Value
## <chr> <dbl> <int> <int> <dbl>
## 1 Summer 37815510. 87849 9998 430.
## 2 Autumn 36926577. 86926 10000 425.
## 3 Winter 36926059. 86063 9998 429.
## 4 Spring 36911917. 88355 10000 418.
## # ℹ 3 more variables: Revenue_Percentage <dbl>, Most_Popular_Category <chr>,
## # Most_Popular_Product <chr>
🧩 Phân tích Code R
Mục đích: Đoạn code này thực hiện phân tích tổng hợp hiệu suất kinh doanh rất chi tiết, phân nhóm theo từng Season (Mùa).
Cách thực hiện: Nhóm (group_by) toàn bộ dữ liệu theo Season, Sử dụng summarise để tính toán 7 chỉ số (KPIs) cho mỗi mùa, bao gồm:
Total_Revenue: Tổng doanh thu.
Total_Orders: Tổng số đơn hàng (đếm ID duy nhất).
Total_Customers: Tổng số khách hàng (đếm ID duy nhất).
Avg_Order_Value: Giá trị đơn hàng trung bình (lấy Doanh thu / Đơn hàng).
Revenue_Percentage: Tỷ trọng doanh thu của mùa đó so với tổng doanh thu cả năm.
Most_Popular_Category/Product: Tìm ra Danh mục và Sản phẩm bán chạy nhất trong mùa đó.
📊 Phân tích Kết quả
##
## 16. PHÂN TÍCH TĂNG TRƯỞNG THEO NĂM:
growth_analysis <- revenue_by_year %>%
mutate(
Revenue_Growth = (Revenue / lag(Revenue) - 1) * 100,
Order_Growth = (Orders / lag(Orders) - 1) * 100
)
print(growth_analysis)## # A tibble: 3 × 6
## Order_Year Revenue Orders Avg_Order_Value Revenue_Growth Order_Growth
## <dbl> <dbl> <int> <dbl> <dbl> <dbl>
## 1 2021 47922604. 116043 413. NA NA
## 2 2022 49821461. 116863 426. 3.96 0.707
## 3 2023 50835997. 116287 437. 2.04 -0.493
🧩 Phân tích Code R
Mục đích: Tính toán tỷ lệ tăng trưởng theo từng năm (Year-over-Year) cho doanh thu và số lượng đơn hàng.
Cách thực hiện:
Code bắt đầu với một data frame revenue_by_year (được giả định là đã tổng hợp dữ liệu theo năm).
Sử dụng mutate() để tạo hai cột mới là Revenue_Growth (Tăng trưởng Doanh thu) và Order_Growth (Tăng trưởng Đơn hàng).
Hàm lag(Revenue) và lag(Orders) được sử dụng để lấy giá trị của hàng (năm) ngay trước đó.
Công thức (Giá trị hiện tại / Giá trị năm trước - 1) * 100 được áp dụng để tính phần trăm tăng trưởng.
📊 Phân tích Kết quả
##
## 17. PHÂN TÍCH GIÁ TRỊ ĐƠN HÀNG:
order_value_analysis <- df %>%
group_by(Order_ID) %>%
summarise(Order_Value = sum(Calculated_Revenue)) %>%
summarise(
Avg_Order_Value = mean(Order_Value),
Median_Order_Value = median(Order_Value),
Min_Order_Value = min(Order_Value),
Max_Order_Value = max(Order_Value)
)
print(order_value_analysis)## # A tibble: 1 × 4
## Avg_Order_Value Median_Order_Value Min_Order_Value Max_Order_Value
## <dbl> <dbl> <dbl> <dbl>
## 1 425. 90.6 0.33 18705.
🧩 Phân tích Code R
Mục đích: Tính toán và tóm tắt các chỉ số thống kê mô tả cơ bản (Trung bình, Trung vị, Nhỏ nhất, Lớn nhất) cho giá trị của từng đơn hàng (Order Value).
Cách thực hiện:
group_by(Order_ID) và summarise(Order_Value = …): Đầu tiên, code tính tổng doanh thu (Calculated_Revenue) cho mỗi Order_ID duy nhất. Kết quả là một danh sách giá trị của từng đơn hàng.
summarise(…) (lần 2): Từ danh sách giá trị đơn hàng vừa tạo ở bước 1, code tiếp tục tính 4 chỉ số: mean , median, min) và max).
📊 Phân tích Kết quả
Diễn giải kết quả: Bảng kết quả hiển thị các số liệu thống kê mô tả cho giá trị đơn hàng:
Giá trị trung bình (Avg_Order_Value): 425
Giá trị trung vị (Median_Order_Value): 90.6
Giá trị nhỏ nhất (Min_Order_Value): 0.33
Giá trị lớn nhất (Max_Order_Value): 18,705
##
## 18. PHÂN TÍCH SẢN PHẨM THEO MỨC GIÁ:
product_price_analysis <- df %>%
group_by(Price_Category, Category) %>%
summarise(
Product_Count = n_distinct(Product),
Total_Revenue = sum(Calculated_Revenue)
) %>%
arrange(Price_Category, desc(Total_Revenue))
print(product_price_analysis)## # A tibble: 10 × 4
## # Groups: Price_Category [4]
## Price_Category Category Product_Count Total_Revenue
## <fct> <chr> <int> <dbl>
## 1 Low Price Water 9 5928626.
## 2 Low Price Soft Drinks 12 4338136.
## 3 Low Price Alcoholic Beverages 7 2556566.
## 4 Low Price Juices 4 1997621.
## 5 Medium Price Juices 7 14790208.
## 6 Medium Price Soft Drinks 7 6155899.
## 7 Medium Price Alcoholic Beverages 12 2783242.
## 8 Medium Price Water 3 204566.
## 9 High Price Alcoholic Beverages 10 16474252.
## 10 Premium Price Alcoholic Beverages 8 93350947.
🧩 Phân tích Code R
Mục đích: Phân tích số lượng sản phẩm và tổng doanh thu, được phân chia theo cả mức giá và loại sản phẩm (Category).
Cách thực hiện: Code sử dụng group_by để nhóm dữ liệu theo 2 cấp: Price_Category (ví dụ: “Low Price”) và Category (ví dụ: “Water”). Sau đó, summarise được dùng để:
Product_Count: Đếm số lượng sản phẩm duy nhất trong mỗi nhóm.
Total_Revenue: Tính tổng doanh thu cho mỗi nhóm.
📊 Phân tích Kết quả
##
## 19. PHÂN TÍCH TẦN SUẤT MUA HÀNG CỦA KHÁCH HÀNG:
customer_frequency <- df %>%
group_by(Customer_ID) %>%
summarise(
Order_Count = n_distinct(Order_ID),
Total_Revenue = sum(Calculated_Revenue),
Days_Since_First_Order = as.numeric(Sys.Date() - min(Order_Date))
) %>%
mutate(
Purchase_Frequency = case_when(
Order_Count == 1 ~ "One-time",
Order_Count <= 3 ~ "Occasional",
Order_Count <= 10 ~ "Regular",
TRUE ~ "Frequent"
)
)
frequency_summary <- customer_frequency %>%
group_by(Purchase_Frequency) %>%
summarise(
Customer_Count = n(),
Avg_Orders = mean(Order_Count),
Avg_Revenue = mean(Total_Revenue)
)
print(frequency_summary)## # A tibble: 1 × 4
## Purchase_Frequency Customer_Count Avg_Orders Avg_Revenue
## <chr> <int> <dbl> <dbl>
## 1 Frequent 10000 34.9 14858.
🧩 Phân tích Code R
Mục đích: Phân tích tần suất mua hàng của khách hàng. Code này phân loại khách hàng vào các nhóm (ví dụ: “One-time”, “Frequent”) dựa trên tổng số đơn hàng họ đã thực hiện, sau đó tính toán giá trị trung bình của từng nhóm.
Cách thực hiện:
Tính toán: Đầu tiên, code nhóm theo Customer_ID để đếm Order_Count và Total_Revenue cho mỗi khách hàng.
Phân loại: Sử dụng case_when để tạo cột Purchase_Frequency. Khách hàng được gán nhãn (“One-time”, “Occasional”, “Regular”, “Frequent”) dựa trên Order_Count của họ (ví dụ: Order_Count > 10 là “Frequent”).
Tổng hợp: Cuối cùng, code nhóm theo các nhãn Purchase_Frequency mới tạo để đếm số lượng khách hàng (Customer_Count) và tính số đơn hàng trung bình (Avg_Orders), doanh thu trung bình (Avg_Revenue) cho mỗi nhóm.
📊 Phân tích Kết quả
##
## 20. TƯƠNG QUAN GIỮA ĐƠN GIÁ VÀ SỐ LƯỢNG BÁN:
correlation_analysis <- df %>%
group_by(Product) %>%
summarise(
Avg_Price = mean(Unit_Price),
Total_Quantity = sum(Quantity)
)
correlation_coef <- cor(correlation_analysis$Avg_Price, correlation_analysis$Total_Quantity)
cat("Hệ số tương quan giữa giá và số lượng:", round(correlation_coef, 4), "\n")## Hệ số tương quan giữa giá và số lượng: -0.3883
🧩 Phân tích Code R
Mục đích: Đo lường mối quan hệ tuyến tính (hệ số tương quan) giữa giá trung bình của sản phẩm và tổng số lượng bán ra của sản phẩm đó.
Cách thực hiện:
group_by(Product) và summarise(): Tính toán giá bán trung bình (Avg_Price) và tổng số lượng bán (Total_Quantity) cho từng sản phẩm riêng lẻ.
cor(): Tính hệ số tương quan Pearson giữa hai cột vừa tạo (Giá trung bình và Tổng số lượng).
cat(): In kết quả đã làm tròn ra màn hình.
📊 Phân tích Kết quả
top_customers <- df %>%
group_by(Customer_ID) %>%
summarise(
Total_Revenue = sum(Calculated_Revenue),
Total_Orders = n_distinct(Order_ID),
Avg_Order_Value = Total_Revenue / Total_Orders,
First_Order = min(Order_Date),
Last_Order = max(Order_Date)
) %>%
arrange(desc(Total_Revenue)) %>%
head(10)
print(top_customers)## # A tibble: 10 × 6
## Customer_ID Total_Revenue Total_Orders Avg_Order_Value First_Order Last_Order
## <chr> <dbl> <int> <dbl> <date> <date>
## 1 CUS3005 84815. 48 1767. 2021-03-17 2023-12-07
## 2 CUS7626 79485. 51 1559. 2021-01-03 2023-09-22
## 3 CUS7825 76404. 35 2183. 2021-01-02 2023-12-28
## 4 CUS4837 74419. 47 1583. 2021-01-01 2023-12-05
## 5 CUS6696 72765. 42 1732. 2021-01-06 2023-11-22
## 6 CUS9351 72544. 33 2198. 2021-01-02 2023-12-16
## 7 CUS6277 72106. 36 2003. 2021-01-13 2023-12-04
## 8 CUS2448 71992. 41 1756. 2021-02-12 2023-12-29
## 9 CUS5391 71402. 35 2040. 2021-02-01 2023-12-30
## 10 CUS6855 70869. 35 2025. 2021-01-06 2023-11-16
🧩 Phân tích Code R
Mục đích: Xác định và liệt kê 10 khách hàng (Customer_ID) có giá trị cao nhất dựa trên tổng doanh thu.
Cách thực hiện:
group_by(Customer_ID): Nhóm toàn bộ dữ liệu theo từng khách hàng.
summarise(…): Tính toán các chỉ số tổng hợp cho mỗi khách hàng, bao gồm: Tổng doanh thu (Total_Revenue), Tổng số đơn hàng riêng biệt (Total_Orders), Giá trị trung bình mỗi đơn (Avg_Order_Value), và ngày đặt hàng đầu tiên/cuối cùng (First_Order, Last_Order).
arrange(desc(Total_Revenue)) %>% head(10): Sắp xếp kết quả theo doanh thu giảm dần và chỉ giữ lại 10 hàng đầu tiên.
📊 Phân tích Kết quả
library(ggplot2)
library(dplyr)
library(lubridate)
library(scales)
library(tidyr)
library(patchwork)
# Thiết lập theme đơn giản
simple_theme <- theme_minimal() +
theme(
plot.title = element_text(size = 14, face = "bold", hjust = 0.5),
axis.title = element_text(size = 12),
axis.text = element_text(size = 10),
legend.position = "bottom",
panel.grid.minor = element_blank()
)p1 <- df %>%
group_by(Customer_ID) %>%
summarise(Revenue = sum(Calculated_Revenue),
Orders = n_distinct(Order_ID)) %>%
arrange(desc(Revenue)) %>%
head(10) %>%
ggplot(aes(x = reorder(substr(Customer_ID, 1, 6), Revenue), y = Revenue)) +
geom_col(fill = "orange", alpha = 0.8) +
geom_text(aes(label = paste0(round(Revenue/1000,0),"K")),
hjust = -0.2, size = 3) +
geom_text(aes(y = Revenue/2, label = paste(Orders, "đơn")),
color = "white", fontface = "bold") +
coord_flip() +
labs(title = "Top 10 khách hàng có doanh thu cao nhất",
x = "Khách hàng", y = "Doanh thu") +
scale_y_continuous(labels = comma, expand = expansion(mult = c(0, 0.1))) +
simple_theme
print(p1)🧩 Phân tích Code R
Mục đích: Trích xuất và vẽ biểu đồ Top 10 khách hàng có doanh thu (Revenue) cao nhất.
Cách thực hiện:
Tổng hợp dữ liệu (dplyr): Từ df, nhóm theo Customer_ID, sau đó tính tổng doanh thu (sum(Calculated_Revenue)) và đếm số lượng đơn hàng duy nhất (n_distinct(Order_ID)) cho mỗi khách hàng.
Lọc Top 10: Sắp xếp (arrange) kết quả theo doanh thu giảm dần và chỉ giữ lại 10 hàng đầu tiên (head(10)).
Trực quan hóa: Dùng ggplot để vẽ biểu đồ. Các thao tác chính bao gồm: geom_col: Vẽ các cột doanh thu, geom_text (2 lần): Thêm nhãn số tiền (ví dụ: “85K”) ở cuối cột và nhãn số đơn hàng (ví dụ: “48 đơn”) ở giữa cột, coord_flip: Lật biểu đồ thành dạng thanh ngang để dễ đọc tên khách hàng.
📊 Phân tích Kết quả
p2 <- df %>%
group_by(Category, Season) %>%
summarise(Revenue = sum(Calculated_Revenue)) %>%
ggplot(aes(x = Season, y = Revenue, fill = Category)) +
geom_col(position = "dodge", alpha = 0.8) +
geom_text(
aes(label = paste0(round(Revenue / 1000, 0), "K")),
position = position_dodge(0.9),
vjust = 1.5,
size = 2.5
) +
facet_wrap(~Category, scales = "free_y") +
labs(
title = "Doanh thu theo danh mục và mùa",
x = "Mùa",
y = "Doanh thu"
) +
scale_y_continuous(labels = comma) +
scale_fill_brewer(palette = "Set2") +
simple_theme +
theme(legend.position = "none")
print(p2)🧩 Phân tích Code R
Mục đích: Trực quan hóa tổng doanh thu theo Mùa (Season) và Danh mục (Category), đồng thời so sánh xu hướng mùa của từng danh mục một cách độc lập.
Cách thực hiện:
Tổng hợp: Dùng group_by(Category, Season) và summarise() để tính tổng doanh thu (Revenue) cho mỗi nhóm.
Trực quan hóa (ggplot2): Dùng geom_col() để vẽ biểu đồ cột cho doanh thu.
Phân tách: Lệnh facet_wrap(~Category, scales = “free_y”) là mấu chốt. Nó tách biểu đồ thành 4 ô riêng biệt (facets) cho 4 danh mục. Tùy chọn scales = “free_y” cho phép mỗi ô có thang đo trục Y (doanh thu) riêng, giúp nhìn rõ xu hướng nội tại của từng nhóm thay vì bị “lép vế” do chênh lệch quy mô.
📊 Phân tích Kết quả
p4 <- df %>%
group_by(Category, Season) %>%
summarise(Revenue = sum(Calculated_Revenue)) %>%
ggplot(aes(x = Season, y = Revenue, fill = Category)) +
geom_col(position = "dodge") +
geom_text(aes(label = paste0(round(Revenue/1000,0),"K")),
position = position_dodge(0.9), vjust = -0.3, size = 2.5) +
labs(title = "Doanh Thu Theo Danh Mục và Mùa",
x = "Mùa", y = "Doanh thu", fill = "Danh mục") +
scale_y_continuous(labels = comma) +
theme_minimal()
print(p4)🧩 Phân tích Code R
Mục đích: Tính toán và vẽ biểu đồ cột so sánh tổng doanh thu (Calculated_Revenue) theo từng Danh mục (Category) và Mùa (Season).
Cách thực hiện:
Dùng group_by và summarise để tính tổng doanh thu cho mỗi nhóm (Danh mục, Mùa).
Sử dụng ggplot để vẽ biểu đồ cột. geom_col(position = “dodge”) là thao tác chính, giúp đặt các cột của 4 danh mục nằm cạnh nhau (thay vì chồng lên nhau) trong từng Mùa để dễ so sánh.
geom_text được dùng để thêm nhãn giá trị (đã làm tròn và định dạng “K” - nghìn) lên phía trên mỗi cột.
📊 Phân tích Kết quả
p5 <- df %>%
group_by(Category) %>%
summarise(Revenue = sum(Calculated_Revenue)) %>%
mutate(Percentage = Revenue / sum(Revenue) * 100) %>%
ggplot(aes(x = "", y = Percentage, fill = Category)) +
geom_col(width = 1) +
geom_text(aes(label = paste0(round(Percentage,1), "%")),
position = position_stack(vjust = 0.5),
color = "black", fontface = "bold") +
coord_polar("y") +
labs(title = "Tỷ Lệ Đóng Góp Doanh Thu Theo Danh Mục") +
theme_void() +
theme(plot.title = element_text(hjust = 0.5, face = "bold"))
print(p5)🧩** Phân tích Code R **
Mục đích: Đoạn code này dùng để tính toán và trực quan hóa tỷ lệ phần trăm đóng góp doanh thu của từng danh mục sản phẩm (Category) dưới dạng biểu đồ tròn.
Cách thực hiện:
Tính toán (dplyr): Dữ liệu (df) được nhóm theo Category, sau đó tính tổng Calculated_Revenue cho mỗi nhóm. Cuối cùng, hàm mutate tạo cột Percentage bằng cách lấy doanh thu mỗi nhóm chia cho tổng doanh thu.
Vẽ biểu đồ (ggplot2): Code sử dụng ggplot để thiết lập dữ liệu. Nó dùng geom_col để tạo biểu đồ cột xếp chồng (stacked bar chart), sau đó dùng coord_polar(“y”) để “uốn cong” biểu đồ cột này thành biểu đồ tròn.
Định dạng: geom_text được dùng để thêm nhãn phần trăm vào giữa các lát bánh. theme_void xóa bỏ các trục và nền xám, và theme được dùng để căn giữa tiêu đề.
📊 Phân tích Kết quả
p6 <- df %>%
group_by(Region) %>%
summarise(Revenue = sum(Calculated_Revenue)) %>%
ggplot(aes(x = reorder(Region, Revenue), y = Revenue)) +
geom_col(fill = "steelblue", alpha = 0.8) +
geom_text(aes(label = paste0(round(Revenue/1000,0),"K")),
hjust = 1.1, size = 3) +
coord_flip() +
labs(title = "Doanh Thu Theo Vùng", x = "Vùng", y = "Doanh thu") +
scale_y_continuous(labels = comma, expand = expansion(mult = c(0, 0.1))) +
theme_minimal()
print(p6)🧩 Phân tích Code R
Mục đích: Tạo một biểu đồ cột ngang để so sánh tổng doanh thu (Revenue) của các Vùng (Region).
Cách thực hiện:
Tổng hợp dữ liệu: Dùng group_by(Region) và summarise() để tính tổng Calculated_Revenue cho mỗi vùng.
Vẽ biểu đồ: Khởi tạo ggplot, dùng geom_col() để vẽ các cột.
Sắp xếp & Định dạng: Dùng reorder(Region, Revenue) để sắp xếp các vùng theo doanh thu tăng dần. coord_flip() được dùng để lật biểu đồ thành dạng ngang.
Thêm nhãn: geom_text() thêm nhãn doanh thu (làm tròn và chia cho 1000, thêm hậu tố “K”) vào cuối mỗi thanh.
📊 Phân tích Kết quả
p7 <- df %>%
group_by(Product) %>%
summarise(Revenue = sum(Calculated_Revenue)) %>%
top_n(10, Revenue) %>%
ggplot(aes(x = reorder(Product, Revenue), y = Revenue)) +
geom_col(fill = "green", alpha = 0.8) +
geom_text(aes(label = paste0(round(Revenue/1000,0),"K")),
hjust = 1.1, size = 3) +
coord_flip() +
labs(title = "Top 10 Sản Phẩm Có Doanh Thu Cao Nhất",
x = "Sản phẩm", y = "Doanh thu") +
scale_y_continuous(labels = comma, expand = expansion(mult = c(0, 0.1))) +
theme_minimal()
print(p7)🧩 Phân tích Code R
Mục đích: Xác định và vẽ biểu đồ Top 10 sản phẩm (Product) có doanh thu (Revenue) cao nhất.
Cách thực hiện:
Tổng hợp dữ liệu (dplyr): Từ df, nhóm theo Product, sau đó tính tổng doanh thu (sum(Calculated_Revenue)) cho mỗi sản phẩm.
Lọc Top 10 (dplyr): Sử dụng top_n(10, Revenue) để lọc ra 10 sản phẩm có doanh thu cao nhất.
Trực quan hóa (ggplot2): Dùng ggplot để vẽ biểu đồ thanh ngang (geom_col + coord_flip), sắp xếp các sản phẩm theo doanh thu (reorder(Product, Revenue)).
Thêm chi tiết: geom_text được dùng để thêm nhãn doanh thu (ví dụ: “26010K”) vào cuối mỗi thanh.
📊 Phân tích Kết quả
p8 <- order_size_analysis %>%
ggplot(aes(x = Order_Size_Category, y = Order_Count)) +
geom_col(aes(fill = Order_Size_Category), alpha = 0.8) +
geom_text(aes(label = Order_Count), vjust = 1.5, size = 4) +
labs(title = "Số Lượng Đơn Hàng Theo Kích Thước",
x = "Kích thước đơn", y = "Số đơn") +
scale_fill_brewer(palette = "Set2") +
theme_minimal() +
theme(legend.position = "none")
print(p8)🧩 Phân tích Code R
Mục đích: Trực quan hóa và so sánh số lượng đơn hàng (Order_Count) giữa các nhóm kích thước đơn hàng khác nhau (Order_Size_Category).
Cách thực hiện:
Dữ liệu: Sử dụng một data frame đã được tổng hợp sẵn là order_size_analysis.
Trực quan hóa (ggplot2): Dùng geom_col() để vẽ biểu đồ cột, ánh xạ Order_Size_Category vào trục X và Order_Count vào trục Y.
Chi tiết: Dùng geom_text() để thêm nhãn số lượng chính xác lên đỉnh mỗi cột, và ẩn phần chú giải (legend) vì màu sắc đã được giải thích qua trục X.
📊 Phân tích Kết quả
p9 <- df %>%
group_by(Order_Year, Order_Month) %>%
summarise(Revenue = sum(Calculated_Revenue)) %>%
ggplot(aes(x = factor(Order_Month), y = Revenue, fill = factor(Order_Year))) +
geom_col(position = "dodge") +
geom_text(aes(label = paste0(round(Revenue/1000,0),"K")),
position = position_dodge(0.9),
vjust = 0.45 ,
size = 2.5,
angle = 90,
hjust = 1.1 ) +
labs(title = "Doanh Thu Theo Tháng",
x = "Tháng", y = "Doanh thu", fill = "Năm") +
scale_y_continuous(labels = comma,
expand = expansion(mult = c(0, 0.25))) +
theme_minimal()
print(p9)🧩 Phân tích Code R
Mục đích: Tính toán và vẽ biểu đồ cột so sánh tổng doanh thu (Calculated_Revenue) của từng tháng qua các năm khác nhau (2021, 2022, 2023).
Cách thực hiện:
Sử dụng group_by (theo Năm, Tháng) và summarise để tính tổng doanh thu cho từng tháng của từng năm.
Dùng ggplot với geom_col(position = “dodge”) để vẽ các cột doanh thu của 3 năm nằm cạnh nhau (so sánh trực tiếp) tại mỗi tháng trên trục hoành.
Tùy chỉnh geom_text với angle = 90 để các nhãn giá trị xoay dọc 90 độ.
Tùy chỉnh scale_y_continuous(expand = …) để tăng khoảng trống ở phía trên trục Y, đảm bảo các nhãn xoay dọc không bị cắt khỏi khung biểu đồ.
📊 Phân tích Kết quả
Diễn giải kết quả:
Tăng trưởng theo năm: Có một xu hướng tăng trưởng rõ rệt qua các năm. Trong hầu hết các tháng, cột năm 2022 (xanh lá) cao hơn 2021 (đỏ), và cột năm 2023 (xanh dương) cao hơn 2022.
Tính thời vụ (Seasonality): Doanh thu có biến động theo tháng. Thường có sự sụt giảm nhẹ vào Tháng 2 và Tháng 4 (so với tháng liền trước). Các tháng cuối năm, đặc biệt là Tháng 12, có xu hướng cho thấy doanh thu cao nhất.
p10 <- df %>%
group_by(Customer_Type, Region) %>%
summarise(Revenue = sum(Calculated_Revenue)) %>%
ggplot(aes(x = Region, y = Revenue, fill = Customer_Type)) +
geom_col(position = "dodge", alpha = 0.8) +
geom_text(aes(label = paste0(round(Revenue/1000,0),"K")),
position = position_dodge(0.9),
vjust = 0.45,
size = 2.5,
angle = 90,
hjust = 1.1) +
labs(title = "Doanh Thu Theo Loại Khách Hàng và Vùng",
x = "Vùng", y = "Doanh thu") +
scale_y_continuous(labels = comma,
expand = expansion(mult = c(0, 0.25))) +
scale_fill_brewer(palette = "Set1") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
print(p10)🧩 Phân tích Code R
Mục đích: Đoạn code này dùng để tính toán và so sánh trực tiếp doanh thu giữa hai loại khách hàng (B2B và B2C) tại từng Vùng (Region) riêng biệt.
Cách thực hiện:
Tính toán (dplyr): Dữ liệu (df) được nhóm theo cả Customer_Type và Region. Sau đó, hàm summarise tính tổng Calculated_Revenue cho từng tổ hợp (ví dụ: B2B tại Berlin, B2C tại Berlin).
Vẽ biểu đồ (ggplot2): Code sử dụng geom_col(position = “dodge”) để tạo biểu đồ cột nhóm. Lệnh này đặt các cột B2B và B2C cạnh nhau trong từng Vùng, giúp việc so sánh trực quan hơn là biểu đồ cột chồng.
Định dạng: geom_text thêm nhãn doanh thu (định dạng “K” - ngàn) được xoay 90 độ (angle = 90) lên trên mỗi cột. theme(axis.text.x = …) xoay tên các Vùng 45 độ để tránh chồng chéo.
📊 Phân tích Kết quả
p11 <- df %>%
group_by(Product) %>%
summarise(Total_Quantity = sum(Quantity)) %>%
arrange(desc(Total_Quantity)) %>%
head(10) %>%
ggplot(aes(x = reorder(Product, Total_Quantity), y = Total_Quantity)) +
geom_col(fill = "skyblue", alpha = 0.8) +
geom_text(aes(label = Total_Quantity),
hjust = 1.5,
size = 3,
angle = 0) +
coord_flip() +
labs(title = "Top 10 Sản Phẩm Bán Chạy Nhất",
x = "Sản phẩm", y = "Tổng số lượng bán") +
scale_y_continuous(expand = expansion(mult = c(0, 0.15))) +
theme_minimal()
print(p11)🧩 Phân tích Code R
Mục đích: Tìm ra 10 sản phẩm (Product) bán chạy nhất dựa trên tổng số lượng (Quantity) và vẽ biểu đồ cột ngang để so sánh.
Cách thực hiện:
Tổng hợp: Dùng group_by(Product) và summarise() để tính tổng số lượng (Total_Quantity) cho từng sản phẩm.
Xếp hạng & Lọc: Dùng arrange(desc(Total_Quantity)) để sắp xếp các sản phẩm theo số lượng giảm dần, sau đó head(10) để chọn ra 10 sản phẩm cao nhất.
Vẽ biểu đồ: Khởi tạo ggplot, dùng geom_col() để vẽ các cột.
Sắp xếp & Định dạng: reorder(Product, Total_Quantity) đảm bảo các sản phẩm trên biểu đồ được sắp xếp đúng thứ tự. coord_flip() lật biểu đồ sang ngang để dễ đọc tên sản phẩm. geom_text() thêm nhãn số lượng cụ thể cho mỗi thanh.
📊 Phân tích Kết quả
p12 <- df %>%
group_by(Customer_Type) %>%
summarise(Order_Count = n_distinct(Order_ID)) %>%
ggplot(aes(x = Customer_Type, y = Order_Count)) +
geom_col(aes(fill = Customer_Type), alpha = 0.8) +
geom_text(aes(label = Order_Count),
vjust = 1.5 ,
size = 5,
angle = 0) +
labs(title = "Số Lượng Đơn Hàng Theo Loại Khách Hàng",
x = "Loại khách hàng", y = "Số đơn") +
scale_y_continuous(expand = expansion(mult = c(0, 0.15))) +
scale_fill_brewer(palette = "Set1") +
theme_minimal() +
theme(legend.position = "none")
print(p12)🧩 Phân tích Code R
Mục đích: Đếm và vẽ biểu đồ cột so sánh tổng số lượng đơn hàng duy nhất (Order_ID) theo từng loại khách hàng (Customer_Type).
Cách thực hiện:
Tổng hợp dữ liệu (dplyr): Từ df, nhóm theo Customer_Type, sau đó dùng summarise và n_distinct(Order_ID) để đếm số đơn hàng duy nhất cho mỗi loại.
Trực quan hóa (ggplot2): Dùng ggplot để tạo biểu đồ cột (geom_col).
Thêm chi tiết: aes(fill = Customer_Type): Tô màu các cột dựa trên loại khách hàng (B2B màu đỏ, B2C màu xanh), geom_text: Thêm nhãn hiển thị con số chính xác (tổng số đơn) lên trên đỉnh mỗi cột, theme(legend.position = “none”): Ẩn phần chú giải (legend) về màu sắc vì thông tin đã rõ ràng trên trục X.
📊 Phân tích Kết quả
p13 <- df %>%
group_by(Order_Year, Order_Month) %>%
summarise(Revenue = sum(Calculated_Revenue)) %>%
ggplot(aes(x = factor(Order_Month), y = Revenue, group = factor(Order_Year))) +
geom_line(aes(color = factor(Order_Year)), size = 1) +
geom_point(aes(color = factor(Order_Year)), size = 2) +
geom_smooth(aes(group = 1), method = "lm", se = FALSE,
color = "black", linetype = "dashed") +
geom_text(aes(label = ifelse(Revenue == max(Revenue),
paste0(round(Revenue/1000,0),"K"), "")),
vjust = -0.5, size = 3) +
facet_wrap(~Order_Year, nrow = 1) +
labs(title = "Doanh thu theo tháng", x = "Tháng", y = "Doanh thu") +
scale_y_continuous(labels = comma) +
scale_color_brewer(palette = "Set1", name = "Năm") +
simple_theme
print(p13)🧩 Phân tích Code R
Mục đích: Trực quan hóa doanh thu theo từng tháng trong 3 năm (2021, 2022, 2023) trên các ô riêng biệt, đồng thời vẽ một đường xu hướng (trendline) chung cho toàn bộ giai đoạn.
Cách thực hiện:
Tổng hợp: Dùng group_by() và summarise() để tính tổng doanh thu (Revenue) cho mỗi tháng của mỗi năm.
Phân tách: Lệnh facet_wrap(~Order_Year, nrow = 1) chia biểu đồ thành 3 ô riêng biệt theo chiều ngang, mỗi ô cho một năm.
Vẽ đường: geom_line và geom_point vẽ dữ liệu doanh thu hàng tháng, với màu sắc khác nhau cho mỗi năm.
Vẽ xu hướng: Lệnh geom_smooth(aes(group = 1), …) là mấu chốt. Bằng cách set group = 1, nó buộc R vẽ một đường xu hướng tuyến tính duy nhất (nét đứt màu đen) chạy xuyên suốt cả 3 ô, thể hiện xu hướng chung của toàn bộ 3 năm thay vì 3 đường riêng lẻ.
Nhãn: geom_text(…) được dùng để thêm nhãn giá trị cho điểm doanh thu cao nhất (max(Revenue)) trong toàn bộ tập dữ liệu
p28 <- df %>%
group_by(Category) %>%
summarise(Revenue = sum(Calculated_Revenue)) %>%
ggplot(aes(x = reorder(Category, Revenue), y = Revenue, fill = Category)) +
geom_col(alpha = 0.8) +
geom_text(aes(label = paste0(round(Revenue/1000,0),"K")),
vjust = -0.5, size = 3.5) +
geom_point(aes(y = mean(Revenue)), color = "red", size = 3, shape = 18) +
labs(title = "Doanh thu theo danh mục", x = "Danh mục", y = "Doanh thu") +
scale_y_continuous(labels = comma) +
scale_fill_brewer(palette = "Set3") + # Palette màu phân biệt
simple_theme +
theme(legend.position = "none")
print(p28)🧩 Phân tích Code R
Mục đích: Tính toán, sắp xếp và vẽ biểu đồ cột soS tổng doanh thu (Calculated_Revenue) của từng Danh mục (Category).
Cách thực hiện:
Dùng group_by và summarise để tính tổng doanh thu cho mỗi danh mục.
Sử dụng ggplot với geom_col. Thao tác quan trọng là reorder(Category, Revenue) giúp tự động sắp xếp các cột trên trục X theo thứ tự doanh thu tăng dần (từ thấp đến cao).
Thêm một lớp geom_point (biểu tượng kim cương đỏ) để vẽ một đường chấm biểu thị mức doanh thu trung bình của 4 danh mục này.
theme(legend.position = “none”) được dùng để ẩn chú giải (legend) vì các danh mục đã được ghi rõ trên trục X.
📊 Phân tích Kết quả
Diễn giải kết quả: Biểu đồ cho thấy được xu hướng như sau:
Sự thống trị: Danh mục “Alcoholic Beverages” (Đồ uống có cồn) chiếm ưu thế tuyệt đối với doanh thu 115,165K (khoảng 115 triệu), cao gấp nhiều lần tổng doanh thu của cả ba danh mục còn lại cộng lại.
Xếp hạng: Các danh mục được sắp xếp rõ ràng: “Water” (6,133K) có doanh thu thấp nhất, tiếp theo là “Soft Drinks” (10,494K) và “Juices” (16,788K).
p27 <- df %>%
group_by(Season) %>%
summarise(Revenue = sum(Calculated_Revenue)) %>%
ggplot(aes(x = Season, y = Revenue, fill = Season)) +
geom_col(alpha = 0.8) +
geom_line(aes(group = 1), color = "red", size = 1) +
geom_point(color = "darkred", size = 3) +
geom_text(aes(label = paste0(round(Revenue/1000,0),"K")),
vjust = -0.5, size = 4, fontface = "bold") +
labs(title = "Doanh thu theo mùa", x = "Mùa", y = "Doanh thu") +
scale_y_continuous(labels = comma) +
scale_fill_viridis_d() + # Sử dụng viridis palette
simple_theme +
theme(legend.position = "none")
print(p27)🧩 Phân tích Code R
Mục đích: Đoạn code này tính toán tổng doanh thu theo từng Mùa (Season) và tạo một biểu đồ kết hợp (combo chart) để vừa so sánh độ lớn (cột) vừa thể hiện xu hướng (đường).
Cách thực hiện:
Tính toán (dplyr): Dùng group_by(Season) và summarise để tính tổng Calculated_Revenue cho mỗi mùa.
Vẽ biểu đồ (ggplot2): Sử dụng geom_col để tạo biểu đồ cột thể hiện doanh thu của từng mùa.
Vẽ lớp phủ: Thêm geom_line(aes(group = 1)) để vẽ một đường nối các đỉnh cột, và geom_point để đánh dấu các điểm dữ liệu. Lệnh group = 1 là cần thiết để buộc R nối tất cả các mùa (vốn là các danh mục riêng rẽ) thành một đường duy nhất.
Định dạng: Thêm nhãn doanh thu (định dạng “K”) bằng geom_text, sử dụng bảng màu viridis (scale_fill_viridis_d), và ẩn chú giải (theme(legend.position = “none”)).
📊 Phân tích Kết quả
p16 <- df %>%
group_by(Order_Year, Order_Month) %>%
summarise(Orders = n_distinct(Order_ID)) %>%
ggplot(aes(x = factor(Order_Month), y = Orders)) +
geom_col(fill = "lightblue", alpha = 0.8) +
geom_line(aes(group = factor(Order_Year), color = factor(Order_Year)),
size = 1) +
geom_point(aes(color = factor(Order_Year)), size = 2) +
facet_wrap(~Order_Year, nrow = 1) +
labs(title = "Số lượng đơn hàng theo tháng", x = "Tháng", y = "Số đơn") +
scale_color_brewer(palette = "Set2", name = "Năm") +
simple_theme
print(p16)🧩 Phân tích Code R
Mục đích: Tạo một biểu đồ kết hợp (cột và đường) để so sánh số lượng đơn hàng (Orders) theo từng tháng, đồng thời tách biệt dữ liệu của mỗi năm thành một biểu đồ con riêng.
Cách thực hiện:
Tổng hợp: Dùng group_by() và summarise(Orders = n_distinct(Order_ID)) để đếm số lượng đơn hàng duy nhất cho mỗi cặp Năm-Tháng.
Vẽ biểu đồ: Sử dụng ggplot với 3 lớp (layer) đồ họa: geom_col() (cột nền), geom_line() (đường xu hướng) và geom_point() (điểm dữ liệu).
Phân tách (Facet): Lệnh quan trọng facet_wrap(~Order_Year, nrow = 1) chia biểu đồ tổng thành 3 biểu đồ con (cho 2021, 2022, 2023) và xếp chúng trên cùng một hàng.
📊 Phân tích Kết quả
p17 <- df %>%
group_by(Price_Category) %>%
summarise(Revenue = sum(Calculated_Revenue),
Products = n_distinct(Product)) %>%
ggplot(aes(x = Price_Category, y = Revenue)) +
geom_col(aes(fill = Price_Category), alpha = 0.8) +
geom_text(aes(label = paste0(round(Revenue/1000,0),"K")),
vjust = -0.5, size = 4, fontface = "bold") +
geom_text(aes(y = Revenue/2, label = paste(Products, "SP")),
color = "white", fontface = "bold") +
labs(title = "Doanh thu theo phân khúc giá",
x = "Phân khúc giá", y = "Doanh thu") +
scale_y_continuous(labels = comma) +
scale_fill_brewer(palette = "Set3") +
simple_theme +
theme(legend.position = "none")
print(p17)🧩 Phân tích Code R
Mục đích: Tính toán và vẽ biểu đồ cột so sánh tổng doanh thu (Revenue) và số lượng sản phẩm (Products) duy nhất theo từng phân khúc giá (Price_Category).
Cách thực hiện:
Tổng hợp dữ liệu (dplyr): Từ df, nhóm theo Price_Category, sau đó tính tổng doanh thu (sum(Calculated_Revenue)) và đếm số sản phẩm duy nhất (n_distinct(Product)) cho mỗi nhóm.
Trực quan hóa (ggplot2): Dùng ggplot để vẽ biểu đồ cột (geom_col), trong đó màu sắc của cột (fill) được quyết định bởi chính Price_Category.
Thêm chi tiết: Sử dụng hai lớp geom_text để thêm nhãn:
Nhãn tổng doanh thu (ví dụ: “93351K”) được đặt phía trên cột (vjust = -0.5).
Nhãn số lượng sản phẩm (ví dụ: “8 SP”) được đặt ở giữa cột (y = Revenue/2).
📊 Phân tích Kết quả
p18 <- df %>%
group_by(Order_Date) %>%
summarise(Daily_Revenue = sum(Calculated_Revenue)) %>%
ggplot(aes(x = Order_Date, y = Daily_Revenue)) +
geom_line(color = "steelblue", alpha = 0.7) +
geom_smooth(se = FALSE, color = "red", size = 1) +
geom_point(data = . %>% filter(Daily_Revenue > quantile(Daily_Revenue, 0.9)),
color = "red", size = 2) +
labs(title = "Xu hướng doanh thu hàng ngày",
x = "Ngày", y = "Doanh thu") +
scale_y_continuous(labels = comma) +
simple_theme
print(p18)🧩 Phân tích Code R
Mục đích: Trực quan hóa doanh thu theo từng ngày để xem xét biến động ngắn hạn và xu hướng dài hạn, đồng thời làm nổi bật những ngày có doanh thu đặc biệt cao.
Cách thực hiện:
Tổng hợp: Dùng group_by(Order_Date) và summarise() để tính tổng doanh thu (Daily_Revenue) cho mỗi ngày.
Vẽ đường: Dùng geom_line() (màu xanh) để vẽ diễn biến doanh thu qua từng ngày.
Vẽ xu hướng: Dùng geom_smooth() (màu đỏ) để vẽ một đường xu hướng tuyến tính, cho thấy chiều hướng chung của doanh thu qua các năm.
Đánh dấu điểm cao: Lệnh geom_point(data = . %>% filter(…)) là mấu chốt. Nó lọc và chỉ đánh dấu (chấm đỏ) những ngày có doanh thu nằm trong top 10% cao nhất (lớn hơn quantile(…, 0.9)).
📊 Phân tích Kết quả
p19 <- df %>%
ggplot(aes(x = Category, y = Unit_Price)) +
geom_boxplot(fill = "lightyellow", alpha = 0.7) +
geom_jitter(alpha = 0.3, width = 0.2, color = "steelblue") +
stat_summary(fun = mean, geom = "point",
shape = 18, size = 3, color = "red") +
labs(title = "Phân bố giá theo danh mục",
x = "Danh mục", y = "Đơn giá") +
scale_y_continuous(labels = comma) +
simple_theme
print(p19)🧩 Phân tích Code R
Mục đích: Trực quan hóa và so sánh sự phân bố (distribution) của Unit_Price (Đơn giá) cho từng Category (Danh mục).
Cách thực hiện:
ggplot(aes(x = Category, y = Unit_Price)): Thiết lập biểu đồ để so sánh Đơn giá giữa các Danh mục.
geom_boxplot (Lớp 1): Vẽ biểu đồ hộp (màu vàng nhạt) để hiển thị các giá trị tóm tắt chính (trung vị, tứ phân vị).
geom_jitter (Lớp 2): Vẽ tất cả các điểm dữ liệu riêng lẻ (chấm tròn xanh, alpha = 0.3 làm chúng mờ đi) và xếp chồng lên boxplot. Điều này giúp hiển thị mật độ và sự phân bố thực tế của dữ liệu.
stat_summary (Lớp 3): Tính toán và thêm vào một điểm (hình kim cương đỏ) để biểu thị giá trị trung bình (mean) của mỗi danh mục.
📊 Phân tích Kết quả
p22 <- df %>%
group_by(Customer_ID) %>%
summarise(
Recency = as.numeric(Sys.Date() - max(Order_Date)),
Frequency = n_distinct(Order_ID),
Monetary = sum(Calculated_Revenue)
) %>%
ggplot(aes(x = Frequency, y = Monetary)) +
geom_point(aes(size = Recency, color = Recency), alpha = 0.7) +
geom_smooth(method = "lm", se = FALSE, color = "red", linetype = "dashed") +
geom_rug(alpha = 0.3) +
scale_color_gradient(low = "blue", high = "red") +
labs(title = "Phân tích RFM Khách Hàng",
subtitle = "Tần suất vs Giá trị, kích thước thể hiện độ mới",
x = "Tần suất mua", y = "Giá trị tổng") +
scale_y_continuous(labels = comma) +
simple_theme
print(p22)🧩 Phân tích Code R
Mục đích: Đoạn code này thực hiện phân tích RFM (Recency, Frequency, Monetary) bằng cách tính toán ba chỉ số này cho mỗi khách hàng và trực quan hóa mối quan hệ giữa chúng.
Cách thực hiện:
Tính toán (dplyr): Dữ liệu được nhóm theo Customer_ID. Hàm summarise được dùng để tính:
Recency: Độ mới (số ngày kể từ lần mua cuối cùng so với ngày hiện tại).
Frequency: Tần suất (số lượng đơn hàng riêng biệt).
Monetary: Giá trị (tổng doanh thu của khách hàng đó).
Vẽ biểu đồ (ggplot2): Code tạo một biểu đồ phân tán (scatter plot) với Frequency trên trục X và Monetary trên trục Y.
Mã hóa Recency: Chỉ số Recency được mã hóa kép: nó vừa điều khiển màu sắc (color = Recency) vừa điều khiển kích thước (size = Recency) của điểm.
Thêm ngữ cảnh: geom_smooth(method = “lm”) vẽ một đường xu hướng tuyến tính (màu đỏ, nét đứt) để chỉ ra mối quan hệ tổng thể, và geom_rug thêm các vạch nhỏ ở hai trục để thể hiện mật độ phân bổ của dữ liệu.
📊 Phân tích Kết quả
Diễn giải kết quả :Biểu đồ “Phân tích RFM” thể hiện Tần suất mua (trục X) và Giá trị tổng (trục Y). Mỗi điểm là một khách hàng, trong đó:
Màu sắc/Kích thước thể hiện độ mới (Recency).
Điểm nhỏ/xanh-tím là khách hàng mới mua gần đây.
Điểm lớn/đỏ là khách hàng đã lâu không mua.
Các xu hướng chính:
Tương quan F-M: Đường xu hướng màu đỏ cho thấy rõ: khách hàng mua càng thường xuyên (Frequency cao) thì chi tiêu càng nhiều (Monetary cao).
Phân cụm: Dữ liệu phân thành hai cụm rõ rệt. Đa số khách hàng tập trung ở nhóm giá trị tổng thấp (dưới 20,000).
Khách hàng VIP: Nhóm khách hàng giá trị nhất (góc trên bên phải) chủ yếu có màu xanh/tím. Điều này cho thấy những khách hàng tốt nhất (mua nhiều, tiêu nhiều) cũng là những người đang hoạt động tích cực (mới mua gần đây).
p24 <- revenue_by_year %>%
mutate(Growth = (Revenue / lag(Revenue) - 1) * 100) %>%
ggplot(aes(x = factor(Order_Year), y = Revenue)) +
geom_col(fill = "lightblue", alpha = 0.8) +
geom_line(aes(group = 1), color = "darkblue", size = 1) +
geom_point(color = "darkblue", size = 3) +
geom_text(aes(label = paste0(round(Revenue/1000,0),"K")),
vjust = -0.5, size = 4) +
geom_text(aes(y = Revenue/2, label = ifelse(!is.na(Growth),
paste0(round(Growth,1),"%"), "")),
color = "white", fontface = "bold") +
labs(title = "Tăng Trưởng Doanh Thu Theo Năm",
x = "Năm", y = "Doanh thu") +
scale_y_continuous(labels = comma) +
theme_minimal()
print(p24)🧩 Phân tích Code R
Mục đích: Tạo một biểu đồ kết hợp (cột và đường) để hiển thị tổng doanh thu theo từng năm, đồng thời tính toán và hiển thị % tăng trưởng so với năm trước.
Cách thực hiện:
Tính tăng trưởng: Dùng mutate() và hàm lag() để tạo cột Growth, tính toán % tăng trưởng doanh thu so với năm liền trước.
Vẽ biểu đồ: Sử dụng ggplot với geom_col (cột), geom_line (đường nối) và geom_point (điểm).
Thêm nhãn (Layers 4 & 5): geom_text (lần 1) thêm nhãn tổng doanh thu (định dạng “K”) bên trên các điểm, geom_text (lần 2) sử dụng ifelse(!is.na(Growth), …) để thêm nhãn % tăng trưởng vào giữa thân cột, và tự động bỏ qua năm đầu tiên (vì không có dữ liệu lag để so sánh).
📊 Phân tích Kết quả
stb_data <- read_excel("STB_Analys.xlsx", sheet = "Sheet1")
cat("✅ Đã đọc dữ liệu thành công từ file Excel")## ✅ Đã đọc dữ liệu thành công từ file Excel
🧩 Phân tích Code R
Cách thực hiện:
library(readxl): Tải thư viện readxl.
read_excel(…): Dùng hàm này để đọc dữ liệu từ “Sheet1” trong file “STB_Analys.xlsx”.
stb_data <- …: Lưu dữ liệu đã đọc vào một biến (data frame) tên là stb_data.
cat(…): In một thông báo ra màn hình để xác nhận hoàn thành.
📊 Phân tích Kết quả
dim_info <- paste("Kích thước dataset:", dim(stb_data)[1], "dòng ×", dim(stb_data)[2], "cột")
cat(dim_info)## Kích thước dataset: 10 dòng × 20 cột
🧩 Phân tích Code R
Cách thực hiện:
head(stb_data, 5): Chọn 5 dòng đầu tiên từ biến stb_data.
kable(…) và kable_styling(…): Sử dụng thư viện kableExtra để chuyển data frame thành một bảng HTML có dạng đẹp (có sọc và hiệu ứng khi di chuột).
📊 Phân tích Kết quả
## CẤU TRÚC DỮ LIỆU:
## Rows: 10
## Columns: 20
## $ ky_bao_cao <chr> "Năm 2024", "Năm 2023", "Năm 2022", "…
## $ tong_tai_san <dbl> 727016711, 674374966, 588643085, 5527…
## $ cho_vay_khach_hang <dbl> 473456903, 438411368, 404997291, 3704…
## $ tien_gui_cua_khach_hang <dbl> 582474928, 536873344, 499789702, 4731…
## $ chung_khoan_dau_tu <dbl> 81332610, 71936877, 61547075, 7174961…
## $ von_dieu_le <dbl> 18852157, 18852157, 18852157, 1885215…
## $ loi_nhuan_chua_phan_phoi <dbl> 26064919, 20316716, 14939922, 9423861…
## $ cac_quy_du_tru <dbl> 5378109, 4757251, 4191077, 3738749, 3…
## $ tong_loi_nhuan_truoc_thue <dbl> 10255932, 9296911, 6665026, 3871050, …
## $ chi_phi_du_phong_rui_ro_tin_dung <dbl> -1921365, -2730016, -4990584, -415202…
## $ loi_nhuan_thuan_truoc_du_phong <dbl> 12177297, 12026927, 11655610, 8023079…
## $ thu_nhap_lai_thuan <dbl> 22512910, 21243756, 18116531, 1329348…
## $ lai_thuan_dich_vu <dbl> 4908618, 4544754, 4606313, 3863308, 3…
## $ tong_thu_nhap_hoat_dong <dbl> 29569157, 27561432, 24717375, 1898634…
## $ tong_chi_phi_hoat_dong <dbl> -17391860, -15534505, -13061765, -109…
## $ loi_nhuan_sau_thue <dbl> 8214618, 7452316, 5334331, 3018665, 2…
## $ tong_von_chu_so_huu <dbl> 35213980, 33967555, 28791443, 3260720…
## $ chung_khoan_kinh_doanh <dbl> 0, 0, 0, 0, 0, 0, 0, 63250, 89891, 95…
## $ lai_co_ban_tren_co_phieu_vnd <dbl> 4135, 3719, 2593, 1414, 1248, 1129, 7…
## $ vay_TCTD_khac <dbl> 31905792, 26241658, 21052126, 1201671…
🧩 Phân tích Code R
Cách thực hiện:
library(tidyverse): Tải thư viện tidyverse (bao gồm dplyr).
glimpse(stb_data): Sử dụng hàm glimpse để hiển thị một bản tóm tắt cấu trúc dữ liệu theo chiều dọc, giúp dễ đọc hơn.
Kết quả: Một danh sách liệt kê 20 cột, kiểu dữ liệu và vài giá trị mẫu của chúng.
📊 Phân tích Kết quả
Diễn giải kết quả: Kết quả xác nhận có 10 quan sát (dòng) và 20 biến (cột). Quan trọng nhất, nó cho thấy:
ky_bao_cao (Kỳ báo cáo) đang ở dạng chữ (
19 biến còn lại (như tong_tai_san, loi_nhuan_sau_thue…) đều ở
dạng số (
variable_dict <- data.frame(
Tên_biến = names(stb_data),
Mô_tả = c("Kỳ báo cáo", "Tổng tài sản", "Cho vay khách hàng", "Tiền gửi khách hàng",
"Chứng khoán đầu tư", "Vốn điều lệ", "Lợi nhuận chưa phân phối",
"Các quỹ dự trữ", "Tổng lợi nhuận trước thuế", "Chi phí dự phòng rủi ro tín dụng",
"Lợi nhuận thuần trước dự phòng", "Thu nhập lãi thuần", "Lãi thuần dịch vụ",
"Tổng thu nhập hoạt động", "Tổng chi phí hoạt động", "Lợi nhuận sau thuế",
"Tổng vốn chủ sở hữu", "Chứng khoán kinh doanh", "Lãi cơ bản trên cổ phiếu",
"Vay TCTD khác")
)
kable(variable_dict, caption = "Từ điển biến số") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE, font_size = 12)| Tên_biến | Mô_tả |
|---|---|
| ky_bao_cao | Kỳ báo cáo |
| tong_tai_san | Tổng tài sản |
| cho_vay_khach_hang | Cho vay khách hàng |
| tien_gui_cua_khach_hang | Tiền gửi khách hàng |
| chung_khoan_dau_tu | Chứng khoán đầu tư |
| von_dieu_le | Vốn điều lệ |
| loi_nhuan_chua_phan_phoi | Lợi nhuận chưa phân phối |
| cac_quy_du_tru | Các quỹ dự trữ |
| tong_loi_nhuan_truoc_thue | Tổng lợi nhuận trước thuế |
| chi_phi_du_phong_rui_ro_tin_dung | Chi phí dự phòng rủi ro tín dụng |
| loi_nhuan_thuan_truoc_du_phong | Lợi nhuận thuần trước dự phòng |
| thu_nhap_lai_thuan | Thu nhập lãi thuần |
| lai_thuan_dich_vu | Lãi thuần dịch vụ |
| tong_thu_nhap_hoat_dong | Tổng thu nhập hoạt động |
| tong_chi_phi_hoat_dong | Tổng chi phí hoạt động |
| loi_nhuan_sau_thue | Lợi nhuận sau thuế |
| tong_von_chu_so_huu | Tổng vốn chủ sở hữu |
| chung_khoan_kinh_doanh | Chứng khoán kinh doanh |
| lai_co_ban_tren_co_phieu_vnd | Lãi cơ bản trên cổ phiếu |
| vay_TCTD_khac | Vay TCTD khác |
🧩 Phân tích Code R
Cách thực hiện:
names(stb_data): Lấy danh sách tên của 20 cột.
data.frame(…): Tạo một data frame mới (variable_dict) với 2 cột: Tên_biến (lấy từ names()) và Mô_tả (được nhập thủ công).
kable(…) %>% kable_styling(…): Định dạng data frame này thành bảng HTML.
📊 Phân tích Kết quả
# Bảng này sẽ là bảng tĩnh, thay vì bảng tương tác
kable(stb_data,
caption = "Dữ liệu gốc từ file Excel",
digits = 0, # Làm tròn số
format.args = list(big.mark = ".", decimal.mark = ",")) %>%
kable_styling(font_size = 9)| ky_bao_cao | tong_tai_san | cho_vay_khach_hang | tien_gui_cua_khach_hang | chung_khoan_dau_tu | von_dieu_le | loi_nhuan_chua_phan_phoi | cac_quy_du_tru | tong_loi_nhuan_truoc_thue | chi_phi_du_phong_rui_ro_tin_dung | loi_nhuan_thuan_truoc_du_phong | thu_nhap_lai_thuan | lai_thuan_dich_vu | tong_thu_nhap_hoat_dong | tong_chi_phi_hoat_dong | loi_nhuan_sau_thue | tong_von_chu_so_huu | chung_khoan_kinh_doanh | lai_co_ban_tren_co_phieu_vnd | vay_TCTD_khac |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Năm 2024 | 727.016.711 | 473.456.903 | 582.474.928 | 81.332.610 | 18.852.157 | 26.064.919 | 5.378.109 | 10.255.932 | -1.921.365 | 12.177.297 | 22.512.910 | 4.908.618 | 29.569.157 | -17.391.860 | 8.214.618 | 35.213.980 | 0 | 4.135 | 31.905.792 |
| Năm 2023 | 674.374.966 | 438.411.368 | 536.873.344 | 71.936.877 | 18.852.157 | 20.316.716 | 4.757.251 | 9.296.911 | -2.730.016 | 12.026.927 | 21.243.756 | 4.544.754 | 27.561.432 | -15.534.505 | 7.452.316 | 33.967.555 | 0 | 3.719 | 26.241.658 |
| Năm 2022 | 588.643.085 | 404.997.291 | 499.789.702 | 61.547.075 | 18.852.157 | 14.939.922 | 4.191.077 | 6.665.026 | -4.990.584 | 11.655.610 | 18.116.531 | 4.606.313 | 24.717.375 | -13.061.765 | 5.334.331 | 28.791.443 | 0 | 2.593 | 21.052.126 |
| Năm 2021 | 552.748.211 | 370.470.970 | 473.197.808 | 71.749.610 | 18.852.157 | 9.423.861 | 3.738.749 | 3.871.050 | -4.152.029 | 8.023.079 | 13.293.486 | 3.863.308 | 18.986.347 | -10.963.268 | 3.018.665 | 32.607.200 | 0 | 1.414 | 12.016.711 |
| Năm 2020 | 492.516.029 | 334.854.576 | 427.971.850 | 75.156.127 | 18.852.157 | 7.303.619 | 3.336.508 | 3.339.280 | -3.036.974 | 6.376.254 | 11.526.554 | 3.744.015 | 17.270.869 | -10.894.615 | 2.681.981 | 28.956.242 | 0 | 1.248 | 7.880.006 |
| Năm 2019 | 453.581.057 | 292.058.715 | 400.844.380 | 76.497.497 | 18.852.157 | 5.411.564 | 2.963.901 | 3.216.746 | -2.152.889 | 5.369.635 | 9.180.688 | 3.322.990 | 14.635.338 | -9.265.703 | 2.454.864 | 26.741.640 | 0 | 1.129 | 3.525.560 |
| Năm 2018 | 406.040.598 | 253.100.111 | 349.388.922 | 75.514.421 | 18.852.157 | 3.521.064 | 2.720.885 | 2.246.991 | -1.592.114 | 3.839.105 | 7.633.794 | 2.682.144 | 11.676.935 | -7.837.830 | 1.790.156 | 24.632.367 | 0 | 780 | 7.300.158 |
| Năm 2017 | 368.468.840 | 220.197.752 | 319.859.587 | 73.188.580 | 18.852.157 | 2.286.118 | 2.549.642 | 1.491.804 | -816.589 | 2.308.393 | 5.278.035 | 2.623.831 | 8.645.286 | -6.336.893 | 1.181.560 | 23.236.292 | 63.250 | 555 | 12.649.006 |
| Năm 2016 | 332.023.043 | 196.428.077 | 291.653.101 | 65.033.141 | 18.852.157 | 1.340.912 | 2.430.405 | 155.591 | -696.243 | 851.834 | 4.020.697 | 1.430.044 | 6.530.157 | -5.678.323 | 88.609 | 22.191.934 | 89.891 | 49 | 8.109.652 |
| Năm 2015 | 292.032.736 | 183.660.021 | 260.994.745 | 39.678.056 | 18.852.157 | 1.264.953 | 2.419.833 | 878.155 | -2.256.014 | 3.134.169 | 6.575.107 | 1.171.263 | 8.288.716 | -5.154.547 | 647.919 | 22.080.495 | 95.334 | 444 | 2.954.073 |
🧩 Phân tích Code R
Cách thực hiện: Dùng hàm kable() để:
Thêm tiêu đề (“Dữ liệu gốc…”).
Làm tròn số (digits = 0).
Dùng dấu chấm (.) phân cách hàng nghìn (big.mark = “.”).
Điều chỉnh cỡ chữ nhỏ (font_size = 9) cho bảng gọn gàng.
📊 Phân tích Kết quả
missing_summary <- stb_data %>%
summarise(across(everything(), ~sum(is.na(.)))) %>%
pivot_longer(everything(), names_to = "Biến", values_to = "Số_lượng_NA")
cat("KẾT QUẢ KIỂM TRA DỮ LIỆU MISSING:\n")## KẾT QUẢ KIỂM TRA DỮ LIỆU MISSING:
kable(missing_summary, caption = "Thống kê dữ liệu missing") %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| Biến | Số_lượng_NA |
|---|---|
| ky_bao_cao | 0 |
| tong_tai_san | 0 |
| cho_vay_khach_hang | 0 |
| tien_gui_cua_khach_hang | 0 |
| chung_khoan_dau_tu | 0 |
| von_dieu_le | 0 |
| loi_nhuan_chua_phan_phoi | 0 |
| cac_quy_du_tru | 0 |
| tong_loi_nhuan_truoc_thue | 0 |
| chi_phi_du_phong_rui_ro_tin_dung | 0 |
| loi_nhuan_thuan_truoc_du_phong | 0 |
| thu_nhap_lai_thuan | 0 |
| lai_thuan_dich_vu | 0 |
| tong_thu_nhap_hoat_dong | 0 |
| tong_chi_phi_hoat_dong | 0 |
| loi_nhuan_sau_thue | 0 |
| tong_von_chu_so_huu | 0 |
| chung_khoan_kinh_doanh | 0 |
| lai_co_ban_tren_co_phieu_vnd | 0 |
| vay_TCTD_khac | 0 |
🧩 Phân tích Code R
Cách thực hiện:
summarise(across(everything(), ~sum(is.na(.)))): Dùng dplyr để đếm (sum) các giá trị is.na (là NA) trên everything() (tất cả các cột).
pivot_longer(…): Chuyển đổi bảng kết quả từ dạng rộng (1 dòng, 20 cột) sang dạng dài (20 dòng, 2 cột) để dễ đọc.
kable(…): Định dạng bảng kết quả.
📊 Phân tích Kết quả
data_types <- stb_data %>%
summarise(across(everything(), class)) %>%
pivot_longer(everything(), names_to = "Biến", values_to = "Kiểu_dữ_liệu")
kable(data_types, caption = "Kiểu dữ liệu của các biến") %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| Biến | Kiểu_dữ_liệu |
|---|---|
| ky_bao_cao | character |
| tong_tai_san | numeric |
| cho_vay_khach_hang | numeric |
| tien_gui_cua_khach_hang | numeric |
| chung_khoan_dau_tu | numeric |
| von_dieu_le | numeric |
| loi_nhuan_chua_phan_phoi | numeric |
| cac_quy_du_tru | numeric |
| tong_loi_nhuan_truoc_thue | numeric |
| chi_phi_du_phong_rui_ro_tin_dung | numeric |
| loi_nhuan_thuan_truoc_du_phong | numeric |
| thu_nhap_lai_thuan | numeric |
| lai_thuan_dich_vu | numeric |
| tong_thu_nhap_hoat_dong | numeric |
| tong_chi_phi_hoat_dong | numeric |
| loi_nhuan_sau_thue | numeric |
| tong_von_chu_so_huu | numeric |
| chung_khoan_kinh_doanh | numeric |
| lai_co_ban_tren_co_phieu_vnd | numeric |
| vay_TCTD_khac | numeric |
🧩 Phân tích Code R
Cách thực hiện:
Dùng summarise(across(…, class)) để lấy kiểu dữ liệu của mọi cột.
Dùng pivot_longer() để xoay dữ liệu từ dạng rộng sang dạng dài (2 cột: “Biến” và “Kiểu_dữ_liệu”).
Dùng kable() và kable_styling() để tạo bảng HTML có định dạng sọc (striped) và gọn gàng.
📊 Phân tích Kết quả
library(psych)
# Tạo bảng thống kê mô tả với describe
basic_stats_psych <- describe(stb_data %>% select(-ky_bao_cao))
# Lựa chọn và định dạng lại các chỉ số cần thiết
basic_stats_formatted <- basic_stats_psych %>%
select(n, min, max, mean, median, sd) %>%
rename(
Số_quan_sát = n,
Tối_thiểu = min,
Tối_đa = max,
Trung_bình = mean,
Trung_vị = median,
Độ_lệch_chuẩn = sd
) %>%
rownames_to_column("Biến") %>%
# Định dạng số với dấu phân cách hàng nghìn
mutate(
Tối_thiểu = format(round(Tối_thiểu), big.mark = ".", decimal.mark = ",", scientific = FALSE),
Tối_đa = format(round(Tối_đa), big.mark = ".", decimal.mark = ",", scientific = FALSE),
Trung_bình = format(round(Trung_bình), big.mark = ".", decimal.mark = ",", scientific = FALSE),
Trung_vị = format(round(Trung_vị), big.mark = ".", decimal.mark = ",", scientific = FALSE),
Độ_lệch_chuẩn = format(round(Độ_lệch_chuẩn), big.mark = ".", decimal.mark = ",", scientific = FALSE)
)
# Hiển thị bảng kết quả
kable(basic_stats_formatted, caption = "Thống kê mô tả cơ bản") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE, font_size = 10)| Biến | Số_quan_sát | Tối_thiểu | Tối_đa | Trung_bình | Trung_vị | Độ_lệch_chuẩn |
|---|---|---|---|---|---|---|
| tong_tai_san | 10 | 292.032.736 | 727.016.711 | 488.744.528 | 473.048.543 | 145.725.203 |
| cho_vay_khach_hang | 10 | 183.660.021 | 473.456.903 | 316.763.578 | 313.456.646 | 103.566.111 |
| tien_gui_cua_khach_hang | 10 | 260.994.745 | 582.474.928 | 414.304.837 | 414.408.115 | 108.591.819 |
| chung_khoan_dau_tu | 10 | 39.678.056 | 81.332.610 | 69.163.399 | 72.562.728 | 11.800.012 |
| von_dieu_le | 10 | 18.852.157 | 18.852.157 | 18.852.157 | 18.852.157 | 0 |
| loi_nhuan_chua_phan_phoi | 10 | 1.264.953 | 26.064.919 | 9.187.365 | 6.357.592 | 8.587.671 |
| cac_quy_du_tru | 10 | 2.419.833 | 5.378.109 | 3.448.636 | 3.150.204 | 1.040.903 |
| tong_loi_nhuan_truoc_thue | 10 | 155.591 | 10.255.932 | 4.141.749 | 3.278.013 | 3.480.925 |
| chi_phi_du_phong_rui_ro_tin_dung | 10 | -4.990.584 | -696.243 | -2.434.482 | -2.204.452 | 1.360.434 |
| loi_nhuan_thuan_truoc_du_phong | 10 | 851.834 | 12.177.297 | 6.576.230 | 5.872.944 | 4.226.821 |
| thu_nhap_lai_thuan | 10 | 4.020.697 | 22.512.910 | 11.938.156 | 10.353.621 | 6.671.124 |
| lai_thuan_dich_vu | 10 | 1.171.263 | 4.908.618 | 3.289.728 | 3.533.502 | 1.300.627 |
| tong_thu_nhap_hoat_dong | 10 | 6.530.157 | 29.569.157 | 16.788.161 | 15.953.104 | 8.312.558 |
| tong_chi_phi_hoat_dong | 10 | -17.391.860 | -5.154.547 | -10.211.931 | -10.080.159 | 4.172.817 |
| loi_nhuan_sau_thue | 10 | 88.609 | 8.214.618 | 3.286.502 | 2.568.422 | 2.805.178 |
| tong_von_chu_so_huu | 10 | 22.080.495 | 35.213.980 | 27.841.915 | 27.766.542 | 4.882.644 |
| chung_khoan_kinh_doanh | 10 | 0 | 95.334 | 24.848 | 0 | 40.819 |
| lai_co_ban_tren_co_phieu_vnd | 10 | 49 | 4.135 | 1.607 | 1.188 | 1.406 |
| vay_TCTD_khac | 10 | 2.954.073 | 31.905.792 | 13.363.474 | 10.063.182 | 9.835.761 |
🧩 Phân tích Code R
Cách thực hiện:
library(psych): Tải thư viện psych để dùng hàm describe.
describe(stb_data %>% select(-ky_bao_cao)): Tính toán thống kê mô tả chi tiết cho tất cả các cột TRỪ cột ky_bao_cao (vì đây là cột chữ).
select(…), rename(…), mutate(format(…)): Chọn lọc các cột thống kê mong muốn (n, min, max, mean…), đổi tên sang tiếng Việt, và định dạng lại các số lớn (dùng dấu “.”) cho dễ đọc.
kable(…): Hiển thị bảng kết quả.
📊 Phân tích Kết quả
Diễn giải kết quả: Bảng này cho thấy phạm vi và xu hướng trung tâm của dữ liệu.
n (Số quan sát) là 10 cho tất cả, xác nhận dữ liệu 10 năm.
Tối_thiểu (Min) và Tối_đa (Max) cho thấy sự tăng trưởng mạnh mẽ, ví dụ: loi_nhuan_sau_thue tăng từ 130.431 (Min) lên 8.214.618 (Max) trong 10 năm.
Trung_bình (Mean) và Trung_vị (Median) ở một số biến (như lợi nhuận) có chênh lệch, gợi ý dữ liệu có thể bị lệch (skewed) về phía các năm có giá trị cao.
time_dist <- stb_data %>%
mutate(Năm = str_extract(ky_bao_cao, "\\d+")) %>%
count(Năm) %>%
rename(Số_quan_sát = n)
cat("PHÂN BỐ DỮ LIỆU THEO NĂM:\n")## PHÂN BỐ DỮ LIỆU THEO NĂM:
kable(time_dist, caption = "Số lượng quan sát theo năm") %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| Năm | Số_quan_sát |
|---|---|
| 2015 | 1 |
| 2016 | 1 |
| 2017 | 1 |
| 2018 | 1 |
| 2019 | 1 |
| 2020 | 1 |
| 2021 | 1 |
| 2022 | 1 |
| 2023 | 1 |
| 2024 | 1 |
🧩 Phân tích Code R
Cách thực hiện:
mutate(Năm = str_extract(ky_bao_cao, “\d+”)): Tạo cột “Năm” mới bằng cách trích xuất chuỗi số (\d+) từ cột ky_bao_cao (ví dụ: “Năm 2015” -> “2015”).
count(Năm): Đếm số lần xuất hiện của mỗi năm.
kable(…): Hiển thị bảng kết quả.
📊 Phân tích Kết quả
trend_analysis <- stb_data %>%
mutate(Năm = as.numeric(str_extract(ky_bao_cao, "\\d+"))) %>%
select(Năm, tong_tai_san, cho_vay_khach_hang, tien_gui_cua_khach_hang, loi_nhuan_sau_thue) %>%
arrange(Năm) %>%
mutate(across(-Năm, list(
Tăng_trưởng_tuyệt_đối = ~. - first(.),
Tăng_trưởng_phần_trăm = ~(. / first(.) - 1) * 100
), .names = "{.col}_{.fn}"))
cat("PHÂN TÍCH XU HƯỚNG CÁC CHỈ SỐ CHÍNH (2015-2024):\n")## PHÂN TÍCH XU HƯỚNG CÁC CHỈ SỐ CHÍNH (2015-2024):
kable(trend_analysis, caption = "Xu hướng và tăng trưởng các chỉ số chính", digits = 0) %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| Năm | tong_tai_san | cho_vay_khach_hang | tien_gui_cua_khach_hang | loi_nhuan_sau_thue | tong_tai_san_Tăng_trưởng_tuyệt_đối | tong_tai_san_Tăng_trưởng_phần_trăm | cho_vay_khach_hang_Tăng_trưởng_tuyệt_đối | cho_vay_khach_hang_Tăng_trưởng_phần_trăm | tien_gui_cua_khach_hang_Tăng_trưởng_tuyệt_đối | tien_gui_cua_khach_hang_Tăng_trưởng_phần_trăm | loi_nhuan_sau_thue_Tăng_trưởng_tuyệt_đối | loi_nhuan_sau_thue_Tăng_trưởng_phần_trăm |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2015 | 292032736 | 183660021 | 260994745 | 647919 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 2016 | 332023043 | 196428077 | 291653101 | 88609 | 39990307 | 14 | 12768056 | 7 | 30658356 | 12 | -559310 | -86 |
| 2017 | 368468840 | 220197752 | 319859587 | 1181560 | 76436104 | 26 | 36537731 | 20 | 58864842 | 23 | 533641 | 82 |
| 2018 | 406040598 | 253100111 | 349388922 | 1790156 | 114007862 | 39 | 69440090 | 38 | 88394177 | 34 | 1142237 | 176 |
| 2019 | 453581057 | 292058715 | 400844380 | 2454864 | 161548321 | 55 | 108398694 | 59 | 139849635 | 54 | 1806945 | 279 |
| 2020 | 492516029 | 334854576 | 427971850 | 2681981 | 200483293 | 69 | 151194555 | 82 | 166977105 | 64 | 2034062 | 314 |
| 2021 | 552748211 | 370470970 | 473197808 | 3018665 | 260715475 | 89 | 186810949 | 102 | 212203063 | 81 | 2370746 | 366 |
| 2022 | 588643085 | 404997291 | 499789702 | 5334331 | 296610349 | 102 | 221337270 | 121 | 238794957 | 91 | 4686412 | 723 |
| 2023 | 674374966 | 438411368 | 536873344 | 7452316 | 382342230 | 131 | 254751347 | 139 | 275878599 | 106 | 6804397 | 1050 |
| 2024 | 727016711 | 473456903 | 582474928 | 8214618 | 434983975 | 149 | 289796882 | 158 | 321480183 | 123 | 7566699 | 1168 |
🧩 Phân tích Code R
Cách thực hiện:
Tạo cột Năm dạng số.
Chọn 4 chỉ số chính và sắp xếp theo Năm.
Dùng mutate(across(…)) để tính 2 chỉ số mới cho mỗi biến:
Tăng_trưởng_tuyệt_đối (Giá trị hiện tại - Giá trị năm 2015).
Tăng_trưởng_phần_trăm (% tăng trưởng so với năm 2015).
Dùng kable() để tạo bảng HTML tĩnh, làm tròn số (digits = 0)..
📊 Phân tích Kết quả
stb_clean <- stb_data
names(stb_clean) <- c("ky_bao_cao", "tong_tai_san", "cho_vay_khach_hang", "tien_gui_cua_khach_hang",
"chung_khoan_dau_tu", "von_dieu_le", "loi_nhuan_chua_phan_phoi",
"cac_quy_du_tru", "tong_loi_nhuan_truoc_thue", "chi_phi_du_phong_rui_ro_tin_dung",
"loi_nhuan_thuan_truoc_du_phong", "thu_nhap_lai_thuan", "lai_thuan_dich_vu",
"tong_thu_nhap_hoat_dong", "tong_chi_phi_hoat_dong", "loi_nhuan_sau_thue",
"tong_von_chu_so_huu", "chung_khoan_kinh_doanh", "lai_co_ban_tren_co_phieu_vnd",
"vay_TCTD_khac")
# Hiển thị kết quả 4 dòng bất kì chuẩn hóa tên biến
ten_bien_moi <- data.frame(
`Tên biến gốc` = names(stb_data),
`Tên biến mới` = names(stb_clean)
)
kable(head(ten_bien_moi, 4), caption = "Kết quả chuẩn hóa tên biến") %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE, font_size = 10)| Tên.biến.gốc | Tên.biến.mới |
|---|---|
| ky_bao_cao | ky_bao_cao |
| tong_tai_san | tong_tai_san |
| cho_vay_khach_hang | cho_vay_khach_hang |
| tien_gui_cua_khach_hang | tien_gui_cua_khach_hang |
🧩 Phân tích Code R
📊 Phân tích Kết quả
stb_clean <- stb_clean %>%
mutate(nam = as.numeric(str_extract(ky_bao_cao, "\\d+")),
ky_bao_cao = factor(ky_bao_cao, levels = unique(ky_bao_cao)))
# Hiển thị các năm trong dữ liệu
nam_trong_dulieu <- data.frame(Năm = sort(unique(stb_clean$nam)))
kable(nam_trong_dulieu, caption = "Các năm trong dữ liệu") %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| Năm |
|---|
| 2015 |
| 2016 |
| 2017 |
| 2018 |
| 2019 |
| 2020 |
| 2021 |
| 2022 |
| 2023 |
| 2024 |
🧩 Phân tích Code R
Cách thực hiện:
Dùng hàm str_extract với mã \d+ (tìm chuỗi số) để rút “năm” từ cột ky_bao_cao.
Chuyển năm sang dạng số (as.numeric) và lưu vào cột mới tên nam.
Tạo một bảng (kable) chỉ chứa các giá trị năm duy nhất (unique) đã được sắp xếp (sort).
📊 Phân tích Kết quả
stb_clean <- stb_clean %>%
mutate(giai_doan_phat_trien = case_when(
nam <= 2017 ~ "Giai đoạn ổn định (2015-2017)",
nam %in% 2018:2020 ~ "Giai đoạn tăng trưởng (2018-2020)",
nam >= 2021 ~ "Giai đoạn bứt phá (2021-2024)"
)) %>%
mutate(giai_doan_phat_trien = factor(giai_doan_phat_trien,
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)")))
# Hiển thị phân loại giai đoạn phát triển
phan_loai_giai_doan <- table(stb_clean$giai_doan_phat_trien) %>%
as.data.frame() %>%
rename(Giai_đoạn = Var1, Số_lượng = Freq)
kable(phan_loai_giai_doan, caption = "Phân loại giai đoạn phát triển") %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| Giai_đoạn | Số_lượng |
|---|---|
| Giai đoạn ổn định (2015-2017) | 3 |
| Giai đoạn tăng trưởng (2018-2020) | 3 |
| Giai đoạn bứt phá (2021-2024) | 4 |
🧩 Phân tích Code R
Cách thực hiện:
Dùng case_when để gán nhãn cho các năm (<= 2017: Giai đoạn ổn định, 2018-2020: Giai đoạn tăng trưởng,>= 2021: Giai đoạn bứt phá).
Chuyển cột mới này thành factor để đảm bảo thứ tự hiển thị đúng.
Dùng table() để đếm số lượng bản ghi (số năm) rơi vào mỗi giai đoạn.
Dùng kable() để tạo bảng thống kê .
📊 Phân tích Kết quả
Diễn giải kết quả: Bảng “Phân loại giai đoạn phát triển” cho thấy:
“Giai đoạn bứt phá (2021-2024)” có 4 quan sát (năm).
“Giai đoạn tăng trưởng (2018-2020)” có 3 quan sát (năm).
“Giai đoạn ổn định (2015-2017)” có 3 quan sát (năm).
# Giả định có missing data trong chung_khoan_kinh_doanh
set.seed(123)
missing_indices <- sample(1:nrow(stb_clean), 2)
stb_clean$chung_khoan_kinh_doanh[missing_indices] <- NA
# Tạo bảng thống kê missing trước và sau xử lý
missing_summary <- data.frame(
Thời_điểm = c("Trước xử lý", "Sau xử lý"),
Số_NA = c(sum(is.na(stb_clean$chung_khoan_kinh_doanh)), NA)
)
# Xử lý missing bằng giá trị trung bình
stb_clean <- stb_clean %>%
mutate(chung_khoan_kinh_doanh = ifelse(is.na(chung_khoan_kinh_doanh),
mean(chung_khoan_kinh_doanh, na.rm = TRUE),
chung_khoan_kinh_doanh))
missing_summary$Số_NA[2] <- sum(is.na(stb_clean$chung_khoan_kinh_doanh))
kable(missing_summary, caption = "Xử lý dữ liệu missing") %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| Thời_điểm | Số_NA |
|---|---|
| Trước xử lý | 2 |
| Sau xử lý | 0 |
🧩 Phân tích Code R
Cách thực hiện:
Tạo NA: Code chủ động tạo ra 2 giá trị NA ngẫu nhiên trong cột chung_khoan_kinh_doanh để mô phỏng dữ liệu lỗi.
Thống kê (Trước): Đếm số lượng NA (kết quả là 2) và lưu vào bảng missing_summary.
Xử lý (Impute): Dùng mutate và ifelse để tìm và thay thế các giá trị NA này bằng giá trị trung bình (mean) của chính cột đó (sử dụng na.rm = TRUE để loại trừ NA khi tính trung bình).
Thống kê (Sau): Đếm lại số lượng NA (kết quả là 0) và cập nhật vào bảng missing_summary.
Đầu ra: Xuất bảng missing_summary bằng kable.
📊 Phân tích Kết quả
stb_clean <- stb_clean %>%
mutate(
# Tỷ suất sinh lời
ROA = loi_nhuan_sau_thue / tong_tai_san * 100,
ROE = loi_nhuan_sau_thue / tong_von_chu_so_huu * 100,
# Tỷ lệ an toàn vốn
ty_le_von_chu_so_huu = tong_von_chu_so_huu / tong_tai_san * 100,
# Tỷ lệ huy động và cho vay
ty_le_cho_vay = cho_vay_khach_hang / tien_gui_cua_khach_hang * 100,
# Hiệu quả hoạt động
ty_le_chi_phi = abs(tong_chi_phi_hoat_dong) / tong_thu_nhap_hoat_dong * 100
)
# Tạo bảng mô tả các biến mới
cac_bien_moi <- data.frame(
Biến = c("ROA", "ROE", "ty_le_von_chu_so_huu", "ty_le_cho_vay", "ty_le_chi_phi"),
Mô_tả = c("Tỷ suất sinh lời trên tài sản",
"Tỷ suất sinh lời trên vốn chủ sở hữu",
"Tỷ lệ vốn chủ sở hữu trên tổng tài sản",
"Tỷ lệ cho vay trên tiền gửi khách hàng",
"Tỷ lệ chi phí hoạt động trên thu nhập hoạt động")
)
kable(cac_bien_moi, caption = "Các chỉ số tài chính mới được tạo") %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| Biến | Mô_tả |
|---|---|
| ROA | Tỷ suất sinh lời trên tài sản |
| ROE | Tỷ suất sinh lời trên vốn chủ sở hữu |
| ty_le_von_chu_so_huu | Tỷ lệ vốn chủ sở hữu trên tổng tài sản |
| ty_le_cho_vay | Tỷ lệ cho vay trên tiền gửi khách hàng |
| ty_le_chi_phi | Tỷ lệ chi phí hoạt động trên thu nhập hoạt động |
🧩 Phân tích Code R
Cách thực hiện:
Tính toán (mutate): Dùng mutate để thêm 5 cột mới vào stb_clean. Mỗi cột được tính bằng một công thức tài chính (ví dụ: ROA = loi_nhuan_sau_thue / tong_tai_san * 100).
Mô tả (data.frame): Tạo một data.frame hoàn toàn riêng biệt (cac_bien_moi) chỉ để lưu trữ mô tả (chú thích) cho 5 biến vừa tạo.
📊 Phân tích Kết quả
numeric_vars <- c("tong_tai_san", "cho_vay_khach_hang", "loi_nhuan_sau_thue", "ROA", "ROE")
stb_clean <- stb_clean %>%
mutate(across(all_of(numeric_vars),
list(z_score = ~as.numeric(scale(.))),
.names = "{.col}_z"))
# Hiển thị một vài giá trị để kiểm tra
z_score_sample <- stb_clean %>%
select(nam, ends_with("_z")) %>%
head(5)
kable(z_score_sample, caption = "Mẫu giá trị Z-score của các biến (5 dòng đầu)") %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| nam | tong_tai_san_z | cho_vay_khach_hang_z | loi_nhuan_sau_thue_z | ROA_z | ROE_z |
|---|---|---|---|---|---|
| 2024 | 1.6350788 | 1.5129787 | 1.7567926 | 1.5047036 | 1.5939740 |
| 2023 | 1.2738389 | 1.1745907 | 1.4850445 | 1.4369481 | 1.4184924 |
| 2022 | 0.6855270 | 0.8519554 | 0.7300175 | 0.8944652 | 0.9871817 |
| 2021 | 0.4392081 | 0.5185807 | -0.0954795 | -0.0878336 | -0.1846072 |
| 2020 | 0.0258809 | 0.1746807 | -0.2155018 | -0.0921231 | -0.1840356 |
🧩 Phân tích Code R
Cách thực hiện:
Dùng across và hàm scale() để thực hiện phép tính Z-score (lấy giá trị trừ trung bình rồi chia cho độ lệch chuẩn) cho từng cột trong 5 cột đã chọn.
Lưu kết quả vào các cột mới có tên tương ứng cộng thêm đuôi _z (ví dụ: ROA_z).
Tạo một bảng mẫu (z_score_sample) chỉ chứa 5 dòng đầu tiên (head(5)) của các cột Z-score này để kiểm tra trực quan.
In bảng mẫu bằng kable..
📊 Phân tích Kết quả
stb_clean <- stb_clean %>%
mutate(
hieu_qua_cao = as.numeric(ROE > median(ROE, na.rm = TRUE)),
tang_truong_duong = as.numeric(loi_nhuan_sau_thue > lag(loi_nhuan_sau_thue)),
lai_co_ban_tang = as.numeric(lai_co_ban_tren_co_phieu_vnd > lag(lai_co_ban_tren_co_phieu_vnd))
)
# Thống kê các biến nhị phân
bien_nhi_phan <- stb_clean %>%
summarise(
`Hiệu quả cao (ROE > trung vị)` = sum(hieu_qua_cao, na.rm = TRUE),
`Tăng trưởng dương LNST` = sum(tang_truong_duong, na.rm = TRUE),
`Lãi cơ bản tăng` = sum(lai_co_ban_tang, na.rm = TRUE)
) %>%
pivot_longer(everything(), names_to = "Biến", values_to = "Số_lượng")
kable(bien_nhi_phan, caption = "Thống kê biến nhị phân") %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| Biến | Số_lượng |
|---|---|
| Hiệu quả cao (ROE > trung vị) | 5 |
| Tăng trưởng dương LNST | 1 |
| Lãi cơ bản tăng | 1 |
🧩 Phân tích Code R
Cách thực hiện:
hieu_qua_cao: Gán giá trị 1 nếu ROE của năm đó lớn hơn trung vị (median) của toàn bộ 10 năm, ngược lại gán 0.
tang_truong_duong: Gán 1 nếu loi_nhuan_sau_thue (LNST) lớn hơn năm liền trước (lag), ngược lại gán 0.
lai_co_ban_tang: Gán 1 nếu lai_co_ban_tren_co_phieu_vnd lớn hơn năm liền trước (lag), ngược lại gán 0.
📊 Phân tích Kết quả
Diễn giải kết quả: Bảng “Thống kê biến nhị phân” cho thấy:
Có 5 năm được xếp là “Hiệu quả cao (ROE > trung vị)”.
Có 1 năm có “Tăng trưởng dương LNST” (so với năm trước).
Có 1 năm có “Lãi cơ bản tăng” (so với năm trước).
handle_outliers_iqr <- function(x) {
Q1 <- quantile(x, 0.25, na.rm = TRUE)
Q3 <- quantile(x, 0.75, na.rm = TRUE)
IQR <- Q3 - Q1
lower_bound <- Q1 - 1.5 * IQR
upper_bound <- Q3 + 1.5 * IQR
x[x < lower_bound] <- lower_bound
x[x > upper_bound] <- upper_bound
return(x)
}
# Áp dụng cho các biến tỷ lệ
ratio_vars <- c("ROA", "ROE", "ty_le_cho_vay")
stb_clean <- stb_clean %>%
mutate(across(all_of(ratio_vars), handle_outliers_iqr))
# Hiển thị thống kê sau khi xử lý outliers - SỬA LẠI CÁCH HIỂN THỊ
outlier_stats <- stb_clean %>%
select(all_of(ratio_vars)) %>%
summarise(across(everything(),
list(Min = ~min(., na.rm = TRUE),
Max = ~max(., na.rm = TRUE),
Mean = ~mean(., na.rm = TRUE)),
.names = "{.col}_{.fn}"))
# Chuyển đổi kết quả sang dạng bảng đơn giản hơn
outlier_table <- data.frame(
Biến = ratio_vars,
Min = c(outlier_stats$ROA_Min, outlier_stats$ROE_Min, outlier_stats$ty_le_cho_vay_Min),
Max = c(outlier_stats$ROA_Max, outlier_stats$ROE_Max, outlier_stats$ty_le_cho_vay_Max),
Mean = c(outlier_stats$ROA_Mean, outlier_stats$ROE_Mean, outlier_stats$ty_le_cho_vay_Mean)
)
# Định dạng số với dấu phân cách
outlier_table <- outlier_table %>%
mutate(
Min = format(round(Min, 2), big.mark = ".", decimal.mark = ",", scientific = FALSE),
Max = format(round(Max, 2), big.mark = ".", decimal.mark = ",", scientific = FALSE),
Mean = format(round(Mean, 2), big.mark = ".", decimal.mark = ",", scientific = FALSE)
)
kable(outlier_table, caption = "Thống kê các biến tỷ lệ sau khi xử lý outliers") %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| Biến | Min | Max | Mean |
|---|---|---|---|
| ROA | 0,03 | 1,13 | 0,58 |
| ROE | 0,40 | 23,33 | 10,72 |
| ty_le_cho_vay | 67,35 | 81,66 | 75,24 |
🧩 Phân tích Code R
Cách thực hiện: Định nghĩa một hàm handle_outliers_iqr để “capping” (chặn):
Tính Q1, Q3, và 2 “biên” (dưới = Q1 - 1.5IQR, trên = Q3 + 1.5IQR).
Bất kỳ giá trị nào thấp hơn biên dưới sẽ bị thay bằng chính biên dưới.
Bất kỳ giá trị nào cao hơn biên trên sẽ bị thay bằng chính biên trên.
Áp dụng hàm “capping” này vào 3 biến đã chọn.
Tính toán thống kê (Min, Max, Mean) của 3 biến sau khi đã xử lý và định dạng lại bảng (dùng dấu , cho số thập phân).
📊 Phân tích Kết quả
stb_clean <- stb_clean %>%
mutate(
quy_mo_hieu_qua = log(tong_tai_san) * ROA,
on_dinh_tang_truong = (tong_tai_san / lag(tong_tai_san) - 1) * ROE
)
# Hiển thị một vài giá trị của biến tương tác
bien_tuong_tac <- stb_clean %>%
select(nam, quy_mo_hieu_qua, on_dinh_tang_truong) %>%
head(5)
kable(bien_tuong_tac, caption = "Mẫu giá trị biến tương tác (5 dòng đầu)") %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| nam | quy_mo_hieu_qua | on_dinh_tang_truong |
|---|---|---|
| 2024 | 23.05516 | NA |
| 2023 | 22.46530 | -1.5885938 |
| 2022 | 18.29936 | -2.3553608 |
| 2021 | 10.99361 | -0.5645232 |
| 2020 | 10.89913 | -1.0092872 |
🧩 Phân tích Code R
Cách thực hiện:
quy_mo_hieu_qua: Tính bằng log(Tổng tài sản) * ROA. Biến này kết hợp Quy mô (đã log) với Hiệu quả sinh lời trên tài sản.
on_dinh_tang_truong: Tính bằng (Tốc độ tăng trưởng tài sản - 1) * ROE. Biến này kết hợp Tốc độ tăng trưởng (so với năm trước, dùng hàm lag) với Hiệu quả sinh lời trên vốn chủ.
📊 Phân tích Kết quả
# Chuyển đổi tất cả các cột matrix thành numeric
stb_clean <- stb_clean %>%
mutate(across(where(is.matrix), as.numeric))
# Kiểm tra lại kiểu dữ liệu
kieu_du_lieu <- data.frame(
Biến = names(stb_clean),
Kiểu = sapply(stb_clean, class)
)
# Đảm bảo tất cả các cột đều là atomic vectors
stb_clean <- as.data.frame(lapply(stb_clean, function(x) if(is.list(x)) unlist(x) else x))
# Lưu dữ liệu đã xử lý
write_csv(stb_clean, "STB_data_processed.csv")
processing_summary <- data.frame(
Hạng_mục = c("Số biến ban đầu", "Số biến sau xử lý", "Số quan sát",
"Biến phân loại", "Biến số", "Biến mới tạo"),
Giá_trị = c(ncol(stb_data), ncol(stb_clean), nrow(stb_clean),
sum(sapply(stb_clean, is.factor)),
sum(sapply(stb_clean, is.numeric)),
ncol(stb_clean) - ncol(stb_data))
)
kable(processing_summary, caption = "Thống kê xử lý dữ liệu") %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| Hạng_mục | Giá_trị |
|---|---|
| Số biến ban đầu | 20 |
| Số biến sau xử lý | 37 |
| Số quan sát | 10 |
| Biến phân loại | 2 |
| Biến số | 35 |
| Biến mới tạo | 17 |
🧩 Phân tích Code R
Cách thực hiện:
Sửa lỗi: Chuyển đổi bất kỳ cột nào có thể đang bị lưu ở dạng matrix (lỗi thường gặp) sang dạng numeric (số).
Kiểm tra: Dùng sapply(…, class) để lặp qua từng cột trong stb_clean và lấy ra kiểu dữ liệu (class) của nó (ví dụ: numeric, factor).
In kết quả : Tạo một data.frame từ kết quả kiểm tra (kieu_du_lieu) và dùng kable để in ra bảng (như trong ảnh).
📊 Phân tích Kết quả
variables_to_remove <- c("nam", paste0(c("tong_tai_san", "cho_vay_khach_hang", "loi_nhuan_sau_thue", "ROA", "ROE"), "_z"))
detailed_stats <- stb_clean %>%
select(where(is.numeric)) %>%
# LOẠI BỎ TẤT CẢ BIẾN KHÔNG CẦN THIẾT
select(-any_of(variables_to_remove)) %>%
# Loại bỏ các cột có quá nhiều NA
select(where(~sum(!is.na(.)) > 0.5 * length(.))) %>%
# Chuyển đổi dữ liệu sang dạng dài
pivot_longer(everything(), names_to = "Biến", values_to = "Giá_trị") %>%
# Nhóm theo biến và tính toán thống kê
group_by(Biến) %>%
summarise(
N = sum(!is.na(Giá_trị)),
Min = min(Giá_trị, na.rm = TRUE),
Q1 = quantile(Giá_trị, 0.25, na.rm = TRUE),
Median = median(Giá_trị, na.rm = TRUE),
Mean = mean(Giá_trị, na.rm = TRUE),
Q3 = quantile(Giá_trị, 0.75, na.rm = TRUE),
Max = max(Giá_trị, na.rm = TRUE),
SD = sd(Giá_trị, na.rm = TRUE),
CV = ifelse(Mean != 0, SD/Mean*100, NA),
.groups = 'drop'
) %>%
# Định dạng số với dấu phân cách và làm tròn
mutate(
Min = format(round(Min, 2), big.mark = ".", decimal.mark = ",", scientific = FALSE),
Q1 = format(round(Q1, 2), big.mark = ".", decimal.mark = ",", scientific = FALSE),
Median = format(round(Median, 2), big.mark = ".", decimal.mark = ",", scientific = FALSE),
Mean = format(round(Mean, 2), big.mark = ".", decimal.mark = ",", scientific = FALSE),
Q3 = format(round(Q3, 2), big.mark = ".", decimal.mark = ",", scientific = FALSE),
Max = format(round(Max, 2), big.mark = ".", decimal.mark = ",", scientific = FALSE),
SD = format(round(SD, 2), big.mark = ".", decimal.mark = ",", scientific = FALSE),
CV = ifelse(is.na(CV), NA, format(round(CV, 2), big.mark = ".", decimal.mark = ",", scientific = FALSE))
)
kable(head(detailed_stats, 4),
caption = "Thống kê mô tả chi tiết") %>%
kable_styling(bootstrap_options = c("striped", "hover"), font_size = 9)| Biến | N | Min | Q1 | Median | Mean | Q3 | Max | SD | CV |
|---|---|---|---|---|---|---|---|---|---|
| ROA | 10 | 0,03 | 0,35 | 0,54 | 0,58 | 0,82 | 1,13 | 0,37 | 63,39 |
| ROE | 10 | 0,40 | 5,63 | 9,22 | 10,72 | 16,21 | 23,33 | 7,91 | 73,81 |
| cac_quy_du_tru | 10 | 2.419.833,00 | 2.592.452,75 | 3.150.204,50 | 3.448.636,00 | 4.077.995,00 | 5.378.109,00 | 1.040.902,70 | 30,18 |
| chi_phi_du_phong_rui_ro_tin_dung | 10 | -4.990.584,00 | -2.960.234,50 | -2.204.451,50 | -2.434.481,70 | -1.674.426,75 | -696.243,00 | 1.360.434,29 | -55,88 |
🧩 Phân tích Code R
Cách thực hiện:
Lọc (select): Chỉ chọn các cột numeric và loại bỏ các cột không cần thống kê (như nam, các cột _z đã tạo trước đó).
Xoay (pivot_longer): Đây là bước quan trọng nhất. Code chuyển đổi dataframe từ dạng rộng (mỗi biến 1 cột) sang dạng dài (chỉ 2 cột: Biến và Giá_trị).
Thống kê (group_by/summarise): Nhóm dữ liệu theo cột Biến mới, sau đó tính 9 chỉ số thống kê mô tả (N, Min, Mean…) cho từng biến.
Định dạng (mutate/kable): Làm đẹp các con số (làm tròn, dùng dấu , thập phân) và in ra bảng HTML.
📊 Phân tích Kết quả
Diễn giải kết quả: Bảng “Thống kê mô tả chi tiết” liệt kê 38 biến số nhưng trước mắt là 4 do dùng head() . Từ bảng này, ta có thể thấy:
Số lượng (N): Hầu hết các biến có N=10 (10 năm). Biến lai_co_ban_tang (và on_dinh_tang_truong) có N=9. Điều này hoàn toàn logic vì chúng được tính bằng hàm lag() (so với năm trước), khiến năm đầu tiên bị NA.
Biến động (CV): Cột CV (Hệ số biến thiên) đo lường rủi ro. Các chỉ số có CV rất cao như lai_co_ban_tang (300%) hay chung_khoan_kinh_doanh (166,56%) cho thấy mức độ biến động rất lớn so với giá trị trung bình của chúng.
Giá trị (Min): Một số biến như chi_phi_du_phong… và on_dinh_tang_truong có giá trị Min âm, điều này phù hợp (chi phí dự phòng có thể hoàn nhập, tăng trưởng có thể âm).
cor_vars <- c("tong_tai_san", "cho_vay_khach_hang", "tien_gui_cua_khach_hang",
"loi_nhuan_sau_thue", "ROA", "ROE", "tong_von_chu_so_huu")
cor_matrix <- stb_clean %>%
select(all_of(cor_vars)) %>%
cor(use = "complete.obs")
cor_df <- as.data.frame(cor_matrix) %>%
rownames_to_column("Biến")
cat("MA TRẬN TƯƠNG QUAN GIỮA CÁC CHỈ SỐ TÀI CHÍNH CHÍNH:\n")## MA TRẬN TƯƠNG QUAN GIỮA CÁC CHỈ SỐ TÀI CHÍNH CHÍNH:
kable(cor_df, caption = "Hệ số tương quan giữa các biến", digits = 3) %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| Biến | tong_tai_san | cho_vay_khach_hang | tien_gui_cua_khach_hang | loi_nhuan_sau_thue | ROA | ROE | tong_von_chu_so_huu |
|---|---|---|---|---|---|---|---|
| tong_tai_san | 1.000 | 0.994 | 0.995 | 0.967 | 0.957 | 0.957 | 0.964 |
| cho_vay_khach_hang | 0.994 | 1.000 | 0.998 | 0.952 | 0.953 | 0.950 | 0.963 |
| tien_gui_cua_khach_hang | 0.995 | 0.998 | 1.000 | 0.946 | 0.946 | 0.943 | 0.964 |
| loi_nhuan_sau_thue | 0.967 | 0.952 | 0.946 | 1.000 | 0.983 | 0.991 | 0.897 |
| ROA | 0.957 | 0.953 | 0.946 | 0.983 | 1.000 | 0.995 | 0.885 |
| ROE | 0.957 | 0.950 | 0.943 | 0.991 | 0.995 | 1.000 | 0.870 |
| tong_von_chu_so_huu | 0.964 | 0.963 | 0.964 | 0.897 | 0.885 | 0.870 | 1.000 |
🧩 Phân tích Code R
Cách thực hiện:
Định nghĩa 7 biến cần phân tích (cor_vars).
Lọc (select) 7 biến này từ stb_clean.
Dùng hàm cor() để tính toán hệ số tương quan giữa từng cặp biến.
Dùng kable() để định dạng ma trận kết quả thành bảng HTML (như trong ảnh).
📊 Phân tích Kết quả
Diễn giải kết quả: Bảng ma trận “Hệ số tương quan” hiển thị tất cả các giá trị đều rất cao và dương (hầu hết > 0.9, thấp nhất cũng là 0.870):
Hệ số gần 1.0 (ví dụ: tong_tai_san và cho_vay_khach_hang là 0.994) cho thấy các biến này gần như di chuyển đồng bộ với nhau (biến này tăng thì biến kia cũng tăng).
loi_nhuan_sau_thue (lợi nhuận) tương quan cực cao với ROA (0.983) và ROE (0.991) là điều dễ hiểu, vì lợi nhuận là tử số (numerator) trong công thức tính cả hai chỉ số này.
growth_analysis <- stb_clean %>%
select(nam, tong_tai_san, cho_vay_khach_hang, loi_nhuan_sau_thue) %>%
# BƯỚC 1: Sắp xếp TĂNG DẦN theo năm
arrange(nam) %>%
# BƯỚC 2: Tính toán (bây giờ lag() sẽ lấy đúng năm TRƯỚC đó)
mutate(across(-nam, list(
Tang_truong_tuyet_doi = ~. - lag(.),
Tang_truong_phan_tram = ~(. / lag(.) - 1) * 100
), .names = "{.col}_{.fn}")) %>%
# Lọc bỏ dòng NA (sẽ là năm 2015)
filter(!is.na(tong_tai_san_Tang_truong_tuyet_doi)) %>%
# BƯỚC 3: Sắp xếp GIẢM DẦN trở lại để xem
arrange(desc(nam))
# In kết quả
cat("PHÂN TÍCH TĂNG TRƯỞNG HÀNG NĂM (5 BIẾN ĐẦU TIÊN):\n")## PHÂN TÍCH TĂNG TRƯỞNG HÀNG NĂM (5 BIẾN ĐẦU TIÊN):
# BƯỚC 4: Chọn 5 cột đầu tiên và in ra
growth_analysis %>%
select(1:5) %>%
head(4) %>%
kable(caption = "Tăng trưởng hàng năm (5 biến đầu tiên)", digits = 2) %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| nam | tong_tai_san | cho_vay_khach_hang | loi_nhuan_sau_thue | tong_tai_san_Tang_truong_tuyet_doi |
|---|---|---|---|---|
| 2024 | 727016711 | 473456903 | 8214618 | 52641745 |
| 2023 | 674374966 | 438411368 | 7452316 | 85731881 |
| 2022 | 588643085 | 404997291 | 5334331 | 35894874 |
| 2021 | 552748211 | 370470970 | 3018665 | 60232182 |
🧩 Phân tích Code R
Cách thực hiện:
Sử dụng across kết hợp lag() để so sánh giá trị năm hiện tại (.) với năm liền trước (lag(.)).
Tuyệt đối: ~. - lag(.) (tức là hiện tại - quá khứ).
Phần trăm: ~(. / lag(.) - 1) * 100.
Lọc bỏ dòng đầu tiên (sẽ bị NA do không có gì để so sánh) và in kết quả ra bảng.
📊 Phân tích Kết quả
phase_stats <- stb_clean %>%
group_by(giai_doan_phat_trien) %>%
summarise(
ROA_trung_binh = mean(ROA, na.rm = TRUE),
ROE_trung_binh = mean(ROE, na.rm = TRUE),
So_nam = n(),
.groups = 'drop'
)
cat("SO SÁNH HIỆU QUẢ HOẠT ĐỘNG THEO GIAI ĐOẠN:\n")## SO SÁNH HIỆU QUẢ HOẠT ĐỘNG THEO GIAI ĐOẠN:
kable(phase_stats, caption = "Chỉ số hiệu quả trung bình theo giai đoạn", digits = 2) %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| giai_doan_phat_trien | ROA_trung_binh | ROE_trung_binh | So_nam |
|---|---|---|---|
| Giai đoạn ổn định (2015-2017) | 0.19 | 2.81 | 3 |
| Giai đoạn tăng trưởng (2018-2020) | 0.51 | 8.57 | 3 |
| Giai đoạn bứt phá (2021-2024) | 0.92 | 18.26 | 4 |
🧩 Phân tích Code R
Cách thực hiện:
group_by(giai_doan_phat_trien): Nhóm dữ liệu theo từng giai đoạn (Ổn định, Tăng trưởng, Bứt phá).
summarise(…): Tính toán các giá trị tóm tắt cho mỗi nhóm: mean(ROA) & mean(ROE): Tính ROA và ROE trung bình.
n(): Đếm số lượng bản ghi (số năm) trong mỗi nhóm.
Kết quả: Một bảng tóm tắt hiển thị các chỉ số hiệu quả trung bình theo 3 giai đoạn phát triển.
📊 Phân tích Kết quả
Diễn giải kết quả: Bảng “Chỉ số hiệu quả trung bình theo giai đoạn” cho thấy sự cải thiện rõ rệt:
ROE_trung_binh: Tăng từ 19% (GĐ ổn định) -> 51% (GĐ tăng trưởng) -> 92% (GĐ bứt phá).
ROA_trung_binh: Tăng từ 2.81 -> 8.57 -> 18.26 .
distribution_stats <- stb_clean %>%
summarise(
ROA_mean = mean(ROA, na.rm = TRUE),
ROA_sd = sd(ROA, na.rm = TRUE),
ROA_skewness = moments::skewness(ROA, na.rm = TRUE),
ROA_kurtosis = moments::kurtosis(ROA, na.rm = TRUE),
ROE_mean = mean(ROE, na.rm = TRUE),
ROE_sd = sd(ROE, na.rm = TRUE),
ROE_skewness = moments::skewness(ROE, na.rm = TRUE),
ROE_kurtosis = moments::kurtosis(ROE, na.rm = TRUE)
)
cat("PHÂN TÍCH PHÂN BỐ ROA VÀ ROE:\n")## PHÂN TÍCH PHÂN BỐ ROA VÀ ROE:
kable(t(distribution_stats), caption = "Thống kê phân bố ROA và ROE", digits = 4) %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| ROA_mean | 0.5783 |
| ROA_sd | 0.3666 |
| ROA_skewness | 0.2764 |
| ROA_kurtosis | 2.0143 |
| ROE_mean | 10.7181 |
| ROE_sd | 7.9108 |
| ROE_skewness | 0.4955 |
| ROE_kurtosis | 1.9238 |
🧩 Phân tích Code R
Cách thực hiện:
summarise(…): Tạo một bảng tóm tắt duy nhất.
mean() và sd(): Tính giá trị trung bình và độ lệch chuẩn.
moments::skewness() và moments::kurtosis(): Sử dụng thư viện moments để đo lường độ xiên (sự bất đối xứng) và độ nhọn (mức độ tập trung/đuôi) của phân bố dữ liệu.
t(distribution_stats): Chuyển vị bảng (transpose), biến các cột (ROA_mean, ROE_mean…) thành các hàng để dễ đọc hơn.
📊 Phân tích Kết quả
Diễn giải kết quả: Bảng “Thống kê phân bố ROA và ROE”:
Trung bình, công ty đạt ROA 0.58% và ROE 10.72%.
Tuy nhiên, độ lệch chuẩn (SD) rất cao (ROE_sd là 7.91, gần bằng 75% giá trị trung bình), cho thấy mức độ biến động và không ổn định rất lớn của hai chỉ số này qua các năm.
capital_analysis <- stb_clean %>%
select(nam, ty_le_von_chu_so_huu) %>%
mutate(
Trung_binh_dong = cummean(ty_le_von_chu_so_huu),
Xu_huong = ifelse(ty_le_von_chu_so_huu > lag(ty_le_von_chu_so_huu), "Tăng", "Giảm")
)
cat("PHÂN TÍCH TỶ LỆ AN TOÀN VỐN:\n")## PHÂN TÍCH TỶ LỆ AN TOÀN VỐN:
kable(head(capital_analysis, 4), caption = "Tỷ lệ vốn chủ sở hữu và xu hướng", digits = 2) %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| nam | ty_le_von_chu_so_huu | Trung_binh_dong | Xu_huong |
|---|---|---|---|
| 2024 | 4.84 | 4.84 | NA |
| 2023 | 5.04 | 4.94 | Tăng |
| 2022 | 4.89 | 4.92 | Giảm |
| 2021 | 5.90 | 5.17 | Tăng |
🧩 Phân tích Code R
Cách thực hiện:
select(…): Chỉ giữ lại cột nam (năm) và ty_le_von_chu_so_huu.
mutate(…): Tạo ra 2 cột mới: Đầu tiên là cummean(…). Tiếp tục là ifelse(…, lag(…), …): Tạo cột “Xu_huong”. Nó so sánh giá trị của hàng hiện tại với giá trị của hàng ngay phía trên (ví dụ: so sánh 2023 với 2024).
📊 Phân tích Kết quả
Diễn giải kết quả: Bảng “Tỷ lệ vốn chủ sở hữu và xu hướng” cho thấy:
Xu hướng chính: Tỷ lệ vốn chủ sở hữu (ty_le_von_chu_so_huu) có xu hướng giảm rõ rệt và nhất quán trong dài hạn, giảm từ 7.56% (năm 2015) xuống chỉ còn 4.84% (năm 2024).
Việc tỷ lệ VCSH liên tục giảm cho thấy công ty đang tăng cường sử dụng nợ vay, tức là tăng đòn bẩy tài chính.
efficiency_stats <- stb_clean %>%
summarise(
Vong_quay_tai_san = mean(tong_thu_nhap_hoat_dong / tong_tai_san, na.rm = TRUE),
Hieu_qua_sinh_loi = mean(loi_nhuan_sau_thue / tong_thu_nhap_hoat_dong, na.rm = TRUE) * 100,
Ty_le_loi_nhuan_tren_doanh_thu = mean(loi_nhuan_sau_thue / (tong_thu_nhap_hoat_dong), na.rm = TRUE) * 100,
Ty_le_chi_phi_hoat_dong = mean(abs(tong_chi_phi_hoat_dong) / tong_thu_nhap_hoat_dong, na.rm = TRUE) * 100
)
cat("CHỈ SỐ HIỆU QUẢ SỬ DỤNG VỐN VÀ CHI PHÍ:\n")## CHỈ SỐ HIỆU QUẢ SỬ DỤNG VỐN VÀ CHI PHÍ:
kable(t(efficiency_stats), caption = "Hiệu quả sử dụng vốn và kiểm soát chi phí", digits = 4) %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| Vong_quay_tai_san | 0.0325 |
| Hieu_qua_sinh_loi | 16.2774 |
| Ty_le_loi_nhuan_tren_doanh_thu | 16.2774 |
| Ty_le_chi_phi_hoat_dong | 64.1724 |
🧩 Phân tích Code R
Giải thích Khối:
summarise(…): Tính toán 4 chỉ số trung bình (mean()) trên toàn bộ 10 năm:
Vong_quay_tai_san (Tổng thu nhập HĐ / Tổng tài sản).
Hieu_qua_sinh_loi (LNST / Tổng thu nhập HĐ).
Ty_le_loi_nhuan_tren_doanh_thu (tương tự Hieu_su_sinh_loi).
Ty_le_chi_phi_hoat_dong (Chi phí HĐ / Tổng thu nhập HĐ - hay còn gọi là CIR).
📊 Phân tích Kết quả
Diễn giải kết quả: Bảng “Hiệu quả sử dụng vốn và kiểm soát chi phí” cho thấy các giá trị trung bình 10 năm:
Vòng quay tài sản (0.0325): Chỉ số này rất thấp, cho thấy cứ 1 đồng tài sản chỉ tạo ra 0.0325 đồng thu nhập hoạt động. (Điều này thường gặp ở ngành ngân hàng do quy mô tổng tài sản rất lớn).
Biên lợi nhuận ròng (16.28%): Cứ 100 đồng thu nhập hoạt động, doanh nghiệp thu về khoảng 16.28 đồng lợi nhuận sau thuế.
Tỷ lệ chi phí hoạt động (64.17%): Chi phí hoạt động chiếm 64.17% tổng thu nhập hoạt động.
credit_risk <- stb_clean %>%
select(nam, chi_phi_du_phong_rui_ro_tin_dung, cho_vay_khach_hang) %>%
mutate(
Ty_le_du_phong = abs(chi_phi_du_phong_rui_ro_tin_dung) / cho_vay_khach_hang * 100,
Du_phong_binh_quan = mean(abs(chi_phi_du_phong_rui_ro_tin_dung), na.rm = TRUE),
Xu_huong_du_phong = ifelse(chi_phi_du_phong_rui_ro_tin_dung > lag(chi_phi_du_phong_rui_ro_tin_dung), "Tăng", "Giảm")
)
cat("PHÂN TÍCH RỦI RO TÍN DỤNG VÀ DỰ PHÒNG:\n")## PHÂN TÍCH RỦI RO TÍN DỤNG VÀ DỰ PHÒNG:
kable(head(credit_risk, 4), caption = "Chỉ số rủi ro tín dụng và dự phòng", digits = 4) %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| nam | chi_phi_du_phong_rui_ro_tin_dung | cho_vay_khach_hang | Ty_le_du_phong | Du_phong_binh_quan | Xu_huong_du_phong |
|---|---|---|---|---|---|
| 2024 | -1921365 | 473456903 | 0.4058 | 2434482 | NA |
| 2023 | -2730016 | 438411368 | 0.6227 | 2434482 | Giảm |
| 2022 | -4990584 | 404997291 | 1.2323 | 2434482 | Giảm |
| 2021 | -4152029 | 370470970 | 1.1207 | 2434482 | Tăng |
🧩 Phân tích Code R
Cách thực hiện:
select: Trích xuất ba cột dữ liệu chính: nam, chi_phi_du_phong_rui_ro_tin_dung (chi phí dự phòng) và cho_vay_khach_hang.
mutate: Tạo ra ba cột mới:
Ty_le_du_phong: Tính tỷ lệ chi phí dự phòng (dùng giá trị tuyệt đối abs) trên tổng dư nợ cho vay.
Du_phong_binh_quan: Tính mức dự phòng trung bình (dùng giá trị tuyệt đối) cho toàn bộ giai đoạn.
Xu_huong_du_phong: So sánh chi phí dự phòng (số âm) của năm hiện tại với năm trước (lag) để gán nhãn “Tăng” hoặc “Giảm”.
📊 Phân tích Kết quả
income_analysis <- stb_clean %>%
select(nam, thu_nhap_lai_thuan, lai_thuan_dich_vu, tong_thu_nhap_hoat_dong) %>%
mutate(
Ty_le_thu_nhap_lai = thu_nhap_lai_thuan / tong_thu_nhap_hoat_dong * 100,
Ty_le_thu_nhap_dich_vu = lai_thuan_dich_vu / tong_thu_nhap_hoat_dong * 100,
Ty_le_thu_nhap_khac = (tong_thu_nhap_hoat_dong - thu_nhap_lai_thuan - lai_thuan_dich_vu) / tong_thu_nhap_hoat_dong * 100
)
cat("PHÂN TÍCH CƠ CẤU THU NHẬP:\n")## PHÂN TÍCH CƠ CẤU THU NHẬP:
kable(head(income_analysis, 4), caption = "Tỷ trọng các loại thu nhập", digits = 2) %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| nam | thu_nhap_lai_thuan | lai_thuan_dich_vu | tong_thu_nhap_hoat_dong | Ty_le_thu_nhap_lai | Ty_le_thu_nhap_dich_vu | Ty_le_thu_nhap_khac |
|---|---|---|---|---|---|---|
| 2024 | 22512910 | 4908618 | 29569157 | 76.14 | 16.60 | 7.26 |
| 2023 | 21243756 | 4544754 | 27561432 | 77.08 | 16.49 | 6.43 |
| 2022 | 18116531 | 4606313 | 24717375 | 73.29 | 18.64 | 8.07 |
| 2021 | 13293486 | 3863308 | 18986347 | 70.02 | 20.35 | 9.64 |
🧩 Phân tích Code R
Cách thực hiện:
Sử dụng select để lọc 4 cột chính: nam, thu_nhap_lai_thuan, lai_thuan_dich_vu, và tong_thu_nhap_hoat_dong từ bộ dữ liệu stb_clean.
Sử dụng mutate để tạo 3 cột tỷ lệ phần trăm mới bằng cách chia từng nguồn thu nhập cho tổng thu nhập hoạt động.
📊 Phân tích Kết quả
leverage_stats <- stb_clean %>%
mutate(
Tong_no = tong_tai_san - tong_von_chu_so_huu,
Ty_le_no = Tong_no / tong_tai_san * 100,
Don_bay_tai_chinh = tong_tai_san / tong_von_chu_so_huu
) %>%
select(nam, Ty_le_no, Don_bay_tai_chinh)
cat("PHÂN TÍCH ĐÒN BẨY TÀI CHÍNH:\n")## PHÂN TÍCH ĐÒN BẨY TÀI CHÍNH:
kable(head(leverage_stats,4 ), caption = "Chỉ số đòn bẩy tài chính", digits = 2) %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| nam | Ty_le_no | Don_bay_tai_chinh |
|---|---|---|
| 2024 | 95.16 | 20.65 |
| 2023 | 94.96 | 19.85 |
| 2022 | 95.11 | 20.45 |
| 2021 | 94.10 | 16.95 |
🧩 Phân tích Code R
Cách thực hiện:
mutate(…): Sử dụng mutate để tạo 3 cột tính toán mới:
Tong_no: Tính tổng nợ (Tài sản - Vốn chủ sở hữu).
Ty_le_no: Tính tỷ lệ Nợ trên Tổng tài sản (%).
Don_bay_tai_chinh: Tính đòn bẩy tài chính (Tổng tài sản / Vốn chủ sở hữu).
select(…): Chỉ chọn và giữ lại các cột nam, Ty_le_no, và Don_bay_tai_chinh cho bảng kết quả cuối cùng.
📊 Phân tích Kết quả
Diễn giải kết quả: Bảng “Chỉ số đòn bẩy tài chính” cho thấy xu hướng tăng rõ rệt:
Ty_le_no (Tỷ lệ nợ): Tăng từ 92.44% lên 95.16%. Điều này cho thấy công ty ngày càng phụ thuộc nhiều hơn vào nguồn vốn vay để tài trợ cho tài sản. Mức tỷ lệ nợ trên 90% được xem là rất cao, tiềm ẩn rủi ro lớn.
Don_bay_tai_chinh (Đòn bẩy): Tăng từ 13.23 lên 20.65. Nghĩa là vào năm 2015, 1 đồng vốn chủ sở hữu “gánh” 13.23 đồng tài sản, nhưng đến 2024, 1 đồng vốn chủ đã phải “gánh” tới 20.65 đồng tài sản.
eps_analysis <- stb_clean %>%
select(nam, lai_co_ban_tren_co_phieu_vnd) %>%
mutate(
Tang_truong_EPS = (lai_co_ban_tren_co_phieu_vnd / lag(lai_co_ban_tren_co_phieu_vnd) - 1) * 100,
Trung_binh_dong_EPS = cummean(lai_co_ban_tren_co_phieu_vnd)
)
cat("PHÂN TÍCH XU HƯỚNG LÃI CƠ BẢN TRÊN CỔ PHIẾU:\n")## PHÂN TÍCH XU HƯỚNG LÃI CƠ BẢN TRÊN CỔ PHIẾU:
kable(head(eps_analysis, 4), caption = "EPS và tăng trưởng EPS", digits = 2) %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| nam | lai_co_ban_tren_co_phieu_vnd | Tang_truong_EPS | Trung_binh_dong_EPS |
|---|---|---|---|
| 2024 | 4135 | NA | 4135.00 |
| 2023 | 3719 | -10.06 | 3927.00 |
| 2022 | 2593 | -30.28 | 3482.33 |
| 2021 | 1414 | -45.47 | 2965.25 |
🧩 Phân tích Code R
Cách thực hiện:
select: Chọn 2 cột nam (năm) và lai_co_ban_tren_co_phieu_vnd (EPS) từ dữ liệu stb_clean.
mutate: Tạo 2 cột mới:
1.Tang_truong_EPS: Tính tăng trưởng EPS. Hàm lag() lấy giá trị của hàng liền trước. Do dữ liệu đang sắp xếp giảm dần theo năm (2024, 2023…), phép tính này đang lấy (EPS_năm_nay / EPS_năm_sau - 1).
2.Trung_binh_dong_EPS: Tính trung bình cộng dồn (cummean). Do sắp xếp giảm dần, nó tính trung bình lùi (ví dụ: hàng 2023 là trung bình của 2024 và 2023; hàng 2022 là trung bình của 2024, 2023, 2022).
📊 Phân tích Kết quả
Diễn giải kết quả: Bảng “EPS và tăng trưởng EPS” cho thấy:
Xu hướng EPS (lai_co_ban…): Nhìn chung, EPS có xu hướng tăng trưởng dài hạn, từ 444 (năm 2015) lên 4135 (năm 2024).
Trung bình động (Trung_binh_dong_EPS): Cột này cho thấy giá trị trung bình EPS của toàn bộ giai đoạn 2015-2024 là 1606.60 (giá trị ở hàng cuối cùng).
size_efficiency <- stb_clean %>%
select(tong_tai_san, ROA, ROE) %>%
summarise(
Correlation_Size_ROA = cor(tong_tai_san, ROA, use = "complete.obs"),
Correlation_Size_ROE = cor(tong_tai_san, ROE, use = "complete.obs"),
Correlation_ROA_ROE = cor(ROA, ROE, use = "complete.obs")
)
cat("PHÂN TÍCH MỐI QUAN HỆ GIỮA QUY MÔ VÀ HIỆU QUẢ:\n")## PHÂN TÍCH MỐI QUAN HỆ GIỮA QUY MÔ VÀ HIỆU QUẢ:
kable(t(size_efficiency), caption = "Hệ số tương quan giữa quy mô và hiệu quả", digits = 4) %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| Correlation_Size_ROA | 0.9568 |
| Correlation_Size_ROE | 0.9575 |
| Correlation_ROA_ROE | 0.9951 |
🧩 Phân tích Code R
Cách thực hiện:
select: Chọn 3 cột dữ liệu quan tâm: tong_tai_san, ROA, và ROE.
summarise(cor(…)): Tính 3 hệ số tương quan: (Tài sản & ROA), (Tài sản & ROE), và (ROA & ROE). use = “complete.obs” đảm bảo chỉ dùng các cặp dữ liệu không bị thiếu.
📊 Phân tích Kết quả
Diễn giải kết quả: Bảng “Hệ số tương quan giữa quy mô và hiệu quả” cho thấy:
Quy mô vs. Hiệu quả (0.9568 và 0.9575): Có mối tương quan dương rất mạnh giữa quy mô (tong_tai_san) và cả hai chỉ số ROA lẫn ROE. Điều này ngụ ý rằng trong tập dữ liệu này, ngân hàng có quy mô tài sản càng lớn thì hiệu quả sinh lời (ROA, ROE) càng cao.
ROA vs. ROE (0.9951): Có mối tương quan gần như tuyệt đối. Điều này là hợp lý vì cả hai đều là chỉ số đo lường khả năng sinh lời và có quan hệ chặt chẽ với nhau về mặt công thức.
volatility <- stb_clean %>%
summarise(
ROA_sd = sd(ROA, na.rm = TRUE),
ROE_sd = sd(ROE, na.rm = TRUE),
EPS_sd = sd(lai_co_ban_tren_co_phieu_vnd, na.rm = TRUE),
Tang_truong_TB_sd = sd((loi_nhuan_sau_thue / lag(loi_nhuan_sau_thue) - 1) * 100, na.rm = TRUE),
Ty_le_von_sd = sd(ty_le_von_chu_so_huu, na.rm = TRUE)
)
cat("PHÂN TÍCH BIẾN ĐỘNG CÁC CHỈ SỐ:\n")## PHÂN TÍCH BIẾN ĐỘNG CÁC CHỈ SỐ:
kable(t(volatility), caption = "Độ lệch chuẩn của các chỉ số chính", digits = 4) %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| ROA_sd | 0.3666 |
| ROE_sd | 7.9108 |
| EPS_sd | 1405.8885 |
| Tang_truong_TB_sd | 222.5038 |
| Ty_le_von_sd | 0.8462 |
🧩 Phân tích Code R
Cách thực hiện:
Sử dụng summarise để tính toán một giá trị duy nhất cho mỗi chỉ số trên toàn bộ dữ liệu.
Áp dụng hàm sd() (standard deviation - độ lệch chuẩn) để đo lường sự phân tán của các chỉ số: ROA, ROE, EPS (lai_co_ban_tren_co_phieu_vnd), và Tỷ lệ vốn CSH.
Riêng chỉ số tăng trưởng, code trước tiên tính tỷ lệ tăng trưởng lợi nhuận (%) so với năm trước (lag), sau đó mới tính độ lệch chuẩn của các tỷ lệ tăng trưởng này.của một chuỗi dữ liệu. SD càng cao, biến động (rủi ro) càng lớn.
📊 Phân tích Kết quả
Diễn giải kết quả: Bảng “Độ lệch chuẩn của các chỉ số chính” cho thấy:
Chỉ số ổn định: ROA_sd (0.3666) và Ty_le_von_sd (0.8462) có giá trị rất thấp. Điều này cho thấy tỷ suất sinh lời trên tổng tài sản (ROA) và tỷ lệ vốn chủ sở hữu rất ổn định, ít biến động qua các năm.
Chỉ số biến động mạnh: Ngược lại, EPS_sd (1405.88) và Tang_truong_TB_sd (222.50) có độ lệch chuẩn cực kỳ cao.
cost_income <- stb_clean %>%
mutate(Ty_le_chi_phi_thu_nhap = abs(tong_chi_phi_hoat_dong) / tong_thu_nhap_hoat_dong * 100) %>%
select(nam, Ty_le_chi_phi_thu_nhap, tong_chi_phi_hoat_dong, tong_thu_nhap_hoat_dong) %>%
arrange(nam) %>%
mutate(
Xu_huong_CI = ifelse(Ty_le_chi_phi_thu_nhap > lag(Ty_le_chi_phi_thu_nhap), "Tăng", "Giảm"),
Chenh_lech_thu_chi = tong_thu_nhap_hoat_dong - abs(tong_chi_phi_hoat_dong)
) %>%
arrange(desc(nam))
cat("PHÂN TÍCH TỶ LỆ CHI PHÍ TRÊN THU NHẬP:\n")## PHÂN TÍCH TỶ LỆ CHI PHÍ TRÊN THU NHẬP:
kable(head(cost_income, 4), caption = "Chỉ số chi phí trên thu nhập (C/I)", digits = 2) %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| nam | Ty_le_chi_phi_thu_nhap | tong_chi_phi_hoat_dong | tong_thu_nhap_hoat_dong | Xu_huong_CI | Chenh_lech_thu_chi |
|---|---|---|---|---|---|
| 2024 | 58.82 | -17391860 | 29569157 | Tăng | 12177297 |
| 2023 | 56.36 | -15534505 | 27561432 | Tăng | 12026927 |
| 2022 | 52.84 | -13061765 | 24717375 | Giảm | 11655610 |
| 2021 | 57.74 | -10963268 | 18986347 | Giảm | 8023079 |
🧩 Phân tích Code R
Cách thực hiện:
mutate(Ty_le_chi_phi_thu_nhap = …): Tạo cột C/I ratio bằng cách lấy chi phí hoạt động (giá trị tuyệt đối) chia cho thu nhập hoạt động (nhân 100).
mutate(Xu_huong_CI = …, Chenh_lech_thu_chi = …): Thêm 2 cột mới:
1.Xu_huong_CI: So sánh C/I năm hiện tại với năm trước đó (dùng lag()) để xác định xu hướng “Tăng” hay “Giảm”.
2.Chenh_lech_thu_chi: Tính lợi nhuận (thu nhập trừ chi phí).
📊 Phân tích Kết quả
Diễn giải kết quả: Bảng “Chỉ số chi phí trên thu nhập (C/I)” cho thấy một xu hướng cải thiện rất tích cực:
Hiệu quả chi phí (C/I): Hiệu quả được cải thiện liên tục từ 2016 (86.96) đến mức tốt nhất vào 2022 (52.84). Tuy nhiên, xu hướng này đã đảo chiều “Tăng” trong 2 năm gần đây (2023-2024), cho thấy chi phí đang tăng nhanh hơn thu nhập.
Lợi nhuận (Chênh lệch): Mặc dù C/I biến động, lợi nhuận tuyệt đối vẫn tăng trưởng mạnh mẽ và đều đặn qua các năm.
liquidity <- stb_clean %>%
mutate(
Ty_le_thanh_khoan = tien_gui_cua_khach_hang / cho_vay_khach_hang * 100,
Chenh_lech_ky_han = cho_vay_khach_hang - tien_gui_cua_khach_hang,
Ty_le_cho_vay_TTS = cho_vay_khach_hang / tong_tai_san * 100
) %>%
select(nam, Ty_le_thanh_khoan, Chenh_lech_ky_han, Ty_le_cho_vay_TTS)
cat("PHÂN TÍCH THANH KHOẢN VÀ CHÊNH LỆCH KỲ HẠN:\n")## PHÂN TÍCH THANH KHOẢN VÀ CHÊNH LỆCH KỲ HẠN:
kable(head(liquidity, 4), caption = "Chỉ số thanh khoản và cơ cấu tài sản", digits = 2) %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| nam | Ty_le_thanh_khoan | Chenh_lech_ky_han | Ty_le_cho_vay_TTS |
|---|---|---|---|
| 2024 | 123.03 | -109018025 | 65.12 |
| 2023 | 122.46 | -98461976 | 65.01 |
| 2022 | 123.41 | -94792411 | 68.80 |
| 2021 | 127.73 | -102726838 | 67.02 |
🧩 Phân tích Code R
Cách thực hiện:
mutate(): Tạo ra ba cột (chỉ số) mới:
1.Ty_le_thanh_khoan: (Tiền gửi khách hàng / Cho vay khách hàng) * 100.
2.Chenh_lech_ky_han: (Cho vay khách hàng - Tiền gửi khách hàng).
3.Ty_le_cho_vay_TTS: (Cho vay khách hàng / Tổng tài sản) * 100.
select(): Chọn lọc và giữ lại chỉ 4 cột: nam và 3 chỉ số vừa tạo.
📊 Phân tích Kết quả
Diễn giải kết quả: Bảng “Chỉ số chi phí trên thu nhập (C/I)” cho thấy một xu hướng cải thiện rất tích cực:
Ty_le_thanh_khoan (Thanh khoản): Có xu hướng giảm dần qua các năm (từ 148.48 xuống 123.03). Tuy nhiên, chỉ số này luôn lớn hơn 100, cho thấy lượng tiền gửi của khách hàng luôn cao hơn lượng cho vay.
Chenh_lech_ky_han (Chênh lệch kỳ hạn): Luôn là số âm (vì Tiền gửi > Cho vay, dựa theo chỉ số trên). Giá trị âm này có xu hướng ngày càng lớn (từ -77 tỷ lên -109 tỷ), cho thấy khoảng cách chênh lệch (phần tiền gửi dôi ra) ngày càng tăng.
Ty_le_cho_vay_TTS (Cơ cấu tài sản): Có xu hướng tăng nhẹ (từ 59.16% lên 65.12%), cho thấy tỷ trọng cho vay trong tổng tài sản của ngân hàng đang tăng lên.
size_efficiency_groups <- stb_clean %>%
mutate(Quy_mo = cut(tong_tai_san,
breaks = quantile(tong_tai_san, probs = c(0, 0.33, 0.67, 1), na.rm = TRUE),
labels = c("Nhỏ", "Trung bình", "Lớn"),
include.lowest = TRUE)) %>%
group_by(Quy_mo) %>%
summarise(
ROA_TB = mean(ROA, na.rm = TRUE),
ROE_TB = mean(ROE, na.rm = TRUE),
EPS_TB = mean(lai_co_ban_tren_co_phieu_vnd, na.rm = TRUE),
Ty_le_von_TB = mean(ty_le_von_chu_so_huu, na.rm = TRUE),
So_quan_sat = n(),
.groups = 'drop'
)
cat("HIỆU QUẢ HOẠT ĐỘNG THEO NHÓM QUY MÔ TÀI SẢN:\n")## HIỆU QUẢ HOẠT ĐỘNG THEO NHÓM QUY MÔ TÀI SẢN:
kable(size_efficiency_groups, caption = "So sánh hiệu quả theo quy mô", digits = 2) %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| Quy_mo | ROA_TB | ROE_TB | EPS_TB | Ty_le_von_TB | So_quan_sat |
|---|---|---|---|---|---|
| Nhỏ | 0.19 | 2.81 | 349.33 | 6.85 | 3 |
| Trung bình | 0.52 | 8.74 | 1142.75 | 5.94 | 4 |
| Lớn | 1.05 | 21.26 | 3482.33 | 4.92 | 3 |
🧩 Phân tích Code R
Cách thực hiện:
mutate(Quy_mo = cut(…)): Tạo một cột phân loại mới tên là Quy_mo.
cut(…, breaks = quantile(…)): Dùng hàm quantile (với probs = c(0, 0.33, 0.67, 1)) để tìm các ngưỡng chia tong_tai_san thành 3 nhóm có số lượng quan sát (số năm) xấp xỉ bằng nhau.
group_by(Quy_mo): Nhóm dữ liệu theo 3 nhóm “Nhỏ”, “Trung bình”, “Lớn” vừa tạo.
summarise(…): Tính các giá trị trung bình (ROA, ROE, EPS, Tỷ lệ vốn) và đếm số quan sát (n()) cho mỗi nhóm.
📊 Phân tích Kết quả
Diễn giải kết quả: Bảng “So sánh hiệu quả theo quy mô” cho thấy một xu hướng tăng tuyệt đối:
ROA_TB (Hiệu quả tài sản) tăng hơn 5 lần (từ 0.19 lên 1.05).
ROE_TB (Hiệu quả vốn chủ) tăng vọt hơn 7.5 lần (từ 2.81 lên 21.26).
EPS_TB (Lợi nhuận trên cổ phiếu) tăng gần 10 lần (từ 349 lên 3482).
capital_profit <- stb_clean %>%
select(tong_von_chu_so_huu, loi_nhuan_sau_thue, ROE) %>%
summarise(
Correlation_Von_Loi_nhuan = cor(tong_von_chu_so_huu, loi_nhuan_sau_thue, use = "complete.obs"),
Correlation_Von_ROE = cor(tong_von_chu_so_huu, ROE, use = "complete.obs"),
He_so_hoi_quy = summary(lm(loi_nhuan_sau_thue ~ tong_von_chu_so_huu))$r.squared
)
cat("PHÂN TÍCH MỐI QUAN HỆ GIỮA VỐN CHỦ SỞ HỮU VÀ LỢI NHUẬN:\n")## PHÂN TÍCH MỐI QUAN HỆ GIỮA VỐN CHỦ SỞ HỮU VÀ LỢI NHUẬN:
kable(t(capital_profit), caption = "Mối quan hệ giữa vốn và lợi nhuận", digits = 4) %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| Correlation_Von_Loi_nhuan | 0.8966 |
| Correlation_Von_ROE | 0.8699 |
| He_so_hoi_quy | 0.8039 |
🧩 Phân tích Code R
Mục đích: Phân tích mức độ quan hệ tuyến tính giữa Vốn chủ sở hữu (tong_von_chu_so_huu) với Lợi nhuận sau thuế (loi_nhuan_sau_thue) và ROE.
Cách thực hiện:
select: Lọc 3 cột liên quan: tong_von_chu_so_huu, loi_nhuan_sau_thue, và ROE từ bộ dữ liệu stb_clean.
summarise: Tính toán 3 chỉ số thống kê: Correlation_Von_Loi_nhuan, Correlation_Von_ROE, He_so_hoi_quy.
📊 Phân tích Kết quả
Diễn giải kết quả: Bảng “Mối quan hệ giữa vốn và lợi nhuận” cho thấy các hệ số rất cao:
Correlation_Von_Loi_nhuan (0.8966): Tương quan gần bằng 1, cho thấy vốn chủ sở hữu và lợi nhuận sau thuế có mối quan hệ đồng biến rất mạnh. Khi vốn chủ sở hữu tăng, lợi nhuận sau thuế gần như luôn tăng theo.
Correlation_Von_ROE (0.8699): Tương quan cũng rất cao, cho thấy khi vốn chủ sở hữu tăng thì khả năng sinh lời trên vốn (ROE) cũng có xu hướng tăng mạnh.
He_so_hoi_quy (0.8039): Nó có nghĩa là 80.39% sự biến động của Lợi nhuận sau thuế có thể được giải thích bởi sự biến động của Vốn chủ sở hữu.
income_structure <- stb_clean %>%
mutate(
Tong_thu_nhap = thu_nhap_lai_thuan + lai_thuan_dich_vu,
Ty_le_thu_nhap_lai = thu_nhap_lai_thuan / Tong_thu_nhap * 100,
Ty_le_thu_nhap_dich_vu = lai_thuan_dich_vu / Tong_thu_nhap * 100,
) %>%
select(nam, Ty_le_thu_nhap_lai, Ty_le_thu_nhap_dich_vu,)
cat("CẤU TRÚC THU NHẬP QUA CÁC NĂM:\n")## CẤU TRÚC THU NHẬP QUA CÁC NĂM:
kable(head(income_structure, 4), caption = "Tỷ trọng các loại thu nhập", digits = 2) %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| nam | Ty_le_thu_nhap_lai | Ty_le_thu_nhap_dich_vu |
|---|---|---|
| 2024 | 82.10 | 17.90 |
| 2023 | 82.38 | 17.62 |
| 2022 | 79.73 | 20.27 |
| 2021 | 77.48 | 22.52 |
🧩 Phân tích Code R
Cách thực hiện:
mutate: Tạo cột Tong_thu_nhap bằng cách chỉ cộng thu_nhap_lai_thuan và lai_thuan_dich_vu.
mutate (tiếp): Tính tỷ lệ phần trăm (Ty_le_…) cho từng loại thu nhập dựa trên Tong_thu_nhap vừa định nghĩa.
select: Giữ lại cột nam và 3 cột tỷ lệ vừa tạo.
📊 Phân tích Kết quả
equity_efficiency <- stb_clean %>%
mutate(
Hieu_qua_su_dung_von_dieu_le = loi_nhuan_sau_thue / von_dieu_le * 100,
Vong_quay_von_dieu_le = tong_thu_nhap_hoat_dong / von_dieu_le
) %>%
select(nam, Hieu_qua_su_dung_von_dieu_le, Vong_quay_von_dieu_le)
cat("HIỆU QUẢ SỬ DỤNG VỐN ĐIỀU LỆ:\n")## HIỆU QUẢ SỬ DỤNG VỐN ĐIỀU LỆ:
kable(head(equity_efficiency, 4), caption = "Chỉ số hiệu quả sử dụng vốn điều lệ", digits = 2) %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| nam | Hieu_qua_su_dung_von_dieu_le | Vong_quay_von_dieu_le |
|---|---|---|
| 2024 | 43.57 | 1.57 |
| 2023 | 39.53 | 1.46 |
| 2022 | 28.30 | 1.31 |
| 2021 | 16.01 | 1.01 |
🧩 Phân tích Code R
Cách thực hiện:
Sử dụng mutate để tạo 2 cột mới từ bộ dữ liệu stb_clean.
Hieu_qua_su_dung_von_dieu_le: Tính tỷ suất lợi nhuận trên vốn điều lệ (Lợi nhuận sau thuế / Vốn điều lệ).
Vong_quay_von_dieu_le: Tính số vòng quay của vốn điều lệ (Tổng thu nhập hoạt động / Vốn điều lệ).
Sử dụng select để lọc và giữ lại cột nam cùng 2 chỉ số vừa tạo.
📊 Phân tích Kết quả
Diễn giải kết quả: Bảng “Chỉ số hiệu quả sử dụng vốn điều lệ” cho thấy sự tăng trưởng đột phá :
Hiệu suất (Lợi nhuận/Vốn): Tăng từ mức rất thấp chỉ 0.47% (năm 2016) lên đến 43.57% (năm 2024). Điều này cho thấy mỗi đồng vốn điều lệ đang tạo ra nhiều lợi nhuận hơn gấp bội.
Vòng quay (Doanh thu/Vốn): Tăng đều từ 0.35 lần (2016) lên 1.57 lần (2024), cho thấy khả năng tạo doanh thu từ vốn điều lệ ngày càng tốt hơn.
final_summary <- stb_clean %>%
summarise(
`Tổng tài sản TB` = mean(tong_tai_san, na.rm = TRUE),
`Lợi nhuận sau thuế TB` = mean(loi_nhuan_sau_thue, na.rm = TRUE),
`ROA TB` = mean(ROA, na.rm = TRUE),
`ROE TB` = mean(ROE, na.rm = TRUE),
`EPS TB` = mean(lai_co_ban_tren_co_phieu_vnd, na.rm = TRUE),
`Tăng trưởng LN bình quân` = mean((loi_nhuan_sau_thue / lag(loi_nhuan_sau_thue) - 1) * 100, na.rm = TRUE),
`Tỷ lệ vốn chủ sở hữu TB` = mean(ty_le_von_chu_so_huu, na.rm = TRUE),
`Tỷ lệ cho vay/Tiền gửi TB` = mean(ty_le_cho_vay, na.rm = TRUE)
) %>%
pivot_longer(everything(), names_to = "Chỉ_số", values_to = "Giá_trị_bình_quân")
cat("📈 TỔNG KẾT CÁC CHỈ SỐ QUAN TRỌNG (2015-2024):\n")## 📈 TỔNG KẾT CÁC CHỈ SỐ QUAN TRỌNG (2015-2024):
kable(final_summary, caption = "Giá trị bình quân các chỉ số chính", digits = 2) %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE)| Chỉ_số | Giá_trị_bình_quân |
|---|---|
| Tổng tài sản TB | 488744527.60 |
| Lợi nhuận sau thuế TB | 3286501.90 |
| ROA TB | 0.58 |
| ROE TB | 10.72 |
| EPS TB | 1606.60 |
| Tăng trưởng LN bình quân | 41.88 |
| Tỷ lệ vốn chủ sở hữu TB | 5.91 |
| Tỷ lệ cho vay/Tiền gửi TB | 75.24 |
🧩 Phân tích Code R
Cách thực hiện:
summarise(…): Tính giá trị trung bình (mean()) cho các chỉ số như tong_tai_san, ROA, ROE, EPS, v.v.
pivot_longer(…): Tái cấu trúc dữ liệu. Thay vì có 1 hàng và 8 cột chỉ số, hàm này “xoay” bảng thành 8 hàng và 2 cột (Chỉ_số và Giá_trị_bình_quân), giúp trình bày bảng dễ đọc hơn.
kable(): Định dạng bảng tóm tắt sang HTML.
📊 Phân tích Kết quả
# Thiết lập theme chung
theme_set(theme_minimal(base_size = 12) +
theme(
plot.title = element_text(face = "bold", size = 14, hjust = 0.5),
plot.subtitle = element_text(size = 11, hjust = 0.5, color = "gray50"),
axis.title = element_text(face = "bold"),
legend.position = "bottom",
legend.title = element_text(face = "bold"),
panel.grid.minor = element_blank()
))
# Màu sắc chung
color_palette <- c("#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b")p2 <- ggplot(stb_clean) +
geom_col(aes(x = nam, y = ROA), fill = color_palette[1], alpha = 0.7, width = 0.6) +
geom_line(aes(x = nam, y = ROE), color = color_palette[2], size = 1.5, group = 1) +
geom_point(aes(x = nam, y = ROE), color = color_palette[2], size = 3) +
geom_text(aes(x = nam, y = ROA, label = sprintf("%.1f%%", ROA)),
vjust = -0.5, size = 3, fontface = "bold") +
geom_text(aes(x = nam, y = ROE, label = sprintf("%.1f%%", ROE)),
vjust = -0.5, size = 3, fontface = "bold", color = color_palette[2]) +
scale_x_continuous(breaks = unique(stb_clean$nam)) +
labs(
title = "HIỆU QUẢ HOẠT ĐỘNG: ROA VÀ ROE",
subtitle = "ROA (cột) và ROE (đường)",
x = "Năm", y = "Tỷ lệ (%)"
)
print(p2)🧩 Phân tích Code R
Cách thực hiện:
Dùng geom_col (biểu đồ cột) để biểu diễn ROA.
Dùng geom_line và geom_point (biểu đồ đường và điểm) để biểu diễn ROE.
Dùng geom_text hai lần để thêm nhãn giá trị (dưới dạng %.1f%%) cho cả cột (ROA) và đường (ROE).
labs được dùng để đặt tiêu đề, phụ đề và tên các trục.
📊 Phân tích Kết quả
Diễn giải kết quả:Biểu đồ thể hiện hiệu quả hoạt động của doanh nghiệp từ 2015 đến 2024:
ROA (cột): Chỉ số này duy trì ở mức rất thấp và tương đối ổn định, luôn dưới 1.1% trong suốt giai đoạn được phân tích.
ROE (đường): Chỉ số này có xu hướng tăng trưởng rõ rệt. Sau giai đoạn 2016-2021 tăng trưởng ổn định (từ 0.4% lên 9.3%), ROE đã tăng vọt mạnh mẽ từ năm 2022 (18.5%) và tiếp tục đạt 23.3% vào năm 2024.
income_structure_long <- stb_clean %>%
mutate(
Ty_le_thu_nhap_lai = thu_nhap_lai_thuan / tong_thu_nhap_hoat_dong * 100,
Ty_le_thu_nhap_dich_vu = lai_thuan_dich_vu / tong_thu_nhap_hoat_dong * 100,
Ty_le_thu_nhap_khac = 100 - Ty_le_thu_nhap_lai - Ty_le_thu_nhap_dich_vu
) %>%
select(nam, Ty_le_thu_nhap_lai, Ty_le_thu_nhap_dich_vu, Ty_le_thu_nhap_khac) %>%
pivot_longer(-nam, names_to = "Loai_thu_nhap", values_to = "Ty_le") %>%
mutate(Loai_thu_nhap = case_when(
Loai_thu_nhap == "Ty_le_thu_nhap_lai" ~ "Thu nhập lãi",
Loai_thu_nhap == "Ty_le_thu_nhap_dich_vu" ~ "Thu nhập dịch vụ",
TRUE ~ "Thu nhập khác"
))
p3 <- ggplot(income_structure_long, aes(x = nam, y = Ty_le, fill = Loai_thu_nhap)) +
geom_area(alpha = 0.8, color = "white", size = 0.3) +
scale_fill_manual(values = color_palette[1:3]) +
scale_x_continuous(breaks = unique(stb_clean$nam)) +
scale_y_continuous(labels = scales::percent_format(scale = 1)) +
labs(
title = "CƠ CẤU THU NHẬP HOẠT ĐỘNG",
subtitle = "Phân bổ tỷ trọng các loại thu nhập",
x = "Năm", y = "Tỷ trọng (%)",
fill = "Loại thu nhập"
) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
print(p3)🧩 Phân tích Code R
Cách thực hiện:
Dùng mutate để tính tỷ trọng (%) của ba loại thu nhập (Lãi, Dịch vụ, Khác) sao cho tổng của chúng luôn là 100%.
Dùng pivot_longer để chuyển đổi dữ liệu từ định dạng rộng (nhiều cột tỷ trọng) sang định dạng dài (một cột loại thu nhập, một cột giá trị tỷ trọng). Đây là bước bắt buộc để vẽ biểu đồ diện tích xếp chồng trong ggplot2.
Dùng geom_area để vẽ biểu đồ diện tích. Khi kết hợp với aes(fill = Loai_thu_nhap), các vùng sẽ tự động được xếp chồng lên nhau.
scale_y_continuous định dạng trục Y hiển thị dưới dạng phần trăm (%).
labs được dùng để đặt tiêu đề chính, tiêu đề phụ, và nhãn cho các trục cũng như phần chú giải (legend).
📊 Phân tích Kết quả
Diễn giải kết quả:
Thu nhập lãi (Xanh lá): Luôn là trụ cột chính, chiếm tỷ trọng lớn nhất. Sau khi sụt giảm trong giai đoạn 2016-2017 (xuống khoảng 60%), tỷ trọng mảng này đã tăng trưởng trở lại và duy trì ổn định ở mức cao (khoảng 75-78%) trong những năm gần đây.
Thu nhập dịch vụ (Xanh dương): Cho thấy sự tăng trưởng rõ rệt về tỷ trọng, đặc biệt là từ năm 2017. Mảng này đã vượt qua “Thu nhập khác” để trở thành nguồn thu lớn thứ hai, ổn định ở mức khoảng 15-20%.
# Tính toán phạm vi kích thước hợp lý hơn
lnst_range <- range(stb_clean$loi_nhuan_sau_thue, na.rm = TRUE)
size_breaks <- seq(lnst_range[1], lnst_range[2], length.out = 5)
p5_optimized <- ggplot(stb_clean, aes(x = tong_tai_san/1e6, y = ROE)) +
# Điểm với kích thước được kiểm soát tốt hơn
geom_point(aes(size = loi_nhuan_sau_thue/1e3, color = giai_doan_phat_trien),
alpha = 0.7, show.legend = TRUE) +
# Đường hồi quy
geom_smooth(method = "lm", se = TRUE, color = "darkred", linetype = "dashed", alpha = 0.2) +
# Nhãn năm với vị trí thông minh hơn
geom_text(aes(label = nam),
vjust = -0.8, size = 2.5, color = "black", fontface = "bold",
check_overlap = FALSE) +
# Kiểm soát kích thước điểm chặt chẽ hơn
scale_size_continuous(
range = c(2, 8), # Giảm phạm vi kích thước
name = "Lợi nhuận sau thuế\n(Tỷ VND)",
labels = function(x) format(round(x), big.mark = ".", decimal.mark = ",", scientific = FALSE),
breaks = size_breaks/1e3 # Điểm ngắt cụ thể
) +
scale_color_manual(values = color_palette[1:3]) +
scale_x_continuous(
labels = scales::comma_format(big.mark = ".", decimal.mark = ","),
n.breaks = 8
) +
labs(
title = "MỐI QUAN HỆ GIỮA QUY MÔ VÀ HIỆU QUẢ",
subtitle = "Kích thước điểm thể hiện lợi nhuận sau thuế",
x = "Tổng tài sản (Nghìn tỷ VND)",
y = "ROE (%)"
) +
# Tách các legend để dễ đọc hơn
guides(
color = guide_legend(override.aes = list(size = 4)),
size = guide_legend(override.aes = list(color = "gray50"))
) +
theme(
legend.box = "vertical",
legend.margin = margin(t = 10)
)
print(p5_optimized)🧩 Phân tích Code R
Cách thực hiện:
Dùng geom_point với aes để ánh xạ tong_tai_san vào trục X, ROE vào trục Y, loi_nhuan_sau_thue vào kích thước (size), và giai_doan_phat_trien vào màu sắc (color).
Dùng geom_smooth (với method = “lm”) để vẽ một đường xu hướng hồi quy tuyến tính (màu đỏ, nét đứt) nhằm thể hiện xu hướng chung.
Dùng geom_text để thêm nhãn nam (Năm) cho từng điểm dữ liệu (bong bóng).
Các hàm scale_* được dùng để định dạng trục X (chia cho 1e6 để ra “Nghìn tỷ VND”), tùy chỉnh thang đo kích thước (cho Lợi nhuận) và màu sắc (cho Giai đoạn).
labs được dùng để đặt Tiêu đề (“MỐI QUAN HỆ GIỮA QUY MÔ VÀ HIỆU QUẢ”), Phụ đề và tên các trục.
📊 Phân tích Kết quả
Diễn giải kết quả:
Xu hướng chính: Biểu đồ cho thấy một mối tương quan dương rõ rệt (đường xu hướng dốc lên). Khi Quy mô (Tổng tài sản) tăng, thì Hiệu quả (ROE) cũng tăng theo.
Tăng trưởng đồng bộ: Qua các năm (từ 2015 đến 2024), doanh nghiệp đã di chuyển rõ rệt từ góc dưới-bên trái (Quy mô nhỏ - Hiệu quả thấp) lên góc trên-bên phải (Quy mô lớn - Hiệu quả cao). Kích thước bong bóng (thể hiện Lợi nhuận) cũng lớn dần, cho thấy lợi nhuận tuyệt đối tăng mạnh cùng quy mô.
Giai đoạn bứt phá: Đặc biệt, các năm 2023 và 2024 không chỉ có quy mô và lợi nhuận lớn nhất mà còn nằm phía trên đường xu hướng, cho thấy hiệu quả hoạt động (ROE) đang tăng tốc và vượt trội hơn mức trung bình dự kiến.
p7 <- ggplot(stb_clean, aes(x = nam, y = ROA)) +
geom_ribbon(aes(ymin = ROA - sd(ROA, na.rm = TRUE),
ymax = ROA + sd(ROA, na.rm = TRUE)),
fill = "lightblue", alpha = 0.3) +
geom_line(aes(y = ROA), color = "blue", size = 1.2) +
geom_point(aes(y = ROA), color = "blue", size = 2.5) +
geom_smooth(aes(y = ROA), method = "loess", se = TRUE, color = "darkblue", linetype = "dashed", alpha = 0.2) +
geom_line(aes(y = ROE/10), color = "red", size = 1.2) +
geom_point(aes(y = ROE/10), color = "red", size = 2.5) +
scale_y_continuous(
name = "ROA (%)",
sec.axis = sec_axis(~.*10, name = "ROE (%)")
) +
labs(title = "XU HƯỚNG ROA VÀ ROE VỚI KHOẢNG TIN CẬY",
subtitle = "Vùng màu thể hiện độ lệch chuẩn của ROA",
x = "Năm") +
theme_minimal() +
theme(
axis.title.y.left = element_text(color = "blue"),
axis.title.y.right = element_text(color = "red"),
plot.title = element_text(face = "bold")
)
print(p7)🧩 Phân tích Code R
Cách thực hiện:
geom_line (xanh/đỏ) vẽ đường xu hướng cho ROA và ROE.
geom_ribbon (vùng xanh nhạt) thể hiện dải độ lệch chuẩn của ROA.
sec_axis(~.*10) tạo trục Y thứ cấp (bên phải) cho ROE để khớp thang đo.
📊 Phân tích Kết quả
Diễn giải kết quả:
Xu hướng chung: Cả ROA (xanh) và ROE (đỏ) đều có xu hướng tăng rõ rệt, đặc biệt là tăng trưởng đột biến sau năm 2021.
Chi tiết: Giai đoạn 2015-2021, cả hai chỉ số tăng chậm và ổn định. Từ 2021, ROA tăng tốc vượt 1.0%, trong khi ROE tăng vọt từ ~10% lên trên 20%.
p8 <- ggplot(stb_clean, aes(x = tong_von_chu_so_huu/1e3, y = loi_nhuan_sau_thue)) +
geom_point(aes(color = ROE, size = tong_tai_san/1e6), alpha = 0.7) +
geom_smooth(method = "lm", se = TRUE, color = "darkred", linetype = "dashed", alpha = 0.2) +
geom_text(aes(label = nam), vjust = -0.8, size = 3, check_overlap = TRUE) +
geom_abline(intercept = 0, slope = mean(stb_clean$loi_nhuan_sau_thue/(stb_clean$tong_von_chu_so_huu/1e3), na.rm = TRUE),
color = "blue", linetype = "dotted", size = 1) +
geom_segment(aes(xend = tong_von_chu_so_huu/1e3, yend = predict(lm(loi_nhuan_sau_thue ~ I(tong_von_chu_so_huu/1e3)))),
color = "gray", alpha = 0.5) +
scale_color_gradient(low = "yellow", high = "red", name = "ROE (%)") +
scale_size_continuous(range = c(3, 8), name = "Tổng tài sản\n(Nghìn tỷ VND)") +
labs(title = "MỐI QUAN HỆ GIỮA VỐN CHỦ SỞ HỮU VÀ LỢI NHUẬN",
subtitle = "Màu sắc thể hiện ROE, kích thước thể hiện tổng tài sản",
x = "Vốn chủ sở hữu (Tỷ VND)", y = "Lợi nhuận sau thuế (Tỷ VND)") +
theme_minimal() +
theme(plot.title = element_text(face = "bold"))
print(p8)🧩 Phân tích Code R
Cách thực hiện:
geom_point: Vẽ các bong bóng (ROE là màu, Tổng tài sản là kích thước).
geom_smooth (đỏ): Vẽ đường xu hướng hồi quy tuyến tính (Lợi nhuận ~ Vốn CSH) kèm khoảng tin cậy (vùng xám).
geom_abline (xanh): Vẽ đường ROE trung bình toàn giai đoạn để tham chiếu.
geom_text: Thêm nhãn nam cho mỗi bong bóng.
labs và scale_…: Định dạng tiêu đề, chú thích, màu sắc và kích thước.
📊 Phân tích Kết quả
Diễn giải kết quả:
Xu hướng chính: Có tương quan dương mạnh (đường đỏ) - khi Vốn chủ sở hữu tăng thì Lợi nhuận sau thuế cũng tăng theo. Kích thước bong bóng (Tổng tài sản) cũng tăng đều qua các năm.
Hiệu suất (ROE):
ROE (màu sắc) cải thiện vượt bậc trong 3 năm gần đây (2022-2024) với màu đỏ/cam đậm, so với giai đoạn trước (màu vàng).
Các năm 2022-2024 nằm cao phía trên cả đường xu hướng (đỏ) và đường ROE trung bình (xanh), cho thấy hiệu suất sinh lời rất cao, vượt trội so với lịch sử và mức dự báo.
efficiency_data <- stb_clean %>%
select(nam, ROA, ROE, ty_le_chi_phi, ty_le_von_chu_so_huu) %>%
pivot_longer(-nam, names_to = "Chi_so", values_to = "Gia_tri") %>%
mutate(Chi_so = case_when(
Chi_so == "ROA" ~ "ROA (%)",
Chi_so == "ROE" ~ "ROE (%)",
Chi_so == "ty_le_chi_phi" ~ "Tỷ lệ chi phí (%)",
Chi_so == "ty_le_von_chu_so_huu" ~ "Tỷ lệ VCSH (%)"
))
p9 <- ggplot(efficiency_data, aes(x = nam, y = Gia_tri, color = Chi_so)) +
geom_line(size = 1.2) +
geom_point(size = 2) +
geom_smooth(method = "lm", se = FALSE, linetype = "dashed", alpha = 0.5) +
geom_hline(data = efficiency_data %>% group_by(Chi_so) %>% summarise(Trung_binh = mean(Gia_tri, na.rm = TRUE)),
aes(yintercept = Trung_binh, color = Chi_so), linetype = "dotted", size = 1) +
geom_text(data = efficiency_data %>% filter(nam == max(nam)),
aes(label = round(Gia_tri, 1)), vjust = -0.8, size = 3, show.legend = FALSE) +
facet_wrap(~Chi_so, scales = "free_y", ncol = 2) +
labs(title = "XU HƯỚNG CÁC CHỈ SỐ HIỆU QUẢ HOẠT ĐỘNG",
x = "Năm", y = "Giá trị", color = "Chỉ số") +
theme_minimal() +
theme(legend.position = "none",
plot.title = element_text(face = "bold"))
print(p9)🧩 Phân tích Code R
Cách thực hiện:
Lọc và Xoay dữ liệu: Dùng select để chọn các cột (năm và 4 chỉ số), sau đó dùng pivot_longer để chuyển dữ liệu từ dạng rộng (nhiều cột chỉ số) sang dạng dài (một cột tên chỉ số, một cột giá trị).
Vẽ biểu đồ (ggplot2): Dùng geom_line và geom_point để vẽ dữ liệu thực tế.
Thêm ngữ cảnh: Dùng geom_smooth (method = “lm”) để thêm đường xu hướng tuyến tính (nét đứt) và geom_hline để thêm đường trung bình (nét chấm) cho từng chỉ số.
Chia nhỏ (Facet): Dùng facet_wrap để tách 4 chỉ số thành 4 ô biểu đồ riêng biệt (grid 2x2) với trục Y tự do (scales = “free_y”).
📊 Phân tích Kết quả
Diễn giải kết quả:
Hiệu quả sinh lời (ROA & ROE): Cả hai chỉ số đều có xu hướng tăng mạnh (đường trendline dốc lên). Đặc biệt là ROE, tăng vọt trong những năm gần đây, kết thúc ở mức cao nhất giai đoạn.
Kiểm soát chi phí (Tỷ lệ chi phí): Có xu hướng giảm (đường trendline dốc xuống), cho thấy việc quản lý chi phí ngày càng hiệu quả hơn (dù có tăng nhẹ ở năm cuối).
Cơ cấu vốn (Tỷ lệ VCSH): Có xu hướng giảm đều, cho thấy doanh nghiệp có thể đang tăng cường sử dụng đòn bẩy tài chính (tăng nợ) để tài trợ hoạt động.
risk_data <- stb_clean %>%
mutate(
Du_phong_abs = abs(chi_phi_du_phong_rui_ro_tin_dung),
Ty_le_du_phong = Du_phong_abs / cho_vay_khach_hang * 100
)
p10 <- ggplot(risk_data, aes(x = nam)) +
geom_col(aes(y = Du_phong_abs/1e3), fill = color_palette[4], alpha = 0.7, width = 0.6) +
geom_line(aes(y = Ty_le_du_phong*10), color = color_palette[5], size = 1.5, group = 1) +
geom_point(aes(y = Ty_le_du_phong*10), color = color_palette[5], size = 3) +
geom_text(aes(y = Du_phong_abs/1e3, label = round(Du_phong_abs/1e3, 1)),
vjust = -0.5, size = 3, fontface = "bold") +
scale_y_continuous(
name = "Dự phòng rủi ro (Tỷ VND)",
sec.axis = sec_axis(~./10, name = "Tỷ lệ dự phòng (%)"),
labels = scales::comma_format(big.mark = ".", decimal.mark = ",")
) +
scale_x_continuous(breaks = unique(stb_clean$nam)) +
labs(
title = "PHÂN TÍCH RỦI RO TÍN DỤNG VÀ DỰ PHÒNG",
subtitle = "Dự phòng tuyệt đối và tỷ lệ dự phòng trên tổng dư nợ",
x = "Năm"
) +
theme(
axis.title.y.left = element_text(color = color_palette[4]),
axis.title.y.right = element_text(color = color_palette[5]),
axis.text.x = element_text(angle = 45, hjust = 1)
)
print(p10)🧩 Phân tích Code R
Cách thực hiện:
Tính toán (dùng mutate): Từ data stb_clean, tạo 2 cột mới:
Trực quan hóa (dùng ggplot2):
📊 Phân tích Kết quả
Diễn giải kết quả:
Dự phòng rủi ro (Cột đỏ): Chi phí dự phòng tuyệt đối có biến động rất mạnh qua các năm. Đáng chú ý nhất là mức tăng vọt lên đỉnh điểm 4.990,6 tỷ VND vào năm 2022, cao hơn gấp đôi so với các năm lân cận (2020-2021). Sau đó, chi phí này giảm mạnh trở lại vào năm 2023 (2.730 tỷ) và 2024 (1.921,4 tỷ).
Tỷ lệ dự phòng (Đường tím): Trái ngược với chi phí tuyệt đối, tỷ lệ dự phòng trên tổng dư nợ lại duy trì ở mức rất thấp và cực kỳ ổn định (gần như bằng 0) trong suốt giai đoạn.
eps_data <- stb_clean %>%
mutate(
Tang_truong_EPS = (lai_co_ban_tren_co_phieu_vnd / lag(lai_co_ban_tren_co_phieu_vnd) - 1) * 100
)
p11 <- ggplot(eps_data, aes(x = nam)) +
geom_col(aes(y = lai_co_ban_tren_co_phieu_vnd), fill = "lightgreen", alpha = 0.7) +
geom_line(aes(y = Tang_truong_EPS * 100), color = "darkgreen", size = 1.5, group = 1) +
geom_point(aes(y = Tang_truong_EPS * 100), color = "darkgreen", size = 2.5) +
geom_hline(yintercept = 0, linetype = "solid", color = "black", size = 0.5) +
geom_text(aes(y = lai_co_ban_tren_co_phieu_vnd, label = lai_co_ban_tren_co_phieu_vnd),
vjust = -0.5, size = 3, fontface = "bold") +
scale_y_continuous(
name = "EPS (VND)",
sec.axis = sec_axis(~./100, name = "Tăng trưởng EPS (%)")
) +
labs(title = "LÃI CƠ BẢN TRÊN CỔ PHIẾU (EPS) VÀ TĂNG TRƯỞNG",
x = "Năm") +
theme_minimal() +
theme(
axis.title.y.left = element_text(color = "darkgreen"),
axis.title.y.right = element_text(color = "darkgreen"),
plot.title = element_text(face = "bold")
)
print(p11)🧩 Phân tích Code R
Cách thực hiện:
Dùng mutate() và lag() để tính toán cột Tang_truong_EPS (tỷ lệ % tăng trưởng so với kỳ trước).
Sử dụng ggplot() để vẽ biểu đồ kết hợp: geom_col() (biểu đồ cột) cho EPS và geom_line() (biểu đồ đường) cho Tăng trưởng EPS.
Dùng scale_y_continuous và sec_axis để tạo trục Y kép, một trục cho giá trị EPS (VND) và trục còn lại cho Tăng trưởng (%).
📊 Phân tích Kết quả
Diễn giải kết quả: Biểu đồ cho thấy xu hướng của EPS (cột) và tốc độ tăng trưởng (đường) qua các năm
EPS (cột): Sau khi sụt giảm rất mạnh vào khoảng năm 2016 (từ 444 xuống chỉ còn 49), EPS đã phục hồi và tăng trưởng đều đặn, đặc biệt tăng tốc mạnh mẽ từ sau năm 2020.
Tăng trưởng EPS (đường): Tốc độ tăng trưởng cực kỳ biến động.
liquidity_data <- stb_clean %>%
mutate(
Ty_le_thanh_khoan = tien_gui_cua_khach_hang / cho_vay_khach_hang * 100
)
p13 <- ggplot(liquidity_data, aes(x = nam)) +
geom_col(aes(y = cho_vay_khach_hang/1e6), fill = color_palette[1], alpha = 0.7, width = 0.6) +
geom_col(aes(y = tien_gui_cua_khach_hang/1e6), fill = color_palette[2], alpha = 0.7, width = 0.6) +
geom_line(aes(y = Ty_le_thanh_khoan), color = color_palette[3], size = 1.5, group = 1) +
geom_point(aes(y = Ty_le_thanh_khoan), color = color_palette[3], size = 3) +
geom_text(aes(y = Ty_le_thanh_khoan, label = paste0(round(Ty_le_thanh_khoan, 1), "%")),
vjust = -0.8, size = 3, color = color_palette[3], fontface = "bold") +
scale_y_continuous(
name = "Cho vay & Tiền gửi (Nghìn tỷ VND)",
sec.axis = sec_axis(~., name = "Tỷ lệ thanh khoản (%)"),
labels = scales::comma_format(big.mark = ".", decimal.mark = ",")
) +
scale_x_continuous(breaks = unique(stb_clean$nam)) +
labs(
title = "PHÂN TÍCH THANH KHOẢN VÀ CHÊNH LỆCH KỲ HẠN",
subtitle = "Cho vay KH (xanh dương), Tiền gửi KH (xanh lá), Tỷ lệ thanh khoản (cam)",
x = "Năm"
) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
print(p13)🧩 Phân tích Code R
Cách thực hiện:
Tính toán: Dùng mutate để tạo một cột mới tên Ty_le_thanh_khoan (Tỷ lệ thanh khoản) bằng cách lấy tien_gui_cua_khach_hang (tiền gửi) chia cho cho_vay_khach_hang (cho vay), rồi nhân 100.
Vẽ biểu đồ: Sử dụng ggplot với nam (năm) làm trục X.
Thêm lớp: Vẽ 2 biểu đồ cột (geom_col) cho cho_vay_khach_hang và tien_gui_cua_khach_hang (đã chia tỷ lệ 1e6). Sau đó, vẽ 1 biểu đồ đường (geom_line, geom_point) cho Ty_le_thanh_khoan.
Trục Y kép: Dùng scale_y_continuous và sec_axis để tạo trục Y thứ hai, cho phép hiển thị song song “Nghìn tỷ VND” (cho cột) và “%” (cho đường).
📊 Phân tích Kết quả
cost_data <- stb_clean %>%
mutate(
Ty_le_chi_phi_thu_nhap = abs(tong_chi_phi_hoat_dong) / tong_thu_nhap_hoat_dong * 100,
Chenh_lech_thu_chi = (tong_thu_nhap_hoat_dong - abs(tong_chi_phi_hoat_dong))/1e6
)
p15 <- ggplot(cost_data, aes(x = nam)) +
geom_col(aes(y = tong_thu_nhap_hoat_dong/1e6), fill = "lightgreen", alpha = 0.7, width = 0.6) +
geom_col(aes(y = abs(tong_chi_phi_hoat_dong)/1e6), fill = "lightcoral", alpha = 0.7, width = 0.6) +
geom_line(aes(y = Ty_le_chi_phi_thu_nhap), color = "darkred", size = 1.5, group = 1) +
geom_point(aes(y = Ty_le_chi_phi_thu_nhap), color = "darkred", size = 2.5) +
geom_line(aes(y = Chenh_lech_thu_chi), color = "darkgreen", size = 1.5, linetype = "dashed") +
geom_hline(yintercept = 50, linetype = "dotted", color = "red", size = 1) +
scale_y_continuous(
name = "Thu nhập & Chi phí (Nghìn tỷ VND)",
sec.axis = sec_axis(~., name = "Tỷ lệ chi phí/thu nhập (%) & Chênh lệch (Nghìn tỷ VND)")
) +
labs(title = "PHÂN TÍCH CHI PHÍ VÀ HIỆU QUẢ HOẠT ĐỘNG",
subtitle = "Thu nhập (xanh lá), Chi phí (đỏ), Tỷ lệ C/I (đỏ đậm), Chênh lệch (xanh đậm)",
x = "Năm") +
theme_minimal() +
theme(plot.title = element_text(face = "bold"))
print(p15)🧩 Phân tích Code R
Cách thực hiện:
Dùng hàm mutate để tạo 2 cột mới trong data cost_data:
Ty_le_chi_phi_thu_nhap: Tính tỷ lệ C/I (Cost/Income ratio) bằng cách lấy chi phí chia cho thu nhập, nhân 100 (%).
Chenh_lech_thu_chi: Tính chênh lệch thu-chi (lợi nhuận) và chia cho 1 triệu (để đơn vị là Nghìn tỷ VND).
Dùng ggplot2 để vẽ biểu đồ:
geom_col: Vẽ biểu đồ cột cho Thu nhập (xanh lá) và Chi phí (đỏ san hô).
geom_line & geom_point: Vẽ biểu đồ đường (đỏ đậm) cho Tỷ lệ C/I.
geom_line (dashed): Vẽ biểu đồ đường nét đứt (xanh đậm) cho Chênh lệch thu-chi.
scale_y_continuous(sec.axis = …): Tạo trục Y thứ hai (bên phải) để hiển thị Tỷ lệ C/I (%) và Chênh lệch.
📊 Phân tích Kết quả
Diễn giải kết quả: Biểu đồ cho thấy rất rõ:
Thu nhập & Chi phí (Cột): Cả tổng thu nhập (cột xanh lá) và tổng chi phí (cột đỏ) đều có xu hướng tăng trưởng ổn định qua các năm, đặc biệt từ sau 2017.5.
Chênh lệch (Đường xanh nét đứt): Chênh lệch thu-chi (lợi nhuận) cũng tăng đều theo thời gian, song hành với sự tăng trưởng của thu nhập.
Tỷ lệ C/I (Đường đỏ đậm): Đây là điểm đáng chú ý nhất. Tỷ lệ C/I (chi phí/thu nhập) đạt đỉnh rất cao vào khoảng năm 2016 (gần 100%), sau đó giảm mạnh và đều đặn về mức khoảng 50% vào năm 2022. Điều này cho thấy hiệu quả hoạt động của tổ chức đang được cải thiện rõ rệt (kiểm soát chi phí tốt hơn so với tốc độ tăng thu nhập).
equity_data <- stb_clean %>%
mutate(
Hieu_su_su_dung_von_dieu_le = loi_nhuan_sau_thue / von_dieu_le * 100,
Vong_quay_von_dieu_le = tong_thu_nhap_hoat_dong / von_dieu_le
)
p19 <- ggplot(equity_data, aes(x = nam)) +
geom_col(aes(y = Hieu_su_su_dung_von_dieu_le), fill = "lightblue", alpha = 0.7, width = 0.6) +
geom_line(aes(y = Vong_quay_von_dieu_le * 10), color = "darkred", size = 1.5, group = 1) +
geom_point(aes(y = Vong_quay_von_dieu_le * 10), color = "darkred", size = 2.5) +
geom_smooth(aes(y = Hieu_su_su_dung_von_dieu_le), method = "loess", se = TRUE,
color = "blue", linetype = "dashed", alpha = 0.2) +
geom_text(aes(y = Hieu_su_su_dung_von_dieu_le, label = round(Hieu_su_su_dung_von_dieu_le, 1)),
vjust = -0.5, size = 3, fontface = "bold") +
scale_y_continuous(
name = "Hiệu suất sử dụng vốn điều lệ (%)",
sec.axis = sec_axis(~./10, name = "Vòng quay vốn điều lệ")
) +
labs(title = "HIỆU QUẢ SỬ DỤNG VỐN ĐIỀU LỆ",
subtitle = "Hiệu suất sử dụng vốn điều lệ (cột) và Vòng quay vốn điều lệ (đường)",
x = "Năm") +
theme_minimal() +
theme(
axis.title.y.left = element_text(color = "blue"),
axis.title.y.right = element_text(color = "darkred"),
plot.title = element_text(face = "bold")
)
print(p19)🧩 Phân tích Code R
Cách thực hiện:
Tính toán chỉ số (mutate): Tạo 2 cột mới từ dữ liệu stb_clean: Hieu_su_su_dung_von_dieu_le (Lợi nhuận sau thuế / Vốn điều lệ), Vong_quay_von_dieu_le (Tổng thu nhập hoạt động / Vốn điều lệ).
Vẽ biểu đồ (ggplot): Dùng geom_col (biểu đồ cột, màu xanh) cho “Hiệu suất”,Dùng geom_line và geom_point (biểu đồ đường, màu đỏ) cho “Vòng quay”.
Tạo trục tung kép (scale_y_continuous): Thiết lập trục Y bên trái (chính) cho “Hiệu suất” và dùng sec_axis để tạo trục Y bên phải (phụ) cho “Vòng quay”.
📊 Phân tích Kết quả
Diễn giải kết quả:
Vòng quay vốn điều lệ (Đường đỏ): Tăng trưởng ổn định và đều đặn, từ 0.5 lần (năm 2016) lên khoảng 1.6 lần (năm cuối). Điều này cho thấy công ty ngày càng tạo ra nhiều doanh thu hơn trên mỗi đồng vốn điều lệ.
Hiệu suất sử dụng vốn điều lệ (Cột xanh): Tăng trưởng bùng nổ và tăng tốc mạnh mẽ, đặc biệt từ sau năm 2020. Chỉ số này tăng từ mức rất thấp (0.5% - 3.4%) lên mức ấn tượng 43.6% ở năm cuối.
risk_return_data <- stb_clean %>%
mutate(
Rui_ro = sd(ROE, na.rm = TRUE) / mean(ROE, na.rm = TRUE) * 100,
Loai_rui_ro = ifelse(ROE > median(ROE, na.rm = TRUE), "Hiệu quả cao", "Hiệu quả thấp")
)
p21 <- ggplot(risk_return_data, aes(x = nam, y = ROE)) +
geom_ribbon(aes(ymin = ROE - sd(ROE, na.rm = TRUE),
ymax = ROE + sd(ROE, na.rm = TRUE)),
fill = "lightcoral", alpha = 0.3) +
geom_line(aes(color = Loai_rui_ro), size = 1.5, alpha = 0.8) +
geom_point(aes(size = abs(chi_phi_du_phong_rui_ro_tin_dung)/1e3, color = Loai_rui_ro), alpha = 0.8) +
geom_hline(yintercept = mean(risk_return_data$ROE, na.rm = TRUE),
linetype = "dashed", color = "blue", size = 1.2) +
geom_smooth(method = "loess", se = FALSE, color = "darkred", linetype = "dotted", size = 1) +
geom_text(aes(label = round(ROE, 1)), vjust = -0.8, size = 3, fontface = "bold") +
scale_color_manual(values = c("Hiệu quả cao" = "darkgreen", "Hiệu quả thấp" = "red")) +
scale_size_continuous(range = c(3, 8), name = "Dự phòng rủi ro\n(Tỷ VND)") +
labs(title = "MỐI QUAN HỆ GIỮA RỦI RO VÀ LỢI NHUẬN",
subtitle = "ROE với khoảng tin cậy, màu sắc thể hiện mức hiệu quả, kích thước thể hiện dự phòng rủi ro",
x = "Năm", y = "ROE (%)", color = "Mức hiệu quả") +
theme_minimal() +
theme(legend.position = "bottom",
plot.title = element_text(face = "bold", size = 14))
print(p21)🧩 Phân tích Code R
Cách thực hiện:
Phân loại : Tạo cột Loai_rui_ro (Hiệu quả cao/thấp) bằng cách so sánh ROE của từng năm với median (trung vị) của toàn bộ giai đoạn.
Trực quan hóa :
geom_line + geom_point: Vẽ ROE theo nam. Màu sắc (đỏ/xanh) được quyết định bởi Loai_rui_ro. Kích thước điểm (size) được ánh xạ với chi_phi_du_phong_rui_ro_tin_dung (đã chuẩn hóa về Tỷ VND).
geom_ribbon: Thêm một dải (vùng màu hồng) biểu thị ROE +/- 1 độ lệch chuẩn (SD) của toàn bộ dữ liệu, thể hiện vùng biến động.
geom_hline: Thêm đường nét đứt màu xanh ngang thể hiện mean (trung bình) của ROE.
📊 Phân tích Kết quả
Diễn giải kết quả:
Lợi nhuận và Rủi ro (Kích thước điểm): Có mối quan hệ đồng biến. Khi ROE tăng cao (giai đoạn 2022-2023), quy mô trích lập dự phòng rủi ro (kích thước điểm) cũng tăng lên mức cao nhất (các điểm màu xanh rất lớn).
Lợi nhuận và Biến động (Vùng hồng): Vùng ribbon màu hồng (biểu thị độ lệch chuẩn) mở rộng đáng kể ở giai đoạn sau. Điều này cho thấy khi lợi nhuận (ROE) tăng cao, thì mức độ biến động (rủi ro) xung quanh nó cũng tăng theo.
library(ggrepel)
# Chuẩn bị dữ liệu cho biểu đồ bubble chart đa chiều
multi_dim_data <- stb_clean %>%
mutate(
Quy_mo = tong_tai_san / 1e6,
Hieu_qua = ROE,
Rui_ro = abs(chi_phi_du_phong_rui_ro_tin_dung) / cho_vay_khach_hang * 100,
Tang_truong = (loi_nhuan_sau_thue / lag(loi_nhuan_sau_thue) - 1) * 100,
Gia_tri = lai_co_ban_tren_co_phieu_vnd
) %>%
filter(!is.na(Tang_truong))
p25_optimized <- ggplot(multi_dim_data, aes(x = Quy_mo, y = Hieu_qua)) +
# Điểm với các thuộc tính đa chiều
geom_point(aes(size = Gia_tri, color = Rui_ro, shape = giai_doan_phat_trien),
alpha = 0.8, stroke = 1) +
# Đường hồi quy
geom_smooth(method = "lm", se = TRUE, color = "darkred", linetype = "dashed",
alpha = 0.2, fill = "lightpink") +
# Nhãn năm sử dụng ggrepel để tránh chồng chéo
geom_text_repel(
aes(label = nam),
size = 3,
point.padding = 0.3,
box.padding = 0.3,
min.segment.length = 0.2,
max.overlaps = 20,
force = 0.5,
force_pull = 0.5,
segment.color = "gray50",
segment.alpha = 0.5,
fontface = "bold"
) +
# Đường trung bình
geom_hline(yintercept = mean(multi_dim_data$Hieu_qua, na.rm = TRUE),
linetype = "dotted", color = "blue", size = 0.8) +
geom_vline(xintercept = mean(multi_dim_data$Quy_mo, na.rm = TRUE),
linetype = "dotted", color = "blue", size = 0.8) +
# Tỉ lệ kích thước điểm
scale_size_continuous(
range = c(3, 10),
name = "EPS (VND)",
breaks = pretty(multi_dim_data$Gia_tri, n = 5),
labels = function(x) format(x, big.mark = ".", decimal.mark = ",", scientific = FALSE)
) +
# Gradient màu cho rủi ro
scale_color_gradient2(
low = "#1a9641",
mid = "#ffffbf",
high = "#d7191c",
midpoint = median(multi_dim_data$Rui_ro, na.rm = TRUE),
name = "Rủi ro tín dụng\n(%)",
labels = function(x) sprintf("%.1f%%", x)
) +
# Hình dạng cho giai đoạn
scale_shape_manual(
values = c(15, 16, 17),
name = "Giai đoạn\nphát triển"
) +
# Định dạng trục
scale_x_continuous(
labels = function(x) paste0(format(x, big.mark = ".", decimal.mark = ","), "K"),
name = "Quy mô tài sản (Nghìn tỷ VND)"
) +
scale_y_continuous(
labels = function(x) paste0(x, "%"),
name = "Hiệu quả (ROE %)"
) +
# Tiêu đề và nhãn
labs(
title = "BIỂU ĐỒ PHÂN TÍCH ĐA CHIỀU HIỆU QUẢ HOẠT ĐỘNG",
subtitle = "Mối quan hệ giữa Quy mô - Hiệu quả - Rủi ro - Giá trị cổ phiếu",
caption = "Chú thích:\n• Kích thước điểm: Lãi cơ bản trên cổ phiếu (EPS)\n• Màu sắc: Tỷ lệ rủi ro tín dụng\n• Hình dạng: Giai đoạn phát triển"
) +
# Theme tối ưu
theme_minimal(base_size = 12) +
theme(
plot.title = element_text(
face = "bold",
size = 16,
hjust = 0.5,
margin = margin(b = 10)
),
plot.subtitle = element_text(
size = 12,
hjust = 0.5,
color = "gray40",
margin = margin(b = 15)
),
plot.caption = element_text(
size = 10,
color = "gray50",
hjust = 0,
margin = margin(t = 15)
),
legend.position = "right",
legend.box = "vertical",
legend.spacing.y = unit(0.5, "cm"),
legend.title = element_text(face = "bold", size = 10),
legend.text = element_text(size = 9),
axis.title = element_text(face = "bold"),
panel.grid.minor = element_blank(),
plot.margin = margin(20, 20, 20, 20)
) +
# Hướng dẫn legend
guides(
size = guide_legend(
order = 1,
override.aes = list(color = "gray50")
),
color = guide_colorbar(
order = 2,
barwidth = 1,
barheight = 10
),
shape = guide_legend(
order = 3,
override.aes = list(size = 4, color = "black")
)
)
print(p25_optimized)🧩 Phân tích Code R
Cách thực hiện:
Dùng mutate() để tính toán và chuẩn hóa 5 chỉ số chính: Quy_mo (Quy mô), Hieu_qua (ROE), Rui_ro (Rủi ro tín dụng), Gia_tri (EPS) và giai_doan_phat_trien.
Sử dụng ggplot() và geom_point() để mã hóa 5 biến này vào 5 thuộc tính đồ họa: trục X (Quy mô), trục Y (Hiệu quả), Kích thước (EPS), Màu sắc (Rủi ro), và Hình dạng (Giai đoạn).
Thêm các lớp phân tích: geom_smooth() (vẽ đường xu hướng hồi quy tuyến tính), geom_hline/geom_vline (vẽ đường trung bình), và geom_text_repel() (thêm nhãn năm tránh chồng chéo).
📊 Phân tích Kết quả
Diễn giải kết quả: Biểu đồ cho thấy một hành trình rõ ràng:
Theo dòng thời gian (2015 đến 2023), doanh nghiệp di chuyển từ góc dưới-trái (Quy mô nhỏ, Hiệu quả thấp) lên góc trên-phải (Quy mô lớn, Hiệu quả cao).
Giai đoạn bứt phá (2021-2023, hình tam giác) cho thấy sự tăng trưởng vượt bậc đồng thời ở cả ba yếu tố: Quy mô tài sản (trên 550K tỷ), Hiệu quả (ROE > 10%) và Giá trị (EPS, kích thước điểm lớn nhất).
# Tạo dữ liệu cho biểu đồ waterfall - phân tích tăng trưởng lợi nhuận
waterfall_data <- stb_clean %>%
select(nam, loi_nhuan_sau_thue) %>%
mutate(
Tang_truong = loi_nhuan_sau_thue - lag(loi_nhuan_sau_thue),
Loai = ifelse(is.na(Tang_truong), "Khởi điểm",
ifelse(Tang_truong >= 0, "Tăng", "Giảm")),
Cumulative = cumsum(ifelse(is.na(Tang_truong), loi_nhuan_sau_thue, Tang_truong)),
Start = ifelse(Loai == "Khởi điểm", 0,
lag(Cumulative)),
End = Cumulative
) %>%
filter(!is.na(Tang_truong) | nam == min(nam))
# Định dạng số với dấu phân cách
format_billions <- function(x) {
paste0(format(round(x/1e3), big.mark = ".", decimal.mark = ",", scientific = FALSE), "B")
}
p26 <- ggplot(waterfall_data) +
geom_rect(aes(xmin = nam - 0.4, xmax = nam + 0.4,
ymin = Start, ymax = End, fill = Loai),
alpha = 0.8, color = "white", size = 0.5) +
geom_segment(aes(x = nam - 0.4, xend = nam + 0.4, y = End, yend = End),
color = "gray", linetype = "dashed", alpha = 0.7) +
geom_text(aes(x = nam, y = (Start + End)/2,
label = ifelse(Loai == "Khởi điểm",
format_billions(End),
paste0(ifelse(Tang_truong > 0, "+", ""),
format_billions(Tang_truong)))),
size = 3.5, fontface = "bold", color = "white") +
geom_point(aes(x = nam, y = End), color = "darkblue", size = 2) +
geom_line(aes(x = nam, y = End, group = 1), color = "darkblue", linetype = "dotted", size = 1) +
scale_fill_manual(
values = c("Khởi điểm" = "#1f77b4", "Tăng" = "#2ca02c", "Giảm" = "#d62728"),
name = "Loại biến động"
) +
scale_y_continuous(
labels = function(x) format_billions(x),
name = "Lợi nhuận sau thuế (Tỷ VND)"
) +
scale_x_continuous(
breaks = waterfall_data$nam,
name = "Năm"
) +
labs(
title = "PHÂN TÍCH BIẾN ĐỘNG LỢI NHUẬN SAU THUẾ",
subtitle = "Waterfall Chart - Phân tích đóng góp của từng năm vào tổng lợi nhuận",
caption = "Ghi chú: Số liệu được làm tròn và hiển thị theo đơn vị Tỷ VND (B = Tỷ)"
) +
theme_minimal(base_size = 12) +
theme(
plot.title = element_text(face = "bold", size = 16, hjust = 0.5, margin = margin(b = 10)),
plot.subtitle = element_text(size = 12, hjust = 0.5, color = "gray40", margin = margin(b = 15)),
plot.caption = element_text(size = 10, color = "gray50", hjust = 0.5, margin = margin(t = 10)),
axis.text.x = element_text(angle = 45, hjust = 1, size = 10),
axis.title = element_text(face = "bold"),
legend.position = "bottom",
panel.grid.minor = element_blank(),
plot.margin = margin(20, 20, 20, 20)
)
print(p26)🧩 Phân tích Code R
Giải thích Khối:
Tính toán Biến động: Sử dụng lag và mutate để xác định Tang_truong (lợi nhuận năm nay - năm trước).
Phân loại & Tổng hợp: Gán Loai (“Tăng”, “Giảm”) và tính Start, End (vị trí cột) dựa trên tổng dồn tích lũy (Cumulative).
Trực quan hóa: Dùng ggplot2 (geom_rect) để vẽ cột và geom_text để gắn nhãn giá trị biến động (định dạng \(B = \text{Tỷ}\)).
📊 Phân tích Kết quả
capital_util_data <- stb_clean %>%
mutate(
Hieu_su_su_dung_von = (cho_vay_khach_hang + chung_khoan_dau_tu) / tong_tai_san * 100,
Ty_le_du_tru = (tong_tai_san - cho_vay_khach_hang - chung_khoan_dau_tu) / tong_tai_san * 100,
Loai_hieu_su = ifelse(Hieu_su_su_dung_von > median(Hieu_su_su_dung_von), "Cao", "Thấp")
)
p27 <- ggplot(capital_util_data, aes(x = nam)) +
geom_area(aes(y = Hieu_su_su_dung_von), fill = "lightgreen", alpha = 0.6, color = "darkgreen", size = 0.5) +
geom_area(aes(y = Ty_le_du_tru), fill = "lightblue", alpha = 0.6, color = "darkblue", size = 0.5) +
geom_line(aes(y = ROA * 2), color = "red", size = 1.5, linetype = "solid", alpha = 0.8) +
geom_point(aes(y = ROA * 2, shape = Loai_hieu_su), color = "red", size = 3, alpha = 0.8) +
geom_hline(yintercept = mean(capital_util_data$Hieu_su_su_dung_von),
linetype = "dashed", color = "darkgreen", size = 1) +
geom_text(aes(y = Hieu_su_su_dung_von, label = paste0(round(Hieu_su_su_dung_von, 1), "%")),
vjust = -0.8, size = 3, color = "darkgreen", fontface = "bold") +
scale_y_continuous(
name = "Hiệu suất sử dụng vốn & Dự trữ (%)",
sec.axis = sec_axis(~./2, name = "ROA (%)")
) +
scale_shape_manual(values = c("Cao" = 16, "Thấp" = 1)) +
labs(title = "PHÂN TÍCH HIỆU QUẢ SỬ DỤNG VỐN HUY ĐỘNG",
subtitle = "Hiệu suất sử dụng vốn (xanh), Tỷ lệ dự trữ (xanh dương), ROA (đỏ)",
x = "Năm", y = "Tỷ lệ (%)", shape = "Mức hiệu suất") +
theme_minimal() +
theme(
axis.title.y.left = element_text(color = "darkgreen"),
axis.title.y.right = element_text(color = "red"),
plot.title = element_text(face = "bold", size = 14)
)
print(p27)🧩 Phân tích Code R
Cách thực hiện:
Tính toán: Dùng mutate để tạo 3 cột mới: Hieu_su_su_dung_von (tỷ lệ tài sản dùng để cho vay và đầu tư), Ty_le_du_tru (phần tài sản còn lại) và Loai_hieu_su (phân loại “Cao” hay “Thấp” dựa trên giá trị median của hiệu suất).
Vẽ biểu đồ: Sử dụng ggplot với nam (năm) làm trục X.
Thêm lớp: Vẽ 2 biểu đồ miền (geom_area) cho Hieu_su_su_dung_von (xanh lá) và Ty_le_du_tru (xanh dương). Vẽ 1 biểu đồ đường (geom_line) và điểm (geom_point) cho ROA (đỏ). Hình dạng của điểm (tròn đặc/rỗng) phụ thuộc vào Loai_hieu_su.
Trục Y kép: Dùng scale_y_continuous và sec_axis để tạo trục Y thứ hai. Dữ liệu ROA được nhân 2 (ROA * 2) để vẽ, và trục phụ được chia 2 (~./2) để hiển thị giá trị ROA gốc trên thang đo riêng (trục phải).
📊 Phân tích Kết quả
cash_flow_data <- stb_clean %>%
mutate(
Ty_le_thu_nhap_lai = thu_nhap_lai_thuan / tong_thu_nhap_hoat_dong * 100,
Ty_le_chi_phi_lai = abs(tong_chi_phi_hoat_dong) / tong_thu_nhap_hoat_dong * 100,
Bien_loi_nhuan = (loi_nhuan_sau_thue / tong_thu_nhap_hoat_dong) * 100,
Dong_tien_hoat_dong = tong_thu_nhap_hoat_dong - abs(tong_chi_phi_hoat_dong)
)
p29 <- ggplot(cash_flow_data, aes(x = nam)) +
geom_col(aes(y = tong_thu_nhap_hoat_dong/1e6), fill = "lightgreen", alpha = 0.7, width = 0.6) +
geom_col(aes(y = abs(tong_chi_phi_hoat_dong)/1e6), fill = "lightcoral", alpha = 0.7, width = 0.6) +
geom_line(aes(y = Dong_tien_hoat_dong/1e6), color = "darkblue", size = 1.5, group = 1) +
geom_point(aes(y = Dong_tien_hoat_dong/1e6), color = "darkblue", size = 3) +
geom_area(aes(y = Bien_loi_nhuan * 100), fill = "gold", alpha = 0.3, color = "orange", size = 0.5) +
geom_text(aes(y = Dong_tien_hoat_dong/1e6, label = paste0(round(Dong_tien_hoat_dong/1e6, 1), "B")),
vjust = -0.8, size = 3, color = "darkblue", fontface = "bold") +
scale_y_continuous(
name = "Thu nhập & Chi phí (Nghìn tỷ VND)",
sec.axis = sec_axis(~./100, name = "Biên lợi nhuận (%)")
) +
labs(title = "PHÂN TÍCH DÒNG TIỀN HOẠT ĐỘNG",
subtitle = "Thu nhập (xanh), Chi phí (đỏ), Dòng tiền hoạt động (xanh đậm), Biên lợi nhuận (vàng)",
x = "Năm", y = "Giá trị (Nghìn tỷ VND)") +
theme_minimal() +
theme(
axis.title.y.left = element_text(color = "darkgreen"),
axis.title.y.right = element_text(color = "orange"),
plot.title = element_text(face = "bold", size = 14)
)
print(p29)🧩 Phân tích Code R
Cách thực hiện:
Dùng mutate để tạo các cột mới: Bien_loi_nhuan (lợi nhuận sau thuế / thu nhập) và Dong_tien_hoat_dong (chênh lệch thu-chi).
Dùng ggplot2 để vẽ biểu đồ phức hợp: geom_col (cột) cho Thu nhập (xanh) và Chi phí (đỏ), geom_area (vùng, màu vàng) cho Bien_loi_nhuan,geom_line và geom_point (đường và điểm, xanh đậm) cho Dong_tien_hoat_dong, geom_text để thêm nhãn giá trị (ví dụ: “3.1B”, “0.9B”) cho dòng tiền.
scale_y_continuous(sec.axis = …): Tạo trục Y thứ hai (bên phải, màu cam) để hiển thị Bien_loi_nhuan (Biên lợi nhuận) theo đơn vị (%).
📊 Phân tích Kết quả
Diễn giải kết quả:
Dòng tiền hoạt động (Đường xanh đậm/Chữ số): Mặc dù nằm sát trục 0 (so với quy mô Nghìn tỷ), giá trị tuyệt đối của dòng tiền (chênh lệch thu-chi) cho thấy xu hướng tăng trưởng ổn định. Nó giảm nhẹ vào 2016 (0.9B) nhưng sau đó tăng liên tục, đạt 12.2B vào cuối giai đoạn.
Biên lợi nhuận (Vùng vàng, Trục Y phải): Đây là xu hướng nổi bật nhất. Biên lợi nhuận cải thiện ấn tượng, giảm xuống mức rất thấp vào năm 2016, nhưng sau đó tăng vọt và liên tục từ khoảng 2017.5, đạt mức rất cao (trên 20%) vào cuối kỳ.
# Tạo dữ liệu tương quan theo từng giai đoạn
correlation_by_phase <- stb_clean %>%
group_by(giai_doan_phat_trien) %>%
summarise(
Corr_Assets_Profit = cor(tong_tai_san, loi_nhuan_sau_thue, use = "complete.obs"),
Corr_Loan_Revenue = cor(cho_vay_khach_hang, thu_nhap_lai_thuan, use = "complete.obs"),
Corr_Capital_ROE = cor(tong_von_chu_so_huu, ROE, use = "complete.obs"),
Corr_Risk_Return = cor(abs(chi_phi_du_phong_rui_ro_tin_dung), ROA, use = "complete.obs"),
So_nam = n(),
.groups = 'drop'
) %>%
pivot_longer(cols = starts_with("Corr"), names_to = "Moi_quan_he", values_to = "He_so_tuong_quan") %>%
mutate(Moi_quan_he = case_when(
Moi_quan_he == "Corr_Assets_Profit" ~ "Tài sản - Lợi nhuận",
Moi_quan_he == "Corr_Loan_Revenue" ~ "Cho vay - Thu nhập lãi",
Moi_quan_he == "Corr_Capital_ROE" ~ "Vốn CSH - ROE",
Moi_quan_he == "Corr_Risk_Return" ~ "Rủi ro - Lợi nhuận"
))
p31 <- ggplot(correlation_by_phase, aes(x = giai_doan_phat_trien, y = He_so_tuong_quan, fill = Moi_quan_he)) +
geom_col(position = "dodge", alpha = 0.8, color = "white", size = 0.3, width = 0.7) +
geom_hline(yintercept = 0, linetype = "solid", color = "black", size = 0.5) +
geom_hline(yintercept = c(-0.5, 0.5), linetype = "dashed", color = "red", alpha = 0.5) +
geom_text(aes(label = round(He_so_tuong_quan, 3),
vjust = ifelse(He_so_tuong_quan > 0, 1.5, -0.5)),
position = position_dodge(width = 0.7),
size = 3,
fontface = "bold",
color = "black") +
geom_point(aes(color = Moi_quan_he), position = position_dodge(width = 0.7), size = 2, alpha = 0.8) +
geom_segment(aes(xend = giai_doan_phat_trien, yend = 0, color = Moi_quan_he),
position = position_dodge(width = 0.7), linetype = "dotted", alpha = 0.6) +
scale_fill_brewer(palette = "Set1") +
scale_color_brewer(palette = "Set1") +
labs(title = "PHÂN TÍCH TƯƠNG QUAN ĐỘNG THEO GIAI ĐOẠN",
subtitle = "Hệ số tương quan giữa các cặp chỉ số quan trọng qua các giai đoạn phát triển",
x = "Giai đoạn phát triển", y = "Hệ số tương quan",
fill = "Mối quan hệ", color = "Mối quan hệ") +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(face = "bold", size = 14),
legend.position = "bottom"
)
print(p31)🧩 Phân tích Code R
Cách thực hiện:
Phân nhóm và Tính toán (group_by, summarise): Chia dữ liệu (stb_clean) theo cột “giai_doan_phat_trien”. Trong từng giai đoạn, dùng hàm cor để tính 4 hệ số tương quan khác nhau (ví dụ: Tài sản vs Lợi nhuận, Rủi ro vs Lợi nhuận).
Tái cấu trúc (pivot_longer): Chuyển đổi 4 cột tương quan (dạng rộng) thành 2 cột (dạng dài): một cột chứa “Mối quan hệ” và một cột chứa “Hệ số tương quan”, để phù hợp với ggplot.
Vẽ biểu đồ (ggplot): Dùng geom_col(position = “dodge”) để vẽ các cột cho từng mối quan hệ đứng cạnh nhau (thay vì chồng lên nhau) trong mỗi giai đoạn phát triển.
📊 Phân tích Kết quả
Diễn giải kết quả:
Giai đoạn 2018-2020 (Tăng trưởng): Tất cả các mối quan hệ đều là tương quan thuận rất mạnh (hệ số đều gần 0.8 - 1.0). Điều này cho thấy sự tăng trưởng đồng pha: quy mô (tài sản, cho vay) tăng kéo theo lợi nhuận/thu nhập tăng.
Giai đoạn 2021-2024 (Bứt phá): Xuất hiện 2 thay đổi quan trọng:
Rủi ro - Lợi nhuận (Xanh dương): Chuyển từ tương quan thuận mạnh (0.812) sang tương quan nghịch mạnh (-0.66). Điều này rất tích cực, có thể ngụ ý lợi nhuận (ROA) tăng lên trong khi chi phí dự phòng rủi ro lại giảm (chất lượng tín dụng được cải thiện).
Vốn CSH - ROE (Tím): Tương quan yếu đi rõ rệt (từ 0.877 xuống 0.325). Điều này cho thấy việc tăng vốn chủ sở hữu không còn là động lực chính thúc đẩy ROE. Thay vào đó, ROE tăng “bứt phá” có thể đến từ việc tối ưu đòn bẩy tài chính hoặc cải thiện mạnh biên lợi nhuận.
# Lấy dữ liệu năm 2024
assets_2024 <- stb_clean %>%
filter(nam == 2024) %>%
mutate(
Cho_vay = cho_vay_khach_hang,
Chung_khoan = chung_khoan_dau_tu + chung_khoan_kinh_doanh,
Tien_gui_NH_khac = vay_TCTD_khac,
Tai_san_khac = tong_tai_san - (cho_vay_khach_hang + chung_khoan_dau_tu + chung_khoan_kinh_doanh + vay_TCTD_khac)
) %>%
select(Cho_vay, Chung_khoan, Tien_gui_NH_khac, Tai_san_khac) %>%
pivot_longer(everything(), names_to = "Loai_tai_san", values_to = "Gia_tri") %>%
mutate(
Phan_tram = Gia_tri / sum(Gia_tri) * 100,
Loai_tai_san = factor(Loai_tai_san,
levels = c("Cho_vay", "Chung_khoan", "Tien_gui_NH_khac", "Tai_san_khac"),
labels = c("Cho vay KH", "Chứng khoán", "Tiền gửi NH khác", "Tài sản khác")),
Gia_tri_ty = Gia_tri / 1e6, # Chuyển sang nghìn tỷ
Label_detail = paste0(
Loai_tai_san, "\n",
round(Phan_tram, 1), "%\n",
format(round(Gia_tri_ty), big.mark = ".", decimal.mark = ",", scientific = FALSE), "K tỷ"
),
Label_simple = paste0(round(Phan_tram, 1), "%")
)
# Phiên bản 1: Biểu đồ tròn đơn giản, rõ ràng
p32_simple <- ggplot(assets_2024, aes(x = "", y = Phan_tram, fill = Loai_tai_san)) +
geom_bar(stat = "identity", width = 1, color = "white", size = 1.5, alpha = 0.9) +
geom_text(aes(label = Label_simple),
position = position_stack(vjust = 0.5),
color = "white", size = 5, fontface = "bold") +
coord_polar("y", start = 0) +
scale_fill_manual(
values = c("Cho vay KH" = "#1f77b4",
"Chứng khoán" = "#ff7f0e",
"Tiền gửi NH khác" = "#2ca02c",
"Tài sản khác" = "#d62728")
) +
labs(
title = "CƠ CẤU TÀI SẢN NĂM 2024",
subtitle = "Phân bổ tỷ trọng các loại tài sản chính",
fill = "Loại tài sản"
) +
theme_void() +
theme(
plot.title = element_text(face = "bold", size = 18, hjust = 0.5, margin = margin(b = 10)),
plot.subtitle = element_text(size = 14, hjust = 0.5, color = "gray40", margin = margin(b = 15)),
legend.position = "bottom",
legend.text = element_text(size = 11),
legend.title = element_text(face = "bold", size = 12)
)
print(p32_simple)🧩 Phân tích Code R
Cách thực hiện:
Lọc và Gộp nhóm : Lấy dữ liệu năm 2024. Tạo 4 cột tài sản chính bằng cách gộp các tài khoản chi tiết và tính Tai_san_khac (Tài sản khác) là phần còn lại.
Tái cấu trúc : Chuyển dữ liệu từ định dạng “rộng” sang “dài” với hai cột chính: Loai_tai_san (Loại tài sản) và Gia_tri (Giá trị).
Tính tỷ trọng & Nhãn: Dùng mutate để tính Phan_tram (%) cho mỗi loại tài sản và tạo nhãn Label_simple (ví dụ: “65.1%”) để hiển thị trên biểu đồ.
Vẽ biểu đồ:
Sử dụng geom_bar với stat = “identity” để tạo biểu đồ cột xếp chồng.
Sử dụng coord_polar(“y”) để “uốn cong” biểu đồ cột thành biểu đồ tròn.
📊 Phân tích Kết quả
Diễn giải kết quả:
“Cho vay KH” (màu xanh dương) chiếm tỷ trọng áp đảo, với 65.1% (gần 2/3) tổng tài sản. Điều này cho thấy hoạt động cốt lõi và quan trọng nhất của ngân hàng là cho vay khách hàng.
“Tài sản khác” (màu đỏ) là nhóm lớn thứ hai, chiếm 19.3%.
Hai nhóm còn lại, “Chứng khoán” (màu cam) và “Tiền gửi NH khác” (màu xanh lá), chiếm tỷ trọng nhỏ hơn đáng kể, lần lượt là 11.2% và 4.4%.
cumulative_data <- stb_clean %>%
arrange(nam) %>%
mutate(
LNST_tich_luy = cumsum(loi_nhuan_sau_thue),
Von_CSH_tich_luy = cumsum(tong_von_chu_so_huu - lag(tong_von_chu_so_huu, default = first(tong_von_chu_so_huu))),
Ty_suat_sinh_loi_tich_luy = LNST_tich_luy / Von_CSH_tich_luy * 100
)
p34 <- ggplot(cumulative_data, aes(x = nam)) +
geom_area(aes(y = LNST_tich_luy/1e3), fill = "lightgreen", alpha = 0.6, color = "darkgreen", size = 1) +
geom_area(aes(y = Von_CSH_tich_luy/1e3), fill = "lightblue", alpha = 0.4, color = "darkblue", size = 1) +
geom_line(aes(y = Ty_suat_sinh_loi_tich_luy * 50), color = "red", size = 1.5, linetype = "solid") +
geom_point(aes(y = Ty_suat_sinh_loi_tich_luy * 50), color = "red", size = 3) +
geom_text(aes(y = LNST_tich_luy/1e3, label = paste0(round(LNST_tich_luy/1e3, 0), "B")),
vjust = -0.8, size = 3, color = "darkgreen", fontface = "bold") +
geom_hline(yintercept = mean(cumulative_data$Ty_suat_sinh_loi_tich_luy, na.rm = TRUE) * 50,
linetype = "dashed", color = "red", alpha = 0.7) +
scale_y_continuous(
name = "Lợi nhuận & Vốn CSH tích lũy (Nghìn tỷ VND)",
sec.axis = sec_axis(~./50, name = "Tỷ suất sinh lời tích lũy (%)")
) +
labs(title = "XU HƯỚNG TÍCH LŨY GIÁ TRỊ QUA THỜI GIAN",
subtitle = "Lợi nhuận sau thuế tích lũy (xanh), Vốn CSH tích lũy (xanh dương), Tỷ suất sinh lời tích lũy (đỏ)",
x = "Năm", y = "Giá trị tích lũy (Nghìn tỷ VND)") +
theme_minimal() +
theme(
axis.title.y.left = element_text(color = "darkgreen"),
axis.title.y.right = element_text(color = "red"),
plot.title = element_text(face = "bold", size = 14)
)
print(p34)🧩 Phân tích Code R
Cách thực hiện:
Sử dụng arrange(nam) để sắp xếp dữ liệu theo thời gian, một bước bắt buộc trước khi tính tổng dồn.
Dùng mutate() và cumsum() để tạo các biến mới: LNST_tich_luy (tổng LNST dồn), Von_CSH_tich_luy (tổng vốn CSH bổ sung dồn), và Ty_suat_sinh_loi_tich_luy (tỷ lệ giữa hai biến này).
Sử dụng ggplot() với geom_area (cho LNST và Vốn, đơn vị Nghìn tỷ VND) và geom_line (cho Tỷ suất sinh lời, đơn vị %).
Áp dụng scale_y_continuous và sec_axis để tạo trục Y kép, cho phép hiển thị hai đơn vị (tiền tệ và %) trên cùng một biểu đồ.
📊 Phân tích Kết quả
liquidity_risk_data <- stb_clean %>%
mutate(
Ty_le_thanh_khoan = tien_gui_cua_khach_hang / cho_vay_khach_hang * 100,
Rui_ro_tin_dung = abs(chi_phi_du_phong_rui_ro_tin_dung) / cho_vay_khach_hang * 100,
Chenh_lech_ky_han = (cho_vay_khach_hang - tien_gui_cua_khach_hang) / 1e6,
Muc_do_an_toan = ifelse(Ty_le_thanh_khoan > 100 & Rui_ro_tin_dung < median(Rui_ro_tin_dung, na.rm = TRUE),
"An toàn", "Cần lưu ý")
)
p35 <- ggplot(liquidity_risk_data, aes(x = Ty_le_thanh_khoan, y = Rui_ro_tin_dung)) +
geom_point(aes(size = abs(Chenh_lech_ky_han), color = Muc_do_an_toan, shape = giai_doan_phat_trien),
alpha = 0.7) +
geom_smooth(method = "lm", se = TRUE, color = "darkred", linetype = "dashed", alpha = 0.2) +
geom_hline(yintercept = median(liquidity_risk_data$Rui_ro_tin_dung, na.rm = TRUE),
linetype = "dotted", color = "blue", size = 1) +
geom_vline(xintercept = 100, linetype = "dotted", color = "green", size = 1) +
geom_text(aes(label = nam), vjust = -0.8, size = 3, check_overlap = TRUE, fontface = "bold") +
geom_segment(aes(xend = Ty_le_thanh_khoan, yend = Rui_ro_tin_dung,
x = 100, y = median(Rui_ro_tin_dung, na.rm = TRUE)),
color = "gray", alpha = 0.3, linetype = "dotted") +
scale_size_continuous(range = c(4, 10), name = "Chênh lệch kỳ hạn\n(Nghìn tỷ VND)") +
scale_color_manual(values = c("An toàn" = "green", "Cần lưu ý" = "orange")) +
scale_shape_manual(values = c(15, 16, 17)) +
labs(title = "MỐI QUAN HỆ GIỮA THANH KHOẢN VÀ RỦI RO TÍN DỤNG",
subtitle = "Tỷ lệ thanh khoản vs Rủi ro tín dụng, màu sắc: Mức độ an toàn, kích thước: Chênh lệch kỳ hạn",
x = "Tỷ lệ thanh khoản (%)", y = "Rủi ro tín dụng (%)",
color = "Mức độ an toàn", shape = "Giai đoạn") +
theme_minimal() +
theme(plot.title = element_text(face = "bold", size = 14))
print(p35)🧩 Phân tích Code R
Cách thực hiện:
Tính toán: Dùng mutate để tính 4 biến quan trọng: Ty_le_thanh_khoan (trục X), Rui_ro_tin_dung (trục Y), Chenh_lech_ky_han (quyết định kích thước bong bóng), và Muc_do_an_toan (quyết định màu sắc).
Vẽ biểu đồ: Dùng geom_point để ánh xạ 5 biến lên các thuộc tính trực quan: X, Y, Kích thước (size), Màu sắc (color), và Hình dạng (shape - theo giai_doan_phat_trien).
Thêm ngữ cảnh: Thêm đường xu hướng hồi quy (geom_smooth), đường tham chiếu 100% thanh khoản (dọc) và đường rủi ro tín dụng trung vị (ngang).
📊 Phân tích Kết quả
Diễn giải kết quả: Biểu đồ cho thấy:
Giai đoạn 2020-2022 : Đây là giai đoạn rủi ro nhất khi Rủi ro tín dụng tăng vọt, đạt đỉnh vào năm 2022, đồng thời thanh khoản cũng ở mức tương đối thấp.
Giai đoạn 2023-2024 : Tình hình đã được cải thiện đáng kể. Rủi ro tín dụng giảm mạnh xuống mức an toàn, trong khi thanh khoản được duy trì ở mức tốt (khoảng 120-125%).