# 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)

1 Phần 1: PHÂN TÍCH BỘ DỮ LIỆU “Orders_Sales_Data

2 GIỚI THIỆU BỘ DỮ LIỆU

2.1 Đọc dữ liệu

df <- read.csv("Orders_Sales_Data.csv", sep = ";")
knitr::kable(head(df, 4 ), caption = "4  dòng đầu của dataset")
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ả

  • Diễn giải kết quả: Bảng hiển thị 11 cột. Chúng ta có thể thấy các cột định danh (Order_ID, Customer_ID), cột phân loại (Customer_Type, Category, Region) và các cột số liệu (Unit_Price, Quantity, Discount, Total_Price).

2.2 Kích thước dataset

dim_df <- data.frame(
  "Số dòng" = nrow(df),
  "Số cột" = ncol(df)
)
knitr::kable(dim_df, caption = "Kích thước dataset")
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:

    • Dùng hàm knitr::kable() để định dạng dim_df thành một bảng HTML đẹp mắt, có tiêu đề là “Kích thước dataset”.

📊 Phân tích Kết quả (Bảng kích thước)

  • Diễn giải kết quả: Dataset có 1.048.575 dòng và 11 cột.

2.3 Tên các cột

col_names <- data.frame("Tên cột" = names(df))
knitr::kable(col_names, caption = "Tên các cột")
Tên các cột
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

  • knitr::kable(…): Sử dụng thư viện knitr để định dạng data frame col_names thành một bảng HTML gọn gàng, có tiêu đề là “Tên các cột”.

📊 Phân tích Kết quả

  • Diễn giải kết quả: Kết quả liệt kê 11 cột, bao gồm các cột định danh (Order_ID, Customer_ID), cột phân loại (Category, Region) và các cột số liệu (Unit_Price, Quantity, Total_Price, Order_Date).

2.4 Cấu trúc dataset

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ấ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ả

  • Diễn giải kết quả: Bảng cho thấy các cột số (Unit_Price, Quantity, Total_Price) đã được nhận diện đúng là numeric (số thực) hoặc integer (số nguyên). Các cột phân loại (Category, Region) là character (văn bản).

2.5 Thống kê tổng quan

# 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ố")
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ả

  • Diễn giải kết quả: Bảng kết quả cung cấp cái nhìn tổng quan về phân phối giá trị của 4 cột số: Unit_Price (Đơn giá), Quantity (Số lượng), Discount (Chiết khấu), và Total_Price (Tổng giá).

2.6 Dữ liệu missing

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")
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ả

  • Diễn giải kết quả: Dựa trên bảng “Dữ liệu missing”, kết quả cho thấy không có bất kỳ giá trị missing (NA) nào trong tất cả các cột của bộ dữ liệu (từ Order_ID đến Order_Date). Cột “Số giá trị missing” đều hiển thị giá trị là 0.

2.7 Dữ liệu trùng lặp

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")
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ả

  • Diễn giải kết quả:Bảng kết quả cho thấy “Số.dòng.trùng.lặp” có giá trị là 0.

2.8 Số lượng duy nhất

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")
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ả

  • Diễn giải kết quả: Có 349,193 Order_ID, 10,000 Customer_ID, 47 Product và 4 Category.

2.9 Phân tích sơ bộ các cột số

# 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ố")
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ả

  • Diễn giải kết quả: Total_Price có SD (Độ lệch chuẩn) rất cao (510.9), xác nhận lại rằng giá trị các đơn hàng rất biến động. Unit_Price (SD 14.78) cũng biến động, trong khi Discount (SD 0.044) tương đối đồng đều.

2.10 Xem thêm một số dòng ngẫu nhiên

set.seed(123)
random_rows <- df %>% sample_n(4)
knitr::kable(random_rows, caption = "4  dòng dữ liệu ngẫu nhiên")
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ả

  • Diễn giải kết quả: Bảng kết quả hiển thị chính xác 4 bản ghi đơn hàng được chọn ngẫu nhiên từ bộ dữ liệu gốc.

3 XỬ LÝ DỮ LIỆU THÔ VÀ MÃ HÓA

3.1 Chuyển đổi định dạng ngày tháng

# 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")
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)

  • Diễn giải kết quả: Bảng cho thấy “Kiểu.dữ.liệu.mới” là Date và “Giá.trị.đầu.tiên” được hiển thị ở định dạng chuẩn của R (“2023-08-23”).

3.2 Trích xuất thông tin thời gian

# 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)")
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)

  • Diễn giải kết quả: Bảng kết quả xác nhận 5 dòng dữ liệu đầu tiên đều có chung ngày đặt hàng là 2023-08-23. Code đã trích xuất thành công các thành phần từ ngày này, bao gồm: Năm (2023), Tháng (8), Quý (3), Ngày (23), và Thứ (Thứ Tư). Dữ liệu trích xuất là nhất quán và chính xác.

3.3 Tạo cột doanh thu tính toán

# 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)")
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ả

  • Diễn giải kết quả: Bảng kết quả xác nhận rằng phép tính doanh thu đã được thực hiện chính xác cho 5 dòng đầu tiên. Cột Calculated_Revenue chính là tích của Unit_Price và Quantity ở mỗi dòng.

3.4 Kiểm tra giá trị âm

# 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ể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ả

  • Diễn giải kết quả: Bảng kết quả cho thấy Count (Số lượng) giá trị âm ở tất cả các cột được kiểm tra đều là 0.

