LỜI CẢM ƠN

Tôi xin gửi lời cảm ơn chân thành đến Thầy Trần Mạnh Tường, người đã tận tình hướng dẫn, hỗ trợ và đóng góp những ý kiến quý báu trong suốt quá trình em thực hiện bài tiểu luận này. Sự chỉ dạy của thầy không chỉ giúp tôi nắm vững kiến thức mà còn rèn luyện tư duy nghiên cứu và phương pháp làm việc khoa học.

Để có thể hoàn thiện bài báo cáo một cách tốt nhất cả về nội dung lẫn hình thức trình bày, bên cạnh sự nổ lực của bản thân, tôi luôn nhận được sự quan tâm, hướng dẫn tận tình từ thầy Tường. Nhờ có sự đóng góp, chia sẻ từ thầy mà tôi có thể hoàn thiện bài báo cáo một cách tốt nhất. Tuy nhiên, dù đã cố gắng hết sức nhưng bài báo cáo sẽ khó tránh khỏi những sai sót và tồn đọng nhiều hạn chế. Chính vì vậy, tôi mong nhận được ý kiến đóng góp từ thầy để bài báo cáo có thể hoàn thiện hơn.

Lời cuối cùng, tôi kính chúc thầy hướng dẫn sẽ có thật nhiều sức khỏe, hạnh phúc và thành công trên con đường sự nghiệp.

LỜI CAM ĐOAN

Tôi xin cam đoan rằng bài tiểu luận này là kết quả nghiên cứu độc lập của cá nhân tôi, được thực hiện dưới sự hướng dẫn của Thầy Trần Mạnh Tường.

Toàn bộ nội dung trong bài được xây dựng dựa trên các tài liệu tham khảo có chọn lọc, được trích dẫn đầy đủ và tuân thủ đúng các quy định về học thuật. Tôi không sao chép hay sử dụng bất kỳ nội dung nào vi phạm nguyên tắc trung thực trong nghiên cứu.

Nếu có bất kỳ sai sót hoặc vi phạm nào liên quan đến tính chính xác và tính trung thực của bài tiểu luận, tôi xin hoàn toàn chịu trách nhiệm trước thầy và nhà trường.

PHẦN 1: PHÂN TÍCH BỘ DỮ LIỆU VỀ PHIM

CHƯƠNG 1: GIỚI THIỆU VỀ DỮ LIỆU

1.1 Giới thiệu tổng quan

Tên bộ dữ liệu: Comprehensive Films Dataset for Analysis.

Nguồn gốc bộ dữ liệu:https://www.kaggle.com/datasets/emanfatima2025/comprehensive-films-dataset-for-analysis/data

Thông tin bộ dữ liệu:là một tập hợp dữ liệu chi tiết về phim ảnh, bao gồm cả đặc điểm định tính và định lượng. Mỗi hồ sơ phản ánh một bộ phim riêng biệt và cung cấp thông tin về danh tính, thể loại, ngày phát hành, quốc gia sản xuất, hiệu suất tài chính, phản ứng của khán giả và các hãng phim lớn.

Mục đích lấy dữ liệu: Dữ liệu được sử dụng để thực hiện các thao tác phân tích, xử lý, trực quan hóa trong tiểu luận môn học.

Nhóm sử dụng bộ dữ liệu này nhằm:

Thực hành kỹ năng phân tích, xử lý dữ liệu thực tế bằng ngôn ngữ R.

Khám phá các đặc trưng của ngành công nghiệp điện ảnh dưới góc nhìn dữ liệu.

Áp dụng thống kê mô tả, kỹ thuật tiền xử lý, lập bảng, vẽ biểu đồ và rút ra nhận xét và thông điệp.

1.2 Thông tin cơ bản về bộ dữ liệu

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

movies_dataset <- read.csv("D:/movies_dataset.csv")

Giải thích: Sử dụng hàm read.csv để đọc file dữ liệu định dạng CSV và lưu vào biến movies_dataset dưới dạng data frame.

dim(movies_dataset)
## [1] 999999     17

Giải thích: Gọi hàm dim() để kiểm tra số dòng (quan sát) và số cột (biến) của movies_dataset.

1.2.2 Tên các biến của bộ dữ liệu

names(movies_dataset)
##  [1] "MovieID"              "Title"                "Genre"               
##  [4] "ReleaseYear"          "ReleaseDate"          "Country"             
##  [7] "BudgetUSD"            "US_BoxOfficeUSD"      "Global_BoxOfficeUSD" 
## [10] "Opening_Day_SalesUSD" "One_Week_SalesUSD"    "IMDbRating"          
## [13] "RottenTomatoesScore"  "NumVotesIMDb"         "NumVotesRT"          
## [16] "Director"             "LeadActor"

Giải thích: Lệnh này xuất ra tên tất cả các biến (cột) trong data frame, hỗ trợ xác định biến sẽ sử dụng cho từng bước phân tích.

1.2.3 Kiểm tra qua các dòng đầu tiên của bộ dữ liệu

kable(t(head(movies_dataset, 4)), booktabs = TRUE) %>%
  kable_styling(latex_options = c("scale_down"))
1 2 3 4
MovieID 1 2 3 4
Title Might toward capital He however experience Star responsibility politics Exactly live
Genre Comedy Comedy Comedy Comedy
ReleaseYear 2003 1988 1971 1998
ReleaseDate 28-09-2003 14-02-1988 2/11/1971 6/8/1998
Country China USA USA USA
BudgetUSD 6577428 1883810 2468079 1447311
US_BoxOfficeUSD 6613686 1930949 4186695 2023684
Global_BoxOfficeUSD 15472036 3637731 7165111 4373820
Opening_Day_SalesUSD 1778530.9 247115.7 878453.9 570657.7
One_Week_SalesUSD 3034053.3 831828.8 2171405.9 898886.0
IMDbRating 6.2 5.2 5.5 7.3
RottenTomatoesScore 58 44 55 87
NumVotesIMDb 7865 1708 4678 2467
NumVotesRT 10596 220 7805 1751
Director Kristina Moore Benjamin Hudson Kayla Young Michael Ross
LeadActor Brian Mccormick Ashley Pena Alexander Haley Patrick Barnett

Giải thích:

Hàm head() lấy 4 dòng đầu tiên của dữ liệu rồi xoay ngang để mỗi biến thành một dòng, giúp xem nhanh cấu trúc và kiểu dữ liệu từng cột.

Dùng hàm kable xuất bảng đẹp, kable_styling(scale_down) co bảng vừa trang, không bị lỗi chữ khi in PDF.

Nhận xét:

Cách trình bày này dễ kiểm tra cấu trúc ban đầu, giúp người xem nhìn rõ tên biến, kiểu và các giá trị mẫu.

Sử dụng transpose kết hợp kable và kableExtra cho báo cáo rất trực quan, phù hợp chuẩn học thuật và tránh bảng dài bị dính chữ, rối mắt.

1.2.4 Kiểm tra qua các dòng cuối của bộ dữ liệu

kable(t(tail(movies_dataset, 4)), booktabs = TRUE) %>%
  kable_styling(latex_options = c("scale_down"))
999996 999997 999998 999999
MovieID 999996 999997 999998 999999
Title Single yourself sister collection Old economic My itself and leave Congress federal policy
Genre Horror Documentary Horror Drama
ReleaseYear 1982 1993 2013 1995
ReleaseDate 27-08-1982 12/2/1993 11/11/2013 9/8/1995
Country USA USA India USA
BudgetUSD 657167.2 29914685.1 1382498.6 7655641.8
US_BoxOfficeUSD 1046950 30619500 3168115 18626549
Global_BoxOfficeUSD 2164587 57774788 5567631 34908325
Opening_Day_SalesUSD 119899.3 6331667.6 438945.4 4342391.5
One_Week_SalesUSD 564950.7 18136941.9 1703606.0 10764342.0
IMDbRating 7.3 4.1 4.3 6.6
RottenTomatoesScore 83 32 23 60
NumVotesIMDb 622 100 2337 24528
NumVotesRT 2327 1394 105 872
Director David Lee Jeremy Davis Melanie Olson Albert Phillips
LeadActor Tracy Klein Lynn Pierce Melissa Mcgee Samantha Sanders

Giải thích:

Hàm tail() lấy 4 dòng cuối cùng của bộ dữ liệu.

t() giúp xoay ngang bảng, cho từng biến thành một dòng để dễ quan sát.

kable xuất bảng đẹp; kable_styling(scale_down) tự động co bảng vừa trang PDF.

Nhận xét:

Giúp kiểm tra nhanh giá trị, kiểu dữ liệu ở các dòng cuối để phát hiện bất thường trong tập dữ liệu.

Bố cục bảng chuẩn, không bị tràn chữ - rất thuận tiện kiểm soát dữ liệu.

1.2.5 Số lượng biến định tính và định lượng

sum(sapply(movies_dataset, is.numeric))
## [1] 11

Giải thích: Áp dụng hàm is.numeric cho từng biến trong movies_dataset bằng sapply, trả về vector TRUE/FALSE. Sử dụng sum để đếm số biến kiểu số, cho ra tổng số biến định lượng.

Nhận xét: Số biến định lượng là 11, thường là các trường về doanh thu, điểm đánh giá, ngân sách,… phù hợp cho phân tích sâu hơn với các phép đo và mô hình thống kê.

sum(sapply(movies_dataset, is.character) | sapply(movies_dataset, is.factor))
## [1] 6

Giải thích: Kiểm tra từng biến trong bảng là kiểu character hoặc factor, phép OR giúp xác định biến định tính. Hàm sum cho ra tổng số biến định tính.

Nhận xét: Tổng số biến định tính là 6, bao gồm các biến phân loại hoặc thuộc tính mô tả (ví dụ: thể loại phim, quốc gia, đạo diễn, diễn viên). Kiểm tra này giúp tôi nhận diện rõ các biến phù hợp cho phân tích nhóm, đối chiếu hoặc trực quan hóa theo từng nhóm đặc trưng.

1.2.6 Kiểu dữ liệu của các biến trong bộ dữ liệu

loai <- data.frame(
Kieu_du_lieu = sapply(movies_dataset, class)
)
kable(loai, col.names = c("Tên biến" , "Kiểu dữ liệu"))
Tên biến Kiểu dữ liệu
MovieID integer
Title character
Genre character
ReleaseYear integer
ReleaseDate character
Country character
BudgetUSD numeric
US_BoxOfficeUSD numeric
Global_BoxOfficeUSD numeric
Opening_Day_SalesUSD numeric
One_Week_SalesUSD numeric
IMDbRating numeric
RottenTomatoesScore integer
NumVotesIMDb integer
NumVotesRT integer
Director character
LeadActor character

Giải thích:

Hàm sapply(movies_dataset, class) áp dụng hàm class cho từng biến trong data frame, trả về vector loại dữ liệu của tất cả các biến.

Kết quả tập hợp thành bảng hai cột: tên biến và kiểu dữ liệu.

Hàm kable() trình bày bảng dưới dạng trực quan, rõ ràng, chuyên nghiệp, hỗ trợ kiểm tra dữ liệu đầu vào.

Nhận xét:

Kết quả kiểm tra cho thấy bộ dữ liệu kết hợp giữa nhiều loại biến (số, chuỗi ký tự, dạng phân loại).

Xác định rõ kiểu dữ liệu từng biến nhằm thiết kế phương án xử lý, phân tích, trực quan hóa phù hợp.

Phân biệt biến định lượng và định tính giúp nâng cao độ chính xác khi lựa chọn công cụ, phương pháp phân tích thống kê.

1.2.7 Tên các thể loại của phim trong biến Genre

unique(movies_dataset$Genre)
## [1] "Comedy"      "Documentary" "Drama"       "Horror"      "Action"     
## [6] "Thriller"    "Romance"     "Sci-Fi"

Giải thích: Hàm unique() liệt kê tất cả các giá trị khác nhau (không lặp lại) xuất hiện trong biến Genre.

Điều này giúp xác định bộ dữ liệu gồm những thể loại phim nào, rất hữu ích khi phân tích đa dạng thể loại hoặc chuẩn bị cho việc vẽ biểu đồ và nhóm dữ liệu theo thể loại.

1.2.8 Đếm tần suất giá trị duy nhất của biến Genre

length(unique(movies_dataset$Genre))
## [1] 8

Giải thích: Hàm này kết hợp unique() (lấy giá trị không lặp lại) và length() (đếm số lượng), cho ra số lượng thể loại phim khác biệt có mặt trong dữ liệu.

Nhờ đó ta biết được độ đa dạng của biến này, qua đó đánh giá mức độ phân chia khi phân tích nhóm.

1.2.9 Tần suất các thể loại phim trong biến Genre

library(knitr)
kable(table(movies_dataset$Genre),col.names = c("Thể loại", "Tần suất"))
Thể loại Tần suất
Action 150131
Comedy 199832
Documentary 50114
Drama 250018
Horror 100010
Romance 100021
Sci-Fi 49802
Thriller 100071

Giải thích:

Đầu tiên, hàm table(movies_dataset$Genre) đếm số lần xuất hiện (tần suất) của từng giá trị trong cột Genre – tức là đếm số phim thuộc mỗi thể loại.

Kết quả là một bảng tần suất; sau đó, kable() xuất bảng này thành định dạng đẹp, có hai cột “Thể loại” và “Tần suất”.

Nhận xét: Thống kê tần suất thể loại giúp mô tả tổng quan đặc điểm biến phân loại trong bộ dữ liệu, đồng thời xác định các nhóm thể loại nổi bật. Việc này là bước tiền đề cho các phân tích thống kê sâu hơn, hỗ trợ việc trực quan hóa hoặc đánh giá độ đa dạng về thể loại của tập phim nghiên cứu.

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

variable_meaning <- data.frame(
  variable = c("MovieID", "Title", "Genre", "ReleaseYear", "ReleaseDate", "Country",
               "BudgetUSD", "US_BoxOfficeUSD", "Global_BoxOfficeUSD",
               "Opening_Day_SalesUSD", "One_Week_SalesUSD", "IMDbRating",
               "RottenTomatoesScore", "NumVotesIMDb", "NumVotesRT",
               "Director", "LeadActor", "ProfitUSD", "Sales_Difference","Thapky"),
  Meaning = c(
    "Mã định danh duy nhất cho mỗi phim",
    "Tên hoặc tựa đề của phim",
    "Thể loại phim (Ví dụ: Hài, Tài liệu, Kinh dị...)",
    "Năm phim được phát hành",
    "Ngày chính xác phim được phát hành",
    "Quốc gia sản xuất phim",
    "Ngân sách đầu tư cho phim (đơn vị USD)",
    "Doanh thu phòng vé tại Mỹ (đơn vị USD)",
    "Doanh thu phòng vé toàn cầu (đơn vị USD)",
    "Doanh thu ngày đầu tiên phim ra mắt (đơn vị USD)",
    "Doanh thu tuần đầu tiên phim ra mắt (đơn vị USD)",
    "Điểm đánh giá phim trên IMDb (thang điểm 10)",
    "Điểm đánh giá phim trên Rotten Tomatoes (thang điểm 100)",
    "Số lượng lượt người đánh giá trên IMDb",
    "Số lượng lượt người đánh giá trên Rotten Tomatoes",
    "Tên đạo diễn của bộ phim",
    "Tên diễn viên chính trong phim",
    "Lợi nhuận phòng vé (đơn vị USD)",
    "Khoản doanh thu trung bình của 6 ngày từ ngày đầu tiên (đơn vị USD)",
    "Thập kỷ"
  ),
  stringsAsFactors = FALSE
)
library(knitr)
kable(variable_meaning, col.names = c("Tên biến", "Ý nghĩa"))
Tên biến Ý nghĩa
MovieID Mã định danh duy nhất cho mỗi phim
Title Tên hoặc tựa đề của phim
Genre Thể loại phim (Ví dụ: Hài, Tài liệu, Kinh dị…)
ReleaseYear Năm phim được phát hành
ReleaseDate Ngày chính xác phim được phát hành
Country Quốc gia sản xuất phim
BudgetUSD Ngân sách đầu tư cho phim (đơn vị USD)
US_BoxOfficeUSD Doanh thu phòng vé tại Mỹ (đơn vị USD)
Global_BoxOfficeUSD Doanh thu phòng vé toàn cầu (đơn vị USD)
Opening_Day_SalesUSD Doanh thu ngày đầu tiên phim ra mắt (đơn vị USD)
One_Week_SalesUSD Doanh thu tuần đầu tiên phim ra mắt (đơn vị USD)
IMDbRating Điểm đánh giá phim trên IMDb (thang điểm 10)
RottenTomatoesScore Điểm đánh giá phim trên Rotten Tomatoes (thang điểm 100)
NumVotesIMDb Số lượng lượt người đánh giá trên IMDb
NumVotesRT Số lượng lượt người đánh giá trên Rotten Tomatoes
Director Tên đạo diễn của bộ phim
LeadActor Tên diễn viên chính trong phim
ProfitUSD Lợi nhuận phòng vé (đơn vị USD)
Sales_Difference Khoản doanh thu trung bình của 6 ngày từ ngày đầu tiên (đơn vị USD)
Thapky Thập kỷ

Giải thích:Đoạn code này tạo một bảng giải thích ý nghĩa cho từng biến có trong bộ dữ liệu phim. Đầu tiên, data.frame được dùng để tạo một bảng với hai cột: Variable (tên biến) và Meaning cho biết ý nghĩa chi tiết, giải thích chức năng và chủ đề từng biến. Việc liệt kê này giúp tôi nhanh chóng nắm được mỗi trường trong dữ liệu nói về điều gì.

CHƯƠNG 2: XỬ LÝ THÔ VÀ MÃ HÓA BỘ DỮ LIỆU

2.1 Kiểm tra quan sát bị trùng lặp và giá trị bị thiếu (NA)

sum(duplicated(movies_dataset))
## [1] 0
any(is.na(movies_dataset))
## [1] FALSE

Giải thích:sum(duplicated()) để đếm số lượng bản ghi trùng lặp trong tập dữ liệu movies_dataset và any(is.na()) để kiểm tra xem trong dữ liệu có giá trị bị thiếu (NA) hay không, trả về TRUE nếu có ít nhất một giá trị bị thiếu.

Nhận xét: Kết quả trả về 0 nghĩa là không có dòng dữ liệu nào bị trùng lặp hoàn toàn trong movies_dataset và kết quả FALSE cho thấy toàn bộ dataset không có giá trị bị thiếu (NA).

2.2 Chuyển đổi kiểu dữ liệu cho biến Releasedate

movies_dataset$ReleaseDate <- parse_date_time(movies_dataset$ReleaseDate, 
                                              orders = c("dmy", "mdy", "ymd"))

Giải thích: Đoạn mã sử dụng hàm parse_date_time() của package lubridate để chuyển đổi giá trị ngày phát hành phim (ReleaseDate) từ các định dạng ký tự khác nhau về chuẩn ngày tháng của R. Tham số orders cho phép tự động nhận biết nhiều định dạng ngày trong dữ liệu.

Nhận xét: Việc chuyển đổi dữ liệu ngày về định dạng chuẩn giúp đảm bảo tính nhất quán cho các thao tác xử lý, phân tích hoặc trực quan hóa thời gian trong các bước tiếp theo.

2.3 Chuyển đổi kiểu dữ liệu biến ReleaseDate thành Date

movies_dataset$ReleaseDate <- as.Date(movies_dataset$ReleaseDate)

Giải thích: Hàm as.Date() chuyển các chuỗi ký tự biểu diễn ngày trong biến ReleaseDate về kiểu ngày tháng chuẩn của R để thao tác thuận tiện.

Nhận xét: Việc chuyển đổi này là thiết yếu cho các phép toán, trực quan hóa hoặc phân tích theo thời gian. Bước chuẩn hóa giúp đảm bảo tính chính xác và đồng nhất dữ liệu ngày tháng.

2.4 Mã hóa điểm IMDbRating về thang điểm 100

movies_dataset$IMDbRating_100 <- movies_dataset$IMDbRating * 10

Giải thích: Đoạn mã tạo biến mới IMDbRating_100 bằng cách nhân điểm IMDbRating (thang 0–10) với 10. Mục đích là chuẩn hóa về cùng thang điểm 100 để so sánh, đối chiếu với các biến khác như RottenTomatoesScore.

Nhận xét: Chuẩn hóa điểm số về cùng đơn vị đo giúp dễ dàng tổng hợp, trực quan hóa và phân tích tương quan giữa các chỉ số đánh giá khác nhau.

2.5 Tạo biến thập kỷ

movies_dataset$ThapKy <- paste0(floor(movies_dataset$ReleaseYear / 10) * 10, "s")

Giải thích: Đoạn mã chia năm phát hành phim cho 10, hàm floor làm tròn xuống rồi nhân lại với 10, sau đó thêm hậu tố “s” để tạo nhãn thập kỷ (ví dụ: “1990s”).

Nhận xét: Biến ThapKy giúp nhóm các bộ phim theo thập kỷ phát hành, làm rõ xu hướng biến đổi theo thời gian, thuận tiện cho các phân tích theo giai đoạn lịch sử.

2.6 Mã hóa tên quốc gia theo châu lục

library(countrycode)
movies_dataset$Continent <- countrycode(movies_dataset$Country,
                                        origin = 'country.name',
                                        destination = 'continent')

Giải thích: Sử dụng thư viện countrycode với hàm countrycode(), chuyển đổi cột Country từ tên quốc gia sang tên châu lục tương ứng trong dữ liệu.

Nhận xét: Việc mã hóa này tạo điều kiện tổng hợp, so sánh, phân tích dữ liệu theo nhóm khu vực lãnh thổ, phù hợp với các nghiên cứu mang tính khu vực hoặc toàn cầu.

2.7 Tạo biến lợi nhuận ròng

movies_dataset$ProfitUSD <- movies_dataset$Global_BoxOfficeUSD - movies_dataset$BudgetUSD

Giải thích: Lợi nhuận ròng được tính bằng doanh thu phòng vé toàn cầu (Global_BoxOfficeUSD) trừ đi chi phí sản xuất (BudgetUSD).

Nhận xét: Biến lợi nhuận ròng là chỉ số tài chính quan trọng, cho phép so sánh hiệu quả kinh doanh giữa các phim và phục vụ cho các phân tích tài chính trong nghiên cứu.

2.8 Tạo biến doanh thu trung bình sau ngày công chiếu đầu tiên

movies_dataset$Doanhthutb <- (movies_dataset$One_Week_SalesUSD - movies_dataset$Opening_Day_SalesUSD)/6

Giải thích: Biến Doanhthutb được tạo để tính mức doanh thu trung bình mỗi ngày trong 6 ngày tiếp theo sau ngày công chiếu đầu tiên, bằng công thức: lấy tổng doanh thu 7 ngày đầu, trừ đi doanh thu ngày đầu, rồi chia cho 6.

Nhận xét: Biến này loại trừ ảnh hưởng doanh thu đột biến ở ngày đầu, phản ánh chính xác hiệu suất duy trì doanh thu của phim trong tuần lễ đầu tiên, hỗ trợ phân tích độ ổn định sức hút phòng vé của bộ phim.

2.9 Trích xuất ngày và tháng

movies_dataset$ReleaseDay <- format(movies_dataset$ReleaseDate, "%d")
movies_dataset$Releasemonth <- format(movies_dataset$ReleaseDate, "%m")

Giải thích: Hai dòng mã sử dụng hàm format() để tách ngày (%d) và tháng (%m) từ biến ReleaseDate đã chuẩn hóa, lưu thành hai biến mới ReleaseDay (ngày) và Releasemonth (tháng) dưới dạng chuỗi ký tự.

Nhận xét: Việc trích xuất ngày và tháng cho phép phân tích, nhóm, hoặc trực quan hóa dữ liệu theo mùa vụ, chu kỳ ra mắt phim, hay xu hướng phát hành theo thời gian trong năm.

2.10 Tạo ra biến Holiday

movies_dataset$Holiday <- ifelse(
  movies_dataset$Releasemonth == "12" & movies_dataset$ReleaseDay == "25", "Giáng sinh",
ifelse(
  movies_dataset$Releasemonth == "01" & movies_dataset$ReleaseDay == "01", "New Year",
ifelse(
  movies_dataset$Releasemonth == "02" & movies_dataset$ReleaseDay == "14", "Valentine",
ifelse(
  movies_dataset$Releasemonth == "10" & movies_dataset$ReleaseDay == "31", "Halloween",
ifelse(
  movies_dataset$Releasemonth == "03" & movies_dataset$ReleaseDay == "08", "Quốc tế phụ nữ",
ifelse(
  movies_dataset$Releasemonth == "06" & movies_dataset$ReleaseDay == "01", "Quốc tế thiếu nhi",
ifelse(
  movies_dataset$Releasemonth == "07" & movies_dataset$ReleaseDay == "04", "Quốc khánh Mỹ",
ifelse(
  movies_dataset$Releasemonth == "11" & (movies_dataset$ReleaseDay >= "22" 
                                         & movies_dataset$ReleaseDay <="28")& weekdays(movies_dataset$ReleaseDate) == "Thursday", "Lễ Tạ ơn (Thanksgiving)","Không có"
))))))))

Giải thích: Đoạn code từ dòng 1 đến dòng 17 sử dụng chuỗi các lệnh ifelse để xác định ngày phát hành phim có rơi vào các dịp lễ lớn không. Mỗi điều kiện so sánh giá trị trường tháng (Releasemonth) và ngày (ReleaseDay) với các ngày lễ phổ biến (Giáng sinh, Năm mới, Valentine, Halloween, Quốc tế phụ nữ, Quốc tế thiếu nhi, Quốc khánh Mỹ, Lễ Tạ ơn).

Nếu ngày phát hành là 25/12 thì gán là “Giáng sinh”. Nếu là 01/01 thì gán “New Year”. Các điều kiện khác tương tự với các dịp lễ như Valentine (14/2), Halloween (31/10), Quốc tế phụ nữ (8/3), Quốc tế thiếu nhi (1/6), Quốc khánh Mỹ (4/7), Lễ Tạ ơn (thứ Năm từ 22-28/11). Nếu không rơi vào dịp nào trên, gán giá trị “Không có”.

Nhận xét: Việc mã hóa các ngày lễ thành một biến rời giúp phân tích sự ảnh hưởng của dịp lễ lên doanh thu phim; cho phép so sánh hiệu suất phát hành mùa lễ với ngày bình thường, góp phần kiểm định các giả thuyết về sự tác động của yếu tố thời điểm tới thành công phòng vé.

2.11 Tạo ra biến tỷ suất lợi nhuận

movies_dataset$ROI <- (movies_dataset$Global_BoxOfficeUSD - movies_dataset$BudgetUSD) / movies_dataset$BudgetUSD

Giải thích: Dòng 1 lệnh trên tạo một biến mới có tên là ROI trong bộ dữ liệu movies_dataset. Global_BoxOfficeUSD là tổng doanh thu toàn cầu; BudgetUSD là chi phí sản xuất phim. Lợi nhuận ròng (doanh thu trừ chi phí) được chia cho chi phí để ra tỷ suất lợi nhuận trên từng USD đầu tư. Kết quả phản ánh mỗi 1 USD sản xuất có thể mang lại bao nhiêu USD lợi nhuận.

Nhận xét: ROI là chỉ số tài chính then chốt để đánh giá mức sinh lời của phim; ROI > 0 cho thấy phim có lãi, ROI < 0 là phim lỗ. Những phim kinh phí nhỏ nhưng ROI cao có thể xem là “phim lời lớn”. Chỉ số này đặc biệt hữu ích trong so sánh hiệu quả đầu tư giữa các phim với quy mô ngân sách khác.

2.12 Tạo biến Tỷ lệ mở màn của ngày đầu tiên trong 1 tuần

movies_dataset$Opening_Ratio <- 
  movies_dataset$Opening_Day_SalesUSD / movies_dataset$One_Week_SalesUSD

Giải thích: Dòng 1 lệnh khởi tạo biến Opening_Ratio bằng cách lấy doanh thu ngày đầu (Opening_Day_SalesUSD) chia cho doanh thu tuần đầu tiên (One_Week_SalesUSD).

Nhận xét: Opening_Ratio giúp đánh giá mức độ “nổ phát súng” của phim ở ngày mở màn so với cả tuần công chiếu. Biến này hữu ích cho phân tích hiệu ứng truyền thông, sức hấp dẫn của phim trước khi ra rạp, hoặc dự báo tiềm năng doanh thu dài hạn từ mức mở màn ban đầu.

2.13 Tạo ra biến độ chênh lệch giữa bình chọn nhà phê bình và khán giả

movies_dataset$Tomatoes_IMDb <- 
  movies_dataset$RottenTomatoesScore - movies_dataset$IMDbRating_100

Giải thích: Biến Tomatoes_IMDb (dòng 1–2) được tạo bằng phép trừ: lấy điểm của giới phê bình trên Rotten Tomatoes (thang 100) trừ đi điểm khán giả trên IMDb (quy chuẩn về cùng thang 100). Biến này thể hiện mức chênh lệch đánh giá giữa nhà phê bình chuyên môn và khán giả đại chúng về cùng một bộ phim.

