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 tôi 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.
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.
Tên bộ dữ liệu: “Comprehensive Films Dataset for Analysis”.
Nguồn bộ dữ liệu: https://www.kaggle.com/datasets/emanfatima2025/comprehensive-films-dataset-for-analysis/data.
Thông tin bộ dữ liệu: Bộ dữ liệu này 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 và tính toán các chỉ tiêu tài chính bằng ngôn ngữ lập trình
R.
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/thông điệp.
movies_dataset <- read.csv("D:/movies_dataset.csv")
Giải thích: Lệnh read.csv được sử dụng để đọc file dữ liệu định dạng CSV vào R và tạo ra một data frame tên movies_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(movies_dataset)
## [1] 999999 17
Giải thích: Kết quả trả về số quan sát là 999999 dòng và số biến là 17 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.
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: Hiển thị tên tất cả các biến, giúp người đọc biết ngay dữ liệu gồm những thông tin gì. Điều này rất quan trọng để chọn biến phù hợp cho từng phân tích.
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(movies_dataset, 4) lấy ra 4 dòng đầu tiên của bộ dữ liệu movies_dataset nhằm minh họa mẫu dữ liệu. Hàm t() thực hiện chuyển vị ma trận, tức là đổi hàng thành cột và cột thành hàng, giúp các biến được hiển thị theo chiều dọc — phù hợp khi bảng có nhiều biến, tránh bị tràn ngang khi xuất ra PDF. Kết hợp với các hàm kable và kable_styling để tạo bảng đẹp cũng như thu hẹp kích thước bảng.
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(movies_dataset, 4) lấy ra 4 dòng cuối cùng của bộ dữ liệu movies_dataset nhằm minh họa mẫu dữ liệu. Hàm t() thực hiện chuyển vị ma trận, tức là đổi hàng thành cột và cột thành hàng, giúp các biến được hiển thị theo chiều dọc — phù hợp khi bảng có nhiều biến, tránh bị tràn ngang khi xuất ra PDF. Kết hợp với các hàm kable và kable_styling để tạo bảng đẹp cũng như thu hẹp kích thước bản
sum(sapply(movies_dataset, is.numeric))
## [1] 11
Giải thích sapply() áp dụng hàm is.numeric() cho tất cả các biến (cột) của bộ dữ liệu movies_dataset = Hàm này sẽ trả về một vector chứa TRUE/FALSE, thể hiện các biến nào là kiểu số (numeric). Hàm sum() tính tổng số TRUE (đếm biến kiểu số).
sum(sapply(movies_dataset, is.character) | sapply(movies_dataset, is.factor))
## [1] 6
Giải thích: sapply() lần lượt kiểm tra từng biến xem có phải kiểu character hoặc factor không. Dùng phép OR | giữa hai kết quả để tổng hợp tất cả biến thuộc dạng phân loại, dạng chữ hoặc phân loại (ví dụ: thể loại phim, quốc gia, đạo diễn). Hàm sum() đếm tổng số biến dạng này.
loại <- data.frame(Kiểu_dữ_liệu = sapply(movies_dataset, class))
kable(loại, col.names = c("Tên biến", "Kiểu dữ liệu"), caption = "Kết quả kiểu dữ liệu các biến")
| 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() để áp dụng hàm class cho tất cả các cột của dữ liệu movies_dataset. Kết quả trả về là một vector gồm các kiểu dữ liệu như “integer”, “numeric”, “character”, “factor”.V.V
Hàm data.frame() chuyển đổi kết quả này thành bảng, gồm hai cột: tên biến và kiểu dữ liệu của biến đó. Hàm kable() giúp hiển thị bảng này dưới dạng đẹp,dễ đọc. Với caption hiện tên của bảng
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.
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ờ đó 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.
kable(table(movies_dataset$Genre), col.names = c("Thể loại", "Tần suất"),
caption = "Kết quả tần suất các thể loại phim")
| 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: Hàm table() đếm số lần xuất hiện của mỗi giá trị trong biến Genre, trả về bảng tần suất của từng thể loại phim. Nhờ vậy, có thể dễ dàng nhận biết thể loại nào nhiều nhất, ít nhất trong tập phim.
Nhận xét chung: Bộ ba thao tác trên cho phép kiểm tra chi tiết đặc điểm biến phân loại: biết được danh sách thể loại, mức độ đa dạng và phân bố tần suất từng loại phim.
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", "thangdu", "PopularityIndex",
"Thapky", "Season"), 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",
"Thặng dư phòng vé (đơn vị USD)", "Chỉ số tổng hợp mức độ phổ biến của phim(thang điểm 10))",
"Thập kỷ", "Yếu tố mùa"), stringsAsFactors = FALSE)
kable(variable_meaning, col.names = c("Tên biến", "Ý nghĩa"), caption = "Kết quả ý nghĩa tên các biến")
| 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 |
| thangdu | Thặng dư phòng vé (đơn vị USD) |
| PopularityIndex | Chỉ số tổng hợp mức độ phổ biến của phim(thang điểm 10)) |
| Thapky | Thập kỷ |
| Season | Yếu tố mùa |
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 (ý nghĩa chi tiết, giải thích chức năng/chủ đề từng biến).
Nhận xét:
Việc bổ sung bảng giải thích biến ngay từ đầu tiểu luận giúp người đọc không bị nhầm lẫn về tên trường, ý nghĩa và cách sử dụng, đặc biệt cần thiết khi tên biến là tiếng Anh hoặc viết tắt khó hiểu. Việc này thể hiện sự khoa học, chuyên nghiệp và là điểm cộng rất lớn về hình thức khi trình bày báo cáo/tài liệu phân tích dữ liệu.
Nhờ bảng này, mọi thao tác, phân tích, trực quan hóa về sau đều trở nên mạch lạc và dễ tiếp cận cho bất kỳ ai sử dụng hay đọc lại báo cáo, ngay cả khi không quen với bộ dữ liệu phim.
sum(duplicated(movies_dataset))
## [1] 0
any(is.na(movies_dataset))
## [1] FALSE
Giải thích:
Dòng 1 kiểm tra trong toàn bộ data frame movies_dataset có bao nhiêu dòng bị lặp lại hoàn toàn so với dòng trước đó. Hàm duplicated() trả về một vector logic (TRUE với dòng là bản sao, FALSE nếu là lần xuất hiện đầu), còn sum() đếm tổng số dòng bị lặp.
Dòng 1 kiểm tra xem có ô dữ liệu nào bị thiếu (NA) trong toàn bộ bảng dữ liệu hay không. Hàm is.na() trả về TRUE ở vị trí nào bị thiếu, any() trả về TRUE nếu có ít nhất một giá trị thiếu, FALSE nếu toàn bộ bảng không có NA.
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), dữ liệu đã được làm sạch và không cần xử lý.
movies_dataset$ReleaseDate <- parse_date_time(movies_dataset$ReleaseDate, orders = c("dmy",
"mdy", "ymd"))
Giải thích: Hàm parse_date_time() chuyển đổi dữ liệu ngày phát hành phim (ReleaseDate) từ dạng chuỗi ký tự với nhiều kiểu nhập khác nhau về chuẩn ngày tháng của R. Tham số orders = c(“dmy”, “mdy”, “ymd”) tự nhận biết chuỗi ngày ở dạng nào và chuyển đổi đúng ý. Sau khi chuyển xong, mọi giá trị đều được hiển thị theo chuẩn quốc tế ISO “yyyy-mm-dd”.
Nhận xét: Kết quả thu được là biến ngày tháng hoàn toàn đồng nhất theo chuẩn “năm-tháng-ngày”, giúp dữ liệu sạch, rất thuận lợi cho các thao tác phân tích chuỗi thời gian, lọc, nhóm theo năm hoặc trực quan hóa.
movies_dataset$ReleaseDate <- as.Date(movies_dataset$ReleaseDate)
Giải thích: as.Date() là hàm xử lý trong R dùng để chuyển đổi các chuỗi ký tự biểu diễn ngày (như “2021-07-24”, “2020-01-01”) thành định dạng ngày
Nhận xét: Việc chuyển đổi về kiểu ngày giúp cho dữ liệu chuẩn hóa, dễ xử lý và phù hợp với các phép toán về thời gian như tính khoảng cách giữa hai ngày, trích xuất tháng/ngày/năm, lọc theo mùa vụ, ngày lễ…
movies_dataset$RottenTomatoes_10Score <- movies_dataset$RottenTomatoesScore/10
Giải thích: Dùng để chuẩn hóa thang điểm của biến RottenTomatoesScore từ thang 100 điểm sang 10 điểm bằng cách lấy RottenTomatoesScore chia cho 10 và có thể so sánh trực tiếp với IMDb Rating. Kết quả trả ra một cột mới tên RottenTomatoes_Score10 trong dataset, chứa giá trị điểm Rotten Tomatoes đã được quy đổi.
Nhận xét: Việc chuẩn hóa thang điểm là bước xử lý dữ liệu thô quan trọng giúp các biến có ý nghĩa tương đồng có thể được so sánh hoặc tính toán thống kê chính xác (như trung bình, tương quan).
movies_dataset$ThapKy <- paste0(floor(movies_dataset$ReleaseYear/10) * 10, "s")
Giải thích: movies_dataset$ReleaseYear / 10 chia năm phát hành cho 10. Hàm floor(…): làm tròn xuống đến số nguyên gần nhất. Sau đó nhân lại với 10 để lấy thập kỷ. paste0(…, “s”): ghép thêm chữ “s” phía cuối thể hiện thập kỷ. Kết quả tạo ra một cột mới tên ThapKy theo công thức:
\[ ThapKy = \lfloor \frac{ReleaseYear}{10} \rfloor \times 10 \]
Nhận xét: Dùng để nhóm các bộ phim theo thập kỷ, giúp phân tích xu hướng theo thời gian dài hạn. Sau đó dùng biến này để làm phân nhóm khi trực quan hóa.
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.
movies_dataset$thangdu <- movies_dataset$Global_BoxOfficeUSD - movies_dataset$BudgetUSD
Giải thích: Dòng lệnh trên sẽ tạo một cột mới tên là thangdu cho bộ dữ liệu với giá trị mỗi dòng tính theo công thức sau:
\[ Thangdu = Global\_BoxOfficeUSD - BudgetUSD \]
Nhận xét: Kết quả phản ánh số tiền thặng dư (lời/lỗ) mà mỗi phim kiếm được so với chi phí đầu tư sản xuất. Có thể dùng biến này để phân tích các nhóm phim lời/lỗ,
movies_dataset$Weekend_boost <- (movies_dataset$One_Week_SalesUSD - movies_dataset$Opening_Day_SalesUSD)/movies_dataset$Opening_Day_SalesUSD
Giải thích:Biến Weekend1_boost sẽ lưu trữ chênh lệch mức tăng trưởng doanh thu trong tuần đầu so với ngày mở màn, công thức được thể hiện như sau:
\[ WeekendBoost = \frac{Revenue_{Day1\text{-}7} - Revenue_{Day1}}{Revenue_{Day1}} \times 100 \]
Nhận xét: Chỉ số này giúp nhà sản xuất, nhà phát hành đánh giá hiệu ứng truyền miệng (word-of-mouth) và sức hút thực sự của phim trong tuần đầu.
movies_dataset$ReleaseDay <- format(movies_dataset$ReleaseDate, "%d")
movies_dataset$ReleaseMonth <- format(movies_dataset$ReleaseDate, "%m")
Giải thích: Dòng 1 và dòng 2 giúp trích xuất ngày và tháng từ biến ReleaseDate đã chuyển thành kiểu ngày, kết quả là tạo thành 2 biến mới.
Nhận xét: Dùng hàm format() để lấy ra riêng phần ngày (%d) và tháng (%m) từ trường ngày phát hành, giúp dễ dàng phân loại, nhóm, hoặc tạo biến ngày lễ/phân tích mùa vụ về sau.
movies_dataset$logBudgetUSD <- log10(movies_dataset$BudgetUSD)
Giải thích: Tạo biến mới logBudgetUSD bằng cách lấy log cơ số 10 của biến BudgetUSD (ngân sách sản xuất phim).
Nhận xét: movies_dataset$BudgetUSD là ngân sách sản xuất phim thường có giá trị rất lớn, log10 là hàm logarit cơ số 10, được sử dụng để giảm độ lệch phân phối (skewness) của dữ liệu, làm cho biến gần với phân phối chuẩn hơn và giảm ảnh hưởng của giá trị ngoại lai.
movies_dataset$Season <- ifelse(movies_dataset$ReleaseMonth %in% c("12", "01", "02"),
"Winter", ifelse(movies_dataset$ReleaseMonth %in% c("03", "04", "05"), "Spring",
ifelse(movies_dataset$ReleaseMonth %in% c("06", "07", "08"), "Summer", "Autumn")))
Giải thích: Dùng hàm ifelse() lồng nhau để
phân nhóm các tháng theo mùa trong năm: Winter, Spring, Summer, Autumn,
%in% dùng để kiểm tra xem tháng có thuộc vào nhóm tháng nào
không.
Kết quả là một biến phân loại mới Season (Winter, Spring, Summer,
Autumn) được thêm vào dataset.
Nhận xét: Biến Season cho phép so sánh doanh thu, điểm đánh giá, hoặc lượng khán giả giữa các mùa khác nhau trong năm. Trong ngành điện ảnh, thời điểm phát hành là yếu tố rất quan trọng — có thể ảnh hưởng mạnh đến doanh thu phòng vé.
movies_dataset$PopularityIndex <- ((movies_dataset$IMDbRating * movies_dataset$NumVotesIMDb) +
((movies_dataset$RottenTomatoes_10Score) * movies_dataset$NumVotesRT))/(movies_dataset$NumVotesIMDb +
movies_dataset$NumVotesRT)
Giải thích: Biến PopularityIndex được tạo để đo lường mức độ phổ biến tổng hợp của phim, bằng cách kết hợp điểm đánh giá (IMDb, Rotten Tomatoes) và số lượng người tham gia đánh giá và được tính theo công thức sau:
\[ PopularityIndex = \frac{ IMDbRating \times NumVotes_{IMDb} + RottenTomatoes\_10Score \times NumVotes_{RT} }{ NumVotes_{IMDb} + NumVotes_{RT} } \]
Nhận xét: Biến PopularityIndex là thước đo tổng hợp, phản ánh mức độ yêu thích và độ phổ biến của phim. Khi phim có chỉ số cao cho thấy sức hút mạnh và khả năng lan tỏa. còn chỉ số thấp thì ngược lại.
Giải thích:
Hàm summary() Thực hiện thống kê mô tả nhanh các đặc trưng của biến số học gồm: giá trị nhỏ nhất (Min), tứ phân vị thứ nhất (Q1), trung vị (Median), trung bình (Mean), tứ phân vị thứ ba (Q3) và giá trị lớn nhất (Max). Giúp mô tả xu hướng trung tâm, độ lệch và phân tán tổng thể của dữ liệu.
Hàm sd() Tính độ lệch chuẩn – thể hiện mức độ dao động trung bình của các quan sá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 ổn định. Đơn vị đo giống biến gốc, thuận tiện khi so sánh.
Hàm var() Tính phương sai – trung bình bình phương độ lệch của các giá trị so với trung bình. Là cơ sở để tính độ lệch chuẩn (sd = sqrt(var)); giá trị càng lớn phản ánh dữ liệu càng biến động. Đơn vị là bình phương của biến gốc..
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:
Kết quả cho thấy ngân sách sản xuất (BudgetUSD) của các bộ phim có độ biến động rất lớn, dao động từ 100.000 USD đến 300 triệu USD. Mức trung bình khoảng 9,8 triệu USD, trong khi độ lệch chuẩn 22,49 triệu USD phản ánh sự phân tán mạnh, nghĩa là có nhiều bộ phim đầu tư vượt xa mức trung bình.
Về kinh tế ngành, điều này phù hợp với đặc điểm của ngành điện ảnh toàn cầu, nơi tồn tại khoảng cách lớn giữa các dòng phim: Phim độc lập (indie) thường có ngân sách thấp, chỉ vài trăm nghìn đến vài triệu USD. Phim bom tấn Hollywood có thể đạt mức đầu tư hàng trăm triệu USD (ví dụ như các phim Marvel, “Avatar”, “Avengers: Endgame” với ngân sách trên 200 triệu USD).
Sự chênh lệch này phản ánh mức độ cạnh tranh và phân tầng trong thị trường điện ảnh, đồng thời cho thấy rủi ro tài chính cao khi đầu tư vào sản xuất phim, vì quy mô vốn không đồng nhất và lợi nhuận phụ thuộc lớn vào sức hút thương mại của từng dự án.
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é tại Mỹ (US_BoxOfficeUSD) có mức dao động rất lớn, từ 40.020 USD đến 1,018 tỷ USD, cho thấy sự khác biệt rõ rệt giữa các phim thương mại nhỏ và các bom tấn. Giá trị trung bình đạt khoảng 14,96 triệu USD, trong khi độ lệch chuẩn 38,79 triệu USD phản ánh mức biến động mạnh — chỉ một số ít phim có doanh thu vượt trội kéo trung bình lên cao. Phương sai 1,50×10¹⁵ củng cố nhận định dữ liệu phân tán mạnh quanh trung bình.
Kết quả phù hợp với đặc điểm của ngành điện ảnh Mỹ, nơi doanh thu phụ thuộc lớn vào quy mô phát hành, chiến lược marketing và thương hiệu phim. Các bom tấn như Avengers: Endgame hay Top Gun: Maverick thường đạt doanh thu hàng trăm triệu USD, trong khi phần lớn phim độc lập hoặc phát hành giới hạn chỉ thu vài triệu USD.
Điều này phản ánh sự tập trung doanh thu cao (high concentration) trong ngành, khi một nhóm nhỏ phim chiếm phần lớn tổng doanh thu — đặc trưng của thị trường giải trí có tính cạnh tranh và rủi ro cao.
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 toàn cầu (Global_BoxOfficeUSD) thể hiện mức biến động rất lớn, từ 100.000 USD đến 1,499 tỷ USD, cho thấy sự chênh lệch rõ giữa các phim quy mô nhỏ và các bom tấn quốc tế. Giá trị trung bình đạt khoảng 27,21 triệu USD, trong khi độ lệch chuẩn 69,54 triệu USD phản ánh dữ liệu phân tán mạnh — chỉ một nhóm phim có doanh thu vượt trội làm lệch trung bình. Phương sai 4,84×10¹⁵ khẳng định mức độ biến động cao.
Kết quả phù hợp với xu hướng toàn cầu hóa của ngành điện ảnh, khi các phim bom tấn không chỉ dựa vào thị trường nội địa mà còn thu lợi nhuận lớn từ thị trường quốc tế. Những thương hiệu như Avatar, Fast & Furious, hay Avengers đạt doanh thu toàn cầu trên 1 tỷ USD, trong khi đa số phim tầm trung chỉ dao động ở vài triệu USD.
Điều này phản ánh sự phân hóa mạnh về năng lực tiếp cận thị trường và chiến lược phát hành quốc tế, đồng thời cho thấy doanh thu toàn cầu là chỉ báo quan trọng nhất về hiệu quả thương mại trong kinh tế ngành điện ảnh hiện nay.
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 (Opening_Day_SalesUSD) có độ biến động rất lớn, từ 4.050 USD đến 295,75 triệu USD, thể hiện sự khác biệt rõ giữa phim phát hành giới hạn và các bom tấn lớn. Giá trị trung bình đạt khoảng 2,99 triệu USD, trong khi độ lệch chuẩn 8,13 triệu USD cho thấy mức phân tán cao, tức chỉ một số ít phim có doanh thu mở màn vượt trội. Phương sai 6,61×10¹³ tiếp tục khẳng định dữ liệu biến động mạnh quanh trung bình.
Kết quả phản ánh rõ vai trò chiến lược của ngày công chiếu đầu tiên trong ngành điện ảnh. Các phim bom tấn thường đạt doanh thu mở màn hàng chục đến hàng trăm triệu USD nhờ chiến dịch quảng bá quy mô lớn, ví dụ như Avengers: Endgame đạt hơn 150 triệu USD chỉ trong ngày đầu.
Ngược lại, phim độc lập hoặc phim nghệ thuật thường mở màn khiêm tốn, doanh thu dưới 1 triệu USD. Điều này cho thấy ngày mở màn là chỉ báo sớm về sức hút thị trường và hiệu quả marketing, đồng thời phản ánh mức độ rủi ro cao của doanh thu ngắn hạn trong kinh tế ngành điện ảnh.
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 (One_Week_SalesUSD) dao động mạnh từ 16.507 USD đến 579,56 triệu USD, thể hiện sự chênh lệch đáng kể giữa các phim phát hành nhỏ và bom tấn phòng vé. Giá trị trung bình đạt 7,48 triệu USD, trong khi độ lệch chuẩn 19,55 triệu USD cho thấy dữ liệu có mức phân tán cao, với một số ít phim đạt doanh thu vượt trội kéo trung bình tăng mạnh. Phương sai 3,82×10¹⁴ củng cố nhận định này.
Doanh thu tuần đầu thường được xem là chỉ báo quyết định cho tiềm năng thương mại của phim, đặc biệt trong ngành điện ảnh Mỹ, nơi hơn 60% tổng doanh thu thường đến từ tuần công chiếu đầu tiên. Các bom tấn như Avengers: Endgame hay Jurassic World từng đạt trên 300 triệu USD chỉ trong tuần đầu, trong khi đa số phim trung bình thu dưới 10 triệu USD.
Điều này phản ánh tính tập trung doanh thu cao, hiệu quả chiến lược marketing và lịch phát hành, đồng thời cho thấy tuần đầu tiên là giai đoạn then chốt để hoàn vốn đầu tư và xác lập vị thế thị trường của một bộ phim.
summary(movies_dataset$thangdu)
## 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$thangdu)
## [1] 50404879
var(movies_dataset$thangdu)
## [1] 2.540652e+15
Nhận xét:
Biến thặng dư (thangdu) — đại diện cho chênh lệch giữa doanh thu và chi phí sản xuất, có phạm vi dao động rộng, từ 0 USD đến 1,199 tỷ USD, cho thấy sự khác biệt cực lớn về khả năng sinh lợi giữa các bộ phim. Giá trị trung bình đạt 17,4 triệu USD, trong khi độ lệch chuẩn 50,40 triệu USD phản ánh mức phân tán rất cao; chỉ một số phim có lợi nhuận vượt trội kéo trung bình tăng mạnh. Phương sai 2,54×10¹⁵ khẳng định biến này có độ biến động lớn.
Kết quả phù hợp với thực trạng ngành điện ảnh toàn cầu, nơi tỷ suất sinh lời giữa các phim rất không đồng đều. Một số bom tấn như Avatar hay Frozen mang lại lợi nhuận hàng trăm triệu USD, trong khi phần lớn phim độc lập thậm chí lỗ vốn do doanh thu không đủ bù chi phí. Sự phân hóa này phản ánh rủi ro tài chính cao và tính bất định của lợi nhuận trong ngành, đồng thời nhấn mạnh tầm quan trọng của quản trị ngân sách, marketing và chiến lược phát hành quốc tế nhằm tối đa hóa hiệu quả đầu tư điện ảnh.
summary(movies_dataset$Weekend_boost)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.3338 1.0010 1.5021 1.7477 2.3337 4.9948
sd(movies_dataset$Weekend_boost)
## [1] 0.9488103
var(movies_dataset$Weekend_boost)
## [1] 0.9002409
Nhận xét:
Biến Weekend_boost (tỷ lệ tăng doanh thu cuối tuần so với ngày thường) dao động từ 0,33 đến 4,99, cho thấy mức chênh lệch lớn giữa các phim về khả năng thu hút khán giả vào dịp cuối tuần. Giá trị trung bình 1,75 cho thấy doanh thu cuối tuần thường cao hơn khoảng 75% so với ngày thường. Độ lệch chuẩn 0,95 và phương sai 0,90 phản ánh sự khác biệt rõ giữa các nhóm phim về hiệu ứng cuối tuần.
Kết quả phù hợp với xu hướng tiêu dùng trong ngành giải trí, khi doanh thu rạp chiếu thường đạt đỉnh vào cuối tuần nhờ thời gian rảnh rỗi của khán giả và chiến dịch quảng bá tập trung. Các bom tấn thương mại hoặc phim gia đình thường ghi nhận hiệu ứng cuối tuần mạnh, trong khi phim nghệ thuật hoặc phát hành hạn chế có mức tăng thấp hơn. Điều này cho thấy Weekend_boost là chỉ báo quan trọng về sức hút thị trường ngắn hạn và hiệu quả phân bổ lịch chiếu, góp phần tối ưu hóa doanh thu phòng vé.
summary(movies_dataset$PopularityIndex)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.01906 5.46409 6.50000 6.48999 7.53236 10.00000
sd(movies_dataset$PopularityIndex)
## [1] 1.523214
var(movies_dataset$PopularityIndex)
## [1] 2.320181
Nhận xét:
Chỉ số PopularityIndex dao động từ 0,02 đến 10, phản ánh sự khác biệt đáng kể về mức độ phổ biến giữa các bộ phim. Giá trị trung bình 6,49, gần với trung vị 6,50, cho thấy phân bố tương đối cân đối, không bị lệch mạnh. Độ lệch chuẩn 1,52 và phương sai 2,32 cho thấy mức biến động vừa phải, tức đa số phim có độ phổ biến ở mức trung bình đến cao.
Chỉ số này tổng hợp cả đánh giá khán giả (rating) và lượng bình chọn, nên phản ánh tốt sức hấp dẫn thương mại và uy tín thương hiệu của phim. Kết quả phù hợp với thực tế khi phần lớn phim thương mại đạt điểm phổ biến quanh mức 6–8, trong khi chỉ một số ít phim được yêu thích đặc biệt đạt trên 9 hoặc ngược lại bị đánh giá thấp dưới 4.
Điều này cho thấy PopularityIndex có thể được xem là thước đo tiềm năng thị trường và mức độ nhận diện thương hiệu, hỗ trợ hữu ích cho phân tích dự báo doanh thu trong kinh tế ngành điện ảnh.
summary(movies_dataset$NumVotesIMDb)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 100 1083 2983 9137 8192 1000000
sd(movies_dataset$NumVotesIMDb)
## [1] 24922.36
var(movies_dataset$NumVotesIMDb)
## [1] 621124167
Nhận xét:
Số lượng bình chọn trên IMDb (NumVotesIMDb) dao động từ 100 đến 1.000.000 lượt, cho thấy sự chênh lệch rất lớn giữa các phim ít được biết đến và các tác phẩm nổi tiếng. Giá trị trung bình 9.137 lượt, trong khi độ lệch chuẩn 24.922 cho thấy dữ liệu phân tán mạnh; chỉ một nhóm nhỏ phim nhận được lượng bình chọn vượt trội, kéo trung bình tăng đáng kể. Phương sai 6,21×10⁸ khẳng định độ biến động cao trong mức độ tương tác khán giả.
Kết quả phù hợp với đặc điểm của nền tảng đánh giá trực tuyến như IMDb, nơi các phim nổi tiếng, có chiến dịch quảng bá toàn cầu hoặc gắn với thương hiệu lớn thường thu hút hàng trăm nghìn đến hàng triệu lượt đánh giá. Ngược lại, phim độc lập hoặc ít quảng bá chỉ nhận được vài trăm đến vài nghìn lượt bình chọn.
Điều này phản ánh tính tập trung trong hành vi người xem, khi một số ít thương hiệu điện ảnh chiếm phần lớn sự chú ý và thảo luận công chúng — yếu tố quan trọng trong phân tích truyền thông và sức mạnh thương hiệu phim.
summary(movies_dataset$NumVotesRT)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 50 119 405 2032 1360 500000
sd(movies_dataset$NumVotesRT)
## [1] 8433.381
var(movies_dataset$NumVotesRT)
## [1] 71121908
Nhận xét:
Số lượng bình chọn trên Rotten Tomatoes (NumVotesRT) dao động từ 50 đến 500.000, cho thấy mức độ quan tâm của khán giả rất khác nhau giữa các bộ phim. Giá trị trung bình 2.032 lượt, trong khi độ lệch chuẩn 8.433 phản ánh sự phân tán lớn — chỉ một số phim nổi bật có lượng bình chọn vượt trội. Phương sai 7,11×10⁷ khẳng định độ biến động cao của dữ liệu.
Kết quả phù hợp với đặc điểm của nền tảng Rotten Tomatoes, nơi mức độ bình chọn phụ thuộc mạnh vào phạm vi phát hành, chiến lược quảng bá và hiệu ứng truyền thông xã hội. Các phim bom tấn thường thu hút hàng trăm nghìn lượt đánh giá, trong khi phim độc lập hoặc giới hạn khán giả chỉ đạt vài trăm lượt. Sự chênh lệch này phản ánh mức độ tập trung chú ý của công chúng và cho thấy rằng NumVotesRT là chỉ báo đáng tin cậy cho mức độ lan tỏa và nhận diện thương hiệu phim, có giá trị trong phân tích hành vi người tiêu dùng điện ảnh.
Outlier <- data.frame(Bien = c("BudgetUSD", "NumVotesIMDb", "NumVotesRT", "US_BoxOfficeUSD",
"Global_BoxOfficeUSD", "Opening_Day_SalesUSD", "One_Week_SalesUSD", "thangdu",
"PopularityIndex", "Weekend_boost"), So_luong_ngoai_lai = c(length(boxplot.stats(movies_dataset$BudgetUSD)$out),
length(boxplot.stats(movies_dataset$NumVotesIMDb)$out), length(boxplot.stats(movies_dataset$NumVotesRT)$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$thangdu)$out), length(boxplot.stats(movies_dataset$PopularityIndex)$out),
length(boxplot.stats(movies_dataset$Weekend_boost)$out)))
kable(Outlier, caption = " Số lượng giá trị ngoại lai của các biến",
colnames = c("Tên biến", "Số lượng giá trị ngoại lai"))
| Bien | So_luong_ngoai_lai |
|---|---|
| BudgetUSD | 109157 |
| NumVotesIMDb | 109066 |
| NumVotesRT | 124226 |
| US_BoxOfficeUSD | 113724 |
| Global_BoxOfficeUSD | 113298 |
| Opening_Day_SalesUSD | 115328 |
| One_Week_SalesUSD | 113889 |
| thangdu | 118280 |
| PopularityIndex | 3891 |
| Weekend_boost | 10404 |
Giải thích:
Dòng 1-3: Tạo một khung dữ liệu mới có tên Outlier được tạo gồm hai cột: Bien: chứa tên các biến cần kiểm tra trong tập dữ liệu movies_dataset và So_luong_ngoai_lai: lưu số lượng các giá trị ngoại lai tương ứng.
Dòng 3-8:Ở từng biến, công thức tính được thực hiện qua ba
bước: (1) xác định các giá trị nghi ngờ ngoại lai dựa trên giới hạn của
biểu đồ hộp, (2) trích riêng phần tử ngoại lai, và (3) đếm số lượng của
chúng.
Các giá trị thu được phản ánh mức độ lệch trong phân phối của từng
biến.
Dòng 9-10: Kết quả được trình bày bằng hàm hiển thị bảng, với tiêu đề “Số lượng giá trị ngoại lai của các biến”.
Nhận xét:
Kết quả cho thấy hầu hết các biến định lượng đều có lượng ngoại lai lớn, đặc biệt là BudgetUSD, US_BoxOfficeUSD, Global_BoxOfficeUSD, và Opening_Day_SalesUSD. Điều này phản ánh phân phối doanh thu và ngân sách phim rất lệch phải, khi chỉ một số ít phim bom tấn có giá trị vượt trội hơn hẳn phần còn lại.
Các biến liên quan đến lượt đánh giá (NumVotesIMDb, NumVotesRT) cũng có nhiều ngoại lai, cho thấy mức độ phổ biến giữa các phim không đồng đều. Một số phim thu hút khán giả mạnh mẽ, trong khi phần lớn chỉ nhận được ít sự chú ý. Ngược lại, PopularityIndex và Weekend_boost có ít ngoại lai hơn, chứng tỏ các biến này ổn định hơn về phân phối. Điều này hợp lý vì chúng là các chỉ số tổng hợp hoặc tỷ lệ, ít chịu tác động từ các giá trị cực đoan. Biến thangdu có số lượng ngoại lai đáng kể, phản ánh sự chênh lệch lớn về lợi nhuận giữa các phim. Nguyên nhân có thể đến từ khác biệt về thể loại, quy mô phát hành, hoặc chiến dịch tiếp thị và phản ánh đúng tính chất của ngành.
kable(table(movies_dataset$Genre, movies_dataset$Continent), col.names = c("Thể loại",
"Americas", "Asia", "Europe", "Oceania"), align = "c", caption = "Kết quả phân tổ số lượng phim mỗi thể loại
theo các châu lục")
| Thể loại | Americas | Asia | Europe | Oceania |
|---|---|---|---|---|
| Action | 113502 | 16756 | 15295 | 4578 |
| Comedy | 150885 | 22608 | 20315 | 6024 |
| Documentary | 37742 | 5616 | 5172 | 1584 |
| Drama | 189046 | 28038 | 25370 | 7564 |
| Horror | 75606 | 11205 | 10147 | 3052 |
| Romance | 75477 | 11230 | 10208 | 3106 |
| Sci-Fi | 37568 | 5576 | 5090 | 1568 |
| Thriller | 75283 | 11315 | 10362 | 3111 |
Giải thích:
Dòng 1 tạo bảng tần suất thể hiện số lượng phim theo từng thể loại (Genre) và châu lục (Continent) với hàm table() . kable() định dạng bảng kết quả thành bảng trình bày trong báo cáo.
Dòng 1-2 đặt tên các cột (“Thể loại”, “Americas”, “Asia”, “Europe”, “Oceania”) với col.names
Dòng 2-3 align = “c”: căn giữa nội dung các cột và caption thêm tiêu đề bảng
Nhận xét:
Châu Mỹ chiếm tỷ trọng vượt trội trong hầu hết thể loại, đặc biệt là Drama (189.046 phim) và Comedy (150.885 phim), phản ánh trung tâm sản xuất phim lớn như Hollywood. Châu Á đứng thứ hai, mạnh ở Drama (28.038 phim) và Comedy (22.608 phim), thể hiện sự phát triển nhanh của điện ảnh Hàn Quốc, Ấn Độ và Trung Quốc.
Châu Âu có số lượng khá đồng đều giữa các thể loại, đặc biệt ở Drama và Comedy, phản ánh đặc trưng đa dạng văn hóa và tính nghệ thuật cao của phim châu Âu. Châu Đại Dương có số lượng thấp nhất, chủ yếu ở Drama và Comedy, phù hợp với quy mô thị trường nhỏ và tập trung vào sản xuất nội địa.
movies_dataset$IMDbGroup <- cut(movies_dataset$IMDbRating, breaks = c(-Inf, 5, 8,
Inf), labels = c("Đánh giá thấp", "Đánh giá trung bình", "Đánh giá xuất sắc"))
kable(table(movies_dataset$Country, movies_dataset$IMDbGroup), col.names = c("Quốc gia",
"Đánh giá thấp", "Đánh giá trung bình", "Đánh giá xuất sắc"),
align = "lccc", caption = "Kết quả phân tổ số lượng phim mỗi thể loại\ntheo từng mức dánh giá IMDb")
| Quốc gia | Đánh giá thấp | Đánh giá trung bình | Đánh giá xuất sắc |
|---|---|---|---|
| Australia | 5059 | 20921 | 4607 |
| Canada | 6856 | 27705 | 5967 |
| China | 5168 | 21196 | 4588 |
| France | 5071 | 20834 | 4566 |
| Germany | 3481 | 13728 | 3002 |
| India | 8423 | 35010 | 7551 |
| Japan | 3434 | 13731 | 3057 |
| South Korea | 1715 | 7000 | 1471 |
| UK | 8723 | 34851 | 7703 |
| USA | 118860 | 488172 | 107549 |
Giải thích:
Dòng 1-2 Dòng lệnh này tạo biến phân nhóm mới IMDbGroup trong bảng dữ liệu movies_dataset để phân loại phim dựa theo điểm IMDb. Hàm cut() chia phổ điểm IMDb thành 3 nhóm: Dưới 5: “Đánh giá thấp”, Từ 5 đến dưới 8: “Đánh giá trung bình”, Từ 8 trở lên: “Đánh giá xuất sắc”. Thiết lập nhãn cho từng nhóm qua tham số labels..
Dòng 3-5 : Lệnh table() tạo bảng tần suất 2 chiều, đếm số phim theo từng quốc và theo nhóm đánh giá IMDb vừa phân loại. kable() xuất ra bảng đẹp, có chú thích mô tả bằng caption, định dạng lại tên cột và căn lề bảng
Nhận xét
Kết quả cho thấy Mỹ (USA) có số lượng phim vượt trội ở cả ba mức đánh giá, đặc biệt là nhóm “đánh giá trung bình” với hơn 480 nghìn phim. Các quốc gia khác như Ấn Độ, Anh và Canada cũng có số lượng phim đáng kể, trong khi Hàn Quốc và Đức có quy mô nhỏ hơn. Điều này phản ánh sự khác biệt rõ rệt về quy mô sản xuất phim giữa các quốc gia, đồng thời cho thấy xu hướng chung: phần lớn phim ở mọi quốc gia đều tập trung trong nhóm “đánh giá trung bình”.
kable(table(movies_dataset$Genre, movies_dataset$ThapKy), caption = "Kết quả phân tổ số lượng phim mỗi thể loại theo từng thập kỷ")
| 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:
Dòng 1 được dùng để tạo bảng tần suất hai chiều giữa biến Genre (thể loại phim) và ThapKy (thập kỷ phát hành). Sau đó, kable() hiển thị kết quả dưới dạng bảng có tiêu đề
Nhận xét:
Kết quả cho thấy số lượng phim tăng dần qua các thập kỷ, đặc biệt từ năm 1990 trở đi phản ánh xu hướng phát triển mạnh của ngành điện ảnh toàn cầu. Trong các thể loại, Drama và Comedy chiếm ưu thế về số lượng, thể hiện sự phổ biến của các thể loại này trong suốt các giai đoạn. Ngược lại, Documentary và Sci-Fi có quy mô nhỏ hơn nhưng vẫn tăng đều, cho thấy sự mở rộng dần của thị hiếu khán giả theo thời gian.
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 >
5e+07, " Nổi tiếng & doanh thu cao ", "Khác")
kable(table(movies_dataset$nhomdaodien), align = "lc", caption = "Số lượng đạo diễn nổi tiếng với doanh thu cao")
| Var1 | Freq |
|---|---|
| Nổi tiếng & doanh thu cao | 124934 |
| Khác | 875065 |
Giải thích:
Dòng 1 được sử dụng để thống kê số lượng phim của từng đạo diễn trong biến Director, kết quả được lưu vào đối tượng director_table.
Dòng 2: Tạo biến famous lưu tên những đạo diễn có trên 5 bộ phim trong dữ liệu, sử dụng cú pháp names() để trích xuất tên tương ứng từ bảng tần suất.
Dòng 3–4: Tạo biến mới nhomdaodien bằng hàm ifelse(). Nếu đạo diễn thuộc nhóm famous và có doanh thu toàn cầu (Global_BoxOfficeUSD) trên 50 triệu USD, phim đó được gán nhãn “Nổi tiếng & doanh thu cao”. Ngược lại, gán nhãn “Khác”.
Dòng 5: Hàm kable() được gọi để hiển thị bảng số lượng phim thuộc hai nhóm trên và có tiêu đề bảng.
Nhận xét:
Kết quả cho thấy chỉ 124.934 phim thuộc nhóm “Nổi tiếng & doanh thu cao”, trong khi phần lớn (875.065 phim) nằm ở nhóm “Khác”. Điều này phản ánh rằng chỉ một tỷ lệ nhỏ phim đạt được cả hai tiêu chí: đạo diễn nổi tiếng và doanh thu cao, cho thấy sự cạnh tranh lớn trong ngành điện ảnh.
kable(table(movies_dataset$Genre, movies_dataset$Season), caption = "Kết quả phân tổ số lượng phim mỗi thể loại theo các mùa trong năm")
| Autumn | Spring | Summer | Winter | |
|---|---|---|---|---|
| Action | 37525 | 37825 | 37874 | 36907 |
| Comedy | 49399 | 50672 | 50486 | 49275 |
| Documentary | 12665 | 12574 | 12599 | 12276 |
| Drama | 62453 | 62796 | 63404 | 61365 |
| Horror | 25145 | 25148 | 24958 | 24759 |
| Romance | 24728 | 25182 | 25323 | 24788 |
| Sci-Fi | 12528 | 12445 | 12659 | 12170 |
| Thriller | 24936 | 25077 | 25160 | 24898 |
Giải thích:
Dòng 1: Hàm table() tạo bảng tần suất hai chiều giữa biến Genre và Season. Sau đó, kable() trình bày bảng với chú thích tiêu đề
Nhận xét:
Kết quả cho thấy số lượng phim phân bổ khá đồng đều giữa các mùa. Thể loại Drama và Comedy có số lượng cao nhất ở tất cả các mùa, phản ánh mức độ phổ biến của hai thể loại này.
Ngược lại, Documentary và Sci-Fi có số lượng thấp hơn nhưng vẫn duy trì mức ổn định giữa các mùa, cho thấy không có sự biến động mạnh theo mùa công chiếu.
kable(table(movies_dataset$Country, movies_dataset$Season), caption = "Kết quả phân tổ số lượng bình chọn theo IMDb\nmỗi quốc gia theo các mùa trong năm")
| Autumn | Spring | Summer | Winter | |
|---|---|---|---|---|
| Australia | 7568 | 7794 | 7667 | 7558 |
| Canada | 9900 | 10162 | 10406 | 10060 |
| China | 7711 | 7694 | 7923 | 7624 |
| France | 7636 | 7606 | 7853 | 7376 |
| Germany | 5025 | 4987 | 5060 | 5139 |
| India | 12842 | 12699 | 12802 | 12641 |
| Japan | 5052 | 5060 | 5075 | 5035 |
| South Korea | 2533 | 2613 | 2570 | 2470 |
| UK | 12963 | 12818 | 12914 | 12582 |
| USA | 178149 | 180286 | 180193 | 175953 |
Giải thích:
Dòng 1 sử dụng hàm kable() để tạo bảng từ kết quả phân tổ hai biến Country và Season trong bộ dữ liệu movies_dataset. Tham số caption giúp chú thích rõ nội dung bảng, thuận tiện cho việc tham khảo trong báo cáo.
Nhận xét:
Kết quả cho thấy số lượng bình chọn IMDb của các quốc gia không có sự biến động lớn giữa các mùa trong năm. Mỗi quốc gia đều có số phiếu bình chọn khá đồng đều qua các mùa, ngoại trừ Hoa Kỳ với số lượng vượt trội so với các nước còn lại. Thực tế này phản ánh quy mô thị trường và tần suất phát hành phim của Mỹ cao hơn đáng kể. Bên cạnh đó, Ấn Độ, Anh và Canada cũng có tổng số bình chọn tương đối ổn định giữa các mùa, cho thấy việc phát hành phim không tập trung vào thời điểm cụ thể nào trong năm. Tóm lại, dữ liệu không cho thấy sự khác biệt rõ rệt về phân bổ bình chọn IMDb giữa các mùa ở mỗi quốc gia; thay vào đó, sự khác biệt chủ yếu nằm ở quy mô quốc gia.
kable(table(movies_dataset$Country, movies_dataset$IMDbGroup), col.names = c("Thể loại",
"Được đánh giá thấp", "Được đánh giá trung bình", "Được đánh giá xuất sắc"),
align = "ccc", caption = "Phân tổ lượng bình chọn theo
mức đánh giá IMDb mỗi quốc gia")
| Thể loại | Được đánh giá thấp | Được đánh giá trung bình | Được đánh giá xuất sắc |
|---|---|---|---|
| Australia | 5059 | 20921 | 4607 |
| Canada | 6856 | 27705 | 5967 |
| China | 5168 | 21196 | 4588 |
| France | 5071 | 20834 | 4566 |
| Germany | 3481 | 13728 | 3002 |
| India | 8423 | 35010 | 7551 |
| Japan | 3434 | 13731 | 3057 |
| South Korea | 1715 | 7000 | 1471 |
| UK | 8723 | 34851 | 7703 |
| USA | 118860 | 488172 | 107549 |
Giải thích:
Dòng 1: Tạo bảng phân tổ số lượng phim theo từng quốc gia và nhóm mức đánh giá IMDb (thấp, trung bình, xuất sắc). Việc dùng hàm table() giúp xử lý dữ liệu dạng phân loại, còn kable() trả kết quả dưới dạng bảng rõ ràng, thuận tiện trình bày báo cáo.
Dòng 2-4: Đặt tên cột bảng bằng tham số col.names giúp nội dung dễ hiểu, định dạng căn giữa các cột bằng align, và thêm chú thích bảng bằng caption để nhấn mạnh ý nghĩa phân tích.
Nhận xét:
Bảng kết quả cho thấy Hoa Kỳ có số lượng phim nhận được các mức đánh giá IMDb vượt trội so với các quốc gia khác. Các nước như Anh, Canada, Ấn Độ cũng có số lượng phim được bình chọn khá cao nhưng vẫn cách biệt lớn so với Mỹ. Số phim xuất sắc, trung bình và thấp đều phân bổ đồng đều giữa các nước, riêng Mỹ nổi bật ở cả ba nhóm điểm nhưng tập trung nhiều nhất ở nhóm trung bình và xuất sắc.
Điều này phản ánh quy mô ngành điện ảnh tại Mỹ, đồng thời cũng cho thấy việc đánh giá phim ở các quốc gia khác có sự ổn định giữa các mức điểm số IMDb, giúp việc phân tích không bị thiên lệch về mùa hoặc quốc gia.
movies_dataset$BudgetGroup <- cut(movies_dataset$BudgetUSD, breaks = c(-Inf, 1e+07,
5e+07, Inf), labels = c("Thấp", "Trung bình", "Cao"), right = FALSE)
kable(table(movies_dataset$BudgetGroup), col.names = c("Mức ngân sách", "Số lượng phim"),
align = "lc", caption = "Kết quả phân loại theo ngân sách phim") %>%
kable_styling(full_width = FALSE, position = "center")
| Mức ngân sách | Số lượng phim |
|---|---|
| Thấp | 771644 |
| Trung bình | 193872 |
| Cao | 34483 |
Giải thích:
Dòng 1-2: Tạo biến phân loại ngân sách phim (BudgetGroup) bằng cách chia biến BudgetUSD thành ba nhóm: thấp (dưới 10 triệu), trung bình (10–50 triệu), cao (trên 50 triệu) USD, sử dụng hàm cut().
Dòng 3: Tạo bảng tần số các nhóm ngân sách bằng table(), đặt tên cột rõ ràng và sử dụng kable() để trình bày.
Dòng 4: Định dạng bảng căn trái-căn giữa cho hai cột, thêm chú thích bảng để giải thích ý nghĩa dữ liệu.
Dòng 5: Dùng kable_styling() để điều chỉnh độ rộng bảng và căn giữa vị trí trên trang.
Nhận xét:
Kết quả cho thấy phần lớn phim thuộc nhóm ngân sách thấp, số lượng vượt trội so với hai nhóm còn lại. Số phim ngân sách trung bình nhỏ hơn đáng kể, còn nhóm cao chỉ có số lượng rất hạn chế.
Phân bố này phản ánh thực trạng đa phần phim được sản xuất với chi phí thấp và khá ít phim có mức đầu tư lớn, phù hợp quy luật ngành điện ảnh nói chung. Việc nhóm hóa ngân sách giúp thuận tiện cho việc phân tích tiếp theo về mối quan hệ giữa ngân sách và chất lượng hoặc thành công của phim.
kable(table(movies_dataset$Genre, movies_dataset$BudgetGroup), col.names = c("Thể loại",
"Đánh giá thấp", "Đánh giá trung bình", "Đánh giá xuất sắc"),
align = "c", caption = "Kết quả phân tổ lượng phim mỗi thể loại
theo ngân sách phim") %>%
kable_styling(full_width = FALSE, position = "center")
| Thể loại | Đánh giá thấp | Đánh giá trung bình | Đánh giá xuất sắc |
|---|---|---|---|
| Action | 116252 | 28889 | 4990 |
| Comedy | 154095 | 38768 | 6969 |
| Documentary | 38699 | 9654 | 1761 |
| Drama | 192658 | 48800 | 8560 |
| Horror | 77133 | 19364 | 3513 |
| Romance | 77240 | 19284 | 3497 |
| Sci-Fi | 38392 | 9724 | 1686 |
| Thriller | 77175 | 19389 | 3507 |
Giải thích:
Dòng 1-2: Tạo bảng phân tổ số lượng phim theo từng thể loại (Genre) và nhóm mức ngân sách phim (BudgetGroup). Hàm table() giúp tóm tắt số lượng phim ở từng nhóm, hàm kable() xuất ra bảng dạng dễ nhìn và gán tên rõ ràng cho từng cột.
Dòng 3-4: Đặt tên các cột theo từng nhóm điểm IMDb, căn giữa nội dung bảng để bài trình bày nhất quán, thêm chú thích bảng giúp người đọc hiểu mục đích phân tích.
Dòng 5: Định dạng bảng, căn giữa trên trang để bảng trình bày trông chuyên nghiệp.
Nhận xét:
Bảng kết quả cho thấy thể loại Drama và Comedy có số lượng phim nhiều nhất ở cả ba nhóm đánh giá. Trong đó, phần lớn phim ở tất cả các thể loại tập trung tại nhóm “Đánh giá thấp”, số phim “Đánh giá xuất sắc” khá ít, đặc biệt nổi bật đối với Drama và Comedy. Các thể loại khác như Documentary, Thriller, Sci-Fi, Romance và Horror cũng chỉ có số phim xuất sắc ở mức thấp.
Kết quả này phản ánh rằng phần lớn phim trong từng thể loại có ngân sách thấp, đồng thời cũng cho thấy phim ngân sách cao chưa chắc đạt được đánh giá xuất sắc. Thông tin này giúp định hướng phân tích về mối quan hệ giữa thể loại phim và chất lượng, đồng thời bổ trợ cho các đánh giá tiếp theo về tác động ngân sách đối với kết quả phim.
votes_by_decade <- movies_dataset %>%
group_by(ThapKy) %>%
summarise(IMDbVotes = mean(NumVotesIMDb, na.rm = TRUE), RTVotes = mean(NumVotesRT,
na.rm = TRUE), .groups = "drop") %>%
pivot_longer(cols = c(IMDbVotes, RTVotes), names_to = "Nentang", values_to = "SoVoteTB")
ggplot(votes_by_decade, aes(x = factor(ThapKy), y = SoVoteTB, fill = Nentang)) +
geom_bar(stat = "identity", position = "dodge", alpha = 0.85, color = "black") +
geom_text(aes(label = format(round(SoVoteTB, 0), big.mark = ".", decimal.mark = ",",
scientific = FALSE)), position = position_dodge(width = 0.9), vjust = -0.4,
size = 3, fontface = "bold") + scale_y_continuous(labels = label_number(big.mark = ".",
decimal.mark = ",")) + scale_fill_manual(values = c(IMDbVotes = "lightblue",
RTVotes = "#F4A261"), labels = c("IMDb", "Rotten Tomatoes")) + labs(title = "Xu hướng số lượng bình chọn trung bình theo thập kỷ và nền tảng",
x = "Thập kỷ phát hành phim", y = "Số lượng bình chọn trung bình",
fill = "Nền tảng") + theme_minimal(base_size = 10) + theme(plot.title = element_text(size = 12,
face = "bold", hjust = 0.5), axis.text.x = element_text(angle = 0, vjust = 0.5,
size = 10), axis.title = element_text(face = "bold", size = 11), legend.title = element_text(face = "bold"),
panel.grid.minor = element_blank())
Giải thích:
Dòng 1–4: Tiền xử lý dữ liệu — nhóm theo thập kỷ, tính trung bình số lượt vote IMDb và Rotten Tomatoes cho từng thập kỷ, rồi chuyển sang dạng dài để phục vụ vẽ biểu đồ so sánh hai nền tảng.
Dòng 5–13 Khởi tạo biểu đồ cột nhóm qua ggplot: Dùng geom_bar vẽ biểu đồ cột so sánh số lượng vote trung bình của hai nền tản. Thêm geom_text gắn nhãn số liệu lên mỗi cột. Thiết lập thang màu cho từng nền tảng scale_fill_manual.
Dòng 14–17Tùy chỉnh chi tiết trình bày: Đặt tiêu đề, tên trục, chú giải cho màu, nền tảng, kiểu chữ tiêu đề/thông tin.
Nhận xét:
Biểu đồ cho thấy số lượng bình chọn trung bình trên IMDb luôn cao vượt trội so với Rotten Tomatoes ở mọi thập kỷ. Rotten Tomatoes thường thấp hơn nhưng vẫn giữ ổn định giữa các thập kỷ. Số lượt bình chọn trung bình ở các nền tảng không biến động lớn theo thời gian, nghĩa là thói quen người dùng vote phim trên hai nền tảng giữ mức ổn định qua từng thời kỳ, và IMDb là nơi có lượng tương tác lớn nhất. Đây là dấu hiệu về quy mô cộng đồng IMDb lớn cũng như vai trò trọng tâm của nền tảng này trong đánh giá phim toàn cầu.
ggplot(movies_dataset, aes(x = IMDbGroup, fill = IMDbGroup)) + geom_bar(width = 0.7,
color = "black", alpha = 0.9) + geom_text(stat = "count", aes(label = format(..count..,
big.mark = ".", decimal.mark = ",", scientific = FALSE)), vjust = -0.4, size = 3,
color = "black", fontface = "bold") + scale_y_continuous(labels = label_number(big.mark = ".",
decimal.mark = ",")) + scale_fill_manual(values = c(`Đánh giá thấp` = "#3CB371",
`Đánh giá trung bình` = "lightblue", `Đánh giá xuất sắc` = "#F4A261")) +
labs(title = "Phân loại phim theo mức điểm IMDb", x = "Nhóm điểm IMDb",
y = "Số lượng phim", fill = "Mức điểm") + theme_minimal() + theme(plot.title = element_text(size = 14,
face = "bold", hjust = 0.5), axis.text = element_text(size = 10), axis.title = element_text(size = 11,
face = "bold"), legend.position = "none", panel.grid.minor = element_blank())
Giải thích:
Dòng 1–2: Khởi tạo đối tượng đồ họa với ggplot(), lấy trục hoành là nhóm mức điểm IMDb (IMDbGroup), phân nhóm màu sắc cột theo giá trị của IMDbGroup. Sử dụng geom_bar() để vẽ biểu đồ cột, định dạng độ rộng và màu cho cột.
Dòng 2–5: Thêm nhãn số phim lên đầu mỗi cột với geom_text(), định dạng số (có dấu phân cách hàng nghìn), căn chỉnh vị trí và kiểu chữ rõ ràng.
Dòng 5–6: Thiết lập hệ trục tung với nhãn dạng số, custom màu các cột qua scale_fill_manual, đảm bảo mỗi nhóm điểm IMDb có màu sắc phân biệt, dễ nhận diện.
Dòng 7–10: Tùy chỉnh tiêu đề biểu đồ, tên trục, định dạng font chữ tiêu đề và trục để phù hợp báo cáo. Bỏ chú giải màu với legend.position = “none”, loại bỏ lưới nhỏ không cần thiết.
Nhận xét:
Biểu đồ cho thấy nhóm phim có đánh giá trung bình trên IMDb chiếm số lượng lớn nhất so với hai nhóm còn lại. Phim xuất sắc và phim đánh giá thấp đều có số lượng tương đương nhau, thấp hơn rõ rệt so với nhóm trung bình. Kết quả này phản ánh thực trạng phổ biến: đa số phim đạt mức điểm trung bình, số phim xuất sắc và số phim bị đánh giá thấp đều hiếm hơn, cho thấy tiêu chí đánh giá khắt khe của IMDb đối với phim xuất bản đại trà.
mean_score <- mean(movies_dataset$RottenTomatoes_10Score, na.rm = TRUE)
ggplot(movies_dataset, aes(x = RottenTomatoes_10Score)) + geom_histogram(aes(y = ..density..),
binwidth = 0.5, fill = "#56B4E9", color = "white", alpha = 0.7) + geom_density(color = "red",
size = 1.2) + geom_vline(xintercept = mean_score, color = "black", linetype = "dashed",
size = 1.1) + annotate("text", x = mean_score + 0.3, y = 0.15, label = paste0("Mean = ",
round(mean_score, 2)), color = "blue", hjust = 0) + labs(title = "Phân phối và trung bình điểm RottenTomatoes (thang 10)",
x = "Điểm số (thang 10)", y = "Mật độ") + theme_minimal() + theme(plot.title = element_text(size = 14,
face = "bold", hjust = 0.5))
Giải thích:
Dòng 1: Tính điểm trung bình (mean_score) của biến điểm số RottenTomatoes (thang 10) từ bộ dữ liệu, loại trừ giá trị thiếu với na.rm = TRUE.
Dòng 2–8: Khởi tạo biểu đồ phân phối điểm số:
Dòng 2-3: Vẽ histogram mật độ điểm số với màu xanh nhạt và độ trong suốt 0.7; kết hợp đường phân phối mật độ geom_density màu đỏ để nhấn mạnh dạng phân phối.
Dòng 4: Thêm đường thẳng đứng (vertical line) đánh dấu vị trí trị trung bình trên trục hoành, dùng đường nét đứt màu đen để dễ nhận biết.
Dòng 5: Chèn nhãn “Mean = …” màu xanh tại vị trí trị trung bình nhằm hỗ trợ đọc giá trị trung bình trực tiếp trên biểu đồ.
Dòng 6–8: Đặt tiêu đề, gán nhãn trục hoành và tung, áp dụng theme tối giản (theme_minimal()), tùy chỉnh font chữ tiêu đề rõ nét, chuyên nghiệp.
Nhận xét:
Biểu đồ Histogram thể hiện điểm số Rotten Tomatoes (thang 10) phân bố chủ yếu quanh giá trị trung bình; trị trung bình mẫu là khoảng 6,48. Số phim đạt trong khoảng 5–8 điểm chiếm trên 80% tổng số, cho thấy đại đa số phim được đánh giá ở mức khá đến tốt. Vùng điểm dưới 4 rất ít phim, và phim đạt điểm tối đa (gần 10) cũng chiếm tỷ lệ nhỏ, không xuất hiện giá trị ngoại lệ rõ rệt. Đường mật độ phủ lên biểu đồ giúp nhận diện nhóm điểm tần suất cao nhất. Vị trí trị trung bình gần đỉnh histogram khẳng định phân phối không lệch mạnh, chủ yếu tập trung quanh mức trung bình. Kết quả này phù hợp với thực tiễn, đa phần phim đạt điểm khá, tỷ lệ phim thật sự xuất sắc hoặc rất kém đều hiếm.
dir_count <- movies_dataset %>%
group_by(nhomdaodien) %>%
summarise(n = n(), .groups = "drop")
ggplot(dir_count, aes(x = nhomdaodien, y = n, fill = nhomdaodien)) + geom_col(width = 0.7,
alpha = 0.8, position = "dodge", color = "black") + geom_text(aes(label = format(n,
big.mark = ".", decimal.mark = ",", scientific = FALSE)), hjust = 0.3, vjust = -0.5,
color = "black", size = 3, angle = -90, fontface = "bold") + scale_y_continuous(labels = label_number(big.mark = ".",
decimal.mark = ",")) + scale_fill_manual(values = c(` Nổi tiếng & doanh thu cao ` = "#F1C40F",
Khác = "lightblue")) + labs(title = "Phân loại đạo diễn nổi tiếng & doanh thu cao",
x = "Nhóm đạo diễn", y = "Số lượng phim") + coord_flip() + theme_minimal() +
theme(plot.title = element_text(size = 14, face = "bold", hjust = 0.5), axis.text = element_text(size = 10),
axis.title = element_text(size = 11, face = "bold"), legend.position = "none",
panel.grid.minor = element_blank())
Giải thích:
Dòng 1–3: Nhóm dữ liệu (movies_dataset) theo biến nhomdaodien để đếm số phim từng nhóm đạo diễn. Dùng group_by() để phân nhóm, summarise() đếm số phim cho mỗi nhóm, kết quả được lưu vào biến dir_count.
Dòng 4–5: Vẽ cột geom_col thể hiện số lượng phim từng nhóm đạo diễn, màu phân biệt theo nhóm, độ rộng cột đều, viền màu đen.
Dòng 5–7: Thêm nhãn số lượng phim trên mỗi cột, dùng font chữ đậm, xoay dọc để dễ đọc.
Dòng 7–8: Định dạng nhãn trục tung thành số có phân cách hàng nghìn, dễ quan sát.
Dòng 8–9: Quy định màu sắc từng nhóm đạo diễn (vàng cho “Nổi tiếng & doanh thu cao”, xanh nhạt cho “Khác”).
Dòng 9–10: Thêm tiêu đề, nhãn trục x và y để biểu đồ rõ nghĩa.
Dòng 10–13: Xoay biểu đồ ngang (coord_flip), dùng theme giản lược, chỉnh font, căn lề và loại bỏ chú giải màu, lưới nhỏ cho bố cục rõ ràng.
Nhận xét:
Biểu đồ minh họa rõ sự chênh lệch lớn giữa hai nhóm đạo diễn: nhóm “Nổi tiếng & doanh thu cao” có 124.934 phim, còn nhóm “Khác” chiếm đa số với 875.065 phim. Điều này cho thấy đa phần phim trong bộ dữ liệu được sản xuất bởi các đạo diễn ngoài nhóm nổi tiếng & doanh thu cao, tỷ lệ phim của nhóm nổi tiếng thực tế chỉ chiếm khoảng 12,5% tổng số phim. Màu sắc và nhãn số giúp dễ dàng nhận biết quy mô từng nhóm, kết quả này phản ánh xu hướng ngành điện ảnh: phần lớn phim đến từ các đạo diễn mới hoặc ít nổi tiếng, rất ít đạo diễn tạo được thương hiệu gắn với doanh thu vượt trội.
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), plot.title = element_text(size = 14,
face = "bold", hjust = 0.5))
Giải thích:
Dòng 1–3: Tạo bảng tổng hợp plot_data: nhóm dữ liệu theo thập kỷ phát hành (ThapKy) và thể loại (Genre), tính số lượng phim từng nhóm bằng hàm summarise(). Kết quả là bảng chứa số phim cho từng thể loại ở mỗi thập kỷ.
Dòng 4-8: Dựng biểu đồ cột nhóm: Trục hoành là thập kỷ, trục tung là số lượng phim, màu cột phân biệt từng thể loại (fill = Genre). Dùng geom_col vẽ các cột đặt cạnh nhau (“dodge”), điều chỉnh độ rộng và độ trong suốt. Gán tiêu đề, nhãn trục, ghi chú màu. Tuỳ chỉnh bảng màu với palette Set1, dạng màu sinh động dễ phân biệt. Chỉnh theme tối giản, xoay nhãn trục hoành, tô đậm tiêu đề, canh lề rõ ràng.
Nhận xét:
Biểu đồ cho thấy số lượng phim của các thể loại đều tăng mạnh từ thập kỷ 1950 đến 2010, đạt đỉnh ở thập kỷ 2010s rồi giảm nhẹ ở 2020s. Các thể loại phổ biến nhất là Drama, Comedy, Action và Thriller với số phim vượt trội so với các thể loại khác qua mọi thập kỷ. Documentary, Sci-Fi, Romance và Horror cũng tăng trưởng nhưng ở mức thấp hơn. Số lượng phim tăng mạnh ở thập kỷ 1980s trở đi, phản ánh sự phát triển của ngành công nghiệp điện ảnh và đa dạng hóa về thể loại. Biểu đồ giúp nhận diện nhanh xu hướng phát triển tổng thể cũng như sự khác biệt về tần suất phát hành giữa các thể loại phim qua từng thời kỳ..
mean_thangdu <- movies_dataset %>%
group_by(ThapKy, Genre) %>%
summarise(mean_thangdu = mean(thangdu, na.rm = TRUE), median_thangdu = median(thangdu,
na.rm = TRUE), .groups = "drop")
ggplot(mean_thangdu, aes(x = Genre, y = mean_thangdu, fill = Genre)) + geom_col(position = position_dodge(width = 0.7),
width = 0.6, alpha = 0.88, color = "black") + geom_text(aes(label = format(round(mean_thangdu/1e+06,
2), big.mark = ".", decimal.mark = ",", scientific = FALSE)), position = position_dodge(width = 0.7),
vjust = 0.5, hjust = 1, size = 2.5, color = "black", fontface = "bold", angle = 90) +
geom_point(aes(y = median_thangdu), shape = 21, color = "red", fill = "white",
size = 2.2, position = position_dodge(width = 0.7)) + geom_hline(yintercept = 0,
linetype = "dashed", color = "gray25", linewidth = 0.6) + facet_wrap(~ThapKy,
ncol = 2) + scale_y_continuous(labels = label_number(big.mark = ".", decimal.mark = ",",
scale = 1/1e+06, suffix = " triệu USD")) + scale_fill_brewer(palette = "Set3") +
labs(title = "Thặng dư trung bình từng thể loại qua thập kỷ", x = NULL,
y = "Thặng dư phim (triệu USD)", fill = "Thể loại phim") + theme_minimal() +
theme(axis.text.x = element_blank(), axis.ticks.x = element_blank(), legend.position = "right",
legend.title = element_text(face = "bold"), strip.text = element_text(size = 13,
face = "bold"), plot.title = element_text(size = 14, face = "bold", hjust = 0.5),
plot.margin = margin(10, 30, 10, 10), panel.grid.minor = element_blank())
Giải thích:
Dòng 1–4: Tạo bảng tóm tắt mean_thangdu bằng cách nhóm dữ liệu theo thập kỷ (ThapKy) và thể loại phim (Genre), đồng thời tính trung bình và trung vị thặng dư (mean_thangdu và median_thangdu) của từng nhóm.
Dòng 5–9: Dùng geom_col vẽ các cột thặng dư trung bình cho từng thể loại theo từng thập kỷ, cột đặt cạnh để dễ so sánh, thêm nhãn số liệu geom_text lên từng cột và căn chỉnh biểu diễn rõ ràng.
Dòng 9–10: Gắn thêm điểm trung vị (median) trên từng cột bằng hình tròn, phân biệt màu sắc, hỗ trợ so sánh trực quan giữa giá trị trung bình và trung vị.
Dòng 10–11: Thêm đường kẻ ngang mức 0 và chỉnh độ rộng đường tham chiếu, mỗi thập kỷ hiển thị thành một panel riêng facet_wrap giúp tiện theo dõi xu hướng theo thời gian.
Dòng 12–15: Định dạng đơn vị trục tung là triệu USD, đặt bảng màu, gán tiêu đề và nhãn trục.
Nhận xét:
Biểu đồ cho thấy Drama, Action và Comedy là các thể loại có thặng dư trung bình cao nhất qua nhiều thập kỷ; giai đoạn 2000s–2010s thặng dư các thể loại này đạt từ 10–15 triệu USD. Documentary, Romance, Horror và Sci-Fi thường có thặng dư trung bình thấp hơn, đa số dưới 7 triệu USD. Qua từng thập kỷ, giá trị trung bình các thể loại đều tăng dần, đặc biệt bước nhảy rõ rệt từ 1990s đến 2010s, sau đó giảm nhẹ ở 2020s.
Các điểm trung vị đa số nhỏ hơn hoặc gần bằng trung bình, thể hiện sự phân phối thặng dư lệch phải (nhiều phim có thặng dư thấp, một số ít phim có thặng dư rất cao kéo giá trị trung bình lên). Điều này phản ánh thị trường điện ảnh hiện đại có nhiều “bom tấn” tạo thặng dư lớn ở các thể loại phổ biến, trong khi đa số phim vẫn có lãi khá khiêm tốn. Mỗi panel giúp nhận diện thay đổi cấu trúc doanh thu theo từng thể loại qua thời gian.
count_by_continent <- movies_dataset %>%
group_by(Continent, Genre) %>%
summarise(SoLuong = n(), .groups = "drop")
ggplot(count_by_continent, aes(x = Genre, y = SoLuong, fill = Genre)) + geom_col(width = 0.75,
alpha = 0.9, color = "black") + geom_text(aes(label = format(SoLuong, big.mark = ".",
decimal.mark = ",", scientific = FALSE)), vjust = -0.1, size = 2.5, fontface = "bold",
color = "black") + facet_wrap(~Continent, ncol = 2) + scale_y_continuous(labels = label_number(big.mark = ".",
decimal.mark = ",")) + scale_fill_brewer(palette = "Set2") + 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() +
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.title = element_text(size = 14, face = "bold", hjust = 0.5),
plot.margin = margin(10, 30, 10, 10), panel.grid.minor = element_blank())
Giải thích:
Dòng 4–6: 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 với summarise(). Kết quả tạo ra bảng tóm tắt count_by_continent phục vụ trực quan.
Dòng 7–19: Vẽ biểu đồ cột nhóm số lượng phim từng thể loại cho từng châu lục và tùy chỉnh: Vẽ cột số lượng phim từng thể loại bằng geom_col, gán màu phân biệt cho mỗi thể loại, căn độ rộng và độ trong suốt cột, viền màu đen. Thêm nhãn số liệu lên mỗi cột cho dễ theo dõi số lượng tuyệt đối. Chia panel cho từng châu lục bằng facet_wrap, thiết lập nhãn trục tung là số có phân cách hàng nghìn, đặt bảng màu dễ nhận diện. Bổ sung tiêu đề, nhãn trục, ghi chú màu, căn chỉnh bố cục và theme để biểu đồ rõ ràng, chuyên nghiệp, phù hợp trình bày báo cáo.
Nhận xét:
Biểu đồ cho thấy số lượng phim các thể loại ở châu Americas luôn nổi trội, đặc biệt Drama (189.496 phim), Comedy (150.885 phim) và Action (113.502 phim) đều chiếm đa số tuyệt đối so với các châu lục còn lại. Ở châu Á, số phim Drama cũng dẫn đầu (28.038 phim), các thể loại còn lại đều có số lượng dưới 17.000 phim. Châu Âu có số lượng thấp hơn hẳn so với Americas và Asia; Drama vẫn chiếm ưu thế (25.370 phim), các thể loại khác đều dưới 16.000 phim. Oceania có số phim ít nhất, số lượng cho từng thể loại đều dưới 8.000 phim.
Các con số cụ thể cho thấy ngành công nghiệp điện ảnh phát triển mạnh nhất ở Americas với sự áp đảo về cả số lượng lẫn đa dạng thể loại. Những thể loại phổ biến nhất như Drama, Action, Comedy giữ vai trò chủ đạo ở mọi châu lục. Ngoài ra, Documentary, Sci-Fi, Romance và Thriller chỉ chiếm tỷ trọng nhỏ và ít khác biệt giữa các khu vực. Việc bố trí panel giúp nhận diện nhanh sự chênh lệch quy mô điện ảnh và sở thích thể loại phim theo từng châu lục.
plot_data <- movies_dataset %>%
group_by(IMDbGroup, Genre) %>%
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", color = "black") + geom_text(aes(label = format(SoLuong,
big.mark = ".", decimal.mark = ",", scientific = FALSE)), vjust = 0.3, size = 2.8,
fontface = "bold", color = "black", position = position_dodge(width = 0.7)) +
facet_wrap(~IMDbGroup, ncol = 1) + scale_y_continuous(labels = label_number(big.mark = ".",
decimal.mark = ",")) + scale_fill_manual(values = c("yellow", "#33C1FF", "#90EE90",
"#9B59B6", "#F1C40F", "pink", "grey", "orange")) + labs(title = "Số lượng phim mỗi thể loại theo từng mức điểm IMDb",
x = "Thể loại phim ", y = "Số lượng phim", fill = "Thể loại") +
theme_minimal() + theme(axis.text.x = element_blank(), axis.ticks.x = element_blank(),
legend.position = "right", plot.title = element_text(size = 14, face = "bold",
hjust = 0.5), strip.text = element_text(size = 10, face = "bold"), axis.title = element_text(size = 11,
face = "bold"), panel.grid.minor = element_blank())
Giải thích:
Dòng 1–3: Nhóm dữ liệu movies_dataset theo nhóm điểm IMDb (IMDbGroup) và thể loại phim (Genre), sau đó đếm số lượng phim từng loại. Kết quả là bảng tóm tắt số phim theo từng tổ hợp nhóm điểm và thể loại, lưu vào biến plot_data.
Dòng 4–15 Xây dựng biểu đồ cột nhóm: Sử dụng geom_col để vẽ số lượng phim từng thể loại theo từng mức điểm IMDb, cột phân biệt màu sắc và có viền đen rõ ràng. Thêm nhãn số lượng phim trên từng cột bằng geom_text, giúp nhận biết giá trị cụ thể ngay trên biểu đồ. Phân tách biểu đồ thành các nhóm (“Đánh giá thấp”, “Đánh giá trung bình”, “Đánh giá xuất sắc”) bằng facet_wrap, mỗi nhóm là một hàng riêng biệt. Thiết lập màu sắc từng thể loại, tùy chỉnh nhãn trục, tiêu đề, bảng màu, theme để biểu đồ rõ ràng, khoa học phù hợp trình bày báo cáo..
Nhận xét:
Drama, Comedy và Action là ba thể loại có số lượng phim nổi bật ở cả ba nhóm điểm. Ở nhóm “Đánh giá trung bình”, Drama (170.810 phim), Comedy (136.705 phim), Action (102.237 phim) đều chiếm số lượng vượt trội. Nhóm “Đánh giá thấp” có số phim ít hơn, ví dụ Drama chỉ 42.011 phim, Comedy 33.121 phim, Action 25.110 phim; các thể loại Documentary, Sci-Fi, Romance luôn có số lượng thấp nhất từng nhóm. Nhóm “Đánh giá xuất sắc” có số phim thấp nhất ở mọi thể loại, ví dụ Drama chỉ 37.197 phim, Documentary 7.547 phim, Sci-Fi 7.591 phim.
Kết quả cho thấy đa số phim thuộc nhóm trung bình, rất ít phim thuộc nhóm xuất sắc và số lượng phim đánh giá thấp cũng dễ nhận biết qua biểu đồ. Phần lớn các thể loại đều duy trì cùng thứ tự phân bổ số lượng qua các nhóm, biểu thị sự ổn định trong thị phần thể loại phim dù chất lượng đánh giá khác nhau.
pie_all <- movies_dataset %>%
group_by(Season, Genre) %>%
summarise(SL = n(), .groups = "drop") %>%
group_by(Season) %>%
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(~Season) + 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 theo mỗi mùa trong năm",
fill = "Thể loại") + theme(plot.title = element_text(face = "bold", size = 14,
color = "black", hjust = 0.5))
Giải thích:
Dòng 1–6: Tiền xử lý dữ liệu để tính tỉ lệ từng thể loại phim theo mùa: Nhóm dữ liệu theo biến Season và Genre, đếm số phim mỗi nhóm summarise(SL = n()). Tổng hợp và tính tỉ lệ phần trăm của mỗi thể loại trong tổng số phim từng mùa bằng hàm mutate(TiLe = SL/sum(SL)). Loại bỏ phân nhóm giữ nguyên với ungroup() để chuẩn bị vẽ biểu đồ.
Dòng 7–12 Vẽ biểu đồ tròn thể hiện tỉ lệ từng thể loại phim trong mỗi mùa: Sử dụng ggplot() với geom_col vẽ cột tương ứng từng tỉ lệ, chuyển thành biểu đồ tròn với coord_polar(“y”). Chia thành nhiều biểu đồ cho từng mùa bằng facet_wrap(~Season). Thêm nhãn phần trăm trên từng lát geom_text, định dạng font, màu sắc từng thể loại bằng palette Set3.
Nhận xét:
Biểu đồ cho thấy tỉ lệ các thể loại phim ổn định qua bốn mùa trong năm. Thể loại Drama luôn chiếm tỉ lệ cao nhất (25% tổng số phim mỗi mùa). Comedy xếp thứ hai với 20%, theo sau là Action (15%). Các thể loại còn lại như Thriller (10%), Horror (10%), Romance (10%), Documentary (5%), Sci-Fi (5%) duy trì số liệu khá ổn định qua mọi mùa. Nhóm thể loại phổ biến tập trung ở Drama, Comedy, Action, còn các nhóm khác chiếm tỉ lệ nhỏ hơn. Điều này phản ánh xu hướng các hãng phim duy trì cơ cấu thể loại ổn định quanh năm, không có mùa nào nổi bật về thể loại riêng biệt.
plot_data <- movies_dataset %>%
group_by(ReleaseYear) %>%
summarise(Avg_IMDb = mean(IMDbRating, na.rm = TRUE), Avg_Rotten = mean(RottenTomatoes_10Score,
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)) + geom_line(size = 1) +
geom_point(size = 1.7) + scale_y_continuous(labels = label_number(big.mark = ".",
decimal.mark = ",")) + labs(title = "So sánh các thang điểm trung bình theo năm",
x = "Năm phát hành", y = "Điểm trung bình", color = "Thang điểm") +
theme_minimal() + scale_color_brewer(palette = "Set2") + theme(plot.title = element_text(face = "bold",
size = 14, color = "black", hjust = 0.5))
Giải thích:
Dòng 1–4: Tạo bảng tổng hợp plot_data: Nhóm dữ liệu theo năm phát hành (ReleaseYear). Tính điểm trung bình IMDb (Avg_IMDb) và Rotten Tomatoes (Avg_Rotten) cho từng năm, loại trừ giá trị thiếu.
Dòng 5-6: Chuyển bảng từ dạng rộng sang dài với pivot_longer, tạo biến mới Score_Type (phân biệt giữa Avg_IMDb và Avg_Rotten), thuận tiện cho việc vẽ biểu đồ so sánh nhiều thang điểm trên cùng một trục.
Dòng 7-12 Vẽ biểu đồ đường: Trục hoành là năm phát hành, trục tung là điểm trung bình; phân biệt loại điểm bằng màu. Vẽ đường và điểm dữ liệu cho từng thang điểm, thiết lập nhãn, tiêu đề biểu đồ, màu sắc, theme và font chữ rõ ràng, chuyên nghiệp.
Nhận xét:
Biểu đồ cho thấy điểm trung bình của hai thang IMDb và Rotten Tomatoes biến động nhẹ quanh giá trị 6,45–6,55 qua các năm từ 1950 đến gần 2020. Giá trị điểm trung bình giữa hai thang khá sát nhau ở hầu hết các năm, không có sự chênh lệch đáng kể giữa hai hệ số. IMDb thường có giá trị ổn định hơn, ít biến động mạnh so với Rotten Tomatoes. Trong các năm giai đoạn đầu (trước 1970), điểm số có dao động nhiều do số lượng phim mỗi năm ít hơn; từ 1980 trở đi, biểu đồ ổn định quanh giá trị 6,48 đối với cả hai thang điểm, phản ánh sự nhất quán về tiêu chuẩn đánh giá phim qua thời gian.
library(dplyr)
library(ggplot2)
ggplot(movies_dataset, aes(x = IMDbRating, y = RottenTomatoes_10Score, color = ThapKy)) +
geom_point(alpha = 0.4) + geom_vline(xintercept = mean(movies_dataset$IMDbRating,
na.rm = TRUE), linetype = "dotted", color = "red", size = 1.2) + labs(title = "IMDbRating & RottenTomatoes theo năm",
x = "IMDbRating", y = "RottenTomatoes_10Score", color = "Năm") + theme_light() +
theme(plot.title = element_text(face = "bold", size = 14, color = "black", hjust = 0.5))
Giải thích:
Dòng 3 khởi tạo biểu đồ phân tán (scatter plot) thể hiện mối quan hệ giữa điểm IMDb (IMDbRating) và điểm Rotten Tomatoes thang 10 (RottenTomatoes_10Score). Màu sắc điểm dữ liệu đại diện cho từng thập kỷ (ThapKy), giúp nhận diện xu hướng từng nhóm năm.
Dòng 4: Dùng geom_point(alpha = 0.4) vẽ các điểm dữ liệu,geom_vline vẽ đường trung bình IMDbrating, dùng nét đứt màu đỏ
Dòng 5-8: Đặt tiêu đề, nhãn trục, bảng giải thích màu cho thập kỷ; tuỳ chỉnh theme sáng và đậm tiêu đề biểu đồ.
Nhận xét:
Biểu đồ thể hiện mối tương quan thuận khá chặt giữa điểm số IMDb và Rotten Tomatoes: phim có IMDb cao thường cũng đạt điểm Rotten Tomatoes cao. Dữ liệu phân bố đậm đặc ở vùng trung bình (IMDb từ 5,5 đến 7,5 và Rotten Tomatoes từ 5 đến 8), cho thấy đa số phim đều đạt hai hệ số này ở mức khá. Chênh lệch nhỏ giữa các thập kỷ, mọi nhóm năm đều cùng tạo thành dải xu hướng tăng. Một số điểm rải rác ở các vùng điểm rất thấp hoặc rất cao nhưng ít xuất hiện, cho thấy trường hợp đặc biệt về phim nổi bật hoặc bị chê mạnh không phổ biến. Đường trung bình IMDb gần với vùng mật độ cao nhất, phù hợp với nhận định trung bình phổ điểm IMDb toàn bộ tập phim. Biểu đồ giúp xác nhận hai thang điểm đi cùng xu hướng và không có sự phân tán bất thường giữa các giai đoạn lịch sử.
freq_genre <- movies_dataset %>%
group_by(Genre) %>%
summarise(SoLuong = n()) %>%
arrange(desc(SoLuong))
tb <- mean(freq_genre$SoLuong)
ggplot(freq_genre, aes(x = reorder(Genre, SoLuong), y = SoLuong, fill = Genre)) +
geom_col(show.legend = FALSE, color = "black", alpha = 0.9) + geom_text(aes(label = format(SoLuong,
big.mark = ".", decimal.mark = ",", scientific = FALSE)), hjust = 1.1, size = 3,
fontface = "bold", color = "black") + geom_hline(yintercept = tb, linetype = "dashed",
color = "red", linewidth = 0.8) + annotate("text", x = 1, y = tb + 5, label = paste0("Mean = ",
format(round(tb, 0), big.mark = ".", decimal.mark = ",")), color = "red", hjust = 0,
size = 3, fontface = "bold") + scale_y_continuous(labels = label_number(big.mark = ".",
decimal.mark = ",")) + scale_fill_brewer(palette = "Set2") + labs(title = "Tần suất các thể loại phim",
x = "Thể loại phim", y = "Số lượng phim") + coord_flip() + theme_minimal() +
theme(plot.title = element_text(size = 14, face = "bold", hjust = 0.5), axis.text = element_text(size = 10),
axis.title = element_text(size = 11, face = "bold"), panel.grid.minor = element_blank())
Giải thích:
Dòng 1–4: Nhóm dữ liệu theo thể loại phim (Genre), đếm số lượng phim mỗi thể loại (SoLuong), sau đó sắp xếp giảm dần theo số lượng để nổi bật các thể loại phổ biến nhất.
Dòng 5: Tính giá trị trung bình số lượng phim trên mỗi thể loại (tb), giúp trực quan so sánh trên biểu đồ.
Dòng 6-12 Vẽ biểu đồ cột ngang và tùy chỉnh trực quan: Vẽ cột biểu diễn số lượng phim từng thể loại dùng geom_col, gán màu sắc và viền rõ ràng. Thêm nhãn số lượng phim trực tiếp trên cột để dễ nhận biết giá trị bằng geom_text. Kẻ đường dọc trung bình (tb ≈ 125.000) bằng geom_hline, dán nhãn giá trị trung bình bằng hàm annotate tại vị trí tương ứng. coord_flip giúp xoay biểu đồ sang ngang giúp dễ đọc tên thể loại và nhận diện thể loại phổ biến nhất. Tùy chỉnh màu, nhãn trục, bảng màu, tiêu đề, theme tối giản để biểu đồ rõ ràng, đồng nhất.
Nhận xét:
Biểu đồ cho thấy thể loại Drama vượt trội với 250.018 phim, cao gấp đôi mức trung bình (125.000 phim/thể loại), tiếp theo là Comedy (199.832 phim) và Action (150.131 phim). Các thể loại Thriller, Romance, Horror ở mức trung bình khoảng 100.000 phim. Thấp nhất là Documentary (50.087 phim) và Sci-Fi (52.027 phim), thấp hơn nhiều so với trung bình. Đường đỏ biểu diễn giá trị trung bình giúp dễ dàng so sánh: chỉ có 3 thể loại vượt mức này; các thể loại còn lại thấp hơn đáng kể. Kết quả phản ánh ngành điện ảnh tập trung vào Drama, Comedy, Action, trong khi Documentary và Sci-Fi là các dòng phim ít xuất hiện nhất.
df_thapky <- movies_dataset %>%
group_by(ThapKy) %>%
summarise(SoLuong = n()) %>%
ungroup()
tb_thapky <- mean(df_thapky$SoLuong)
ggplot(df_thapky, aes(x = ThapKy, y = SoLuong, fill = ThapKy)) + geom_col(show.legend = FALSE,
color = "black", alpha = 0.9) + geom_hline(yintercept = tb_thapky, linetype = "dashed",
color = "red", linewidth = 0.8) + geom_text(aes(label = format(SoLuong, big.mark = ".",
decimal.mark = ",", scientific = FALSE)), vjust = -0.5, fontface = "bold", size = 3) +
annotate("text", x = 1, y = tb_thapky + 200, label = paste0("Mean = ", format(round(tb_thapky,
0), big.mark = ".", decimal.mark = ",")), color = "red", fontface = "bold",
hjust = 0, vjust = -1, size = 3) + scale_y_continuous(labels = label_number(big.mark = ".",
decimal.mark = ",")) + scale_fill_brewer(palette = "Set3") + labs(title = "Tần suất số lượng phim theo thập kỷ",
x = "Thập kỷ", y = "Số lượng phim") + theme_minimal() + theme(plot.title = element_text(size = 14,
face = "bold", hjust = 0.5), axis.text = element_text(size = 10), axis.title = element_text(size = 11,
face = "bold"), panel.grid.minor = element_blank())
Giải thích:
Dòng 1-4: Nhóm dữ liệu theo thập kỷ phát hành (ThapKy), đếm số lượng phim từng thập kỷ (SoLuong), loại bỏ phân nhóm với ungroup().
Dòng 5: Tính giá trị trung bình số lượng phim theo thập kỷ (tb_thapky), là mốc tham chiếu trực quan trên biểu đồ.
Dòng 6–16 Dựng biểu đồ cột: Vẽ cột thể hiện số lượng phim từng thập kỷ, tô màu và viền rõ ràng geom_col. Vẽ đường ngang trung bình (tb ≈ 125.000) (nét đứt đỏ), ghi chú rõ ngay trên trục số. Thêm nhãn số lượng phim trực tiếp trên cột aes(label = SoLuong), giúp nhận biết số liệu cụ thể từng mốc năm. Tùy chỉnh màu sắc, nhãn trục, tiêu đề, theme tối giản và định dạng font chữ cho biểu đồ khoa học, dễ tra cứu.
Nhận xét:
Biểu đồ cho thấy số lượng phim ra mắt mỗi thập kỷ tăng dần qua thời gian. Thập kỷ 1950s chỉ có 36.811 phim, thấp nhất, các thập kỷ sau tăng mạnh: 65.730 (1960s), 94.142 (1970s), 122.806 (1980s). Bắt đầu từ 1990s tăng vượt trung bình với 151.655 phim, 2000s đạt 180.198 phim, đỉnh là 209.655 phim ở 2010s. Đến 2020s, số lượng giảm về 139.002 phim nhưng vẫn vượt mức trung bình là 125.000 phim/thập kỷ. Đường ngang màu đỏ dễ dàng giúp nhìn ra các thập kỷ vượt hoặc thấp hơn trung bình. Xu hướng này phản ánh ngành điện ảnh mở rộng quy mô sản xuất mạnh từ 1980s, đạt đỉnh ở 2010s, gần đây xuất hiện dấu hiệu chững lại nhưng chưa giảm đáng kể.
ggplot(movies_dataset, aes(x = logBudgetUSD, fill = ThapKy)) + geom_density(alpha = 0.7) +
facet_wrap(~ThapKy, ncol = 2) + scale_fill_brewer(palette = "Set3") + labs(title = "Phân phối log ngân sách phim theo từng thập kỷ",
x = "Log10 Ngân sách (USD)", y = "Mật độ") + theme_minimal() + theme(plot.title = element_text(face = "bold",
size = 14, color = "black", hjust = 0.5))
Giải thích:
Dòng 1: Khởi tạo biểu đồ với ggplot(), trục hoành là logBudgetUSD, màu sắc phân biệt theo thập kỷ (fill = ThapKy). Sử dụng geom_density để vẽ đường mật độ, làm nổi bật dạng phân phối và giảm chồng lấn số liệu.
Dòng 2 Dùng facet_wrap(~ThapKy, ncol = 2) để phân thành các panel nhỏ, mỗi panel đại diện cho một thập kỷ. Thiết lập bảng màu phân biệt từng thập kỷ qua scale_fill_brewer().
Dòng 2-4 Đặt tiêu đề, tên trục x, trục y rõ ràng, chỉnh font chữ tiêu đề đậm và cỡ lớn, theme tối giản để biểu đồ rõ ràng, đồng nhất.
Nhận xét:
Biểu đồ cho thấy phân phối log ngân sách phim qua các thập kỷ đều có dạng xấp xỉ chuẩn, tập trung cao nhất quanh giá trị log10 ngân sách từ 6 đến 7 (tương đương khoảng 1 triệu – 10 triệu USD). Từ 1950s đến 2020s, tâm phân phối dần dịch chuyển nhẹ sang phải, phản ánh xu hướng ngân sách phim tăng dần theo thời gian. Biên độ phân phối rộng hơn ở các thập kỷ gần đây (2000s–2020s), thể hiện sự đa dạng về mức đầu tư sản xuất. Không có thập kỷ nào xuất hiện bất thường hoặc dạng phân phối lệch mạnh, cho thấy ngành điện ảnh duy trì mức độ ổn định, chỉ có sự phát triển về quy mô vốn qua từng thời kỳ.
count_country_imdb <- movies_dataset %>%
group_by(Country, IMDbGroup) %>%
summarise(SoLuong = n(), .groups = "drop") %>%
group_by(Country) %>%
mutate(TiLe = SoLuong/sum(SoLuong) * 100) %>%
ungroup()
ggplot(count_country_imdb, aes(x = reorder(Country, -TiLe), y = TiLe, fill = IMDbGroup)) +
geom_bar(stat = "identity", width = 0.7, alpha = 0.9) + geom_text(aes(label = paste0(round(TiLe,
1), "%")), position = position_stack(vjust = 0.5), size = 2.8, color = "black",
fontface = "bold") + scale_fill_brewer(palette = "Set2") + labs(title = "Tỷ lệ phim theo mức điểm IMDb tại từng quốc gia",
x = "Quốc gia sản xuất phim", y = "Tỷ lệ (%)", fill = "Nhóm điểm IMDb") +
theme_minimal() + theme(axis.text.x = element_text(angle = 30, hjust = 1, size = 9),
plot.title = element_text(size = 13, face = "bold"))
Giải thích:
Dòng 1–6: Nhóm dữ liệu theo quốc gia (Country) và nhóm điểm IMDb (IMDbGroup) (dòng 3–4), đếm số lượng phim từng nhóm bằng summarise (dòng 5). Nhóm lại theo quốc gia (dòng 6) tính tỉ lệ phần trăm của mỗi nhóm điểm trên tổng số phim từng quốc gia bằng mutate (dòng 7), sau đó bỏ phân nhóm với ungroup (dòng 8). Kết quả nhận được bảng tóm tắt count_country_imdb với tỉ lệ % số phim mỗi nhóm điểm theo từng quốc gia.
Dòng 7–13: Vẽ biểu đồ cột chồng tỉ lệ (%) từng nhóm điểm IMDb của mỗi quốc gia bằng geom_bar, chỉnh độ rộng và trong suốt của cột. Thêm nhãn phần trăm lên từng nhóm điểm trên cột geom_text, định dạng số rõ ràng. Chọn bảng màu Set2 phân biệt các nhóm điểm, bổ sung tiêu đề, nhãn trục, bảng chú giải và theme tối giản theme_minimal. Bố trí tên quốc gia xiên 30 độ cho hiển thị gọn và căn lề tiêu đề đậm, chuyên nghiệp.
Nhận xét:
Biểu đồ minh họa rõ sự phân bố tỉ lệ phim theo mức điểm IMDb tại từng quốc gia. Hầu hết các quốc gia đều có cơ cấu gần tương tự nhau: nhóm phim “Đánh giá trung bình” chiếm phần lớn tỉ trọng, dao động từ 67,9% đến 68,7%. Nhóm “Đánh giá thấp” dao động quanh mức 16,5%–17,2%, không có quốc gia nào chênh lệch vượt trội. Tỉ trọng phim đạt “Đánh giá xuất sắc” khá đồng đều giữa các nước, từ 14,4% đến 15,1%.
Điều này phản ánh phổ điểm IMDb phân bố rất ổn định ở các quốc gia sản xuất phim lớn như Mỹ, Anh, Pháp, Hàn, Nhật, Úc… Không có quốc gia nào nổi bật về tỉ lệ phim xuất sắc hoặc phim bị đánh giá thấp. Kết quả này cho thấy chất lượng phim các nước chủ yếu tập trung ở mức trung bình, phần còn lại chia đều cho hai nhóm “thấp” và “xuất sắc”, tạo nên đặc trưng chung cho ngành công nghiệp điện ảnh hiện đại.
pie_df <- data.frame(DoanhThu = c("Mỹ", "Phần còn lại"), Value = c(sum(movies_dataset$US_BoxOfficeUSD),
sum(movies_dataset$Global_BoxOfficeUSD) - sum(movies_dataset$US_BoxOfficeUSD)))
pie_df$TiLe <- pie_df$Value/sum(pie_df$Value)
ggplot(pie_df, aes(x = "", y = TiLe, fill = DoanhThu)) + geom_col(width = 1, color = "white") +
coord_polar("y") + labs(title = "Tỉ lệ doanh thu Mỹ so với toàn cầu",
fill = "Khu vực") + theme_void() + geom_text(aes(label = scales::percent(TiLe,
accuracy = 0.1)), position = position_stack(vjust = 0.5)) + theme(plot.title = element_text(face = "bold",
size = 14, color = "black", hjust = 0.5))
Giải thích:
Dòng 1-3: Tạo bảng tóm tắt pie_df với 2 giá trị: “Mỹ” (tổng doanh thu phòng vé tại Mỹ) và “Phần còn lại” (doanh thu toàn cầu trừ phần Mỹ). Tính tỉ lệ doanh thu của từng nhóm so với toàn bộ doanh thu (thêm biến TiLe).
Dòng 4-8: Vẽ biểu đồ tròn (pie chart) thể hiện tỉ lệ doanh thu Mỹ so với toàn thế giới: Dùng geom_col dựng các lát biểu đồ từ pie_df, định dạng hình dạng tròn với coord_polar(“y”). Thêm nhãn phần trăm trực tiếp lên biểu đồ. Tùy chỉnh tiêu đề, bảng màu chú giải theo từng khu vực, định dạng đơn giản hóa giao diện bằng theme_void(), làm nổi bật tiêu đề.
Nhận xét:
Biểu đồ cho thấy doanh thu phòng vé tại Mỹ chiếm 55% tổng doanh thu toàn cầu, trong khi phần còn lại của thế giới chiếm 45%. Mức chênh lệch này phản ánh vai trò trung tâm của thị trường điện ảnh Mỹ so với quốc tế: Mỹ là thị trường điện ảnh lớn nhất, quyết định phần lớn doanh thu toàn ngành. Tuy nhiên, phần doanh thu ngoài Mỹ cũng rất đáng kể, cho thấy thị trường quốc tế ngày càng đóng vai trò quan trọng đối với thành công thương mại của phim điện ảnh.
ggplot(movies_dataset, aes(x = BudgetGroup, fill = BudgetGroup)) + geom_bar(width = 0.7,
color = "black", alpha = 0.9) + geom_text(stat = "count", aes(label = format(..count..,
big.mark = ".", decimal.mark = ",", scientific = FALSE)), vjust = -0.4, size = 3,
color = "black", fontface = "bold") + scale_y_continuous(labels = label_number(big.mark = ".",
decimal.mark = ",")) + scale_fill_manual(values = c(Thấp = "#3CB371", `Trung bình` = "lightblue",
Cao = "#F4A261")) + labs(title = "Phân loại phim theo nhóm ngân sách",
x = "Nhóm ngân sách phim", y = "Số lượng phim", fill = "Nhóm ngân sách") +
theme_minimal() + theme(plot.title = element_text(size = 14, face = "bold", hjust = 0.5),
axis.text = element_text(size = 10), axis.title = element_text(size = 11), legend.title = element_text(face = "bold"),
panel.grid.minor = element_blank())
Giải thích:
Dòng 1: Khởi tạo biểu đồ cột với ggplot. Trục x là nhóm ngân sách (BudgetGroup), màu sắc đại diện nhóm ngân sách. Vẽ các cột với geom_bar, chỉnh chiều rộng (0.7), viền đen, độ trong suốt 0.9.
Dòng 2-4: Thêm nhãn số lượng phim trực tiếp lên đầu cột bằng hàm geom_text, định dạng số theo chuẩn Việt Nam (chấm hàng nghìn, phẩy thập phân), căn chỉnh vị trí, cỡ chữ rõ ràng, chữ in đậm màu đen.
Dòng 4-5: Định dạng nhãn trục tung với scale_y_continuous, đảm bảo số liệu hiển thị có dấu hàng nghìn.
Dòng 5-6: Quy định bảng màu cho từng nhóm ngân sách: Thấp (xanh lá), Trung bình (xanh nhạt), Cao (cam nhạt).
Dòng 6-7: Đặt tiêu đề, nhãn trục và tên bảng màu, đảm bảo rõ nội dung.
Dòng 8–10: Thiết lập theme tối giản, nhấn mạnh tiêu đề, tùy chỉnh cỡ chữ trục, tiêu đề, legend và loại bỏ lưới phụ cho bố cục gọn, dễ nhìn.
Nhận xét:
Biểu đồ cho thấy đa phần phim thuộc nhóm ngân sách thấp (771.644 phim, chiếm áp đảo gần 75% tổng số). Nhóm ngân sách trung bình có 193.872 phim, còn nhóm cao chỉ có 34.483 phim, tỷ lệ rất thấp so với hai nhóm còn lại. Số liệu này phản ánh ngành điện ảnh chủ yếu sản xuất phim kinh phí thấp; phim ngân sách cao là thiểu số do đòi hỏi nguồn lực lớn. Sự khác biệt rõ rệt giữa ba nhóm ngân sách thể hiện doanh nghiệp hoặc nhà sản xuất ưu tiên mức đầu tư thấp tới trung bình, còn phim quy mô lớn là đặc thù của các hãng lớn hoặc dự án đặc biệt.
count_budget_genre <- movies_dataset %>%
group_by(Genre, BudgetGroup) %>%
summarise(SoLuong = n(), .groups = "drop")
ggplot(count_budget_genre, aes(x = Genre, y = SoLuong, fill = BudgetGroup)) + geom_col(position = "dodge",
width = 0.75, alpha = 0.9, color = "black") + geom_text(aes(label = format(SoLuong,
big.mark = ".", decimal.mark = ",", scientific = FALSE)), position = position_dodge(width = 0.75),
vjust = -0.3, size = 2.5, fontface = "bold") + scale_y_continuous(labels = label_number(big.mark = ".",
decimal.mark = ",")) + scale_fill_brewer(palette = "Set2") + labs(title = "Số lượng phim mỗi thể loại theo nhóm ngân sách",
x = "Thể loại phim", y = "Số lượng phim", fill = "Nhóm ngân sách") +
theme_minimal() + theme(axis.text.x = element_text(angle = 30, hjust = 1, size = 10),
plot.title = element_text(size = 14, face = "bold"), axis.title = element_text(size = 11),
legend.title = element_text(face = "bold"), panel.grid.minor = element_blank())
Giải thích:
Dòng 1-3: Nhóm dữ liệu theo Genre (thể loại phim) và nhóm ngân sách (BudgetGroup), sau đó đếm số lượng phim từng nhóm bằng summarise. Kết quả thu được bảng tổng hợp số lượng phim cho từng tổ hợp thể loại và nhóm ngân sách.
Dòng 4–12: Khởi tạo biểu đồ cột nhóm: Vẽ các cột số lượng phim từng thể loại phân theo nhóm ngân sách bằng geom_col, hiệu chỉnh vị trí dodge, chiều rộng cột, màu viền. Hiển thị nhãn số lượng trên đỉnh mỗi cột bằng geom_text, định dạng số liệu rõ ràng, căn chỉnh vjust, in đậm. Định dạng trục tung thể hiện rõ số liệu bằng dấu chấm ngàn; phân biệt màu nhóm ngân sách bằng bảng màu Set2 scale_fill_brewer. Đặt tiêu đề, nhãn trục x/y, nhãn màu cho nhóm ngân sách, xoay nhãn trục x 30 độ. Chọn theme tối giản theme_minimal, chỉnh font tiêu đề và các nhãn cho biểu đồ chuyên nghiệp.
Nhận xét:
Biểu đồ cho thấy trong tất cả các thể loại phim, nhóm ngân sách thấp luôn chiếm số lượng lớn nhất: ví dụ, Drama có 192.658 phim ngân sách thấp, Comedy (154.095), Action (116.252). Phim ngân sách trung bình tập trung cao nhất ở Drama (48.800 phim), Comedy và Action đều vượt 20.000 phim. Số lượng phim ngân sách cao ở mọi thể loại đều rất thấp; nhiều thể loại chỉ dao động từ 1.500–8.500 phim ngân sách cao. Điều này phản ánh đặc điểm ngành điện ảnh: đa số phim các thể loại đều sản xuất với chi phí thấp hoặc trung bình, toàn ngành có rất ít phim chi phí cao.
count_season_imdb <- movies_dataset %>%
group_by(Season, IMDbGroup) %>%
summarise(SoLuong = n(), .groups = "drop")
ggplot(count_season_imdb, aes(x = Season, y = SoLuong, fill = IMDbGroup)) + geom_col(position = "dodge",
width = 0.7, alpha = 0.9, color = "black") + geom_text(aes(label = format(SoLuong,
big.mark = ".", decimal.mark = ",", scientific = FALSE)), position = position_dodge(width = 0.7),
vjust = 0.5, hjust = 2, size = 3, fontface = "bold", angle = 90) + scale_y_continuous(labels = label_number(big.mark = ".",
decimal.mark = ",")) + scale_fill_manual(values = c(`Đánh giá thấp` = "#3CB371",
`Đánh giá trung bình` = "lightblue", `Đánh giá xuất sắc` = "#F4A261")) +
labs(title = "Số lượng phim theo mức đánh giá IMDb trong từng mùa trong năm",
x = "Mùa trong năm", y = "Số lượng phim", fill = "Nhóm điểm IMDb") +
theme_minimal() + theme(plot.title = element_text(size = 14, face = "bold", hjust = 0.5),
axis.text.x = element_text(size = 10), legend.position = "right") + theme(plot.title = element_text(face = "bold",
size = 14, color = "black", hjust = 0.25))
Giải thích:
Dòng 1-3: Tổng hợp dữ liệu bằng cách nhóm theo mùa (Season) và nhóm điểm IMDb (IMDbGroup), rồi đếm số lượng phim thuộc từng tổ hợp sử dụng summarise. Kết quả là bảng count_season_imdb với số liệu cho từng mùa và mức đánh giá IMDb.
Dòng 4–14: Khởi tạo biểu đồ cột nhóm: Trục x là mùa trong năm, trục y là số lượng phim, nhóm màu (fill) đại diện cho mức đánh giá IMDb. Dùng geom_col vẽ các cột, sắp xếp vị trí nhóm song song (dodge), hiệu chỉnh độ rộng, trong suốt, viền rõ ràng. Thêm nhãn số lượng phim trực tiếp lên đầu mỗi cột với định dạng đẹp (hàng nghìn, hàng thập phân). Định dạng trục tung và bảng màu cho từng nhóm điểm IMDb: Xanh lá cho “Đánh giá thấp”, xanh nhạt cho “Trung bình”, cam cho “Xuất sắc”. Đặt tiêu đề, nhãn trục x/y, chú thích màu và chỉnh font chữ tiêu đề, trục x. Theme giản lược giúp biểu đồ gọn gàng, chuyên nghiệp.
votes_season <- movies_dataset %>%
group_by(Season) %>%
summarise(TongVote = sum(NumVotesIMDb, na.rm = TRUE), .groups = "drop")
ggplot(votes_season, aes(x = Season, y = TongVote, fill = Season)) + geom_col(width = 0.6,
alpha = 0.9, color = "black") + geom_text(aes(label = format(TongVote, big.mark = ".",
decimal.mark = ",", scientific = FALSE)), hjust = 1, vjust = 0, size = 3, fontface = "bold",
color = "black") + scale_y_continuous(labels = label_number(big.mark = ".", decimal.mark = ",")) +
scale_fill_brewer(palette = "Pastel1") + labs(title = "Tổng số lượt bình chọn IMDb theo mùa trong năm",
x = "Mùa trong năm", y = "Tổng số lượt bình chọn (vote)", fill = "Mùa") +
coord_flip() + theme_minimal() + theme(plot.title = element_text(size = 13, face = "bold",
hjust = 0.5), axis.text = element_text(size = 11), axis.title = element_text(size = 10,
face = "bold"), legend.position = "none", panel.grid.minor = element_blank()) +
theme(plot.title = element_text(face = "bold", size = 15, color = "black", hjust = 0.5))
Giải thích:
Dòng 1–3: Tổng hợp dữ liệu: nhóm phim theo mùa (Season), tính tổng số lượt bình chọn IMDb cho từng mùa bằng summarise, bỏ nhóm sau khi tổng hợp.
Dòng 4–13 Vẽ biểu đồ cột ngang tổng số lượt bình chọn IMDb theo mùa: Vẽ cột biểu diễn tổng bình chọn cho từng mùa bằng geom_col, màu phân theo mùa, viền đen, điều chỉnh độ trong suốt. Thêm nhãn số liệu định dạng lớn, rõ ràng trên từng cột geom_text để dễ đọc. Định dạng trục y với dấu phân tách hàng triệu/phần nghìn, chọn bảng màu Pastel1. Đặt tiêu đề, nhãn trục, ẩn chú giải, xoay trục hoành thành biểu đồ ngang cho dễ so sánh. Tùy chỉnh font, kích cỡ tiêu đề, nhãn trục, loại bỏ lưới phụ, căn giữa tiêu đề cho báo cáo chuyên nghiệp.
Nhận xét:
Biểu đồ cho thấy tổng số lượt bình chọn IMDb qua các mùa khá đồng đều, dao động không lớn: Summer cao nhất (2.313.799.164 lượt), Spring đứng thứ hai (2.290.778.182 lượt), Autumn (2.257.185.358 lượt), Thấp nhất là Winter (2.275.412.170 lượt).
Chênh lệch giữa mùa cao nhất (Summer) và thấp nhất (Autumn) chỉ khoảng 56 triệu lượt, nhỏ so với tổng bình chọn trên từng mùa (trên 2,2 tỷ lượt/mùa). Không có mùa nào vượt trội rõ rệt về số lượt bình chọn. Điều này chứng tỏ thói quen bình chọn phim trên IMDb duy trì ổn định quanh năm, không phụ thuộc vào yếu tố mùa vụ.
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.
library(readxl)
data_acl <- read_excel("C:/Users/PC/Documents/Zalo Received Files/nnlt/Bao cao tai chinh.xlsx")
Giải thích: Lệnh read_excel được sử dụng để đọc file dữ liệu định dạng excel vào R và tạo ra một data frame tên data_acl. Đâ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(data_acl)
## [1] 44 11
Giải thích: Kết quả trả về số quan sát là 44 dòng và số biến là 11 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.
names(data_acl)
## [1] "Quý" "TÀI SẢN NGẮN HẠN"
## [3] "Tiền và các khoản tương đương tiền" "Hàng tồn kho"
## [5] "TÀI SẢN DÀI HẠN" "Tài sản cố định"
## [7] "TỔNG CỘNG TÀI SẢN" "NỢ PHẢI TRẢ"
## [9] "Nợ ngắn hạn" "VỐN CHỦ SỞ HỮU"
## [11] "Lợi nhuận sau thuế chưa phân phối"
Giải thích: Hiển thị tên tất cả các biến, giúp người đọc biết ngay dữ liệu gồm những thông tin gì.
sum(sapply(data_acl, is.numeric))
## [1] 10
Giải thích: sapply() áp dụng hàm is.numeric() cho tất cả các biến (cột) của bộ dữ liệu data_acl Hàm này sẽ trả về một vector chứa TRUE/FALSE, thể hiện các biến nào là kiểu số (numeric). sum() tính tổng số TRUE (đếm biến kiểu số).
sum(sapply(data_acl, is.character) | sapply(data_acl, is.factor))
## [1] 1
Giải thích: sapply() lần lượt kiểm tra từng biến xem có phải kiểu character hoặc factor không. Dùng phép OR | giữa hai kết quả để tổng hợp tất cả biến thuộc dạng phân loại, dạng chữ hoặc phân loại, sum() đếm tổng số biến dạng này.
loại <- data.frame(Kiểu_dữ_liệu = sapply(data_acl, class))
kable(loại, col.names = c("Tên biến", "Kiểu dữ liệu"), caption = "Kết quả kiểu dữ liệu các biến")
| Tên biến | Kiểu dữ liệu |
|---|---|
| Quý | character |
| TÀI SẢN NGẮN HẠN | numeric |
| Tiền và các khoản tương đương tiền | numeric |
| Hàng tồn kho | numeric |
| TÀI SẢN DÀI HẠN | numeric |
| Tài sản cố định | numeric |
| TỔNG CỘNG TÀI SẢN | numeric |
| NỢ PHẢI TRẢ | numeric |
| Nợ ngắn hạn | numeric |
| VỐN CHỦ SỞ HỮU | numeric |
| Lợi nhuận sau thuế chưa phân phối | numeric |
Giải thích:
Dòng 1: Áp dụng hàm class lên từng cột của bảng dữ liệu data_acl để xác định kiểu dữ liệu, rồi chuyển kết quả này thành một bảng (data frame) có một cột đặt tên là Kiểu_dữ_liệu.
Dòng 2: Dùng hàm kable() để xuất bảng vừa tạo ra, đặt tên cột lần lượt là “Tên biến” và “Kiểu dữ liệu”, đồng thời thêm chú thích tiêu đề cho bảng.
data_acl <- data_acl %>%
rename(Times = "Quý", TSNH = "TÀI SẢN NGẮN HẠN", Tien_Tuongduong = "Tiền và các khoản tương đương tiền",
HangTonKho = "Hàng tồn kho", TSDH = "TÀI SẢN DÀI HẠN", TSCD = "Tài sản cố định",
TongTS = "TỔNG CỘNG TÀI SẢN", NoPhaiTra = "NỢ PHẢI TRẢ", NoNH = "Nợ ngắn hạn",
VonCSH = "VỐN CHỦ SỞ HỮU", LNSTCPP = "Lợi nhuận sau thuế chưa phân phối")
names(data_acl)
## [1] "Times" "TSNH" "Tien_Tuongduong" "HangTonKho"
## [5] "TSDH" "TSCD" "TongTS" "NoPhaiTra"
## [9] "NoNH" "VonCSH" "LNSTCPP"
Giải thích:
Dòng 1-5 này sử dụng hàm rename() trong dplyr để gán lại tên các biến/cột trong bảng data_acl cho dễ hiểu, thuận tiện khi xử lý và xuất báo cáo. Các tên biến được chuyển sang tiếng Việt hoặc ký tự tắt. Dòng 6 để xem các tên cột hiện tại trong bảng.
sum(duplicated(data_acl))
## [1] 0
any(is.na(data_acl))
## [1] FALSE
Giải thích:
Dòng 1 kiểm tra trong toàn bộ data frame data_acl có bao nhiêu dòng bị lặp lại hoàn toàn so với dòng trước đó. Hàm duplicated() trả về một vector logic (TRUE với dòng là bản sao, FALSE nếu là lần xuất hiện đầu), còn sum() đếm tổng số dòng bị lặp.
Dòng 1 kiểm tra xem có ô dữ liệu nào bị thiếu (NA) trong toàn bộ bảng dữ liệu hay không. Hàm is.na() trả về TRUE ở vị trí nào bị thiếu, any() trả về TRUE nếu có ít nhất một giá trị thiếu, FALSE nếu toàn bộ bảng không có NA.
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 data_acl và kết quả FALSE cho thấy toàn bộ dataset không có giá trị bị thiếu (NA), dữ liệu đã được làm sạch và không cần xử lý.
kable(t(head(data_acl, 4)), booktabs = TRUE) %>%
kable_styling(latex_options = c("scale_down"))
| Times | 2014_Q1 | 2014_Q2 | 2014_Q3 | 2014_Q4 |
| TSNH | 495149001095 | 582107097202 | 582107097202 | 582107097202 |
| Tien_Tuongduong | 16639425815 | 28785124424 | 28785124424 | 28785124424 |
| HangTonKho | 221977767688 | 297528217603 | 297528217603 | 297528217603 |
| TSDH | 254964642870 | 255233815978 | 255233815978 | 255233815978 |
| TSCD | 239139673139 | 245668340815 | 245668340815 | 245668340815 |
| TongTS | 750113643965 | 837340913180 | 837340913180 | 837340913180 |
| NoPhaiTra | 479308668141 | 556952420410 | 556952420410 | 556952420410 |
| NoNH | 455240553148 | 545255807591 | 545255807591 | 545255807591 |
| VonCSH | 270804975824 | 280388492770 | 280388492770 | 280388492770 |
| LNSTCPP | 49976746197 | 62747163143 | 62747163143 | 62747163143 |
Giải thích: Hàm head(, 4) lấy ra 4 dòng đầu của bộ dữ liệu nhằm minh họa mẫu dữ liệu. Hàm t() thực hiện chuyển vị ma trận.
Dòng 1 được dùng để tạo bảng LaTeX chuẩn học thuật, với tùy chọn booktabs = TRUE giúp thêm các đường viền mảnh, đẹp và chuyên nghiệp.
Dòng 2 giúp tự động thu nhỏ kích thước bảng để bảng không bị vượt ra ngoài khổ giấy khi knit sang PDF.
kable(t(tail(data_acl, 4)), booktabs = TRUE) %>%
kable_styling(latex_options = c("scale_down"))
| Times | 2024_Q1 | 2024_Q2 | 2024_Q3 | 2024_Q4 |
| TSNH | 1.433256e+12 | 1.505383e+12 | 1.465223e+12 | 1.409402e+12 |
| Tien_Tuongduong | 49990831136 | 93987468614 | 86172237584 | 66101069152 |
| HangTonKho | 1.091113e+12 | 1.043961e+12 | 1.008491e+12 | 9.532548e+11 |
| TSDH | 260875039618 | 253204293349 | 253708128100 | 260438284197 |
| TSCD | 244830758412 | 237301930030 | 233344188065 | 235269264122 |
| TongTS | 1.694131e+12 | 1.758587e+12 | 1.718931e+12 | 1.669840e+12 |
| NoPhaiTra | 892714300023 | 953959626178 | 911380207328 | 860161085924 |
| NoNH | 892714300023 | 953959626178 | 911380207328 | 860161085924 |
| VonCSH | 801416471768 | 804627690100 | 807550514997 | 809678857682 |
| LNSTCPP | 269425804736 | 272637023068 | 275559847965 | 277688190650 |
Giải thích: Hàm tail(, 4) lấy ra 4 dòng cuối cùng của bộ dữ liệu nhằm minh họa mẫu dữ liệu. Hàm t() thực hiện chuyển vị ma trận.
Dòng 1 được dùng để tạo bảng LaTeX chuẩn học thuật, với tùy chọn booktabs = TRUE giúp thêm các đường viền mảnh, đẹp và chuyên nghiệp.
Dòng 2 giúp tự động thu nhỏ kích thước bảng để bảng không bị vượt ra ngoài khổ giấy khi knit sang PDF.
variable_meaning_acl <- data.frame(variable = c("Times", "TSNH", "Tien_Td", "HangTK",
"TSDH", "TSCD", "TongTS", "NoPhaiTra", "NoNH", "VonCSH", "LNST"), Meaning = c("Biến thời gian thể hiện quý báo cáo",
"Tài sản ngắn hạn", "Tiền và các khoản tương đương tiền",
"Hàng tồn kho", "Tài sản dài hạn", "Tài sản cố định", "Tổng cộng tài sản",
"Nợ phải trả", "Nợ ngắn hạn", "Vốn chủ sở hữu", "Lợi nhuận sau thuế chưa phân phối"),
stringsAsFactors = FALSE)
kable(variable_meaning_acl, col.names = c("Tên biến", "Ý nghĩa"), align = "l",
caption = "Ý nghĩa các biến trong bộ dữ liệu")
| Tên biến | Ý nghĩa |
|---|---|
| Times | Biến thời gian thể hiện quý báo cáo |
| TSNH | Tài sản ngắn hạn |
| Tien_Td | Tiền và các khoản tương đương tiền |
| HangTK | Hàng tồn kho |
| TSDH | Tài sản dài hạn |
| TSCD | Tài sản cố định |
| TongTS | Tổng cộng tài sản |
| NoPhaiTra | Nợ phải trả |
| NoNH | Nợ ngắn hạn |
| VonCSH | Vốn chủ sở hữu |
| LNST | Lợi nhuận sau thuế chưa phân phối |
Giải thích:
Dòng 1–6 Khởi tạo bảng chú thích biến : Dùng data.frame() để tạo bảng variable_meaning_acl gồm 2 cột: variable và Meaning. Tham số stringsAsFactors = FALSE để đảm bảo các giá trị không bị chuyển thành kiểu factor, thuận tiện khi trình bày và sử dụng.
Dòng 7–8: Xuất bảng bằng kable() chú thích (caption) vào tài liệu báo cáo
data_acl <- data_acl %>%
tidyr::extract(col = Times, into = c("Year", "Quarter"), regex = "([0-9]{4}).*Q([1-4])",
remove = FALSE)
Giải thích:
Dòng 1: Gán lại giá trị cho bảng dữ liệu data_acl, khởi đầu cho một chuỗi xử lý (pipe %>%).
Dòng 2–3: Sử dụng hàm tidyr::extract() để tách dữ liệu từ biến chuỗi Times, chỉ định cột nguồn là Times, into: Định nghĩa hai cột mới “Year” và “Quarter” để lưu giá trị tách ra, regex Dùng biểu thức chính quy để nhận diện năm và quý .
Nhận xét:
Đoạn mã này không trực tiếp tạo biểu đồ mà giúp chuẩn hóa, làm sạch dữ liệu, rất quan trọng trước khi trực quan hóa hoặc phân tích số liệu tài chính/quý/năm.
data_acl$Year <- as.numeric(data_acl$Year)
Giải thích:
Dòng 1 giúp lấy cột Year trong bảng dữ liệu data_acl và chuyển toàn bộ giá trị sang kiểu số học (numeric) bằng hàm as.numeric().
Nhận xét:
Đây là bước chuẩn hóa dữ liệu quan trọng trong quá trình xử lý dữ liệu thời gian; giúp đảm bảo mọi thao tác phân tích hoặc trực quan hóa (vẽ biểu đồ đường, cột, v.v.) đều chạy đúng và chính xác.
head(data_acl[, c("Quarter", "Year")])
class(data_acl$Year)
## [1] "numeric"
Giải thích:
head() xuất 6 dòng đầu tiên trong bảng dữ liệu data_acl, chỉ hiện hai cột: Quarter (ký hiệu quý) và Year (năm). class() kiểm tra kiểu dữ liệu của cột Year sau khi đã chuẩn hóa. Trả về kết quả “numeric”, xác định rằng cột này đã chuyển sang dạng số học, phù hợp cho các phép tính toán hoặc trực quan hóa.
Nhận xét:
Việc tách và chuẩn hóa kiểu dữ liệu cho cột Year đã thành công. Kết quả này đảm bảo dữ liệu đã “sạch” và sẵn sàng cho phân tích theo năm.
data_acl$SIZE <- log(data_acl$TongTS)
Giải thích:
Dòng này tạo ra biến mới SIZE trong bảng dữ liệu data_acl, biểu thị quy mô doanh nghiệp (SIZE). Giá trị SIZE được tính bằng logarit tự nhiên của tổng tài sản
Nhận xét:
Việc tạo biến SIZE theo logarit tổng tài sản là thông lệ chuẩn trong các nghiên cứu tài chính (giúp ổn định phương sai, giảm ảnh hưởng của doanh nghiệp siêu lớn, không bị lệch về phía các doanh nghiệp lớn vượt trội).
data_acl <- mutate(data_acl, DR = NoPhaiTra/TongTS)
Giải thích:
Sử dụng hàm mutate() từ gói dplyr để thêm biến mới (DR) vào bảng dữ liệu data_acl, trong đó giá trị của từng quan sát được tính theo công thức:
\[ DR_i = \frac{NoPhaiTra_i}{TongTS_i} \]
Nhận xét:
Chỉ số DR phản ánh mức độ sử dụng đòn bẩy tài chính (vay nợ) của doanh nghiệp. Đây là biến đặc trưng trong phân tích rủi ro tài chính, kiểm toán, định giá doanh nghiệp hoặc đánh giá cấu trúc vốn.
data_acl <- mutate(data_acl, TyLeVon_TS = VonCSH/TongTS)
Giải thích:
Hàm mutate() trong gói dplyr để tạo thêm biến mới có tên TyLeVon_TS, đại diện cho tỷ lệ vốn chủ sở hữu trên tổng tài sản (Equity-to-Asset Ratio). Trong trường hợp này, biến TyLeVon_TS được tính bằng công thức sau:
\[ E/A_i = \frac{VonCSH_i}{TongTS_i} \]
Nhận xét:
Biến TyLeVon_TS (hay Equity-to-Asset Ratio) là thước đo quan trọng trong phân tích cơ cấu tài chính, thường được sử dụng song song với Tỷ lệ nợ/Tổng tài sản (DR) để đánh giá mức độ an toàn vốn và khả năng chống chịu rủi ro tài chính.
data_acl <- mutate(data_acl, AS = NoNH/NoPhaiTra)
Giải thích:
Hàm mutate() trong gói dplyr để tạo biến mới có tên AS, đại diện cho cấu trúc tài sản (Asset Structure), được đo bằng tỷ trọng nợ ngắn hạn trong tổng nợ phải trả. Công thức tính toán được biểu diễn bằng ký hiệu toán học như sau:
\[ AS_i = \frac{NoNH_i}{NoPhaiTra_i} \]
Nhận xét:
Chỉ tiêu AS phản ánh mức độ tập trung của doanh nghiệp vào nguồn vốn ngắn hạn trong tổng cơ cấu nợ. Khi giá trị AS càng cao, doanh nghiệp phụ thuộc nhiều hơn vào nợ ngắn hạn, điều này giúp tăng tính linh hoạt tài chính trong ngắn hạn nhưng cũng làm tăng rủi ro thanh khoản nếu dòng tiền không ổn định và ngược lại
data_acl <- mutate(data_acl, ROA = LNSTCPP/TongTS)
Giải thích:
Hàm mutate() trong gói dplyr để tạo biến mới có tên ROA, đại
diện cho tỷ suất sinh lời trên tổng tài sản (Return on Assets) — một chỉ
tiêu đo lường mức độ hiệu quả trong việc sử dụng tài sản để tạo ra lợi
nhuận.
Công thức tính tỷ suất sinh lời trên tổng tài sản (ROA): \[
ROA_i = \frac{LNSTCPP_i}{TongTS_i}
\]
Nhận xét:
Chỉ tiêu ROA phản ánh hiệu quả sử dụng tài sản của doanh nghiệp — tức là mỗi đồng tài sản đang tạo ra bao nhiêu đồng lợi nhuận. Giá trị ROA càng cao cho thấy doanh nghiệp hoạt động hiệu quả, sử dụng tài sản hợp lý để sinh lời và ngược lại
data_acl <- mutate(data_acl, ROE = LNSTCPP/VonCSH)
Giải thích:
Tạo biến mới có tên ROE bằng hàm mutate trong bộ dữ liệu
data_acl, đại diện cho tỷ suất sinh lời trên vốn chủ sở hữu (Return on
Equity) – một chỉ tiêu quan trọng phản ánh khả năng sinh lợi của doanh
nghiệp từ nguồn vốn góp của cổ đông được biểu diễn bằng ký hiệu toán học
như sau:
\[
ROE_i = \frac{LNSTCPP_i}{VonCSH_i}
\]
Nhận xét:
Chỉ tiêu ROE phản ánh hiệu quả sử dụng vốn của chủ sở hữu, cho biết mỗi đồng vốn góp tạo ra bao nhiêu đồng lợi nhuận. ROE càng cao thể hiện khả năng sinh lời tốt, thu hút nhà đầu tư và thể hiện năng lực quản trị vốn hiệu quả. Ngược lại, ROE thấp hoặc âm cho thấy doanh nghiệp đang gặp khó khăn trong hoạt động kinh doanh hoặc cơ cấu vốn chưa hợp lý.
data_acl <- mutate(data_acl, CR = TSNH/NoNH)
Giải thích:
Biến CR được tạo thêm trong bộ dữ liệu data bằng hàm
mutate(), biểu thị khả năng thanh toán hiện hành (Current
Ratio) — một chỉ tiêu tài chính quan trọng phản ánh năng lực của doanh
nghiệp trong việc sử dụng tài sản ngắn hạn để chi trả các khoản nợ ngắn
hạn được tính theo công thức toán học sau:
\[
CR_i = \frac{TSNH_i}{NoNH_i}
\]
Nhận xét:
Chỉ tiêu CR cho biết mức độ đảm bảo của tài sản ngắn hạn đối với các khoản nợ ngắn hạn. Một doanh nghiệp có CR cao thường được đánh giá là an toàn về thanh khoản, vì có đủ tài sản ngắn hạn để chi trả các nghĩa vụ ngắn hạn đến hạn.
data_acl <- mutate(data_acl, QR = (TSNH - HangTonKho)/NoNH)
Giải thích:
Hàm mutate() trong gói dplyr để tạo thêm biến mới có tên QR,
biểu thị khả năng thanh toán nhanh (Quick Ratio) — một chỉ tiêu đo lường
khả năng của doanh nghiệp trong việc thanh toán các khoản nợ ngắn hạn
bằng công thức sau:
\[
QR_i = \frac{TSNH_i - HangTonKho_i}{NoNH_i}
\]
Nhận xét:
Chỉ tiêu QR phản ánh khả năng thanh toán nợ ngắn hạn mà không cần bán hàng tồn kho, do hàng tồn kho thường mất thời gian chuyển đổi thành tiền mặt. Giá trị QR càng cao cho thấy doanh nghiệp có khả năng thanh toán tốt trong ngắn hạn, giảm rủi ro tài chính.Ngược lại, QR thấp cho thấy rủi ro thanh khoản cao, doanh nghiệp có thể gặp khó khăn khi phải đáp ứng nghĩa vụ nợ ngắn hạn trong điều kiện dòng tiền hạn chế.
data_acl <- mutate(data_acl, Season = case_when(Quarter == 1 ~ "Spring", Quarter ==
2 ~ "Summer", Quarter == 3 ~ "Autumn", Quarter == 4 ~ "Winter"))
Giải thích:
Hàm mutate() được sử dụng để tạo thêm biến mới Season trong bộ dữ liệu data_acl. Hàm case_when() là một công cụ mạnh trong dplyr, cho phép gán nhãn theo điều kiện. Ở đây, mỗi quý được phân loại thành một mùa tương ứng: Quý 1 → Spring (Mùa xuân); Quý 2 → Summer (Mùa hè); Quý 3 → Autumn (Mùa thu); Quý 4 → Winter (Mùa đông). Kết quả là một biến phân loại mới Season được thêm vào data.
Nhận xét: - Biến Season giúp doanh nghiệp phân tích chu kỳ mùa vụ tài chính — ví dụ: quý 2–3 là mùa sản xuất và thu hoạch, quý 4 là chu kỳ kết toán và bán hàng. Đây là cơ sở để đánh giá biến động tồn kho (HangTonKho) và lợi nhuận (LNSTCPP) theo mùa.
data_acl <- mutate(data_acl, HalfYear = ifelse(Quarter %in% c(1, 2), "Đầu năm",
"Cuối năm"))
Giải thích:
Dùng hàm mutate() để thêm biến mới HalfYear vào bảng dữ liệu data_acl. Sử dụng hàm ifelse() để phân loại các quý tài chính:Nếu Quarter nằm trong các giá trị 1 hoặc 2 thì gán nhãn “Đầu năm”. Nếu không (tức là quý 3 hoặc 4), gán nhãn “Cuối năm”.
Nhận xét:
Việc phân loại này đặc biệt hữu ích khi đánh giá xu hướng kinh doanh, biến động tài sản, lợi nhuận,… theo từng giai đoạn đầu năm và cuối năm.
Giải thích:
Hàm summary() Thực hiện thống kê mô tả nhanh các đặc trưng của biến số học gồm: giá trị nhỏ nhất (Min), tứ phân vị thứ nhất (Q1), trung vị (Median), trung bình (Mean), tứ phân vị thứ ba (Q3) và giá trị lớn nhất (Max). Giúp mô tả xu hướng trung tâm, độ lệch và phân tán tổng thể của dữ liệu.
Hàm sd() Tính độ lệch chuẩn – thể hiện mức độ dao động trung bình của các quan sá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 ổn định. Đơn vị đo giống biến gốc, thuận tiện khi so sánh.
Hàm var() Tính phương sai – trung bình bình phương độ lệch của các giá trị so với trung bình. Là cơ sở để tính độ lệch chuẩn (sd = sqrt(var)); giá trị càng lớn phản ánh dữ liệu càng biến động. Đơn vị là bình phương của biến gốc..
summary(data_acl$QR)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.2946 0.4095 0.4613 0.4776 0.5316 0.6700
sd(data_acl$QR)
## [1] 0.09869096
var(data_acl$QR)
## [1] 0.009739905
Nhận xét:
QR có giá trị trung bình 0.4776, trung vị 0.4613; nghĩa là phân phối hơi lệch phải, số lượng lớn giá trị nằm gần trung bình nhưng vẫn có xu hướng cao lên tới tối đa 0.67. Biên độ dao động khá vừa: QR thấp nhất 0.2946, cao nhất 0.6700 (chênh lệch không quá lớn so với trung bình). Độ lệch chuẩn nhỏ (≈0.0987) và phương sai thấp cho thấy dữ liệu QR ít phân tán, khá tập trung quanh trung bình và trung vị, ít có giá trị bất thường hoặc cực đoan.
Biến QR của công ty ACL thể hiện rằng khả năng thanh toán nhanh ở mức thấp và ổn định, cho thấy doanh nghiệp phụ thuộc nhiều vào hàng tồn kho hoặc chưa giữ đủ tài sản thanh khoản để đáp ứng nợ ngắn hạn tức thời. Tuy nhiên, mức độ ổn định (SD thấp) cũng là tín hiệu quản lý dòng tiền tương đối nhất quán qua các kỳ.
summary(data_acl$CR)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.9998 1.0676 1.3680 1.3355 1.5707 1.9906
sd(data_acl$CR)
## [1] 0.2774623
var(data_acl$CR)
## [1] 0.07698531
Nhận xét:
Giá trị trung bình CR = 1.34, trung vị = 1.37 doanh nghiệp đa phần đáp ứng được khả năng thanh toán ngắn hạn. Khoảng biến động khá hẹp: min ≈ 1, max ≈ 2; độ lệch chuẩn 0.28 cho thấy doanh nghiệp đều có mức thanh khoản tốt, ít gặp rủi ro thanh toán.
Nhiều nghiên cứu tại Việt Nam, Hoa Kỳ, các quốc gia khác đều lấy ngưỡng tối ưu CR từ 1.2–2 tùy ngành nghề, nếu quá cao cũng chưa chắc tốt (có thể doanh nghiệp chưa tận dụng hiệu quả dòng vốn). Tổng thể, chỉ số CR của ACL ổn định và an toàn, phù hợp với doanh nghiệp trong ngành chế biến thủy sản có tính chu kỳ sản xuất – tiêu thụ cao.
summary(data_acl$ROE)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.1845 0.2530 0.3096 0.3409 0.3527 0.6259
sd(data_acl$ROE)
## [1] 0.1286856
var(data_acl$ROE)
## [1] 0.01655999
Nhận xét:
ROE trung bình (mean = 0.3409 ~ 34,1%) khá cao so với tiêu chuẩn thực tế. Trung vị là 0.3096 (30,96%) và phân vị thứ ba 0.3527 cho thấy đa số các quý trong mẫu đạt hiệu suất sinh lời trên vốn chủ sở hữu tốt. Khoảng biến động: ROE thấp nhất là 18,45% và cao nhất 62,59%, biên độ tương đối lớn nhưng đa số các giá trị đều tập trung dưới mức 35%. Độ lệch chuẩn 0.1287 (12,87%), phương sai 0.0166 – chỉ ra rằng mức độ phân tán ROE vừa phải, không quá lệch, ít các quý có dấu hiệu cực đoan.
Trong môi trường tài chính - kế toán, ROE > 15% đã được coi là rất tốt với nhiều ngành. Tại Việt Nam và quốc tế, ROE phổ biến doanh nghiệp tốt khoảng 12-25%, doanh nghiệp vượt mức này (30–40%) thường là các công ty phát triển nhanh hoặc ngành đặc thù. ROE quá cao (trên 60%) có thể là dấu hiệu bất thường, cần kiểm tra lại cơ cấu vốn. Như vậy, các doanh nghiệp trong dữ liệu đăng kiểm có hiệu suất sinh lời vốn chủ sở hữu khá tốt, phân bổ đều, cấu trúc tài chính bền vững – đây là tín hiệu tích cực khi đánh giá hiệu quả sử dụng vốn.
summary(data_acl$ROA)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.05794 0.07846 0.13296 0.14392 0.18024 0.30920
sd(data_acl$ROA)
## [1] 0.07316189
var(data_acl$ROA)
## [1] 0.005352661
Nhận xét:
ROA trung bình = 0.14392 (~14,4%) phản ánh mức sinh lời trên tổng tài sản của doanh nghiệp trong tập dữ liệu, khá tốt so với mặt bằng chung hiện nay (nhiều ngành duy trì ROA ở mức 4–10%).
Khoảng biến động của ROA thấp nhất là 5,8%; cao nhất là 30,9%. Đa số doanh nghiệp có ROA ở ngưỡng an toàn: giá trị trung vị 13,3%, phân vị thứ nhất 7,8%, phân vị ba 18%, tập trung mạnh quanh mức 10–18%. Độ lệch chuẩn (0.073) và phương sai nhỏ chứng tỏ dữ liệu ít phân tán, ít outlier.
ROA là chỉ số quan trọng phản ánh hiệu quả sử dụng tài sản: giá trị càng cao nghĩa là doanh nghiệp càng tận dụng tốt nguồn lực (tài sản) để tạo ra lợi nhuận.Doanh nghiệp trong dữ liệu phân tích đều đạt ROA khá (trung bình >10%), chứng tỏ có năng lực quản trị, sử dụng vốn hiệu quả so với thực tiễn thị trường.
summary(data_acl$AS)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.8758 0.9573 1.0000 0.9757 1.0000 1.0000
sd(data_acl$AS)
## [1] 0.03555156
var(data_acl$AS)
## [1] 0.001263913
Nhận xét:
Biến AS có giá trị trung bình (mean) và trung vị đều tiến sát 1 (trung bình 0.9757, trung vị/3rd Qu./max đều 1.0000); giá trị nhỏ nhất cũng khá cao (0.8758). Khoảng biến động rất nhỏ: độ lệch chuẩn ≈ 0.0356, phương sai ≈ 0.00126. Điều này chứng tỏ cơ cấu tài sản của các doanh nghiệp trong mẫu phân tích rất đồng đều, phần lớn đều duy trì tỷ lệ hoặc cấu trúc tài sản ổn định.
Trong phân tích tài chính doanh nghiệp, biến cấu trúc tài sản thường phản ánh tỷ lệ TS ngắn hạn/dài hạn hoặc tỷ lệ từng loại tài sản so với tổng tài sản. Giá trị gần bằng 1 và biên độ biến động hẹp có nghĩa các doanh nghiệp cùng ngành/lĩnh vực tuân thủ chuẩn chung về quản trị tài sản. Nếu giá trị này dao động lớn, có thể doanh nghiệp có chính sách đầu tư khác biệt hay rủi ro quản trị tài sản cao.
summary(data_acl$TyLeVon_TS)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.2340 0.3221 0.4316 0.4074 0.4760 0.5999
sd(data_acl$TyLeVon_TS)
## [1] 0.09399764
var(data_acl$TyLeVon_TS)
## [1] 0.008835556
Nhận xét:
Trung bình tỷ lệ vốn trên tài sản (mean = 0.4074, tức 40,74%) và trung vị 43,16% – cho thấy phần lớn doanh nghiệp dùng vốn chủ sở hữu chiếm từ 32–48% tổng tài sản. Vùng giá trị trải hẹp (min-max: 23,4% đến 59,99%), biên độ dao động vừa phải. Phân tán không lớn khi có độ lệch chuẩn thấp (≈0.094) và phương sai (≈0.0088) thể hiện sự đồng đều về tỷ lệ vốn ở doanh nghiệp; không có quý nào quá thấp hoặc quá cao vượt trội về mức sở hữu vốn.
Tỷ lệ này càng cao, doanh nghiệp càng tự chủ tài chính, ít rủi ro về nợ; tỷ lệ thấp nghĩa là doanh nghiệp phụ thuộc nhiều vào vốn vay – điều này có thể làm tăng rủi ro tài chính khi môi trường lãi suất biến động.
summary(data_acl$DR)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.4001 0.5240 0.5684 0.5926 0.6779 0.7660
sd(data_acl$DR)
## [1] 0.09399764
var(data_acl$DR)
## [1] 0.008835556
Nhận xét:
Giá trị trung bình (Mean) DR: 0.5926 – tức là Nợ phải trả của ACL chiếm khoảng 59% tổng tài sản. Đây là mức mà doanh nghiệp thường được xếp vào nhóm sử dụng vốn vay ở mức cao nhưng vẫn nằm trong vùng an toàn của ngành sản xuất thủy sản tại Việt Nam. Trung vị (Median) DR: 0.5684, các phân vị thứ nhất (Q1) 0.5240 và thứ ba (Q3) 0.6779 cho thấy phần lớn các quý trong suốt 11 năm đều có tỷ lệ vốn vay/tổng tài sản ổn định quanh ngưỡng này.
Khoảng biến động tương đối hẹp với Min = 0.4001 và Max = 0.7660, độ lệch chuẩn 0.094 và phương sai 0.0088: Điều này tức là tỷ lệ DR của ACL không có giá trị cực đoan, gần như duy trì cấu trúc vốn ổn định trong dài hạn. Sự biến động vừa phải cho thấy ACL kiểm soát rủi ro nợ khá tốt, không để chi phí vay vượt ngưỡng an toàn dài hạn.
summary(data_acl$SIZE)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 27.34 27.79 27.96 27.90 28.06 28.20
sd(data_acl$SIZE)
## [1] 0.2247278
var(data_acl$SIZE)
## [1] 0.05050259
Nhận xét:
SIZE (log tài sản) của công ty ACL trong giai đoạn 2014–2024 có giá trị trung bình 27.90, khá sát với trung vị 27.96 và các phân vị (Q1=27.79, Q3=28.06). Khoảng biến động nhỏ: chỉ từ 27.34 (min) đến 28.20 (max). Độ lệch chuẩn 0.22 và phương sai 0.05 cho thấy quy mô tài sản ACL biến động rất ít qua các quý. Điều này phản ánh sự ổn định về quy mô, không có biến động lớn hoặc thay đổi mạnh về tổng tài sản suốt 11 năm liên tục.
Với doanh nghiệp thủy sản sản xuất như ACL, quy mô tài sản ổn định là dấu hiệu tích cực: dễ kiểm soát dòng vốn, an toàn tài chính, ít rủi ro tăng trưởng nóng hoặc suy giảm đột ngột. Nếu quy mô tài sản đột biến trong giai đoạn này, thường là do sáp nhập, đầu tư lớn hoặc biến động ngành.
summary(data_acl$LNSTCPP)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 4.998e+10 9.288e+10 1.859e+11 2.027e+11 2.761e+11 4.323e+11
sd(data_acl$LNSTCPP)
## [1] 116718852005
var(data_acl$LNSTCPP)
## [1] 1.362329e+22
Nhận xét:
LNSTCPP của ACL có giá trị trung bình 2.027e+11 (~2027 tỷ đồng) qua các quý, trung vị 1.859e+11. Biến động tầm từ ~50 tỷ lên đến ~432 tỷ, thể hiện các quý chênh lệch lợi nhuận tích lũy tương đối cao. Độ lệch chuẩn rất lớn (gần 117 tỷ), phương sai cao, cho thấy lợi nhuận chưa phân phối qua các quý biến động mạnh, có thể do ảnh hưởng bởi hiệu quả kinh doanh từng năm hoặc các yếu tố đột biến.
LNSTCPP thể hiện sức mạnh tích lũy nội lực doanh nghiệp; biến động lớn nhưng với xu thế giá trị ngày càng tăng là tín hiệu tốt. Nếu lợi nhuận chưa phân phối giảm bất thường cần kiểm tra yếu tố phân phối lợi nhuận hoặc phát sinh lỗ lớn.
options(kableExtra.latex.load_packages = FALSE)
options(knitr.kable.NA = "")
options(knitr.table.format = "latex")
vars <- c("SIZE", "DR", "TyLeVon_TS", "AS", "ROA", "ROE", "CR", "QR", "HangTonKho",
"LNSTCPP")
corr_matrix <- round(cor(data_acl[, vars], use = "complete.obs"), 2)
kable(corr_matrix, caption = " Ma trận tương quan giữà 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 1–3: Cài đặt tuỳ chọn xuất bảng latex và loại bỏ giá trị NA, định dạng bảng cho RMarkdown và kable.
Dòng 4-5: Xây dựng vector tên các biến cần phân tích tương quan và tính ma trận tương quan cor(data_acl[, vars], … ) tạo ra ma trận tương quan giữa các biến số, round(…, 2) làm tròn tới 2 chữ số thập phân.
Dòng 6–13: Xuất kết quả ma trận tương quan ra bảng latex đẹp, căn giữa, có tiêu đề và chú thích
Công thức tính hệ số tương quan Pearson giữa hai biến X và Y được
biểu diễn như sau:
\[
r_{XY} = \frac{\sum_{i=1}^{n} (X_i - \bar{X})(Y_i - \bar{Y})}
{\sqrt{\sum_{i=1}^{n} (X_i - \bar{X})^2} \sqrt{\sum_{i=1}^{n} (Y_i -
\bar{Y})^2}}
\]
Nhận xét:
Bảng ma trận cho thấy các cặp biến tài chính độc lập của ACL giai đoạn 2014–2024 có nhiều mối quan hệ mạnh và trung bình:
SIZE & HangTonKho (0.90) cho thấy mối tương quan mạnh, chứng tỏ quy mô lớn thì tồn kho cũng cao. DR & CR (-0.96) có giá trị âm lớn, nghĩa là khi tỷ lệ nợ cao thì khả năng thanh khoản hiện hành thấp đi rõ rệt. TyLeVon_TS & DR (-1.00) chỉ ra tương quan đảo chiều hoàn hảo, phản ánh cấu trúc vốn vay/chủ sở hữu là hai mặt đối lập của tổng tài sản.
Các mối tương quan mạnh như SIZE với CR (0.72), ROA với ROE (0.94), LNSTCPP với ROA/ROE cũng rất cao (0.87, 0.87). Nhiều mối liên hệ trung bình (0.5–0.8)như biến SIZE với LNSTCPP (0.66), AS với TyLeVon_TS (0.85)…
Nhìn tổng thể, các biến tài chính của ACL có tương quan logic, phản ánh đúng thực tế: doanh nghiệp quy mô lớn thì khả năng tích lũy, tồn kho, sinh lời cao hơn, vốn vay và chủ đều ảnh hưởng ngược chiều rõ nét. Bảng này giúp phát hiện cặp biến quá tương quan (cần lưu ý khi đưa vào mô hình hồi quy để tránh đa cộng tuyến) và nhận diện bản chất tài chính, cấu trúc vốn, hiệu quả vận hành ACL trong 11 năm liên tục.
data_acl$DebtLevel <- cut(data_acl$DR, breaks = c(-Inf, 0.5, 0.65, Inf), labels = c("Thấp",
"Trung bình", "Cao"))
kable(table(data_acl$DebtLevel), col.names = c("Mức đòn bẩy tài chính", "Số lượng quan sát"),
align = "lc", caption = "Phân tổ mức đòn bẩy tài chính của doanh nghiệp") %>%
kable_styling(full_width = FALSE, position = "center", latex_options = "H")
Giải thích:
Dòng 1-2 sử dụng hàm cut() để phân loại mức đòn bẩy tài chính theo biến số DR. Thiết lập ngưỡng: DR < 0.5 là “Thấp”; 0.5 ≤ DR < 0.65 là “Trung bình”; DR ≥ 0.65 là “Cao”. Kết quả là tạo biến mới DebtLevel trong bảng dữ liệu, lưu nhãn phân cấp mức độ đòn bẩy.
Dòng 3–5 dùng hàm table() tổng hợp số lượng từng mức độ đòn bẩy tài chính xuất hiện trong dữ liệu. Hàm kable() xuất bảng tóm tắt với hai cột: “Mức đòn bẩy tài chính”, “Số lượng quan sát”, căn lề và caption chú thích. Hàm kable_styling(…) căn giữa bảng, không trải hết chiều rộng file PDF, kiểu trình bày đẹp mắt.
Nhận xét:
Bảng tổng hợp sau khi chạy thể hiện tỷ lệ doanh nghiệp/quý thuộc từng mức độ đòn bẩy tài chính trong toàn bộ dữ liệu (2014–2024). Giúp nhận diện nhanh: Có bao nhiêu quan sát “Thấp” (doanh nghiệp an toàn về vốn vay), “Trung bình” (chấp nhận được), “Cao” (rủi ro tài chính cao). Hỗ trợ phân tích xu hướng ngành, xác định tỷ lệ doanh nghiệp cần kiểm soát/bổ sung vốn chủ sở hữu hoặc giảm vay.
data_acl$ProfitLevel <- cut(data_acl$ROE, breaks = c(-Inf, 0.25, 0.35, Inf), labels = c("Thấp",
"Trung bình", "Cao"))
kable(table(data_acl$ProfitLevel), col.names = c("Mức sinh lời (ROE)", "Số lượng quan sát"),
align = "lc", caption = "Phân tổ mức sinh lời (ROE) của doanh nghiệp") %>%
kable_styling(full_width = FALSE, position = "center", latex_options = "H")
Giải thích:
Dòng 1-2 sử dụng hàm cut() để phân loại mức sinh lời (ROE) của từng quan sát trong dữ liệu: Chia biến ROE thành 3 nhóm: “Thấp”: ROE < 0.25, “Trung bình”: 0.25 ≤ ROE < 0.35, “Cao”: ROE ≥ 0.35. Tạo biến mới ProfitLevel chứa nhãn phân nhóm cho từng dòng dữ liệu.
Dòng 3–5 dùng hàm table() để tổng hợp số lượng mỗi nhóm ROE xuất hiện trong bộ dữ liệu, đếm số lần mỗi mức “Thấp”, “Trung bình”, “Cao”. kable() xuất bảng kết quả với hai cột: “Mức sinh lời” và “Số lượng quan sát” tên cột, caption rõ ràng, căn lề đẹp. kable_styling() căn giữa bảng, trình bày latex đẹp, phù hợp xuất PDF/báo cáo.
Nhận xét:
Phần lớn các quý/doanh nghiệp có ROE từ trung bình trở lên (ROE > 25%), chứng tỏ hiệu suất sinh lời trên vốn chủ sở hữu của ACL qua các năm đa phần khá và cao, chỉ một phần nhỏ rơi vào nhóm thấp (hiệu suất sinh lời yếu hơn). Việc nhóm “Trung bình” chiếm nhiều nhất cho thấy doanh nghiệp vận hành ổn định, ít bị biến động mạnh về sinh lời, phù hợp thực tiễn các công ty đầu ngành.
data_acl$LiquidityLevel <- cut(data_acl$CR, breaks = c(-Inf, 1, 1.5, Inf), labels = c("Yếu",
"Trung bình", "Tốt"))
kable(table(data_acl$LiquidityLevel), col.names = c("Mức độ thanh khoản (CR)",
"Số lượng quan sát"), align = "lc", caption = "Phân tổ mức độ thanh khoản (CR) của doanh nghiệp") %>%
kable_styling(full_width = FALSE, position = "center", latex_options = "H")
Giải thích:
Dòng 1-2 sử dụng hàm cut() để phân loại mức độ thanh khoản theo giá trị CR (Current Ratio) của từng doanh nghiệp/quý theo ngưỡng chia: CR < 1 là “Yếu”, 1 ≤ CR < 1.5 là “Trung bình”, CR ≥ 1.5 là “Tốt”. Tạo biến mới LiquidityLevel trong bảng dữ liệu.
Dòng 3–5 dùng hàm table() đếm số lượng doanh nghiệp/quý rơi vào từng nhóm thanh khoản (Yếu/Trung bình/Tốt). Hàm kable() xuất bảng kết quả với cột tên mức độ thanh khoản và số lượng quan sát, caption rõ ràng, căn lề cho từng cột, dùng kable_styling() trình bày latex đẹp cho báo cáo.
Nhận xét:
Phần lớn doanh nghiệp/quý ACL có khả năng thanh toán ngắn hạn ở mức trung bình và tốt, chỉ duy nhất một điểm yếu, cho thấy kiểm soát vòng vốn an toàn qua nhiều năm. Nhóm “Tốt” (CR ≥ 1.5) chiếm tỷ lệ cao cho thấy nhiều thời kỳ ACL có cấu trúc vốn lưu động an toàn vượt chuẩn ngành, hạn chế rủi ro mất khả năng thanh toán. Số lượng “Trung bình” chiếm nhiều nhất phản ánh đa phần doanh nghiệp vận động ổn định ở mức vừa phải, phù hợp thực tế quản trị tài chính Việt Nam.
data_acl$SizeGroup <- cut(data_acl$SIZE, breaks = c(-Inf, 27.8, 28, Inf), labels = c("Nhỏ",
"Vừa", "Lớn"))
kable(table(data_acl$SizeGroup), col.names = c("Quy mô doanh nghiệp", "Số lượng quan sát"),
align = "lc", caption = "Phân tổ quy mô (SIZE) của doanh nghiệp") %>%
kable_styling(full_width = FALSE, position = "center", latex_options = "H")
Giải thích:
Dòng 1-2 sử dụng hàm cut() để phân loại quy mô doanh nghiệp (SIZE) trong dữ liệu. Quy chuẩn: SIZE < 27.8 là “Nhỏ”; 27.8 ≤ SIZE < 28 là “Vừa”; SIZE ≥ 28 là “Lớn”. Các ngưỡng này được xác định dựa trên logarit tổng tài sản. Kết quả tạo ra biến mới SizeGroup trong bảng.
Dòng 3–4 dùng hàm table() để tổng hợp số lượng doanh nghiệp ở từng nhóm quy mô. kable() xuất bảng thống kê với hai cột: “Quy mô doanh nghiệp” và “Số lượng quan sát”, có caption mô tả, có căn lề cho cột số liệu. kable_styling() căn giữa/thu nhỏ bảng, trình bày.
Nhận xét:
Quy mô doanh nghiệp ACL qua các quý chủ yếu tập trung ở nhóm “Lớn” và “Vừa” (chiếm đa số). Điều này cho thấy trong bộ dữu liệu, nhiều doanh nghiệp/quý có tổng tài sản tương đối cao, phản ánh năng lực sản xuất và tài chính lớn hơn mặt bằng chung ngành. Nhóm “Nhỏ” chiếm số lượng thấp, phù hợp thực tế ngành thủy sản thường tập trung ở các công ty quy mô lớn trên sàn niêm yết.
Việc phân tổ này giúp kiểm tra xu hướng chuyển dịch quy mô tài sản, phân tích sâu hơn hiệu quả vận hành, so sánh đặc trưng từng nhóm và làm nhóm kiểm định (thống kê, mô hình hóa, so sánh theo SIZE).
roe_by_debt <- data_acl %>%
group_by(DebtLevel) %>%
summarise(ROE_tb = mean(ROE, na.rm = TRUE), ROE_min = min(ROE, na.rm = TRUE),
ROE_max = max(ROE, na.rm = TRUE))
kable(roe_by_debt, caption = "Thống kê ROE trung bình theo nhóm đòn bẩy tài chính",
col.names = c("Mức đòn bẩy tài chính", "ROE trung bình", "ROE nhỏ nhất",
"ROE lớn nhất"), align = "lccc") %>%
kable_styling(full_width = FALSE, position = "center", latex_options = "H")
Giải thích:
Dòng 1-2 khởi tạo biến roe_by_debt, sử dụng group_by(DebtLevel) để phân nhóm dữ liệu theo từng mức đòn bẩy tài chính đã phân loại trước đó (“Thấp”, “Trung bình”, “Cao”). Điều này giúp thực hiện các phép tính tổng hợp trên từng nhóm đòn bẩy.
Dòng 3–4 dùng summarise() để tính toán thống kê ROE (Return on Equity) cho từng nhóm đòn bẩy theo giá trị trung bình (mean), giá trị lớn nhất (max), giá trị nhỏ nhất(min)
Dòng 5–7 dùng kable() xuất bảng thống kê ra báo cáo với chú thích rõ ràng, đặt tên cột chi tiết và tùy chỉnh trình bày căn lề với align = “lccc”.
Nhận xét:
Nhóm đòn bẩy tài chính trung bình có ROE trung bình cao nhất (0.4030), vượt khá xa hai nhóm còn lại.Sử dụng đòn bẩy tài chính ở mức hợp lý (trung bình) giúp ACL tối ưu hiệu quả sử dụng vốn chủ sở hữu để sinh lời – doanh nghiệp có thể tận dụng nguồn vốn vay hợp lý nhưng không bị rủi ro chi phí lãi vay quá lớn.Đòn bẩy quá thấp đồng nghĩa ít tận dụng vốn vay, ROE không cao nhất; đòn bẩy quá cao lại khiến chi phí tài chính lớn làm giảm hiệu quả sinh lời.
roa_by_season <- data_acl %>%
group_by(Season) %>%
summarise(ROA_tb = mean(ROA, na.rm = TRUE), sd_ROA = sd(ROA, na.rm = TRUE))
kable(roa_by_season, caption = "Tỷ suất sinh lời trên tài sản (ROA) theo mùa vụ",
col.names = c("Mùa", "ROA trung bình", "Độ lệch chuẩn"), align = "lcc") %>%
kable_styling(full_width = FALSE, position = "center", latex_options = "H")
Giải thích:
Dòng 1-2 khởi tạo biến roa_by_season, Sử dụng group_by(Season) để phân nhóm biến ROA theo từng mùa vụ.
Dòng 3 dùng summarise() để tính toán thống kê ROA cho từng mùa: giá trị trung bình (mean), độ lệch chuẩn mỗi mùa (sd).
Dòng 4–5 sử dụng kable() để xuất bảng kết quả với chú thích rõ ràng, tên các cột đã chuẩn hóa (“Mùa”, “ROA trung bình”, “Độ lệch chuẩn”), tham số align căn lề đẹp cho từng cột.
Nhận xét:
ROA trung bình của các mùa khá đồng đều (từ ~0.143 đến ~0.146), không mùa nào vượt trội hay thấp bất thường. Điều này chứng tỏ hoạt động sinh lời trên tổng tài sản của ACL không chịu ảnh hưởng mạnh từ yếu tố mùa vụ. Độ lệch chuẩn thấp (dao động từ 0.076 đến 0.081): Dữ liệu ROA từng mùa ít phân tán, đa phần các kỳ trong mỗi mùa đều đạt giá trị gần với trung bình chung.
size_by_profit <- data_acl %>%
group_by(ProfitLevel) %>%
summarise(SIZE_tb = mean(SIZE, na.rm = TRUE), sd_SIZE = sd(SIZE, na.rm = TRUE))
kable(size_by_profit, caption = "Quy mô doanh nghiệp (SIZE) theo mức sinh lời ROE",
col.names = c("Nhóm lợi nhuận", "Quy mô trung bình", "Độ lệch chuẩn"),
align = "lcc") %>%
kable_styling(full_width = FALSE, position = "center", latex_options = "H")
Giải thích:
Dòng 1-2 tạo biến size_by_profit, sử dụng group_by(ProfitLevel) để phân nhóm dữ liệu theo mức sinh lời (ProfitLevel).
Dòng 3 dùng summarise() để tổng hợp chỉ số SIZE: giá trị trung bình (mean), độ lệch chuẩn (sd)
Dòng 4-6 dùng kable() xuất bảng với tiêu đề, đặt lại tên các cột và căn lề bảng đẹp mắt bằng tham số align
Nhận xét:
Kết quả cho thấy, doanh nghiệp/quý có quy mô tài sản lớn hơn (SIZE cao) có khả năng đạt mức lợi nhuận trên vốn chủ sở hữu (ROE) cao hơn và ổn định hơn. Nhóm có lợi nhuận cao thì quy mô vừa lớn vừa ít phân tán, phản ánh cấu trúc hoạt động bài bản, hiệu quả, kiểm soát tốt cả về tài sản và sinh lời.
cr_by_size <- data_acl %>%
group_by(SizeGroup) %>%
summarise(CR_tb = mean(CR, na.rm = TRUE), sd_CR = sd(CR, na.rm = TRUE))
kable(cr_by_size, caption = "Khả năng thanh khoản (CR) theo quy mô doanh nghiệp",
col.names = c("Nhóm quy mô", "CR trung bình", "Độ lệch chuẩn"), align = "lcc") %>%
kable_styling(full_width = FALSE, position = "center", latex_options = "H")
Giải thích:
Dòng 1-2 tạo biến cr_by_size, sử dụng group_by(SizeGroup) để phân nhóm dữ liệu theo quy mô doanh nghiệp.
Dòng 3 dùng summarise() để tổng hợp chỉ số thanh khoản CR (Current Ratio): giá trị trung bình (mean), độ lệch chuẩn (sd)
Dòng 4–5 dùng kable() xuất bảng với tiêu đề, đặt lại tên các cột, căn lề bảng
Nhận xét:
Nhóm doanh nghiệp lớn có CR trung bình cao nhất (1.56), độ lệch chuẩn thấp nhất (0.14): Thanh khoản tốt, ít biến động. Điều này phản ánh doanh nghiệp lớn vừa kiểm soát vốn lưu động hiệu quả, vừa chủ động nguồn tài sản ngắn hạn để trả nợ. Quy mô doanh nghiệp càng lớn, khả năng kiểm soát thanh khoản càng tốt, rủi ro mất khả năng thanh toán giảm rõ rệt.
lnst_by_season <- data_acl %>%
group_by(Season) %>%
summarise(LNST_tb = mean(LNSTCPP, na.rm = TRUE), LNST_min = min(LNSTCPP, na.rm = TRUE),
LNST_max = max(LNSTCPP, na.rm = TRUE), sd_LNST = sd(LNSTCPP, na.rm = TRUE))
kable(lnst_by_season, caption = "Thống kê lợi nhuận sau thuế (LNST) theo mùa vụ trong năm",
col.names = c("Mùa", "LNST trung bình", "LNST nhỏ nhất", "LNST lớn nhất",
"Độ lệch chuẩn"), align = "lcccc") %>%
kable_styling(full_width = FALSE, position = "center", latex_options = "H")
Giải thích:
Dòng 1-2 tạo biến lnst_by_season từ bảngSử dụng group_by(Season) để phân nhóm dữ liệu theo từng mùa vụ trong năm (Autumn, Spring, Summer, Winter), giúp tổng hợp số liệu theo đặc điểm kinh doanh từng mùa.
Dòng 3 dùng summarise() tính toán các chỉ số thống kê về lợi nhuận sau thuế: trung bình (mean), giá trị thấp nhất (min), cao nhất (max), độ lệch chuẩn (sd).
Dòng 4-7 dùng hàm kable() để xuất bảng với các tên cột và có chú thích, các tham số như caption, col.names giúp trình bày bảng đẹp và dễ hiểu
Nhận xét:
Lợi nhuận sau thuế trung bình theo mùa dao động từ gần 19,388 tỷ (Spring) đến khoảng 20,828 tỷ (Autumn), chênh lệch không lớn giữa các mùa, cho thấy khả năng sinh lời của công ty ACL khá ổn định trong năm, không có mùa vụ nào chiếm ưu thế rõ rệt. Độ lệch chuẩn lớn (trên 11 tỷ) phản ánh lợi nhuận từng quý trong mùa vụ có biến động khá mạnh. Giá trị lợi nhuận thấp nhất là khoảng 5,000 tỷ (ở tất cả các mùa giống nhau, có thể là điểm đặc biệt hoặc dữ liệu phân bổ). Lợi nhuận lớn nhất trong mùa Autumn cao nhất (~43,227 tỷ), các mùa còn lại cũng ở mức trên 40,000 tỷ, thể hiện những quý có hiệu suất cao và quý có thể có biến động đáng kể.
roe_by_year <- data_acl %>%
group_by(Year) %>%
summarise(ROE_tb = mean(ROE, na.rm = TRUE), ROA_tb = mean(ROA, na.rm = TRUE),
DR_tb = mean(DR, na.rm = TRUE))
kable(roe_by_year, caption = "Thống kê ROE, ROA và DR trung bình theo từng năm",
col.names = c("Năm", "ROE trung bình", "ROA trung bình", "Đòn bẩy tài chính trung bình"),
align = "lccc") %>%
kable_styling(full_width = FALSE, position = "center", latex_options = "H")
Giải thích:
Dòng 1-2 khởi tạo biến roe_by_year, sử dụng group_by(Year) để phân nhóm dữ liệu theo từng năm.
Dòng 3 dùng summarise() để tính toán số liệu trung bình theo nhóm năm: mean theo ROE, ROA và DR
Dòng 4–7 sử dụng kable() để xuất bảng với các cột rõ nghĩa, caption chú thích, căn lề logic cho báo cáo.
Nhận xét:
Qua bảng, doanh nghiệp ACL đã chuyển biến mạnh từ cấu trúc vốn vay cao, hiệu quả sinh lời khiêm tốn (giai đoạn đầu) sang giai đoạn hiệu quả sinh lời (ROE, ROA) tăng rõ rệt, tỷ lệ vay nợ giảm dần, tài chính lành mạnh. Đây là minh chứng cho chiến lược quản trị tài chính hợp lý, kiểm soát rủi ro tốt, tận dụng tốt cơ hội thị trường và tăng cường năng lực sinh lời qua từng năm.
halfyear_stat <- data_acl %>%
group_by(HalfYear) %>%
summarise(ROE_tb = mean(ROE, na.rm = TRUE), ROA_tb = mean(ROA, na.rm = TRUE),
DR_tb = mean(DR, na.rm = TRUE))
kable(halfyear_stat, caption = "So sánh hiệu quả tài chính giữa nửa đầu và nửa cuối năm",
col.names = c("Giai đoạn", "ROE trung bình", "ROA trung bình", "Đòn bẩy tài chính trung bình"),
align = "lccc") %>%
kable_styling(full_width = FALSE, position = "center", latex_options = "H")
Giải thích:
Dòng 1-2 tạo biến halfyear_stat, sử dụng group_by(HalfYear) để phân nhóm dữ liệu theo hai giai đoạn: “Đầu năm” và “Cuối năm”.
Dòng 3 dùng summarise() để tính toán chỉ số tài chính trung bình của từng giai đoạn: mean theo ROE, ROA, DR
Dòng 4-7 dùng hàm kable() xuất kết quả bảng với caption, đặt lại tên các cột, căn lề hợp lý cho báo cáo khoa học.
Nhận xét:
Sự chênh lệch rất nhỏ ở tất cả các chỉ tiêu cho thấy cấu trúc tài chính và hiệu quả sinh lời của ACL khá cân bằng qua các giai đoạn, không có biến động lớn đầu/cuối năm. Đây là dấu hiệu công ty quản trị tài chính ổn định, không gặp rủi ro lớn do tác động mùa vụ hoặc vận hành.
theme_title <- theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"),
axis.title = element_text(size = 12, face = "bold"), axis.text = element_text(size = 10))
Giải thích:
Dòng 1 định dạng tiêu đề biểu đồ căn giữa (hjust = 0.5), in đậm (face = “bold”), và đặt cỡ chữ 14pt để đảm bảo nổi bật khi xuất PDF.
Dòng 2 với axis.title sẽ điều chỉnh nhãn trục X và Y có cỡ chữ 12pt, in đậm, giúp người đọc dễ nhận biết tên chỉ tiêu tài chính (ví dụ “ROE”, “Đòn bẩy”, “Tổng tài sản”, …), còn axis.text sẽ thiết lập cỡ chữ 10pt cho các giá trị trục (tick labels), đảm bảo cân đối giữa độ rõ và thẩm mỹ khi in hoặc trình chiếu.
Nhận xét:
Đoạn mã theme_title được tạo nhằm đồng nhất định dạng toàn bộ 20 biểu đồ trong chương này.Thay vì phải lặp lại theme(…) cho từng biểu đồ, ta chỉ cần thêm + theme_title cuối mỗi biểu đồ, giúp mã ngắn gọn hơn và tạo phong cách trực quan nhất quán.
roe_year <- data_acl %>%
group_by(Year) %>%
summarise(ROE = mean(ROE, na.rm = TRUE))
mean_roe <- mean(data_acl$ROE, na.rm = TRUE)
ggplot(roe_year, aes(Year, ROE)) + geom_line(color = "#1f77b4", linewidth = 1) +
geom_point(size = 2.5, color = "#1f77b4") + geom_text(aes(label = round(ROE,
3)), vjust = -0.7, size = 3.5, fontface = "bold") + geom_smooth(se = FALSE, color = "#2ca02c",
linetype = "dashed", linewidth = 0.7) + geom_hline(yintercept = mean_roe, color = "red",
linetype = "dotted", linewidth = 0.8) + annotate("text", x = max(roe_year$Year) -
0.2, y = mean_roe + 0.002, label = paste0("Trung bình ROE = ", round(mean_roe,
3)), color = "red", fontface = "bold", size = 3.5, hjust = 3, vjust = 1.5) +
labs(title = "ROE Trung Bình Theo Năm", x = "Năm", y = "ROE") + theme_title
Giải thích:
Dòng 1-2 tạo biến roe_year từ dữ liệu gốc, hàm group_by(Year): phân nhóm dữ liệu theo từng năm để chuẩn bị tính toán trung bình.
Dòng 3 tính ROE trung bình từng năm với summarise(ROE = mean(ROE, na.rm = TRUE)). Kết quả là bảng tóm tắt ROE cho từng năm, bỏ qua giá trị thiếu.
Dòng 4 tính ROE trung bình toàn bộ dữ liệu với: mean_roe <- mean(data_acl$ROE, na.rm = TRUE).
Dòng 5–13 khơi tạo biểu đồ với ggplot có trục x là năm, trục y là ROE. Vẽ đường nối ROE trung bình theo năm geom_line và điểm đánh dấu từng năm geom_point. Thêm nhãn giá trị ROE từng năm bằng geom_text. Vẽ đường xu hướng bằng geom_smooth (màu xanh lá). Vẽ đường trung bình ROE toàn bộ (màu đỏ) bằng geom_hline và chú thích bằng annotate(text, …) phía trên đường này để nêu rõ giá trị trung bình.
Nhận xét:
Giai đoạn 2014–2017: ROE duy trì quanh mức thấp (dưới 0.3), thể hiện hiệu suất sinh lời trên vốn chủ sở hữu chưa cao. 2018–2019: ROE tăng vọt, đỉnh điểm năm 2019 đạt giá trị 0.608, phản ánh giai đoạn doanh nghiệp bứt phá mạnh về hiệu quả sử dụng vốn. 2020 trở đi: ROE giảm lại sau giai đoạn tăng sốc nhưng vẫn duy trì quanh ngưỡng 0.34–0.34, ổn định trên mức trung bình toàn bộ là 0.341.
Biểu đồ vừa giúp kiểm tra tổng quát xu hướng hiệu quả sử dụng vốn chủ sở hữu ACL, vừa trực quan chỉ ra các mốc chuyển biến về năng lực tài chính để phục vụ báo cáo hoặc dự báo.
roa_year <- data_acl %>%
group_by(Year) %>%
summarise(ROA = mean(ROA, na.rm = TRUE))
mean_roa <- mean(data_acl$ROA, na.rm = TRUE)
ggplot(roa_year, aes(Year, ROA)) + geom_line(color = "#9467bd", linewidth = 1) +
geom_point(size = 2.5, color = "#9467bd") + geom_text(aes(label = round(ROA,
3)), vjust = -0.7, size = 3.5, fontface = "bold") + geom_smooth(se = FALSE, color = "#8c564b",
linetype = "dashed", linewidth = 0.7) + geom_hline(yintercept = mean_roa, color = "red",
linetype = "dotted", linewidth = 0.8) + annotate("text", x = max(roa_year$Year) -
0.2, y = mean_roa + 0.002, label = paste0("Trung bình ROA = ", round(mean_roa,
3)), color = "red", fontface = "bold", size = 3.5, hjust = 4, vjust = -1.2) +
labs(title = "ROA Trung Bình Theo Năm", x = "Năm", y = "ROA") + theme_title
Giải thích:
Dòng 1-2 tạo biến roa_year từ dữ liệu gốc, hàm group_by(Year): phân nhóm dữ liệu theo từng năm để chuẩn bị tính toán trung bình.
Dòng 3 tính ROE trung bình từng năm với summarise(ROA = mean(ROA, na.rm = TRUE)). Kết quả là bảng tóm tắt ROA cho từng năm, bỏ qua giá trị thiếu.
Dòng 4 tính ROA trung bình toàn bộ dữ liệu với: mean_roa <- mean(data_acl$ROA, na.rm = TRUE).
Dòng 5–13 khơi tạo biểu đồ với ggplot có trục x là năm, trục y là ROE. Vẽ đường nối ROE trung bình theo năm geom_line và điểm đánh dấu từng năm geom_point. Thêm nhãn giá trị ROE từng năm bằng geom_text. Vẽ đường xu hướng bằng geom_smooth (màu xanh lá). Vẽ đường trung bình ROE toàn bộ (màu đỏ) bằng geom_hline và chú thích bằng annotate(text, …) phía trên đường này để nêu rõ giá trị trung bình.
Nhận xét:
ROA giai đoạn 2014–2017: Trung bình thấp, chỉ khoảng 0.067–0.095, cho thấy hiệu quả sinh lời trên tài sản thấp. Năm 2018–2019: ROA tăng mạnh, đỉnh là 0.285 năm 2019. Đây là giai đoạn doanh nghiệp ACL tăng trưởng hiệu quả rõ rệt về sử dụng tài sản. Từ 2020 trở đi: ROA giảm lại, nhưng vẫn duy trì trên mức trung bình toàn bộ giai đoạn (~0.144), không xuất hiện biến động quá lớn. Nhìn chung, ACL giữ ổn định sau giai đoạn tăng trưởng nóng.
Biểu đồ ROA trung bình từng năm trực quan hóa quá trình biến động, tối ưu hóa hiệu quả sử dụng tài sản của ACL trong giai đoạn dài, chỉ ra các năm thành công nổi bật và các thời đoạn cần cải thiện để duy trì hiệu suất sinh lời.
dr_year <- data_acl %>%
group_by(Year) %>%
summarise(DR = mean(DR, na.rm = TRUE))
mean_dr <- mean(data_acl$DR, na.rm = TRUE)
ggplot(dr_year, aes(Year, DR)) + geom_area(fill = "#9edae5", alpha = 0.6) + geom_line(color = "#17becf",
linewidth = 1) + geom_point(size = 2.5) + geom_text(aes(label = round(DR, 2)),
vjust = -0.5, size = 3.5, fontface = "bold") + geom_hline(yintercept = mean_dr,
color = "red", linetype = "dotted", linewidth = 0.8) + annotate("text", x = max(dr_year$Year) -
0.2, y = mean_dr + 0.01, label = paste0("Trung bình DR = ", round(mean_dr, 2)),
color = "red", fontface = "bold", size = 3.5, hjust = 1) + labs(title = "Đòn Bẩy Tài Chính (DR) Theo Năm",
x = "Năm", y = "DR") + theme_title
Giải thích:
Dòng 1-4 khởi tạo bảng tổng hợp trung bình DR từng năm (dr_year) bằng cách nhóm dữ liệu theo biến Year, rồi tính giá trị trung bình DR cho từng năm bằng summarise() (bỏ qua giá trị thiếu với na.rm = TRUE). Tính giá trị DR trung bình toàn bộ giai đoạn với mean_dr.
Dòng 5-11 vẽ biểu đồ DR theo năm bằng ggplot: geom_area tạo vùng nền màu xanh nhạt dưới đường xu hướng DR; geom_line vẽ đường biểu diễn DR từng năm; geom_point đánh dấu các điểm DR ở các năm; geom_text hiển thị nhãn giá trị DR chi tiết tại từng điểm; geom_hline vẽ đường DR trung bình toàn bộ giai đoạn màu đỏ, nét đứt; annotate đặt nhãn chú thích giá trị trung bình DR ở góc phải phía trên. Thiết lập tiêu đề, nhãn trục x/y bằng labs.
Nhận xét:
Giai đoạn 2015–2017: DR tăng dần, đạt đỉnh 0.73 năm 2017. Đây là thời kỳ ACL sử dụng tỷ trọng vốn vay/nợ rất cao so với tổng tài sản. Sau 2017: DR giảm rõ rệt từng năm, đặc biệt giảm mạnh xuống còn 0.53 năm 2019 và thấp nhất là 0.45 năm 2021. Các năm gần đây (2022–2024) DR duy trì thấp (0.52–0.53).
Biểu đồ trực quan hóa chuyển biến tích cực về cơ cấu tài chính ACL: từ tỷ lệ đòn bẩy cao về mức an toàn, ổn định suốt các năm gần đây. Đây là nền tảng tốt cho khả năng phát triển bền vững và hạn chế nguy cơ tài chính về dài hạn.
cr_year <- data_acl %>%
group_by(Year) %>%
summarise(CR = mean(CR, na.rm = TRUE))
mean_cr <- mean(data_acl$CR, na.rm = TRUE)
ggplot(cr_year, aes(Year, CR)) + geom_col(fill = "#aec7e8") + geom_text(aes(label = round(CR,
2)), vjust = -0.3, size = 3.5, fontface = "bold") + geom_hline(yintercept = mean_cr,
color = "red", linetype = "dotted", linewidth = 0.8) + annotate("text", x = max(cr_year$Year) -
0.2, y = mean_cr + 0.03, label = paste0("Trung bình CR = ", round(mean_cr, 2)),
color = "red", fontface = "bold", size = 3.5, hjust = 1, vjust = -1) + geom_smooth(se = FALSE,
color = "#1f77b4", linetype = "dotted") + labs(title = "Khả Năng Thanh Toán Hiện Hành (CR) Theo Năm",
x = "Năm", y = "CR") + theme_title
Giải thích:
Dòng 1–4 tạo bảng trung bình CR từng năm: Nhóm dữ liệu theo Year, tính CR trung bình theo năm bằng summarise(), đồng thời tính giá trị trung bình toàn bộ với mean_cr (bỏ qua NA).
Dòng 5–11 vẽ biểu đồ cột với ggplot: geom_col tạo cột trình bày trung bình CR của từng năm. geom_text chèn nhãn là giá trị CR lên mỗi cột, dễ đọc kết quả. geom_hline vẽ đường trung bình CR toàn chuỗi, giúp dễ so với chuẩn chung. annotate() đặt nhãn “Trung bình CR = …” geom_smooth vẽ đường xu hướng thể hiện chuyển động tăng trưởng của CR qua năm.
Nhận xét:
Giai đoạn 2015–2018: CR của ACL quanh mức 1.02–1.16, thấp hơn mức trung bình toàn chuỗi (1.34). Điều này phản ánh khả năng thanh toán ngắn hạn còn vừa phải, tiềm ẩn rủi ro nếu dòng tiền gặp vấn đề. Giai đoạn 2019–2023: CR cải thiện rõ rệt, đạt 1.41 (2019), tăng mạnh lên 1.8 (2021), và duy trì quanh 1.6 những năm sau. Điều này nghĩa là công ty đã củng cố an toàn vốn lưu động và khả năng trả nợ ngắn hạn.
Biểu đồ trực quan hóa rõ sự chuyển biến tích cực về an toàn tài chính của ACL giai đoạn 2015–2023, từ mức thanh khoản “vừa đủ” sang ổn định và vượt chuẩn ngành những năm gần đây.
qr_year <- data_acl %>%
group_by(Year) %>%
summarise(QR = mean(QR, na.rm = TRUE))
mean_qr <- mean(data_acl$QR, na.rm = TRUE)
ggplot(qr_year, aes(Year, QR)) + geom_col(fill = "#ffbb78") + geom_text(aes(label = round(QR,
2)), vjust = -0.3, size = 3.5, fontface = "bold") + geom_hline(yintercept = mean_qr,
color = "red", linetype = "dotted", linewidth = 0.8) + annotate("text", x = max(qr_year$Year) -
0.2, y = mean_qr + 0.03, label = paste0("Trung bình QR = ", round(mean_qr, 2)),
color = "blue", fontface = "bold", size = 3.5, hjust = 1) + geom_smooth(se = FALSE,
color = "blue", linetype = "dotted") + labs(title = "Khả Năng Thanh Toán Tức Thời (QR) Theo Năm",
x = "Năm", y = "QR") + theme_title
Giải thích:
Dòng 1-3 nhóm dữ liệu theo năm bằng group_by(), tính giá trị trung bình của biến QR qua hàm summarise mỗi năm.
Dòng 4 tính giá trị trung bình QR trên toàn bộ tập dữ liệu (tất cả các năm), dùng để vẽ đường chuẩn so sánh trên biểu đồ.
Dòng 5-11 sử dụng ggplot vẽ biểu đồ kết hợp: geom_area tô màu nền phía dưới đường QR hàng năm. geom_text thêm nhãn giá trị QR (làm tròn 2 chữ số) trên mỗi cột thể hiện QR kiểu cột. geom_hline vẽ đường thẳng ngang biểu diễn giá trị trung bình QR (màu đỏ, nét đứt). annotate thêm nhãn text hiển thị “Trung bình QR = …” gần đường trung bình.
Nhận xét:
Giá trị QR trung bình các năm dao động từ khoảng 0.34 (năm thấp nhất) đến 0.63 (năm cao nhất). Năm 2017 có QR cao nhất (0.63), phản ánh khả năng thanh toán tức thời tốt nhất trong giai đoạn. Những năm 2019, 2020, 2021 có giá trị QR thấp nhất (khoảng 0.38 - 0.39), chỉ dưới mức trung bình 0.48. Xu hướng QR qua các năm biến động, không ổn định, có giai đoạn tăng cao rồi giảm thấp cho thấy sức khỏe thanh khoản tức thời có lúc mạnh, có lúc yếu.
mean_roe_all <- mean(data_acl$ROE, na.rm = TRUE)
ggplot(data_acl, aes(ROE, fill = Season)) + geom_density(alpha = 0.5) + geom_vline(xintercept = mean_roe_all,
color = "red", linetype = "dotted", linewidth = 0.8) + annotate("text", x = mean_roe_all,
y = 0, label = paste0("Mean ROE = ", round(mean_roe_all, 3)), vjust = -1, color = "red",
fontface = "bold") + labs(title = "Phân Phối ROE Theo Mùa", x = "ROE", y = "Mật độ",
fill = "Mùa") + theme_title
Giải thích:
Dòng 1 tính giá trị trung bình ROE toàn bộ tập dữ liệu (mean_roe_all <- mean(data_acl$ROE, na.rm = TRUE)), dùng cho vẽ đường trung bình trên biểu đồ.
Dòng 2-6 sử dụng ggplot vẽ biểu đồ mật độ phân phối ROE, chia theo từng mùa vụ (fill = Season). Hàm geom_density vẽ biểu đồ mật độ, giúp quan sát sự chồng lấn của các mùa. geom_vline vẽ đường thẳng đứng (đỏ đứt nét) biểu thị giá trị trung bình ROE toàn tập. annotate() thêm nhãn chú thích “Mean ROE = 0.341” ngay vị trí đường trung bình, font đỏ, đậm, dễ nhận diện trên biểu đồ. labs() đặt tiêu đề, nhãn trục và chú thích cho biểu đồ.
Nhận xét:
Đa số giá trị ROE của các mùa tập trung quanh mức trung bình ~0.30–0.35. Phần lớn các mùa (Xuân, Hạ, Thu, Đông) có mật độ cao nhất ở vùng ROE dưới 0.35, thể hiện mức sinh lời trên vốn chủ sở hữu ổn định qua các mùa trong năm, không bị ảnh hưởng rõ nét bởi yếu tố mùa vụ. Một số mùa có đuôi phân phối kéo dài tới vùng ROE cao (>0.5), đặc biệt là mùa Xuân và Hạ, cho thấy xuất hiện vài quý/năm đạt mức sinh lời nổi bật so với trung bình chung. Đường trung bình ROE màu đỏ (0.341) giúp dễ dàng nhận diện phần lớn giá trị ROE thực tế của doanh nghiệp ACL đều nằm dưới hoặc sát ngưỡng này, chỉ có vùng ROE >0.5 là hiếm gặp hơn.
mean_lnst <- mean(data_acl$LNSTCPP, na.rm = TRUE)
ggplot(data_acl, aes(Season, LNSTCPP)) + geom_boxplot(fill = "#c5b0d5", outlier.alpha = 0.25) +
geom_jitter(width = 0.12, alpha = 0.3) + stat_summary(fun = mean, geom = "point",
shape = 23, fill = "white", size = 3) + geom_hline(yintercept = mean_lnst, color = "red",
linetype = "dotted") + annotate("text", x = 1, y = mean_lnst, label = paste0("Mean LNST = ",
(scales::label_number(big.mark = "."))(round(mean_lnst, 0))), vjust = -1, hjust = -1,
color = "red", fontface = "bold") + scale_y_continuous(labels = label_number(big.mark = ".")) +
labs(title = "LNST Sau Thuế Theo Mùa", x = "Mùa", y = "LNST (USD)") + theme_title
Giải thích:
Dòng 1 tính giá trị trung bình của lợi nhuận sau thuế chưa phân phối (LNSTCPP) toàn bộ dữ liệu.
Dòng 2–8 vẽ boxplot và trực quan hóa so sánh LNSTCPP giữa các mùa với trục x là mùa vụ, trục y là LNSTCPP. geom_boxplot() vẽ boxplot cho từng mùa, màu fill tím nhạt, chỉ ra sự phân bố, trung vị, phân vị và các giá trị ngoại lai qua các mùa. geom_jitter() thêm các điểm dữ liệu thực tế stat_summary() vẽ điểm thống kê giá trị trung bình bằng ký hiệu kim cương màu trắng. geom_hline() vẽ đường ngang biểu thị giá trị trung bình LNST toàn chuỗi (dotted, màu đỏ). annotate() thêm nhãn “Mean LNST = …” màu đỏ ngay vị trí đường trung bình. scale_y_continuous() định dạng trục y theo chuẩn số lớn (dùng dấu chấm là phân tách nghìn).
Nhận xét:
Trung bình toàn giai đoạn: Mean LNST = 202,689,024,284 (USD), vẽ bằng đường đỏ đứt nét ở giữa biểu đồ để dễ quan sát mức chuẩn. Các mùa vụ đều có mức trung vị, phân vị và giá trị trung bình sát nhau, không mùa nào vượt trội rõ nét. Phân bố LNST khá rộng, có các điểm ngoại lai ở cả phía trên và dưới boxplot, thể hiện một số quý có lợi nhuận đặc biệt cao/thấp. Phần lớn giá trị LNST nằm quanh mức trung bình toàn chuỗi, cho thấy doanh nghiệp ACL duy trì hiệu quả sinh lời đều trong năm, không bị ảnh hưởng nhiều bởi yếu tố mùa vụ.
mean_roe2 <- mean(data_acl$ROE, na.rm = TRUE)
ggplot(data_acl, aes(DebtLevel, ROE, fill = DebtLevel)) + geom_boxplot(alpha = 0.75) +
geom_jitter(width = 0.12, alpha = 0.25) + stat_summary(fun = mean, geom = "point",
shape = 21, fill = "white", size = 3) + geom_hline(yintercept = mean_roe2, color = "red",
linetype = "dotted") + annotate("text", x = 1, y = mean_roe2, label = paste0("Mean ROE = ",
round(mean_roe2, 3)), vjust = -1.5, color = "red", fontface = "bold") + labs(title = "ROE Theo Mức Đòn Bẩy Tài Chính",
x = "Đòn bẩy tài chính", y = "ROE") + theme_title
Giải thích:
Dòng 1 tính ROE trung bình toàn bộ dữ liệu (mean_roe2) để vẽ đường chuẩn trên biểu đồ.
Dòng 2–7 sử dụng ggplot để vẽ boxplot ROE phân nhóm theo mức đòn bẩy tài chính: geom_boxplot vẽ boxplot cho từng nhóm đòn bẩy, màu nền theo nhóm. geom_jitter() rải nhẹ các điểm dữ liệu thực tế ROE của từng quan sát để xem phân bố cụ thể. stat_summary() hiển thị giá trị trung bình (mean) từng nhóm bằng chấm trắng lớn trên boxplot. geom_hline() vẽ đường ngang mức ROE trung bình toàn bộ (màu đỏ đứt nét). annotate() thêm nhãn “Mean ROE = …” màu đỏ ngay trên đường trung bình. labs()đặt tiêu đề, nhãn trục x/y.
Nhận xét:
ROE trung bình toàn bộ giai đoạn: 0.341 (đường đỏ đứt nét); đây là mức chuẩn để so sánh 3 nhóm đòn bẩy tài chính. Nhóm đòn bẩy thấp: ROE tập trung quanh giá trị 0.33–0.35 và thấp hơn một chút so với trung bình toàn bộ. Nhóm đòn bẩy trung bình: Có giá trị trung bình cao nhất, nhiều điểm ROE vượt xa trung bình toàn bộ, dao động rộng nhất và xuất hiện các quý có ROE trên 0.6. Nhóm đòn bẩy cao: ROE trung bình thấp nhất, phân bố hẹp, đa phần dưới mức trung bình toàn chuỗi.
Biểu đồ minh họa rõ mối quan hệ giữa mức đòn bẩy tài chính và hiệu quả sinh lời ROE tại ACL, từ đó hỗ trợ cho việc xây dựng chiến lược quản trị vốn vay hợp lý.
stat_hy <- data_acl %>%
group_by(HalfYear) %>%
summarise(ROA_tb = mean(ROA, na.rm = TRUE), sd_ROA = sd(ROA, na.rm = TRUE))
mean_roa2 <- mean(data_acl$ROA, na.rm = TRUE)
ggplot(stat_hy, aes(HalfYear, ROA_tb, fill = HalfYear)) + geom_col(width = 0.6, color = "black",
alpha = 0.9) + geom_errorbar(aes(ymin = ROA_tb - sd_ROA, ymax = ROA_tb + sd_ROA),
width = 0.15) + geom_text(aes(label = round(ROA_tb, 3)), vjust = -0.4, fontface = "bold") +
geom_hline(yintercept = mean_roa2, linetype = "dotted", color = "red") + annotate("text",
x = 1, y = mean_roa2, label = paste0("Mean ROA = ", round(mean_roa2, 3)), vjust = -2,
hjust = -1, color = "red", fontface = "bold") + labs(title = "ROA Theo Nửa Năm",
x = "Giai đoạn", y = "ROA") + theme_title
Giải thích:
Dòng 1–3 tạo bảng tổng hợp chỉ số ROA theo hai nửa năm. Sử dụng hàm group_by() để chia dữ liệu thành hai nhóm (đầu năm/cuối năm). Hàm summarise tính trung bình ROA (ROA_tb) và độ lệch chuẩn (sd_ROA) cho từng nhóm.
Dòng 4 tính giá trị trung bình ROA toàn bộ tập dữ liệu bằng mean(data_acl$ROA, na.rm = TRUE), lưu vào biến mean_roa2.
Dòng 5–11 tạo biểu đồ bằng ggplot với các cột: geom_col vẽ cột màu đại diện cho hai nửa năm, thể hiện ROA trung bình của từng giai đoạn. geom_errorbar vẽ thanh lỗi (error bar) cho biết mức biến động (± độ lệch chuẩn) mỗi cột ROA. geom_text hiện trị số ROA trung bình ngay trên đầu mỗi cột. geom_hline và annotate vẽ thêm đường và nhãn biểu diễn giá trị trung bình ROA toàn chuỗi (0.144).
Nhận xét:
ROA trung bình hai nửa năm gần như bằng nhau: Đầu năm và cuối năm đều ở mức 0.144, sát với trung bình toàn chuỗi (đường đỏ đứt nét). Thanh lỗi (error bar) lớn, cho thấy trong mỗi giai đoạn đầu/cuối năm, ROA từng quý vẫn biến động mạnh quanh giá trị trung bình, thể hiện có quý đạt cao hoặc thấp hơn nhiều so với mức chung.
stat_sz <- data_acl %>%
group_by(SizeGroup) %>%
summarise(CR_tb = mean(CR, na.rm = TRUE), sd_CR = sd(CR, na.rm = TRUE))
mean_cr2 <- mean(data_acl$CR, na.rm = TRUE)
ggplot(stat_sz, aes(SizeGroup, CR_tb, fill = SizeGroup)) + geom_col(width = 0.6,
color = "black", alpha = 0.9) + geom_errorbar(aes(ymin = CR_tb - sd_CR, ymax = CR_tb +
sd_CR), width = 0.15) + geom_text(aes(label = round(CR_tb, 2)), vjust = -0.4,
fontface = "bold") + geom_hline(yintercept = mean_cr2, linetype = "dotted", color = "red") +
annotate("text", x = 1, y = mean_cr2, label = paste0("Mean CR = ", round(mean_cr2,
2)), vjust = -1, color = "red", fontface = "bold") + labs(title = "CR Theo Quy Mô Doanh Nghiệp",
x = "Quy mô", y = "CR") + theme_title
Giải thích:
Dòng 1–3 tạo bảng tổng hợp với các nhóm quy mô doanh nghiệp (SizeGroup) từ dữ liệu gốc. Tính toán trung bình chỉ số thanh toán hiện hành (CR) cho từng nhóm và độ lệch chuẩn của CR trong nhóm bằng hàm summarise()
Dòng 4 tính giá trị trung bình CR toàn bộ dữ liệu (mean_cr2).
Dòng 5–11 vẽ biểu đồ cột cho từng nhóm quy mô: geom_col vẽ cột màu đại diện cho từng nhóm quy mô doanh nghiệp, CR trung bình làm chiều cao cột. geom_errorbar vẽ thanh lỗi trên mỗi cột để thể hiện độ lệch chuẩn của CR. geom_text chèn nhãn số CR trung bình trên từng cột, dễ so sánh. geom_hline vẽ đường ngang đỏ, nét đứt tại giá trị trung bình CR. annotate gắn nhãn nhận diện rõ giá trị trung bình này.
Nhận xét:
Nhóm doanh nghiệp nhỏ: CR trung bình thấp nhất (1.06), dù độ lệch chuẩn thấp, phản ánh khả năng thanh toán ngắn hạn chỉ “đủ dùng”, tiềm ẩn rủi ro nếu gặp biến động dòng tiền. Nhóm doanh nghiệp vừa: CR trung bình cao hơn (1.27), có độ lệch chuẩn nhất định, cho thấy quản lý vốn lưu động cải thiện rõ hơn nhóm nhỏ. Nhóm doanh nghiệp lớn: CR trung bình cao nhất (1.56), độ lệch chuẩn vừa phải, tức đa số doanh nghiệp lớn duy trì được khả năng thanh toán tốt, an toàn tài chính cao.
vars8 <- c("SIZE", "DR", "TyLeVon_TS", "AS", "ROA", "ROE", "CR", "QR")
cm <- cor(data_acl[, vars8], use = "complete.obs")
cm_df <- as.data.frame(as.table(cm))
ggplot(cm_df, aes(Var1, Var2, fill = Freq)) + geom_tile(color = "white") + geom_text(aes(label = round(Freq,
2)), fontface = "bold") + scale_fill_gradient2(low = "#e23242", mid = "white",
high = "#17becf", midpoint = 0) + labs(title = "Ma Trận Tương Quan Giữa Các Biến Tài Chính",
x = NULL, y = NULL, fill = "r") + theme_title + theme(axis.text.x = element_text(angle = 45,
hjust = 1))
Giải thích:
Dòng 1 khởi tạo biến vars8 chứa 8 tên biến tài chính muốn phân tích tương quan: SIZE, DR, TyLeVon_TS, AS, ROA, ROE, CR, QR. Giúp lấy đúng các cột dữ liệu cần cho ma trận.
Dòng 2 sử dụng để tính toán ma trận tương quan Pearson giữa các cặp biến trong vars8 đã chọn. Tham số use = “complete.obs” giúp loại bỏ các dòng dữ liệu NA.
Dòng 3 biến ma trận tương quan thành bảng dữ liệu cho ggplot (cm_df), để dễ vẽ hình vuông heatmap.
Dòng 4–8 sử dụng ggplot để vẽ heatmap ma trận tương quan: geom_tile() vẽ từng ô tương quan với viền trắng cho dễ nhìn. geom_text() in giá trị tương quan (2 số thập phân) đậm ngay từng ô. scale_fill_gradient() phối màu từ đỏ (tương quan âm mạnh), qua trắng (không tương quan), tới xanh (tương quan dương mạnh).
Nhận xét:
Có nhiều cặp biến tài chính của ACL giai đoạn 2014–2024 liên hệ chặt chẽ với nhau (vừa logic thực tế ngành, vừa cảnh báo phải kiểm tra đa cộng tuyến nếu dùng cho hồi quy mô hình). Những cặp tương quan âm cao cảnh báo cấu trúc vốn/xây dựng mô hình tài chính phải cân đối, tránh dùng cả 2 biến đảo chiều cùng lúc. Các cặp dương cao thì minh chứng hiệu quả vận hành giữa quy mô, tài sản, tỷ lệ sinh lời…
mean_roe3 <- mean(data_acl$ROE, na.rm = TRUE)
mean_dr2 <- mean(data_acl$DR, na.rm = TRUE)
ggplot(data_acl, aes(DR, ROE, color = Season)) + geom_point(alpha = 0.6) + geom_smooth(se = FALSE) +
geom_hline(yintercept = mean_roe3, linetype = "dotted", color = "red") + geom_vline(xintercept = mean_dr2,
linetype = "dotted", color = "red") + annotate("text", x = mean_dr2, y = max(data_acl$ROE,
na.rm = TRUE), label = paste0("Mean DR = ", round(mean_dr2, 2)), vjust = 1.2,
color = "red", fontface = "bold") + annotate("text", x = max(data_acl$DR, na.rm = TRUE),
y = mean_roe3, label = paste0("Mean ROE = ", round(mean_roe3, 3)), hjust = 1.1,
color = "red", fontface = "bold") + labs(title = "Tương Quan ROE - DR Theo Mùa",
x = "DR", y = "ROE", color = "Mùa") + theme_title
Giải thích:
Dòng 1-2 tính ROE trung bình toàn bộ dữ liệu (mean_roe3) và DR trung bình (mean_dr2), dùng để vẽ đường chuẩn trên biểu đồ.
Dòng 3 vẽ biểu đồ với ggplot: trục x là DR, trục y là ROE, phân nhóm màu theo Season (mùa vụ). geom_point vẽ các điểm dữ liệu ROE–DR thực tế của từng quý, từng mùa vụ, màu khác nhau cho từng mùa. geom_smooth() vẽ đường xu hướng ROE–DR cho từng mùa, không lấy khoảng tin cậy.
Dòng 4–10: geom_hline() vẽ đường ngang tại ROE trung bình toàn mẫu. geom_vline() vẽ đường dọc tại DR trung bình toàn mẫu. annotate chèn nhãn ‘Mean DR’ và ‘Mean ROE’ màu đỏ, in đậm tại vị trí giá trị trung bình
Nhận xét:
Hai đường đỏ nét đứt chia biểu đồ thành 4 vùng: trên/dưới trung bình ROE, trái/phải trung bình DR. Các điểm phân bố tương đối đều và phần lớn tập trung quanh mức trung bình (ROE ~0.34, DR ~0.59).
Mùa Hạ (xanh cyan) ROE đỉnh cao nhất khi DR ~0.54, sau đó giảm nhanh khi DR tăng >0.6. Mùa Xuân và Thu có ROE tối ưu hơn khi DR nằm quanh ~0.53–0.57. Khi DR vượt trung bình (0.59), hầu hết các mùa đều chứng kiến ROE giảm rõ rệt, xác nhận ảnh hưởng tiêu cực của đòn bẩy cao tới hiệu suất sinh lời vốn chủ.
mean_roa3 <- mean(data_acl$ROA, na.rm = TRUE)
ggplot(data_acl, aes(SIZE, ROA, color = Year)) + geom_point(alpha = 0.6) + geom_smooth(se = FALSE,
color = "black") + geom_hline(yintercept = mean_roa3, linetype = "dotted", color = "red") +
annotate("text", x = min(data_acl$SIZE, na.rm = TRUE), y = mean_roa3, label = paste0("Mean ROA = ",
round(mean_roa3, 3)), vjust = -1, color = "red", fontface = "bold") + labs(title = "Mối Quan Hệ ROA và Quy Mô Tài Sản",
x = "SIZE (log Tổng tài sản)", y = "ROA") + theme_title
Giải thích:
Dòng 1 tính giá trị trung bình ROA toàn bộ mẫu (mean_roa3) dùng cho vẽ đường tham chiếu trên biểu đồ
Dòng 2–6 Sử dụng ggplot để vẽ biểu đồ mối quan hệ giữa ROA và SIZE (log tổng tài sản) có trục x là SIZE, trục y là ROA, điểm màu theo từng năm để xem biến động theo thời gian. geom_point() vẽ các điểm thể hiện từng quan sát ROA-SIZE. geom_smooth() vẽ đường xu hướng spline đen cho toàn bộ các điểm, không có khoảng tin cậy (se=FALSE). geom_hline() vẽ đường ngang đỏ (nét đứt) tại ROA trung bình (0.144). annotate() thêm nhãn “Mean ROA = 0.144” bên cạnh đường trung bình.
Nhận xét:
Đường đen thể hiện rõ tính phi tuyến: Khi SIZE nhỏ (~27.4–27.6), ROA trung bình thấp (~0.07–0.09). SIZE tăng lên mức ~27.8 trở đi, ROA tăng mạnh và đạt đỉnh (>0.2, có điểm lên trên 0.3), phản ánh quy mô lớn hơn giúp doanh nghiệp tận dụng tài sản hiệu quả hơn. Tuy nhiên, khi SIZE >28, đường xu hướng ROA lại giảm xuống ~0.14–0.16 dù quy mô vẫn lớn, cho thấy doanh nghiệp quá lớn có thể gặp hiệu suất suy giảm.
Dải màu điểm từ nhạt tới đậm: Điểm màu xanh nhạt (2014–2016) tập trung vùng SIZE nhỏ–ROA thấp, trong khi các năm gần đây (xanh đậm hơn, 2022–2024) xuất hiện ở phía bên phải (SIZE lớn hơn), nhiều ROA cao, chứng tỏ quá trình phát triển, gia tăng hiệu quả qua thời gian. Đường trung bình ROA (đỏ) giúp dễ nhận biết những SIZE nào đang mang lại ROA trên hoặc dưới mức trung bình toàn chuỗi; phần lớn các điểm SIZE >27.8 cho ROA cao hơn chuẩn này.
cap_long <- data_acl %>%
group_by(Year) %>%
summarise(VonCSH = sum(VonCSH, na.rm = TRUE), NoPhaiTra = sum(NoPhaiTra, na.rm = TRUE)) %>%
pivot_longer(-Year, names_to = "Khoan", values_to = "GiaTri")
ggplot(cap_long, aes(Year, GiaTri, fill = Khoan)) + geom_area(alpha = 0.7) + geom_line(position = "stack",
linewidth = 0.4, color = "black") + geom_point(position = position_stack(vjust = 0.5),
size = 1) + geom_vline(xintercept = 2020, linetype = "dashed", color = "grey40") +
annotate("text", x = 2020, y = Inf, label = "2020", vjust = 2) + scale_y_continuous(labels = label_number(big.mark = ".")) +
labs(title = "Cơ Cấu Vốn: Vốn CSH vs Nợ Phải Trả", x = "Năm", y = "Giá trị (USD)") +
theme_title
Giải thích:
Dòng 1-3 nhóm dữ liệu theo từng năm bằng group_by() và tính tổng VonCSH cùng Tổng NoPhaiTra cho từng năm bằng hàm summarise. Sử dụng pivot_longer chuyển dữ liệu từ dạng rộng sang dạng dài để biểu diễn từng năm với cả hai khoản vốn trên cùng trục y.
Dòng 4-10 vẽ biểu đồ stacked area bằng ggplot: geom_area() tô màu diện tích cho Vốn CSH (xanh) và Nợ Phải Trả (đỏ) xếp chồng theo từng năm. geom_line(): vẽ đường viền cho từng khu vực vốn. geom_point() đánh dấu các giá trị từng năm trên từng lớp của biểu đồ. geom_vline() vẽ đường dọc tại năm 2020, làm mốc so sánh xu hướng trước/sau năm này.
Nhận xét:
Cả Vốn Chủ Sở Hữu và Nợ Phải Trả đều tăng dần qua các năm, đặc biệt rõ sau 2020. Cấu trúc vốn mở rộng, phản ánh quy mô tài sản của ACL ngày càng lớn. Tỷ trọng Nợ Phải Trả (vùng đỏ) tuy vẫn lớn hơn Vốn Chủ Sở Hữu (xanh) nhưng khoảng cách thu hẹp dần sau năm 2020, cho thấy công ty tăng cường tự chủ tài chính, bớt phụ thuộc vào nguồn vốn vay. Sau mốc 2020, cả hai khoản vốn đều mở rộng nhanh, chứng tỏ doanh nghiệp trải qua giai đoạn phát triển vượt bậc về quy mô tài sản và tài chính.
cnt_debt <- as.data.frame(table(data_acl$DebtLevel))
mean_cnt <- mean(cnt_debt$Freq)
ggplot(data_acl, aes(DebtLevel, fill = DebtLevel)) + geom_bar(color = "black") +
geom_text(stat = "count", aes(label = ..count..), vjust = -0.5, hjust = -0.3,
fontface = "bold") + geom_hline(yintercept = mean_cnt, linetype = "dotted",
color = "red") + annotate("text", x = 1, y = mean_cnt, label = paste0("Mean count = ",
round(mean_cnt, 0)), vjust = -1, color = "red", fontface = "bold") + labs(title = "Phân Bố Nhóm Đòn Bẩy Tài Chính",
x = "Nhóm đòn bẩy", y = "Số quan sát") + theme_title + coord_flip()
Giải thích:
Dòng 1 tạo bảng đếm số lượng quan sát từng nhóm đòn bẩy tài chính (DebtLevel) bằng hàm table(), chuyển thành data frame (cnt_debt).
Dòng 2 tính giá trị trung bình số quan sát mỗi nhóm đòn bẩy (mean_cnt).
Dòng 3-9 vẽ biểu đồ với ggplot: trục x là số quan sát, trục y là nhóm đòn bẩy. geom_bar()vẽ các cột ngang (mỗi cột là 1 nhóm: Thấp, Trung bình, Cao), tô màu theo nhóm DebtLevel. geom_text() hiện số lượng từng nhóm ngay trên mỗi cột. geom_hline() vẽ đường ngang đứt nét tại giá trị trung bình số quan sát. annotate() thêm nhãn “Mean count = …”.
Nhận xét:
Nhóm Trung bình có số quan sát nhiều nhất (22), nhóm Cao có 16, nhóm Thấp chỉ có 6. Đường màu đỏ đứt nét (“Mean count”) chia mốc trung bình là 15. Hầu hết các nhóm đều lệch so với mức này, riêng nhóm “Trung bình” vượt khá xa. Cho thấy trong tập dữ liệu, doanh nghiệp/quý thuộc nhóm đòn bẩy tài chính “Trung bình” chiếm tỷ lệ cao nhất, chiếm ưu thế rõ. Nhóm “Thấp” khá hiếm gặp, đồng nghĩa đa số không quá thận trọng trong sử dụng nợ, còn nhóm “Cao” cũng chiếm tỷ trọng tương đối (chưa đến 1/3).
liq_df <- as.data.frame(table(data_acl$LiquidityLevel)) %>%
mutate(p = Freq/sum(Freq), lab = paste0(round(100 * p, 1), "%"))
ggplot(liq_df, aes(x = 2, y = p, fill = Var1)) + geom_col(color = "white") + coord_polar(theta = "y") +
geom_text(aes(label = lab), position = position_stack(vjust = 0.5), size = 3) +
geom_segment(aes(x = 1.8, xend = 2.2, y = 0, yend = 0), color = "white") + xlim(0.5,
2.5) + labs(title = "Cơ Cấu Mức Thanh Khoản (CR)", fill = "Mức") + theme_void() +
theme(plot.title = element_text(hjust = 0.5, size = 14, face = "bold"))
Giải thích:
Dòng 1 tổng hợp số lượng từng mức thanh khoản (Yếu, Trung bình, Tốt) thành bảng dữ liệu để vẽ biểu đồ.
Dòng 2 tính tỉ lệ phần trăm cho từng mức (p) và tạo nhãn “lab” hiển thị phần trăm trên biểu đồ.
Dòng 3–7 dùng ggplot() tạo biểu đồ tròn kiểu donut: aes() gom các nhóm theo mức thanh khoản (Var1), vẽ với tỉ lệ phần trăm. geom_col() vẽ từng lát màu. coord_polar()chuyển từ biểu đồ cột sang biểu đồ tròn. geom_text() chèn nhãn phần trăm các lát. geom_segment() thêm vạch phân tách giữa các lát màu.
Nhận xét:
Phần lớn (63.6%) các quan sát ở mức thanh khoản trung bình, 34.1% ở mức tốt, rất ít (2.3%) ở mức yếu. Điều này cho thấy đa số doanh nghiệp/quý ACL có khả năng thanh khoản ổn định hoặc tốt, chỉ có một vài điểm yếu tiềm ẩn về dòng tiền ngắn hạn. Cơ cấu an toàn: Doanh nghiệp kiểm soát tốt vốn lưu động, hạn chế rủi ro thanh toán.
prof_year <- data_acl %>%
count(Year, ProfitLevel)
mean_prof_n <- mean(prof_year$n)
ggplot(prof_year, aes(Year, n, fill = ProfitLevel)) + geom_col(position = "stack",
color = "black") + geom_text(aes(label = n), position = position_stack(vjust = 0.5),
size = 2.8, fontface = "bold") + geom_hline(yintercept = mean_prof_n, linetype = "dotted",
color = "black") + annotate("text", x = min(prof_year$Year, na.rm = TRUE), y = mean_prof_n,
label = paste0("Mean = ", round(mean_prof_n, 0)), vjust = -1.5, hjust = -0.5,
color = "black", fontface = "bold") + labs(title = "Phân Bố Mức Sinh Lời (ROE) Theo Năm",
x = "Năm", y = "Số quan sát", fill = "ProfitLevel") + theme_title
Giải thích:
Dòng 1 tạo bảng tổng hợp số lượng từng mức sinh lời (ProfitLevel) của từng năm bằng count(Year, ProfitLevel) trên dữ liệu ban đầu.
Dòng 2 tính số lượng trung bình các quan sát của các nhóm sinh lời mỗi năm (mean_prof_n). Sẽ dùng giá trị này để vẽ đường tham chiếu trên biểu đồ.
Dòng 3–10 vẽ biểu đồ bằng ggplot: geom_col() vẽ cột stacked, thể hiện số lượng từng mức sinh lời trong từng năm, mỗi màu một nhóm. geom_text() giúp hiện nhãn số lượng từng nhóm ngay trên phần màu của mỗi cột. geom_hline() Vẽ đường ngang nét đứt tại vị trí trung bình số quan sát (Mean = 2). annotate() Chèn nhãn “Mean = …” ở vị trí đường trung bình.
Nhận xét:
Số quan sát nhóm Trung bình (màu xanh lá) cao và xuất hiện đều ở nhiều năm; nhóm Cao (xanh dương) nhiều nhất quanh năm 2019–2022; nhóm Thấp (đỏ) giảm rõ từ 2018 trở về sau. Đường trung bình nét đứt (Mean = 2) cho thấy phần lớn các năm có số quan sát từng mức từ 2 đến 4, nhóm “Thấp” dần ít hơn, trong khi nhóm “Trung bình” và “Cao” tăng lên qua các năm.
Doanh nghiệp ACL dần giảm số quý có hiệu suất sinh lời thấp, tăng số quan sát có mức sinh lời trung bình và cao. Điều này phản ánh xu hướng cải thiện hiệu quả tài chính trong giai đoạn mới nhất.
med_season <- data_acl %>%
group_by(Season) %>%
summarise(med = median(ROA, na.rm = TRUE))
mean_roa4 <- mean(data_acl$ROA, na.rm = TRUE)
ggplot(data_acl, aes(ROA, fill = Season)) + geom_density(alpha = 0.45) + geom_vline(data = med_season,
aes(xintercept = med, color = Season), linetype = "dashed") + geom_vline(xintercept = mean_roa4,
color = "red", linetype = "dotted") + annotate("text", x = mean_roa4, y = 0,
label = paste0("Mean ROA = ", round(mean_roa4, 3)), vjust = -1, color = "red",
fontface = "bold") + labs(title = "Mật Độ ROA Theo Mùa", x = "ROA", y = "Mật độ") +
theme_title
Giải thích:
Dòng 1-3 tính trung vị ROA theo từng mùa vụ (med_season) bằng cách nhóm theo Season rồi áp dụng hàm median cho biến ROA.
Dòng 4 tính giá trị trung bình ROA toàn bộ tập dữ liệu để so sánh (biến mean_roa4).
Dòng 5-10 vẽ biểu đồ phân phối mật độ ROA theo mùa vụ bằng ggplot: geom_density() Vẽ đường mật độ ROA của từng mùa. geom_vline() Vẽ các đường dọc đứt nét biểu diễn trung vị ROA của từng mùa lên trục x. geom_vline() Vẽ đường tham chiếu giá trị trung bình ROA toàn kỳ (màu đỏ đứt nét). annotate() Thêm nhãn chữ đỏ tại vị trí trung bình ROA.
Nhận xét:
Hầu hết giá trị ROA của tất cả các mùa đều tập trung quanh mức trung bình 0.144. Đỉnh mật độ đều nằm trong vùng ROA 0.09-0.12. Đường trung vị của mỗi mùa không lệch đáng kể so với trung bình, cho thấy hiệu quả sử dụng tài sản của từng mùa khá ổn định, không có mùa vụ nào vượt trội hoặc yếu kém rõ nét. Độ rộng phân phối các mùa tương đối sát nhau, biên độ ROA chủ yếu dưới 0.2, rất ít quan sát trên 0.3.
lnst_yoy <- data_acl %>%
group_by(Year) %>%
summarise(LNST = sum(LNSTCPP, na.rm = TRUE)) %>%
arrange(Year) %>%
mutate(Delta = LNST - dplyr::lag(LNST), Pos = ifelse(Delta >= 0, "Tăng", "Giảm")) %>%
filter(!is.na(Delta))
ggplot(lnst_yoy, aes(Year, Delta, fill = Pos)) + geom_col(color = "black", width = 0.65) +
geom_hline(yintercept = 0, color = "grey50", linetype = "dashed") + geom_text(aes(label = paste0(round(Delta/1e+09,
1), "B"), vjust = ifelse(Delta >= 0, -0.4, 1.2)), angle = 0, hjust = 0.6, vjust = 0,
size = 3.5, fontface = "bold", color = "black") + scale_fill_manual(values = c(Tăng = "#00BFC4",
Giảm = "#F8766D")) + labs(title = "Biến Động LNSTCPP Theo Năm (YoY)",
x = "Năm", y = "Chênh lệch YoY (tỷ đồng)", fill = "Xu hướng") + theme_title
Giải thích:
Dòng 1–5 nhóm dữ liệu theo năm bằng group_by(), tính tổng LNSTCPP từng năm summarise, sắp xếp thứ tự năm để dễ tính chênh lệch, rồi tính biến động từng năm so với năm trước bằng mutate().. Gán nhãn “Tăng” khi Delta ≥ 0, “Giảm” nếu Delta < 0 bằng biến Pos.
Dòng 6-11 vẽ biểu đồ cột Delta, tô màu theo xu hướng tăng/giảm: geom_col vẽ cột màu xanh khi lợi nhuận tăng, đỏ khi giảm, viền đen. geom_hline vẽ trục chuẩn 0. geom_text Hiện số chênh lệch từng năm trên đỉnh/má cột, định dạng tỉ lệ tỷ đồng. scale_fill_manual Quy định màu .
Nhận xét:
Lợi nhuận sau thuế chưa phân phối của ACL tăng mạnh các năm 2017–2019, đặc biệt năm 2019 tăng vượt bậc (+751 tỷ đồng), sau đó hai năm liên tiếp 2020–2021 giảm sâu nhất là -478 tỷ đồng. Đến 2022, lại phục hồi (tăng +346 tỷ đồng). Đa số các năm còn lại biến động nhẹ, chủ yếu tăng ít.
ts_long <- data_acl %>%
group_by(Year) %>%
summarise(TongTS = sum(TongTS, na.rm = TRUE), VonCSH = sum(VonCSH, na.rm = TRUE),
NoPhaiTra = sum(NoPhaiTra, na.rm = TRUE)) %>%
pivot_longer(-Year, names_to = "Khoan", values_to = "GiaTri")
ggplot(ts_long, aes(Year, GiaTri, color = Khoan)) + geom_line(linewidth = 1) + geom_point(size = 2) +
geom_smooth(se = FALSE, linetype = "dotted") + geom_vline(xintercept = 2020,
linetype = "dashed", color = "grey40") + scale_y_continuous(labels = label_number(big.mark = ",")) +
labs(title = "Động Thái: Tổng Tài Sản, Vốn CSH, Nợ Phải Trả",
x = "Năm", y = "Giá trị (USD)", color = NULL) + theme_title
Giải thích:
Dòng 1–4 nhóm dữ liệu theo từng năm bằng group_by, rồi tính tổng từng khoản tài chính gồm: Tổng tài sản, Vốn Chủ Sở Hữu, Nợ Phải Trả bằng hàm summarise. Sử dụng pivot_longer để chuyển dữ liệu từ dạng rộng sang dạng dài giúp vẽ nhiều đường cho từng khoản trên cùng một biểu đồ.
Dòng 5-10 Vẽ biểu đồ line chart nhiều lớp bằng ggplot: trục x là năm, trục y là giá trị USD. geom_line vẽ các đường động thái của từng khoản tài chính theo năm, phân biệt màu sắc. geom_point chấm các điểm dữ liệu từng năm. geom_smooth vẽ đường xu hướng spline dot cho thấy chuyển động mềm mại theo chuỗi năm. geom_vline vẽ mốc năm 2020 (nét đứt xám) để nhấn mạnh mốc chuyển dịch tài chính quan trọng.
Nhận xét:
Cả ba khoản tài chính đều tăng trưởng mạnh qua các năm. Đặc biệt Tổng tài sản (đường xanh lá) tăng bền bỉ, lên sát 7,000 tỷ USD vào năm mới nhất. Sau năm 2020 (đường nét đứt), cả Vốn chủ sở hữu (xanh nước biển) và Nợ phải trả (đỏ) cùng tăng rõ rệt, riêng Vốn CSH tăng mạnh hơn, phản ánh doanh nghiệp ưu tiên mở rộng tài sản và tăng vốn tự chủ. Khoảng cách Nợ phải trả - Vốn CSH thu hẹp dần: cho thấy cấu trúc tài chính ACL dần tự chủ hơn, bớt phụ thuộc vào vốn vay trong giai đoạn tăng trưởng quy mô.