3.5 Chuẩn hóa dữ liệu văn bản

# 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)")
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ả

  • Diễn giải kết : Bảng cho thấy Product (“VIO WASSER”) đã IN HOA. Category (“Water”) và Region (“Baden-Württemberg”) đã được viết hoa chữ cái đầu tiên.

3.6 Phân loại mùa

# 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)")
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ả

  • Diễn giải kết quả:Bảng kết quả cho thấy 5 dòng dữ liệu đầu tiên. Cả 5 dòng này đều có giá trị Order_Month là 8.

3.7 Phân loại giá sản phẩm

# 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)")
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ả

  • Diễn giải kết quả: Bảng kết quả hiển thị 5 dòng đầu tiên của dữ liệu đã được phân loại.Ví dụ, các giá trị 1.66, 1.56, 1.17, và 0.87 (đều nhỏ hơn 2) được gán đúng nhãn “Low Price”. Giá trị 3.22 (nằm trong khoảng từ 2 đến 5) được gán đúng nhãn “Medium Price”.

3.8 Tạo ID đơn giản

# 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)")
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…).

3.9 Phân tích kích thước đơn hàng

# 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)")
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”.

3.10 Tổng quan kết quả xử lý

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")
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ả

  • Diễn giải kết quả: Bảng kết quả đóng vai trò như một “checklist”, xác nhận rằng tất cả 8 bước xử lý dữ liệu và tạo đặc trưng đã được thực hiện thành công.

4 THỐNG KÊ CƠ BẢN

4.1 Tổng quan doanh thu và số lượng

cat("1. TỔNG QUAN DOANH THU VÀ SỐ LƯỢNG:\n")
## 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ả

  • Diễn giải kết quả: Bảng kết quả cung cấp một cái nhìn tổng quan về quy mô kinh doanh. Toàn bộ dữ liệu ghi nhận tổng doanh thu là 148.6 triệu (đơn vị tiền tệ) từ việc bán ra 24.3 triệu sản phẩm.Các chỉ số này đến từ 349,193 đơn hàng riêng biệt.

4.2 Doanh thu theo năm

cat("\n2. DOANH THU THEO NĂM:\n")
## 
## 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ả

  • Diễn giải kết quả:Doanh thu (Revenue) và Giá trị trung bình đơn (Avg_Order_Value) đều cho thấy xu hướng tăng trưởng đều đặn qua các năm.Doanh thu tăng từ 47.9 triệu (2021) lên 50.8 triệu (2023).Giá trị trung bình đơn tăng từ 413 (2021) lên 437 (2023).

4.3 Top 10 sản phẩm có doanh thu cao nhất

cat("\n3. TOP 10 SẢN PHẨM DOANH THU CAO NHẤT:\n")
## 
## 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ả

  • Diễn giải kết quả: Bảng kết quả cho thấy top 10 sản phẩm tạo ra nhiều doanh thu nhất. Dẫn đầu là VEUVE CLICQUOT (khoảng 2.6 triệu) và MOËT & CHANDON (khoảng 2.2 triệu), 9/10 sản phẩm trong danh sách này thuộc danh mục ‘Alcoholic Beverages’ (Đồ uống có cồn). Doanh thu cao của nhóm này chủ yếu đến từ Avg_Price (Giá trung bình) cao, đặc biệt là 2 sản phẩm top đầu (giá 94.1 và 62.4).

4.4 Top 10 sản phẩm bán chạy nhất

cat("\n4. TOP 10 SẢN PHẨM BÁN CHẠY NHẤT:\n")
## 
## 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ả

  • Diễn giải kết quả: Danh mục “Juices” (Nước ép) chiếm ưu thế tuyệt đối, nắm giữ 7 vị trí đầu tiên trong danh sách. Sản phẩm dẫn đầu là “HOHES C ORANGE” với 907,035 đơn vị bán ra. Danh mục “Water” (Nước) cũng có 3 sản phẩm lọt vào top 10 (“SAN PELLEGRINO”, “VITTEL”, “SELTERS”) nhưng ở các vị trí thấp hơn (8, 9, 10).

4.5 Phân tích theo danh mục

cat("\n5. PHÂN TÍCH THEO DANH MỤC:\n")
## 
## 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ả

  • Diễn giải kết quả: “Alcoholic Beverages” (Đồ uống có cồn) chiếm 77.5% tổng doanh thu (115M), cao vượt trội so với 3 danh mục còn lại. Ngược lại, “Water” (Nước) có Avg_Price (Giá trung bình) thấp nhất (0.965).

4.6 Phân tích theo mùa

cat("\n6. PHÂN TÍCH THEO MÙA:\n")
## 
## 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ả

  • Diễn giải kết quả: Doanh thu giữa 4 mùa rất đồng đều: “Summer” (37.8M), “Autumn” (36.9M), “Winter” (36.9M), “Spring” (36.9M). Sự chênh lệch là không đáng kể.

4.7 Phân tích theo tháng

cat("\n7. DOANH THU THEO THÁNG:\n")
## 
## 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ả

  • Diễn giải kết quả: Bảng này tóm tắt hiệu suất kinh doanh hàng tháng. Ví dụ, tháng 1 năm 2021 (dòng 1) có doanh thu là 4,126,548 và 10,025 đơn hàng

4.8 Phân tích theo phân khúc giá

cat("\n9. PHÂN TÍCH THEO PHÂN KHÚC GIÁ:\n")
## 
## 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.

4.9 Phân tích kích thước đơn hàng