Nhận xét: Nếu giá trị dương, phim được nhà phê bình đánh giá cao hơn khán giả. Nếu giá trị âm, phim hấp dẫn với số đông khán giả đại chúng nhưng chưa thuyết phục giới chuyên môn. Nếu gần bằng 0, phim cân bằng giữa nghệ thuật và thương mại. Biến này hữu ích để tìm hiểu độ phân hóa thị hiếu, nghiên cứu sự đồng thuận hay khác biệt về đánh giá giữa các nhóm khán giả và giúp chọn hướng tiếp cận khi sản xuất phim

CHƯƠNG 3: THỰC HIỆN CÁC THỐNG KÊ CƠ BẢN

3.1 Phân tích thống kê mô tả của các biến

Giải thích:

Các hàm summary(), sd(), var() dùng để phân tích thống kê mô tả cho biến số học trong R.

summary() cho các giá trị tóm tắt trung tâm và phân vị: min, 1st Qu., median, mean, 3rd Qu., max.

sd() tính độ lệch chuẩn, đo mức độ dao động/chênh lệch giữa các giá trị so với giá trị trung bình.

var() tính phương sai, thể hiện độ phân tán của dữ liệu; giá trị càng lớn dữ liệu càng dao động mạnh.

Các chỉ số này giúp nắm tổng quan về phân phối, trung vị, trung bình, mức ổn định và mức độ biến thiên của dữ liệu.

3.1.1 Biến BudgetUSD

summary(movies_dataset$BudgetUSD)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
##    100000   1190511   3265790   9802824   9002791 300000000
sd(movies_dataset$BudgetUSD)
## [1] 22494208
var(movies_dataset$BudgetUSD)
## [1] 5.059894e+14

Nhận xét: Ngân sách sản xuất phim cực kỳ phân hóa—đa số phim có chi phí vừa và tầm trung (median khoảng 3,3 triệu USD), nhưng chỉ cần một ít bom tấn “siêu đắt” đã đẩy giá trị trung bình vượt xa thực tế thị trường và làm biến động thống kê rất lớn. Khi phân tích, nên ưu tiên dùng trung vị và phân vị để nắm sát mặt bằng, còn nhóm ngân sách khủng cần phân tích riêng vì ảnh hưởng mạnh đến rủi ro và lợi nhuận toàn ngành phim.

3.1.2 Biến US_BoxOfficeUSD

summary(movies_dataset$US_BoxOfficeUSD)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
## 4.002e+04 1.490e+06 4.389e+06 1.496e+07 1.288e+07 1.018e+09
sd(movies_dataset$US_BoxOfficeUSD)
## [1] 38794034
var(movies_dataset$US_BoxOfficeUSD)
## [1] 1.504977e+15

Nhận xét:

Doanh thu phòng vé Mỹ cực kỳ phân cực, đa số phim có doanh thu thấp/trung vị khoảng 44 triệu USD.Trung bình bị kéo lên rất cao (149,6 triệu USD) do một số bom tấn ngoại lệ thắng lớn, còn đa số phim thu thấp hơn nhiều.

Độ lệch chuẩn và phương sai “siêu lớn” xác nhận mức độ biến động dữ dội, với rủi ro lớn cho nhà đầu tư điện ảnh.Đa phần phim chỉ đạt từ 15–129 triệu USD; nhóm “siêu doanh thu” quyết định toàn thị trường.

Khi phân tích nên so sánh trung vị, các phân vị và đặc biệt chú ý phim bom tấn để tránh nhầm lẫn do các ngoại lệ cực đoan.

3.1.3 Biến Global_BoxOfficeUSD

summary(movies_dataset$Global_BoxOfficeUSD)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
## 1.000e+05 2.762e+06 8.090e+06 2.721e+07 2.355e+07 1.499e+09
sd(movies_dataset$Global_BoxOfficeUSD)
## [1] 69542938
var(movies_dataset$Global_BoxOfficeUSD)
## [1] 4.83622e+15

Nhận xét:

Doanh thu phòng vé toàn cầu cực kỳ phân hóa: đa số phim có doanh thu vừa phải, trung vị chỉ 27,2 triệu USD.

Trung bình bị kéo lên rất cao (235,5 triệu USD), do một số ít bom tấn vượt trội; max lên tới 1,5 tỷ USD.Độ lệch chuẩn, phương sai đều cực lớn, phản ánh sự “vênh” dữ dội giữa phần đông phim và nhóm “siêu phẩm” đỉnh cao.

Khoảng cách giữa phân vị Q1 (~2,8 triệu USD) và Q3 (>250 triệu USD) cho thấy chỉ vài phim đạt mức siêu cao, đa số phim doanh thu khá khiêm tốn.Cần đánh giá qua trung vị, các phân vị và phân tích riêng nhóm bom tấn, vì chính các phim này quyết định biến động, rủi ro và thành bại của ngành.

3.1.4 Biến Opening_Day_SalesUSD

summary(movies_dataset$Opening_Day_SalesUSD)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
##      4050    279026    838722   2992745   2510360 295751068
sd(movies_dataset$Opening_Day_SalesUSD)
## [1] 8132438
var(movies_dataset$Opening_Day_SalesUSD)
## [1] 6.613655e+13

Nhận xét:

Doanh thu ngày mở màn phim cực kỳ phân hóa, median chỉ gần 3 triệu USD – đa số phim mở màn khiêm tốn.Trung bình (mean) bị đẩy lên rất cao do nhóm nhỏ “siêu bom tấn” có doanh thu cực khủng, max gần 3 tỷ USD.

Độ lệch chuẩn và phương sai rất lớn, cho thấy biến động giữa các phim cực mạnh; nhóm phim mở màn rực rỡ quyết định toàn ngành.Q1 rất thấp (khoảng 0,28 triệu), Q3 trên 25 triệu, nhấn mạnh một số ít phim vượt trội hoàn toàn so với phần đông.

Khi phân tích hiệu quả phát hành nên dùng median, quartile và cần xem xét riêng nhóm bom tấn, tránh để số liệu mean bị méo mó vì ngoại lệ quá lớn.

3.1.5 Biến One_Week_SalesUSD

summary(movies_dataset$One_Week_SalesUSD)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
##     16507    738315   2179436   7483442   6415143 579555113
sd(movies_dataset$One_Week_SalesUSD)
## [1] 19553372
var(movies_dataset$One_Week_SalesUSD)
## [1] 3.823344e+14

Nhận xét:

Doanh thu tuần đầu phòng vé cực kỳ phân hóa: trung vị chỉ ~7,5 triệu USD – phần lớn phim thu vừa phải, rất ít nổi bật.Trung bình (mean) bị kéo lên cao (64 triệu USD) vì vài phim bom tấn, max lên tới 579 triệu USD.

Độ lệch chuẩn, phương sai rất lớn, xác nhận biến động doanh thu chủ yếu do nhóm nhỏ phim dẫn đầu ngành.Từ Q1 (0,74 triệu) lên Q3 (21,8 triệu), chỉ nhóm siêu phẩm mới thực sự vượt trội.

Khi phân tích hiệu quả thương mại, nhất thiết nên dựa vào median, quartile, và tách riêng nhóm bom tấn vì mean tổng thể dễ bị “ảo giác” do số ít phim đột biến.

3.1.6 Biến ProfitUSD

summary(movies_dataset$ProfitUSD )
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
## 0.000e+00 8.291e+05 3.971e+06 1.740e+07 1.394e+07 1.199e+09
sd(movies_dataset$ProfitUSD)
## [1] 50404879
var(movies_dataset$ProfitUSD)
## [1] 2.540652e+15

Nhận xét:

Lợi nhuận ngành phim cực kỳ phân cực: median chỉ gần 4 triệu USD, đa số phim chỉ lời nhẹ hoặc thấp.Mean vượt xa median (17,4 triệu USD), cho thấy chỉ cần vài phim siêu lợi nhuận là đủ kéo toàn bộ chỉ số trung bình lên rất cao.

Độ lệch chuẩn, phương sai cực lớn, phản ánh môi trường “high risk – high return” đặc trưng: chỉ số nhỏ tạo rủi ro, số ít bom tấn làm nên lợi nhuận cho toàn thị trường.Q1 và Q3 cho thấy chênh lệch rõ giữa lợi nhuận nhóm phổ thông với nhóm phim đột biến.

Khi đánh giá hiệu quả tài chính ngành phim cần ưu tiên median/quartile, đồng thời tách riêng nhóm bom tấn, vì chỉ số mean dễ bị méo bởi ít phim xuất sắc vượt trội.

3.1.7 Biến doanh thu trung bình sau ngày công chiếu đầu tiên

summary(movies_dataset$Doanhthutb )
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
##      853    70312   211021   748449   630286 73716683
sd(movies_dataset$Doanhthutb)
## [1] 2021650
var(movies_dataset$Doanhthutb)
## [1] 4.087071e+12

Nhận xét:

Doanh thu trung bình ngày sau mở màn của đa số phim chỉ quanh 211.000 USD các phim bom tấn mới kéo mean lên tới gần 750.000 USD.Độ lệch chuẩn rất lớn (2 triệu USD), khoảng chỉ số trải rộng cực mạnh (từ vài trăm USD đến hơn 73 triệu USD), xác nhận đặc trưng phim thắng lớn hay phim thua lỗ của ngành điện ảnh.

Nếu doanh thu duy trì cao, ổn định sau ngày chiếu đầu thể hiện chất lượng tốt hoặc truyền miệng mạnh; nếu doanh thu giảm mạnh là dấu hiệu phim bùng nổ ngắn hạn.Chỉ số này rất quan trọng trong thực tế để nhà phát hành dự đoán hiệu quả quảng bá và chiến lược giữ doanh thu, giúp tối ưu hóa hoạt động kinh doanh phim trong tuần đầu tiên.

3.1.8 Biến Tỷ suất lợi nhuận

summary(movies_dataset$ROI)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.0000  0.6258  1.7514  1.7785  2.8762  4.0000
sd(movies_dataset$ROI)
## [1] 1.257058
var(movies_dataset$ROI)
## [1] 1.580195

Nhận xét:

Giá trị ROI trung bình đạt khoảng 1.78, nghĩa là phần lớn phim đều thu lại gần gấp đôi số vốn đầu tư, cho thấy đa số dự án có lãi khá ổn. Giá trị trung vị ROI khá gần với trung bình, phản ánh các phim có lợi nhuận đều nhau, không xuất hiện quá nhiều trường hợp lệch quá xa.

Khoảng chênh lệch ROI lớn giữa nhóm có lãi thấp và nhóm lãi cao, chứng tỏ đặc thù ngành phim: một số phim thắng cực lớn, còn lại đa phần chỉ lời vừa phải hoặc hoà vốn, lỗ nhẹ.Độ lệch chuẩn, phương sai khá cao, xác nhận lợi nhuận ngành phim biến động mạnh. Làm phim mang rủi ro rất lớn, nhưng nếu thành công có thể mang lại khoản lời vượt trội.

Các chỉ số này có ý nghĩa thực tiễn rõ rệt, giúp nhà sản xuất nhận diện cơ hội và khu vực rủi ro khi đầu tư; từ đó lựa chọn chiến lược sản xuất, truyền thông và phát hành phù hợp..

3.1.9 Biến Tỷ lệ mở màn của ngày đầu tiên trong 1 tuần

summary(movies_dataset$Opening_Ratio)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.1668  0.3000  0.3997  0.4052  0.4997  0.7497
sd(movies_dataset$Opening_Ratio)
## [1] 0.1269897
var(movies_dataset$Opening_Ratio)
## [1] 0.01612639

Nhận xét:

Tỷ lệ mở màn trung bình là 40,5%, cho thấy doanh thu tập trung nhiều vào ngày đầu ra mắt, nhấn mạnh vai trò của quảng bá. Tỷ lệ dao động từ 16,7% đến 74,9%, phản ánh có phim doanh thu bền vững, có phim bùng nổ mạnh ngày đầu.

Độ biến động vừa phải, không quá lệch lạc giữa các phim. Ngày mở màn cao thường nhờ chiến dịch marketing tốt, lượng fan đông và thương hiệu mạnh. Ngược lại, tỷ lệ thấp cho thấy doanh thu lan toả theo thời gian nhờ đánh giá và truyền miệng.

3.1.10 Biến độ chênh lệch giữa bình chọn nhà phê bình và khán giả

summary(movies_dataset$Tomatoes_IMDb)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
## -48.0000  -7.0000   0.0000  -0.1715   6.0000  47.0000
sd(movies_dataset$Tomatoes_IMDb)
## [1] 9.837395
var(movies_dataset$Tomatoes_IMDb)
## [1] 96.77435

Nhận xét:

Giá trị trung bình = -0.1715 gần bằng 0, cho thấy đánh giá của khán giả và giới phê bình tương đối đồng thuận.Giá trị âm nghĩa là khán giả thích phim hơn phê bình (thường phim giải trí, bom tấn).

Giá trị dương nghĩa là phê bình ưu ái hơn khán giả (phim nghệ thuật, độc lập).Độ lệch chuẩn lớn cho thấy sự khác biệt lớn trong cảm nhận giữa hai nhóm. Biến này giúp phân tích mối quan hệ giữa chất lượng nghệ thuật và sức hút thương mại của phim.

3.2 Kiểm tra các giá trị ngoại lai

library(knitr)
outlier_summary <- data.frame(
  Bien = c("BudgetUSD", "US_BoxOfficeUSD", "Global_BoxOfficeUSD", "Opening_Day_SalesUSD",
           "One_Week_SalesUSD", "ProfitUSD", "Doanhthutb",
           "ROI", "Opening_Ratio", "Tomatoes_IMDb"),
  So_luong_ngoai_lai = c(
    length(boxplot.stats(movies_dataset$BudgetUSD )$out),
    length(boxplot.stats(movies_dataset$US_BoxOfficeUSD)$out),
    length(boxplot.stats(movies_dataset$Global_BoxOfficeUSD)$out),
    length(boxplot.stats(movies_dataset$Opening_Day_SalesUSD)$out),
    length(boxplot.stats(movies_dataset$One_Week_SalesUSD)$out),
    length(boxplot.stats(movies_dataset$ProfitUSD)$out),
    length(boxplot.stats(movies_dataset$Doanhthutb)$out),
    length(boxplot.stats(movies_dataset$ROI)$out),
    length(boxplot.stats(movies_dataset$Opening_Ratio)$out),
    length(boxplot.stats(movies_dataset$Tomatoes_IMDb)$out)))
kable(outlier_summary, col.names = c("Tên biến", "Số lượng giá trị ngoại lai"))
Tên biến Số lượng giá trị ngoại lai
BudgetUSD 109157
US_BoxOfficeUSD 113724
Global_BoxOfficeUSD 113298
Opening_Day_SalesUSD 115328
One_Week_SalesUSD 113889
ProfitUSD 118280
Doanhthutb 114837
ROI 0
Opening_Ratio 0
Tomatoes_IMDb 8153

Giải thích:

Đoạn code này tạo một bảng tóm tắt số lượng giá trị ngoại lai cho 10 biến dữ liệu của bộ phim. Sử dụng hàm boxplot.stats(…$out) để lấy các điểm ngoại lai của từng biến, sau đó dùng length(…) để đếm số lượng các giá trị này. Bảng được trình bày bằng hàm kable() giúp hiển thị kết quả rõ ràng, dễ đọc trong tài liệu R Markdown

Nhận xét:

Hầu hết các biến doanh thu, ngân sách, doanh số, lợi nhuận đều có nhiều giá trị ngoại lai, cho thấy sự phân hóa mạnh giữa phim bom tấn với phần còn lại. Các biến tỷ số như ROI, Opening Ratio có rất ít hoặc không có giá trị ngoại lai, phản ánh đặc tính ổn định hơn về mặt tỷ lệ so với giá trị tuyệt đối.

Số lượng ngoại lai nhiều là đặc trưng của ngành công nghiệp điện ảnh, nơi tồn tại những phim thắng lớn hoặc thất bại bất thường so với phần lớn các phim còn lại.

3.3 Phân tích thống kê suy diễn các biến

vars_bien <- c("BudgetUSD", "One_Week_SalesUSD", "ProfitUSD","Doanhthutb","ROI")
corr_matrix_bien <- round(cor(movies_dataset[, vars_bien], use = "complete.obs"), 2)
kable(corr_matrix_bien,
      caption = " Ma trận tương quan giữa các biến ",
      align = "c",
      booktabs = TRUE,
      longtable = TRUE,        
      linesep = "") %>%
  kable_styling(
    latex_options = c("striped", "hold_position", "scale_down"), 
    font_size = 10,
    full_width = FALSE,        
    position = "center") %>%
  add_header_above(c(" " = 1, "Các biến tài chính " = 5))
Ma trận tương quan giữa các biến
Các biến tài chính
BudgetUSD One_Week_SalesUSD ProfitUSD Doanhthutb ROI
BudgetUSD 1.00 0.88 0.79 0.85 0.00
One_Week_SalesUSD 0.88 1.00 0.96 0.98 0.17
ProfitUSD 0.79 0.96 1.00 0.93 0.24
Doanhthutb 0.85 0.98 0.93 1.00 0.17
ROI 0.00 0.17 0.24 0.17 1.00

Giải thích:

Dòng 1: Khai báo biến vars_bien chứa tên các biến tài chính muốn kiểm tra tương quan.

Dòng 2: Tính ma trận hệ số tương quan Pearson giữa các biến tài chính trong dataset bằng hàm cor(), làm tròn 2 số thập phân và lưu vào corr_matrix_bien.

Dòng 3–8: Xuất bảng ma trận tương quan bằng kable() cho đẹp mắt, đặt caption, căn lề, tuỳ chọn bảng mở rộng… và thêm tiêu đề nhóm cột rõ ràng.

Nhận xét: Lợi nhuận và doanh thu có tương quan rất mạnh với doanh số tuần đầu tiên (hệ số > 0.9). Ngân sách liên quan chặt chẽ đến doanh thu, lợi nhuận (hệ số 0.79–0.88). ROI có tương quan rất yếu (gần 0) với ngân sách và thấp với các biến còn lại, cho thấy biến ROI phần lớn độc lập, không bị chi phối bởi quy mô ngân sách hoặc doanh số tức thì.

3.4 Phân tổ và phân loại các biến

3.4.1 Số lượng phim của mỗi thể loại theo từng Quốc gia

library(knitr)
kable(table(movies_dataset$Genre, movies_dataset$Country ))
Australia Canada China France Germany India Japan South Korea UK USA
Action 4578 6052 4629 4551 3036 7533 3003 1591 7708 107450
Comedy 6024 8114 6200 6136 4040 10219 4172 2017 10139 142771
Documentary 1584 2017 1575 1566 1029 2557 1006 478 2577 35725
Drama 7564 10132 7669 7413 5067 12900 4937 2532 12890 178914
Horror 3052 4046 3086 3060 1976 5123 2011 985 5111 71560
Romance 3106 4080 3063 3145 1952 5101 2035 1031 5111 71397
Sci-Fi 1568 2068 1560 1526 1032 2533 990 493 2532 35500
Thriller 3111 4019 3170 3074 2079 5018 2068 1059 5209 71264

Giải thích:

Dòng 1: Nạp thư viện knitr để xuất bảng đẹp trong R Markdown.

Dòng 2: Tạo bảng tần suất thể loại phim theo từng quốc gia sản xuất bằng hàm table(), trình bày bằng kable() để dễ nhìn, dễ in.

Nhận xét:

Mỹ sản xuất nhiều phim nhất: Drama 178.914, Comedy 142.771, Action 107.450 — chứng tỏ vị thế công nghiệp điện ảnh số 1 thế giới với thị trường tiêu thụ khổng lồ và xuất khẩu mạnh.

Ấn Độ đứng thứ hai: Drama 12.900, Comedy 10.219, phản ánh vùng tiêu thụ nội địa lớn và ảnh hưởng của Bollywood.

Anh, Nhật, Hàn… có số lượng trung bình nhưng mạnh ở dòng phim riêng, thể hiện bản sắc văn hóa.

3.4.2 Phân loại theo mức điểm IMDb

movies_dataset$IMDbGroup <- cut(
  movies_dataset$IMDbRating_100,
  breaks = c(-Inf, 50, 80, Inf),
  labels = c("Kém hấp dẫn", "Được đón nhận", "Được yêu thích")
)
kable(table(movies_dataset$IMDbGroup), col.names = c("Mức đánh giá","Số lượng"))
Mức đánh giá Số lượng
Kém hấp dẫn 166790
Được đón nhận 683148
Được yêu thích 150061

Giải thích: Hàm cut() được sử dụng để phân loại biến định lượng IMDbRating_100 (thang điểm 100) thành ba nhóm chất lượng phim dựa trên mức đánh giá của khán giả: “Kém hấp dẫn” (dưới 50), “Được đón nhận” (từ 50–80), “Được yêu thích” (trên 80), sau đó đếm số lượng từng nhóm và xuất bảng với kable().

Nhận xét:

Phân loại theo điểm IMDb cho thấy: có tới 683.148 phim chỉ đạt mức “Được đón nhận” (50–80 điểm), 166.790 phim “Kém hấp dẫn” (<50 điểm), và chỉ 150.061 phim thực sự “Được yêu thích” (>80 điểm).

Điều này phản ánh đặc thù ngành phim là tính cạnh tranh rất cao: chỉ một tỷ lệ nhỏ sản phẩm nổi bật mới tạo ra giá trị kinh tế thực sự lớn.

Đầu tư vào nội dung chất lượng, chiến lược quảng bá, phân phối hiệu quả và tận dụng dữ liệu người xem trở thành yếu tố sống còn để phim đạt đánh giá cao, giúp doanh nghiệp điện ảnh tối ưu hóa lợi nhuận, giảm lãng phí tài nguyên khi đa phần phim chỉ dừng ở mức trung bình hoặc thấp.

3.4.3 Phân tổ số lượng phim mỗi thể loại theo từng mức điểm IMDb

kable(table(movies_dataset$Genre, movies_dataset$IMDbGroup))
Kém hấp dẫn Được đón nhận Được yêu thích
Action 25119 102337 22675
Comedy 33121 136705 30006
Documentary 8253 34314 7547
Drama 42011 170810 37197
Horror 16653 68112 15245
Romance 16777 68430 14814
Sci-Fi 8226 33985 7591
Thriller 16630 68455 14986

Giải thích:

Đoạn code dùng table() và kable() để tạo bảng đếm số lượng phim theo thể loại (Genre) và mức điểm IMDb (Kém hấp dẫn, Được đón nhận, Được yêu thích) một cách gọn đẹp.

Nhận xét:

Nhóm phim “Được đón nhận” (50–80 điểm) chiếm tỷ lệ lớn ở tất cả thể loại: ví dụ Action có 102.337 phim, Drama 170.817, Comedy 136.705. Phim đạt mức “Được yêu thích” (>80) rất ít, Action chỉ có 22.675, Drama 37.197.

Điều này phản ánh ngành phim cực kỳ cạnh tranh: hầu hết sản phẩm chỉ dừng ở mức chấp nhận được, rất ít phim thực sự thành công cả về danh tiếng lẫn kinh tế. Đầu tư vào chất lượng nội dung, sáng tạo, quảng bá hiệu quả là yếu tố sống còn để tạo ra giá trị vượt trội. Đây cũng là lý do các hãng phim lớn chi mạnh cho kịch bản, marketing và xây dựng thương hiệu lâu dài để chạm tới nhóm phim “hit” tạo doanh thu lớn cho ngành.

3.4.4 Phân tổ số lượng phim của từng thể loại theo từng thập kĩ

kable(table(movies_dataset$Genre, movies_dataset$ThapKy ))
1950s 1960s 1970s 1980s 1990s 2000s 2010s 2020s
Action 5555 9768 14218 18493 22757 26963 31479 20898
Comedy 7305 13072 18681 24773 30381 36163 41746 27711
Documentary 1806 3267 4748 6183 7711 9001 10399 6999
Drama 9224 16559 23611 30479 37531 44981 52667 34966
Horror 3750 6607 9401 12162 15377 18110 20870 13733
Romance 3700 6579 9343 12297 15208 18020 20998 13876
Sci-Fi 1768 3306 4751 6116 7656 9018 10453 6734
Thriller 3703 6572 9389 12303 15034 17942 21043 14085

Giải thích: Đoạn code dùng table() và kable() để tạo bảng đếm số lượng phim cho từng thể loại (Genre) theo từng thập kỷ (ThapKy), giúp phân tích xu hướng phát triển của các dòng phim qua thời gian.

Nhận xét:

Số lượng phim của tất cả thể loại đều tăng ổn định qua các thập kỷ, đặc biệt sự bùng nổ từ thập niên 1990 trở đi (ví dụ: Drama tăng từ 8.567 film 1950s lên 34.966 film 2010s, Action từ 5.555 lên 24.137, Comedy từ 9.768 lên 27.711).

Xu hướng này cho thấy ngành phim trở thành một thị trường kinh tế lớn mạnh, nhờ sự phát triển công nghệ, mở rộng quy mô sản xuất, nhu cầu giải trí toàn cầu và đầu tư của các tập đoàn truyền thông. Việc tăng tốc sản xuất giúp doanh thu, việc làm và tính cạnh tranh trong ngành phim liên tục mở rộng, thúc đẩy đổi mới, đa dạng nội dung phục vụ cho nhiều nhóm đối tượng và thị trường khác nhau.

3.4.5 Phân tổ kết hợp “đạo diễn – quốc gia”

library(dplyr)
library(knitr)
director_country <- movies_dataset %>%
  group_by(Country, Director) %>%
  summarise(TotalRevenue = sum(Global_BoxOfficeUSD, na.rm = TRUE)) %>%
  group_by(Country) %>%
  summarise(MeanDirectorRevenue = mean(TotalRevenue, na.rm = TRUE)) %>%
  arrange(desc(MeanDirectorRevenue))

kable(head(director_country, 10),
      col.names = c("Quốc gia", "Doanh thu TB mỗi đạo diễn (USD)"))
Quốc gia Doanh thu TB mỗi đạo diễn (USD)
USA 129503935208
UK 9497363516
India 9295309091
Canada 7377356099
China 5534322415
Australia 5518205491
France 5502122418
Germany 3671617482
Japan 3621058062
South Korea 1853553392

Giải thích:

Dòng 1 và 2 : Tải gói dplyr và knitr hỗ trợ hiển thị bảng và định dạng trong rmarkdown.

Dòng 4–5: Nhóm dữ liệu theo quốc gia và đạo diễn, sau đó tính tổng doanh thu toàn cầu của từng đạo diễn tại mỗi quốc gia.

Dòng 6–8: Gom lại theo quốc gia, tính doanh thu trung bình của mỗi đạo diễn, rồi sắp xếp giảm dần để tìm quốc gia có đạo diễn đạt doanh thu cao nhất.

Dòng 11: Hiển thị bảng 10 quốc gia dẫn đầu bằng kable() cho kết quả rõ ràng trong báo cáo.

Nhận xét:

Các quốc gia có ngành công nghiệp phim mạnh như Mỹ (trên 12,9 tỷ USD/đạo diễn), Anh (9,4 tỷ), Ấn Độ (9,2 tỷ) đạt doanh thu trung bình mỗi đạo diễn rất cao, thể hiện lợi thế về thị trường rộng lớn, đầu tư lớn và khả năng xuất khẩu phim toàn cầu.

Những nước này không chỉ chi phối doanh thu ngành mà còn có sức ảnh hưởng đến xu hướng sản xuất phim, cho thấy tiềm năng sinh lợi của ngành giải trí và văn hoá ở quy mô quốc tế.

3.4.6 Phân loại đạo diễn chỉ có 1 phim và doanh thu phim cao trên 100 triệu đô

director_table <- table(movies_dataset$Director)
famous <- names(director_table[director_table > 5])
movies_dataset$Nhomdaodien<- ifelse(movies_dataset$Director %in% famous & movies_dataset$Global_BoxOfficeUSD > 50000000, " Nổi tiếng & doanh thu cao", "Khác")
table(movies_dataset$Nhomdaodien)
## 
##  Nổi tiếng & doanh thu cao                       Khác 
##                     124934                     875065

Giải thích:

Dòng 1: Tạo bảng tần suất, đếm số phim mỗi đạo diễn trong dữ liệu.

Dòng 2: Lọc ra danh sách đạo diễn có hơn 5 phim, gọi là “nổi tiếng”.

Dòng 3: Gán nhãn cho mỗi phim: nếu đạo diễn thuộc nhóm nổi tiếng và phim có doanh thu trên 50 triệu USD thì dán nhãn “Nổi tiếng & doanh thu cao”, ngược lại là “Khác”.

Dòng 4: Đếm số lượng phim thuộc từng nhóm bằng hàm table().

Nhận xét:

Chỉ có 124.934 phim (gần 13% dữ liệu) thuộc nhóm “Nổi tiếng & doanh thu cao”, còn lại tới 875.065 phim thuộc nhóm “Khác”. Điều này phản ánh trong ngành phim, chỉ một nhóm nhỏ đạo diễn có sức ảnh hưởng lớn và khả năng tạo ra phim siêu lợi nhuận.

Đa số đạo diễn chỉ thực hiện số lượng ít phim hoặc phim khó đạt mức doanh thu nổi bật. Việc xây dựng thương hiệu đạo diễn và đầu tư cho các dự án quy mô lớn là chiến lược then chốt để thành công về mặt kinh tế, tạo ra giá trị bền vững và lợi thế cạnh tranh trên thị trường điện ảnh.

3.4.7 Phân tổ số lượng phim mỗi thể loại theo các ngày lễ

kable(table(movies_dataset$Genre, movies_dataset$Holiday))
Giáng sinh Halloween Không có New Year Quốc khánh Mỹ Quốc tế phụ nữ Quốc tế thiếu nhi Valentine
Action 404 413 147224 430 439 406 409 406
Comedy 517 535 196023 525 536 577 546 573
Documentary 128 135 49145 140 143 134 146 143
Drama 697 662 245241 675 687 675 723 658
Horror 280 289 98008 283 292 281 286 291
Romance 251 320 98110 244 298 259 279 260
Sci-Fi 139 117 48861 128 151 126 137 143
Thriller 286 253 98113 284 303 285 268 279

Giải thích: Dòng code dùng hàm table() để phân tổ số lượng phim cho từng thể loại (Genre) theo từng dịp lễ (Holiday) trong dataset, sau đó xuất bảng bằng kable() để trình bày rõ ràng.

Nhận xét: Số lượng phim phát hành vào các ngày lễ lớn cao hơn khá nhiều so với ngày thường: Drama có 697 phim vào dịp Giáng sinh, 675 phim vào Năm mới và 723 phim vào ngày Quốc tế thiếu nhi. Comedy cũng có 517 phim vào Giáng sinh, 573 phim vào dịp Valentine.

Điều này cho thấy phát hành phim vào các dịp lễ là chiến lược kinh doanh quan trọng để tận dụng sức mua giải trí của khán giả, giúp tăng doanh thu phòng vé cho ngành phim. Các hãng phim thường dồn phim tiềm năng hoặc phim phù hợp chủ đề vào dịp lễ để tận dụng nhu cầu gia tăng, qua đó tối ưu hóa lợi nhuận và tăng sức cạnh tranh thị trường.

CHƯƠNG 4: TRỰC QUAN HÓA DỮ LIỆU

4.1 Biểu đồ trực quan hóa mức đánh giá IMDbRating thang điểm 100 theo thập kĩ

rating_summary <- movies_dataset %>%
  group_by(ThapKy) %>%
  summarise(
    MeanRating = mean(IMDbRating_100, na.rm = TRUE),
    MedianRating = median(IMDbRating_100, na.rm = TRUE),
    MaxRating = max(IMDbRating_100, na.rm = TRUE),
    MinRating = min(IMDbRating_100, na.rm = TRUE),
    Count = n()) %>%
  mutate(
    Rating_scaled = MeanRating * max(Count) / 100  )
ggplot(rating_summary, aes(x = ThapKy)) +
  geom_col(aes(y = Count), fill = "#FF6F61", alpha = 0.7) +
  geom_line(aes(y = Rating_scaled, group = 1), color = "#6B5B95", size = 1.5) + geom_point(aes(y = Rating_scaled), color = "#6B5B95", size = 4) +
 geom_ribbon(aes(ymin = MinRating * max(Count) / 100,
                  ymax = MaxRating * max(Count) / 100),
              fill = "#88B04B", alpha = 0.3) +
 geom_line(aes(y = MedianRating * max(Count) / 100, group = 1),
            color = "#F7CAC9", linetype = "dashed", size = 1.2) +
geom_text(
    aes(y = Rating_scaled + max(Count)*0.04,   
        label = round(MeanRating, 1)),
    color = "#4B3869",
    fontface = "bold",
    size = 4
  ) + scale_y_continuous(
    name = "Số lượng phim",
    sec.axis = sec_axis(~ . * 100 / max(rating_summary$Count),
                        name = "Điểm IMDb (thang 100)")
  ) + labs(
    title = "Trực quan hóa mức đánh giá IMDbRating theo thập kỷ",
    x = "Thập kỷ"
  ) + theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1, size = 11),
    axis.title.y = element_text(size = 12),
    axis.title.y.right = element_text(size = 12),
    plot.title = element_text(face = "bold", size = 14, hjust = 0.5)
  ) +  theme(
    plot.title = element_text(face = "bold", size = 20, color = "#1B1F8A", hjust = 0.5))

Giải thích:

Dòng 1–10: Nhóm dữ liệu theo thập kỷ (ThapKy), tính các chỉ số tổng hợp (Mean, Median, Max, Min, Count), và chuẩn hóa điểm trung bình (Rating_scaled) phục vụ so sánh thị phần giữa các thập kỷ.

Dòng 11–24: Vẽ cột số lượng phim từng thập kỷ (geom_col), đường thể hiện điểm IMDb trung bình đã chuẩn hóa (geom_line, geom_point), dải số liệu min-max (geom_ribbon), đường median, và gắn nhãn trực quan.

Dòng 25–39: Cài đặt trục y kép (số lượng phim và điểm IMDb), đặt tiêu đề, trục x, đổi giao diện và chỉnh lại các thuộc tính văn bản của biểu đồ.

Nhận xét: Biểu đồ cho thấy số lượng phim tăng mạnh qua từng thập kỷ, nhưng điểm IMDb trung bình duy trì quanh mức 64–65, chứng tỏ chất lượng phim ổn định dù sản lượng tăng nhanh.

4.2 Biểu đồ phân bố điểm đánh giá IMDb thang 100 của các bộ phim

movies_dataset <- movies_dataset %>%
  mutate(IMDbGroup = case_when(
    IMDbRating_100 < 40 ~ "Đánh giá thấp",
    IMDbRating_100 >= 40 & IMDbRating_100 < 70 ~ "Đánh giá trung bình",
    IMDbRating_100 >= 70 ~ "Đánh giá xuất sắc"))
rating_summary <- movies_dataset %>%
  group_by(IMDbGroup) %>%
  summarise(
    Mean = mean(IMDbRating_100, na.rm = TRUE),
    Median = median(IMDbRating_100, na.rm = TRUE),
    Min = min(IMDbRating_100, na.rm = TRUE),
    Max = max(IMDbRating_100, na.rm = TRUE),
    Count = n() )
ggplot(rating_summary, aes(x = reorder(IMDbGroup, Mean), y = Count, fill = Mean)) +
  geom_col(alpha = 0.85, color = "white", width = 0.55) +
  geom_errorbar(
    aes(ymin = Min * max(Count) / 100, ymax = Max * max(Count) / 100),
    width = 0.25, color = "#1B1F8A", linewidth = 1.1) +
  geom_line(aes(y = Mean * max(Count) / 100, group = 1),
            color = "#5A189A", linewidth = 1.3) +
  geom_point(aes(y = Mean * max(Count) / 100),
             color = "#5A189A", size = 5) +
  geom_text(aes(y = Mean * max(Count) / 100 + max(Count)*0.3,
                label = paste0("TB: ", round(Mean, 1))),
            color = "#2C3E50", fontface = "bold", size = 3.5) +
  geom_text(aes(y = Count / 2,
                label = paste0("SL: ", Count)),
            color = "black", fontface = "bold", size = 3.5) +
  scale_fill_gradientn(
    colours = c("#FF5F6D", "#FFC371", "#00C9A7"),
    name = "Điểm IMDb TB") +
  scale_y_continuous(
    name = "Số lượng phim",
    sec.axis = sec_axis(~ . * 100 / max(rating_summary$Count),
                        name = "Điểm IMDb (thang 100)")) +
  labs(
    title = "Phân tích điểm IMDb theo nhóm đánh giá (Thang 100)",
    subtitle = "Kết hợp giữa số lượng phim, vùng dao động và điểm trung bình IMDb",
    x = "Nhóm phim theo mức đánh giá IMDb") +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", size = 12, hjust = 0.5, color = "#2C3E50"),
    plot.subtitle = element_text(size = 12, hjust = 0.5, color = "#616A6B"),
    axis.text.x = element_text(size = 10, face = "bold", color = "#1B1B1B"),
    axis.title.x = element_text(size = 13, face = "bold"),
    axis.title.y = element_text(size = 13),
    axis.title.y.right = element_text(color = "#6B5B95", face = "bold"),
    legend.position = "top",
    legend.text = element_text(size = 12),
    legend.title = element_text(size = 12, face = "bold"),
    panel.grid.minor = element_blank() ) +
  theme(
    plot.title = element_text(face = "bold", size = 20, color = "#1B1F8A", hjust = 0.5))

Giải thích:

Dòng 1–6: Gán nhóm đánh giá IMDb theo thang 100 cho từng phim (thấp/trung bình/xuất sắc) bằng case_when và tạo biến phân loại.

Dòng 7–13: Tổng hợp dữ liệu theo nhóm đánh giá: tính trung bình (Mean), trung vị (Median), cực trị (Min/Max), và số lượng phim (Count), phục vụ dựng biểu đồ.

Dòng 14–53: Vẽ biểu đồ cột về số lượng phim từng nhóm đánh giá, thêm nhãn số lượng lên cột, vẽ errorbar thể hiện vùng dao động điểm IMDb, dựng đường nối thể hiện điểm trung bình và tùy chỉnh màu sắc, trục, tiêu đề.

Nhận xét: Phim có đánh giá trung bình chiếm tỷ trọng lớn nhất, tiếp đến là nhóm xuất sắc với điểm IMDb TB ≈ 80, trong khi nhóm đánh giá thấp ít xuất hiện. Điều này phản ánh xu hướng sản xuất tập trung vào phim chất lượng trung–cao, phù hợp thị hiếu khán giả.

4.3 Biểu đồ trực quan hóa mức điểm RottenTomatoesScore

rating_summary_rt <- movies_dataset %>%
group_by(Genre) %>%
summarise(MeanRT = mean(RottenTomatoesScore, na.rm = TRUE))

ggplot(movies_dataset, aes(x = Genre, y = RottenTomatoesScore, fill = Genre)) +
geom_boxplot(alpha = 0.6, outlier.color = "red", width = 0.6) +
geom_jitter(width = 0.15, alpha = 0.4, color = "gray40") +
geom_point(data = rating_summary_rt,
aes(y = MeanRT),
color = "#E63946", size = 4, shape = 18) +
geom_text(data = rating_summary_rt,
aes(y = MeanRT + 0.2, label = round(MeanRT, 1)),
color = "white", size = 3, fontface = "bold") +
labs(
title = "Điểm đánh giá Rotten Tomatoes theo thể loại phim",
x = "Thể loại phim",
y = "Điểm Rotten Tomatoes (thang 100)"
) +
scale_fill_brewer(palette = "Set3") +
theme_minimal(base_size = 13) +
theme(
  plot.title = element_text(face = "bold", size = 14, hjust = 0.5, 
                            color = "#2C3E50", margin = margin(b = 10)),
  plot.subtitle = element_text(size = 10, hjust = 0.5, 
                               color = "#616A6B", margin = margin(b = 10)),
  axis.text.x = element_text(size = 9, angle = 30, 
                             vjust = 0.8, hjust = 1),
  axis.title.x = element_text(size = 11, face = "bold"),
  axis.title.y = element_text(size = 11),
  legend.position = "right",
  legend.text = element_text(size = 9),
  legend.title = element_text(size = 10, face = "bold")
) + 
  theme(
    plot.title = element_text(face = "bold", size = 20, color = "#1B1F8A", hjust = 0.5))

Giải thích:

Dòng 1–3: Tính điểm trung bình MeanRT cho từng thể loại.

Dòng 4–13: Dựng boxplot thể hiện phân bố điểm, thêm jitter hiển thị từng phim, điểm trung bình và nhãn giá trị.

Dòng 14–35: Thiết lập màu, tiêu đề và định dạng trục để biểu đồ dễ đọc.

Nhận xét: Điểm trung bình giữa các thể loại dao động hẹp (≈64 đến 65 điểm), cho thấy mức đánh giá khá ổn định. Các thể loại như Drama và Documentary có độ phân tán cao hơn, phản ánh sự đa dạng về chất lượng phim, trong khi Comedy và Romance điểm tập trung hơn, chứng tỏ đánh giá đồng đều.

4.4 Biểu độc trực quan hóa các đạo diễn nổi tiếng với doanh thu cao

top_directors <- movies_dataset %>%
  group_by(Director) %>%
  summarise(MeanDirectorRevenue = mean(Global_BoxOfficeUSD, na.rm = TRUE)) %>%
  arrange(desc(MeanDirectorRevenue)) %>%
  slice_head(n = 10)
avg_revenue <- mean(top_directors$MeanDirectorRevenue, na.rm = TRUE)
top_name <- top_directors$Director[1]
ggplot(top_directors, aes(x = reorder(Director, MeanDirectorRevenue),
                          y = MeanDirectorRevenue)) +
  geom_col(aes(fill = MeanDirectorRevenue), 
           width = 0.6, color = "white", alpha = 0.9) +
  geom_hline(yintercept = avg_revenue, 
             linetype = "dashed", color = "#2C3E50", linewidth = 1) +
  geom_point(data = filter(top_directors, Director == top_name),
             aes(y = MeanDirectorRevenue),
             color = "#FF0054", size = 5) +
  geom_text(aes(label = paste0("$", round(MeanDirectorRevenue / 1e6, 2), "M")),
            hjust = -0.1, size = 3.3, fontface = "bold", color = "black") +
  coord_flip(clip = "off") +
  scale_fill_gradientn(
    colours = c("#00C9A7", "#FFC300", "#FF5F6D"),
    name = "Doanh thu TB (USD)",
    labels = label_number(scale = 1e-6, suffix = "M", accuracy = 0.1)) +
  scale_y_continuous(
    labels = label_number(scale = 1e-6, suffix = "M"),
    name = "Doanh thu trung bình (USD)",
    expand = expansion(mult = c(0, 0.1)) ) +
  labs(
    title = "Top 10 đạo diễn có doanh thu trung bình toàn cầu cao nhất",
    x = "Đạo diễn") +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", size = 15, hjust = 0.5, color = "#2C3E50"),
    plot.subtitle = element_text(size = 10, hjust = 0.5, color = "#616A6B"),
    axis.text.x = element_text(size = 10),
    axis.text.y = element_text(size = 10, face = "bold"),
    axis.title.x = element_text(size = 12, face = "bold"),
    axis.title.y = element_text(size = 12),
    legend.position = "top",
    legend.title = element_text(size = 10, face = "bold"),
    legend.text = element_text(size = 9),
    legend.key.width = unit(2, "cm"),
    legend.spacing.x = unit(0.3, "cm"),
    legend.margin = margin(0, 0, 5, 0),
    plot.margin = margin(10, 40, 10, 20)) +
   theme(
    plot.title = element_text(face = "bold", size = 20, color = "#1B1F8A", hjust = 0.5))

Giải thích:

Dòng 1-5: Tính toán top 10 đạo diễn có doanh thu trung bình toàn cầu cao nhất bằng cách: nhóm dữ liệu theo đạo diễn, tính doanh thu trung bình, sắp xếp giảm dần, lấy top 10.

Dòng 6-7: Tính giá trị trung bình chung và lưu tên đạo diễn đứng đầu top phục vụ vẽ biểu đồ nổi bật.

Dòng 8-18: Tạo biểu đồ cột (barplot) với ggplot. Các cột biểu diễn doanh thu trung bình từng đạo diễn (geom_col), thêm đường trung bình toàn top (geom_hline), làm nổi bật đạo diễn dẫn đầu (geom_point), chèn nhãn giá trị doanh thu lên từng cột (geom_text).

Dòng 19-47: Tùy chỉnh giao diện biểu đồ: xoay trục (coord_flip), chỉnh bảng màu theo giá trị (scale_fill_gradient), đặt tiêu đề, nhãn trục và format số liệu, chọn theme đơn giản, cấu hình hiển thị legend và định dạng văn bản cho biểu đồ rõ ràng, đẹp mắt.

Nhận xét:

Biểu đồ cho thấy top 10 đạo diễn có doanh thu trung bình toàn cầu rất cao, dao động từ khoảng 28.5 triệu đến 29.3 triệu USD/phim, với đạo diễn dẫn đầu là Albert Phillips đạt 29.32 triệu USD/phim. Sự chênh lệch giữa các vị trí trong top 10 không lớn, cho thấy mặt bằng doanh thu các đạo diễn hàng đầu khá đồng đều – phản ánh năng lực sản xuất phim thành công ổn định của nhóm này.

Việc làm nổi bật đạo diễn dẫn đầu và đặt các cột sát nhau giúp so sánh trực quan, đồng thời biểu đồ này cho thấy giá trị kinh tế vượt trội mà các đạo diễn nổi tiếng mang lại, họ giữ vai trò then chốt trong lợi nhuận ngành điện ảnh, góp phần nâng tầm thương hiệu và thu hút nhà đầu tư cho các dự án phim lớn.

4.5 Biểu đồ trực quan hóa các bộ phim mỗi thể loại theo từng thập kỷ

plot_data <- movies_dataset %>%
  group_by(ThapKy, Genre) %>%
  summarise(Count = n(), .groups = "drop")
ggplot(plot_data, aes(x = ThapKy, y = Count, fill = Genre)) +
  geom_col(position = "dodge", width = 0.8, alpha = 0.9) +          
  labs(
    title = "Số lượng phim mỗi thể loại qua từng thập kỹ",
    x = "Thập kỹ",
    y = "Số lượng phim",
    fill = "Thể loại" ) +
  scale_fill_brewer(palette = "Set1") +                             
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 30, hjust = 1)) +
   theme(
    plot.title = element_text(face = "bold", size = 20, color = "#1B1F8A", hjust = 0.5))

Giải thích:

Dòng 1-3: Tạo bảng tổng hợp plot_data bằng cách nhóm theo thập kỷ (ThapKy) và thể loại (Genre), sau đó đếm số phim từng nhóm. Đây là bước chuẩn bị dữ liệu đầu vào cho biểu đồ.

Dòng 4-15: Dựng biểu đồ cột nhóm bằng ggplot. Sử dụng geom_col vẽ cột số lượng phim cho từng thể loại ở mỗi thập kỷ. Tuỳ chỉnh nhãn trục, tiêu đề, bảng màu, căn chỉnh văn bản, giao diện tối giản và làm đẹp biểu đồ bằng các hàm labs, scale_fill_brewer, và theme.

Nhận xét:

Biểu đồ thể hiện rõ xu hướng sản xuất phim tăng dần theo thời gian: số lượng phim ở mọi thể loại đều tăng mạnh từ thập niên 1980-1990 đến nay.

Các dòng phim phổ biến như Drama, Comedy, Action luôn chiếm tỷ trọng lớn và tăng đều qua các thập kỷ, phản ánh nhu cầu giải trí đa dạng và sức hút ổn định của các thể loại này với thị trường toàn cầu.

Sự gia tăng mạnh số lượng phim theo thời gian chứng tỏ ngành công nghiệp điện ảnh ngày càng mở rộng quy mô, thu hút nhiều đầu tư và ngày càng có vai trò quan trọng trong nền kinh tế sáng tạo và giải trí hiện đại.

4.6 Biểu đồ trực quan hóa các bộ phim mỗi thể loại theo từng châu lục

count_by_continent <- movies_dataset %>%
  group_by(Continent, Genre) %>%
  summarise(SoLuong = n())
ggplot(count_by_continent, aes(x = Genre, y = SoLuong, fill = Genre)) +
  geom_col(width = 0.75, alpha = 0.9) +
  geom_text(aes(label = SoLuong), 
            vjust = 0.5, size = 2, fontface = "bold", color = "black") +
  facet_wrap(~Continent, ncol = 2) +
  labs(
    title = "Số lượng phim từng thể loại tại mỗi châu lục",
    x = NULL,
    y = "Số lượng phim",
    fill = "Thể loại phim") +
  theme_minimal() +
  scale_fill_brewer(palette = "Set2") +
  scale_fill_brewer(palette = "Set2") +
  theme(
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank(),
    legend.position = "bottom",
    legend.direction = "horizontal",
    legend.title = element_text(size = 11, face = "bold"),
    legend.text = element_text(size = 8),
    strip.text = element_text(size = 13, face = "bold"),
    plot.margin = margin(10, 30, 10, 10)) +
  theme(
    plot.title = element_text(face = "bold", size = 20, color = "#1B1F8A", hjust = 0.5))

Giải thích:

Dòng 1-4: Nhóm dữ liệu theo châu lục (Continent) và thể loại phim (Genre), sau đó đếm số lượng phim từng nhóm bằng hàm n(). Kết quả là bảng tần suất từng thể loại tại mỗi châu lục.

Dòng 5-8: Dựng biểu đồ cột nhóm (geom_col) về số lượng phim, chèn nhãn số lượng phim lên từng cột (geom_text), chia biểu đồ theo từng châu lục (facet_wrap).

Dòng 9-25: Tuỳ chỉnh tiêu đề, trục, màu sắc cột, giao diện tối giản, định dạng nhãn, vị trí và kiểu chữ cho legend—giúp biểu đồ rõ ràng, dễ nhìn hơn.

Dòng 26-27: Điều chỉnh theme cho tiêu đề lớn, màu và căn chỉnh các thành phần biểu đồ chuyên nghiệp.

Nhận xét: Biểu đồ trên thể hiện số lượng phim theo từng thể loại tại bốn châu lục gồm Americas, Europe, Asia và Oceania. Kết quả cho thấy châu Mỹ chiếm tỷ trọng lớn nhất, với các thể loại phổ biến là Drama, Comedy và Action. Châu Âu đứng thứ hai, có cơ cấu thể loại tương tự nhưng quy mô nhỏ hơn. Trong khi đó, châu Á tuy sản xuất ít hơn nhưng các thể loại khá cân bằng, thể hiện sự đa dạng nội dung. Châu Đại Dương có số lượng phim thấp nhất ở tất cả các thể loại. Nhìn chung, biểu đồ phản ánh sự tập trung của ngành công nghiệp điện ảnh chủ yếu ở châu Mỹ và châu Âu, trong khi các khu vực khác vẫn đang phát triển về quy mô sản xuất.

4.8 Biểu đồ trực quan hóa các bộ phim mỗi thể loại theo các ngày lễ

library(dplyr)
library(ggplot2)
plot_data <- movies_dataset %>%
  group_by(Genre, Holiday) %>%
  filter(Holiday != "Không có") %>%
  summarise(SoLuong = n(), .groups = "drop")
ggplot(plot_data, aes(x = Genre, y = SoLuong, fill = Genre)) +
  geom_col(width = 0.7, alpha = 0.88, position = "dodge") +
  facet_wrap(~Holiday) + 
  geom_text(aes(label = SoLuong), vjust = 0.5, size = 2, fontface = "bold") +
  labs(
    title = "Số lượng phim mỗi thể loại phát hành theo các ngày lễ",
    x = NULL,
    y = "Số lượng phim",
    fill = "Thể loại") +
  theme_minimal() +
  theme(
    axis.text.x = element_blank(),
    legend.position = "right",
    plot.title = element_text(size = 14, face = "bold"),
     strip.text = element_text(size = 10, face = "bold")) + 
  theme(
    plot.title = element_text(face = "bold", size = 20, color = "#1B1F8A", hjust = 0.5))

Giải thích:

Dòng 3-6: Tạo bảng tổng hợp số lượng phim (plot_data) cho từng thể loại (Genre) và dịp lễ (Holiday) bằng cách nhóm dữ liệu, lọc bỏ ngày thường, sau đó đếm số phim từng nhóm.

Dòng 7-10: Dựng biểu đồ cột nhóm (geom_col) thể hiện số lượng phim mỗi thể loại theo từng ngày lễ, gắn nhãn số trên cột, chia biểu đồ thành nhiều panel theo từng dịp lễ (facet_wrap).

Dòng 11-23: Tuỳ chỉnh tiêu đề, bảng màu, font chữ, căn chỉnh, theme để biểu đồ đẹp và rõ ràng, phù hợp với báo cáo.

Nhận xét:

Biểu đồ cho thấy các ngày lễ như Giáng sinh, Quốc tế thiếu nhi, Halloween… đều có số lượng phim phát hành cao ở tất cả thể loại. Đặc biệt, thể loại Drama và Comedy sản xuất vượt trội, mỗi dịp lễ đều có từ 600-700 phim, Action cũng khá cao.

Việc phân bố số lượng lớn phim vào dịp lễ phản ánh chiến lược ra mắt nhằm tranh thủ lượng khán giả tăng cao, tối ưu doanh thu phòng vé. Các hãng phim chủ động dồn phim tiềm năng, đa dạng thể loại vào dịp cao điểm để tận dụng kinh tế mùa lễ hội và tăng khả năng cạnh tranh trên thị trường.

4.9 Biểu đồ trực quan hóa tỉ lệ các thể loại phim theo quốc gia

pie_all <- movies_dataset %>%
  group_by(Country, Genre) %>%
  summarise(SL = n(), .groups = "drop") %>%
  group_by(Country) %>%
  mutate(TiLe = SL / sum(SL)) %>% 
  ungroup()
ggplot(pie_all, aes(x = "", y = TiLe, fill = Genre)) +
  geom_col(width = 1, color = "white") +
  coord_polar("y", start = 0) +
  facet_wrap(~Country) +
  theme_void() +
  geom_text(aes(label = scales::percent(TiLe, accuracy = 1)),
            position = position_stack(vjust = 0.5), size = 2) +
  scale_fill_brewer(palette = "Set3") +
  scale_fill_brewer(palette = "Set3") +
  labs(
    title = "Tỉ lệ các thể loại phim tại từng quốc gia",
    fill = "Thể loại") +
  theme(
    plot.title = element_text(face = "bold", size = 20, color = "#1B1F8A", hjust = 0.5))

Giải thích:

Dòng 1-5: Nhóm dữ liệu theo quốc gia và thể loại phim, đếm số lượng từng thể loại trong mỗi quốc gia (summarise), sau đó tính tỷ lệ % thể loại so với tổng phim của quốc gia (mutate).

Dòng 6: Bỏ nhóm (ungroup) để vẽ biểu đồ dễ dàng.

Dòng 7-10: Tạo biểu đồ pie chart (tròn) theo từng quốc gia, thể hiện tỷ lệ % từng thể loại trên tổng số phim mỗi nước (geom_col, coord_polar, facet_wrap).

Dòng 11-20: Thêm nhãn %, dùng bảng màu cho từng thể loại, đặt tiêu đề, chỉnh theme nổi bật và căn chỉnh cho biểu đồ.

Nhận xét:

Biểu đồ cho thấy các thể loại Drama, Romance, Action luôn chiếm tỷ lệ lớn ở hầu hết quốc gia, đặc biệt ở Mỹ, Anh, Ấn Độ và Nhật. Một số nước như Nhật, Hàn Quốc tỷ lệ Romance và Sci-Fi khá cao, phản ánh xu hướng nội dung đặc thù vùng châu Á.

Tích cực đa dạng hóa thể loại giúp khai thác nhiều tệp khán giả, tối ưu hóa đầu tư sản xuất phim, giảm rủi ro kinh tế cho ngành điện ảnh từng quốc gia. Các nền điện ảnh lớn thường có tỷ lệ phân bổ cân đối giữa các thể loại nhằm tăng giá trị xuất khẩu phim và phục vụ rộng rãi trong nước.

4.10 Biểu đồ trực quan hóa trung bình điểm IMDb và RottenTomatiesScore qua các năm

plot_data <- movies_dataset %>%
  group_by(ReleaseYear) %>%
  summarise(
    Avg_IMDb = mean(IMDbRating_100, na.rm = TRUE),
    Avg_Rotten = mean(RottenTomatoesScore, na.rm = TRUE),
    .groups = "drop")
plot_long <- pivot_longer(
  plot_data,
  cols = c(Avg_IMDb, Avg_Rotten),
  names_to = "Score_Type",
  values_to = "Score")
ggplot(plot_long, aes(x = ReleaseYear, y = Score, color = Score_Type, group = Score_Type)) +
  geom_line(linewidth = 1.2) +
  geom_point(size = 2.2) +
  geom_smooth(se = FALSE, linetype = "dashed", linewidth = 0.9, alpha = 0.6) +  
  scale_color_manual(values = c("#1B9E77", "#D95F02"),
                     labels = c("IMDb Rating (thang 100)", 
                                "Rotten Tomatoes ")) +
  labs(
    title = "Xu hướng trung bình điểm IMDb và Rotten Tomatoes qua các năm",
    subtitle = "Biểu đồ thể hiện sự biến động chất lượng phim được
    đánh giá bởi khán giả và giới phê bình",
    x = "Năm phát hành",
    y = "Điểm trung bình (thang 100)",
    color = "Thang điểm" ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", size = 20, color = "#1B1F8A", hjust = 0.5),
    plot.subtitle = element_text(size = 13, color = "gray30", hjust = 0.5),
    axis.title = element_text(size = 14, face = "bold"),
    axis.text = element_text(size = 12),
    legend.position = "bottom",
    legend.title = element_text(face = "bold"),
    plot.margin = margin(20, 30, 20, 30) ) +
  scale_y_continuous(labels = number_format(accuracy = 1))