cat("\n10. PHÂN TÍCH KÍCH THƯỚC ĐƠN HÀNG:\n")
## 
## 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ả

  • Diễn giải kết quả: Dữ liệu đã được sắp xếp, cho thấy ngay lập tức khu vực nào có doanh thu cao nhất (đứng đầu) và khu vực nào thấp nhất (đứng cuối). Cột Percentage làm rõ mức độ đóng góp tương đối của mỗi khu vực vào tổng doanh thu chung.

4.10 Phân tích chi tiết doanh thu theo danh mục

cat("\n11. PHÂN TÍCH CHI TIẾT DOANH THU THEO DANH MỤC:\n")
## 
## 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ả

  • Diễn giải kết quả: Kết quả cho thấy “Alcoholic Beverages” là danh mục áp đảo tuyệt đối, chiếm 77.5% tổng doanh thu (115 triệu).

4.11 Phân tích xu hướng doanh thu theo năm

cat("\n12. PHÂN TÍCH XU HƯỚNG DOANH THU THEO NĂM:\n")
## 
## 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ả

  • Diễn giải kết quả: Bảng kết quả cho thấy một xu hướng kinh doanh tích cực và bền vững. Doanh thu tăng trưởng đều đặn qua các năm, từ 47.9 triệu (2021) lên 50.8 triệu (2023).

4.12 Phân tích sâu top sản phẩm

cat("\n13. PHÂN TÍCH SÂU TOP SẢN PHẨM:\n")
## 
## 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ả

  • Diễn giải kết quả:Bảng kết quả (ảnh) cho thấy 15 sản phẩm có doanh thu cao nhất .Danh mục đồ uống có cồn chiếm ưu thế tuyệt đối trong top 15, với các thương hiệu như VEUVE CLICQUOT, MOËT & CHANDON và JOHNNIE WALKER dẫn đầu.

4.13 Phân bố đơn giá theo nhóm

# Phân bố đơn giá theo nhóm
cat("\nPhân bố đơn giá theo nhóm:\n")
## 
## 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ả

  • Diễn giải kết quả: Bảng kết quả cho thấy sự phân bố của các mặt hàng/đơn hàng theo 4 nhóm giá đã định nghĩa. Xu hướng chính: Các mặt hàng thuộc nhóm ‘Low Price’ (Giá thấp) chiếm đa số áp đảo, với 582,906 lượt, tương đương 55.6% tổng dữ liệu. Nhóm ‘Medium Price’ (Giá trung bình) đứng thứ hai với 31.5%. Các nhóm ‘Premium’ và ‘High’ chiếm tỷ trọng nhỏ hơn đáng kể.

4.14 Phân tích chi tiết theo mùa

cat("\n15. PHÂN TÍCH CHI TIẾT THEO MÙA:\n")
## 
## 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ả

  • Diễn giải kết quả: Bảng kết quả cho thấy sự khác biệt về hiệu quả kinh doanh giữa các mùa. Xu hướng chính: “Summer” (Mùa hè) là mùa hoạt động hiệu quả nhất, dẫn đầu rõ rệt về Tổng Doanh thu (khoảng 378 triệu) và Giá trị Đơn hàng Trung bình (430). Ba mùa còn lại (Autumn, Winter, Spring) có doanh thu tương đối đồng đều và thấp hơn đáng kể (cùng ở mức 369 triệu).

4.15 Phân tích tăng trưởng theo năm

cat("\n16. PHÂN TÍCH TĂNG TRƯỞNG THEO NĂM:\n")
## 
## 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ả

  • Diễn giải kết quả: Bảng kết quả cho thấy hiệu suất kinh doanh qua 3 năm. Doanh thu (Revenue) tăng trưởng liên tục (tăng 3.96% năm 2022 và 2.04% năm 2023).

4.16 Phân tích giá trị đơn hàng trung bình

cat("\n17. PHÂN TÍCH GIÁ TRỊ ĐƠN HÀNG:\n")
## 
## 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

4.17 Phân tích sản phẩm theo mức giá

cat("\n18. PHÂN TÍCH SẢN PHẨM THEO MỨC GIÁ:\n")
## 
## 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ả

  • Diễn giải kết quả: Bảng kết quả hiển thị 4 cột: Price_Category, Category, Product_Count (số sản phẩm) và Total_Revenue (tổng doanh thu). Dữ liệu được nhóm theo 4 Price_Category (mức giá) khác nhau.

4.18 Phân tích khách hàng theo tần suất mua hàng

cat("\n19. PHÂN TÍCH TẦN SUẤT MUA HÀNG CỦA KHÁCH HÀNG:\n")
## 
## 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ả

  • Diễn giải kết quả: Bảng kết quả chỉ hiển thị một hàng duy nhất là “Frequent”.Điều này có nghĩa là 100% khách hàng (10,000 người) trong bộ dữ liệu này đều thuộc nhóm “Frequent

4.19 Tương quan giữa đơn giá và số lượng bán

cat("\n20. TƯƠNG QUAN GIỮA ĐƠN GIÁ VÀ SỐ LƯỢNG BÁN:\n")
## 
## 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ả

  • Diễn giải kết quả: Kết quả -0.3883 cho thấy một mối tương quan nghịch (âm) ở mức độ trung bình yếu.