Giải thích:

Dòng 1-6: Tạo bảng tổng hợp plot_data, nhóm theo năm phát hành (ReleaseYear), tính điểm IMDb và RottenTomato trung bình mỗi năm.

Dòng 7-11: Chuẩn hóa dữ liệu thành dạng dài (pivot_longer) để vẽ nhiều đường trên 1 biểu đồ.

Dòng 12-18: Dựng biểu đồ đường với ggplot: trục x là năm phát hành, trục y là điểm trung bình, màu phân biệt IMDb và RottenTomato. Thêm đường biểu diễn (geom_line), các điểm dữ liệu (geom_point), mượt hóa đường (geom_smooth), đặt chú thích màu rõ ràng.

Dòng 19-35: Tùy chỉnh màu, tiêu đề, chú thích trục, phông chữ, căn chỉnh vị trí legend, định dạng số liệu và giao diện theo chuẩn báo cáo.

Nhận xét: Biểu đồ cho thấy cả điểm IMDb và Rotten Tomatoes trung bình đều dao động hầu như ổn định theo thời gian (giai đoạn 1950-2020), trung bình quanh 64-66 điểm cho IMDb và 63-65 cho Rotten Tomatoes. Điểm IMDb luôn cao hơn Rotten Tomatoes một chút nhưng chênh lệch không lớn, phản ánh sự khác biệt nhỏ giữa đánh giá của khán giả với giới phê bình.

4.11 Biểu đồ trực quan hóa mối tương quan giữa IMDbRating_100 và RottenTomatoesScore của từng bộ phim

movies_dataset <- movies_dataset %>%
  mutate(IMDbRating_100 = IMDbRating * 10)
ggplot(movies_dataset, aes(x = IMDbRating_100, y = RottenTomatoesScore)) +
  geom_bin2d(bins = 20) +
  scale_fill_gradient(low = "#00C9A7", high = "#FF5F6D", name = "Số lượng phim") +
  labs(
    title = "Mối tương quan giữa IMDbRating và RottenTomatoesScore",
   subtitle = "Màu đậm thể hiện nhiều phim có cùng mức đánh giá",
    x = "Điểm IMDb (thang 100)",
    y = "Điểm Rotten Tomatoes") +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", size = 20, color = "#1B1F8A", hjust = 0.5),
    plot.subtitle = element_text(size = 13, color = "gray30", face = "bold", hjust = 0.5),
    axis.title = element_text(size = 14, face = "bold"),
    axis.text.y = element_text(size = 12, face = "bold"),
    axis.text.x = element_text(size = 12),
    plot.margin = margin(20, 30, 20, 30))

Giải thích:

Dòng 1-2: Tạo biến mới IMDBRating_100 bằng cách nhân điểm IMDb gốc với 10 (chuyển điểm IMDb sang thang 100).

Dòng 3: Dùng ggplot với trục x là điểm IMDb (thang 100), trục y là RottenTomatoesScore, biểu diễn tương quan giữa hai điểm số.

Dòng 4: Tạo biểu đồ theo kiểu hộp phân vị hình vuông (2D binning) với 20 ô phân chia theo trục x, y, giúp so sánh mật độ dữ liệu.

Dòng 5: Dùng dải màu gradient: màu đậm thể hiện nơi có nhiều phim cùng mức đánh giá (nhiều dữ liệu trùng nhau).

Dòng 6-10: Gán tiêu đề, phụ đề, chú thích trục và bảng màu để người đọc dễ hiểu.

Dòng 11-18: Chỉnh sửa giao diện: theme tối giản, tô đậm tiêu đề, căn giữa, đổi font và lề cho biểu đồ đẹp và nổi bật hơn.

Nhận xét:

Biểu đồ thể hiện mối tương quan tuyến tính khá rõ giữa điểm IMDb và điểm RottenTomatoes, tập trung dày đặc ở vùng 50-80 điểm trên cả hai trục. Màu sắc đậm ở trung tâm cho thấy phần lớn phim nhận đánh giá trung bình-khá từ cả hai hệ thống. Rất ít phim đạt điểm xuất sắc hoàn toàn hoặc thấp quá, chứng tỏ sự đồng thuận tương đối giữa hai điểm số.

Kết quả này phản ánh ngành phim có chất lượng mặt bằng chung ổn định, đồng thời chỉ một số ít phim thật sự nổi bật vượt trội. Do đó, nếu muốn tác phẩm đạt được cả sự yêu thích của công chúng (IMDb) và phê bình (RottenTomatoes), các hãng phim cần đầu tư bài bản về nội dung, sản xuất và truyền thông để vươn lên vùng đỉnh của biểu đồ, qua đó tối ưu hoá tác động kinh tế và thương hiệu của phim.

4.12 Biểu đồ trực quan hóa tần suất phim theo thể loại phim

freq_genre <- movies_dataset %>%
  group_by(Genre) %>%
  summarise(SoLuong = n()) %>%
  arrange(desc(SoLuong))
tb <- mean(freq_genre$SoLuong, na.rm = TRUE)
ggplot(freq_genre, aes(x = reorder(Genre, SoLuong), y = SoLuong, fill = Genre)) +
  geom_col(width = 0.7, color = "black", show.legend = FALSE) +
  coord_flip() +
  geom_hline(yintercept = tb, linetype = "dashed", 
             color = "red", linewidth = 1) +
  geom_text(aes(label = paste0(comma(SoLuong))),
            hjust = -0.1, size = 4, fontface = "bold", color = "black") +
  annotate("text",
           x = 1, y = tb + max(freq_genre$SoLuong) * 0.05,
           label = paste0("Trung bình = ", comma(round(tb, 0))),
           color = "red", fontface = "bold", size = 4, hjust = 0) +
  scale_fill_brewer(palette = "Set2") +
  scale_y_continuous(labels = comma_format()) +
  labs(
    title = "Tần suất sản xuất phim theo Thể loại",
    subtitle = "So sánh số lượng phim thuộc các thể loại khác nhau 
    trong toàn bộ tập dữ liệu",
    x = "Thể loại phim",
    y = "Số lượng phim (bộ)" ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", size = 20, color = "#1B1F8A", hjust = 0.5),
    plot.subtitle = element_text(size = 13, color = "gray30", face = "bold", hjust = 0.5),
    axis.title = element_text(size = 14, face = "bold"),
    axis.text.y = element_text(size = 12, face = "bold"),
    axis.text.x = element_text(size = 12),
    plot.margin = margin(20, 30, 20, 30) ) +
  expand_limits(y = max(freq_genre$SoLuong) * 1.1)

Giải thích:

Dòng 1-5: Tiền xử lý dữ liệu nhóm theo thể loại phim (Genre), đếm số lượng từng loại, sắp xếp giảm dần, rồi tính giá trị trung bình toàn ngành (tb). Đây là bước tổng hợp số liệu làm đầu vào phân tích và trực quan hóa.

Dòng 6-8: Khởi tạo biểu đồ với ggplot, vẽ cột ngang cho từng thể loại, mỗi cột thể hiện số lượng phim, dùng màu nền theo nhóm phim và xoay cột sang ngang cho dễ nhìn, dễ so sánh.

Dòng 9-16: Bổ sung chi tiết: vẽ đường ngang màu đỏ nét đứt tại mức trung bình ngành (geom_hline); gắn nhãn số lượng phim lên từng cột (geom_text); ghi chú nổi bật giá trị trung bình cạnh đường bằng màu đỏ, font đậm (annotate).

Dòng 17-25: Tùy chỉnh bảng màu từng nhóm phim (scale_fill_brewer) và format lại trục số theo dạng phân tách dấu phẩy giúp biểu đồ dễ đọc hơn.

Dòng 26-33: Hoàn thiện giao diện: đặt tiêu đề, phụ đề, trục x/y, màu sắc, phông chữ, căn chỉnh margin, mở rộng trục y (nếu cần) bằng các hàm theme.

Nhận xét:

Drama (250.018 phim), Comedy (199.832), Action (150.131) là ba thể loại vượt xa mức trung bình ngành (125.000 phim/thể loại), chứng tỏ định hướng sản xuất tập trung vào nhóm có sức hút và thị trường lớn nhất. Các thể loại còn lại như Thriller, Romance, Documentary, Sci-Fi, Horror đều thấp hơn nhiều so với trung bình. Điều này phản ánh kinh tế ngành phim ưu tiên dòng chính để tối ưu hóa doanh thu.

Đường trung bình màu đỏ giúp nhanh chóng xác định nhóm thể loại vượt trội hoặc kém so với mặt bằng chung ngành. Biểu đồ trình bày sắc nét, rõ ràng, phục vụ tốt cho việc nhận diện xu hướng, chiến lược đầu tư vào các dòng phim chủ lực.

4.13 Biểu đồ trực quan hóa tần suất phim theo thập kỷ

df_thapky <- movies_dataset %>%
  group_by(ThapKy) %>%
  summarise(SoLuong = n()) %>%
  ungroup()
tb_thapky <- mean(df_thapky$SoLuong, na.rm = TRUE)
ggplot(df_thapky, aes(x = ThapKy, y = SoLuong, fill = ThapKy)) +
  geom_col(width = 0.7, color = "black", show.legend = FALSE) +
  geom_hline(yintercept = tb_thapky, 
             linetype = "dashed", color = "red", linewidth = 1) +
  geom_text(aes(label = comma(SoLuong)), 
            vjust = -0.4, size = 4, fontface = "bold", color = "black") +
  annotate("text", x = 1, y = tb_thapky + max(df_thapky$SoLuong) * 0.03,
           label = paste0("Trung bình = ", comma(round(tb_thapky, 0)), " phim"),
           color = "red", fontface = "bold", size = 4, hjust = 0) +
scale_fill_brewer(palette = "Set3") +
labs(
    title = "Tần suất sản xuất phim qua các thập kỷ",
    subtitle = "Biểu đồ thể hiện xu hướng thay đổi số lượng phim được
    phát hành theo từng giai đoạn thời gian",
    x = "Thập kỷ",
    y = "Số lượng phim (bộ)" ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5, size = 20, color = "#1B1F8A"),
    plot.subtitle = element_text(hjust = 0.5, size = 13, color = "gray30", face = "bold"),
    axis.title = element_text(size = 14, face = "bold"),
    axis.text.x = element_text(size = 12, face = "bold"),
    axis.text.y = element_text(size = 12, face = "bold"),
    plot.margin = margin(20, 30, 20, 30) )

Giải thích:

Dòng 1-5: Tiền xử lý: nhóm dữ liệu theo thập kỷ (ThapKy), đếm số lượng phim từng nhóm, sắp xếp giảm dần; tính trung bình toàn ngành để vẽ đường tham chiếu.

Dòng 6-7: Khởi tạo và dựng biểu đồ cột ngang với ggplot, mỗi cột là một thập kỷ. Áp dụng màu nền và xoay trục để so sánh dễ dàng.

Dòng 8-11: Vẽ đường ngang màu đỏ tại mức trung bình ngành, gắn nhãn số lượng phim lên từng cột, chú thích giá trị trung bình nổi bật bằng màu đỏ, font đậm.

Dòng 12-15: Tùy chỉnh bảng màu từng cột (theo thập kỷ) và format trục số với dấu phẩy cho rõ ràng.

Dòng 16-29: Hoàn thiện giao diện: tiêu đề, phụ đề, tên trục, màu sắc, phông chữ, căn chỉnh margin, mở rộng trục y giúp biểu đồ rõ, đẹp và chuyên nghiệp.

Nhận xét:

Số lượng phim sản xuất tăng rõ rệt qua các thập kỷ, đặc biệt từ 1980s trở đi (từ 36.811 phim lên tới 209.655 phim trong 2010s), thể hiện sự mở rộng mạnh mẽ của ngành điện ảnh hiện đại. Các thập kỷ gần đây đều vượt mức trung bình ngành (125.000 phim/thập kỷ), chứng tỏ xu hướng tăng trưởng rõ rệt về quy mô sản xuất.

Đường trung bình màu đỏ giúp nhận diện nhanh nhóm thập kỷ vượt trội hoặc thấp hơn mặt bằng chung. Đây là điểm nhấn giúp doanh nghiệp, nhà quản lý ngành xác định giai đoạn phát triển và chiến lược đầu tư hợp lý theo chu kỳ thị trường phim.

4.14 Biểu đồ trực quan hóa tần suất phim theo quốc gia

df_country <- movies_dataset %>%
  group_by(Country) %>%
  summarise(SoLuong = n()) %>%
  arrange(desc(SoLuong))   
ggplot(df_country, aes(x = reorder(Country, SoLuong), y = SoLuong, fill = Country)) +
  geom_col(width = 0.7, color = "black", show.legend = FALSE) +
  geom_text(aes(label = comma(SoLuong)),
            hjust = -0.1, size = 4, fontface = "bold", color = "black") +
  coord_flip() +
  labs(
    title = "Số lượng phim sản xuất theo quốc gia",
    subtitle = "Phân bố số lượng phim của 10 quốc gia trong tập dữ liệu",
    x = "Quốc gia",
    y = "Số lượng phim (bộ)" ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", size = 20, 
                              color = "#1B1F8A", hjust = 0.5),
    plot.subtitle = element_text(size = 13, 
                                 color = "gray30", hjust = 0.5, face = "bold"),
    axis.title = element_text(size = 14, face = "bold"),
    axis.text.y = element_text(size = 12, face = "bold"),
    axis.text.x = element_text(size = 12),
    plot.margin = margin(20, 30, 20, 30)) +
  scale_y_continuous(labels = comma_format()) +
  scale_fill_brewer(palette = "Set3") +
  expand_limits(y = max(df_country$SoLuong) * 1.1)

Giải thích:

Dòng 1-4: Tiền xử lý dữ liệu – nhóm theo Country, đếm số lượng phim mỗi nước, sắp xếp giảm dần (lấy top quốc gia có nhiều phim nhất).

Dòng 5-9: Tạo biểu đồ với ggplot: cột ngang thể hiện số lượng phim từng quốc gia (geom_col), gán nhãn số lên cột (geom_text), đổi chiều biểu đồ (trục số nằm ngang bằng coord_flip).

Dòng 10-15: Thêm tiêu đề, phụ đề, tên trục x/y và tuỳ chỉnh giao diện (font, màu, căn giữa, co giãn margin) bằng hàm labs và theme.

Dòng 16-27: Hoàn thiện giao diện bằng các tuỳ chỉnh theme nâng cao (cỡ chữ, màu sắc, căn chỉnh tiêu đề, phụ đề, trục, căn lề, đảm bảo đồ hoạ sắc nét khi xuất báo cáo).

Nhận xét:

Biểu đồ cột ngang thể hiện rất rõ sự chênh lệch số lượng phim giữa các quốc gia. Mỹ vượt trội toàn bộ với hơn 714,500 phim; tiếp theo là Anh hơn 51,000, Ấn Độ khoảng 51,000, còn các nước khác đều dưới 41,000 phim.

Xu hướng ngành phim tập trung vào quốc gia có nền công nghiệp điện ảnh mạnh, đặc biệt Mỹ chiếm thị phần lớn nhất. Các nước như UK, Ấn Độ, Canada, Trung Quốc cũng có tiềm lực, nhưng quy mô nhỏ hơn rất nhiều.

4.15 Biểu đồ tròn thể hiện doanh thu Mỹ so với thế giới

pie_df <- data.frame(
  DoanhThu = c("Mỹ", "Phần còn lại thế giới"),
  Value = c(
    sum(movies_dataset$US_BoxOfficeUSD, na.rm = TRUE),
    sum(movies_dataset$Global_BoxOfficeUSD, na.rm = TRUE) - 
      sum(movies_dataset$US_BoxOfficeUSD, na.rm = TRUE))) %>%
  mutate(TiLe = Value / sum(Value))

ggplot(pie_df, aes(x = "", y = TiLe, fill = DoanhThu)) +
  geom_col(width = 1, color = "white") +
  coord_polar(theta = "y", start = 0) +
  geom_text(aes(label = paste0(round(TiLe * 100, 1), "%")),
            position = position_stack(vjust = 0.5),
            color = "black", size = 5, fontface = "bold") +
  labs(
    title = "Tỉ lệ doanh thu Mỹ so với doanh thu toàn cầu",
    subtitle = "So sánh mức đóng góp của thị trường nội địa Mỹ trong
    tổng doanh thu phim toàn thế giới",
    fill = "Khu vực" ) +
  scale_fill_brewer(palette = "Set2") +
  theme_void(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", size = 20, color = "#2B2EBE", hjust = 0.5),
    plot.subtitle = element_text(size = 13, color = "gray25", hjust = 0.5, face = "bold"),
    legend.title = element_text(face = "bold", size = 13),
    legend.text = element_text(size = 12, face = "bold"),
    plot.margin = margin(20, 30, 20, 30))

Giải thích:

Dòng 1-7: Tiền xử lý tạo bảng dữ liệu tổng hợp cho biểu đồ tròn: tạo data frame gồm hai nhóm “Mỹ” và “Phần còn lại thế giới”, tính tổng doanh thu phòng vé tại Mỹ (US_BoxOfficeUSD), tổng doanh thu phim toàn cầu (Global_BoxOfficeUSD), rồi chuẩn hóa thành tỷ lệ phần trăm từng vùng so với tổng.

Dòng 9-14: Dựng biểu đồ tròn bằng ggplot: dùng geom_col tạo lát, chuyển sang biểu đồ tròn với coord_polar, thêm nhãn phần trăm lên từng lát (geom_text) giúp trực quan và so sánh rõ rệt.

Dòng 15-27: Tuỳ chỉnh giao diện: thiết lập tiêu đề, phụ đề, tên chú thích màu (labs), chọn palette màu nổi bật cho vùng biểu đồ, xóa nền biểu đồ (theme_void), căn chỉnh font chữ, màu sắc tiêu đề, legend và margin cho biểu đồ sắc nét.

Nhận xét:

Biểu đồ tròn thể hiện doanh thu phòng vé của Mỹ chiếm 55% tổng doanh thu phim toàn cầu, phần còn lại thế giới là 45%. Điều này cho thấy thị trường điện ảnh Mỹ có sức ảnh hưởng cực lớn, đóng vai trò chủ lực trong ngành công nghiệp phim toàn cầu về mặt kinh tế.

Phân chia lát rõ ràng, màu sắc tách biệt làm nổi bật đôi bên giúp so sánh nhanh, nhận diện vai trò vượt trội của Mỹ trên bản đồ doanh thu điện ảnh thế giới. Đây là điểm cần nhấn mạnh khi trình bày báo cáo hoặc phân tích tiềm lực thị trường phim.

4.16 Biểu đồ thể hiện doanh thu trung bình toàn cầu trong các dịp lễ qua từng thập kĩ

library(ggplot2)
library(dplyr)
library(scales)
holiday_summary <- movies_dataset %>%
  filter(Holiday != "Không có") %>%
  group_by(ThapKy, Holiday) %>%
  summarise(
    MeanRevenue = mean(Global_BoxOfficeUSD, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  group_by(ThapKy) %>%
  mutate(MaxRev = MeanRevenue == max(MeanRevenue)) %>%  
  ungroup()
ggplot(holiday_summary, aes(x = ThapKy, y = MeanRevenue, fill = Holiday)) +
  geom_col(position = position_dodge(width = 0.8), width = 0.7, alpha = 0.9, color = "black") +
  geom_text(
    data = filter(holiday_summary, MaxRev),
    aes(label = paste0("$", format(round(MeanRevenue / 1e6, 1), big.mark = ","), "M")),
    position = position_dodge(width = 0.8),
    vjust = -0.4, size = 4, fontface = "bold", color = "black"
  ) +
  geom_hline(yintercept = mean(holiday_summary$MeanRevenue, na.rm = TRUE),
             color = "darkgreen", linetype = "dashed", size = 1) +
  scale_y_continuous(labels = label_dollar(scale = 1, suffix = " USD", big.mark = ",")) +
  scale_fill_brewer(palette = "Set2") +
  labs(
    title = "Doanh thu trung bình toàn cầu của phim phát hành vào dịp lễ",
    subtitle = "So sánh theo từng thập kỷ và loại ngày lễ 
    (không bao gồm phim không ra mắt dịp lễ)",
    x = "Thập kỷ",
    y = "Doanh thu trung bình (USD)",
    fill = "Ngày lễ"
  ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5, size = 20, color = "#2B2EBE"),
    plot.subtitle = element_text(hjust = 0.5, size = 14, color = "gray25", face = "bold"),
    axis.title = element_text(size = 14, face = "bold"),
    axis.text.x = element_text(size = 12, face = "bold", angle = 25, vjust = 0.9, hjust = 0.9),
    axis.text.y = element_text(size = 12, face = "bold"),
    legend.position = "bottom",
    legend.title = element_text(face = "bold", size = 13),
    legend.text = element_text(size = 12, face = "bold"),
    plot.margin = margin(20, 30, 20, 30))

Giải thích:

Dòng 4-13: Tiền xử lý: lọc ra phim có ngày lễ, nhóm theo thập kỷ và ngày lễ rồi tính trung bình doanh thu toàn cầu từng nhóm; đánh dấu nhóm có doanh thu cao nhất mỗi thập kỷ.

Dòng 14-21: Dựng biểu đồ cột: trục x là thập kỷ, trục y là doanh thu trung bình USD, màu thể hiện ngày lễ; chỉnh độ rộng, màu viền cột. Thêm nhãn doanh thu lên cột lớn nhất mỗi thập kỷ (geom_text).

Dòng 22-23: Vẽ đường ngang thể hiện giá trị doanh thu trung bình tất cả nhóm bằng geom_hline nét đứt xanh lá (làm mốc so sánh).

Dòng 24-25: Format trục y theo kiểu tiền tệ USD, thêm bảng màu chia rõ nhóm ngày lễ.

Dòng 26-44: Tuỳ chỉnh tiêu đề, nhãn trục, chú thích, phông chữ, màu sắc, căn lề và kích thước các thành phần để biểu đồ rõ ràng, nổi bật.

Nhận xét: Doanh thu trung bình toàn cầu của phim phát hành vào các dịp lễ duy trì ở mức cao, ổn định qua nhiều thập kỷ. Các dịp nổi bật như Giáng sinh, New Year thường có doanh thu vượt mức trung bình toàn bộ ngành. Sự chênh lệch giữa các loại ngày lễ không lớn qua các giai đoạn, nhưng xu hướng cho thấy phim phát hành dịp lễ lớn thường mang lại hiệu quả kinh tế tốt hơn so với các dịp nhỏ. Biểu đồ khẳng định vai trò quan trọng của thời điểm phát hành phim đối với kết quả doanh thu phòng vé.

4.17 Biểu đồ thể hiện phân bố điểm IMDb theo từng thập kỹ

rating_summary <- movies_dataset %>%
  group_by(ThapKy) %>%
  summarise(MeanRating = mean(IMDbRating_100, na.rm = TRUE))
ggplot(rating_summary, aes(x = ThapKy, y = MeanRating, group = 1)) +
  geom_line(color = "#1B1F8A", size = 1.2) +                        
  geom_point(size = 3.8, color = "#E57373") +                        
  geom_text(aes(label = round(MeanRating, 2)),                      
            vjust = -0.7, size = 4.2, fontface = "bold", 
            color = "black") +
  geom_hline(yintercept = mean(rating_summary$MeanRating, na.rm = TRUE), 
             color = "darkgreen", linetype = "dashed", size = 1) +  
  geom_smooth(method = "lm", se = FALSE, linetype = "dotted", size = 0.9, color = "gray40") +  
  labs(
    title = "Xu hướng điểm IMDb trung bình qua các Thập kỷ",
    subtitle = "Thể hiện sự thay đổi chất lượng phim được
    đánh giá bởi khán giả theo thời gian",
    x = "Thập kỷ",
    y = "Điểm IMDb trung bình (thang 100)" ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5, size = 20, color = "#1B1F8A"),
    plot.subtitle = element_text(hjust = 0.5, size = 14, color = "gray25", 
                                 face = "bold"),
    axis.title = element_text(size = 14, face = "bold"),
    axis.text.x = element_text(angle = 25, vjust = 0.9, hjust = 0.9, size = 12, face = "bold"),
    axis.text.y = element_text(size = 12, face = "bold"),
    plot.margin = margin(20, 30, 20, 30) )

Giải thích:

Dòng 1-3: Tiền xử lý dữ liệu: Nhóm dữ liệu phim theo thập kỷ (ThapKy), tính điểm IMDb trung bình (MeanRating) cho từng nhóm bằng hàm mean(IMDbRating_100, na.rm = TRUE), kết quả lưu tại rating_summary.

Dòng 4: Khởi tạo đối tượng biểu đồ bằng ggplot. Thiết lập trục x là thập kỷ, trục y là điểm trung bình, nhóm duy nhất (group = 1) để nối các điểm liên tiếp.

Dòng 5-6: Vẽ đường nối các thập kỷ bằng geom_line (màu xanh dương, dày nét). Đánh dấu từng thập kỷ bằng chấm tròn đỏ trên đường (geom_point).

Dòng 7-9: Gắn nhãn giá trị điểm IMDb trung bình lên mỗi chấm bằng geom_text, làm nổi bật số liệu từng năm. Vị trí, font và màu chữ được chỉnh cho dễ đọc.

Dòng 10-12: Vẽ đường ngang nét đứt màu xanh lá tại giá trị trung bình toàn bộ các thập kỷ bằng geom_hline (làm mốc so sánh) và thêm đường xu hướng tuyến tính giúp minh họa chiều hướng tổng thể của điểm IMDb qua thời gian.

Dòng 13-27: Cấu hình nội dung trình bày biểu đồ: thiết lập tiêu đề, phụ đề, tên trục x/y (labs), giao diện tối giản theme_minimal() và chỉnh các thuộc tính theme giúp biểu đồ rõ ràng.

Nhận xét:

Điểm IMDb trung bình qua các thập kỷ dao động nhẹ quanh ngưỡng 64.89–65.01, không có giai đoạn vượt trội hoặc giảm mạnh, chứng tỏ chất lượng phim ổn định lâu dài. Đường xu hướng tổng thể hơi đi lên ở các thập kỷ cuối, phản ánh xu thế cải thiện nhẹ về chất lượng phim theo đánh giá khán giả.

Đường ngang màu xanh lá giúp nhận diện nhanh thập kỷ nào vượt/trung bình ngành, thuận tiện phân tích hoặc thuyết trình xu hướng điện ảnh qua các thời kỳ.

4.18 Biểu đồ thể hiện số lượng phim trong các ngày lễ theo từng châu lục

holiday_distribution <- movies_dataset %>%
  filter(Holiday != "Không có") %>%
  group_by(Continent, Holiday) %>%
  summarise(NumMovies = n(), .groups = "drop")
ggplot(holiday_distribution, aes(x = Continent, y = NumMovies, fill = Holiday)) +
  geom_bar(stat = "identity", color = "black", width = 0.75) +
  geom_text(
    aes(label = ifelse(NumMovies > 1000, NumMovies, "")), 
    position = position_stack(vjust = 0.5),
    size = 4,
    color = "black",       
    fontface = "bold") +
  labs(
    title = "Số lượng phim phát hành trong các ngày lễ theo từng Châu lục",
    subtitle = "So sánh mức độ phát hành phim vào dịp lễ giữa các khu vực địa lý",
    x = "Châu lục",
    y = "Số lượng phim (bộ)",
    fill = "Ngày lễ" ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5, size = 20, color = "black"),
    plot.subtitle = element_text(hjust = 0.5, size = 14, color = "gray25", face = "bold"),
    axis.title = element_text(size = 14, face = "bold"),
    axis.text.x = element_text(angle = 25, vjust = 0.9, hjust = 0.9, size = 12, face = "bold"),
    axis.text.y = element_text(size = 12, face = "bold"),
    legend.position = "bottom",
    legend.title = element_text(size = 13, face = "bold"),
    legend.text = element_text(size = 12, face = "bold"),
    plot.margin = margin(20, 30, 20, 30)) +
  scale_fill_brewer(palette = "Set2")

Giải thích:

Dòng 1–4 (Tiền xử lý và tổng hợp dữ liệu): Từ bảng movies_dataset, tôi loại bỏ các phim không có thông tin ngày lễ , sau đó nhóm dữ liệu theo châu lục và dịp lễ , cuối cùng đếm số lượng phim từng nhóm bằng hàm n() và bỏ lớp nhóm sau tổng hợp. Kết quả lưu vào bảng holiday_distribution.

Dòng 5–6 (Vẽ biểu đồ cột): Khởi tạo biểu đồ với ggplot dựa trên bảng vừa tổng hợp. Hàm geom_bar dựng biểu đồ cột, biểu diễn số phim theo từng châu lục, phân biệt các dịp lễ bằng màu sắc và viền đen cho từng cột để dễ quan sát (dòng 11).

Dòng 7–12 (Thêm nhãn giá trị lớn): Sử dụng geom_text, chèn nhãn số lượng phim lên các nhóm từ 1.000 phim trở lên để làm nổi bật chênh lệch lớn, giúp người xem nhận biết trực tiếp các châu lục có quy mô vượt trội.

Dòng 13–19 (Tiêu đề, nhãn trục, ghi chú): Cụm labs đặt tiêu đề, phụ đề, tên trục x/y và tên cho chú thích màu sắc, đảm bảo biểu đồ đầy đủ ngữ nghĩa.

Dòng 20–30 (Tùy chỉnh giao diện): Sử dụng theme tối giản, chỉnh font chữ, kích thước tiêu đề, nhãn trục, chữ trên trục hoành, và vị trí chú thích.

Nhận xét: Biểu đồ cho thấy số lượng phim phát hành vào dịp lễ ở khu vực Americas vượt trội so với các khu vực khác. Những dịp như Giáng sinh, Năm mới và Halloween chiếm tỷ trọng lớn nhất tại đây. Châu Á, châu Âu và châu Đại Dương có quy mô phát hành phim vào lễ thấp hơn, điều này phản ánh sự chênh lệch về chiến lược sản xuất phim giữa các khu vực địa lý.

4.19 Biểu đồ thể hiện doanh thu toàn cầu của các nước trong giai đoạn COVID 19

covid_years <- c(2020, 2021, 2022)
covid_data <- movies_dataset %>%
  filter(ReleaseYear %in% covid_years) %>%
  group_by(Country, ReleaseYear) %>%
  summarise(TotalGlobalRevenue = sum(Global_BoxOfficeUSD, na.rm = TRUE), .groups = 'drop')
ggplot(covid_data, aes(x = Country, y = TotalGlobalRevenue, fill = factor(ReleaseYear))) +
  geom_bar(stat = "identity", position = "dodge", color = "black") +
  scale_y_continuous(labels = label_dollar(scale = 1, suffix = " USD")) +
  labs(
    title = "Doanh thu toàn cầu theo quốc gia trong giai đoạn COVID-19",
    subtitle = "So sánh tổng doanh thu phòng vé (USD) giữa các quốc gia từ 2020–2022",
    x = "Quốc gia",
    y = "Tổng doanh thu (USD)",
    fill = "Năm") +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5, size = 20, 
                              color = "black"),
    plot.subtitle = element_text(hjust = 0.5, size = 14, 
                                 color = "gray25", 
                                 face = "bold"),
    axis.title = element_text(size = 14, face = "bold"),
    axis.text.x = element_text(angle = 40, vjust = 0.9, 
                               hjust = 0.9, size = 12, 
                               face = "bold"),
    axis.text.y = element_text(size = 12, face = "bold"),
    legend.position = "right",
    legend.title = element_text(face = "bold", size = 13),
    legend.text = element_text(size = 12, face = "bold"),
    plot.margin = margin(20, 30, 20, 30)) +
  scale_fill_brewer(palette = "Set1")

Giải thích:

Dòng 1–5: Khai báo danh sách các năm COVID-19. Lọc tập phim phát hành trong giai đoạn này, nhóm theo quốc gia và năm, sau đó tính tổng doanh thu phòng vé toàn cầu từng nhóm.

Dòng 6–8: Khởi tạo biểu đồ với dữ liệu vừa tổng hợp, sử dụng geom_bar để vẽ cột doanh thu theo quốc gia, nhóm màu đại diện từng năm, viền đen và các cột cùng quốc gia nằm cạnh nhau.

Dòng 9–31: Cấu hình tiêu đề, nhãn trục, chú thích màu cho các năm, theme tối giản, tăng kích cỡ chữ, căn chỉnh vị trí các thành phần và dùng bảng màu “Set1” giúp phân biệt rõ các nhóm năm.

Nhận xét: Biểu đồ thể hiện mức doanh thu phòng vé toàn cầu của từng quốc gia trong giai đoạn COVID-19 từ năm 2020 đến 2022. Mỹ dẫn đầu tuyệt đối về doanh thu, áp đảo rõ rệt so với các quốc gia còn lại. Các nước như Anh, Ấn Độ, Trung Quốc, Nhật Bản và Canada có doanh thu đáng kể nhưng vẫn thấp hơn nhiều so với Mỹ. Doanh thu của phần lớn các quốc gia giữ ổn định hoặc chênh lệch nhẹ qua ba năm khảo sát. Sự chênh lệch lớn này cho thấy ngành điện ảnh Mỹ chi phối mạnh thị trường toàn cầu về mặt kinh tế, trong khi các quốc gia khác chưa có sự bứt phá đáng kể về quy mô doanh thu phòng vé trong giai đoạn ảnh hưởng của đại dịch

4.20 Biểu đồ trực quan lợi nhuận trung bình từng thể loại qua thập kỉ

profit_summary <- movies_dataset %>%
  group_by(ThapKy, Genre) %>%
  summarise(mean_profit = mean(ProfitUSD, na.rm = TRUE))
ggplot(profit_summary, aes(x = ThapKy, y = mean_profit, color = Genre, group = Genre)) +
  geom_line(size = 1.1) +                                  
  geom_point(size = 3.5) +                                 
  geom_text(aes(label = round(mean_profit / 1e6, 1)),      
            vjust = -0.6, size = 4.2, fontface = "bold", 
            show.legend = FALSE) +
  geom_hline(yintercept = mean(profit_summary$mean_profit, na.rm = TRUE),
             color = "darkgreen", linetype = "dashed", size = 1) + 
  geom_smooth(method = "lm", se = FALSE, linetype = "dotted", size = 0.9) + 
  labs(
    title = "Lợi nhuận trung bình theo Thể loại phim qua các Thập kỷ",
    subtitle = "So sánh sự thay đổi lợi nhuận (USD) 
    giữa các thể loại phim theo thời gian",
    x = "Thập kỷ",
    y = "Lợi nhuận trung bình (USD)",
    color = "Thể loại phim"  ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5, 
                              size = 20, color = "black"),
    plot.subtitle = element_text(hjust = 0.5, size = 14, 
                                 color = "gray25", face = "bold"),
    axis.title = element_text(size = 14, face = "bold"),
    axis.text.x = element_text(angle = 25, vjust = 0.9, 
                               hjust = 0.9, size = 12, face = "bold"),
    axis.text.y = element_text(size = 12, face = "bold"),
    legend.position = "bottom",
    legend.title = element_text(face = "bold", size = 13),
    legend.text = element_text(size = 12, face = "bold"),
    plot.margin = margin(20, 30, 20, 30)) +
  scale_y_continuous(labels = label_number(scale_cut = cut_short_scale())) +
  scale_color_brewer(palette = "Set2")

Giải thích:

Dòng 1–3: Tính lợi nhuận trung bình cho từng thập kỷ (ThapKy) và thể loại phim (Genre) bằng cách nhóm dữ liệu và lấy trung bình biến ProfitUSD (loại bỏ giá trị thiếu). Kết quả lưu vào bảng profit_summary.

Dòng 4–12: Tạo biểu đồ đường thể hiện biến động lợi nhuận trung bình qua các thập kỷ và từng thể loại phim. Sử dụng geom_line để vẽ đường, geom_point để đánh dấu từng điểm dữ liệu, và geom_text hiển thị nhãn giá trị lên biểu đồ (làm tròn theo triệu USD). Thêm đường ngang là mức lợi nhuận trung bình toàn bộ bằng geom_hline và đường xu hướng bằng geom_smooth. Các màu phân biệt theo thể loại phim.

Dòng 13–20: Đặt tiêu đề, phụ đề, nhãn trục và tên chú thích bằng labs. Áp dụng theme tối giản và điều chỉnh các thuộc tính trực quan như font chữ, kích thước, căn lề và màu sắc cho các thành phần biểu đồ.

Dòng 21–35: Điều chỉnh chi tiết cấu hình trục, nhãn trục hoành, chú thích, margin và định dạng giá trị trục y. Dùng palette màu “Set2” để thể loại phim dễ phân biệt.

Nhận xét: Biểu đồ cho thấy lợi nhuận trung bình từng thể loại phim thay đổi theo thời gian và giữa các thập kỷ không đồng nhất. Một số thể loại như Documentary và Sci-Fi có biến động lợi nhuận lớn qua các thập kỷ, trong khi các thể loại còn lại dao động quanh mức trung bình toàn tập (đường ngang xanh lá). Xu hướng lợi nhuận trung bình nhìn chung ổn định nhưng có sự khác biệt rõ rệt giữa các nhánh thể loại. Điều này phản ánh ảnh hưởng của bối cảnh kinh tế, thị hiếu khán giả và các yếu tố đặc thù từng giai đoạn đối với hiệu quả tài chính của từng dòng phim.

PHẦN 2: PHÂN TÍCH BỘ DỮ LIỆU BÁO CÁO TÀI CHÍNH MÃ CHỨNG KHOÁN ACL

CHƯƠNG 1: GIỚI THIỆU VỀ BỘ DỮ LIỆU VỀ MÃ CHỨNG KHOÁN ACL

1.1 Giới thiệu tổng quan

Tên bộ dữ liệu: “Báo cáo tài chính của Công ty Cổ phần Thủy sản Cửu Long An Giang (ACL)”.

Nguồn bộ dữ liệu: Dữ liệu được tổng hợp từ Báo cáo tài chính các năm của ACL do Sở Giao dịch Chứng khoán TP. Hồ Chí Minh (HOSE) công bố.

Thông tin bộ dữ liệu:

Bộ dữ liệu này bao gồm các chỉ tiêu tài chính được trích xuất từ bảng cân đối kế toán của Công ty Cổ phần Thủy sản Cửu Long An Giang (mã chứng khoán: ACL).

Các chỉ tiêu thể hiện tình hình tài sản, nguồn vốn, nợ phải trả, và vốn chủ sở hữu của doanh nghiệp qua nhiều năm.

Dữ liệu được thu thập theo năm, giúp phản ánh sự thay đổi trong cơ cấu tài chính và hiệu quả hoạt động của công ty trong giai đoạn nghiên cứu.

Mục đích lấy dữ liệu: Dữ liệu được sử dụng để thực hiện các thao tác phân tích, xử lý, trực quan hóa và tính toán các chỉ tiêu tài chính bằng ngôn ngữ lập trình R.Mục tiêu là đánh giá tình hình tài chính, khả năng thanh toán và mức độ ổn định của doanh nghiệp ACL qua các năm.

Nhóm sử dụng bộ dữ liệu này nhằm: Thực hành kỹ năng phân tích và xử lý dữ liệu tài chính thực tế bằng ngôn ngữ R.Tính toán và so sánh các chỉ tiêu tài chính quan trọng của doanh nghiệp (tài sản, nợ, vốn, tỷ suất,…).Áp dụng các kỹ thuật mô tả, biểu đồ, và thống kê để rút ra nhận xét, xu hướng và kết luận về tình hình tài chính của doanh nghiệp.

1.2 Thông tin cơ bản về bộ dữ liệu

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

library(readxl)
baocaotc_dataset <- read_excel("D:/Tieu luan NNLT/data_ck.xlsx")

Giải thích: Lệnh read_excel được sử dụng để đọc file dữ liệu định dạng xlsx vào R và tạo ra một data frame tên baocaotc_dataset. Đây là thao tác khởi đầu cho mọi phân tích dữ liệu, giúp biến dữ liệu từ file bên ngoài thành đối tượng để thao tác trong R

dim(baocaotc_dataset)
## [1] 44 14

Giải thích: Trả về số quan sát là 44 dòng và số biến là 14 cột trong bộ dữ liệu. Việc này giúp kiểm tra quy mô bộ dữ liệu và xác định ngay có đủ dữ liệu để phân tích chuyên sâu không

1.2.2 Tên các biến của bộ dữ liệu

colnames(baocaotc_dataset)
##  [1] "Tiêu chí"                           "A- TÀI SẢN NGẮN HẠN"               
##  [3] "Tiền và các khoản tương đương tiền" "Các khoản phải thu ngắn hạn"       
##  [5] "B. TÀI SẢN DÀI HẠN"                 "Tài sản cố định hữu hình"          
##  [7] "Tài sản cố định vô hình"            "C. NỢ PHẢI TRẢ"                    
##  [9] "Nợ ngắn hạn"                        "Phải trả người bán ngắn hạn"       
## [11] "Phải trả người lao động"            "D.VỐN CHỦ SỞ HỮU"                  
## [13] "Vốn góp của chủ sở hữu"             "Lợi nhuận sau thuế chưa phân phối"

Giải thích: Sử dụng hàm colnames(baocaotc_dataset) để liệt kê toàn bộ tên biến (cột) của data frame baocaotc_dataset vừa tạo.

1.2.3 Đổi tên các biến trong bộ dữ liệu

colnames(baocaotc_dataset) <- c("Quy","TSNH","Tien_Tuong_Duong",   "Khoan_Thu_Ngan_Han", "TSDH","TSCD_HH","TSCD_VH","No_Phai_Tra",      "No_Ngan_Han", "Phai_Tra_BN","Phai_Tra_LD","VCSH", "Von_Gop_CSH",   "LNST")

Giải thích: Sử dụng hàm colnames() để đổi tên tất cả các biến của data frame baocaotc_dataset thành một danh sách mới, giúp các tên biến ngắn gọn, dễ nhớ và phản ánh đúng nội dung từng trường dữ liệu trong bộ dữ liệu tài chính.

Nhận xét: Đoạn lệnh này hỗ trợ chuẩn hóa tên biến, tạo thuận lợi khi thao tác, kiểm tra và trình bày kết quả phân tích sau này.

1.2.4 Kiểm tra qua các dòng đầu tiên của bộ dữ liệu

head(baocaotc_dataset)

Giải thích: head(): Hàm này sẽ hiển thị các dòng đầu tiên của bộ dữ liệu. Mặc định nó sẽ hiển thị 6 dòng đầu tiên, nhưng bạn có thể thay đổi tham số để chọn số dòng muốn hiển thị.

1.2.5 Kiểm tra qua các dòng cuối của bộ dữ liệu

tail(baocaotc_dataset)

Giải thích: tail(): Hàm này được sử dụng để hiển thị các dòng cuối của bộ dữ liệu.

1.2.6 Số lượng biến định tính và định lượng

sum(sapply(baocaotc_dataset, is.numeric))
## [1] 13
sum(sapply(baocaotc_dataset, is.character) | sapply(baocaotc_dataset, is.factor))
## [1] 1

Giải thích:

sum(sapply(baocaotc_dataset, is.numeric)) — Đếm số lượng cột trong bảng dữ liệu baocaotc_dataset có kiểu dữ liệu số (numeric).Kết quả có 13 biến định lượng

sum(sapply(baocaotc_dataset, is.character) | sapply(baocaotc_dataset, is.factor)) — Đếm số lượng cột có kiểu dữ liệu chuỗi (character) hoặc phân loại (factor). Kết quả có 1 biến định tính

1.2.7 Kiểu dữ liệu của các biến trong bộ dữ liệu

library(knitr)
loai <- data.frame( Kieu_du_lieu = sapply(baocaotc_dataset , class))
kable(loai, col.names = c("Tên biến" , "Kiểu dữ liệu"))
Tên biến Kiểu dữ liệu
Quy character
TSNH numeric
Tien_Tuong_Duong numeric
Khoan_Thu_Ngan_Han numeric
TSDH numeric
TSCD_HH numeric
TSCD_VH numeric
No_Phai_Tra numeric
No_Ngan_Han numeric
Phai_Tra_BN numeric
Phai_Tra_LD numeric
VCSH numeric
Von_Gop_CSH numeric
LNST numeric

Giải thích: Dòng 2 và 3: Tạo data frame mới tên loai, trong đó chứa loại (class) của từng biến trong baocaotc_dataset bằng cách dùng sapply() duyệt toàn bộ các cột. Xuất bảng phân loại biến với kable(), hiển thị tên biến và kiểu dữ liệu tương ứng, dễ theo dõi khi trình bày trong báo cáo.

1.2.8 Kiểm tra các giá trị bị thiếu của bộ dữ liệu

colSums(is.na(baocaotc_dataset))
##                Quy               TSNH   Tien_Tuong_Duong Khoan_Thu_Ngan_Han 
##                  0                  0                  0                  0 
##               TSDH            TSCD_HH            TSCD_VH        No_Phai_Tra 
##                  0                  0                  0                  0 
##        No_Ngan_Han        Phai_Tra_BN        Phai_Tra_LD               VCSH 
##                  0                  0                  0                  0 
##        Von_Gop_CSH               LNST 
##                  0                  0

Giải thích: is.na(): Kiểm tra các giá trị thiếu (NA) trong bộ dữ liệu kết hợp với colSums(): Tính tổng số giá trị thiếu trong mỗi cột của bộ dữ liệu.

1.2.9 Kiểm tra quan sát bị trùng lặp

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

Giải thích: sum(duplicated()) giúp kiểm tra toàn bộ các dòng của bộ dữ liệu, hàm duplicated() trả về giá trị TRUE nếu dòng đó trùng lặp với dòng trước, sau đó hàm sum() cộng tất cả các giá trị TRUE để đếm tổng số dòng trùng lặp.

Nhận xét: Đoạn code xác định số quan sát bị trùng lặp, đảm bảo dữ liệu không chứa dòng lặp trước khi phân tích.

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

variable_meaning_ck <- data.frame(
variable_ck = c("Quy", "TSNH", "Tien_Tuong_Duong", "Khoan_Thu_Ngan_Han", "TSDH",
"TSCD_HH", "TSCD_VH", "No_Phai_Tra", "No_Ngan_Han", "Phai_Tra_BN",
"Phai_Tra_LD", "VCSH", "Von_Gop_CSH", "LNST"),
Meaning_ck = c(
"Thời gian (Quý) của bộ dữ liệu tài chính",
"Tài sản ngắn hạn (nghìn đồng)",
"Tiền và các khoản tương đương tiền (nghìn đồng)",
"Khoản thu ngắn hạn (nghìn đồng)",
"Tài sản dài hạn (nghìn đồng)",
"Tài sản cố định hữu hình (nghìn đồng)",
"Tài sản cố định vô hình (nghìn đồng)",
"Nợ phải trả (nghìn đồng)",
"Nợ ngắn hạn (nghìn đồng)",
"Phải trả người bán ngắn hạn (nghìn đồng)",
"Phải trả người lao động (nghìn đồng)",
"Vốn chủ sở hữu (nghìn đồng)",
"Vốn góp của chủ sở hữu (nghìn đồng)",
"Lợi nhuận sau thuế chưa phân phối (nghìn đồng)"
),
stringsAsFactors = FALSE
)
kable(variable_meaning_ck, col.names = c("Tên biến", "Ý nghĩa"))
Tên biến Ý nghĩa
Quy Thời gian (Quý) của bộ dữ liệu tài chính
TSNH Tài sản ngắn hạn (nghìn đồng)
Tien_Tuong_Duong Tiền và các khoản tương đương tiền (nghìn đồng)
Khoan_Thu_Ngan_Han Khoản thu ngắn hạn (nghìn đồng)
TSDH Tài sản dài hạn (nghìn đồng)
TSCD_HH Tài sản cố định hữu hình (nghìn đồng)
TSCD_VH Tài sản cố định vô hình (nghìn đồng)
No_Phai_Tra Nợ phải trả (nghìn đồng)
No_Ngan_Han Nợ ngắn hạn (nghìn đồng)
Phai_Tra_BN Phải trả người bán ngắn hạn (nghìn đồng)
Phai_Tra_LD Phải trả người lao động (nghìn đồng)
VCSH Vốn chủ sở hữu (nghìn đồng)
Von_Gop_CSH Vốn góp của chủ sở hữu (nghìn đồng)
LNST Lợi nhuận sau thuế chưa phân phối (nghìn đồng)

Giải thích:

Dòng 1–20: Khởi tạo bảng chú giải ý nghĩa biến trong bộ dữ liệu tài chính bằng cách kết hợp hai vector: variable_ck và Meaning_ck . Hai vector này được ghép thành data frame variable_meaning_ck, hỗ trợ đối chiếu biến và nội dung phân tích trong các bước kế tiếp.

Dòng 23: Sử dụng hàm kable() để xuất bảng variable_meaning_ck với hai cột “Tên biến” và “Ý nghĩa”.

CHƯƠNG 2: XỬ LÝ THÔ VÀ MÃ HÓA BỘ DỮ LIỆU

2.1 Kiểm tra các giá trị bị thiếu và số lượng giá trị bị thiếu của 1 cột

any(is.na(baocaotc_dataset))
## [1] FALSE

Giải thích: Sử dụng hàm is.na(baocaotc_dataset) để kiểm tra toàn bộ bảng dữ liệu, trả về một ma trận giá trị TRUE nếu xuất hiện NA (giá trị thiếu), FALSE nếu không kết hợp hàm any() kiểm tra xem trong ma trận trên có ô nào là NA không. Nếu có một giá trị thiếu bất kỳ sẽ trả về TRUE, nếu dữ liệu đầy đủ sẽ trả về FALSE.

Nhận xét: Đoạn code này giúp xác định nhanh toàn bộ dữ liệu có xuất hiện giá trị thiếu (NA) ở bất kỳ cột/dòng nào hay không, từ đó đánh giá mức độ sạch của dữ liệu trước khi phân tích.

2.2 Tạo ra biến Năm từ Quý

baocaotc_dataset$Nam <- substr(baocaotc_dataset$Quy, 1, 4)

Giải thích: Tạo biến mới Nam(Năm) bằng cách sử dụng hàm substr() để trích xuất 4 ký tự đầu từ biến Quy(Quý) trong bảng dữ liệu baocaotc_dataset.

Nhận xét: Đoạn code này giúp lấy thông tin năm từ giá trị chuỗi dạng quý (ví dụ: “2014_Q1”, “2015_Q2”), bằng cách lấy vị trí 1 đến 4 trong chuỗi (tức là năm). Biến mới tạo ra dùng để phân tích, tổng hợp hoặc nhóm dữ liệu theo năm.

2.3 Tạo ra biến Quý không bao gồm Năm

baocaotc_dataset$Quy_Tach <- substr(baocaotc_dataset$Quy, 6, 7)

Giải thích: Sử dụng hàm substr() để tách ký tự số 6 và 7 của biến chuỗi Quy (ví dụ: “2014_Q1”), tạo thành biến mới Quy_Tach chỉ chứa thông tin quý (“Q1”, “Q2”, …), loại phần năm.

Nhận xét: Kết quả tạo ra biến Quy_Tach chỉ gồm thông tin quý, tách riêng so với phần năm. Nhờ đó, việc phân tích, so sánh dữ liệu theo quý được đơn giản và linh hoạt, thuận tiện cho các mục đích tổng hợp hoặc trình bày mà không bị ảnh hưởng bởi năm.

2.4 Kiểm tra biến Năm sau khi tách từ Quy

class(baocaotc_dataset$Nam)
## [1] "character"

Giải thích: Sử dụng hàm class() để kiểm tra kiểu dữ liệu của biến Nam trong bảng dữ liệu tài chính.

Nhận xét: Mục đích: Đoạn mã này giúp kiểm tra xem biến Nam có đúng kiểu dữ liệu là character hay không (chính là kiểu chuỗi văn bản). Điều này rất quan trọng để đảm bảo rằng biến này có thể sử dụng trong các phép phân tích dữ liệu về thời gian như phân tích theo quý.

2.5 Chuyển kiểu dữ liệu biến Năm về dạng numeric

baocaotc_dataset$Nam <- as.numeric(baocaotc_dataset$Nam)
class(baocaotc_dataset$Nam)
## [1] "numeric"

Giải thích: Dòng 1: Sử dụng as.numeric() để chuyển kiểu dữ liệu của biến Nam trong baocaotc_dataset từ chuỗi (character) sang số học (numeric). Dòng 2: Dùng class() để kiểm tra lại kiểu dữ liệu của biến Nam sau khi chuyển đổi. Kết quả trả về là “numeric”.

Nhận xét: Sau khi chạy lệnh, biến Nam đã ở dạng số, có thể sử dụng cho so sánh, thống kê, phân tích xu hướng qua các năm. Chuẩn hóa kiểu dữ liệu giúp thao tác phân tích dữ liệu tài chính chính xác và linh hoạt hơn trong các bước tiếp theo.

2.6 Kiểm tra giá trị NA của biến TSNH và TSDH

sum(is.na(baocaotc_dataset$TSNH))
## [1] 0
sum(is.na(baocaotc_dataset$TSNH))
## [1] 0

Giải thích:

sum(is.na(baocaotc_dataset$TSNH)): Đếm tổng số giá trị bị thiếu (NA) trong biến TSNH của bảng dữ liệu.

sum(is.na(baocaotc_dataset$TSDH)): Đếm tổng số giá trị NA trong biến TSDH.

Nhận xét: Kết quả đều bằng 0 cho cả hai biến, khẳng định dữ liệu tại hai cột TSNH và TSDH không có giá trị thiếu. Điều này giúp đảm bảo độ đầy đủ và tin cậy khi thực hiện các bước xử lý, phân tích tiếp theo.

2.7 Tạo biến tỷ lệ Nợ trên Tài sản

baocaotc_dataset$TyLe_No_TS <- (baocaotc_dataset$No_Phai_Tra/(baocaotc_dataset$TSNH + 
                                                                baocaotc_dataset$TSDH))

Giải thích: Tạo biến mới TyLe_No_TS trong bảng dữ liệu, tính bằng tổng nợ phải trả chia cho tổng tài sản (tài sản ngắn hạn TSNH cộng tài sản dài hạn TSDH). Công thức này được gán trực tiếp vào cột mới của khung dữ liệu baocaotc_dataset.

Nhận xét: Tỷ lệ này giúp đánh giá khả năng tự chủ tài chính: Nếu TyLe_No_TS cao (hoặc trên 1), doanh nghiệp dùng nhiều nợ vay, tiềm ẩn rủi ro tài chính và khả năng thanh toán thấp. Nếu chỉ số này thấp (dưới 0.5), cho thấy doanh nghiệp chủ yếu sử dụng vốn chủ sở hữu, ít phụ thuộc vào nợ vay. Đây là chỉ tiêu then chốt để đánh giá đòn bẩy tài chính và mức độ an toàn vốn của doanh nghiệp qua các kỳ báo cáo.

2.8 Tạo biến Tổng tài sản

baocaotc_dataset$TTS <- (baocaotc_dataset$TSNH + baocaotc_dataset$TSDH)

Giải thích: Dòng code tạo biến mới TTS là tổng của hai biến: Tài sản ngắn hạn (TSNH) và Tài sản dài hạn (TSDH). Biến này được thêm trực tiếp vào bảng dữ liệu baocaotc_dataset bằng cú pháp dấu $.

Nhận xét: Đoạn code này giúp xác định tổng giá trị tài sản của doanh nghiệp tại mỗi kỳ/quý, là cơ sở cho các phân tích chỉ số tài chính như tỷ lệ tài sản, vốn, ROA…

2.9 Tạo ra biến ROE

baocaotc_dataset$ROE <- (baocaotc_dataset$LNST/baocaotc_dataset$VCSH)

Giải thích: Dòng code tạo biến mới ROE bằng cách chia Lợi nhuận sau thuế (LNST) cho Vốn chủ sở hữu (VCSH) ngay trong bảng dữ liệu. Công thức này thể hiện tỷ suất lợi nhuận trên vốn của cổ đông.

Nhận xét: Đoạn lệnh giúp tính nhanh tỷ lệ sinh lời trên vốn chủ sở hữu từng kỳ/quý, phục vụ phân tích hiệu quả sử dụng vốn doanh nghiệp.

2.10 Tạo biến ROA

baocaotc_dataset$ROA <- (baocaotc_dataset$LNST/baocaotc_dataset$TTS)

Giải thích: Dòng code tạo biến mới ROA trong bộ dữ liệu, tính bằng lợi nhuận sau thuế (LNST) chia cho tổng tài sản (TTS, gồm TSNH và TSDH).

Nhận xét: Tính toán nhanh tỷ suất sinh lời trên tài sản (ROA) cho từng quan sát, phục vụ đánh giá hiệu quả sử dụng tài sản trong doanh nghiệp theo từng kỳ.

CHƯƠNG 3: THỰC HIỆN CÁC THỐNG KÊ CƠ BẢN

3.1 Phân tích thống kê mô tả của các biến

Giải thích: Sử dụng các hàm thống kê mô tả cơ bản trong R: summary(), sd(), var() cho biến số học (numeric).

Hàm summary(): Sử dụng để thống kê nhanh các đặc trưng như: giá trị nhỏ nhất (Min), phần tư thứ nhất (Q1), trung vị (Median), giá trị trung bình (Mean), phần tư thứ ba (Q3), giá trị lớn nhất (Max). Kết quả cho biết tổng thể tình hình phân bố, trung tâm và mức lan tỏa dữ liệu.