4.20 Top 10 khách hàng có giá trị đơn hàng cao nhất

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ả

  • Diễn giải kết quả: Bảng hiển thị 10 khách hàng hàng đầu, với khách hàng CUS3005 dẫn đầu (doanh thu 84,815). Tất cả 10 khách hàng này đều là khách hàng trung thành, với giao dịch kéo dài từ đầu năm 2021 đến cuối năm 2023.

5 TRỰC QUAN HÓA DỮ LIỆU

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()
  )

5.1 Biểu đồ Top 10 khách hàng có doanh thu cao nhất

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ả

  • Diễn giải kết quả: Biểu đồ thể hiện 10 khách hàng mang lại doanh thu cao nhất, xếp hạng từ CUS300 (cao nhất, khoảng 85K) đến CUS685 và CUS539 (thấp nhất trong top 10, cùng 71K).

5.2 Biểu đồ Doanh thu theo danh mục và mùa

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ả

  • Diễn giải kết quả: Có sự chênh lệch rất lớn giữa các danh mục. “Alcoholic Beverages” (Đồ uống có cồn) có doanh thu vượt trội (khoảng 28-29 triệu), cao hơn gấp nhiều lần so với các nhóm còn lại (Juices ~4.2 triệu, Soft Drinks ~2.6 triệu, Water ~1.5 triệu).

5.3 Biểu đồ Doanh thu theo danh mục và mùa

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ả

  • Diễn giải kết quả: Biểu đồ cho thấy một sự chênh lệch rất lớn về doanh thu. Danh mục “Alcoholic Beverages” (Đồ uống có cồn) chiếm ưu thế tuyệt đối, với doanh thu (khoảng 28 - 29 triệu) cao gấp nhiều lần so với ba danh mục còn lại cộng lại ở tất cả các mùa.

5.4 Biểu đồ Tỷ lệ đóng góp doanh thu theo danh mục

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ả

  • Diễn giải kết quả:Biểu đồ (Tỷ Lệ Đóng Góp Doanh Thu Theo Danh Mục) cho thấy sự chênh lệch rất lớn về đóng góp doanh thu giữa các danh mục.”Alcoholic Beverages” (Đồ uống có cồn) chiếm đa số áp đảo với 77.5% tổng doanh thu. Các danh mục còn lại đóng góp phần nhỏ hơn nhiều: “Juices” (Nước ép) đứng thứ hai (11.3%), tiếp theo là “Soft Drinks” (Nước ngọt) (7.1%) và “Water” (Nước) (4.1%).

5.5 Biểu đồ Doanh thu theo vùng

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ả

  • Diễn giải kết quả:Biểu đồ trực quan hóa doanh thu theo 16 vùng, được sắp xếp theo thứ tự tăng dần từ dưới lên trên. Xu hướng chính: Có sự chênh lệch rõ ràng về doanh thu giữa các vùng. Hamburg là vùng có doanh thu cao nhất (10423K), theo sau sát là Hessen (9951K). Bremen là vùng ghi nhận doanh thu thấp nhất trong danh sách (8197K).

5.6 Biểu đồ Doanh thu theo từng sản phẩm trong top 10

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ả

  • Diễn giải kết quả: Biểu đồ xếp hạng 10 sản phẩm mang lại doanh thu cao nhất. “VEUVE CLICQUOT” (26010K) và “MOËT & CHANDON” (21981K) là hai sản phẩm dẫn đầu, bỏ xa các sản phẩm còn lại.

5.7 Biểu đồ Tỷ lệ đơn hàng theo kích thước

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ả

  • Diễn giải kết quả:Biểu đồ cho thấy sự phân bổ số lượng đơn hàng theo ba nhóm kích thước. Xu hướng chính: Kích thước “Medium” (Trung bình) chiếm tỷ trọng lớn nhất một cách áp đảo với 194,255 đơn hàng. Nhóm “Large” (Lớn) đứng thứ hai với 117,343 đơn. Nhóm “Small” (Nhỏ) có số lượng đơn hàng ít nhất, chỉ 37,595 đơn.

5.8 Biểu đồ Doanh thu theo tháng

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.

5.9 Biểu đồ thể hiện doanh thu theo loại khách hàng và vùng

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ả

  • Diễn giải kết quả: Có một xu hướng rất rõ ràng: tại tất cả các vùng, doanh thu từ B2B (màu đỏ) đều vượt trội hoàn toàn so với doanh thu từ B2C (màu xanh). Doanh thu B2B thường cao gấp 3 đến 4 lần B2C.

5.10 Biểu đồ Top 10 sản phẩm bán chạy nhất

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ả

  • Diễn giải kết quả:HOHES C ORANGE là sản phẩm bán chạy nhất một cách rõ rệt, đạt 907,035 đơn vị. Có một nhóm bám đuổi sát sao gồm 6 sản phẩm (từ TOMATO JUICE đến GRANINI APPLE) với số lượng bán rất gần nhau, dao động trong khoảng hẹp từ 854K đến 865K. Ba sản phẩm cuối bảng (SAN PELLEGRINO, VITTEL, và SELTERS) tạo thành một nhóm riêng biệt với số lượng bán thấp hơn đáng kể (674K - 688K).

5.11 Biểu đồ Số lượng đơn hàng theo loại khách hàng

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ả

  • Diễn giải kết quả:Khối khách hàng B2C (224,846 đơn) có số lượng đơn hàng cao vượt trội, gần gấp đôi so với khối khách hàng B2B (124,347 đơn). Điều này cho thấy khách hàng cá nhân thực hiện nhiều giao dịch mua hàng riêng lẻ hơn so với khách hàng doanh nghiệp.

5.12 Biểu đồ Doanh thu theo tháng

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

5.13 Biểu đồ doanh thu theo danh mục

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).

5.14 Biểu đồ doanh thu theo mùa

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ả

  • Diễn giải kết quả:Biểu đồ “Doanh thu theo mùa” cho thấy một xu hướng rất rõ ràng: doanh thu cực kỳ ổn định và gần như không có sự khác biệt giữa các mùa. Không có “tính mùa vụ” (seasonality) rõ rệt. Cả bốn cột đều có chiều cao gần như bằng nhau, và đường kẻ (màu đỏ) gần như là một đường thẳng.

5.15 Biểu đồ Số lượng đơn hàng theo tháng

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ả

  • Diễn giải kết quả: Biểu đồ “Số lượng đơn hàng theo tháng” hiển thị số lượng đơn hàng cho 12 tháng, với mỗi năm (2021, 2022, 2023) được thể hiện trên một ô riêng biệt. Nhìn chung, số lượng đơn hàng rất ổn định qua cả 3 năm, không có sự tăng trưởng hay suy giảm đột biến. Hầu hết các tháng đều duy trì ở mức cao, dao động trong khoảng 9.000 đến 10.000 đơn.

5.16 Biểu đồ Doanh thu theo phân khúc giá

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:

      1. Nhãn tổng doanh thu (ví dụ: “93351K”) được đặt phía trên cột (vjust = -0.5).

      2. 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ả

  • Diễn giải kết quả: Biểu đồ cho thấy doanh thu được phân bổ không đều giữa các phân khúc giá. Phân khúc “Premium Price” (Giá Cao cấp) tạo ra 93,351K, chiếm phần lớn tổng doanh thu và vượt trội hoàn toàn so với ba phân khúc còn lại (Medium Price: 23,934K, High Price: 16,474K, và Low Price: 14,821K).

5.17 Biểu đồ Xu hướng doanh thu hàng ngày

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ả

  • Diễn giải kết quả: Đường xu hướng màu đỏ cho thấy rõ ràng doanh thu hàng ngày có xu hướng tăng trưởng đều đặn qua 3 năm (từ 2021 đến đầu 2024). Biểu đồ đường màu xanh (steelblue) cho thấy doanh thu hàng ngày có mức độ biến động (dao động) rất lớn. Có sự chênh lệch đáng kể giữa các ngày liền kề, với nhiều đỉnh nhọn và đáy sâu.

5.18 Biểu đồ Phân bố giá theo danh mục

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ả

  • Diễn giải kết quả: Danh mục “Alcoholic Beverages” (Đồ uống có cồn) có sự phân bố giá rất rộng, trải dài từ gần 0 đến hơn 150. Hàng nghìn điểm dữ liệu (chấm xanh) cho thấy sự đa dạng lớn về giá trong danh mục này .Ba danh mục còn lại (“Juices”, “Soft Drinks”, “Water”) có phân bố giá rất hẹp và tập trung ở mức rất thấp (gần như 0 - 5).

5.19 Biểu đồ Phân tích RFM của khách hàng

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).

5.20 Biểu đồ Tăng trưởng doanh thu theo năm

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ả

  • Diễn giải kết quả :Biểu đồ “Tăng Trưởng Doanh Thu Theo Năm” thể hiện doanh thu tăng đều qua các năm, từ 47,923K (2021) lên 49,821K (2022) và đạt 50,836K (2023). Doanh thu vẫn đang trên đà tăng trưởng.
library(tidyverse)
library(readxl)
library(DT)
library(scales)
library(kableExtra)
library(lubridate)

6 Phần 2: PHÂN TÍCH BỘ DỮ LIỆU CHỨNG KHOÁN (MÃ-STB)

7 GIỚI THIỆU BỘ DỮ LIỆU

7.1 Đọc dữ liệu từ file Excel

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ả

  • Diễn giải kết quả: Output hiển thị dòng chữ: “✅ Đã đọc dữ liệu thành công từ file Excel”.

7.2 Kiểm tra kích thước dataset

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ả

  • Diễn giải kết quả:Bảng kết quả cho thấy dữ liệu được sắp xếp với năm mới nhất (2024) ở trên cùng. Các chỉ số tài chính (ví dụ: tong_tai_san, loi_nhuan_sau_thue) đều là các con số rất lớn, thể hiện đơn vị tính có thể là triệu VNĐ.

7.3 Hiển thị cấu trúc dữ liệu

cat("CẤU TRÚC DỮ LIỆU:\n")
## CẤU TRÚC DỮ LIỆU:
glimpse(stb_data)
## 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ố ().

7.4 Hiển thị tên biến và ý nghĩa

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ừ điển biến số
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ả

  • Diễn giải kết quả: Bảng “Từ điển biến số” giúp người đọc hiểu rõ ý nghĩa của từng biến. Ví dụ: cho_vay_khach_hang nghĩa là “Cho vay khách hàng”, loi_nhuan_sau_thue là “Lợi nhuận sau thuế”.

7.5 Hiển thị dữ liệu raw dạng bảng

# 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)
Dữ liệu gốc từ file Excel
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ả

  • Diễn giải kết quả: Bảng trong ảnh hiển thị toàn bộ 10 năm dữ liệu. Các số tài chính rất lớn (ví dụ: tong_tai_san) đã được định dạng lại thành công (ví dụ: 851.011.026), giúp người xem đọc dữ liệu dễ dàng hơn nhiều so với dạng số thô.