Hàm sd(): Tính độ lệch chuẩn, phản ánh mức dao động trung bình của từng phần tử quanh giá trị trung bình. Độ lệch chuẩn càng lớn dữ liệu càng phân tán, càng nhỏ dữ liệu càng tập trung.

Hàm var(): Tính phương sai, cho biết mức độ dao động dữ liệu so với trung bình cộng; chính là bình phương của độ lệch chuẩn.

3.1.1 Biến TSNH

summary(baocaotc_dataset$TSNH)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
## 5.821e+11 8.099e+11 1.071e+12 1.047e+12 1.275e+12 1.512e+12
sd(baocaotc_dataset$TSNH)
## [1] 287054184055
var(baocaotc_dataset$TSNH)
## [1] 8.24001e+22

Nhận xét:

Biến TSNH có giá trị trung bình và trung vị gần bằng nhau, cho thấy dữ liệu phân phối cân đối.

Độ lệch chuẩn và phương sai đều cao, phản ánh tài sản ngắn hạn giữa các doanh nghiệp khá khác biệt và biến động rộng.

Dữ liệu không xuất hiện giá trị ngoại biên bất thường lớn, phù hợp để phân tích tiếp các chỉ số tài chính.

3.1.2 Biến tiền và các khoản tương đương

summary(baocaotc_dataset$Tien_Tuong_Duong)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
## 1.792e+10 3.581e+10 5.083e+10 5.750e+10 8.115e+10 1.218e+11
sd(baocaotc_dataset$Tien_Tuong_Duong)
## [1] 27077813039
var(baocaotc_dataset$Tien_Tuong_Duong)
## [1] 7.33208e+20

Nhận xét:

Biến Tiền và các khoản tương đương tiền có giá trị trung bình khoảng 5.75×10¹⁰, cho thấy doanh nghiệp duy trì lượng tiền mặt và tài sản thanh khoản cao tương đối ổn định.

Giá trị nhỏ nhất là 1.79×10¹⁰ và lớn nhất 1.22×10¹¹, thể hiện biên độ dao động khá lớn, phản ánh sự thay đổi trong chính sách quản lý tiền mặt theo từng quý.

Độ lệch chuẩn 2.71×10¹⁰ và phương sai 7.33×10²⁰ cho thấy mức độ phân tán vừa phải - tức là dòng tiền biến động nhưng vẫn trong phạm vi có kiểm soát.

3.1.3 Biến Khoản thu ngắn hạn

summary(baocaotc_dataset$Khoan_Thu_Ngan_Han )
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
## 1.058e+11 1.928e+11 2.479e+11 2.392e+11 2.838e+11 3.745e+11
sd(baocaotc_dataset$Khoan_Thu_Ngan_Han)
## [1] 68351662935
var(baocaotc_dataset$Khoan_Thu_Ngan_Han)
## [1] 4.67195e+21

Nhận xét:

Biến Khoản thu ngắn hạn có giá trị trung bình 2.39×10¹¹, trong khi giá trị nhỏ nhất là 1.06×10¹¹ và lớn nhất 3.74×10¹¹, cho thấy dao động đáng kể giữa các kỳ.

Độ lệch chuẩn 6.84×10¹⁰ và phương sai 4.67×10²¹ cho thấy mức biến động tương đối cao, phản ánh sự thay đổi trong hoạt động tín dụng ngắn hạn hoặc khả năng thu hồi công nợ.

Khoản phải thu ngắn hạn tăng có thể do doanh nghiệp mở rộng tín dụng bán hàng hoặc tăng doanh số ghi nhận chưa thu tiền, trong khi giảm thể hiện hiệu quả thu hồi vốn tốt hơn.

3.1.4 Biến TSDH

summary(baocaotc_dataset$TSDH)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
## 2.475e+11 2.673e+11 3.332e+11 3.220e+11 3.693e+11 4.035e+11
sd(baocaotc_dataset$TSDH)
## [1] 51675927821
var(baocaotc_dataset$TSDH)
## [1] 2.670402e+21

Nhận xét:

Biến TSDH có giá trị trung bình 3.220e+11, và dao động từ 2.475e+11 (min) đến 4.035e+11 (max), cho thấy biến động tài sản dài hạn giữa các kỳ khá lớn.

Độ lệch chuẩn và phương sai đều khá lớn (5.1676e+10 và 2.6704e+21), cho thấy mức độ biến động trong các khoản đầu tư dài hạn của doanh nghiệp.

Tài sản dài hạn chủ yếu bao gồm các khoản đầu tư lâu dài, như bất động sản, nhà xưởng, thiết bị, và biến động lớn có thể phản ánh sự thay đổi trong đầu tư tài sản cố định hoặc giá trị các khoản đầu tư dài hạn trong các kỳ báo cáo.

3.1.5 Biến Tài sản cố định hữu hình

summary(baocaotc_dataset$TSCD_HH)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
## 1.782e+11 2.005e+11 2.612e+11 2.482e+11 2.882e+11 3.270e+11
sd(baocaotc_dataset$TSCD_HH)
## [1] 45431206637
var(baocaotc_dataset$TSCD_HH)
## [1] 2.063995e+21

Nhận xét:

Biến TSCD_HH có giá trị trung bình khoảng 2.48×10¹¹, thể hiện quy mô đầu tư vào tài sản cố định hữu hình tương đối lớn của doanh nghiệp.

Khoảng dao động từ 1.78×10¹¹ đến 3.27×10¹¹ cho thấy có sự thay đổi đáng kể giữa các kỳ, có thể do mua sắm, khấu hao hoặc thanh lý tài sản.

Độ lệch chuẩn 4.54×10¹⁰ cho thấy biến động vừa phải, phản ánh hoạt động đầu tư tài sản cố định có sự điều chỉnh theo từng quý.

3.1.6 Biến Tài sản cố định vô hình

summary(baocaotc_dataset$TSCD_VH)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
## 4.310e+10 4.840e+10 5.123e+10 5.215e+10 5.658e+10 6.193e+10
sd(baocaotc_dataset$TSCD_VH)
## [1] 5428738404
var(baocaotc_dataset$TSCD_VH)
## [1] 2.94712e+19

Nhận xét:

Giá trị TSCD_VH trung bình (≈5.22×10¹⁰) cho thấy doanh nghiệp có mức đầu tư ổn định vào tài sản vô hình như bản quyền, phần mềm, thương hiệu, sáng chế hoặc chi phí nghiên cứu phát triển.

Khoảng giá trị từ 4.31×10¹⁰ đến 6.19×10¹⁰ thể hiện mức dao động tương đối thấp, chứng tỏ doanh nghiệp duy trì chiến lược quản lý tài sản vô hình ổn định qua các kỳ.

Độ lệch chuẩn nhỏ (5.43×10⁹) so với quy mô giá trị cho thấy sự ổn định cao trong giá trị TSCD vô hình, ít chịu biến động so với các loại tài sản khác như tài sản hữu hình hay tài sản ngắn hạn.

3.1.7 Biến Nợ phải trả

summary(baocaotc_dataset$No_Phai_Tra)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
## 5.475e+11 7.197e+11 7.993e+11 7.915e+11 9.006e+11 9.695e+11
sd(baocaotc_dataset$No_Phai_Tra)
## [1] 122852611491
var(baocaotc_dataset$No_Phai_Tra)
## [1] 1.509276e+22

Nhận xét:

Giá trị Nợ phải trả trung bình là khoảng 7.92×10¹¹, cho thấy doanh nghiệp duy trì mức nợ tương đối cao trong cơ cấu tài chính.

Khoảng dao động từ 5.48×10¹¹ đến 9.70×10¹¹ phản ánh biến động nợ ổn định, không có sự thay đổi quá lớn giữa các kỳ.

Độ lệch chuẩn 1.23×10¹¹ và phương sai 1.51×10²² cho thấy mức phân tán vừa phải, chứng tỏ doanh nghiệp kiểm soát nợ khá nhất quán theo thời gian.

3.1.8 Biến Vốn chủ sở hữu

summary(baocaotc_dataset$VCSH)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
## 2.725e+11 3.542e+11 6.701e+11 5.771e+11 7.966e+11 8.263e+11
sd(baocaotc_dataset$VCSH)
## [1] 211206254631
var(baocaotc_dataset$VCSH)
## [1] 4.460808e+22

Nhận xét:

Giá trị trung bình của Vốn chủ sở hữu (VCSH) đạt khoảng 5.77×10¹¹, phản ánh quy mô vốn khá lớn của doanh nghiệp, đảm bảo khả năng tự chủ tài chính cao.

Phạm vi dao động từ 2.73×10¹¹ đến 8.26×10¹¹, cho thấy sự tăng trưởng rõ rệt của vốn chủ qua thời gian, có thể do tích lũy lợi nhuận giữ lại hoặc tăng vốn đầu tư từ chủ sở hữu.

Độ lệch chuẩn 2.11×10¹¹ thể hiện mức độ biến động vừa phải, phản ánh sự ổn định tương đối trong nguồn vốn chủ sở hữu.

3.1.9 Biến Lợi nhuận sau thuế

summary(baocaotc_dataset$LNST)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
## 5.489e+10 9.577e+10 1.929e+11 2.079e+11 2.784e+11 4.323e+11
sd(baocaotc_dataset$LNST)
## [1] 114863748693
var(baocaotc_dataset$LNST)
## [1] 1.319368e+22

Nhận xét:

Lợi nhuận sau thuế trung bình đạt khoảng 2.08×10¹¹, cho thấy doanh nghiệp có khả năng sinh lời tương đối tốt qua các kỳ.

Khoảng dao động lớn (từ 5.49×10¹⁰ đến 4.32×10¹¹) phản ánh biến động lợi nhuận đáng kể, có thể do thay đổi trong doanh thu hoặc chi phí vận hành từng quý.

Độ lệch chuẩn 1.15×10¹¹ cho thấy sự phân tán cao, hàm ý hiệu quả kinh doanh không ổn định tuyệt đối, có thể bị ảnh hưởng bởi các yếu tố thị trường hoặc biến động chi phí đầu vào.

3.1.10 Biến ROE

summary(baocaotc_dataset$ROE)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.2014  0.2565  0.3153  0.3446  0.3527  0.6259
sd(baocaotc_dataset$ROE)
## [1] 0.126404
var(baocaotc_dataset$ROE)
## [1] 0.01597798

Nhận xét:

ROE trung bình 0.3446 (≈34.46%) thể hiện mỗi đồng vốn chủ sở hữu tạo ra khoảng 0.34 đồng lợi nhuận, đây là mức sinh lời cao và cho thấy doanh nghiệp sử dụng vốn hiệu quả.

Giá trị ROE dao động từ 20.14% đến 62.59%, cho thấy sự thay đổi trong hiệu quả sử dụng vốn, song vẫn duy trì ở mức tích cực trong toàn bộ giai đoạn.

Độ lệch chuẩn 0.1260 và phương sai nhỏ 0.0159 cho thấy mức độ biến động ROE vừa phải, phản ánh sự ổn định tương đối trong khả năng sinh lời trên vốn chủ.

3.1.11 Biến ROA

summary(baocaotc_dataset$ROA)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
## 0.05794 0.08414 0.14039 0.14604 0.18024 0.30920
sd(baocaotc_dataset$ROA)
## [1] 0.07221482
var(baocaotc_dataset$ROA)
## [1] 0.00521498

Nhận xét:

ROA trung bình đạt 0.146 (tức 14.6%), thể hiện rằng mỗi đồng tài sản tạo ra khoảng 0.15 đồng lợi nhuận sau thuế đây là mức sinh lời tốt, phản ánh hiệu quả sử dụng tài sản cao.

Giá trị ROA dao động từ 5.8% đến 30.9%, cho thấy doanh nghiệp có hiệu quả hoạt động khá ổn định, với xu hướng cải thiện dần qua các kỳ.

Độ lệch chuẩn 0.0722 và phương sai nhỏ 0.0052 chứng tỏ mức độ biến động ROA thấp, tức là khả năng sinh lời trên tài sản duy trì ổn định.

3.2 Ma trận tương quan giữa ROE và các biến tài chính chính

library(knitr)
library(kableExtra)
options(kableExtra.latex.load_packages = FALSE)
options(knitr.kable.NA = "")
options(knitr.table.format = "latex")
vars_roe <- c("ROE", "LNST", "VCSH", "No_Phai_Tra", "TTS", "TyLe_No_TS")
corr_matrix_roe <- round(cor(baocaotc_dataset[, vars_roe], use = "complete.obs"), 2)
kable(corr_matrix_roe,
      caption = " Ma trận tương quan giữa ROE và các biến tài chính độc lập",
      align = "c",
      booktabs = TRUE,
      longtable = TRUE,        
      linesep = "") %>%
  kable_styling(
    latex_options = c("striped", "hold_position", "scale_down"), 
    font_size = 10,
    full_width = FALSE,        
    position = "center") %>%
  add_header_above(c(" " = 1, "Các biến tài chính độc lập" = 5)) %>%
  footnote(
    general_title = "Ghi chú:",
    general = "|r| > 0.8: tương quan mạnh; 0.5–0.8: trung bình; < 0.5: yếu.",
    threeparttable = TRUE)

Giải thích:

Dòng 4: Khai báo biến vars_roe chứa các tên biến tài chính độc lập và ROE để phân tích.

Dòng 5: Tính ma trận hệ số tương quan Pearson giữa các biến (corr_matrix_roe), làm tròn 2 số thập phân.

Dòng 6-17: Xuất bảng ma trận tương quan theo định dạng kable với caption, format, footnote chuẩn để báo cáo; nhóm cột và bổ sung ghi chú ý nghĩa hệ số tương quan mạnh, trung bình, yếu.

Nhận xét: ROE tương quan rất mạnh với LNST (0.86), trung bình với VCSH (0.35) và TTS (0.25), ngược chiều rõ rệt với tỷ lệ nợ/tài sản (-0.42).Mối liên hệ giữa ROE và tổng nợ hoặc các chỉ số tài sản, vốn đều không mạnh (dưới 0.5), cho thấy sinh lợi vốn chủ yếu phụ thuộc vào LNST và khả năng kiểm soát nợ.Kết quả này định hướng nên tập trung vào biến động lợi nhuận và cơ cấu tài chính khi muốn nâng cao ROE cho doanh nghiệp.

3.3 Phân tổ các biến

3.3.1 Phân tổ hiệu quả hoạt động kinh doanh theo Quý

phan_to_quy <- baocaotc_dataset %>%
  group_by(Quy_Tach) %>%
  summarise(
    ROE_tb = mean(ROE, na.rm = TRUE),
    ROA_tb = mean(ROA, na.rm = TRUE),
    TyLe_No_TS_tb = mean(TyLe_No_TS, na.rm = TRUE))
kable(phan_to_quy, 
      caption = "Phân tổ theo Quý (Trung bình ROE, ROA, và Tỷ lệ Nợ/Tổng tài sản)",
      booktabs = TRUE,
      align = "c",
      format = "latex") %>%
  kable_styling(
    latex_options = c("striped", "HOLD_position", "scale_down"),
    font_size = 14,
    full_width = FALSE,
    position = "center")

Giải thích:

Dòng 1-6: Nhóm dữ liệu theo quý (Quy_Tach) rồi tính trung bình các chỉ số ROE, ROA, tỷ lệ nợ/tổng tài sản cho từng quý nhờ group_by và summarise.

Dòng 7-16: Xuất bảng trung bình các chỉ tiêu theo quý với format đẹp.

Nhận xét: ROE, ROA và tỷ lệ nợ/tổng tài sản dao động nhưng khá cân bằng giữa các quý. Không quý nào có mức nổi bật vượt trội; các chỉ số duy trì ổn định cho thấy doanh nghiệp vận hành đều qua các quý, không có dấu hiệu biến động mạnh do yếu tố quý/năm.

3.3.2 Phân tổ hiệu quả hoạt động kinh doanh theo Quý

baocaotc_dataset <- baocaotc_dataset %>%
  mutate(Nhom_No = case_when(
    TyLe_No_TS < 0.4 ~ "Thấp (<40%)",
    TyLe_No_TS < 0.7 ~ "Trung bình (40%-70%)",
    TRUE ~ "Cao (>70%)" ))
phan_to_no <- baocaotc_dataset %>%
  group_by(Nhom_No) %>%
  summarise(
    ROE_tb = mean(ROE, na.rm = TRUE),
    ROA_tb = mean(ROA, na.rm = TRUE),
    TTS_tb = mean(TTS, na.rm = TRUE))
kable(phan_to_no,
      caption = "Phân tổ theo mức độ nợ (Trung bình ROE, ROA, và Tổng tài sản)",
      booktabs = TRUE, align = "c", format = "latex") %>%
  kable_styling(
    latex_options = c("striped", "HOLD_position", "scale_down"),
    font_size = 14,
    full_width = FALSE,
    position = "center" )

Giải thích:

Dòng 1-5: Tạo biến phân nhóm theo tỷ lệ nợ trên tổng tài sản (TyLe_No_TS), chia ba nhóm: Thấp (<40%), Trung bình (40-70%), Cao (>70%) bằng mutate và case_when.

Dòng 6-11: Tóm tắt trung bình ROE, ROA, tổng tài sản cho từng nhóm nợ với group_by và summarise.

Dòng 12-19: Xuất bảng tóm tắt, trình bày chỉ số trung bình của ba nhóm với định dạng bảng đẹp.

Nhận xét: Nhóm nợ cao (>70%) có ROE, ROA trung bình thấp nhất, đồng thời tổng tài sản cũng nhỏ hơn nhóm nợ thấp/trung bình. Doanh nghiệp vay nhiều dễ tiềm ẩn rủi ro, sức sinh lời giảm; nhóm nợ thấp hoặc trung bình có kết quả hoạt động ổn định và quy mô tài sản lớn hơn.

3.3.3 Phân tổ hiệu quả hoạt động kinh doanh theo Năm

phan_to_nam <- baocaotc_dataset %>%
  group_by(Nam) %>%
  summarise(
    ROE_tb = mean(ROE, na.rm = TRUE),
    ROA_tb = mean(ROA, na.rm = TRUE),
    No_tb = mean(No_Phai_Tra, na.rm = TRUE),
    VCSH_tb = mean(VCSH, na.rm = TRUE))
kable(phan_to_nam,
      caption = "Phân tổ theo năm (Trung bình ROE, ROA, Nợ, VCSH)",
      booktabs = TRUE, align = "c", format = "latex") %>%
  kable_styling(
    latex_options = c("striped", "HOLD_position", "scale_down"),
    font_size = 14,
    full_width = FALSE,
    position = "center")

Giải thích:

Dòng 1 và 2: Gán kết quả vào biến phan_to_nam, khởi đầu chuỗi xử lý bằng baocaotc_dataset %>% và nhóm dữ liệu theo biến Nam để xử lý riêng cho từng năm.

Dòng 3-7: Hàm summarise() tính trung bình các chỉ số tài chính theo năm, gồm ROE_tb (trung bình ROE), ROA_tb (trung bình ROA), No_tb (trung bình Nợ phải trả), VCSH_tb (trung bình Vốn chủ sở hữu). Mỗi chỉ số đều loại bỏ giá trị thiếu (na.rm = TRUE).

Dòng 9-10: Sử dụng kable() để trình bày bảng phan_to_nam với dòng tiêu đề mô tả rõ ràng, căn giữa nội dung, định dạng bảng latex.

Dòng 11-16: Hàm kable_styling() tạo viền sọc, giữ vị trí bảng khi in ra, phông chữ 14, căn giữa và không chiếm toàn bộ chiều rộng trang để bảng hiển thị cân đối.

Nhận xét: Bảng kết quả thể hiện trung bình các chỉ số ROE, ROA, Nợ phải trả và Vốn chủ sở hữu theo từng năm trong giai đoạn nghiên cứu. Dữ liệu này giúp tôi nhận diện rõ xu hướng và sự biến động các chỉ số tài chính quan trọng qua từng năm.

3.3.4 Phân tổ theo quy mô tài sản

baocaotc_dataset <- baocaotc_dataset %>%
  mutate(Nhom_TTS = case_when(
    TTS < quantile(TTS, 0.33, na.rm = TRUE) ~ "Nhỏ",
    TTS < quantile(TTS, 0.67, na.rm = TRUE) ~ "Trung bình",
    TRUE ~ "Lớn"))
phan_to_quymo <- baocaotc_dataset %>%
  group_by(Nhom_TTS) %>%
  summarise(
    ROE_tb = mean(ROE, na.rm = TRUE),
    ROA_tb = mean(ROA, na.rm = TRUE),
    LNST_tb = mean(LNST, na.rm = TRUE),
    TyLe_No_TS_tb = mean(TyLe_No_TS, na.rm = TRUE))
kable(phan_to_quymo,
      caption = "Phân tổ theo quy mô tổng tài sản",
      booktabs = TRUE, align = "c", format = "latex") %>%
  kable_styling(
    latex_options = c("striped", "HOLD_position", "scale_down"),
    font_size = 14,
    full_width = FALSE,
    position = "center")

Giải thích:

Dòng 1-4: Sử dụng mutate() kết hợp case_when() để phân loại biến theo tỷ lệ hoặc giá trị mong muốn, giúp gán nhãn cho từng nhóm đối tượng nhanh chóng và trực quan.

Dòng 5-12: Kết hợp group_by() và summarise() nhằm tổng hợp các chỉ số trung bình hoặc tổng cho từng nhóm đối tượng, đảm bảo tính chính xác bằng việc loại trừ giá trị thiếu với na.rm = TRUE.

Dòng 13-20: Xuất kết quả dưới dạng bảng trình bày rõ ràng, sử dụng các hàm định dạng để bảng dễ đọc, bố cục chuẩn.

Nhận xét: Bảng phân tổ cho thấy các chỉ số trung bình ROE, ROA, LNST và tỷ lệ nợ trên tổng tài sản có sự khác biệt rõ rệt giữa ba nhóm quy mô tổng tài sản. Doanh nghiệp quy mô trung bình đạt giá trị ROE và ROA trung bình cao nhất, cho thấy hiệu quả sử dụng tài sản và vốn tốt hơn so với nhóm lớn và nhỏ. Ngoài ra, nhóm doanh nghiệp nhỏ có tỷ lệ nợ trên tổng tài sản cao nhất, phản ánh mức độ sử dụng đòn bẩy tài chính lớn hơn nhưng hiệu quả tài chính lại thấp. Kết quả này giúp tôi nhận diện vai trò của quy mô tổng tài sản trong hiệu quả hoạt động và rủi ro tài chính của các công ty, tạo cơ sở cho phân tích chuyên sâu ở các phần sau.

3.3.5 Phân tổ theo khả năng sinh lời ROE

baocaotc_dataset <- baocaotc_dataset %>%
  mutate(Nhom_ROE = case_when(
    ROE < 0.25 ~ "Thấp",
    ROE < 0.4 ~ "Trung bình",
    TRUE ~ "Cao" ))
phan_to_roe <- baocaotc_dataset %>%
  group_by(Nhom_ROE) %>%
  summarise(
    TTS_tb = mean(TTS, na.rm = TRUE),
    No_tb = mean(No_Phai_Tra, na.rm = TRUE),
    VCSH_tb = mean(VCSH, na.rm = TRUE),
    TyLe_No_TS_tb = mean(TyLe_No_TS, na.rm = TRUE))
kable(phan_to_roe,
      caption = "Phân tổ theo mức sinh lời ROE",
      booktabs = TRUE, align = "c", format = "latex") %>%
  kable_styling(
    latex_options = c("striped", "HOLD_position", "scale_down"),
    font_size = 14,
    full_width = FALSE,
    position = "center" )

Giải thích:

Dòng 1-5: Sử dụng mutate(Nhom_ROE = case_when(…)) để tạo biến phân nhóm hiệu quả sinh lời (ROE): áp dụng điều kiện để chia doanh nghiệp thành ba nhóm: “Thấp” (ROE < 0.25), “Trung bình” (ROE < 0.4), còn lại là “Cao”.

Dòng 6-13: Tổng hợp dữ liệu trung bình từng nhóm ROE: Dùng group_by(Nhom_ROE) để nhóm theo phân loại vừa tạo. Dùng summarise() tính trung bình các chỉ số: tổng tài sản (TTS_tb), nợ (No_tb), vốn chủ sở hữu (VCSH_tb), tỷ lệ nợ trên tài sản (TyLe_No_TS_tb) cho từng nhóm. Các hàm mean đều loại bỏ giá trị thiếu bằng na.rm = TRUE.

Dòng 14-20: Xuất bảng tổng hợp: Sử dụng kable() tạo bảng hiển thị rõ ràng, có tiêu đề. Thêm kable_styling() để format bảng đẹp.

Nhận xét: Bảng phân tổ theo ROE cho thấy sự khác biệt về quy mô tài sản, nợ và vốn giữa các nhóm hiệu quả sinh lời. Doanh nghiệp ROE trung bình thường có tổng tài sản và vốn chủ sở hữu cao nhất, trong khi nhóm ROE thấp lại có tỷ lệ nợ trên tổng tài sản lớn nhất. Điều này cho phép tôi đánh giá mối liên hệ giữa hiệu quả sinh lời và cấu trúc tài chính của doanh nghiệp.

3.3.6 Phân tổ theo khả năng sinh lời ROA

baocaotc_dataset <- baocaotc_dataset %>%
mutate(Nhom_ROA = case_when(
ROA < 0.08 ~ "Thấp",
ROA < 0.15 ~ "Trung bình",
TRUE ~ "Cao"))
phan_to_roa <- baocaotc_dataset %>%
group_by(Nhom_ROA) %>%
summarise(
TTS_tb = mean(TTS, na.rm = TRUE),
No_tb = mean(No_Phai_Tra, na.rm = TRUE),
VCSH_tb = mean(VCSH, na.rm = TRUE),
TyLe_No_TS_tb = mean(TyLe_No_TS, na.rm = TRUE))
kable(phan_to_roa,
      caption = "Phân tổ theo mức sinh lời ROA",
      booktabs = TRUE, align = "c", format = "latex") %>%
  kable_styling(
    latex_options = c("striped", "HOLD_position", "scale_down"),
    font_size = 14,
    full_width = FALSE,
    position = "center")

Giải thích:

Dòng 1-4: Sử dụng mutate() kết hợp case_when() để phân nhóm khả năng sinh lời trên tổng tài sản (ROA) thành ba mức: “Thấp” (ROA < 0.08), “Trung bình” (ROA < 0.15), còn lại là “Cao”. Biến phân nhóm này (Nhóm_ROA) giúp chia doanh nghiệp thành các nhóm hiệu quả sinh lời khác nhau.

Dòng 5-7: Sử dụng group_by(Nhom_ROA) để nhóm dữ liệu theo các mức sinh lời vừa phân loại, chuẩn bị để tổng hợp số liệu.

Dòng 8-11: Dùng summarise() để tính trung bình các chỉ số tài chính theo từng nhóm ROA: tổng tài sản (TTS_tb), nợ phải trả (No_tb), vốn chủ sở hữu (VCSH_tb), tỷ lệ nợ trên tổng tài sản (TyLe_No_TS_tb). Mỗi hàm đều loại bỏ giá trị thiếu bằng na.rm = TRUE, đảm bảo kết quả chính xác.

Dòng 12-20: Sử dụng các hàm xuất bảng (kable() và kable_styling()) để trình bày kết quả rõ ràng.

Nhận xét: Bảng cho thấy nhóm ROA cao có tỷ lệ nợ thấp nhất và vốn chủ sở hữu lớn hơn, phản ánh doanh nghiệp hoạt động hiệu quả hơn. Ngược lại, nhóm ROA thấp có tỷ lệ nợ cao nhưng hiệu quả sử dụng tài sản chưa tốt. Sự khác biệt này thể hiện rõ vai trò của khả năng sinh lời trong kiểm soát rủi ro và cơ cấu tài chính doanh nghiệp ngành phim.

3.3.7 Phân tổ theo nhóm đòn bẫy tài chính

baocaotc_dataset <- baocaotc_dataset %>%
mutate(DonBay = No_Phai_Tra / VCSH) %>%
mutate(Nhom_DonBay = case_when(
DonBay < 1 ~ "Thấp (≤1)",
DonBay < 2 ~ "Trung bình (1–2)",
TRUE ~ "Cao (>2)"))
phan_to_donbay <- baocaotc_dataset %>%
group_by(Nhom_DonBay) %>%
summarise(
ROE_tb = mean(ROE, na.rm = TRUE),
ROA_tb = mean(ROA, na.rm = TRUE),
TyLe_No_TS_tb = mean(TyLe_No_TS, na.rm = TRUE),
VCSH_tb = mean(VCSH, na.rm = TRUE),
No_tb = mean(No_Phai_Tra, na.rm = TRUE))
kable(phan_to_donbay,
caption = "Phân tổ theo mức độ đòn bẩy tài chính (Nợ/VCSH)",
booktabs = TRUE, align = "c", format = "latex") %>%
kable_styling(
latex_options = c("striped", "HOLD_position", "scale_down"),
font_size = 14,
full_width = FALSE,
position = "center")