7.6 Kiểm tra dữ liệu missing

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)
Thống kê dữ liệu missing
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ả

  • Diễn giải kết quả: Bảng “Thống kê dữ liệu missing” cho thấy cột “Số_lượng_NA” bằng 0 cho tất cả 20 biến. Điều này khẳng định bộ dữ liệu rất sạch và đầy đủ, không có giá trị nào bị thiếu.

7.7 Phân tích kiểu dữ liệu

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)
Kiểu dữ liệu của các biến
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ả

  • Diễn giải kết quả: Bảng trong ảnh hiển thị đúng tiêu đề “Kiểu dữ liệu của các biến” và liệt kê 20 cột.

7.8 Thống kê mô tả cơ bản

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)
Thống kê mô tả cơ bản
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.

7.9 Phân bố thời gian dữ liệu

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)
Số lượng quan sát theo năm
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ả

  • Diễn giải kết quả:Bảng cho thấy mỗi năm từ 2015 đến 2024 đều có Số_quan_sát là 1. Điều này xác nhận đây là bộ dữ liệu chuỗi thời gian thường niên (annual) đầy đủ, không bị khuyết năm nào trong giai đoạn 10 năm.

7.10 Phân tích xu hướng các chỉ số chính

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)
Xu hướng và tăng trưởng các chỉ số chính
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ả

  • Diễn giải kết quả: Bảng trong ảnh hiển thị 10 năm dữ liệu, bao gồm 5 cột dữ liệu gốc và 8 cột tính toán mới (tăng trưởng tuyệt đối và % cho 4 chỉ số).

8 XỬ LÝ DỮ LIỆU THÔ VÀ MÃ HÓA

8.1 Chuẩn hóa tên biến

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)
Kết quả chuẩn hóa tên biến
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

  • Tạo bảng so sánh tên cột cũ (names(stb_data)) và tên cột mới (names(stb_clean)).

📊 Phân tích Kết quả

  • Diễn giải kết quả:: Bảng “Kết quả chuẩn hóa tên biến” cho thấy cột “Tên.biến.gốc” và “Tên.biến.mới” giống hệt nhau. Điều này có nghĩa là dữ liệu gốc stb_data vốn đã có tên biến chuẩn (giống hệt danh sách tên mới).Thao tác đổi tên không làm thay đổi gì, mà chỉ xác nhận lại tên biến đã đúng định dạng.

8.2 Tách thông tin năm từ kỳ báo cáo

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)
Các năm trong dữ liệu
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ả

  • Diễn giải kết quả: Bảng “Các năm trong dữ liệu” là kết quả của lệnh kable, liệt kê một cột “Năm”.

8.3 Mã hóa biến phân loại giai đoạn phát triển

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)
Phân loại giai đoạn phát triển
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).

8.4 Xử lý dữ liệu missing (giả định tình huống)

# 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)
Xử lý dữ liệu missing
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ả

  • Diễn giải kết quả: Bảng “Xử lý dữ liệu missing” cho thấy rõ: “Thời điểm” = “Trước xử lý” có Số_NA = 2, và “Thời điểm” = “Sau xử lý” có Số_NA = 0.

8.5 Tạo biến tỷ lệ tài chính quan trọng

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)
Các chỉ số tài chính mới được tạo
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ả

  • Diễn giải kết quả: Bảng “Các chỉ số tài chính mới được tạo” liệt kê 5 biến mới (ROA, ROE, v.v.) và cung cấp “Mô tả” rõ ràng cho từng biến (ví dụ: “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”).

8.6 Chuẩn hóa dữ liệu số (Z-score)

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)
Mẫu giá trị Z-score của các biến (5 dòng đầu)
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ả

  • Diễn giải kết quả: Bảng “Mẫu giá trị Z-score” là kết quả của kable(z_score_sample). (Lưu ý: 5 dòng đầu này tương ứng với 5 năm mới nhất, 2020-2024).Dữ liệu cho thấy các năm gần đây (2022-2024) có các chỉ số (như tong_tai_san_z, ROE_z) với Z-score dương và cao (ví dụ: 1.63, 1.59). Điều này có nghĩa là hiệu suất của các năm này vượt trội đáng kể so với mức trung bình chung. Ngược lại, năm 2020-2021 có Z-score âm/gần 0, cho thấy hiệu suất dưới mức trung bình.

8.7 Mã hóa biến nhị phân cho hiệu quả kinh doanh

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)
Thống kê biến nhị phân
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).

8.8 Xử lý outliers bằng phương pháp IQR

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)
Thống kê các biến tỷ lệ sau khi xử lý outliers
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ả

  • Diễn giải kết quả: Quan sát Bảng “Thống kê các biến tỷ lệ sau khi xử lý outliers” là kết quả của lệnh kable(outlier_table), hiển thị Min, Max, Mean của 3 biến sau khi xử lý. Các con số trong bảng này đại diện cho phạm vi dữ liệu mới đã được “làm sạch”

8.9 Tạo biến tương tác cho phân tích nâng cao

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)
Mẫu giá trị biến tương tác (5 dòng đầu)
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ả

  • Diễn giải kết quả: Giá trị NA này là đúng logic. Nó xảy ra vì công thức lag(tong_tai_san) cần lấy dữ liệu của năm liền trước (previous row) để tính tốc độ tăng trưởng. Dựa trên bảng mẫu, 2024 là dòng đầu tiên, không có dữ liệu “lag” để so sánh, do đó phép tính trả về NA.

8.10 Lưu dataset đã xử lý

# 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)
Thống kê xử lý dữ liệu
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ả

  • Diễn giải kết quả:Bảng này xác nhận rằng các kiểu dữ liệu đã đúng như mong muốn cho phân tích: ky_bao_cao (biến phân loại) là factor. Tất cả các biến tài chính khác (như tong_tai_san, cho_vay_khach_hang…) đều là numeric (số), sẵn sàng cho việc tính toán và lập mô hình.

9 THỐNG KÊ CƠ BẢN

9.1 Thống kê mô tả chi tiết cho tất cả biến số

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)
Thống kê mô tả chi tiết
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).

9.2 Phân tích ma trận tương quan

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)
Hệ số tương quan giữa các biến
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.

9.3 Phân tích xu hướng tăng trưởng theo thời gian

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)
Tăng trưởng hàng năm (5 biến đầu tiên)
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ả

  • Diễn giải kết quả: Bảng “Tăng trưởng tuyệt đối và phần trăm hàng năm” cho thấy sự biến động theo từng năm ví dụ như loi_nhuan_sau_thue so với mỗi năm với nhau đều tăng trưởng đều và có xu hướng mạnh vào những năm 2023 và 2024.

9.4 So sánh hiệu quả hoạt động theo giai đoạn

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)
Chỉ số hiệu quả trung bình theo giai đoạn
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 .

9.5 Phân tích phân bố của ROA và ROE

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)
Thống kê phân bố ROA và ROE
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.

9.6 Phân tích tỷ lệ an toàn vốn

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)
Tỷ lệ vốn chủ sở hữu và xu hướng
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.

9.7 Phân tích hiệu quả sử dụng vốn

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)
Hiệu quả sử dụng vốn và kiểm soát chi phí
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.

9.8 Phân tích rủi ro tín dụ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)
Chỉ số rủi ro tín dụng và dự phòng
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ả

  • Diễn giải kết quả: Bảng kết quả hiển thị các chỉ số rủi ro tín dụng từ 2015 đến 2024. Cột Du_phong_binh_quan là một giá trị cố định (2.43M), dùng làm mốc so sánh chung. Tỷ lệ dự phòng (Ty_le_du_phong) có biến động lớn. Sau khi tăng lên mức cao nhất vào năm 2022 (1.2323), tỷ lệ này đã giảm mạnh trong hai năm gần nhất, xuống 0.6227 (2023) và 0.4058 (2024), cho thấy chi phí dự phòng đang chiếm tỷ trọng nhỏ hơn trong tổng dư nợ.

9.9 So sánh thu nhập lãi và phi lãi

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)
Tỷ trọng các loại thu nhập
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ả

  • Diễn giải kết quả: Bảng “Tỷ trọng các loại thu nhập” cho thấy một xu hướng rất rõ ràng là (2019-2024) Tỷ trọng thu nhập lãi thuần (Ty_le_thu_nhap_lai) đang tăng đều trở lại (từ 62.7% lên 76.1%), cho thấy sự tập trung quay về hoạt động kinh doanh cốt lõi.

9.10 Phân tích đòn bẩy tài chính

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)
Chỉ số đòn bẩy tài chính
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.

9.11 Phân tích xu hướng lãi cơ bản trên cổ phiếu

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)
EPS và tăng trưởng EPS
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).

9.12 Phân tích mối quan hệ giữa quy mô và hiệu quả

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)
Hệ số tương quan giữa quy mô và hiệu quả
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.

9.13 Phân tích biến động các chỉ số qua năm

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)
Độ lệch chuẩn của các chỉ số chính
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.

9.14 Phân tích tỷ lệ chi phí trên thu nhập

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)
Chỉ số chi phí trên thu nhập (C/I)
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.

9.15 Phân tích thanh khoản

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)
Chỉ số thanh khoản và cơ cấu tài sản
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.

9.16 Phân tích hiệu quả theo quy mô tài sả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)
So sánh hiệu quả theo quy mô
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).

9.17 Phân tích mối quan hệ giữa vốn và lợi nhuận

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)
Mối quan hệ giữa vốn và lợi nhuận
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.

9.18 Phân tích cấu trúc thu nhập

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)
Tỷ trọng các loại thu nhập
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ả

  • Diễn giải kết quả: Bảng “Tỷ trọng các loại thu nhập” (khi chỉ xét Lãi và Dịch vụ) cho thấy xu hướng chính: Thu nhập từ lãi (Ty_le_thu_nhap_lai) là nguồn thu chủ đạo, luôn chiếm đa số (thường trên 73%). Tỷ trọng này giảm sâu nhất vào năm 2017 (còn 66.79%) nhưng đã tăng trở lại và ổn định ở mức cao trong 2 năm gần nhất (khoảng 82%). Tỷ lệ thu nhập dịch vụ (Ty_le_thu_nhap_dich_vu) biến động ngược chiều tương ứng.

9.19 Phân tích hiệu quả sử dụng vốn điều lệ

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)
Chỉ số hiệu quả sử dụng vốn điều lệ
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.

9.20 Tổng kết các chỉ số quan trọng

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)
Giá trị bình quân các chỉ số chính
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ả

  • Diễn giải kết quả:: Bảng này cung cấp cái nhìn tổng quan về hiệu suất trung bình trong 10 năm. Nổi bật nhất là Tăng trưởng LN bình quân (41.88%), cho thấy tốc độ tăng trưởng lợi nhuận trung bình hàng năm rất ấn tượng. Các chỉ số sinh lời như ROA TB (0.58%) và ROE TB (10.72%) ở mức vừa phải. Tỷ lệ cho vay/Tiền gửi TB (75.24%) cho thấy ngân hàng duy trì mức thanh khoản tốt (không cho vay quá 100% tiền gửi).