Giải thích:

Dòng 1 và 2: Sao chép dữ liệu gốc sang biến mới để đảm bảo nguyên vẹn dữ liệu đầu vào và tạo biến DonBay bằng tỷ lệ Nợ phải trả trên Vốn chủ sở hữu, phản ánh mức độ sử dụng nợ tài chính.

Dòng 3-5: Phân loại mức độ đòn bẩy tài chính (DonBay) thành ba nhóm: “Thấp (≤1)”, “Trung bình (1–2)”, “Cao (>2)” dùng mutate() kết hợp case_when(), giúp phân nhóm doanh nghiệp theo tiêu chí chi tiết.

Dòng 7: Gán dữ liệu đã phân nhóm sang biến mới phục vụ tổng hợp tiếp theo.

Dòng 8: Sử dụng group_by(Nhom_DonBay) để nhóm doanh nghiệp theo mức độ đòn bẩy.

Dòng 9-14: Dùng summarise() lần lượt tính trung bình các chỉ số: ROE, ROA, tỷ lệ nợ trên tài sản, VCSH, Nợ phải trả cho từng nhóm. Mỗi lệnh đều loại trừ giá trị thiếu bằng na.rm = TRUE.

Dòng 15-22: Xuất kết quả bảng tổng hợp bằng kable() kết hợp kable_styling(), tạo tiêu đề rõ ràng, bố cục bảng chuyên nghiệp giúp so sánh các nhóm dễ dàng.

Nhận xét: Kết quả bảng chỉ ra: Nhóm đòn bẩy thấp và trung bình đạt hiệu quả sinh lời (ROE, ROA) cao hơn, tỷ lệ nợ/tổng tài sản thấp hơn. Nhóm đòn bẩy cao có chỉ số sinh lời thấp và tỷ lệ nợ cao nhất, phản ánh rủi ro tài chính tăng lên khi doanh nghiệp sử dụng nhiều nợ. Đây là cơ sở nhận diện ảnh hưởng của cấu trúc vốn đến hiệu quả hoạt động doanh nghiệp.

3.3.8 Phân tổ theo quy mô lợi nhuận sau thuế

baocaotc_dataset <- baocaotc_dataset %>%
mutate(Nhom_LNST = case_when(
LNST < quantile(LNST, 0.33, na.rm = TRUE) ~ "Nhỏ (dưới 33%)",
LNST < quantile(LNST, 0.67, na.rm = TRUE) ~ "Trung bình (33%)",
TRUE ~ "Lớn (trên 33%)"))
phan_to_LNST <- baocaotc_dataset %>%
group_by(Nhom_LNST) %>%
summarise(
ROE_tb = mean(ROE, na.rm = TRUE),
ROA_tb = mean(ROA, na.rm = TRUE),
TyLe_No_TS_tb = mean(TyLe_No_TS, na.rm = TRUE),
TTS_tb = mean(TTS, na.rm = TRUE),
VCSH_tb = mean(VCSH, na.rm = TRUE))
kable(phan_to_LNST,
caption = "Phân tổ theo quy mô lợi nhuận sau thuế (LNST)",
booktabs = TRUE, align = "c", format = "latex") %>%
kable_styling(
latex_options = c("striped", "HOLD_position", "scale_down"),
font_size = 14,
full_width = FALSE,
position = "center")

Giải thích:

Dòng 1-5: Tạo biến phân nhóm theo lợi nhuận sau thuế (LNST), chia làm 3 nhóm bằng hàm mutate kết hợp case_when dựa trên các phân vị.

Dòng 6-13: Tóm tắt số liệu từng nhóm (mean của ROE, ROA, TyLe_No_TS, TTS, VCSH) bằng hàm summarise() sau khi đã nhóm bằng group_by().

Dòng 14-21: Xuất bảng kết quả tóm tắt theo format đẹp, caption rõ ràng, cỡ chữ lớn và căn giữa.

Nhận xét:

Nhóm có LNST lớn nhất sở hữu ROE, ROA, tài sản, vốn chủ và hiệu suất sinh lời đều vượt trội.

Nhóm nhỏ (dưới 33%) có chỉ số hiệu quả thấp, tỷ lệ nợ trên tài sản cao (hệ số TyLe_No_TS lớn).

Sự phân hóa rõ nét giữa các nhóm cho thấy quy mô lợi nhuận sau thuế có mối liên hệ chặt chẽ với hiệu quả sinh lời, sức mạnh tài chính và chính sách vay vốn của doanh nghiệp.

CHƯƠNG 4: TRỰC QUAN HÓA DỮ LIỆU

4.1 Biểu đồ xu hướng tổng tài sản qua các năm

df <- baocaotc_dataset %>% arrange(Nam) %>% mutate(TTS_Ty = TTS/1e9)
maxy <- max(df$TTS_Ty, na.rm=TRUE) * 1.15
ggplot(df, aes(x = factor(Nam), y = TTS_Ty, group=1)) +
  geom_col(fill="yellow", alpha=0.85, width=0.6) +                  
  geom_line(aes(y = TTS_Ty), color="#1B4F72", size=1) +              
  geom_point(aes(y = TTS_Ty), color="#154360", size=3) +            
  geom_text_repel(aes(y = TTS_Ty, label = paste0(round(TTS_Ty,1), " Tỷ")),
                  nudge_y = maxy * 0.02,        
                  size = 3.4, fontface = "bold", color = "#1B1F8A",
                  segment.color = "grey70", max.overlaps = 20, show.legend = FALSE) +
  geom_hline(yintercept = mean(df$TTS_Ty, na.rm=TRUE), linetype="dashed", color="darkred") +
  scale_y_continuous(labels = label_number(accuracy = 1, suffix = " Tỷ"), limits = c(0, maxy)) +
  labs(title="Xu hướng Tổng tài sản (TTS) của ACL",
       subtitle="Giai đoạn 2014–2025 | Đơn vị: Tỷ đồng",
       x="Năm", y="Tổng tài sản (Tỷ đồng)") +
  theme_minimal(base_size = 14) +
  theme(plot.title = element_text(face="bold", size=20, hjust=0.5, color="#1B1F8A"),
        plot.subtitle = element_text(size=12, hjust=0.5, color="gray40"))

Giải thích:

Dòng 1-2: Sắp xếp dữ liệu theo năm, tạo biến chuyển đổi tổng tài sản từ đồng sang tỷ đồng để trực quan hóa trên biểu đồ.

Dòng 3: Xác định giá trị trục y tối đa, lấy max tổng tài sản và nhân hệ số để giãn trục.

Dòng 4-18: Vẽ biểu đồ tích hợp bằng ggplot2: Thiết kế cột biểu diễn tổng tài sản từng năm, điểm và đường nối xu hướng theo từng năm (các dòng chứa geom_col, geom_line, geom_point). Gắn nhãn từng năm bằng geom_text_repel. Thiết lập định dạng trục, nhãn, màu sắc bảng, tiêu đề, chú thích, căn chỉnh font và vị trí; phân biệt rõ đơn vị tỷ đồng, đảm bảo biểu đồ hiển thị chuyên nghiệp, dễ đọc.

Nhận xét: Biểu đồ thể hiện tổng tài sản ACL tăng dần qua từng năm, xu hướng phát triển ổn định ngoại trừ một số năm dao động nhẹ do yếu tố sản xuất kinh doanh và thị trường. Đây là minh chứng rõ cho quá trình tích lũy tài sản của công ty trong dài hạn.

4.2 Biểu đồ cơ cấu Tài sản ngắn hạn (TSNH) và Tài sản dài hạn (TSDH) qua các năm

tsnh_tsdh_long <- baocaotc_dataset %>%
select(Nam, TSNH, TSDH, TTS) %>%
pivot_longer(cols = c(TSNH, TSDH),
names_to = "Loai_TS",
values_to = "GiaTri")
ggplot(tsnh_tsdh_long, aes(x = factor(Nam), y = GiaTri, fill = Loai_TS)) +
geom_bar(stat = "identity", color = "black", linewidth = 0.3, alpha = 0.9) +       
geom_text(aes(label = paste0(round(GiaTri/1e12, 2), " T")),          
position = position_stack(vjust = 0.5), size = 4, fontface = "bold") +
geom_hline(yintercept = mean(baocaotc_dataset$TTS),                  
color = "darkgray", linetype = "dotted", linewidth = 1) +
geom_line(data = baocaotc_dataset,                                
aes(x = factor(Nam), y = TTS, group = 1),
color = "#C0392B", linewidth = 1.3, 
linetype = "dashed", inherit.aes = FALSE) +
scale_fill_manual(values = c("TSNH" = "#76D7C4", "TSDH" = "#5DADE2"),
name = "Loại tài sản",
labels = c("Tài sản ngắn hạn", "Tài sản dài hạn")) +
scale_y_continuous(labels = label_number(scale = 1e-12, suffix = " Tỷ", accuracy = 0.01)) +
labs(
title = "Cơ cấu Tài sản ngắn hạn và Tài sản dài hạn của ACL qua các năm",
subtitle = "Đơn vị: Tỷ đồng – minh họa tỷ trọng và xu hướng biến động giữa hai nhóm tài sản",
x = "Năm",
y = "Giá trị tài sản (Tỷ đồng)"
) +
theme_minimal(base_size = 14) +
theme(
plot.title = element_text(face = "bold", size = 20, color = "#1B1F8A", hjust = 0.5),
plot.subtitle = element_text(size = 13, color = "gray30", hjust = 0.5),
axis.title = element_text(size = 14, face = "bold"),
axis.text = element_text(size = 12),
legend.position = "right",
plot.margin = margin(20, 30, 20, 30))

Giải thích:

Dòng 1-3: Tiền xử lý dữ liệu. Lấy cột năm, tài sản ngắn hạn (TSNH), tài sản dài hạn (TSDH), tổng tài sản (TTS) từ bộ dữ liệu và chuyển sang dạng dài với pivot_longer(), tạo biến phân loại loại tài sản và giá trị tương ứng cho từng năm.

Dòng 4-12: Vẽ biểu đồ cột chồng qua các năm với ggplot2: Sử dụng geom_bar() để biểu diễn từng loại tài sản theo từng năm bằng màu khác nhau. Thêm nhãn giá trị tài sản lên từng cột bằng geom_text(). Tạo trục trung bình tổng tài sản các năm với geom_hline() (vạch ngang chấm).Thêm đường xu hướng tổng tài sản với geom_line() và geom_point() để minh hoạ sự biến động theo thời gian. Tuỳ chỉnh màu cho hai nhóm tài sản với scale_fill_manual() và set lại đơn vị trục y thành tỷ đồng.

Dòng 13–34: Hiệu chỉnh tiêu đề, nhãn trục, phông chữ, vị trí chú thích, margin giúp bảng biểu đồ rõ ràng, đáp ứng quy định trình bày bài luận.

Nhận xét: Biểu đồ cho thấy tài sản của ACL qua các năm tăng ổn định, tỷ trọng tài sản dài hạn luôn chiếm phần lớn, tài sản ngắn hạn biến động nhẹ và có xu hướng tăng dần. Việc minh hoạ trực quan này giúp tôi nhận diện nhanh sự thay đổi cơ cấu tài sản và đánh giá mức độ ổn định tài chính của doanh nghiệp.

4.3 Biểu đồ so sánh Tài sản cố định hữu hình và vô hình của ACL qua các năm

df <- baocaotc_dataset %>%
  summarise(TSCD_HuuHinh = mean(TSCD_HH, na.rm=TRUE),
            TSCD_VoHinh  = mean(TSCD_VH,  na.rm=TRUE)) %>%
  tidyr::pivot_longer(everything(), names_to="Loai", values_to="GiaTri")
ggplot(df, aes(x=Loai, y=GiaTri/1e9, fill=Loai)) +
  geom_col(width=0.6, color="black", alpha=0.85) +
  geom_text(aes(label=round(GiaTri/1e9,2)), vjust=-0.5, size=5) +
  scale_fill_manual(values=c("#2874A6","#82E0AA"),
                    labels=c("TSCĐ Hữu hình","TSCĐ Vô hình")) +
  labs(title="So sánh trung bình TSCĐ Hữu hình và Vô hình của ACL",
       y="Giá trị (Tỷ đồng)", x=NULL, fill=NULL) +
  theme_minimal(base_size=14) +
  theme(plot.title=element_text(face="bold", hjust=0.5, color="#1B1F8A"),
        legend.position="none")

Giải thích:

Dòng 1-4: Sử dụng summarise() để tính trung bình TSCĐ hữu hình (TSCD_HH) và vô hình (TSCD_VH) cho từng năm từ dữ liệu gốc, sau đó chuyển dữ liệu sang dạng dài với pivot_longer() (tạo hai biến loại và giá trị).

Dòng 5-14: Dùng ggplot vẽ biểu đồ cột so sánh giá trị trung bình của TSCĐ hữu hình và vô hình. geom_col() tạo cột, geom_text() gắn nhãn giá trị lên cột. Tuỳ chỉnh màu sắc, nhãn, tiêu đề và định dạng để biểu đồ rõ ràng, dễ đọc.

Nhận xét: Khoảng cách giữa hai cột cho thấy TSCĐ hữu hình của ACL vượt trội so với vô hình. Điều này phản ánh đặc trưng ngành thủy sản: tài sản chủ yếu là máy móc thiết bị, cơ sở vật chất hữu hình.

4.4 Biểu đồ so sánh Nợ phải trả và Vốn chủ sở hữu của ACL qua các năm

df <- baocaotc_dataset %>%
  select(Nam, No_Phai_Tra, VCSH) %>%
  pivot_longer(-Nam, names_to="Loai", values_to="GiaTri")
ggplot(df, aes(factor(Nam), GiaTri/1e9, fill=Loai)) +
  geom_col(color="black", alpha=0.9, width=0.7) +
  geom_text(aes(label=paste0(round(GiaTri/1e9,1)," Tỷ")),
            position=position_stack(vjust=0.5), size=3.3, fontface="bold") +
  scale_fill_manual(values=c("#2874A6","#52BE80"),
                    labels=c("Nợ phải trả","Vốn chủ sở hữu")) +
  labs(title="So sánh Nợ phải trả và Vốn chủ sở hữu của ACL qua các năm",
       subtitle="Đơn vị: Tỷ đồng",
       x="Năm", y="Giá trị (Tỷ đồng)", fill="Thành phần") +
  theme_minimal(base_size=14) +
  theme(plot.title=element_text(face="bold", size=18, hjust=0.5, color="#1B1F8A"),
        plot.subtitle=element_text(hjust=0.5, color="gray40"),
        legend.position="top")

Giải thích:

Dòng 1-3: Chọn biến Năm, Nợ phải trả, Vốn chủ sở hữu từ dữ liệu gốc, sau đó dùng pivot_longer() để đưa về dạng dài với hai loại thành phần (Nợ và VCSH).

Dòng 4-10: Dùng ggplot() vẽ biểu đồ cột chồng: geom_col() tạo cột phân biệt hai thành phần từng năm; geom_text() hiển thị giá trị thực lên từng phần cột. scale_fill_manual() đặt màu phân biệt Nợ và Vốn. labs() đặt tên trục, tiêu đề, chú giải.

Dòng 11-16: Tuỳ chỉnh định dạng bảng biểu: theme kiểu tối giản, font 14, tiêu đề bôi đậm, chú thích để rõ tên từng thành phần, vị trí bảng chú thích bên trên.

Nhận xét: Biểu đồ cho thấy Nợ phải trả và Vốn chủ sở hữu của ACL đều tăng qua các năm, trong đó nợ chiếm tỷ trọng lớn hơn vốn. Xu hướng này phản ánh doanh nghiệp mở rộng quy mô chủ yếu dựa vào nguồn vốn vay, tiềm ẩn rủi ro nếu nợ tăng quá nhanh.

4.5 Biểu đồ xu hướng Lợi nhuận sau thuế (LNST) theo năm

lnst_tb <- baocaotc_dataset %>%
  group_by(Nam) %>%
  summarise(LNST_TB = mean(LNST, na.rm=TRUE))
ggplot(lnst_tb, aes(x=factor(Nam), y=LNST_TB/1e9, group=1)) +
  geom_area(fill="#A9CCE3", alpha=0.5) +
  geom_line(color="#1B4F72", linewidth=1.3) +
  geom_point(color="#27AE60", size=3) +
  geom_text(aes(label=paste0(round(LNST_TB/1e9,1)," Tỷ")),
            vjust=-0.6, size=3.3, fontface="bold") +
  labs(title="Xu hướng trung bình Lợi nhuận sau thuế (LNST) của ACL",
       subtitle="Giai đoạn 2014–2025 | Đơn vị: Tỷ đồng",
       x="Năm", y="LNST trung bình (Tỷ đồng)") +
  theme_minimal(base_size=14) +
  theme(plot.title=element_text(face="bold", size=18, hjust=0.5, color="#1B1F8A"),
        plot.subtitle=element_text(hjust=0.5, color="gray40"))

Giải thích:

Dòng 1-3 : Nhóm dữ liệu theo năm (group_by(Nam)), chuẩn bị cho bước tổng hợp và tính trung bình LNST từng năm .

Dòng 4-9: Vẽ biểu đồ xu hướng trung bình LNST theo năm bằng ggplot2: Sử dụng ggplot() + geom_area() tô nền biểu đồ, geom_line() vẽ đường xu hướng LNST, geom_point() đánh dấu giá trị mỗi năm, geom_text() gắn nhãn giá trị lên từng điểm.

Dòng 10-15: Thiết lập tiêu đề, nhãn trục, màu sắc, font và định dạng tối giản cho biểu đồ.

Nhận xét: Biểu đồ cho thấy LNST ACL tăng nhanh giai đoạn 2018–2019, sau đó giảm rồi ổn định từ 2021–2025. Xu hướng này phản ánh rõ đà phát triển mạnh rồi đi vào ổn định của doanh nghiệp qua các năm.

4.6 Biểu đồ quan hệ LNST và Tổng tài sản (TTS)

ggplot(baocaotc_dataset, aes(x = TTS/1e9, y = LNST/1e9, color = Nam, label = Nam)) +
  geom_point(size = 4, alpha = 0.9) +
  geom_smooth(method = "lm", se = FALSE, color = "#2C3E50",
              linetype = "dashed", linewidth = 1) +
  geom_text_repel(size = 3.5, fontface = "bold", color = "black", show.legend = FALSE) +
  scale_color_viridis(option = "plasma", direction = -1, name = "Năm") +
  labs(title = "Quan hệ giữa LNST và Tổng tài sản của ACL",
       subtitle = "Giai đoạn 2014–2025 | Đơn vị: Tỷ đồng",
       x = "Tổng tài sản (Tỷ đồng)", y = "Lợi nhuận sau thuế (Tỷ đồng)") +
  theme_minimal(base_size = 14) +
  theme(plot.title = element_text(face="bold", size=18, color="#1B1F8A", hjust=0.5),
        plot.subtitle = element_text(hjust=0.5, color="gray40"),
        legend.position = "right",
        panel.grid.minor = element_blank())

Giải thích:

Dòng 1 để thể hiện mối quan hệ giữa Tổng tài sản (TTS) và Lợi nhuận sau thuế (LNST) theo từng năm. Hai biến được chia cho 1e9 để quy đổi sang tỷ đồng.

Dòng 2-4 lần lượt vẽ các điểm dữ liệu và đường hồi quy tuyến tính có dạng gạch đứt, cho phép quan sát xu hướng giữa TTS và LNST. Dòng 5 thêm nhãn năm, tránh chồng chữ. Dòng 6 thiết lập dải màu thời gian từ vàng đến tím, dễ phân biệt các giai đoạn.

Dòng 7-14 định dạng tiêu đề, nhãn trục, màu sắc và bố cục biểu đồ, giúp trình bày rõ ràng, cân đối và phù hợp với chuẩn học thuật.

Nhận xét: Biểu đồ cho thấy LNST của ACL tăng cùng với tổng tài sản qua các năm, hai biến có tương quan thuận mạnh. Đường xu hướng nét đứt xác nhận mỗi khi tổng tài sản tăng, LNST cũng tăng theo, phản ánh hiệu quả phát triển hợp lý tại doanh nghiệp.

4.7 Biểu đồ phân bố LNST hàng năm của ACL

d <- baocaotc_dataset %>% mutate(Nam=factor(Nam))
m <- mean(d$LNST, na.rm=TRUE)
stats <- d %>% group_by(Nam) %>% summarise(Med=median(LNST,na.rm=TRUE),Mean=mean(LNST,na.rm=TRUE))
ggplot(d,aes(Nam,LNST,fill=Nam))+
  geom_boxplot(alpha=.7,width=.6,color="gray30",outlier.colour="red")+
  geom_jitter(width=.15,alpha=.4,color="gray40")+
  geom_point(data=stats,aes(y=Mean),shape=23,fill="white",size=3)+
  geom_text(data=stats,aes(y=Med,label=paste0(round(Med/1e9,1)," Tỷ")),vjust=-1,size=4, colour = "black",)+
  geom_hline(yintercept=m,linetype="dashed",color="#1A5276")+
  scale_y_continuous(labels=label_number(scale=1e-9,suffix=" Tỷ"))+
  scale_fill_brewer(palette="Set3",guide=FALSE)+
  labs(title="Phân bố LNST của ACL theo năm",x="Năm",y="LNST (Tỷ đồng)")+
  theme_minimal(base_size=13)+
  theme(
    plot.title = element_text(face = "bold", size = 20, color = "#1B1F8A", hjust = 0.5))

Giải thích:

Dòng 1-2: Tiền xử lý dữ liệu, chuyển đổi biến năm và tính trung bình LNST.

Dòng 3: Tổng hợp theo năm, tính trung vị và trung bình LNST dùng summarise().

Dòng 4-15: Vẽ biểu đồ boxplot phân bố LNST từng năm với ggplot2, thể hiện giá trị trung bình, trung vị, điểm ngoại lai, và nhãn giá trị rõ trên cột. Tuỳ chỉnh màu, tiêu đề, nhãn trục, font để bảng chuyên nghiệp, dễ nhìn.

Nhận xét: Biểu đồ phân bố cho thấy LNST ACL qua các năm tăng trưởng mạnh từ 2018–2020, sau đó dao động quanh mức ổn định. Các năm 2019–2020 ghi nhận đột biến LNST cao, phản ánh hiệu quả kinh doanh vượt trội giai đoạn này.

4.8 Biểu đồ phân bố Tổng tài sản của ACL

df <- baocaotc_dataset %>% mutate(TTS_Ty=TTS/1e9) %>% filter(!is.na(TTS_Ty))
bw <- diff(range(df$TTS_Ty))/30
m1 <- mean(df$TTS_Ty); m2 <- median(df$TTS_Ty)
ggplot(df,aes(TTS_Ty))+
  geom_histogram(binwidth=bw,fill="#5DADE2",color="#1A5276",alpha=.85)+
  geom_density(aes(y=..count..*bw),color="#117864",fill="#117864",alpha=.2)+
  geom_vline(xintercept=m1,linetype="dotted",color="red")+
  geom_vline(xintercept=m2,linetype="dashed",color="blue")+
  annotate("text",x=m1,y=Inf,label=paste0("Mean: ",round(m1,1)," Tỷ"),vjust=1.5,color="red",size=3.8)+
  annotate("text",x=m2,y=Inf,label=paste0("Median: ",round(m2,1)," Tỷ"),vjust=3,color="blue",size=3.8)+
  labs(title="Phân bố Tổng tài sản (TTS) của ACL",x="Tổng tài sản (Tỷ đồng)",y="Tần suất")+
  scale_x_continuous(labels=label_number(suffix=" Tỷ"))+
  theme_classic(base_size=13)+
   theme(
    plot.title = element_text(face = "bold", size = 20, color = "#1B1F8A", hjust = 0.5))

Giải thích: Dòng 1: Tiền xử lý: Lấy tổng tài sản từ bộ dữ liệu và chuyển đổi sang tỷ đồng; loại bỏ giá trị thiếu (filter(!is.na(TTS_Ty))).

Dòng 2: Tạo biến bw là độ rộng các bin (khoảng) cho histogram.

Dòng 3: Tính trung bình (m1) và trung vị (m2) của tổng tài sản dạng tỷ đồng.

Dòng 4–15: Vẽ biểu đồ phân phối: geom_histogram() vẽ biểu đồ tần suất tổng tài sản, geom_density() vẽ đường mật độ, geom_vline() và annotate() đánh dấu, chú thích giá trị trung bình và trung vị.

Nhận xét: Biểu đồ cho thấy tổng tài sản ACL phân bố không đều, nghiêng trái; trung vị nhỏ hơn trung bình, cho thấy một số năm tài sản tăng đột biến kéo giá trị trung bình lên cao.

4.9 Biểu đồ tích lũy trung bình phải trả người bán và lao động

df_mean <- baocaotc_dataset %>%
  select(Nam, Phai_Tra_BN, Phai_Tra_LD) %>%
  pivot_longer(-Nam, names_to="Loai", values_to="GiaTri") %>%
  group_by(Nam, Loai) %>%
  summarise(mean_Ty = mean(GiaTri, na.rm=TRUE)/1e9, .groups="drop") %>%
  mutate(Nam_num = as.integer(as.character(Nam)))
ggplot(df_mean, aes(x = Nam_num, y = mean_Ty, color = Loai, group = Loai)) +
  geom_line(size = 1.2) +
  geom_point(size = 3) +
  geom_text(aes(label = round(mean_Ty,1)), vjust = -1, size = 3.5, fontface = "bold") +
  geom_smooth(method = "lm", se = FALSE, linetype = "dashed", size = 0.9) +
  scale_x_continuous(breaks = unique(df_mean$Nam_num),
                     labels = unique(df_mean$Nam)) +
  scale_color_manual(values = c("Phai_Tra_BN" = "#1F77B4", "Phai_Tra_LD" = "#E67E22"),
                     labels = c("Người bán", "Lao động")) +
  labs(title = "Xu hướng trung bình Phải trả người bán & lao động",
       x = "Năm", y = "Giá trị trung bình (Tỷ đồng)", color = "") +
  theme_minimal(base_size = 14) +
  theme(plot.title = element_text(face="bold", hjust=0.5))

Giải thích:

Dòng 1-6: chọn hai biến Phải trả người bán và Phải trả lao động, chuyển dữ liệu sang dạng dài, tính trung bình theo năm và quy đổi sang tỷ đồng

Dòng 7-19: Các lệnh geom_line(), geom_point() và geom_text() thể hiện xu hướng, giá trị thực tế và nhãn trung bình từng năm; geom_smooth(method=“lm”) thêm đường hồi quy để nhận diện khuynh hướng biến động.

Nhận xét: Kết quả cho thấy phải trả người bán tăng mạnh, dao động rõ rệt qua các năm, trong khi phải trả lao động duy trì mức thấp và ổn định. Điều này chứng tỏ nghĩa vụ với nhà cung cấp chiếm tỷ trọng cao hơn, phản ánh đặc thù hoạt động tài chính của doanh nghiệp.

4.10 Biểu đồ tích lũy Phải trả người bán và lao động

data_von <- baocaotc_dataset %>%
  group_by(Nam) %>%
  summarise(
    No_Phai_Tra = mean(No_Phai_Tra, na.rm = TRUE),
    VCSH = mean(VCSH, na.rm = TRUE)
  ) %>%
  tidyr::pivot_longer(cols = c(No_Phai_Tra, VCSH),
                      names_to = "ChiTieu", values_to = "GiaTri")
ggplot(data_von, aes(x = factor(Nam), y = GiaTri / 1e9, fill = ChiTieu)) +
  geom_col(position = position_dodge(0.8), width = 0.7, alpha = 0.85) +
  geom_text(aes(label = round(GiaTri / 1e9, 1)),
            position = position_dodge(0.8), vjust = -0.4,
            size = 3.6, family = "TimesVN", fontface = "bold") +
  geom_hline(yintercept = mean(data_von$GiaTri / 1e9),
             color = "#E67E22", linetype = "dashed", linewidth = 1) +
  scale_fill_manual(values = c("No_Phai_Tra" = "#5DADE2", "VCSH" = "#52BE80"),
                    labels = c("Nợ phải trả", "Vốn chủ sở hữu")) +
  labs(title = "So sánh Nợ phải trả và Vốn chủ sở hữu của ACL theo năm",
       subtitle = "Giai đoạn 2014–2025 (Đơn vị: Tỷ đồng)",
       x = "Năm", y = "Giá trị (Tỷ đồng)", fill = "Chỉ tiêu") +
  theme_minimal(base_family = "TimesVN", base_size = 14) +
  theme(plot.title = element_text(face = "bold", hjust = 0.5, color = "#1B1F8A", size = 18),
        plot.subtitle = element_text(hjust = 0.5, size = 13, color = "#566573"),
        legend.position = "top")

Giải thích:

Dòng 1–6: Tổng hợp trung bình Nợ phải trả và Vốn chủ sở hữu từng năm, sau đó chuyển về dạng dài với pivot_longer() để chuẩn bị dữ liệu cho biểu đồ.

Dòng 7–24: Vẽ biểu đồ cột so sánh hai chỉ tiêu từng năm bằng ggplot(), sử dụng geom_col() (cột), geom_text() (nhãn giá trị trên cột), và geom_hline() (đường trung bình). Tùy chỉnh màu sắc, nhãn trục, tiêu đề, kích thước chữ giúp biểu đồ dễ đọc, phù hợp báo cáo.

Nhận xét: Biểu đồ cho thấy Nợ phải trả của ACL luôn cao hơn VCSH nhưng vốn tăng đều, đến cuối kỳ hai chỉ tiêu gần bằng nhau. Xu hướng này phản ánh doanh nghiệp đang cải thiện cân bằng vốn, giảm phụ thuộc vào nợ qua các năm.

4.11 Biểu đồ so sánh Nợ ngắn hạn và Nợ phải trả

no_mean <- baocaotc_dataset %>%
  select(Nam, No_Ngan_Han, No_Phai_Tra) %>%
  group_by(Nam) %>%
  summarise(across(c(No_Ngan_Han, No_Phai_Tra), ~mean(.x, na.rm=TRUE))) %>%
  pivot_longer(-Nam, names_to="Loai_No", values_to="GiaTri") %>%
  mutate(Nam_num = as.integer(as.character(Nam)),
         GiaTy = GiaTri / 1e9) %>%
  arrange(Nam_num)
years <- unique(no_mean$Nam_num)
labs  <- unique(no_mean$Nam)
ggplot(no_mean, aes(x = Nam_num, y = GiaTy, fill = Loai_No)) +
  geom_col(aes(group = Loai_No), 
           position = position_dodge(width = 0.8),  
           width = 0.6, color = "black", alpha = 0.9) +
  geom_text(aes(label = round(GiaTy,1)),
            position = position_dodge(width = 0.8), 
            hjust = -0.2, size = 4, fontface = "bold", color = "black") +
  geom_smooth(aes(color = Loai_No),
              method = "lm", se = FALSE, linetype = "dashed", size = 0.9) +
  scale_x_continuous(breaks = years, labels = labs) +
  scale_fill_manual(values = c("#82E0AA","#5D6D7E"),
                    labels = c("Nợ ngắn hạn","Tổng nợ")) +
  scale_color_manual(values = c("#239B56","#2E4053"), guide = "none") +
  labs(title = "Xu hướng trung bình Nợ ngắn hạn và Tổng nợ của ACL",
       x = "Năm", y = "Giá trị trung bình (Tỷ đồng)", fill = "Loại nợ") +
  coord_flip() +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face="bold", hjust=0.5, size=18, color="#1B1F8A"),
    axis.title = element_text(face="bold", size=13),
    axis.text = element_text(size=12, face="bold"))

Giải thích:

Dòng 1–8: Chuẩn bị dữ liệu: chọn các biến Nam, No_Ngan_Han, No_Phai_Tra, nhóm theo năm, tính trung bình từng loại nợ, chuyển dữ liệu sang dạng dài (pivot_longer), quy đổi sang tỷ đồng và sắp xếp thứ tự năm.

Dòng 9–10: Tạo danh sách năm (years, labs) để làm mốc hiển thị trên trục tọa độ.

Dòng 11–17: Khởi tạo biểu đồ với ggplot(). Dùng geom_col() vẽ các cột song song biểu diễn giá trị trung bình từng loại nợ, geom_text() hiển thị nhãn giá trị trên cột, và geom_smooth() thêm đường xu hướng hồi quy tuyến tính dạng gạch đứt.

Dòng 18–23: Thiết lập thang và màu sắc cho trục, phân biệt hai loại nợ bằng màu xanh đậm và xanh nhạt; ẩn phần legend của đường xu hướng.

Dòng 24–31: Đặt tiêu đề, tên trục, nhãn và kiểu hiển thị. coord_flip() xoay biểu đồ nằm ngang; theme_minimal() và theme() căn giữa tiêu đề, chỉnh cỡ chữ và làm đậm nội dung.

Nhận xét: Biểu đồ cho thấy Tổng nợ và Nợ ngắn hạn đều tăng qua các năm, thể hiện xu hướng mở rộng quy mô vay của doanh nghiệp. Khoảng cách giữa hai loại nợ không lớn, cho thấy nợ ngắn hạn chiếm tỷ trọng chủ yếu trong tổng nợ, phản ánh đặc trưng tài chính của ACL với nguồn vốn ngắn hạn là chủ đạo.

4.12 Biểu đồ Tỷ trọng Vốn chủ sở hữu vs Nợ

df13 <- baocaotc_dataset %>%
mutate(VCSH_Ty = VCSH/1e9, No_Ty = No_Phai_Tra/1e9) %>%
select(Nam, VCSH_Ty, No_Ty) %>%
pivot_longer(cols = c(VCSH_Ty, No_Ty), names_to = "Type", values_to = "Value") %>%
group_by(Nam) %>%
mutate(pc = Value / sum(Value, na.rm = TRUE))
ggplot(df13, aes(x = factor(Nam), y = pc, fill = Type)) +
geom_col(position = "fill", color = "white", width = 0.7) + 
scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
geom_text(aes(label = ifelse(pc>0.08, paste0(round(pc*100,0), "%"), "")),
position = position_fill(vjust = 0.5), size = 3) + 
geom_hline(yintercept = 0.5, linetype = "dashed", color = "gray60") +
labs(title = "Tỷ trọng Vốn chủ sở hữu vs Nợ",
x = "Năm", y = "Tỷ lệ (%)", fill = "Nguồn vốn") +
theme_minimal(base_size = 14) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5, size = 18, color = "#1B1F8A"),
    axis.title = element_text(face = "bold"))

Giải thích:

Dòng 1–3: Tạo dữ liệu mới df13, quy đổi hai biến VCSH và No_Phai_Tra sang tỷ đồng, chỉ giữ các cột cần thiết (Nam, VCSH_Ty, No_Ty).

Dòng 4–6: Dùng pivot_longer() để chuyển dữ liệu sang dạng dài, gom hai cột thành một cột “Type”, rồi nhóm theo năm để tính tỷ trọng từng loại vốn (pc = Value / tổng giá trị năm).

Dòng 7–10: Vẽ biểu đồ cột chồng (geom_col()) thể hiện tỷ lệ phần trăm VCSH và Nợ qua các năm, hiển thị nhãn phần trăm bằng geom_text() và định dạng trục Y ở dạng %.

Dòng 11–19: Thêm geom_hline(yintercept = 0.5) làm đường mốc 50% để dễ so sánh tỷ trọng giữa hai thành phần. Đặt tiêu đề, tên trục, chú giải và theme tối giản giúp biểu đồ rõ ràng, dễ đọc và cân đối khi in.

Nhận xét: Biểu đồ cho thấy nợ chiếm tỷ trọng cao hơn vốn chủ sở hữu trong hầu hết các năm, thể hiện doanh nghiệp phụ thuộc vào nguồn vốn vay. Từ năm 2021 trở đi, tỷ trọng vốn chủ sở hữu tăng nhẹ, đến năm 2025 đạt gần 46%, cho thấy xu hướng cải thiện cơ cấu tài chính theo hướng an toàn và bền vững hơn.

4.13 Biểu đồ ma trận tương quan giữa các chỉ tiêu tài chính của ACL

corr_data <- baocaotc_dataset[, c("TTS", "TSNH", "No_Phai_Tra", "VCSH", "ROA", "ROE", "LNST")]
corr_matrix <- cor(corr_data, use = "complete.obs")
ggcorrplot(corr_matrix,
           hc.order = TRUE, lab = TRUE,
           lab_size = 4, outline.col = "white",
           colors = c("#B71C1C", "white", "#1B5E20")) +
  labs(title = "Ma trận tương quan giữa các chỉ tiêu tài chính của ACL",
       subtitle = "Độ đậm thể hiện mức độ tương quan giữa các biến") +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", size = 20, color = "#1B1F8A", hjust = 0.5),
    plot.subtitle = element_text(size = 13, color = "gray40", hjust = 0.5),
    axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1, face = "bold"),
    axis.text.y = element_text(face = "bold"),
    plot.margin = margin(20, 30, 20, 30))

Giải thích:

Dòng 1–2: Trích xuất dữ liệu gồm các biến tài chính quan trọng và tính ma trận tương quan Pearson giữa chúng bằng cor().

Dòng 3–5: Sử dụng ggcorrplot() để trực quan hóa ma trận tương quan dưới dạng ô màu, trong đó màu xanh biểu thị tương quan dương và đỏ biểu thị tương quan âm. Các giá trị tương quan được hiển thị trực tiếp và sắp xếp theo nhóm.

Dòng 6–15: Tùy chỉnh bố cục biểu đồ gồm tiêu đề, phụ đề, màu sắc, phông chữ và góc nghiêng trục.

Nhận xét: Kết quả cho thấy các biến TTS, TSNH, VCSH, LNST, ROA và ROE có tương quan dương mạnh (r > 0.6), thể hiện rằng khi quy mô tài sản và vốn tăng thì lợi nhuận và khả năng sinh lời cũng tăng. Ngược lại, Nợ phải trả có tương quan yếu hoặc âm với các chỉ tiêu lợi nhuận, cho thấy tăng vay nợ có thể làm giảm hiệu quả tài chính của doanh nghiệp ACL.

4.14 Biểu đồ xu hướng trung bình ROA và ROE theo Năm của ACL

roa_roe_tb_nam <- baocaotc_dataset %>%
  group_by(Nam) %>%
  summarise(ROA_tb = mean(ROA, na.rm = TRUE),
            ROE_tb = mean(ROE, na.rm = TRUE)) %>%
  pivot_longer(cols = c("ROA_tb", "ROE_tb"),
               names_to = "ChiTieu", values_to = "GiaTri")
ggplot(roa_roe_tb_nam, aes(x = factor(Nam), y = GiaTri, color = ChiTieu, group = ChiTieu)) +
  geom_line(linewidth = 1.3) +                                
  geom_point(size = 4, alpha = 0.9) +                        
  geom_smooth(method = "lm", se = FALSE, linetype = "dotted",
              linewidth = 1, color = "gray40") +               
  geom_text(aes(label = round(GiaTri,2)), vjust = -0.6, 
            size = 3.5, fontface = "bold", color = "#2E4053") + 
  geom_hline(yintercept = mean(roa_roe_tb_nam$GiaTri),         
             linetype = "dashed", color = "gray50", linewidth = 1) +
  scale_color_manual(values = c("ROA_tb"="#1ABC9C", "ROE_tb"="#9B59B6"),
                     labels = c("ROA trung bình", "ROE trung bình")) +
  labs(title = "Xu hướng trung bình ROA và ROE theo Năm của ACL",
       subtitle = "Trung bình các chỉ tiêu sinh lời qua từng năm trong giai đoạn phân tích",
       x = "Năm", y = "Tỷ lệ (%)", color = "Chỉ tiêu") +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face="bold", size=20, color="#1B1F8A", hjust=.5),
    plot.subtitle = element_text(size=13, color="gray40", hjust=.5),
    axis.title = element_text(face="bold"),
    axis.text = element_text(face="bold"),
    panel.grid.minor = element_blank(),
    panel.grid.major.y = element_line(color="gray85"),
    plot.margin = margin(20,30,20,30))

Giải thích:

Dòng 1–3: Tạo dữ liệu mới roa_roe_tb_nam từ baocaotc_dataset, nhóm theo Nam và tính giá trị trung bình của ROA và ROE qua từng năm bằng summarise().

Dòng 4: Dùng pivot_longer() để chuyển dữ liệu sang dạng dài, gom hai biến ROA_tb và ROE_tb thành cột “Chỉ tiêu”.

Dòng 5–13: vẽ đồ thị đường thể hiện xu hướng ROA và ROE theo năm; thêm điểm (geom_point) và nhãn giá trị (geom_text). Đường xu hướng (geom_smooth) giúp làm mượt dữ liệu và dễ nhận dạng xu thế.

Dòng 14–29: Thêm đường trung bình toàn kỳ (geom_hline) làm mốc so sánh; thiết lập màu sắc thủ công cho từng chỉ tiêu. Tùy chỉnh tiêu đề, trục và theme để biểu đồ cân đối.

Nhận xét: Biểu đồ cho thấy ROE luôn cao hơn ROA, chứng tỏ doanh nghiệp sử dụng đòn bẩy tài chính tích cực. Giai đoạn 2018–2019 ROE tăng mạnh đạt đỉnh 0.61, phản ánh hiệu quả sinh lời cao, nhưng sau đó giảm do chi phí vốn tăng. Từ năm 2021–2025, cả ROA và ROE ổn định quanh mức thấp hơn, cho thấy hoạt động tài chính dần ổn định nhưng hiệu suất giảm so với giai đoạn trước.

4.15 Biểu đồ radar hiệu quả sinh lời của ACL

radar2 <- baocaotc_dataset %>%
select(Nam, ROA, ROE, LNST) %>%
group_by(Nam) %>%
summarise(across(where(is.numeric), mean, na.rm = TRUE)) %>%
arrange(Nam)
radar_scaled2 <- as.data.frame(scale(radar2[,-1], center = TRUE, scale = TRUE))
radar_scaled2$Nam <- radar2$Nam
radar_scaled2 <- radar_scaled2 %>% relocate(Nam)
bound2 <- ceiling(max(abs(as.matrix(radar_scaled2[,-1])), na.rm = TRUE) * 1.2)
radar_plot2 <- rbind(rep(bound2, ncol(radar_scaled2)-1),
rep(-bound2, ncol(radar_scaled2)-1),
radar_scaled2[,-1])
rownames(radar_plot2) <- c("Max","Min", radar_scaled2$Nam)
colors2 <- brewer.pal(n = min(8, nrow(radar_scaled2)), name = "Set3")
par(mar = c(2, 2, 6, 2))
radarchart(radar_plot2,
axistype = 1,
pcol = colors2,
pfcol = alpha(colors2, 0.3),
plwd = 3,               
cglcol = "gray60",
cglty = 1,
axislabcol = "gray40",
caxislabels = pretty(c(-bound2, bound2), 5),
cglwd = 1.0,
vlcex = 1.3,            
title = "Radar chart – Hiệu quả sinh lời của ACL")
legend("topright", legend = radar_scaled2$Nam,
col = colors2, lwd = 3, bty = "n", cex = 1.1)

Giải thích:

Dòng 1–5: Tạo dữ liệu radar2 từ baocaotc_dataset, chọn ba biến đo hiệu quả sinh lời và nhóm theo Năm. Hàm summarise() tính giá trị trung bình từng năm, sau đó arrange() sắp xếp theo thứ tự năm tăng dần.

Dòng 6–8: Chuẩn hóa dữ liệu bằng scale() để các biến có thang đo tương đồng, chuyển về dạng dataframe, thêm lại cột Nam và di chuyển nó ra vị trí đầu tiên để dễ thao tác khi vẽ biểu đồ.

Dòng 9–15: Tạo biến biên độ (bound2) làm giới hạn cực trị cho biểu đồ radar, ghép giá trị giới hạn trên và dưới (Max, Min) vào dữ liệu. Dòng 13–15: Đặt tên hàng theo năm và sinh bảng màu Set3 bằng brewer.pal để mỗi năm hiển thị màu riêng biệt.

Dòng 16–29: Thiết lập thông số vẽ bằng par() và radarchart(): độ trong (pfcol), màu viền (cglcol, cglty), tiêu đề (title), nhãn trục (axislabels) và chú thích (legend). Biểu đồ được hiển thị dưới dạng tam giác thể hiện đồng thời ba chỉ tiêu ROA, ROE, LNST.

Nhận xét: Biểu đồ radar cho thấy năm 2019 có diện tích lớn nhất, phản ánh hiệu quả sinh lời cao nhất trong giai đoạn 2014–2025. Giai đoạn 2020–2021 vùng radar thu hẹp cho thấy suy giảm lợi nhuận, trong khi các năm 2023–2025 diện tích ổn định hơn, chứng tỏ doanh nghiệp ACL đã phục hồi và duy trì mức sinh lời ổn định nhưng chưa đạt lại đỉnh hiệu suất như năm 2019.

4.16 Biểu đồ cơ cấu tài sản và nguồn vốn của ACL

baocaotc_dataset %>%
  select(Nam, TTS, No_Phai_Tra, VCSH) %>%
  pivot_longer(-Nam, names_to="Cấu_phần", values_to="Giá_trị") %>%
  ggplot(aes(factor(Nam), Giá_trị/1e9, fill=Cấu_phần)) +
  geom_col(alpha=.9, color="black", width=.75) +
  geom_text(aes(label=round(Giá_trị/1e9,1)), 
            position=position_stack(vjust=.5), size=3, fontface="bold") +
  scale_fill_manual(values=c("#5DADE2","#F5B041","#58D68D"),
                    labels=c("Tổng tài sản","Nợ phải trả","Vốn CSH")) +
  scale_y_continuous(labels=label_number(suffix=" Tỷ")) +
  labs(title="Cơ cấu tài sản và nguồn vốn của ACL",
       x="Năm", y="Giá trị (Tỷ đồng)", fill="Thành phần") +
  theme_minimal(base_size=14) +
  theme(plot.title=element_text(face="bold",size=18,hjust=.5,color="#1B1F8A"),
        axis.title=element_text(face="bold"))

Giải thích:

Dòng 1–3: Lấy các biến chính (Nam, TTS, No_Phai_Tra, VCSH) từ bộ dữ liệu baocaotc_dataset, sau đó chuyển sang dạng dài bằng pivot_longer() để dễ biểu diễn nhiều thành phần trên cùng trục năm.

Dòng 4–7: Dùng ggplot() vẽ biểu đồ cột chồng (geom_col) thể hiện cơ cấu tài sản và nguồn vốn theo năm, thêm nhãn giá trị (geom_text) để minh họa chi tiết từng phần.

Dòng 8–15: Thiết lập màu sắc, nhãn trục, tiêu đề và theme giúp biểu đồ rõ ràng và dễ đọc khi in PDF.

Nhận xét: Biểu đồ cho thấy tổng tài sản, nợ phải trả và vốn chủ sở hữu của ACL đều tăng qua các năm. Tỷ trọng nợ phải trả chiếm phần lớn, chứng tỏ doanh nghiệp duy trì đòn bẩy tài chính cao, trong khi vốn chủ sở hữu tăng chậm hơn, phản ánh sự ổn định nhưng phụ thuộc vào nguồn vốn vay.

4.17 Biểu đồ thể hiện mối quan hệ giữa ROA và ROE của ACL

ggplot(baocaotc_dataset, aes(x = ROA, y = ROE)) +
  geom_point(aes(color = factor(Nam)), size = 4, alpha = 0.9) +
  geom_smooth(method = "lm", se = FALSE, color = "#2C3E50", linewidth = 1.2, linetype = "dashed") +
  geom_text_repel(aes(label = Nam, color = factor(Nam)),
                  size = 4, fontface = "bold",
                  box.padding = 0.3, point.padding = 0.25,
                  max.overlaps = 20,    # hạn chế chồng nhãn
                  show.legend = FALSE) +
  scale_color_viridis_d(option = "turbo", name = "Năm") +
  labs(
    title = "Mối quan hệ giữa ROA và ROE của ACL",
    subtitle = "Tương quan giữa hiệu quả sử dụng tài sản và vốn chủ sở hữu",
    x = "ROA (%)",
    y = "ROE (%)"
  ) +
  theme_minimal(base_size = 16) +
  theme(
    plot.title = element_text(face = "bold", size = 22, color = "#1B1F8A", hjust = 0.5),
    plot.subtitle = element_text(size = 13, color = "gray40", hjust = 0.5),
    axis.title = element_text(face = "bold"),
    panel.grid.minor = element_blank(),
    panel.grid.major = element_line(color = "gray85", linewidth = 0.4),
    legend.position = "right")

Giải thích:

Dòng 1–3: Vẽ biểu đồ phân tán giữa ROA và ROE để xem mối tương quan hai chỉ tiêu sinh lời; các điểm được mã màu theo từng năm, thêm đường hồi quy tuyến tính (geom_smooth) biểu thị xu hướng chung.

Dòng 4–10: Dùng geom_text_repel() gắn nhãn năm vào từng điểm, tránh chồng chữ giúp dễ nhận diện. Dùng scale_color_viridis_d() tạo bảng màu gradient liên tục từ năm 2014–2025, tăng tính trực quan.

Dòng 11–23: Tùy chỉnh tiêu đề, trục, chú thích và theme (theme_minimal) để biểu đồ rõ ràng, nổi bật mối tương quan.

Nhận xét: Biểu đồ cho thấy ROE tăng đồng biến với ROA, chứng tỏ mối tương quan thuận rất mạnh giữa hiệu quả sử dụng tài sản và vốn chủ sở hữu. Giai đoạn 2018–2019 có giá trị cao nhất, phản ánh hiệu suất sinh lời tối ưu, trong khi các năm 2021–2025 thu hẹp dần, cho thấy doanh nghiệp duy trì ổn định nhưng biên lợi nhuận giảm nhẹ.

4.18 Biểu đồ Thác nước – Biến động LNST của ACL

lnst_change <- baocaotc_dataset %>%
  group_by(Nam) %>%
  summarise(LNST = mean(LNST, na.rm = TRUE)) %>%
  arrange(Nam) %>%
  mutate(Change = LNST - lag(LNST, default = first(LNST)),
         Direction = ifelse(Change >= 0, "Tăng", "Giảm"),
         Label = paste0(round(LNST / 1e9, 1), " Tỷ"))
lnst_change$Start <- c(0, head(cumsum(lnst_change$Change), -1))
lnst_change$End <- cumsum(lnst_change$Change)
lnst_change$id <- 1:nrow(lnst_change)
ggplot(lnst_change, aes(x = id)) +
  geom_rect(aes(xmin = id - 0.4, xmax = id + 0.4,
                ymin = pmin(Start, End), ymax = pmax(Start, End),
                fill = Direction),
            color = "black", alpha = 0.9) +
  geom_text(aes(y = End, label = Label),
            vjust = ifelse(lnst_change$Change >= 0, -0.5, 1.2),
            size = 4, fontface = "bold", color = "#2C3E50") +
  scale_fill_manual(values = c("Tăng" = "#58D68D", "Giảm" = "#E74C3C")) +
  scale_y_continuous(labels = label_number(scale = 1e-9, suffix = " Tỷ")) +
  scale_x_continuous(breaks = lnst_change$id, labels = lnst_change$Nam) +
  labs(title = "Biểu đồ Thác nước – Biến động LNST của ACL",
       subtitle = "Thể hiện mức tăng/giảm lợi nhuận sau thuế qua các năm",
       x = "Năm", y = "LNST (Tỷ đồng)", fill = "Xu hướng") +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face="bold", size=20, color="#1B1F8A", hjust=.5),
    plot.subtitle = element_text(size=13, color="gray40", hjust=.5),
    axis.title = element_text(face="bold"),
    axis.text = element_text(size=12))

Giải thích:

Dòng 1–3: Nhóm dữ liệu theo Năm, tính LNST trung bình mỗi năm và sắp xếp tăng dần theo thời gian.

Dòng 4–7: Tính mức thay đổi lợi nhuận so với năm trước bằng lag(), gán nhãn “Tăng” hoặc “Giảm”, đồng thời tạo cột nhãn hiển thị giá trị (đơn vị tỷ đồng).

Dòng 8–10: Xác định điểm bắt đầu và kết thúc cho từng thanh trong biểu đồ thác nước.

Dòng 11–30: Vẽ biểu đồ với geom_rect() biểu diễn các đoạn tăng (màu xanh) và giảm (màu đỏ), thêm giá trị bằng geom_text(), tùy chỉnh trục, màu sắc, tiêu đề và theme.

Nhận xét: Biểu đồ cho thấy LNST của ACL tăng mạnh từ 2017–2019, đạt đỉnh 402.8 tỷ năm 2019, sau đó giảm sâu năm 2020 và ổn định trở lại từ 2022–2025. Xu hướng này phản ánh giai đoạn phục hồi sau khủng hoảng và hiệu quả kinh doanh dần ổn định.

4.19 Biểu đồ cơ cấu vốn của ACL qua các năm

von_long <- baocaotc_dataset %>%
  select(Nam, No_Phai_Tra, VCSH) %>%
  group_by(Nam) %>%
  summarise(across(everything(), mean, na.rm = TRUE)) %>%
  pivot_longer(cols = c(No_Phai_Tra, VCSH), 
               names_to = "Thành_phần", 
               values_to = "Giá_trị")
ggplot(von_long, aes(x = factor(Nam), y = Giá_trị, fill = Thành_phần)) +
  geom_bar(stat = "identity", color = "black", alpha = 0.9, 
           position = position_dodge(width = 0.9)) +
  geom_text(aes(label = paste0(round(Giá_trị / 1e9, 1))),
            position = position_dodge(width = 0.9),
            vjust = -0.3, size = 4.2, fontface = "bold", color = "#2C3E50") +
  scale_y_continuous(labels = label_number(scale = 1e-9, suffix = " Tỷ")) +
  scale_fill_manual(values = c("#F5B041", "#5DADE2"),
                    labels = c("Nợ phải trả", "Vốn chủ sở hữu")) +
  labs(title = "Cơ cấu vốn của ACL qua các năm",
       x = "Năm", y = "Giá trị (Tỷ đồng)", fill = "Thành phần") +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5, size = 18, color = "#1B1F8A"),
    axis.text = element_text(size = 12),
    legend.position = "top")

Giải thích:

Dòng 1–4: Lọc các biến Nam, No_Phai_Tra, VCSH, tính giá trị trung bình mỗi năm, chuyển sang dạng dài bằng pivot_longer() để vẽ so sánh hai thành phần vốn.

Dòng 5–9: Vẽ biểu đồ cột song song (grouped bar chart) thể hiện nợ phải trả và vốn chủ sở hữu, thêm giá trị bằng geom_text() để minh họa cụ thể.

Dòng 10–23: Tùy chỉnh trục, màu sắc, nhãn và theme để biểu đồ rõ ràng, dễ đọc khi in PDF.

Nhận xét: Biểu đồ cho thấy nợ phải trả luôn cao hơn vốn chủ sở hữu, phản ánh doanh nghiệp sử dụng nhiều vốn vay. Tuy nhiên, vốn chủ sở hữu tăng dần từ 2018–2025, cho thấy ACL đang cải thiện khả năng tự chủ tài chính và giảm dần phụ thuộc vào nợ vay.

4.20 Biểu đồ thể hiện tăng trưởng Tổng tài sản của ACL qua các năm

data_tts <- baocaotc_dataset %>%
  group_by(Nam) %>%
  summarise(TTS = mean(TTS, na.rm = TRUE))
ggplot(data_tts, aes(x = Nam, y = TTS/1e9, group = 1)) +
  geom_area(fill = "#A9DFBF", alpha = 0.5) +
  geom_line(color = "#1B4F72", linewidth = 1.2) +
  geom_point(size = 3.5, color = "#117A65") +
  geom_text(aes(label = round(TTS/1e9, 1)),
            vjust = -0.5, size = 3.8, fontface = "bold", family = "TimesVN") +
  geom_hline(yintercept = mean(data_tts$TTS/1e9),
             linetype = "dashed", color = "#F39C12", linewidth = 1) +
  labs(title = "Tăng trưởng Tổng tài sản của ACL qua các năm",
       subtitle = "Giai đoạn 2014–2025 (Đơn vị: Tỷ đồng)",
       x = "Năm", y = "Tổng tài sản (Tỷ đồng)") +
  theme_minimal(base_size = 14, base_family = "TimesVN") +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5, color = "#1B1F8A", size = 18),
    plot.subtitle = element_text(hjust = 0.5, size = 13, color = "#34495E"),
    axis.title.y = element_text(face = "bold"),
    axis.title.x = element_text(face = "bold") )

Giải thích:

Dòng 1–3: Nhóm dữ liệu theo Nam, tính tổng tài sản trung bình của ACL qua từng năm.

Dòng 4–10: Vẽ biểu đồ vùng (area chart) thể hiện xu hướng tăng của tổng tài sản, kết hợp đường viền (geom_line) và điểm (geom_point) để làm rõ giá trị từng năm.

Dòng 11–14: Thêm đường trung bình (geom_hline) để so sánh mức tài sản từng năm so với trung bình giai đoạn 2014–2025.

Dòng 15–20: Tùy chỉnh tiêu đề, nhãn trục và font để biểu đồ rõ ràng, dễ đọc khi in PDF.

Nhận xét: Tổng tài sản của ACL tăng liên tục từ 2014 đến 2025, nổi bật nhất giai đoạn 2018–2020 và duy trì xu hướng ổn định sau 2021. Mức tài sản trung bình toàn kỳ khoảng 1.450 tỷ đồng, cho thấy quy mô tài sản doanh nghiệp ngày càng mở rộng và phát triển bền vững.