10 TRỰC QUAN HÓA DỮ LIỆU

# 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")

10.1 Biểu đồ so sánh ROA và ROE

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.

10.2 Biểu đồ cơ cấu thu nhập (Stacked Area)

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%.

10.3 Biểu đồ phân tán giữa quy mô và hiệu quả

# 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.

10.4 Biểu đồ đường với confidence interval

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%.

10.5 Biểu đồ thể hiện mối quan hệ vốn và lợi nhuận

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):

      1. 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).

      2. 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.

10.6 Biểu đồ thể hiện hiệu quả hoạt động

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.

10.7 Biểu đồ thể hiện rủi ro tín dụ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:

      1. Du_phong_abs: Lấy giá trị tuyệt đối (abs) của chi phí dự phòng (do chi phí có thể được ghi nhận là số âm).
      2. Ty_le_du_phong: Tính tỷ lệ bằng cách lấy Du_phong_abs chia cho cho_vay_khach_hang (tổng dư nợ), sau đó nhân 100 để ra %.
    • Trực quan hóa (dùng ggplot2):

      1. geom_col: Vẽ biểu đồ cột cho Du_phong_abs (chia 1e3 để đổi đơn vị thành Tỷ VND).
      2. geom_line & geom_point: Vẽ biểu đồ đường cho Ty_le_du_phong.

📊 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.

10.8 Biểu đồ thể hiện EPS và tăng trưởng

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.

10.9 Biểu đồ thể hiện thanh khoản

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ả

  • Diễn giải kết quả: Biểu đồ cho thấy cả Tiền gửi khách hàng (phần màu cam) và Cho vay khách hàng (phần màu nâu) đều có xu hướng tăng trưởng liên tục và mạnh mẽ qua giai đoạn 2015-2024. Tuy nhiên, Tỷ lệ thanh khoản (đường màu xanh lá) lại có xu hướng giảm dần đều, từ 142.1% (năm 2015) xuống còn 123% (năm 2024). Điều này cho thấy tốc độ tăng trưởng cho vay đang nhanh hơn tốc độ tăng trưởng huy động tiền gửi.

10.10 Biểu đồ thể hiện xu hướng chi phí

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:

      1. 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 (%).

      2. 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 đồ:

      1. geom_col: Vẽ biểu đồ cột cho Thu nhập (xanh lá) và Chi phí (đỏ san hô).

      2. geom_line & geom_point: Vẽ biểu đồ đường (đỏ đậm) cho Tỷ lệ C/I.

      3. 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).

10.11 Biểu đồ thể hiện hiệu quả sử dụng vốn điều lệ

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.

10.12 Biểu đồ thể hiện mối quan hệ giữa rủi ro và lợi nhuận

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.

10.13 Biểu đồ thể hiện mối tương quan đa chiều

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).

10.14 Biểu đồ thể hiện phân tích biến động (Waterfall chart)

# 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ả

  • Diễn giải kết quả:Sự sụt giảm lợi nhuận tích lũy trong 3 năm liên tiếp (2021-2023) đã khiến tổng Lợi nhuận sau thuế cuối năm 2023 trở về mức rất thấp, gần bằng mức khởi điểm năm 2015, báo hiệu một vấn đề lớn về hiệu quả kinh doanh

10.15 Biểu đồ phân tích hiệu quả sử dụng vốn huy động

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ả

  • Diễn giải kết quả: Biểu đồ cho thấy Hiệu suất sử dụng vốn (miền xanh lá) của ngân hàng duy trì ở mức rất cao và ổn định, luôn dao động trong khoảng 75% - 83%. Điều này có nghĩa là phần lớn tài sản (khoảng 80%) được dùng cho các hoạt động sinh lời như cho vay và đầu tư. Ngược lại, Tỷ lệ dự trữ (miền xanh dương) chiếm phần nhỏ còn lại (khoảng 20%).

10.16 Biểu đồ phân tích dòng tiền hoạt động

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ỳ.

10.17 Biểu đồ phân tích tương quan động theo thời gian

# 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:

      1. 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).

      2. 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.

10.18 Biểu đồ tròn thể hiện cơ cấu tài sản năm 2024

# 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%.

10.19 Biểu đồ thể hiện xu hướng tích lũy giá trị

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ả

  • Diễn giải kết quả: Biểu đồ cho thấy cả Lợi nhuận sau thuế tích lũy (vùng xanh) và Vốn CSH tích lũy (vùng xanh dương) đều tăng trưởng liên tục qua các năm. Đến cuối giai đoạn, tổng lợi nhuận tích lũy (32,865B) đã tăng tốc mạnh mẽ và vượt đáng kể tổng vốn tích lũy.Tỷ suất sinh lời tích lũy (đường đỏ) bắt đầu ở mức cực kỳ cao (trên 600%) khi nền vốn ban đầu còn nhỏ. Sau đó, tỷ suất này sụt giảm nhanh chóng trong giai đoạn 2015-2017 khi vốn được tích lũy nhanh hơn lợi nhuận.

10.20 Biểu đồ thể hiện mối quan hệ giữa thanh khoản và rủi ro

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%).