Mục tiêu
Bộ dữ liệu được sử dụng trong phân tích này phản ánh các đặc điểm nhân khẩu học và tình trạng việc làm của lực lượng lao động Hoa Kỳ năm 2023. Dữ liệu được thu thập từ IPUMS CPS, bao gồm nhiều biến quan trọng như giới tính, trình độ học vấn, ngành nghề, tình trạng hôn nhân và thu nhập.
Mục tiêu của phần này là giới thiệu tổng quan về bộ dữ liệu, đồng thời thực hiện kiểm tra và làm sạch dữ liệu nhằm đảm bảo tính chính xác và độ tin cậy trước khi tiến hành các phân tích chuyên sâu ở những chương tiếp theo.
# 1. Đọc dữ liệu từ file Excel
df_raw <- read_excel("data_2023_hoaKy.xlsx")
# 2. Chuẩn hóa tên cột để thuận tiện thao tác
df <- df_raw %>%
clean_names()
# 3. Xem 6 dòng đầu tiên sau khi làm sạch
library(flextable)
df %>%
head() %>%
flextable() %>%
set_caption("6 dòng dữ liệu đầu tiên sau khi làm sạch tên cột") %>%
theme_box() %>%
autofit()
age | sex | race | marst | empstat | occ | ind | educ | fullpart | incwage |
|---|---|---|---|---|---|---|---|---|---|
66 | Female | White | Separated | Not in labor force | Not in universe | N/A (not applicable) | Some college | Not working | 0 |
68 | Female | White | Separated | Not in labor force | Not in universe | N/A (not applicable) | Some college | Not working | 0 |
52 | Female | White | Married, spouse present | Not in labor force | Not in universe | N/A (not applicable) | Some college | Not working | 0 |
51 | Male | White | Married, spouse present | Employed | Retail salespersons | Auto parts, accessories, and tire stores | Some college | Full-time | 42,000 |
78 | Female | White | Separated | Not in labor force | Not in universe | N/A (not applicable) | Some college | Not working | 0 |
65 | Male | White | Married, spouse present | Not in labor force | Janitors and building cleaners | Colleges and universities, including junior colleges | Some college | Full-time | 55,000 |
Sau khi thu thập, dữ liệu được nhập vào môi trường R thông qua hàm read_excel() thuộc gói readxl. Hàm này cho phép đọc trực tiếp tệp Excel với khả năng chỉ định cụ thể trang tính, phạm vi ô hoặc kiểu dữ liệu của từng cột.
Tiếp theo, hàm clean_names() từ gói janitor được sử dụng để chuẩn hóa tên biến, giúp loại bỏ các ký tự đặc biệt, chuyển toàn bộ sang chữ thường và thay khoảng trắng bằng dấu gạch dưới. Việc này đảm bảo dữ liệu có cấu trúc rõ ràng, đồng nhất, thuận lợi cho thao tác và phân tích về sau.
Bảng hiển thị 6 dòng đầu tiên được tạo bằng hàm flextable() nhằm kiểm tra nhanh kết quả đọc dữ liệu. Qua đó có thể đánh giá sơ bộ về định dạng, phát hiện sớm các lỗi phổ biến như tiêu đề sai hàng, dữ liệu bị ghép cột hoặc kiểu biến không phù hợp (ví dụ: biến số bị nhận dạng thành ký tự).
Việc đọc và chuẩn hóa dữ liệu là bước tiền xử lý quan trọng, giúp đảm bảo tính chính xác và nhất quán của toàn bộ quá trình phân tích ở các chương tiếp theo.
# 4. Xác định biến định tính cần chuyển đổi
factor_vars <- c("sex", "race", "marst", "empst", "occ", "ind", "educ", "fullpart","empstat")
# 5. Giữ lại các biến thực sự tồn tại trong dataset
factor_vars <- factor_vars[factor_vars %in% names(df)]
# 6. Chuyển các biến ký tự sang factor
df <- df %>%
mutate(across(where(is.character), as.factor))
# 7. Kiểm tra lại cấu trúc dữ liệu
glimpse(df)
Rows: 146,133
Columns: 10
$ age <dbl> 66, 68, 52, 51, 78, 65, 68, 74, 74, 76, 75, 63, 64, 41, 1, 52…
$ sex <fct> Female, Female, Female, Male, Female, Male, Female, Female, M…
$ race <fct> White, White, White, White, White, White, White, White, White…
$ marst <fct> "Separated", "Separated", "Married, spouse present", "Married…
$ empstat <fct> Not in labor force, Not in labor force, Not in labor force, E…
$ occ <fct> "Not in universe", "Not in universe", "Not in universe", "Ret…
$ ind <fct> "N/A (not applicable)", "N/A (not applicable)", "N/A (not app…
$ educ <fct> Some college, Some college, Some college, Some college, Some …
$ fullpart <fct> Not working, Not working, Not working, Full-time, Not working…
$ incwage <dbl> 0, 0, 0, 42000, 0, 55000, 52000, 0, 0, 0, 0, 22000, 45000, 0,…
Trong bước này, các biến mang tính chất phân loại được chuẩn hóa về kiểu factor nhằm đảm bảo R hiểu đúng bản chất dữ liệu trong các phép phân tích sau. Trước hết, danh sách các biến định tính thường gặp như giới tính, chủng tộc, tình trạng hôn nhân, ngành nghề hay trình độ học vấn được xác định. Hàm %in% được dùng để lọc chỉ những biến thực sự tồn tại trong bộ dữ liệu, tránh lỗi khi chạy lệnh.
Tiếp đó, hàm mutate(across(where(is.character), as.factor)) tự động chuyển toàn bộ các biến dạng ký tự (character) sang dạng nhân tố (factor). Cách làm này giúp quá trình chuẩn hóa nhanh gọn, đồng nhất và tránh sai sót khi số lượng biến nhiều. Lệnh glimpse() được sử dụng để kiểm tra lại cấu trúc bộ dữ liệu sau khi chuyển đổi, bao gồm số lượng biến, kiểu dữ liệu và một số giá trị mẫu.
Việc chuyển các biến phân loại sang dạng factor có ý nghĩa quan trọng trong phân tích thống kê, vì giúp R xử lý đúng cách khi thực hiện các thao tác như nhóm dữ liệu (group_by), tạo bảng tần suất (table), hoặc xây dựng mô hình hồi quy (tự động tạo biến giả cho các nhóm). Đồng thời, điều này giúp hiển thị biểu đồ phân loại trong ggplot2 chính xác hơn, khi phần mềm có thể tự nhận biết và sắp xếp các mức độ (levels) của từng biến.
# 8. Cấu trúc dữ liệu
str(df)
tibble [146,133 × 10] (S3: tbl_df/tbl/data.frame)
$ age : num [1:146133] 66 68 52 51 78 65 68 74 74 76 ...
$ sex : Factor w/ 2 levels "Female","Male": 1 1 1 2 1 2 1 1 2 2 ...
$ race : Factor w/ 6 levels "American Indian",..: 6 6 6 6 6 6 6 6 6 6 ...
$ marst : Factor w/ 6 levels "Divorced","Married, spouse absent",..: 4 4 3 3 4 3 3 3 3 3 ...
$ empstat : Factor w/ 4 levels "Employed","NIU",..: 3 3 3 1 3 3 3 3 3 3 ...
$ occ : Factor w/ 527 levels "Accountants and auditors",..: 319 319 319 438 319 251 368 319 319 319 ...
$ ind : Factor w/ 264 levels "Accounting, tax preparation, bookkeeping and payroll services",..: 149 149 149 20 149 45 120 149 149 149 ...
$ educ : Factor w/ 5 levels "Associate degree",..: 4 4 4 4 4 4 1 5 5 3 ...
$ fullpart: Factor w/ 3 levels "Full-time","Not working",..: 2 2 2 1 2 1 1 2 3 2 ...
$ incwage : num [1:146133] 0 0 0 42000 0 55000 52000 0 0 0 ...
# 9. Kích thước bộ dữ liệu
cat(paste("Bộ dữ liệu có", nrow(df), "quan sát và", ncol(df), "biến.\n"))
Bộ dữ liệu có 146133 quan sát và 10 biến.
Trong bước này, cấu trúc và kích thước của bộ dữ liệu được kiểm tra để đảm bảo tính chính xác trước khi tiến hành phân tích. Lệnh str(df) cung cấp tổng quan về số biến, số quan sát và kiểu dữ liệu của từng biến, đồng thời hiển thị một số giá trị đầu tiên, giúp phát hiện các cột chưa được chuyển đổi đúng kiểu hoặc có lỗi nhập liệu như số được lưu thành ký tự.
Lệnh nrow() và ncol() xác định số dòng và cột, cung cấp thông tin về quy mô mẫu. Kiến thức về kích thước dataset giúp lựa chọn phương pháp phân tích phù hợp: ví dụ, khi số lượng quan sát lớn, các phương pháp phi tham số hoặc hồi quy tuyến tính có độ tin cậy cao hơn. Việc kiểm tra cấu trúc và kích thước dữ liệu là bước quan trọng trong quy trình tiền xử lý, đảm bảo toàn bộ phân tích sau đó diễn ra chính xác và nhất quán.
names(df)
[1] "age" "sex" "race" "marst" "empstat" "occ"
[7] "ind" "educ" "fullpart" "incwage"
head(df)
Bước này nhằm xác nhận rằng các biến trong bộ dữ liệu đã được đặt tên đúng và dữ liệu mẫu hiển thị như mong đợi. Lệnh names(df) trả về danh sách tất cả các cột, giúp kiểm tra xem quá trình chuẩn hóa tên biến trước đó đã thực hiện chính xác. Lệnh head(df) hiển thị sáu dòng đầu tiên, cho phép quan sát trực quan dữ liệu mẫu và phát hiện các giá trị bất thường dạng văn bản (ví dụ: “Unknown”, “N/A”, “Prefer not to say”) mà hàm read_excel() có thể chưa tự động gán NA.
Việc kiểm tra này đảm bảo rằng các biến và dữ liệu đầu vào đã phù hợp để tiến hành phân tích, đồng thời xác định các giá trị cần xử lý thêm, như chuyển chuỗi đại diện thành NA hoặc gán levels hợp lệ cho biến phân loại, nhằm tránh sai lệch trong các phép thống kê hoặc biểu đồ sau này.
# 10. Kiểm tra NA
na_by_column <- colSums(is.na(df))
columns_with_na <- na_by_column[na_by_column > 0]
if(length(columns_with_na) > 0) {
cat("Các cột có giá trị bị thiếu:\n")
data.frame(
Column = names(columns_with_na),
NA_Count = columns_with_na
) %>%
kable(caption = "Số lượng giá trị NA theo cột") %>%
kable_styling()
} else {
cat("\nBộ dữ liệu không có giá trị bị thiếu nào.")
}
Bộ dữ liệu không có giá trị bị thiếu nào.
Trong bước này, bộ dữ liệu được kiểm tra để xác định các giá trị khuyết thiếu (NA). Lệnh is.na(df) tạo một ma trận logic cùng kích thước với dữ liệu, trong đó các vị trí chứa giá trị NA được đánh dấu là TRUE. Hàm colSums() cộng các giá trị TRUE theo từng cột, từ đó xác định số lượng NA ở mỗi biến. Biến columns_with_na lọc ra các cột có giá trị bị thiếu để tiện quan sát.
Nếu tồn tại cột có NA, code sẽ hiển thị bảng tổng hợp với tên cột và số lượng NA, định dạng bằng kable() và trang trí bằng kable_styling(). Ngược lại, nếu không có NA, thông báo trực tiếp sẽ cho biết dataset hoàn toàn đầy đủ.
Việc kiểm tra dữ liệu khuyết thiếu là bước quan trọng để đánh giá chất lượng dữ liệu. Một dataset không có giá trị NA chứng tỏ dữ liệu đầy đủ, giảm rủi ro sai lệch trong phân tích thống kê và mô hình hóa, đồng thời không cần thực hiện các bước xử lý NA bổ sung như loại bỏ hoặc thay thế giá trị.
# 11. Đếm số dòng trùng hoàn toàn
num_duplicated_rows <- sum(duplicated(df))
cat(paste("Tổng số dòng bị trùng lặp hoàn toàn là:", num_duplicated_rows, "\n"))
Tổng số dòng bị trùng lặp hoàn toàn là: 65820
# 12. Thống kê tần suất xuất hiện của từng dòng
duplicated_summary <- df %>%
group_by(across(everything())) %>%
summarise(Frequency = n(), .groups = "drop") %>%
arrange(desc(Frequency))
duplicated_summary %>%
sample_n(10) %>%
flextable() %>%
set_caption("Bảng: 10 dòng ngẫu nhiên trong bảng tần suất trùng lặp toàn bộ biến") %>%
autofit() %>% # Tự căn độ rộng cột
theme_box() # Thêm khung viền gọn gàng
age | sex | race | marst | empstat | occ | ind | educ | fullpart | incwage | Frequency |
|---|---|---|---|---|---|---|---|---|---|---|
31 | Male | White | Married, spouse present | Employed | Industrial truck and tractor operators | Services incidental to transportation | Some college | Full-time | 50,000 | 1 |
46 | Male | White | Married, spouse present | Employed | Electricians | Construction | Some college | Full-time | 60,000 | 1 |
39 | Male | White | Married, spouse present | Employed | Detectives and criminal investigators | Justice, public order, and safety activities | Some college | Full-time | 100,000 | 1 |
31 | Male | White | Married, spouse present | Employed | Miscellaneous vehicle and mobile equipment mechanics, installers, and repairers | Auto parts, accessories, and tire stores | Some college | Full-time | 45,000 | 1 |
43 | Female | White | Single | Employed | Cooks | Restaurants and other food services | Some high school | Full-time | 60,000 | 1 |
38 | Female | White | Married, spouse present | Employed | Social and community service managers | Civic, social, advocacy organizations, and grantmaking and giving services | Graduate degree | Full-time | 57,000 | 1 |
27 | Male | Black | Single | Employed | Sales and related workers, all other | Truck transportation | Associate degree | Full-time | 200,000 | 1 |
22 | Male | Black | Single | Employed | Not in universe | N/A (not applicable) | Some college | Not working | 0 | 1 |
23 | Male | White | Single | Employed | Personal care aides | Nursing care facilities (skilled nursing facilities) | Some college | Full-time | 24,000 | 1 |
64 | Female | White | Married, spouse present | Employed | Medical and health services managers | General medical and surgical hospitals, and specialty (except psychiatric and substance abuse) hospitals | Graduate degree | Full-time | 60,000 | 1 |
# 13. Tần suất trùng lặp cao nhất và thấp nhất
max_freq <- max(duplicated_summary$Frequency)
min_freq <- min(duplicated_summary$Frequency)
cat(paste("Tần suất trùng lặp cao nhất là:", max_freq, "\n"))
Tần suất trùng lặp cao nhất là: 906
cat(paste("Tần suất trùng lặp thấp nhất là:", min_freq, "\n"))
Tần suất trùng lặp thấp nhất là: 1
Bước này nhằm đánh giá mức độ trùng lặp hoàn toàn giữa các quan sát trong bộ dữ liệu. Lệnh duplicated(df) trả về một vector logic, trong đó các dòng giống hệt một dòng xuất hiện trước đó được đánh dấu TRUE, và sum() đếm tổng số dòng trùng lặp.
Tiếp theo, toàn bộ dữ liệu được nhóm theo tất cả các cột bằng group_by(across(everything())) để xác định tần suất xuất hiện của mỗi tổ hợp đặc điểm duy nhất. Hàm summarise(Frequency = n()) tính số lần xuất hiện của mỗi dòng, và arrange(desc(Frequency)) sắp xếp giảm dần theo tần suất. Để minh họa, 10 dòng ngẫu nhiên được hiển thị bằng flextable() với tiêu đề và định dạng trực quan. Cuối cùng, giá trị tần suất cao nhất và thấp nhất được tính để đánh giá mức độ trùng lặp tổng thể.
Theo kết quả kiểm tra, bộ dữ liệu có một số lượng đáng kể khoảng 65,820 dòng trùng (≈45%) điều này là hợp lý trong bối cảnh dữ liệu điều tra lao động quy mô lớn, khi nhiều cá nhân có cùng đặc điểm nhân khẩu học, trình độ học vấn và nghề nghiệp. Về mặt phân tích, hiện tượng trùng lặp không ảnh hưởng đến các thống kê mô tả như tần suất, tỷ lệ hay trung bình. Với các mô hình hồi quy OLS, các ước lượng hệ số vẫn chính xác, nhưng sai số chuẩn có thể bị đánh giá thấp do giả định quan sát độc lập bị vi phạm. Trong bối cảnh này, việc tiếp tục phân tích với dữ liệu gốc là hợp lý, đồng thời nên cân nhắc sử dụng các phương pháp ước lượng robust khi cần suy luận thống kê.
# 14. Thống kê cơ bản
summary(df)
age sex race
Min. : 0.00 Female:74834 American Indian : 2195
1st Qu.:18.00 Male :71299 Asian : 10757
Median :38.00 Black : 17187
Mean :38.73 Multiracial : 806
3rd Qu.:58.00 Pacific Islander: 3567
Max. :85.00 White :111621
marst empstat
Divorced :11085 Employed :69160
Married, spouse absent : 1642 NIU :29483
Married, spouse present:58636 Not in labor force:45018
Separated : 6564 Unemployed : 2472
Single :66223
Widowed : 1983
occ
Not in universe :74481
Managers, all other : 2510
Elementary and middle school teachers : 1638
Driver/sales workers and truck drivers : 1619
Registered nurses : 1498
First-Line supervisors of retail sales workers: 1324
(Other) :63063
ind
N/A (not applicable) :74481
Construction : 5368
Elementary and secondary schools : 4493
Restaurants and other food services : 4264
General medical and surgical hospitals, and specialty (except psychiatric and substance abuse) hospitals: 3349
Colleges and universities, including junior colleges : 1718
(Other) :52460
educ fullpart incwage
Associate degree :11033 Full-time :59851 Min. : 0
Elementary school:29813 Not working:72947 1st Qu.: 0
Graduate degree :38150 Part-time :13335 Median : 0
Some college :51903 Mean : 31456
Some high school :15234 3rd Qu.: 45000
Max. :1549999
Bước này cung cấp các thống kê tóm tắt cơ bản của bộ dữ liệu. Lệnh summary(df) trả về các thông tin khác nhau tùy theo kiểu biến:
+ Biến số: min, Q1, median, mean, Q3, max, giúp đánh giá phân bố, độ lệch và sự tập trung dữ liệu.
+ Biến phân loại: hiển thị tần số các mức (level) của từng biến, thuận tiện để nhận biết sự cân bằng hoặc chiếm ưu thế của các nhóm.
Kết quả kiểm tra cho thấy:
Tuổi (age): Trung bình khoảng 38.73 tuổi, phạm vi từ 0–85, phân bố khá cân đối.
Giới tính (sex): Tương đối cân bằng, với 74,834 nữ và 71,299 nam.
Chủng tộc (race): Đa số là White (76.4%), tiếp theo là Black (11.8%).
Tình trạng hôn nhân: Đa số là married (40.1%) và single (45.3%).
Tình trạng việc làm: 47.3% employed, 30.8% not in labor force.
Thu nhập (incwage): Trung vị = 0, trung bình = 31,456, giá trị lớn nhất 1,550,000, cho thấy phân phối lệch phải, nhiều người không có thu nhập.
Thông tin thống kê cơ bản này giúp nắm tổng quan về dữ liệu, phát hiện các giá trị bất thường, phân bố lệch hay mức độ cân bằng của các biến phân loại, từ đó làm cơ sở cho các bước phân tích tiếp theo, như hồi quy, phân tích tần suất hay vẽ biểu đồ.
df <- df %>%
# Ép incwage sang numeric an toàn (tránh lỗi factor -> logical)
mutate(incwage = as.numeric(as.character(incwage))) %>%
# Loại bỏ NA và các giá trị âm hoặc quá lớn (>500000)
filter(!is.na(incwage) & incwage >= 0 & incwage < 500000)
Trong quá trình xử lý dữ liệu, biến thu nhập cá nhân (incwage) được kiểm tra và làm sạch nhằm loại bỏ các giá trị bất thường có thể ảnh hưởng đến độ chính xác của phân tích. Cụ thể, biến này được ép kiểu về dạng số (numeric) bằng hàm mutate() trong gói dplyr, đảm bảo các giá trị được nhận diện đúng là số thực thay vì dạng ký tự hoặc nhân tố. Tiếp theo, dữ liệu được lọc bằng hàm filter() để loại bỏ các quan sát có giá trị khuyết (NA), âm, hoặc quá lớn (lớn hơn 500.000 USD). Việc sử dụng toán tử nối %>% giúp kết hợp các bước trên một cách mạch lạc, khi đầu ra của thao tác trước được dùng làm đầu vào cho thao tác sau.
Bước xử lý này giúp loại bỏ những giá trị ngoại lai hoặc sai lệch có thể do lỗi nhập liệu hoặc các trường hợp hiếm không mang tính đại diện. Nhờ đó, tập dữ liệu thu nhập trở nên nhất quán và đáng tin cậy hơn, hạn chế ảnh hưởng tiêu cực đến các chỉ tiêu thống kê mô tả như trung bình, độ lệch chuẩn hay phân vị, đồng thời đảm bảo kết quả phân tích ở các phần tiếp theo phản ánh sát hơn thực trạng thu nhập của lực lượng lao động.
# Chỉ xét người thực sự có thu nhập (>0) và đang làm việc
df_income <- df %>%
filter(incwage > 0 & empstat %in% c("Employed", "Employed - at work", "Employed - absent"))
# Tính Q1, Q3 và IQR
Q1 <- quantile(df_income$incwage, 0.25, na.rm = TRUE)
Q3 <- quantile(df_income$incwage, 0.75, na.rm = TRUE)
IQR_val <- IQR(df_income$incwage, na.rm = TRUE)
# Xử lý outlier: giá trị ngoài ngưỡng IQR -> NA
df <- df %>%
mutate(
incwage = ifelse(
incwage < (Q1 - 1.5 * IQR_val) | incwage > (Q3 + 1.5 * IQR_val),
NA, incwage
)
)
# Kiểm tra kiểu dữ liệu và thống kê trên nhóm có thu nhập thực
str(df$incwage) # num
num [1:145673] 0 0 0 42000 0 55000 52000 0 0 0 ...
summary(df_income$incwage)
Min. 1st Qu. Median Mean 3rd Qu. Max.
2 30000 50000 64418 80000 495000
Sau khi loại bỏ các giá trị thu nhập bất thường, bước tiếp theo tập trung xử lý các quan sát ngoại lai (outlier) nhằm tăng độ tin cậy cho biến thu nhập cá nhân (incwage). Trước hết, dữ liệu được lọc để chỉ giữ lại những cá nhân có thu nhập thực sự lớn hơn 0 và đang thuộc lực lượng lao động, tức là có tình trạng việc làm nằm trong nhóm “Employed”, “Employed – at work” hoặc “Employed – absent”. Việc này giúp đảm bảo quá trình xác định ngoại lai không bị sai lệch bởi những quan sát có thu nhập bằng 0 hoặc không tham gia lao động.
Trên tập dữ liệu đã lọc, các phần vị thứ nhất (Q1) và phần vị thứ ba (Q3) được tính bằng hàm quantile(), từ đó xác định khoảng tứ phân vị (IQR = Q3 − Q1). Theo quy tắc Tukey, những giá trị nằm ngoài khoảng [Q1−1.5×IQR,Q3+1.5×IQR] được xem là ngoại lai. Các quan sát này không bị loại bỏ hoàn toàn mà được thay thế bằng giá trị NA thông qua hàm ifelse() trong mutate(). Cách làm này giúp giữ nguyên cấu trúc dữ liệu gốc, đồng thời đánh dấu rõ những trường hợp không hợp lệ để tránh ảnh hưởng đến các phép tính sau.
Kết quả thống kê mô tả cho nhóm người có thu nhập thực cho thấy Q1 = 30.000, Median = 50.000, Mean = 64.418, Q3 = 80.000 và Max = 495.000. Điều này cho thấy phần lớn người lao động có thu nhập nằm trong khoảng từ 30.000 đến 80.000 USD, phản ánh sự tập trung thu nhập quanh mức trung vị. Việc giá trị trung bình cao hơn trung vị cho thấy phân phối thu nhập lệch phải, nghĩa là có một số cá nhân có thu nhập rất cao kéo giá trị trung bình lên.
Nhờ bước xử lý này, các giá trị cực trị — có thể phát sinh do nhập sai đơn vị hoặc do những trường hợp đặc biệt — được kiểm soát hiệu quả, giúp các chỉ tiêu thống kê mô tả và các ước lượng hồi quy sau này phản ánh chính xác hơn xu hướng chung của dữ liệu. Ngoài ra, việc làm sạch này còn tạo nền tảng cho quá trình phân nhóm thu nhập ở các bước tiếp theo, đảm bảo rằng ranh giới giữa các nhóm được xác định dựa trên dữ liệu đã được chuẩn hóa và hợp lý.
df <- df %>%
mutate(Thu_nhập = case_when(
incwage < 25000 ~ "Thấp",
incwage >= 25000 & incwage < 50000 ~ "Trung bình thấp",
incwage >= 50000 & incwage < 100000 ~ "Trung bình cao",
incwage >= 100000 ~ "Cao",
TRUE ~ "Không xác định"
))
df$Thu_nhập <- factor(df$Thu_nhập,
levels = c("Thấp", "Trung bình thấp", "Trung bình cao", "Cao"))
df %>%
count(Thu_nhập, name = "Số lượng") %>%
knitr::kable(caption ="Tần số các nhóm thu nhập sau khi phân loại")
| Thu_nhập | Số lượng |
|---|---|
| Thấp | 92646 |
| Trung bình thấp | 19105 |
| Trung bình cao | 22165 |
| Cao | 7977 |
| NA | 3780 |
Sau khi xử lý các giá trị bất thường và ngoại lai, biến thu nhập cá nhân (incwage) được phân nhóm nhằm phục vụ cho các phân tích mô tả và mô hình hóa sau này. Cụ thể, biến mới Thu_nhập được tạo ra bằng hàm mutate() kết hợp với case_when() trong gói dplyr, cho phép gán nhãn theo từng khoảng giá trị: dưới 25.000 USD được xếp vào nhóm “Thấp”, từ 25.000–50.000 USD là “Trung bình thấp”, từ 50.000–100.000 USD là “Trung bình cao”, và từ 100.000 USD trở lên là “Cao”. Các trường hợp không xác định hoặc bị loại ở bước trước được giữ nguyên dưới dạng giá trị khuyết (NA). Đồng thời, biến phân loại này được chuyển sang dạng factor với thứ tự hiển thị hợp lý giữa các nhóm thu nhập.
Kết quả thống kê cho thấy phần lớn người lao động thuộc nhóm thu nhập thấp với 92.646 quan sát, trong khi nhóm trung bình cao và cao chiếm tỷ trọng nhỏ hơn đáng kể. Điều này phản ánh sự phân bố thu nhập không đồng đều trong mẫu khảo sát, với xu hướng tập trung chủ yếu ở mức thấp và trung bình. Việc phân nhóm biến thu nhập không chỉ giúp thuận tiện cho việc mô tả và so sánh giữa các nhóm, mà còn tạo nền tảng cho các phân tích hồi quy hoặc kiểm định trong các chương sau.
df <- df %>%
mutate(empstat = str_squish(as.character(empstat)),
Tình_trạng_việc_làm = case_when(
empstat %in% c("Employed") ~ "Có việc làm",
empstat %in% c("Unemployed") ~ "Thất nghiệp",
empstat %in% c("Not in labor force", "NIU") ~ "Ngoài lực lượng lao động",
TRUE ~ "Không xác định"
))
df$Tình_trạng_việc_làm <- factor(df$Tình_trạng_việc_làm,
levels = c("Có việc làm", "Thất nghiệp",
"Ngoài lực lượng lao động", "Không xác định"))
df %>%
count(Tình_trạng_việc_làm, name = "Số lượng") %>%
knitr::kable(caption ="Tần số theo tình trạng việc làm")
| Tình_trạng_việc_làm | Số lượng |
|---|---|
| Có việc làm | 68710 |
| Thất nghiệp | 2470 |
| Ngoài lực lượng lao động | 74493 |
Biến tình trạng việc làm (empstat) được chuẩn hóa nhằm đảm bảo tính nhất quán và thuận tiện cho việc phân tích sau này. Trước hết, các giá trị của biến được chuyển từ dạng factor sang character và xử lý bằng hàm str_squish() để loại bỏ khoảng trắng thừa, tránh sai lệch khi so sánh chuỗi. Sau đó, hàm case_when() được sử dụng để ánh xạ các giá trị gốc thành bốn nhóm có ý nghĩa rõ ràng hơn: “Có việc làm” (Employed), “Thất nghiệp” (Unemployed), “Ngoài lực lượng lao động” (Not in labor force, NIU) và “Không xác định” cho các trường hợp khác. Biến mới này được chuyển sang dạng factor với thứ tự hiển thị xác định sẵn, phục vụ trực quan hóa và phân tích thống kê.
Kết quả cho thấy nhóm “Ngoài lực lượng lao động” chiếm tỷ trọng cao nhất (74.493 người), phản ánh đúng cấu trúc dân số bao gồm người nghỉ hưu, học sinh, sinh viên hoặc nội trợ. Nhóm “Có việc làm” có 68.710 quan sát, trong khi “Thất nghiệp” chiếm 2.470, phù hợp với tỷ lệ thất nghiệp thực tế trong năm 2023. Việc gộp nhóm NIU vào “Ngoài lực lượng lao động” là hợp lý về mặt phương pháp, vì những đối tượng này không thuộc phạm vi khảo sát về việc làm và thu nhập. Biến đã chuẩn hóa giúp việc so sánh và phân tích thu nhập giữa các nhóm trở nên chính xác hơn, đồng thời đóng vai trò quan trọng trong các mô hình mô tả và dự báo thị trường lao động ở các chương tiếp theo.
df <- df %>%
mutate(Trình_độ_học_vấn = case_when(
educ %in% c( "Elementary school") ~ "Thấp",
educ %in% c("Some high school", "Some college") ~ "Trung bình",
educ %in% c("Associate degree", "Graduate degree") ~ "Cao",
TRUE ~ "Không xác định"
))
df$Trình_độ_học_vấn <- factor(df$Trình_độ_học_vấn, levels = c("Thấp", "Trung bình", "Cao"))
df %>%
count(Trình_độ_học_vấn, name = "Số lượng") %>%
knitr::kable(caption ="Tần số trình độ học vấn sau khi nhóm")
| Trình_độ_học_vấn | Số lượng |
|---|---|
| Thấp | 29811 |
| Trung bình | 67039 |
| Cao | 48823 |
Để phục vụ cho việc phân tích, các giá trị chi tiết của biến educ được chuẩn hóa và nhóm lại thành ba cấp độ chính, nhằm phản ánh mức độ học vấn của người lao động theo hướng đơn giản nhưng vẫn đảm bảo tính phân biệt. Cụ thể, những cá nhân có trình độ “Elementary school” được xếp vào nhóm “Thấp”, các trường hợp “Some high school” và “Some college” được gộp vào nhóm “Trung bình”, trong khi “Associate degree” và “Graduate degree” được xếp vào nhóm “Cao”. Các giá trị không thuộc các nhóm trên được mã hóa là “Không xác định”.
Sau khi phân loại, biến mới “Trình_độ_học_vấn” được chuyển thành factor với thứ tự mức độ từ thấp đến cao (“Thấp” → “Trung bình” → “Cao”), nhằm đảm bảo tính logic khi hiển thị trong biểu đồ cũng như khi sử dụng trong các phân tích thống kê có tính thứ tự. Kết quả thống kê tần suất cho thấy phân bố học vấn trong mẫu dữ liệu khá hợp lý: nhóm “Trung bình” chiếm tỷ trọng cao nhất với khoảng 46%, phản ánh thực tế phần lớn lao động có trình độ phổ thông hoặc một phần đại học; nhóm “Cao” chiếm khoảng 33%, cho thấy tỷ lệ đáng kể người lao động có bằng cao đẳng hoặc đại học trở lên; trong khi nhóm “Thấp” chỉ chiếm khoảng 20%.
Việc chuẩn hóa và tái nhóm này không chỉ giúp đơn giản hóa cấu trúc dữ liệu, mà còn tạo điều kiện thuận lợi cho các phân tích tiếp theo như so sánh thu nhập theo trình độ học vấn, đánh giá tỷ lệ việc làm giữa các nhóm, hoặc xây dựng mô hình dự báo với biến phân loại có trật tự rõ ràng. Đồng thời, phân bố thu được cũng phản ánh tương đối sát thực trạng cơ cấu học vấn của lực lượng lao động tại Hoa Kỳ, nơi trình độ trung học và đại học chiếm ưu thế.
df <- df %>%
mutate(
Tình_trạng_hôn_nhân = case_when(
marst %in% c("Married, spouse present", "Married, spouse absent", "Separated") ~ "Đã kết hôn",
marst %in% c("Single", "Widowed", "Divorced") ~ "Độc thân",
TRUE ~ "Khác"
)
)
df$Tình_trạng_hôn_nhân <- factor(df$Tình_trạng_hôn_nhân, levels = c("Đã kết hôn", "Độc thân", "Khác"))
df %>%
count(Tình_trạng_hôn_nhân, name = "Số lượng") %>%
knitr::kable(caption = "Tần số trạng thái hôn nhân (đã nhóm)")
| Tình_trạng_hôn_nhân | Số lượng |
|---|---|
| Đã kết hôn | 66475 |
| Độc thân | 79198 |
Biến marst được chuẩn hóa để nhóm các tình trạng hôn nhân chi tiết thành ba nhóm chính, phục vụ cho việc phân tích. Cụ thể, những cá nhân có tình trạng “Married, spouse present”, “Married, spouse absent” hoặc “Separated” được xếp vào nhóm “Đã kết hôn”, trong khi “Single”, “Widowed” và “Divorced” được gộp vào nhóm “Độc thân”. Các trường hợp khác được phân loại là “Khác”. Biến mới này được chuyển sang dạng factor với thứ tự hợp lý để đảm bảo tính nhất quán khi trực quan hóa và phân tích.
Kết quả thống kê cho thấy nhóm “Độc thân” chiếm tỷ lệ cao hơn với khoảng 54% số quan sát, trong khi nhóm “Đã kết hôn” chiếm 46%, phản ánh xu hướng xã hội hiện đại tại Hoa Kỳ, nơi tỷ lệ sống độc thân tăng và kết hôn muộn hoặc không kết hôn ngày càng phổ biến. Việc chuẩn hóa này giúp phân tích mối quan hệ giữa tình trạng hôn nhân với thu nhập, việc làm và các yếu tố nhân khẩu học trở nên chính xác hơn, đồng thời cung cấp cơ sở dữ liệu hợp lý cho các mô hình kinh tế lượng ở các chương tiếp theo.
df <- df %>%
mutate(
Chủng_tộc = case_when(
race %in% c("White") ~ "Da trắng",
race %in% c("Black") ~ "Da đen",
race %in% c("Asian") ~ "Châu Á",
race %in% c("Pacific Islander") ~ "Người đảo Thái Bình Dương",
race %in% c("American Indian") ~ "Người bản địa Mỹ",
race %in% c("Multiracial") ~ "Đa chủng tộc",
TRUE ~ "Khác"
)
)
df$Chủng_tộc <- factor(df$Chủng_tộc, levels = c("Da trắng", "Da đen", "Châu Á", "Người đảo Thái Bình Dương","Người bản địa Mỹ", "Đa chủng tộc", "Khác"))
df %>%
count(Chủng_tộc, name = "Số lượng") %>%
knitr::kable(caption = "Tần số chủng tộc (đã nhóm)")
| Chủng_tộc | Số lượng |
|---|---|
| Da trắng | 111253 |
| Da đen | 17160 |
| Châu Á | 10702 |
| Người đảo Thái Bình Dương | 3560 |
| Người bản địa Mỹ | 2193 |
| Đa chủng tộc | 805 |
Biến race được chuẩn hóa và nhóm thành các nhóm chủng tộc chính nhằm phục vụ phân tích nhân khẩu học và các mô hình kinh tế lượng. Cụ thể, các giá trị được ánh xạ thành: “Da trắng”, “Da đen”, “Châu Á”, “Người đảo Thái Bình Dương”, “Người bản địa Mỹ”, “Đa chủng tộc”; các giá trị khác hoặc ngoại lệ được gán là “Khác”. Biến mới được chuyển sang dạng factor với thứ tự levels hợp lý, ưu tiên nhóm đông nhất lên đầu, giúp kiểm soát thứ tự hiển thị trong bảng biểu và biểu đồ, đồng thời thuận tiện cho các phân tích thống kê.
Kết quả cho thấy Da trắng chiếm đa số tuyệt đối với khoảng 76% số quan sát, tiếp theo là Da đen 12% và Châu Á 7%, trong khi các nhóm còn lại chiếm tỷ lệ nhỏ, phản ánh đúng cơ cấu chủng tộc đa dạng của Hoa Kỳ. Việc chuẩn hóa này không chỉ làm dữ liệu gọn gàng, dễ đọc và trực quan, mà còn là cơ sở quan trọng cho các phân tích về bất bình đẳng thu nhập theo chủng tộc, sự khác biệt về cơ hội việc làm, và nghiên cứu các vấn đề đa văn hóa, công bằng xã hội trong thị trường lao động.
df <- df %>%
mutate(age = as.numeric(age),
Nhóm_tuổi = case_when(
age < 25 ~ "Dưới 25",
age >= 25 & age < 40 ~ "25–39",
age >= 40 & age < 60 ~ "40–59",
age >= 60 ~ "Từ 60 trở lên",
TRUE ~ "Không xác định"
))
df$Nhóm_tuổi <- factor(df$Nhóm_tuổi,
levels = c("Dưới 25", "25–39", "40–59", "Từ 60 trở lên"))
df %>%
count(Nhóm_tuổi, name = "Số lượng") %>%
knitr::kable(caption ="Tần số các nhóm tuổi")
| Nhóm_tuổi | Số lượng |
|---|---|
| Dưới 25 | 47603 |
| 25–39 | 28110 |
| 40–59 | 36195 |
| Từ 60 trở lên | 33765 |
Biến age được chuẩn hóa sang dạng số và phân nhóm thành bốn cấp độ có ý nghĩa nhân khẩu học: “Dưới 25” (thanh niên, mới gia nhập thị trường lao động), “25–39” (giai đoạn sự nghiệp phát triển), “40–59” (trung niên, ổn định trong công việc), và “Từ 60 trở lên” (gần nghỉ hưu hoặc đã nghỉ hưu). Biến mới Nhóm_tuổi được chuyển sang dạng factor với thứ tự tăng dần, đảm bảo trực quan và thuận tiện cho các phân tích tiếp theo.
Kết quả thống kê cho thấy nhóm “Dưới 25” chiếm khoảng 32,6% số quan sát, nhóm “40–59” chiếm 24,8%, nhóm “25–39” chiếm 19,2% và nhóm “Từ 60 trở lên” chiếm 23,1%, phản ánh cơ cấu tuổi đa dạng trong lực lượng lao động. Việc phân nhóm này tạo cơ sở cho các phân tích so sánh thu nhập, nghề nghiệp, tỷ lệ thất nghiệp và chuyển đổi nghề nghiệp theo tuổi, đồng thời cung cấp thông tin quan trọng cho các chính sách lao động phù hợp với từng nhóm tuổi.
df <- df %>%
mutate(
fulltime_bin = ifelse(fullpart == "Full-time", 1,
ifelse(fullpart == "Part-time", 0, NA))
)
df %>%
count(fullpart, fulltime_bin, name = "Số lượng") %>%
knitr::kable(caption = "Biến nhị phân fulltime_bin (1 = Full-time, 0 = Part-time)")
| fullpart | fulltime_bin | Số lượng |
|---|---|---|
| Full-time | 1 | 59411 |
| Not working | NA | 72947 |
| Part-time | 0 | 13315 |
Biến fullpart được mã hóa thành biến nhị phân fulltime_bin để phân biệt người làm việc full-time (gán 1) và part-time (gán 0). Các trường hợp không thuộc hai nhóm này, chẳng hạn “Not working”, được gán giá trị NA nhằm tránh nhiễu dữ liệu trong các phân tích nhị phân. Biến nhị phân này thuận tiện cho các mô hình hồi quy logistic, so sánh thu nhập, ngành nghề, nhân khẩu học giữa nhóm full-time và part-time, cũng như đánh giá tác động của chính sách lao động đối với tỷ lệ việc làm full-time.
Kết quả cho thấy trong lực lượng lao động, khoảng 44% làm full-time (59.411 người) và 10% làm part-time (13.315 người), trong khi 46% còn lại không làm việc và được loại khỏi phân tích nhị phân. Việc mã hóa này giúp đơn giản hóa phân tích, dễ diễn giải kết quả dưới dạng xác suất hoặc tỉ lệ odds, đồng thời phù hợp cho các bài toán phân loại và dự báo trong nghiên cứu thị trường lao động. Khi sử dụng biến này, cần lưu ý loại bỏ hoặc xử lý các giá trị NA để đảm bảo tính chính xác của kết quả.
df <- df %>%
mutate(
sex_emp_interact = paste(sex, empstat, sep = "_")
)
df %>%
select(sex, empstat, sex_emp_interact) %>%
head(10) %>%
knitr::kable(caption = "Biến tương tác giới tính × tình trạng việc làm")
| sex | empstat | sex_emp_interact |
|---|---|---|
| Female | Not in labor force | Female_Not in labor force |
| Female | Not in labor force | Female_Not in labor force |
| Female | Not in labor force | Female_Not in labor force |
| Male | Employed | Male_Employed |
| Female | Not in labor force | Female_Not in labor force |
| Male | Not in labor force | Male_Not in labor force |
| Female | Not in labor force | Female_Not in labor force |
| Female | Not in labor force | Female_Not in labor force |
| Male | Not in labor force | Male_Not in labor force |
| Male | Not in labor force | Male_Not in labor force |
Để phân tích tác động kết hợp giữa giới tính và tình trạng việc làm, hai biến sex và empstat được kết hợp thành biến tương tác sex_emp_interact bằng cách nối các giá trị với ký tự phân cách “_”. Biến này cho phép nhận dạng các tổ hợp duy nhất như “Female_Employed”, “Male_Unemployed”, và có thể chuyển sang dạng factor khi cần thiết cho trực quan hóa hoặc mô hình hóa.
Biến tương tác này giúp nghiên cứu không chỉ hiệu ứng riêng lẻ của giới tính hay tình trạng việc làm lên thu nhập, mà còn khám phá xem ảnh hưởng của giới tính có khác nhau giữa các nhóm việc làm hay không. Ví dụ, có thể so sánh khoảng cách thu nhập giữa nam và nữ trong nhóm employed so với nhóm unemployed, hoặc đánh giá sự khác biệt về tỷ lệ thất nghiệp theo giới tính. Việc tạo biến tương tác này đóng vai trò quan trọng trong các phân tích về bất bình đẳng giới, chênh lệch thu nhập và cơ hội nghề nghiệp trong lực lượng lao động.
Phân bố tổng quan của các biến trong tập dữ liệu Trước khi đi vào các phân tích chuyên sâu, việc khảo sát phân bố tổng quan của các biến trong tập dữ liệu giúp nắm bắt đặc điểm mẫu và nhận diện các bất thường tiềm ẩn.
# Biểu đồ 1. Phân bố các biến định tính
important_vars <- df %>% select(sex, race, marst, empstat, educ, fullpart)
plot_bar(important_vars, title = "Phân bố các biến định tính chính")
Các biến như giới tính, tình trạng việc làm, chủng tộc và trình độ học vấn được khảo sát bằng hàm count() từ dplyr để tính tần số và trình bày trong bảng, đồng thời minh họa phân bố bằng biểu đồ cột (ggplot2 hoặc hàm plot_bar()). Kết quả cho thấy:
Giới tính: Tỷ lệ nam và nữ tương đối cân bằng, mỗi nhóm chiếm khoảng 70.000 quan sát, phản ánh tính đại diện giới trong mẫu.
Tình trạng việc làm: Nhóm Employed chiếm ưu thế (~70.000 quan sát), tiếp theo là nhóm Not in labor force (~50.000), phù hợp với cấu trúc lực lượng lao động Hoa Kỳ, nơi nhiều cá nhân không tham gia thị trường lao động (học sinh, sinh viên, nội trợ, nghỉ hưu).
Chủng tộc: Người da trắng chiếm đa số (~110.000), trong khi các nhóm thiểu số chiếm tỷ lệ nhỏ hơn đáng kể, phản ánh cơ cấu dân số đa chủng tộc.
Trình độ học vấn: Phân bố khá đa dạng, với nhóm Some college và High school chiếm tỷ lệ cao nhất, thể hiện lực lượng lao động chủ yếu có trình độ từ trung học đến đại học.
# Biểu đồ 2. Phân bố các biến định lượng
plot_histogram(df, title = "Phân bố của các biến định lượng")
Các biến tuổi và thu nhập được khảo sát bằng hàm summary() và hist() để quan sát các chỉ số trung tâm, tán xạ và phân bố. Biểu đồ histogram cho thấy:
Tuổi (age): Phân phối gần hình chuông với đỉnh tập trung ở khoảng 40–50 tuổi, thể hiện cơ cấu tuổi điển hình của lực lượng lao động trưởng thành.
Thu nhập (incwage): Phân phối lệch phải, chủ yếu dưới 50.000, với một số quan sát có thu nhập rất cao (tối đa gần 500.000), phản ánh sự bất bình đẳng thu nhập trong mẫu.
Mục tiêu: Cung cấp cái nhìn toàn cảnh về đặc điểm nhân khẩu học và phân bố thu nhập của toàn bộ mẫu.
Phân bố cơ cấu độ tuổi
# 1. Thống kê mô tả biến tuổi
summary(df$age)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0 18.0 38.0 38.7 58.0 85.0
# 2. Tuổi trung bình
mean(df$age, na.rm=TRUE)
[1] 38.70309
# 3. Độ lệch chuẩn tuổi
sd(df$age, na.rm=TRUE)
[1] 23.33852
# Biểu đồ 3. Biểu đồ histogram của tuổi
hist(df$age, main="Phân bố độ tuổi", xlab="Tuổi", ylab = "Tần suất", col="lightgreen", border="white")
Thống kê mô tả về biến tuổi được thực hiện thông qua các hàm summary(), mean() và sd() để tính toán các chỉ số cơ bản. Kết quả phân tích cho thấy:
Độ tuổi trong mẫu nghiên cứu dao động từ 0 đến 85 tuổi
Tuổi trung bình của mẫu là 38.7 tuổi
Độ lệch chuẩn là 23.3, cho thấy mức độ phân tán đáng kể của dữ liệu
Các giá trị này phản ánh tính đa dạng về mặt nhân khẩu học trong tập dữ liệu, với sự tham gia của nhiều nhóm tuổi khác nhau trong lực lượng lao động, từ trẻ em (0 tuổi) đến người cao tuổi (85 tuổi). Độ lệch chuẩn lớn (23.3) so với giá trị trung bình (38.7) cho thấy dữ liệu tuổi có sự phân tán rộng, không tập trung quanh giá trị trung bình.
Biểu đồ histogram được tạo bằng hàm hist() giúp trực quan hóa phân bố tuổi, cho thấy phân bố gần chuẩn nhưng hơi lệch phải, với đỉnh tập trung chủ yếu trong khoảng 40–50 tuổi.
Số lượng quan sát giảm dần ở hai đầu tuổi: nhóm thanh niên dưới 25 tuổi ít hơn do nhiều người đang học tập hoặc mới gia nhập thị trường lao động, trong khi nhóm trên 60 tuổi giảm nhanh, phù hợp với độ tuổi nghỉ hưu.
Phân tích này cung cấp cơ sở để đánh giá sự khác biệt về thu nhập, tình trạng việc làm theo độ tuổi và các chính sách lao động phù hợp với từng nhóm tuổi.
Phân bố theo giới tính và việc làm
# 4. Bảng chéo giữa giới tính và tình trạng việc làm
table(df$sex, df$empstat)
Employed NIU Not in labor force Unemployed
Female 32910 14293 26400 1087
Male 35800 15190 18610 1383
# 5. Thống kê tần suất theo tình trạng việc làm
bang_tansuat <- df %>%
count(empstat, name = "Tần_suất") %>%
mutate(Tỷ_lệ = round(Tần_suất / sum(Tần_suất) * 100, 2))
kable(
bang_tansuat,
caption = "Thống kê tần suất và tỷ lệ theo tình trạng việc làm (%)"
)
| empstat | Tần_suất | Tỷ_lệ |
|---|---|---|
| Employed | 68710 | 47.17 |
| NIU | 29483 | 20.24 |
| Not in labor force | 45010 | 30.90 |
| Unemployed | 2470 | 1.70 |
Bảng chéo sử dụng hàm table() cho thấy: trong nhóm có việc làm, nam chiếm 35.800 người so với nữ là 32.910; ngược lại, nhóm ngoài lực lượng lao động có tỷ lệ nữ cao hơn (26.400 so với 18.610 nam). Nhóm thất nghiệp có quy mô nhỏ nhất với 1.383 nam và 1.087 nữ.
Phân tích tần suất sử dụng hàm count() kết hợp mutate() cho thấy: nhóm có việc làm chiếm 47,2%, nhóm ngoài lực lượng lao động chiếm 30,9%, tiếp theo là nhóm không xác định (20,2%) và thất nghiệp (1,7%). Kết quả này phản ánh cơ cấu lao động thực tế, trong đó đa số người trong độ tuổi lao động đang làm việc, tỷ lệ thất nghiệp thấp và nhóm ngoài lực lượng lao động chủ yếu là sinh viên, người nghỉ hưu và nội trợ. Sự phân bổ giới tính tương đối cân bằng giữa các nhóm, phù hợp với xu hướng tham gia thị trường lao động theo giới tính.
Xu hướng trung tâm và độ phân tán
# 6. Trung bình thu nhập cá nhân
mean(df$incwage, na.rm=TRUE)
[1] 23616.89
# 7. Độ lệch chuẩn thu nhập
sd(df$incwage, na.rm=TRUE)
[1] 35107.79
# 8. Trung vị thu nhập
median(df$incwage, na.rm=TRUE)
[1] 0
Các thống kê cơ bản về xu hướng trung tâm và độ phân tán được tính bằng các hàm mean(), sd() và median() trong R, loại bỏ các giá trị NA thông qua tham số na.rm = TRUE.
Phân tích các thống kê mô tả về thu nhập cho thấy sự chênh lệch đáng kể trong phân phối. Giá trị trung bình đạt 23.617 USD, nhưng trung vị bằng 0 cho thấy phần lớn mẫu nghiên cứu có thu nhập thấp hoặc không có thu nhập. Độ lệch chuẩn cao (35.108 USD) phản ánh mức độ phân tán rộng của dữ liệu, với sự hiện diện của các cá nhân có thu nhập rất cao làm kéo giá trị trung bình lên.
Đặc điểm này xác nhận phân phối thu nhập lệch phải mạnh - hiện tượng phổ biến trong thị trường lao động Mỹ, nơi tồn tại khoảng cách thu nhập đáng kể giữa các nhóm dân cư. Kết quả phân tích tạo cơ sở vững chắc cho việc phân nhóm thu nhập thành các hạng mục “Thấp”, “Trung bình thấp”, “Trung bình cao” và “Cao” trong các phân tích tiếp theo, đồng thời giải thích sự cần thiết của các phương pháp phân tích phù hợp với dữ liệu không chuẩn.
Hình dạng và đặc điểm phân phối
# Biểu đồ 4. Histogram thu nhập
hist(df$incwage, main="Phân bố thu nhập cá nhân", xlab="Thu nhập", ylab = "Tần suất", col="orange", border="white")
# Biểu đồ 5. Biểu đồ density (mật độ) thu nhập theo giới tính
ggplot(df, aes(x = incwage, fill = sex)) +
geom_density(alpha = 0.5) +
scale_fill_brewer(palette = "Pastel1") +
labs(title = "Mật độ thu nhập theo giới tính",
x = "Thu nhập", y = "Mật độ", fill = "Giới tính") +
theme_minimal() +
coord_cartesian() +
scale_y_continuous(labels = function(x) format(x, scientific = FALSE))
Biểu đồ 4. Histogram thu nhập cá nhân Biểu đồ được tạo bằng hàm hist(dfincwage, main=“Phân bố thu nhập cá nhân”, xlab=“Thu nhập”, ylab=“Tần suất”, col=“orange”, border=“white”), hist(dfincwage, …): Vẽ biểu đồ histogram cho biến incwage (thu nhập cá nhân) và border=“white”: Màu viền thanh để phân tách dễ nhìn.
Histogram cho thấy phân phối thu nhập có dạng lệch phải mạnh, với phần lớn quan sát tập trung dưới 50,000 USD. Điều này phản ánh thực tế rằng đa số người lao động có thu nhập trung bình và thấp, trong khi chỉ một tỷ lệ nhỏ có thu nhập cao (trên 100,000 USD). Phân phối lệch phải là đặc trưng thường gặp trong dữ liệu thu nhập, phù hợp với quy luật Pareto – một số ít cá nhân chiếm phần lớn tổng thu nhập.
Biểu đồ 5. Biểu đồ mật độ thu nhập theo giới tính
Biểu đồ được vẽ bằng hàm ggplot(df, aes(x = incwage, fill = sex)) + geom_density(alpha = 0.5), ggplot(df, aes(…)): Khởi tạo đối tượng đồ họa với dữ liệu df, trong đó x = incwage chỉ định biến thu nhập trên trục hoành, và fill = sex gán màu theo giới tính, geom_density(alpha = 0.5): Vẽ đường mật độ ước lượng phân phối thu nhập, với độ trong suốt 50% (alpha = 0.5) để dễ so sánh hai nhóm.
Biểu đồ thể hiện sự khác biệt về phân phối thu nhập giữa nam và nữ: đường phân phối của nam giới kéo dài về phía phải hơn, cho thấy nam giới có tỷ lệ thu nhập cao hơn, nữ giới tập trung nhiều ở mức thu nhập thấp và trung bình. Sự khác biệt này cung cấp bằng chứng trực quan về khoảng cách thu nhập theo giới tính, đồng thời phản ánh các xu hướng bất bình đẳng thu nhập phổ biến trên thị trường lao động.
Mục tiêu: Đi sâu so sánh sự khác biệt và mối quan hệ của từng yếu tố với thu nhập.
Ảnh hưởng của yếu tố giới tính
# 9. So sánh thu nhập trung bình theo giới tính.
tapply(df$incwage, df$sex, mean, na.rm=TRUE)
Female Male
20202.69 27300.78
# Biểu đồ 6. Biểu đồ tỷ lệ thu nhập cao/thấp theo giới tính và hôn nhân.
df_for_plot <- df %>%
mutate(income_group = ifelse(incwage > median(incwage, na.rm = TRUE), "Cao", "Thấp"))
ggplot(df_for_plot, aes(x = sex, fill = income_group)) + # LAYER 1 & 2
geom_bar(position = "fill") + # LAYER 3
facet_wrap(~ Tình_trạng_hôn_nhân) + # LAYER 4
labs(title = "Tỷ lệ thu nhập (cao/thấp) theo giới tính và hôn nhân",
x = "Giới tính",
y = "Tỷ lệ",
fill = "Nhóm thu nhập") + # LAYER 5 (nhãn)
theme_minimal()
Phân tích thu nhập theo giới tính sử dụng hàm tapply() để tính giá trị trung bình của biến thu nhập (incwage) riêng cho từng nhóm giới tính (sex), đồng thời loại bỏ các giá trị thiếu (na.rm=TRUE). Kết quả cho thấy thu nhập trung bình của nữ là 20,203 USD, thấp hơn đáng kể so với mức 27,301 USD của nam giới, phản ánh sự chênh lệch rõ rệt về thu nhập trên thị trường lao động.
Biểu đồ tỷ lệ thu nhập cao/thấp được xây dựng bằng thư viện ggplot2 theo quy trình:
Tạo biến phân nhóm thu nhập (income_group) dựa trên giá trị trung vị
Sử dụng geom_bar(position = “fill”) để chuẩn hóa biểu đồ dạng tỷ lệ phần trăm
Áp dụng facet_wrap() để tách biệt kết quả theo tình trạng hôn nhân
Kết quả phân tích cho thấy:
Nam giới có tỷ lệ thu nhập cao lớn hơn nữ giới ở cả hai nhóm hôn nhân
Khoảng cách thu nhập thể hiện rõ nét nhất trong nhóm đã kết hôn, cho thấy tác động của “hiệu ứng hôn nhân”
Nữ giới độc thân tập trung chủ yếu ở nhóm thu nhập thấp, trong khi nam giới đã kết hôn chiếm tỷ lệ cao nhất trong nhóm thu nhập cao
Kết luận: Nghiên cứu xác nhận sự tồn tại của bất bình đẳng thu nhập theo giới tính, với mức độ ảnh hưởng khác nhau tùy thuộc vào tình trạng hôn nhân. Phát hiện này cung cấp cơ sở thực tiễn cho việc hoạch định các chính sách thúc đẩy bình đẳng giới và cải thiện an sinh xã hội.
Ảnh hưởng của tình trạng hôn nhân
# 10. So sánh thu nhập trung bình theo tình trạng hôn nhân
tapply(df$incwage, df$marst, mean, na.rm=TRUE)
Divorced Married, spouse absent Married, spouse present
29787.748 27884.507 37122.676
Separated Single Widowed
8153.787 12602.455 26215.861
# Biểu đồ 7. Phân bố thu nhập theo tình trạng hôn nhân
ggplot(df, aes(x = marst, y = incwage, fill = marst)) +
geom_violin(trim = FALSE, alpha = 0.7) + # Layer 1
geom_boxplot(width = 0.1, fill = "white", alpha = 0.6, outlier.shape = NA) + # Layer 2
scale_fill_brewer(palette = "Set2") + # Layer 3
labs(title = "Phân bố thu nhập theo tình trạng hôn nhân", x = "Tình trạng hôn nhân", y = "Thu nhập", fill = "Tình trạng hôn nhân") + # Layer 4
theme_classic() + coord_flip() # Layer 5
Phân tích thu nhập theo tình trạng hôn nhân sử dụng hàm tapply() để tính giá trị trung bình của biến thu nhập (incwage) riêng cho từng nhóm tình trạng hôn nhân (marst), đồng thời loại bỏ các giá trị thiếu (na.rm=TRUE). Kết quả cho thấy nhóm “Married, spouse present” có thu nhập trung bình cao nhất (37,122 USD), trong khi nhóm “Separated” có thu nhập thấp nhất (8,153 USD), phản ánh sự chênh lệch đáng kể giữa các nhóm hôn nhân khác nhau.
Biểu đồ phân bố thu nhập được xây dựng bằng thư viện ggplot2 theo quy trình:
Sử dụng geom_violin() để hiển thị hình dạng phân phối mật độ thu nhập
Kết hợp geom_boxplot() để hiển thị các thống kê tóm tắt và vị trí trung vị
Áp dụng coord_flip() để tối ưu hóa hiển thị cho các nhãn danh mục dài
Kết quả phân tích cho thấy:
Nhóm đang kết hôn và sống cùng vợ/chồng có phân phối thu nhập cao nhất và ổn định nhất
Nhóm ly thân (Separated) có thu nhập thấp nhất với độ phân tán hẹp
Các nhóm góa bụa và ly dị có phân phối thu nhập tương đối đồng đều ở mức trung bình
Nhóm độc thân có phân phối thu nhập lệch phải với đa số ở mức thấp nhưng vẫn có một tỷ lệ nhất định có thu nhập cao
Kết luận: Tình trạng hôn nhân có ảnh hưởng sâu sắc đến thu nhập cá nhân, trong đó việc duy trì hôn nhân ổn định thể hiện mối tương quan tích cực với thu nhập. Phát hiện này cung cấp cơ sở quan trọng cho các nghiên cứu về kinh tế hộ gia đình và chính sách an sinh xã hội.
Ảnh hưởng của chủng tộc
# Biểu đồ 8. Biểu đồ thu nhập trung bình theo chủng tộc
df %>%
group_by(race) %>%
summarise(mean_income = mean(incwage, na.rm = TRUE)) %>%
ggplot(aes(x = reorder(race, mean_income), y = mean_income, fill = race)) +
geom_col(alpha = 0.8) + # Layer 1
geom_text(aes(label = round(mean_income, 0)),
hjust = -0.05, size = 3.5, color = "black") + # Layer 2
scale_fill_brewer(palette = "Set2") + # Layer 3
labs(title = "Thu nhập trung bình theo chủng tộc",
x = "Chủng tộc", y = "Thu nhập trung bình", fill = "Chủng tộc") + # Layer 4
theme_minimal() +
coord_flip() + # Layer 5
scale_y_continuous(limits = c(0, 40000))
Phân tích thu nhập theo chủng tộc sử dụng phương pháp tổng hợp dữ liệu với các hàm group_by() và summarise() để tính giá trị trung bình của biến thu nhập (incwage) cho từng nhóm chủng tộc (race), đồng thời loại bỏ các giá trị thiếu (na.rm=TRUE). Kết quả cho thấy nhóm người gốc Á có thu nhập trung bình cao nhất (28,645 USD), tiếp theo là người da trắng (23,956 USD), trong khi nhóm người đảo Thái Bình Dương có thu nhập thấp nhất (16,072 USD), phản ánh sự chênh lệch đáng kể giữa các nhóm chủng tộc khác nhau.
Biểu đồ thu nhập trung bình được xây dựng bằng thư viện ggplot2 theo quy trình:
Sử dụng geom_col() để hiển thị giá trị trung bình dưới dạng cột
Áp dụng geom_text() để hiển thị chính xác giá trị số trên mỗi cột
Sử dụng reorder() để sắp xếp các nhóm chủng tộc theo thứ tự thu nhập tăng dần
Áp dụng coord_flip() để tối ưu hóa hiển thị cho các nhãn danh mục
Kết quả phân tích cho thấy:
Người gốc Á và da trắng có thu nhập trung bình cao hơn đáng kể so với các nhóm chủng tộc khác
Người da đen có thu nhập trung bình ở mức trung bình thấp (21,044 USD)
Các nhóm thiểu số như người bản địa Mỹ và đa chủng tộc có thu nhập ở mức thấp
Khoảng cách thu nhập giữa nhóm cao nhất và thấp nhất lên tới 12,573 USD
Kết luận: Chủng tộc là yếu tố có ảnh hưởng rõ rệt đến thu nhập cá nhân, thể hiện sự bất bình đẳng sâu sắc trong cơ hội kinh tế giữa các nhóm dân cư. Phát hiện này cung cấp bằng chứng quan trọng cho các nghiên cứu về công bằng xã hội và chính sách phát triển cộng đồng.
Ảnh hưởng của loại hình công việc
# 11. So sánh thu nhập trung bình giữa nhóm làm việc full-time- part-time
tapply(df$incwage, df$fullpart, mean, na.rm=TRUE)
Full-time Not working Part-time
55998.91 0.00 17486.34
# 12. Phân tích chéo: giới tính × loại hình công việc (full/part-time)
table(df$sex, df$fullpart)
Full-time Not working Part-time
Female 26512 39767 8411
Male 32899 33180 4904
# 13. Trung bình thu nhập theo giới tính và tình trạng việc làm
aggregate(incwage ~ sex + empstat, data = df, mean, na.rm=TRUE)
# Biểu đồ 9. Thu nhập trung bình theo giới tính và việc làm
df %>%
group_by(empstat) %>%
summarise(mean_income = mean(incwage, na.rm = TRUE)) %>%
ggplot(aes(x = reorder(empstat, mean_income), y = mean_income, fill = empstat)) +
geom_col(alpha = 0.8) +
geom_text(aes(label = round(mean_income, 0)), hjust = -0.1) +
scale_fill_brewer(palette = "Set2") +
labs(title = "Thu nhập trung bình theo tình trạng việc làm", x = "Tình trạng việc làm", y = "Thu nhập trung bình", fill = "Tình trạng việc làm") + theme_bw() + coord_flip() +
scale_y_continuous(limits = c(0, 70000))
Phân tích thu nhập theo tình trạng việc làm sử dụng đa dạng các phương pháp thống kê để làm rõ mối quan hệ giữa việc làm và thu nhập. Hàm tapply() được sử dụng để so sánh thu nhập trung bình theo loại hình công việc, cho thấy nhóm làm full-time có thu nhập cao nhất (55,999 USD), trong khi nhóm part-time chỉ đạt 17,486 USD và nhóm không làm việc có thu nhập bằng 0.
Phân tích chéo sử dụng hàm table() cho thấy sự phân bổ giới tính trong các loại hình công việc: nữ giới chiếm ưu thế trong nhóm part-time (8,411 so với 4,904 nam) và không làm việc (39,767 so với 33,180 nam), trong khi nam giới chiếm tỷ lệ cao hơn trong nhóm full-time (32,899 so với 26,512 nữ).
Phân tích sâu hơn với hàm aggregate() theo cả giới tính và tình trạng việc làm cho thấy:
Trong nhóm có việc làm, nam giới có thu nhập cao hơn nữ giới (53,918 USD so với 44,723 USD)
Nhóm thất nghiệp có thu nhập thấp hơn đáng kể so với nhóm có việc làm
Nhóm ngoài lực lượng lao động có thu nhập gần như bằng 0
Biểu đồ thu nhập trung bình được xây dựng với:
geom_col() để hiển thị giá trị trung bình
geom_text() để hiển thị chính xác giá trị số
reorder() để sắp xếp các nhóm theo thứ tự thu nhập
coord_flip() để tối ưu hóa hiển thị
Kết quả phân tích cho thấy:
Nhóm có việc làm đạt thu nhập cao nhất (49,409 USD)
Khoảng cách thu nhập lớn giữa nhóm có việc làm và các nhóm khác
Nhóm thất nghiệp và ngoài lực lượng lao động có thu nhập rất thấp
Kết luận: Tình trạng việc làm là yếu tố quyết định quan trọng nhất đến thu nhập cá nhân, với sự chênh lệch rõ rệt giữa các nhóm làm việc full-time, part-time và không làm việc. Phát hiện này nhấn mạnh tầm quan trọng của việc tạo cơ hội việc làm ổn định trong các chính sách phát triển kinh tế và xã hội.
Ảnh hưởng của trình độ học vấn
# Biểu đồ 10. Biểu đồ cột thu nhập theo học vấn.
ggplot(df, aes(x = educ, y = incwage)) +
stat_summary(fun = mean, geom = "bar", fill = "skyblue") +
labs(
title = "Thu nhập trung bình theo trình độ học vấn",
x = "Trình độ học vấn",
y = "Thu nhập trung bình"
) +
theme_minimal()
Biểu đồ sử dụng kỹ thuật stat_summary() kết hợp với geom = “bar” để tự động tính toán và hiển thị giá trị trung bình của thu nhập theo từng nhóm trình độ học vấn. Cụ thể:
Hàm stat_summary(fun = mean) tính giá trị trung bình của biến thu nhập (incwage) cho mỗi nhóm trình độ học vấn (educ)
Tham số geom = “bar” vẽ các cột đại diện cho các giá trị trung bình vừa tính toán
Màu fill = “skyblue” được sử dụng đồng nhất cho tất cả các cột
Kết quả cho thấy:
Trình độ Graduate degree có thu nhập trung bình cao nhất
Trình độ Elementary school có thu nhập trung bình thấp nhất
Các trình độ Associate degree và Some college có thu nhập trung bình ở mức trung bình
Khoảng cách thu nhập giữa nhóm cao nhất và thấp nhất khá lớn
Kết luận: biểu đồ cho thấy mối quan hệ tích cực rõ rệt giữa trình độ học vấn và thu nhập trung bình. Cá nhân có trình độ học vấn càng cao thường đạt được thu nhập trung bình càng lớn, điều này nhấn mạnh tầm quan trọng của giáo dục trong việc nâng cao thu nhập và chất lượng cuộc sống.
# Biểu đồ 11. Boxplot thu nhập theo học vấn
ggplot(df, aes(x = reorder(educ, incwage, FUN = median), y = incwage, fill = educ)) +
geom_boxplot(outlier.shape = NA, alpha = 0.7, color = "gray40", width = 0.6) +
stat_summary(fun = mean, geom = "point", color = "red", size = 2.5) +
scale_fill_brewer(palette = "Pastel1") +
scale_y_continuous(labels = comma, limits = c(0, quantile(df$incwage, 0.95, na.rm = TRUE))) +
coord_flip() +
labs(title = "Phân bố thu nhập theo trình độ học vấn",
x = "Trình độ học vấn",
y = "Thu nhập (USD)") +
theme_minimal(base_size = 13) +
theme(plot.title = element_text(face = "bold", hjust = 0.5),
legend.position = "none")
Biểu đồ sử dụng kỹ thuật boxplot kết hợp với điểm trung bình để so sánh phân bố thu nhập:
geom_boxplot(): Hiển thị hộp và râu thể hiện các phần tư phân bố (Q1, median, Q3, IQR)
stat_summary(): Thêm điểm màu đỏ để đánh dấu giá trị trung bình của từng nhóm
reorder(): Sắp xếp các nhóm trình độ học vấn theo thứ tự tăng dần của thu nhập trung vị
coord_flip(): Đảo trục để tên các trình độ học vấn dễ đọc hơn
scale_y_continuous(): Giới hạn phạm vi trục y để loại bỏ các giá trị ngoại lai cực đoan
Kết quả cho thấy:
Trình độ Graduate degree có thu nhập trung bình và trung vị cao nhất
Trình độ Elementary school có thu nhập thấp nhất và biến động ít
Khoảng cách thu nhập giữa các nhóm học vấn rõ rệt, thể hiện mối quan hệ tích cực giữa học vấn và thu nhập
Hầu hết các nhóm đều có điểm trung bình (đỏ) cao hơn trung vị (vạch giữa hộp), cho thấy phân bố thu nhập lệch phải
Kết luận: biểu đồ khẳng định mối tương quan tích cực rõ rệt giữa trình độ học vấn và thu nhập. Sự chênh lệch đáng kể giữa các nhóm cho thấy giá trị của giáo dục trong việc nâng cao thu nhập. Phân bố lệch phải ở hầu hết các nhóm phản ánh sự tồn tại của một số cá nhân có thu nhập rất cao so với mặt bằng chung.
# 14. Trung bình thu nhập theo nhóm học vấn
df_clean <- df %>%
filter(!is.na(incwage), !is.na(Trình_độ_học_vấn))
tb_hocvan_df <- df_clean %>%
group_by(Trình_độ_học_vấn) %>%
summarise(
Số_người = n(),
Thu_nhập_trung_bình = mean(incwage, na.rm = TRUE),
Thu_nhập_trung_vị = median(incwage, na.rm = TRUE),
Độ_lệch_chuẩn = sd(incwage, na.rm = TRUE)
)
kable(
tb_hocvan_df,
caption = "Thu nhập trung bình theo nhóm học vấn",
digits = 2
)
| Trình_độ_học_vấn | Số_người | Thu_nhập_trung_bình | Thu_nhập_trung_vị | Độ_lệch_chuẩn |
|---|---|---|---|---|
| Thấp | 29811 | 87.70 | 0 | 1794.86 |
| Trung bình | 66601 | 20014.96 | 1200 | 28291.91 |
| Cao | 45481 | 44313.91 | 40000 | 43138.33 |
Sử dụng group_by() và summarise() để tính các chỉ số thống kê cơ bản
Ý nghĩa:
Nhóm “Cao” có thu nhập trung bình 44,314 USD, gấp hơn 2 lần nhóm “Trung bình” và 500 lần nhóm “Thấp”
Độ lệch chuẩn tăng dần theo trình độ, phản ánh sự đa dạng về thu nhập trong nhóm có học vấn cao
Trung vị = 0 ở nhóm “Thấp” cho thấy đa số không có thu nhập
# Biểu đồ 12. Biểu đồ tỷ lệ thu nhập cao theo học vấn.
df %>%
mutate(High_income = ifelse(incwage > median(incwage, na.rm = TRUE), "Cao", "Thấp")) %>%
group_by(educ, High_income) %>%
summarise(Count = n(), .groups = "drop") %>%
mutate(Tỷ_lệ = Count / sum(Count) * 100) %>%
ggplot(aes(x = educ, y = Tỷ_lệ, fill = High_income)) +
geom_col(position = "fill") + # Layer 1
scale_y_continuous(labels = scales::percent) + # Layer 2
scale_fill_manual(values = c("#66c2a5", "#fc8d62")) + # Layer 3
labs(title = "Tỷ lệ thu nhập cao/thấp theo học vấn", x = "Trình độ học vấn", y = "Tỷ lệ (%)", fill = "Nhóm thu nhập") + # Layer 4
theme_minimal() + coord_flip() # Layer 5
Sử dụng geom_col(position = “fill”) để chuẩn hóa thành tỷ lệ phần trăm, kết hợp coord_flip() để hiển thị rõ các nhãn
Ý nghĩa:
Nhóm Graduate degree có tỷ lệ thu nhập cao chiếm ưu thế tuyệt đối
Nhóm Elementary school hầu như chỉ có thu nhập thấp
Các nhóm Some college và Some high school có sự pha trộn cả hai mức thu nhập
Kết luận tổng quan: Tất cả các phân tích đều khẳng định mối quan hệ tích cực và mạnh mẽ giữa trình độ học vấn với thu nhập, cung cấp bằng chứng thuyết phục cho các chính sách đầu tư vào giáo dục.
Mục tiêu: Khám phá sự kết hợp phức tạp của nhiều yếu tố cùng lúc tác động đến thu nhập.
Xu hướng và mô hình quan hệ
# 15. Tính Hệ số tương quan giữa tuổi và thu nhập
cor(df$age, df$incwage, use = "complete.obs")
[1] 0.1531655
# Biểu đồ 13. Biểu đồ phân tán giữa tuổi và thu nhập
plot(df$age, df$incwage,
main = "Mối quan hệ giữa tuổi và thu nhập",
xlab = "Tuổi", ylab = "Thu nhập",
pch = 19, col = "steelblue")
# 16. Thêm đường hồi quy tuyến tính
model_age <- lm(incwage ~ age, data = df)
abline(model_age, col = "red", lwd = 2)
Sử dụng hàm cor() tính hệ số tương quan Pearson kết hợp biểu đồ phân tán plot() với đường hồi quy tuyến tính từ mô hình lm()
Kết quả: Hệ số tương quan 0.153 cho thấy mối quan hệ tuyến tính yếu đến trung bình giữa tuổi và thu nhập
Ý nghĩa: Đường hồi quy dốc lên thể hiện xu hướng tích cực - thu nhập có xu hướng tăng theo độ tuổi, tuy nhiên độ phân tán lớn của các điểm dữ liệu cho thấy còn nhiều yếu tố khác ảnh hưởng đến thu nhập
# 17. Thu nhập trung bình theo nhóm tuổi
thu_nhập_tuổi_tb <- df %>%
group_by(Nhóm_tuổi) %>%
summarise(Thu_nhập_trung_bình = mean(incwage, na.rm = TRUE))
thu_nhập_tuổi_tb
# Biểu đồ 14. Biểu đồ đường thu nhập trung bình theo độ tuổi
ggplot(thu_nhập_tuổi_tb, aes(x = Nhóm_tuổi, y = Thu_nhập_trung_bình, group = 1)) +
geom_line(color = "steelblue", size = 1.2) +
geom_point(color = "steelblue", size = 3) +
geom_text(aes(label = round(Thu_nhập_trung_bình, 0)),
vjust = 1.5, size = 4, color = "black") +
labs(title = "Thu nhập trung bình theo nhóm tuổi",
x = "Nhóm tuổi",
y = "Thu nhập trung bình") +
theme_minimal() +
scale_y_continuous(labels = scales::number_format(big.mark = ""))
Sử dụng group_by() và summarise() để tính thu nhập trung bình theo nhóm tuổi, kết hợp biểu đồ đường geom_line() và điểm geom_point() để hiển thị xu hướng
Kết quả: Thu nhập tăng dần từ nhóm “Dưới 25” (4,034 USD) lên đỉnh điểm ở nhóm “40-59” (45,513 USD), sau đó giảm mạnh ở nhóm “Từ 60 trở lên” (13,439 USD)
Ý nghĩa: Thể hiện rõ quỹ đạo thu nhập trong vòng đời - tăng trong giai đoạn sự nghiệp và giảm khi về hưu, phù hợp với lý thuyết vòng đời về thu nhập
Tương tác giới tính - trình độ học vấn
# 18. Phân tích thu nhập trung bình theo giới tính và nhóm học vấn
aggregate(incwage ~ sex + Trình_độ_học_vấn, data = df, mean, na.rm=TRUE)
# Biểu đồ 15.Thu nhập trung bình theo giới tính và trình độ học vấn
df %>%
group_by(sex, Trình_độ_học_vấn) %>%
summarise(mean_income = mean(incwage, na.rm = TRUE), .groups = 'drop') %>%
ggplot(aes(x = Trình_độ_học_vấn, y = mean_income, fill = sex)) +
geom_col(alpha = 0.8, position = "dodge") +
geom_text(aes(label = round(mean_income, 0)),
position = position_dodge(0.9), vjust = -0.5, size = 3.5) +
scale_fill_brewer(palette = "Set2") +
labs(title = "Thu nhập trung bình theo giới tính và trình độ học vấn",
x = "Trình độ học vấn",
y = "Thu nhập trung bình",
fill = "Giới tính") +
theme_minimal() +
scale_y_continuous(labels = scales::number_format(big.mark = ""))
Sử dụng hàm aggregate() để tính thu nhập trung bình theo tổ hợp giới tính và trình độ học vấn, biểu đồ cột sử dụng geom_col(position = “dodge”) để hiển thị song song các nhóm, geom_text() với position_dodge() để hiển thị giá trị số đúng vị trí, scale_fill_brewer() tạo palette màu phân biệt giới tính
Kết quả: Ở mọi trình độ học vấn, nam giới đều có thu nhập cao hơn nữ giới
Khoảng cách thu nhập theo giới tính tăng dần theo trình độ học vấn: từ 38 USD (trình độ thấp) lên 11,092 USD (trình độ trung bình) và 12,548 USD (trình độ cao)
Trình độ học vấn có tác động mạnh mẽ đến thu nhập: nhóm trình độ cao có thu nhập gấp 571 lần so với nhóm trình độ thấp ở nữ giới, và 485 lần ở nam giới
# Biểu đồ 16. Mật độ thu nhập theo trình độ học vấn và giới tính
ggplot(df %>% filter(incwage > 0 & incwage <= 150000), aes(x = incwage, fill = sex)) +
geom_density(alpha = 0.5) + # Layer 1
facet_wrap(~ Trình_độ_học_vấn, ncol = 1) + # Layer 2
scale_fill_brewer(palette = "Set2") + # Layer 3
labs(title = "Mật độ thu nhập theo trình độ học vấn và giới tính",
x = "Thu nhập",
y = "Mật độ",
fill = "Giới tính") + # Layer 4
theme_minimal() + # Layer 5
scale_x_continuous(labels = scales::number_format(big.mark = ""))+
scale_y_continuous(labels = scales::number_format(big.mark = ""))
Sử dụng filter() để loại bỏ thu nhập bằng 0 và giá trị cực lớn (>150,000), geom_density(alpha = 0.5) tạo biểu đồ mật độ trong suốt để so sánh, facet_wrap(~ Trình_độ_học_vấn, ncol = 1) tách thành 3 biểu đồ theo trình độ học vấn, scale_fill_brewer() duy trì tính nhất quán về màu sắc
Kết quả: Ở nhóm trình độ thấp: cả nam và nữ đều tập trung ở mức thu nhập gần 0, không có sự khác biệt đáng kể
Ở nhóm trình độ trung bình: phân phối thu nhập của nam giới dịch phải rõ rệt, cho thấy nhiều nam giới đạt thu nhập trung bình cao hơn
Ở nhóm trình độ cao: đường phân phối của nam giới kéo dài hơn về phía phải, thể hiện tỷ lệ nam giới có thu nhập rất cao lớn hơn nữ giới
Kết luận: Tồn tại hiệu ứng tương tác kép giữa giới tính và trình độ học vấn - bất bình đẳng thu nhập theo giới tính không chỉ tồn tại mà còn trầm trọng hơn ở các nhóm có trình độ học vấn cao, phản
Tương tác trình độ - việc làm
# 19. Thống kê thu nhập trung bình theo học vấn và tình trạng việc làm
tb_hocvan_vieclam <- df %>%
group_by(educ, empstat) %>%
summarise(
Thu_nhập_trung_bình = mean(incwage, na.rm = TRUE),
.groups = 'drop'
) %>%
mutate(
empstat = factor(empstat,
levels = c("Employed", "Unemployed", "Not in labor force", "NIU"))
)
kable(
tb_hocvan_vieclam,
caption = "Bảng 17. Thu nhập trung bình theo nhóm học vấn và tình trạng việc làm",
digits = 2
)
| educ | empstat | Thu_nhập_trung_bình |
|---|---|---|
| Associate degree | Employed | 48508.38 |
| Associate degree | Not in labor force | 2427.22 |
| Associate degree | Unemployed | 22452.11 |
| Elementary school | Employed | 26421.67 |
| Elementary school | NIU | 0.00 |
| Elementary school | Not in labor force | 629.92 |
| Elementary school | Unemployed | 10750.00 |
| Graduate degree | Employed | 66146.02 |
| Graduate degree | Not in labor force | 3052.00 |
| Graduate degree | Unemployed | 31515.31 |
| Some college | Employed | 39920.47 |
| Some college | Not in labor force | 1942.43 |
| Some college | Unemployed | 19302.90 |
| Some high school | Employed | 23151.84 |
| Some high school | Not in labor force | 768.94 |
| Some high school | Unemployed | 10283.99 |
Sử dụng group_by(educ, empstat) để nhóm dữ liệu theo tổ hợp trình độ học vấn và tình trạng việc làm, hàm summarise() tính thu nhập trung bình cho từng nhóm với na.rm = TRUE để loại bỏ giá trị thiếu, mutate() kết hợp factor() xác định thứ tự levels cho biến empstat, đảm bảo trình tự hợp lý khi hiển thị
Kết quả:
Nhóm có việc làm (Employed) luôn đạt thu nhập cao nhất ở mọi trình độ học vấn, từ 23,152 USD (Some high school) đến 66,146 USD (Graduate degree)
Khoảng cách thu nhập giữa nhóm có việc làm và không làm việc là rất lớn: nhóm Graduate degree có việc làm có thu nhập gấp 21.7 lần so với cùng nhóm trình độ nhưng không làm việc
Nhóm thất nghiệp (Unemployed) vẫn có thu nhập đáng kể (10,284 - 31,515 USD), có thể do trợ cấp thất nghiệp hoặc các nguồn thu nhập khác
Nhóm không tham gia lực lượng lao động có thu nhập thấp nhất, gần như bằng 0 ở các trình độ thấp
# Biểu đồ 17. Thu nhập trung bình theo học vấn và việc làm.
ggplot(tb_hocvan_vieclam, aes(x = educ, y = empstat, fill = Thu_nhập_trung_bình)) +
geom_tile(color = "white") + # Layer 1: heatmap
scale_fill_gradient(low = "lightyellow", high = "red") + # Layer 2
geom_text(aes(label = round(Thu_nhập_trung_bình, 0)),
color = "black", size = 3) + # Layer 3: nhãn số
labs(
title = "Thu nhập trung bình theo học vấn và tình trạng việc làm",
x = "Trình độ học vấn",
y = "Tình trạng việc làm",
fill = "Thu nhập trung bình"
) + # Layer 4
theme_light() + # Layer 5
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
coord_cartesian()
geom_tile() tạo lưới heatmap với màu sắc thể hiện giá trị thu nhập, scale_fill_gradient() thiết lập gradient màu từ vàng nhạt (thấp) đến đỏ (cao), geom_text() hiển thị giá trị số trên mỗi ô với màu chữ tương phản, theme(axis.text.x = element_text(angle = 45, hjust = 1)) xoay nhãn trục hoành 45 độ để hiển thị rõ, theme_light() cung cấp nền sáng tối ưu cho biểu đồ heatmap
Kết quả:
Phân tích theo trình độ học vấn:
Thu nhập tăng dần theo trình độ: Some high school (23,152 USD) → Graduate degree (66,146 USD)
Khoảng cách thu nhập lớn nhất giữa trình độ cao nhất và thấp nhất: 43,000 USD
Phân tích theo tình trạng việc làm:
Nhóm Employed chiếm ưu thế tuyệt đối về thu nhập ở mọi trình độ
Nhóm Unemployed có thu nhập trung bình, phản ánh trợ cấp thất nghiệp
Nhóm Not in labor force có thu nhập thấp nhất, gần bằng 0
Hiệu ứng tương tác:
Trình độ Graduate degree kết hợp với Employed cho thu nhập cao nhất (66,146 USD)
Hiệu ứng tích cực của giáo dục chỉ phát huy đầy đủ khi có việc làm ổn định
Khoảng cách thu nhập giữa các nhóm việc làm tăng theo trình độ học vấn
Kết luận: Heatmap cung cấp cái nhìn trực quan về hiệu ứng tương tác mạnh mẽ giữa giáo dục và việc làm, khẳng định cả hai yếu tố đều quan trọng trong quyết định thu nhập cá nhân.
Tình trạng việc làm đóng vai trò “công tắc” khuếch đại hiệu ứng của trình độ học vấn lên thu nhập. Điều này nhấn mạnh tầm quan trọng của việc không chỉ nâng cao trình độ học vấn mà còn cần tạo cơ hội việc làm phù hợp để tối đa hóa thu nhập.
Tương tác đa biến tổng hợp
# Biểu đồ 18. Biểu đồ phân tán thu nhập theo tuổi và nhóm học vấn.
ggplot(df %>% filter(!is.na(age), !is.na(incwage), !is.na(Trình_độ_học_vấn)),
aes(x = age, y = incwage, color = Trình_độ_học_vấn)) +
geom_point(alpha = 0.2, size = 1) + # Layer 1 - GIẢM ALPHA VÀ SIZE
geom_smooth(aes(group = Trình_độ_học_vấn), method = "lm", se = FALSE,
lwd = 1.5) + #Layer 2
scale_color_brewer(palette = "Set2") + # Layer 3
labs(
title = "Quan hệ giữa tuổi và thu nhập theo nhóm học vấn",
x = "Tuổi",
y = "Thu nhập cá nhân (incwage)",
color = "Nhóm học vấn"
) + # Layer 4
theme_classic(base_size = 13) + # Layer 5
coord_cartesian(ylim = c(0, quantile(df$incwage, 0.99, na.rm = TRUE)[[1]])) +
scale_y_continuous(labels = scales::number_format(big.mark = ""))
Sử dụng filter(!is.na(age), !is.na(incwage), !is.na(Trình_độ_học_vấn)) để loại bỏ giá trị thiếu trên cả 3 biến, geom_point(alpha = 0.2, size = 1) giảm độ trong suốt và kích thước điểm để xử lý overplotting, geom_smooth(method = “lm”, se = FALSE) vẽ đường hồi quy tuyến tính không hiển thị khoảng tin cậy, coord_cartesian(ylim = c(0, quantile(df$incwage, 0.99))) giới hạn trục y đến phân vị 99% để loại bỏ outlier ảnh hưởng
Kết quả:
Đường hồi quy dốc lên cho thấy thu nhập tăng theo tuổi ở cả 3 nhóm học vấn
Độ dốc lớn nhất ở nhóm trình độ Cao, thể hiện tốc độ tăng thu nhập nhanh hơn theo tuổi
Nhóm trình độ Thấp có đường gần như nằm ngang, cho thấy ít biến động thu nhập theo tuổi
# Biểu đồ 19. Biểu đồ tương tác học vấn – giới tính – thu nhập.
ggplot(df, aes(x = reorder(educ, incwage, FUN = median), y = incwage, fill = sex)) +
geom_boxplot(
position = position_dodge(width = 0.8),
alpha = 0.7,
outlier.shape = NA, # Layer 1: ẩn giá trị ngoại lai
width = 0.6,
color = "gray40"
) +
stat_summary(
fun = mean,
geom = "point",
position = position_dodge(width = 0.8),
shape = 18, size = 2.5, color = "black"
) + # Layer 2: điểm trung bình
scale_fill_brewer(palette = "Set2", name = "Giới tính") + # Layer 3: bảng màu
scale_y_continuous(
labels = scales::comma,
limits = c(0, quantile(df$incwage, 0.95, na.rm = TRUE))
) + # Layer 4: trục y chuẩn hóa
labs(
title = "Tương tác giữa trình độ học vấn-giới tính-thu nhập",
x = "Trình độ học vấn",
y = "Thu nhập (USD)"
) +
coord_flip() + # Layer 5: lật ngang
theme_light(base_size = 13) +
theme(
plot.title = element_text(face = "bold", hjust = 0.5),
legend.position = "top"
)
Sử dụng reorder(educ, incwage, FUN = median) sắp xếp trình độ học vấn theo trung vị thu nhập, geom_boxplot(position = position_dodge(width = 0.8)) tạo boxplot song song cho từng giới tính, stat_summary() với geom = “point” thêm điểm trung bình (hình thoi) cho mỗi nhóm, outlier.shape = NA ẩn các điểm ngoại lai để tập trung vào xu hướng chính, coord_flip() tối ưu hóa hiển thị cho các nhãn trình độ học vấn dài
Kết quả: Ở mọi trình độ học vấn, boxplot của nam giới đều nằm cao hơn nữ giới
Khoảng cách giữa trung vị (box) và trung bình (điểm) lớn ở nhóm trình độ cao, phản ánh phân phối lệch phải
Sự chênh lệch thu nhập theo giới tính tăng dần theo trình độ học vấn
Nhóm Graduate degree có khoảng cách thu nhập nam-nữ lớn nhất
Kết luận đa biến: Cả ba yếu tố tuổi, trình độ học vấn và giới tính đều tương tác phức tạp trong việc quyết định thu nhập, trong đó trình độ học vấn đóng vai trò then chốt trong việc khuếch đại các hiệu ứng tuổi và giới tính.
Phân tích Heatmap toàn cảnh
# 20. Thống kê thu nhập trung bình theo nhóm tuổi và giới tính
tb_tuoi_gioitinh <- df %>%
mutate(age_group = cut(age, breaks = c(0, seq(15, 80, 5), 100), include.lowest = TRUE)) %>%
group_by(age_group, sex) %>%
summarise(
Thu_nhập_trung_bình = mean(incwage, na.rm = TRUE),
.groups = "drop"
)
kable(
tb_tuoi_gioitinh,
caption = "Bảng 20. Thu nhập trung bình theo nhóm tuổi và giới tính",
digits = 2
)
| age_group | sex | Thu_nhập_trung_bình |
|---|---|---|
| [0,15] | Female | 53.88 |
| [0,15] | Male | 90.31 |
| (15,20] | Female | 5347.66 |
| (15,20] | Male | 6394.29 |
| (20,25] | Female | 21447.82 |
| (20,25] | Male | 26631.55 |
| (25,30] | Female | 33976.78 |
| (25,30] | Male | 44922.39 |
| (30,35] | Female | 37569.05 |
| (30,35] | Male | 52678.32 |
| (35,40] | Female | 38566.12 |
| (35,40] | Male | 56060.37 |
| (40,45] | Female | 40034.59 |
| (40,45] | Male | 57344.55 |
| (45,50] | Female | 40069.76 |
| (45,50] | Male | 55122.65 |
| (50,55] | Female | 38039.26 |
| (50,55] | Male | 54265.14 |
| (55,60] | Female | 31772.92 |
| (55,60] | Male | 44600.04 |
| (60,65] | Female | 21506.44 |
| (60,65] | Male | 33229.76 |
| (65,70] | Female | 10710.21 |
| (65,70] | Male | 15822.77 |
| (70,75] | Female | 5184.24 |
| (70,75] | Male | 8381.14 |
| (75,80] | Female | 2555.36 |
| (75,80] | Male | 4411.99 |
| (80,100] | Female | 1007.23 |
| (80,100] | Male | 2111.00 |
# Biểu đồ 20. Thu nhập trung bình theo nhóm tuổi và giới tính
ggplot(tb_tuoi_gioitinh, aes(x = age_group, y = sex, fill = Thu_nhập_trung_bình)) +
geom_tile(color = "white", width = 0.9, height = 0.9) + # Layer 1
geom_text(aes(label = round(Thu_nhập_trung_bình, 0)),
color = "black", size = 2.8) + # Layer 2: nhãn số
scale_fill_gradient(low = "lightblue", high = "darkred") +
labs(
title = "Thu nhập trung bình theo nhóm tuổi và giới tính",
x = "Nhóm tuổi",
y = "Giới tính",
fill = "Thu nhập trung bình"
) + theme_minimal() + theme(axis.text.x = element_text(angle = 45, hjust = 1)) + coord_cartesian(expand = FALSE) # Layer 5: bố cục
Phân tích đa chiều được thực hiện thông qua xử lý dữ liệu và trực quan hóa bằng biểu đồ heatmap. Dữ liệu tuổi được phân nhóm theo khoảng 5 năm bằng hàm cut(), kết hợp với biến giới tính để tính thu nhập trung bình cho từng tổ hợp. Biểu đồ heatmap sử dụng geom_tile() hiển thị giá trị thu nhập thông qua scale_fill_gradient(): sử dụng gradient màu từ xanh nhạt đến đỏ đậm, kết hợp nhãn số trong mỗi ô để cung cấp thông tin chi tiết.
Kết quả cho thấy xu hướng thu nhập theo vòng đời rõ rệt: thu nhập đạt đỉnh ở nhóm tuổi 40-45 cho cả nam (57,345 USD) và nữ (40,035 USD), tăng nhanh từ giai đoạn đầu sự nghiệp và giảm mạnh sau tuổi 60. Phân tích cũng khẳng định sự tồn tại của bất bình đẳng giới tính xuyên suốt các nhóm tuổi, với khoảng cách lớn nhất ở độ tuổi đỉnh cao sự nghiệp (17,310 USD). Những phát hiện này cung cấp cơ sở thực tiễn quan trọng cho việc hoạch định chính sách bảo hiểm xã hội, hưu trí và thúc đẩy bình đẳng giới trong thị trường lao động.
# 1 Đọc tất cả tên sheet trong file gốc
file_path <- "BCTC_VIC.xlsx"
sheet_names <- excel_sheets(file_path)
# 2 Tách từng sheet ra thành file riêng (chỉ làm nếu chưa có)
for (sheet in sheet_names) {
out_file <- paste0(sheet, ".xlsx")
if (!file.exists(out_file)) {
data <- read_excel(file_path, sheet = sheet)
write_xlsx(data, out_file)
}
}
# 3 Đọc riêng sheet "Bảng cân đối kế toán"
balance <- read_excel("Bảng cân đối kế toán.xlsx")
# 4 Transpose bảng
# Dùng t() để đổi hàng-cột, rồi chuyển lại data frame
balance_t <- as.data.frame(t(balance))
# 5 Đặt tên cột, xử lý trùng
colnames(balance_t) <- make.unique(as.character(balance_t[1, ])) # tránh trùng tên
balance_t <- balance_t[-1, ]
# 6: Tạo cột "Năm"
balance_t <- balance_t %>%
mutate(Năm = rownames(balance_t)) %>%
select(Năm, everything())
rownames(balance_t) <- NULL
# 7 Giữ lại các cột cần thiết
cols_keep_index <- c(1, 3, 11, 16, 30, 39, 43, 57, 70, 76, 84)
balance_t <- balance_t[, cols_keep_index, drop = FALSE]
# 8 Đặt bảng này làm dữ liệu chính để phân tích
data_analysis <- balance_t
# 9 Kiểm tra xem dữ liệu xử lý đã đúng chưa
head(data_analysis)
Phần mã trên thực hiện quy trình xử lý dữ liệu tài chính nhằm chuẩn bị cho bước phân tích thống kê và hồi quy.
Bước (1) và (2) đọc toàn bộ các sheet trong file gốc BCTC_VIC.xlsx và tách từng sheet ra thành tệp riêng, giúp việc thao tác và kiểm tra dữ liệu thuận tiện hơn, đồng thời tránh lỗi định dạng khi đọc nhiều sheet cùng lúc.
Bước (3) chỉ định đọc riêng sheet “Bảng cân đối kế toán”, đây là bảng chứa các chỉ tiêu tài chính quan trọng được trình bày theo năm, là nguồn dữ liệu chính phục vụ phân tích.
Bước (4), bảng được chuyển vị (transpose) bằng hàm t() để đổi vị trí hàng và cột, bảo đảm rằng mỗi hàng tương ứng một năm, còn mỗi cột là một chỉ tiêu tài chính. Việc này rất quan trọng về mặt kỹ thuật vì các mô hình thống kê (như OLS) yêu cầu dữ liệu ở dạng tidy — nghĩa là mỗi biến là một cột và mỗi quan sát là một hàng.
Bước (5) dùng make.unique() để xử lý trùng tên cột, đồng thời loại bỏ hàng tiêu đề cũ. Điều này giúp tránh lỗi khi chọn biến và đảm bảo mỗi chỉ tiêu có tên duy nhất.
Bước (6) tạo thêm cột “Năm” từ tên hàng (rownames), giúp định danh quan sát rõ ràng và dễ dàng sắp xếp, tính toán theo thời gian.
Bước (7), chỉ giữ lại một số cột quan trọng phục vụ phân tích, bao gồm các chỉ tiêu chính như Tiền và các khoản tương đương tiền, Phải thu ngắn hạn của khách hàng, Hàng tồn kho,… Các chỉ tiêu này thuộc nhóm tài sản ngắn hạn, phản ánh trực tiếp khả năng thanh khoản, hiệu quả quản trị vốn lưu động và xu hướng đầu tư của doanh nghiệp.
Cuối cùng, bước (8) đặt bảng kết quả làm dữ liệu phân tích chính (data_analysis), và bước (9) hiển thị 6 dòng đầu để kiểm tra lại cấu trúc.
# 1. Tạo bộ dữ liệu ở dạng dài (long format) phục vụ phân tích
data_long <- data_analysis %>%
# 2. Chuyển các cột số (trừ Năm) từ chr sang numeric
mutate(across(-Năm, ~ as.numeric(trimws(.)))) %>%
# 3. Chuyển dữ liệu sang dạng dài
pivot_longer(
cols = -Năm,
names_to = "Khoản_mục",
values_to = "Giá_trị"
) %>%
# 4. Tạo cột giá trị tính theo TỶ
mutate(
Giá_trị_tỷ = Giá_trị / 1e9
)
# 5. Kiểm tra kiểu dữ liệu
str(data_analysis)
'data.frame': 10 obs. of 11 variables:
$ Năm : chr "2015" "2016" "2017" "2018" ...
$ I. Tiền và các khoản tương đương tiền: chr " 6938465.10" " 9833332.22" " 8141750.03" " 13557055.28" ...
$ Phải thu ngắn hạn của khách hàng : chr " 2438800.37" " 3170762.78" " 5744460.45" " 7594009.96" ...
$ IV. Hàng tồn kho : chr " 28027417.69" " 49782780.79" " 56058815.19" " 55105513.55" ...
$ Tài sản cố định hữu hình : chr " 19114346.09" " 26066821.88" " 34973533.34" " 48549322.92" ...
$ III. Bất động sản đầu tư : chr " 16827664.24" " 17362127.30" " 18198420.91" " 26733365.59" ...
$ Chi phí xây dựng cơ bản dở dang : chr " 18115293.36" " 33991567.27" " 37492138.28" " 58529122.78" ...
$ Phải trả người bán ngắn hạn : chr " 4578748.20" " 6458154.24" " 8245460.36" " 14773384.44" ...
$ Vay và nợ thuê tài chính dài hạn : chr " 33122684.52" " 34168826.60" " 31219525.90" " 61770712.36" ...
$ Vốn góp của chủ sở hữu : chr " 18681880.87" " 26377079.54" " 26377079.54" " 32756212.30" ...
$ Lợi nhuận sau thuế chưa phân phối : chr " 1601415.50" " 942345.25" " 5583084.56" " 5095996.48" ...
# 6. Xem kích thước bảng
dim(data_analysis)
[1] 10 11
# 7. Kiểm tra dữ liệu bị thiếu
na_by_column <- colSums(is.na(data_analysis))
columns_with_na <- na_by_column[na_by_column > 0]
if(length(columns_with_na) > 0) {
cat("Các cột có giá trị bị thiếu:\n")
data.frame(
Column = names(columns_with_na),
NA_Count = columns_with_na
) %>%
kable(caption = "Số lượng giá trị NA theo cột") %>%
kable_styling()
} else {
cat("\nBộ dữ liệu không có giá trị bị thiếu nào.")
}
Bộ dữ liệu không có giá trị bị thiếu nào.
# 8. Kiểm tra dữ liệu bị trùng
num_duplicated_rows <- sum(duplicated(data_analysis))
cat(paste("Tổng số dòng bị trùng lặp hoàn toàn là:", num_duplicated_rows, "\n"))
Tổng số dòng bị trùng lặp hoàn toàn là: 0
# 9. Kiểm tra lại
str(data_analysis)
'data.frame': 10 obs. of 11 variables:
$ Năm : chr "2015" "2016" "2017" "2018" ...
$ I. Tiền và các khoản tương đương tiền: chr " 6938465.10" " 9833332.22" " 8141750.03" " 13557055.28" ...
$ Phải thu ngắn hạn của khách hàng : chr " 2438800.37" " 3170762.78" " 5744460.45" " 7594009.96" ...
$ IV. Hàng tồn kho : chr " 28027417.69" " 49782780.79" " 56058815.19" " 55105513.55" ...
$ Tài sản cố định hữu hình : chr " 19114346.09" " 26066821.88" " 34973533.34" " 48549322.92" ...
$ III. Bất động sản đầu tư : chr " 16827664.24" " 17362127.30" " 18198420.91" " 26733365.59" ...
$ Chi phí xây dựng cơ bản dở dang : chr " 18115293.36" " 33991567.27" " 37492138.28" " 58529122.78" ...
$ Phải trả người bán ngắn hạn : chr " 4578748.20" " 6458154.24" " 8245460.36" " 14773384.44" ...
$ Vay và nợ thuê tài chính dài hạn : chr " 33122684.52" " 34168826.60" " 31219525.90" " 61770712.36" ...
$ Vốn góp của chủ sở hữu : chr " 18681880.87" " 26377079.54" " 26377079.54" " 32756212.30" ...
$ Lợi nhuận sau thuế chưa phân phối : chr " 1601415.50" " 942345.25" " 5583084.56" " 5095996.48" ...
# 10. Tóm tắt thống kê các biến số
summary(data_analysis)
Năm I. Tiền và các khoản tương đương tiền
Length:10 Length:10
Class :character Class :character
Mode :character Mode :character
Phải thu ngắn hạn của khách hàng IV. Hàng tồn kho Tài sản cố định hữu hình
Length:10 Length:10 Length:10
Class :character Class :character Class :character
Mode :character Mode :character Mode :character
III. Bất động sản đầu tư Chi phí xây dựng cơ bản dở dang
Length:10 Length:10
Class :character Class :character
Mode :character Mode :character
Phải trả người bán ngắn hạn Vay và nợ thuê tài chính dài hạn
Length:10 Length:10
Class :character Class :character
Mode :character Mode :character
Vốn góp của chủ sở hữu Lợi nhuận sau thuế chưa phân phối
Length:10 Length:10
Class :character Class :character
Mode :character Mode :character
Sau khi chuyển đổi bảng cân đối kế toán sang dạng chuẩn, dữ liệu được chuyển về định dạng dài (long format) bằng hàm pivot_longer() thuộc gói tidyr. Cách tổ chức này giúp gom tất cả các khoản mục tài chính vào chung một cột, với hai biến định danh là Năm và Khoản_mục. Việc này giúp cho việc mô tả, trực quan hóa và phân tích xu hướng theo thời gian trở nên linh hoạt hơn.
Tiếp đó, các giá trị tài chính được ép kiểu từ ký tự (character) sang số (numeric) thông qua hàm mutate(across()). Đây là thao tác cần thiết vì dữ liệu sau khi nhập từ Excel thường chứa khoảng trắng hoặc dấu tách thập phân khiến R nhận dạng sai kiểu biến. Việc chuyển đổi này đảm bảo rằng các phép tính thống kê (như trung bình, độ lệch chuẩn, hay hồi quy) được thực hiện chính xác.
Biến Giá_trị_ty được tạo thêm để quy đổi toàn bộ giá trị về đơn vị tỷ đồng (VNĐ), giúp dữ liệu dễ đọc, dễ so sánh và thuận tiện cho việc trình bày kết quả trong phần mô tả và biểu đồ sau.
Kết quả kiểm tra cấu trúc bằng hàm str() cho thấy bộ dữ liệu có 10 quan sát và 11 biến, tương ứng với 10 năm tài chính và 11 khoản mục chính trong bảng cân đối kế toán. Các lệnh kiểm tra NA (is.na()) và trùng lặp (duplicated()) cho thấy không có giá trị thiếu hoặc dòng bị trùng hoàn toàn, thể hiện dữ liệu có độ đầy đủ và tính nhất quán cao.
Việc tóm tắt sơ bộ bằng hàm summary() giúp đánh giá nhanh cấu trúc và phạm vi giá trị của từng biến, qua đó phát hiện sớm các lỗi nhập liệu hoặc sai định dạng (nếu có).
Nhìn chung, bước kiểm tra cấu trúc dữ liệu này giúp xác nhận độ tin cậy và tính sẵn sàng của dữ liệu trước khi thực hiện các bước thống kê mô tả và mô hình hóa hồi quy ở các phần tiếp theo.
# 1. Chuẩn hóa tên cột: loại bỏ khoảng trắng thừa, ký tự lạ
names(data_analysis) <- str_trim(names(data_analysis))
# 2. Chuyển toàn bộ cột số từ chuỗi sang dạng numeric (bỏ dấu phẩy)
data_analysis <- data_analysis %>%
mutate(across(
-Năm,
~ as.numeric(gsub(",", "", trimws(.)))
))
# 3. Đổi tên các cột cho gọn (nếu có dấu La Mã ở đầu)
data_analysis <- data_analysis %>%
rename(
"Tiền và các khoản tương đương tiền" = "I. Tiền và các khoản tương đương tiền",
"Hàng tồn kho" = "IV. Hàng tồn kho",
"Bất động sản đầu tư" = "III. Bất động sản đầu tư"
)
# 4. Kiểm tra giá trị thiếu (NA)
na_count <- sum(is.na(data_analysis))
cat("Tổng số giá trị NA trong dữ liệu:", na_count, "\n")
Tổng số giá trị NA trong dữ liệu: 0
# 5. Thay thế giá trị thiếu bằng 0 (nếu có)
data_analysis[is.na(data_analysis)] <- 0
# 6. Tạo mã khoản mục tự động (K1, K2, K3,...)
data_analysis <- data_analysis %>%
mutate(Mã_khoản_mục = paste0("K", row_number()))
# 7. Chuyển dữ liệu sang dạng dài (long format)
data_long <- data_analysis %>%
pivot_longer(
cols = -c(Năm, Mã_khoản_mục),
names_to = "Khoản_mục",
values_to = "Giá_trị"
)
# 8. Mã hóa Nhóm khoản mục theo nội dung
data_long <- data_long %>%
mutate(
Nhóm = case_when(
str_detect(Khoản_mục, "Tiền|Phải thu|Hàng tồn kho|Tài sản|Bất động sản|Chi phí xây dựng") ~ "Tài sản",
str_detect(Khoản_mục, "Phải trả|Vay|Vốn") ~ "Nguồn vốn",
str_detect(Khoản_mục, "Lợi nhuận") ~ "Kết quả kinh doanh",
TRUE ~ "Khác"
)
)
# 9. Chuyển cột Năm sang kiểu numeric
data_long <- data_long %>%
mutate(Năm = as.numeric(trimws(Năm)))
Dữ liệu thô sau khi được nhập vào được chuẩn hóa tên biến nhằm loại bỏ các ký tự đặc biệt và khoảng trắng thừa ở đầu/cuối chuỗi. Việc này giúp đảm bảo tính đồng nhất, hạn chế lỗi khi gọi biến trong các thao tác phân tích hoặc khi sử dụng trong mô hình hồi quy.
Toàn bộ các biến định lượng được chuyển từ kiểu ký tự sang kiểu numeric, thông qua thao tác loại bỏ dấu phẩy (,). Điều này cần thiết vì dữ liệu kế toán thường có định dạng chuỗi, khiến R không thể thực hiện tính toán thống kê nếu chưa chuyển kiểu dữ liệu.
Một số tên cột chứa ký hiệu La Mã được đổi lại ngắn gọn và rõ nghĩa hơn (ví dụ: “I. Tiền và các khoản tương đương tiền” được đổi thành “Tiền và các khoản tương đương tiền”). Cách đặt tên này giúp tăng tính trực quan khi trình bày bảng và biểu đồ.
Sau khi chuẩn hóa, dữ liệu được kiểm tra giá trị thiếu (NA) bằng hàm is.na(). Kết quả cho thấy không có giá trị NA trong bộ dữ liệu; tuy nhiên, đoạn mã vẫn bao gồm bước thay thế giá trị thiếu bằng 0 nhằm đảm bảo tính an toàn và nhất quán khi xử lý các bộ dữ liệu tương tự.
Để thuận tiện cho việc truy xuất, mỗi dòng dữ liệu được gán mã định danh tự động (K1, K2, …) bằng cách kết hợp hàm row_number() và paste0(). Việc gán mã này giúp dễ dàng theo dõi và quản lý từng khoản mục trong các bước xử lý hoặc khi đối chiếu lại với nguồn dữ liệu gốc.
Dữ liệu được chuyển từ dạng rộng (wide format) sang dạng dài (long format) bằng hàm pivot_longer(). Trong đó, mỗi quan sát thể hiện một cặp Khoản_mục – Giá_trị, đi kèm theo thông tin Năm và Mã_khoản_mục. Dạng dài giúp cho việc tổng hợp, phân nhóm và trực quan hóa dữ liệu bằng biểu đồ trở nên linh hoạt và hiệu quả hơn.
Tiếp theo, biến Nhóm được tạo mới nhằm phân loại các khoản mục theo nội dung kinh tế, bao gồm ba nhóm chính: Tài sản, Nguồn vốn, và Kết quả kinh doanh. Việc mã hóa này dựa trên từ khóa nhận dạng trong tên khoản mục (ví dụ: “Tiền”, “Phải thu”, “Vay”, “Lợi nhuận”), giúp phục vụ tốt hơn cho việc mô tả và phân tích xu hướng.
Cuối cùng, cột Năm được ép kiểu sang dạng số (numeric) để đảm bảo tính liên tục của dữ liệu thời gian, đồng thời hỗ trợ cho việc vẽ biểu đồ chuỗi thời gian và các phân tích định lượng sau này.
Nhìn chung, bước chuẩn hóa và mã hóa dữ liệu giúp tạo ra một tập dữ liệu có cấu trúc chặt chẽ, rõ ràng và phù hợp cho các bước thống kê mô tả và mô hình hóa hồi quy ở các chương tiếp theo.
# 10. Kiểm tra tổng giá trị tài sản mỗi năm để đảm bảo dữ liệu hợp lý
Tổng_ts <- data_long %>%
filter(Nhóm == "Tài sản") %>%
group_by(Năm) %>%
summarise(Tổng_tài_sản = sum(Giá_trị, na.rm = TRUE))
Tổng_ts %>%
flextable() %>%
set_caption("Tổng giá trị tài sản mỗi năm") %>%
autofit() %>%
theme_box()
Năm | Tổng_tài_sản |
|---|---|
2,015 | 91,461,987 |
2,016 | 140,207,392 |
2,017 | 160,609,118 |
2,018 | 210,068,390 |
2,019 | 289,125,132 |
2,020 | 314,386,478 |
2,021 | 313,308,392 |
2,022 | 395,436,846 |
2,023 | 407,403,256 |
2,024 | 457,450,882 |
Bảng trên trình bày tổng giá trị tài sản của doanh nghiệp qua từng năm, hàm filter() được sử dụng để lọc riêng nhóm “Tài sản”, sau đó group_by(Năm) giúp nhóm dữ liệu theo từng năm quan sát. Cuối cùng, summarise() tổng hợp giá trị bằng cách tính tổng tài sản từng năm, bỏ qua các giá trị thiếu (na.rm = TRUE). Sau khi lọc nhóm “Tài sản” và cộng tổng bằng summarise().
Kết quả cho thấy tài sản tăng đều từ 91.462 tỷ đồng năm 2015 lên 457.451 tỷ đồng năm 2024, phản ánh xu hướng mở rộng quy mô hoạt động.
Điều này chứng tỏ dữ liệu hợp lý, nhất quán và không có biến động bất thường, đủ tin cậy cho các bước phân tích tiếp theo.
# 11. Tính tỷ trọng từng khoản mục trong tổng tài sản mỗi năm
Tỷ_trọng_ts <- data_long %>%
filter(Nhóm == "Tài sản") %>%
group_by(Năm, Khoản_mục) %>%
summarise(Giá_trị = sum(Giá_trị, na.rm = TRUE), .groups = 'drop') %>%
group_by(Năm) %>%
mutate(Tỷ_trọng = Giá_trị / sum(Giá_trị) * 100) %>%
arrange(Năm, desc(Tỷ_trọng))
Tỷ_trọng_ts %>%
head(10) %>%
flextable() %>%
set_caption("Top 10 khoản mục có tỷ trọng lớn nhất") %>%
autofit() %>%
theme_box()
Năm | Khoản_mục | Giá_trị | Tỷ_trọng |
|---|---|---|---|
2,015 | Hàng tồn kho | 28,027,418 | 30.643788 |
2,015 | Tài sản cố định hữu hình | 19,114,346 | 20.898678 |
2,015 | Chi phí xây dựng cơ bản dở dang | 18,115,293 | 19.806363 |
2,015 | Bất động sản đầu tư | 16,827,664 | 18.398533 |
2,015 | Tiền và các khoản tương đương tiền | 6,938,465 | 7.586174 |
2,015 | Phải thu ngắn hạn của khách hàng | 2,438,800 | 2.666463 |
2,016 | Hàng tồn kho | 49,782,781 | 35.506531 |
2,016 | Chi phí xây dựng cơ bản dở dang | 33,991,567 | 24.243777 |
2,016 | Tài sản cố định hữu hình | 26,066,822 | 18.591617 |
2,016 | Bất động sản đầu tư | 17,362,127 | 12.383175 |
Bảng trên thể hiện tỷ trọng từng khoản mục trong tổng tài sản của doanh nghiệp theo từng năm. Dữ liệu được lọc theo nhóm “Tài sản” bằng filter(), sau đó nhóm lại theo group_by(Năm, Khoản_mục) để tính tổng giá trị từng khoản mục với summarise(). Tiếp đó, hàm mutate() được sử dụng để xác định tỷ trọng (%) của mỗi khoản mục trong tổng tài sản năm tương ứng.
Kết quả cho thấy “Hàng tồn kho”, “Tài sản cố định hữu hình” và “Chi phí xây dựng cơ bản dở dang” chiếm tỷ trọng lớn nhất trong cơ cấu tài sản, phản ánh đặc điểm của doanh nghiệp sản xuất – đầu tư xây dựng với phần lớn vốn tập trung vào tài sản hữu hình và dự án dở dang.
Việc phân tích tỷ trọng giúp hiểu rõ cơ cấu tài sản và xu hướng phân bổ nguồn lực, làm cơ sở cho các đánh giá về hiệu quả sử dụng vốn ở các phần sau.
# 12. Tính tốc độ tăng trưởng tổng tài sản theo năm
Tăng_trưởng_ts <- Tổng_ts %>%
mutate(Tăng_trưởng = (Tổng_tài_sản / lag(Tổng_tài_sản) - 1) * 100)
Tăng_trưởng_ts %>%
flextable() %>%
set_caption("Tốc độ tăng trưởng tổng tài sản hàng năm (%)") %>%
autofit() %>%
theme_box()
Năm | Tổng_tài_sản | Tăng_trưởng |
|---|---|---|
2,015 | 91,461,987 | |
2,016 | 140,207,392 | 53.2958085 |
2,017 | 160,609,118 | 14.5511058 |
2,018 | 210,068,390 | 30.7948094 |
2,019 | 289,125,132 | 37.6338115 |
2,020 | 314,386,478 | 8.7371671 |
2,021 | 313,308,392 | -0.3429174 |
2,022 | 395,436,846 | 26.2132953 |
2,023 | 407,403,256 | 3.0261242 |
2,024 | 457,450,882 | 12.2845425 |
Bảng trên trình bày tốc độ tăng trưởng tổng tài sản của doanh nghiệp qua các năm. Hàm lag() được sử dụng để lấy giá trị tài sản của năm liền trước, giúp tính mức thay đổi tương ứng cho từng năm.
Kết quả cho thấy tài sản tăng mạnh giai đoạn 2016–2019, đặc biệt năm 2016 đạt mức tăng hơn 53%, sau đó biến động chậm lại và có một năm giảm nhẹ (2021).
Nhìn chung, xu hướng tăng trưởng dương trong hầu hết các năm phản ánh sự mở rộng quy mô và tích lũy tài sản ổn định của doanh nghiệp, đồng thời khẳng định tính hợp lý và liên tục của chuỗi dữ liệu
# 13. Thống kê mô tả các khoản mục tài sản
thong_ke_mo_ta <- data_long %>%
filter(Nhóm == "Tài sản") %>%
group_by(Khoản_mục) %>%
summarise(
Trung_bình = mean(Giá_trị, na.rm = TRUE),
Median = median(Giá_trị, na.rm = TRUE),
Min = min(Giá_trị, na.rm = TRUE),
Max = max(Giá_trị, na.rm = TRUE),
Độ_lệch_chuẩn = sd(Giá_trị, na.rm = TRUE)
)
thong_ke_mo_ta %>%
flextable() %>%
set_caption("Thống kê mô tả các khoản mục tài sản") %>%
autofit() %>%
theme_box()
Khoản_mục | Trung_bình | Median | Min | Max | Độ_lệch_chuẩn |
|---|---|---|---|---|---|
Bất động sản đầu tư | 27,173,178 | 30,302,812 | 13,033,418 | 38,307,078 | 9,891,874 |
Chi phí xây dựng cơ bản dở dang | 66,001,043 | 63,225,371 | 18,115,293 | 113,358,029 | 32,297,534 |
Hàng tồn kho | 69,100,531 | 59,277,042 | 28,027,418 | 114,090,183 | 26,836,418 |
Phải thu ngắn hạn của khách hàng | 15,345,121 | 16,333,837 | 2,438,800 | 29,080,916 | 10,139,550 |
Tiền và các khoản tương đương tiền | 20,145,179 | 18,399,602 | 6,938,465 | 42,582,366 | 11,361,390 |
Tài sản cố định hữu hình | 80,180,736 | 94,821,632 | 19,114,346 | 145,305,970 | 44,719,237 |
Bảng trên trình bày thống kê mô tả cơ bản của các khoản mục tài sản, bao gồm giá trị trung bình, trung vị, nhỏ nhất, lớn nhất và độ lệch chuẩn. Các chỉ tiêu được tính bằng hàm summarise() sau khi nhóm dữ liệu theo từng khoản mục tài sản bằng group_by(Khoản_mục).
Kết quả cho thấy “Tài sản cố định hữu hình” và “Chi phí xây dựng cơ bản dở dang” có giá trị trung bình và độ biến động cao nhất, phản ánh đặc trưng đầu tư mạnh vào cơ sở vật chất và dự án dài hạn. Ngược lại, “Phải thu ngắn hạn” và “Tiền” có quy mô nhỏ hơn và ổn định hơn qua các năm.
Phân tích thống kê mô tả giúp nhận diện cấu trúc và mức độ biến động giữa các nhóm tài sản, từ đó hỗ trợ việc đánh giá hiệu quả sử dụng vốn và chiến lược đầu tư trong giai đoạn nghiên cứu.
# 14. Phân tích tương quan giữa các khoản mục chính
correlation_analysis <- data_analysis %>%
select(where(is.numeric)) %>%
cor(use = "complete.obs")
correlation_analysis %>%
as.data.frame() %>%
tibble::rownames_to_column(var = "Khoản_mục") %>%
flextable() %>%
set_caption("Ma trận tương quan giữa các khoản mục") %>%
autofit() %>%
theme_box()
Khoản_mục | Tiền và các khoản tương đương tiền | Phải thu ngắn hạn của khách hàng | Hàng tồn kho | Tài sản cố định hữu hình | Bất động sản đầu tư | Chi phí xây dựng cơ bản dở dang | Phải trả người bán ngắn hạn | Vay và nợ thuê tài chính dài hạn | Vốn góp của chủ sở hữu | Lợi nhuận sau thuế chưa phân phối |
|---|---|---|---|---|---|---|---|---|---|---|
Tiền và các khoản tương đương tiền | 1.0000000 | 0.8888599 | 0.8419813 | 0.9274071 | 0.2217029 | 0.8751629 | 0.9134635 | 0.9048011 | 0.7791184 | 0.8188309 |
Phải thu ngắn hạn của khách hàng | 0.8888599 | 1.0000000 | 0.8667315 | 0.9707210 | 0.4824710 | 0.9420725 | 0.9532413 | 0.9462226 | 0.9000064 | 0.6967462 |
Hàng tồn kho | 0.8419813 | 0.8667315 | 1.0000000 | 0.8102063 | 0.2400938 | 0.8217102 | 0.9202005 | 0.7915335 | 0.7649768 | 0.7808375 |
Tài sản cố định hữu hình | 0.9274071 | 0.9707210 | 0.8102063 | 1.0000000 | 0.4607927 | 0.9060209 | 0.9011168 | 0.9491000 | 0.9036343 | 0.6803025 |
Bất động sản đầu tư | 0.2217029 | 0.4824710 | 0.2400938 | 0.4607927 | 1.0000000 | 0.4028828 | 0.2883837 | 0.5049686 | 0.6176116 | -0.2670125 |
Chi phí xây dựng cơ bản dở dang | 0.8751629 | 0.9420725 | 0.8217102 | 0.9060209 | 0.4028828 | 1.0000000 | 0.9557506 | 0.9324875 | 0.9107417 | 0.7379034 |
Phải trả người bán ngắn hạn | 0.9134635 | 0.9532413 | 0.9202005 | 0.9011168 | 0.2883837 | 0.9557506 | 1.0000000 | 0.9024016 | 0.8279443 | 0.8371326 |
Vay và nợ thuê tài chính dài hạn | 0.9048011 | 0.9462226 | 0.7915335 | 0.9491000 | 0.5049686 | 0.9324875 | 0.9024016 | 1.0000000 | 0.9075287 | 0.6498572 |
Vốn góp của chủ sở hữu | 0.7791184 | 0.9000064 | 0.7649768 | 0.9036343 | 0.6176116 | 0.9107417 | 0.8279443 | 0.9075287 | 1.0000000 | 0.5087493 |
Lợi nhuận sau thuế chưa phân phối | 0.8188309 | 0.6967462 | 0.7808375 | 0.6803025 | -0.2670125 | 0.7379034 | 0.8371326 | 0.6498572 | 0.5087493 | 1.0000000 |
Bảng trên được tạo bằng hàm cor() trên các biến numeric trong data_analysis, đo mức độ tương quan tuyến tính giữa từng cặp khoản mục. Tham số use = “complete.obs” đảm bảo bỏ qua các giá trị thiếu nếu có.
Kết quả:
Hầu hết các khoản mục tài sản và nguồn vốn có tương quan dương mạnh, đặc biệt giữa Tiền, Phải thu, Tài sản cố định và Chi phí xây dựng cơ bản dở dang, phản ánh sự phát triển đồng bộ của tài sản và vốn.
Một số khoản như Bất động sản đầu tư có tương quan thấp hơn, cho thấy tính riêng biệt trong cơ cấu đầu tư.
Phân tích tương quan giúp nhận diện mối quan hệ tiềm ẩn và hỗ trợ lựa chọn biến trong các mô hình hồi quy hoặc đánh giá rủi ro tài chính ở các phân tích tiếp theo.
# 15. Phân tích biến động theo thời gian - tính độ lệch chuẩn của từng khoản mục
Biến_động <- data_long %>%
group_by(Khoản_mục) %>%
summarise(
Độ_biến_động = sd(Giá_trị, na.rm = TRUE) / mean(Giá_trị, na.rm = TRUE) * 100
) %>%
arrange(desc(Độ_biến_động))
Biến_động %>%
head(10) %>%
flextable() %>%
set_caption("Top 10 khoản mục có độ biến động cao nhất") %>%
autofit() %>%
theme_box()
Khoản_mục | Độ_biến_động |
|---|---|
Lợi nhuận sau thuế chưa phân phối | 132.44166 |
Phải trả người bán ngắn hạn | 66.89025 |
Phải thu ngắn hạn của khách hàng | 66.07671 |
Tiền và các khoản tương đương tiền | 56.39757 |
Tài sản cố định hữu hình | 55.77304 |
Chi phí xây dựng cơ bản dở dang | 48.93488 |
Vay và nợ thuê tài chính dài hạn | 46.39915 |
Hàng tồn kho | 38.83677 |
Bất động sản đầu tư | 36.40308 |
Vốn góp của chủ sở hữu | 21.02406 |
Bảng trên thể hiện độ biến động theo thời gian của từng khoản mục, được tính bằng hệ số biến động (CV) trong đó sd() tính độ lệch chuẩn và mean() tính giá trị trung bình. Hàm group_by(Khoản_mục) và summarise() giúp thực hiện tính toán cho từng khoản mục riêng biệt.
Kết quả cho thấy “Lợi nhuận sau thuế chưa phân phối” có độ biến động cao nhất (132%), tiếp theo là các khoản Phải trả người bán, Phải thu ngắn hạn, Tiền và Tài sản cố định hữu hình, phản ánh tính biến động lớn của dòng tiền và nợ ngắn hạn, trong khi các khoản đầu tư dài hạn như Bất động sản đầu tư hay Vốn góp của chủ sở hữu có độ biến động thấp hơn.
Phân tích này giúp đánh giá mức độ ổn định từng khoản mục theo thời gian, hỗ trợ việc lập kế hoạch tài chính và quản lý rủi ro trong các giai đoạn tiếp theo.
# Kiểm tra nhanh cấu trúc sau xử lý
options(scipen = 999)
head(data_long)
Chủ đề
Đánh giá cơ cấu tài sản và biến động tài chính của doanh nghiệp qua thời gian, từ 2015 đến 2024.
Mục tiêu
Phân tích cách tài sản của Vingroup thay đổi theo từng năm, xác định các khoản mục chiếm tỷ trọng lớn, đồng thời đánh giá mức độ biến động và ổn định của từng khoản mục nhằm cung cấp cơ sở cho các nhận định về hiệu quả sử dụng vốn và chiến lược tài chính.
# 1. Thống kê số lượng quan sát
nrow(data_long)
[1] 100
# 2. Số khoản mục duy nhất mỗi năm
data_long %>%
group_by(Năm) %>%
summarise(Số_khoản_mục = n_distinct(Khoản_mục))
# 3. Số năm trong dữ liệu
length(unique(data_long$Năm))
[1] 10
# 4. Số khoản mục tài chính
length(unique(data_long$Khoản_mục))
[1] 10
cat("TỔNG QUAN DỮ LIỆU PHÂN TÍCH\n", "- Tổng số quan sát:", nrow(data_long), "dòng dữ liệu\n", "- Thời gian:", length(unique(data_long$Năm)), "năm (2015-2024)\n", "- Số khoản mục:", length(unique(data_long$Khoản_mục)), "chỉ tiêu tài chính\n")
TỔNG QUAN DỮ LIỆU PHÂN TÍCH
- Tổng số quan sát: 100 dòng dữ liệu
- Thời gian: 10 năm (2015-2024)
- Số khoản mục: 10 chỉ tiêu tài chính
# 5. Tổng giá trị mỗi năm
Tổng_nam <- data_long %>%
group_by(Năm) %>%
summarise(Tổng_giá_trị = sum(Giá_trị, na.rm = TRUE))
print(Tổng_nam)
# A tibble: 10 × 2
Năm Tổng_giá_trị
<dbl> <dbl>
1 2015 149446716.
2 2016 208153798.
3 2017 232034269.
4 2018 324464696.
5 2019 428548208
6 2020 470014300
7 2021 478361762
8 2022 604815737
9 2023 592187236
10 2024 714782035
# 6. Trung bình giá trị mỗi khoản mục theo năm
trung_binh_nam <- data_long %>%
group_by(Năm) %>%
summarise(Trung_bình = mean(Giá_trị, na.rm = TRUE))
print(trung_binh_nam)
# A tibble: 10 × 2
Năm Trung_bình
<dbl> <dbl>
1 2015 14944672.
2 2016 20815380.
3 2017 23203427.
4 2018 32446470.
5 2019 42854821.
6 2020 47001430
7 2021 47836176.
8 2022 60481574.
9 2023 59218724.
10 2024 71478204.
# 7. Năm có tổng giá trị cao nhất
nam_cao_nhat <- data_long %>%
group_by(Năm) %>%
summarise(Tổng = sum(Giá_trị, na.rm = TRUE)) %>%
slice_max(Tổng, n = 1)
print(nam_cao_nhat)
# A tibble: 1 × 2
Năm Tổng
<dbl> <dbl>
1 2024 714782035
# Tổng hợp kết quả
cat("\nQUY MÔ TỔNG THỂ\n", "- Quy mô trung bình:", round(mean(Tổng_nam$Tổng_giá_trị), 0), "triệu VND/năm\n", "- Năm có quy mô lớn nhất:", nam_cao_nhat$Năm, "(", round(nam_cao_nhat$Tổng), "triệu VND)\n", "- Giá trị trung bình/khoản mục:", round(mean(trung_binh_nam$Trung_bình), 0), "triệu VND\n")
QUY MÔ TỔNG THỂ
- Quy mô trung bình: 420280876 triệu VND/năm
- Năm có quy mô lớn nhất: 2024 ( 714782035 triệu VND)
- Giá trị trung bình/khoản mục: 42028088 triệu VND
# Biểu đồ 1: Biểu đồ đường thể hiện xu hướng tổng giá trị tài sản của Vingroup giai đoạn 2015–2024.
library(ggrepel)
data_long %>%
filter(Nhóm == "Tài sản") %>%
group_by(Năm) %>%
summarise(Tổng_giá_trị = sum(Giá_trị, na.rm = TRUE)) %>%
ggplot(aes(Năm, Tổng_giá_trị, label = format(Tổng_giá_trị, big.mark = ".", scientific = FALSE))) +
geom_line(color = "steelblue", size = 1) +
geom_point(color = "darkred", size = 3) +
geom_text_repel(size = 3.5, nudge_y = 3e6, max.overlaps = Inf) +
labs(
title = "Tổng giá trị tài sản của Vingroup (2015–2024)",
x = "Năm", y = "Giá trị (triệu VND)"
) +
scale_x_continuous(breaks = 2015:2024, expand = expansion(mult = c(0.03, 0.05))) +
theme_minimal(base_size = 14) +
theme(
plot.title = element_text(face = "bold", hjust = 0.5, size = 18),
axis.title.x = element_text(face = "italic")
)
Biểu đồ đường được xây dựng bằng thư viện ggplot2 kết hợp ggrepel để thể hiện biến động tổng giá trị tài sản của Vingroup trong giai đoạn 2015–2024. Dữ liệu được xử lý bằng cách lọc nhóm “Tài sản”, tổng hợp theo năm và tính tổng giá trị. Đường đồ thị (geom_line) kết nối các điểm dữ liệu theo thời gian, trong khi geom_point nhấn mạnh từng mốc giá trị cụ thể. Thư viện ggrepel được sử dụng để hiển thị nhãn giá trị mà không bị chồng chéo.
Kết quả:
Xu hướng tăng trưởng mạnh mẽ của tổng tài sản Vingroup từ 91,462 tỷ đồng (2015) lên 395,437 tỷ đồng (2021), sau đó ổn định ở mức trên 300,000 tỷ đồng trong các năm tiếp theo. Đỉnh điểm vào năm 2021 phản ánh giai đoạn phát triển vượt bậc của tập đoàn, trong khi sự điều chỉnh giảm nhẹ những năm sau đó cho thấy sự ổn định và tái cấu trúc trong chiến lược phát triển. Biểu đồ cung cấp cái nhìn tổng quan về quy mô và tốc độ tăng trưởng của một trong những tập đoàn tư nhân hàng đầu Việt Nam.
# 7. Khoản mục lớn nhất mỗi năm
data_long %>%
group_by(Năm) %>%
slice_max(Giá_trị, n = 1)
# 8. Khoản mục nhỏ nhất mỗi năm
data_long %>%
group_by(Năm) %>%
slice_min(Giá_trị, n = 1)
# 9. Trung bình giá trị theo khoản mục
data_long %>%
group_by(Khoản_mục) %>%
summarise(Giá_trị_trung_bình = mean(Giá_trị, na.rm = TRUE)) %>%
arrange(desc(Giá_trị_trung_bình))
Phân tích cơ cấu tài sản và nguồn vốn của Vingroup dựa trên giá trị trung bình và thứ tự các khoản mục từ 2015–2024 cho thấy: Tài sản cố định hữu hình là khoản mục có giá trị trung bình cao nhất (80.180 tỷ đồng), chiếm tỷ trọng chủ đạo, trong khi Vay và nợ thuê tài chính dài hạn (79.090 tỷ đồng) phản ánh cơ cấu tài trợ chủ yếu từ nợ vay. Tài sản cố định hữu hình duy trì vị trí lớn nhất từ 2019–2024, thể hiện chiến lược đầu tư dài hạn, còn Lợi nhuận sau thuế chưa phân phối luôn là khoản mục nhỏ nhất, phản ánh chính sách phân phối lợi nhuận tích cực. Xu hướng chuyển dịch từ Hàng tồn kho (2016–2017) sang Tài sản cố định hữu hình (2019 trở đi) và sự xuất hiện của Bất động sản đầu tư nhỏ nhất năm 2024 cho thấy sự điều chỉnh chiến lược đầu tư và cơ cấu tài sản của tập đoàn.
# Biểu đồ 2: Cơ cấu tài sản năm 2024
data_long %>%
filter(Năm == 2024, Nhóm == "Tài sản") %>%
mutate(Tỷ_trọng = Giá_trị / sum(Giá_trị) * 100) %>%
ggplot(aes("", Tỷ_trọng, fill = Khoản_mục)) +
geom_bar(stat = "identity", width = 1, color = "white") +
coord_polar("y") +
geom_text(aes(label = paste0(round(Tỷ_trọng, 1), "%")),
position = position_stack(vjust = 0.5), size = 3) +
labs(title = "Cơ cấu tài sản năm 2024", fill = "Khoản mục") +
scale_fill_brewer(palette = "Paired") +
theme_void()
Biểu đồ tròn được tạo bằng ggplot2, với dữ liệu lọc nhóm Tài sản năm 2024 và tính tỷ trọng (%) của từng khoản mục so với tổng tài sản bằng hàm (mutate(Tỷ_trọng = Giá_trị / sum(Giá_trị) * 100)). Các hàm geom_bar(stat=“identity”) và coord_polar(“y”) chuyển biểu đồ cột thành tròn, trong khi geom_text() hiển thị nhãn phần trăm.
Kết quả cho thấy cơ cấu tài sản năm 2024 tập trung vào ba khoản mục chính: tài sản cố định hữu hình (31,8%), Hàng tồn kho (24,9%) và Chi phí xây dựng cơ bản dở dang (24,8%), phản ánh đầu tư dài hạn và quy mô hoạt động sản xuất. Các khoản thanh khoản cao như Tiền và các khoản tương đương tiền chỉ chiếm 6,4%, thể hiện tối ưu hóa sử dụng vốn. Cơ cấu này phù hợp với mô hình kinh doanh đa ngành của Vingroup, trọng tâm là bất động sản và công nghiệp.
# Biểu đồ 3: Tỷ trọng tài sản qua các năm
data_long %>%
filter(Nhóm == "Tài sản") %>%
ggplot(aes(Năm, Giá_trị, fill = Khoản_mục)) +
geom_col(position = "fill") +
scale_y_continuous(labels = scales::percent) + scale_x_continuous(breaks = 2015:2024) + labs(title = "Tỷ trọng các khoản mục tài sản (2015–2024)", x = "Năm", y = "Tỷ lệ (%)", fill = "Khoản mục") + theme_minimal()
Biểu đồ được tạo bằng ggplot2, với dữ liệu lọc nhóm Tài sản và hàm geom_col(position = “fill”) chuẩn hóa chiều cao cột thành 100%, hiển thị tỷ trọng tương đối từng khoản mục theo năm. Các hàm scale_y_continuous(labels = scales::percent) và scale_x_continuous(breaks = 2015:2024) giúp định dạng trục dễ đọc.
Kết quả cho thấy:
Tài sản cố định hữu hình tăng mạnh từ ~20% (2015) lên gần 40% (2024)
Hàng tồn kho giảm tỷ trọng sau 2018
Chi phí xây dựng cơ bản dở dang ổn định ở mức 20–30%
Các khoản thanh khoản như Tiền và tương đương tiền duy trì tỷ trọng thấp
Xu hướng này phản ánh chiến lược đầu tư dài hạn và tập trung vào tài sản cố định, phù hợp với định hướng phát triển bất động sản và hạ tầng quy mô lớn của Vingroup.
# 10. So sánh quy mô giữa các nhóm lớn
data_long %>%
group_by(Nhóm) %>%
summarise(Tổng = sum(Giá_trị, na.rm = TRUE),
Trung_bình = mean(Giá_trị, na.rm = TRUE))
Bảng trên so sánh quy mô tổng và trung bình giữa ba nhóm chính: Tài sản, Nguồn vốn và Kết quả kinh doanh. Hàm group_by(Nhóm) và summarise() được sử dụng để tổng hợp giá trị theo nhóm, với sum() tính tổng và mean() tính giá trị trung bình từng nhóm, bỏ qua giá trị thiếu (na.rm = TRUE).
Kết quả cho thấy Tài sản có quy mô lớn nhất (tổng 2.779.458.873 triệu đồng), tiếp theo là Nguồn vốn (1.325.009.829 triệu đồng), trong khi Kết quả kinh doanh có quy mô nhỏ hơn nhiều (98.341.054 triệu đồng). Điều này phản ánh cơ cấu tài chính tập trung vào tài sản và vốn, trong khi kết quả kinh doanh chiếm tỷ trọng thấp hơn so với quy mô tổng tài sản.
# 11. Tổng giá trị từng nhóm theo năm
data_long %>%
group_by(Năm, Nhóm) %>%
summarise(Tổng_Giá_trị = sum(Giá_trị, na.rm = TRUE)) %>%
arrange(Năm)
# Biểu đồ 4: So sánh tài sản và nguồn vốn qua năm
data_long %>%
filter(Nhóm %in% c("Tài sản", "Nguồn vốn")) %>%
group_by(Năm, Nhóm) %>%
summarise(Giá_trị = sum(Giá_trị, na.rm = TRUE), .groups = "drop") %>%
ggplot(aes(factor(Năm), Giá_trị, fill = Nhóm)) +
geom_col(position = "dodge", width = 0.7) +
geom_text(
aes(label = format(Giá_trị, big.mark = ".", scientific = FALSE)),
position = position_dodge(width = 0.7),
angle = 90, size = 5, color = "black", vjust = 0.5, hjust = 1
) +
geom_hline(yintercept = 0, color = "black") +
labs(
title = "So sánh tổng tài sản và nguồn vốn qua năm",
x = "Năm", y = "Triệu VND", fill = "Nhóm"
) +
scale_fill_brewer(palette = "Set2") +
theme_light(base_size = 14) +
coord_cartesian(clip = "off") +
theme(
plot.title = element_text(face = "bold", hjust = 0.5, size = 18),
axis.title.x = element_text(face = "italic", size = 14),
axis.title.y = element_text(size = 14)
)
Biểu đồ được tạo bằng ggplot2, với dữ liệu lọc hai nhóm tài sản và nguồn vốn, hàm (group_by(Năm, Nhóm) %>% summarise(Giá_trị = sum(Giá_trị))) tổng hợp giá trị theo năm kết hợp với hàm (geom_col(position = “dodge”)) hiển thị cột xếp song song, kèm nhãn giá trị (geom_text(position_dodge())). Màu sắc được phân biệt bằng scale_fill_brewer(palette = “Set2”).
Kết quả cho thấy tổng Tài sản và tổng Nguồn vốn luôn tương ứng nhau theo từng năm, phản ánh nguyên tắc kế toán cơ bản: Tổng tài sản ≈ Tổng nguồn vốn. Quy mô tập đoàn tăng mạnh từ ~90 tỷ (2015) lên ~457 tỷ (2021), sau đó duy trì ở mức cao ~400–450 tỷ (2022–2024).
Xu hướng này cho thấy sự mở rộng quy mô liên tục, cấu trúc tài chính ổn định, và chiến lược phát triển dài hạn được duy trì trong suốt 10 năm.
# Biểu đồ 5: Phân tách biến động tài sản và nguồn vốn
data_long %>%
filter(Nhóm %in% c("Tài sản", "Nguồn vốn")) %>%
group_by(Năm, Nhóm, Khoản_mục) %>%
summarise(Giá_trị = sum(Giá_trị), .groups = "drop") %>%
ggplot(aes(factor(Năm), Giá_trị, fill = Khoản_mục)) +
geom_col(width = 0.6) +
facet_wrap(~Nhóm, scales = "free_y") +
labs(
title = "Phân tách biến động tài sản và nguồn vốn",
x = "Năm", y = "Giá trị (triệu VND)", fill = "Khoản mục"
) +
scale_fill_brewer(palette = "Paired") +
theme_light(base_size = 16) +
theme(
plot.title = element_text(face = "bold", hjust = 0.5, size = 18),
axis.text.x = element_text(angle = 45, hjust = 1, size = 12),
panel.grid.minor = element_blank()
)
Biểu đồ sử dụng ggplot2 với facet_wrap(~Nhóm, scales = “free_y”) để tách riêng hai nhóm Tài sản và Nguồn vốn, kèm geom_col() hiển thị tổng giá trị và cơ cấu từng khoản mục. Màu sắc được phân biệt bằng scale_fill_brewer(palette = “Paired”).
Kết quả cho thấy:
Tài sản: Tài sản cố định hữu hình tăng mạnh từ 2019, Hàng tồn kho và Chi phí XDCB dở dang duy trì ở mức cao, Bất động sản đầu tư ổn định nhưng chiếm tỷ trọng nhỏ.
Nguồn vốn: Nợ vay dài hạn là nguồn tài trợ chủ yếu, tăng nhanh; vốn góp tăng đều nhưng chậm hơn; phải trả người bán ngắn hạn chiếm tỷ trọng đáng kể.
Nhận định: Cơ cấu tài chính của Vingroup nghiêng về sử dụng đòn bẩy cao, với nợ vay dài hạn làm nguồn chính cho đầu tư tài sản cố định, phù hợp với mô hình kinh doanh bất động sản và hạ tầng vốn lớn.
# 12. Độ lệch chuẩn giá trị theo năm
data_long %>%
group_by(Năm) %>%
summarise(Độ_lệch_chuẩn = sd(Giá_trị, na.rm = TRUE))
Bảng trên trình bày độ lệch chuẩn của các khoản mục theo từng năm, được tính bằng group_by(Năm) %>% summarise(Độ_lệch_chuẩn = sd(Giá_trị, na.rm = TRUE)). Hàm sd() đo mức độ phân tán của các khoản mục so với giá trị trung bình, giúp đánh giá biến động tổng thể trong cơ cấu tài chính.
Kết quả cho thấy độ biến động tăng dần qua các năm, từ khoảng 10,8 triệu (2015) lên 48,2 triệu (2024), phản ánh sự mở rộng quy mô tài sản và nguồn vốn đồng thời gia tăng khối lượng giao dịch và biến động trong các khoản mục tài chính của Vingroup theo thời gian.
# 13. Hệ số biến thiên (CV) của từng khoản mục
data_long %>%
group_by(Khoản_mục) %>%
summarise(Hệ_số_biến_thiên = sd(Giá_trị, na.rm = TRUE) / mean(Giá_trị, na.rm = TRUE))
Bảng trên trình bày hệ số biến thiên của từng khoản mục, được tính bằng sd(Giá_trị, na.rm = TRUE) / mean(Giá_trị, na.rm = TRUE). Hệ số biến thiên đo mức độ biến động tương đối, cho phép so sánh biến động giữa các khoản mục có quy mô giá trị khác nhau.
Kết quả cho thấy:
Lợi nhuận sau thuế chưa phân phối có CV cao nhất (1,32), phản ánh biến động mạnh qua các năm;
Phải thu và phải trả ngắn hạn cũng có biến động đáng kể (CV ~0,66);
Vốn góp của chủ sở hữu có CV thấp nhất (0,21), chứng tỏ mức ổn định cao
Các khoản mục như Tài sản cố định, Chi phí XDCB dở dang, Hàng tồn kho, Tiền mặt có biến động trung bình, phản ánh sự thay đổi vừa phải theo quy mô hoạt động và chiến lược đầu tư của tập đoàn.
Hệ số biến thiên cung cấp cái nhìn tương đối về rủi ro và ổn định tài chính của từng khoản mục trong cơ cấu tài sản và nguồn vốn.
# 14. Khoản mục biến động mạnh nhất
data_long %>%
group_by(Khoản_mục) %>%
summarise(Biến_động = sd(Giá_trị, na.rm = TRUE)) %>%
arrange(desc(Biến_động)) %>%
slice_head(n = 1)
# 15. Khoản mục ổn định nhất
data_long %>%
group_by(Khoản_mục) %>%
summarise(Biến_động = sd(Giá_trị, na.rm = TRUE)) %>%
arrange(Biến_động) %>%
slice_head(n = 1)
Phân tích biến động tuyệt đối của từng khoản mục được thực hiện bằng hàm sd(Giá_trị, na.rm = TRUE), giúp xác định khoản mục nào có dao động lớn nhất và nhỏ nhất trong giai đoạn 2015–2024.
Kết quả cho thấy:
Tài sản cố định hữu hình là khoản mục biến động mạnh nhất với độ lệch chuẩn 44,7 triệu, phản ánh quy mô đầu tư lớn và thay đổi liên tục theo chiến lược mở rộng dự án bất động sản và cơ sở hạ tầng.
Ngược lại, Vốn góp của chủ sở hữu là khoản mục ổn định nhất với độ lệch chuẩn 6,9 triệu, cho thấy nguồn vốn chủ sở hữu duy trì ổn định, ít chịu biến động so với các khoản đầu tư và nợ vay dài hạn.
Nhận định này giúp hiểu rõ rủi ro và tính ổn định trong cơ cấu tài chính của Vingroup, phục vụ cho các phân tích chiến lược đầu tư và quản lý tài chính.
# Biểu đồ 6. So sánh phân phối giá trị giữa các nhóm
ggplot(data_long, aes(x = Nhóm, y = Giá_trị, fill = Nhóm)) +
geom_boxplot(alpha = 0.6, outlier.color = "red") +
scale_y_log10() +
labs(title = "Phân phối giá trị theo nhóm tài chính",
y = "Giá trị (log scale)", x = "Nhóm") +
theme_bw()
Biểu đồ được xây dựng bằng boxplot trên thang logarit để so sánh phân phối giá trị giữa các nhóm tài chính: Tài sản, Nguồn vốn và Kết quả kinh doanh. Cụ thể:
geom_boxplot() hiển thị 5 số tổng hợp và các giá trị ngoại lai (màu đỏ).
scale_y_log10() chuyển trục tung sang thang logarit, giúp so sánh các giá trị chênh lệch lớn.
Kết quả:
Tài sản có phân phối giá trị cao nhất, trung vị khoảng 100 triệu, phản ánh quy mô tài sản lớn và trải rộng giữa các khoản mục.
Nguồn vốn tương ứng với Tài sản, phù hợp với nguyên tắc cân đối kế toán.
Kết quả kinh doanh có phân phối thấp hơn, trung vị khoảng 1 triệu, cho thấy lợi nhuận khiêm tốn so với quy mô tài sản.
Các nhóm Tài sản và Nguồn vốn có nhiều giá trị ngoại lai, trong khi Kết quả kinh doanh tập trung hơn, thể hiện tính ổn định tương đối.
Nhận định: Biểu đồ cho thấy sự chênh lệch rõ rệt về quy mô giá trị giữa các nhóm, với Tài sản và Nguồn vốn chiếm ưu thế, còn Kết quả kinh doanh có giá trị nhỏ hơn nhiều, phản ánh đặc thù tài chính của Vingroup.
# Biểu đồ 7. Biểu diễn độ dao động của từng khoản mục
ggplot(data_long, aes(x = Khoản_mục, y = Giá_trị, fill = Nhóm)) +
geom_violin(trim = FALSE, alpha = 0.7) +
geom_boxplot(width = 0.1, color = "black", alpha = 0.3) +
coord_flip() +
labs(title = "Độ biến động giá trị các khoản mục trong giai đoạn 2015–2024",
y = "Giá trị (triệu VND)", x = "Khoản mục") +
theme_minimal()
Biểu đồ kết hợp violin plot và boxplot để minh họa độ biến động giá trị các khoản mục qua các năm:
geom_violin(trim = FALSE, alpha = 0.7) hiển thị hình dạng phân phối, giữ nguyên các “đuôi” dữ liệu.
geom_boxplot(width = 0.1, color = “black”, alpha = 0.3) thêm các thống kê tóm tắt (Q1, median, Q3) bên trong violin.
coord_flip() lật trục để dễ đọc tên các khoản mục.
Màu sắc phân biệt theo nhóm: Tài sản, Nguồn vốn, Kết quả kinh doanh.
Kết quả:
Biến động cao: “Vay và nợ thuê tài chính dài hạn”, “Tài sản cố định hữu hình”, “Hàng tồn kho”, “Chi phí XDCB dở dang” – phản ánh các đợt đầu tư lớn và chu kỳ dự án không đều.
Ổn định: “Vốn góp chủ sở hữu”, “Lợi nhuận sau thuế chưa phân phối”, các khoản mục ngắn hạn – biến động thấp, tăng trưởng đều.
Nhận định: Các khoản mục dài hạn có xu hướng biến động mạnh hơn các khoản mục ngắn hạn và vốn chủ sở hữu, phản ánh đặc thù tài chính và chiến lược đầu tư dài hạn của Vingroup.
# 16. Tốc độ tăng trưởng tổng giá trị qua các năm
Tổng_ts <- data_long %>%
group_by(Năm) %>%
summarise(Tổng = sum(Giá_trị, na.rm = TRUE)) %>%
arrange(Năm) %>%
mutate(Tăng_trưởng = (Tổng - lag(Tổng)) / lag(Tổng) * 100)
Tổng_ts
# 17. Năm tăng trưởng cao nhất
Tổng_ts %>%
slice_max(Tăng_trưởng, n = 1)
# 18. Tốc độ tăng trưởng trung bình toàn kỳ
mean(Tổng_ts$Tăng_trưởng, na.rm = TRUE)
[1] 19.90773
Dữ liệu tổng giá trị các khoản mục được nhóm theo năm và tính tổng bằng summarise().
Kết quả:
Năm 2018 có tốc độ tăng trưởng cao nhất 39.83%, phản ánh giai đoạn mở rộng mạnh mẽ.
Tăng trưởng trung bình toàn kỳ đạt 19.91%, cho thấy quy mô tài sản và nguồn vốn của Vingroup tăng ổn định qua 10 năm.
Một số năm tăng trưởng thấp hoặc âm (ví dụ 2023: -2.09%) phản ánh điều chỉnh trong cơ cấu tài chính hoặc biến động thị trường.
Nhận xét: Biểu đồ tốc độ tăng trưởng cung cấp cái nhìn tổng quan về mức độ mở rộng và biến động của tổng giá trị tài chính theo thời gian, hỗ trợ đánh giá hiệu quả chiến lược đầu tư và quản lý tài chính của tập đoàn.
# Biểu đồ 8. Xu hướng tài sản và điểm cực đại giá trị
data_ts <- data_long %>% filter(Nhóm == "Tài sản")
max_point <- slice_max(data_ts, Giá_trị, n = 1)
ggplot(data_ts, aes(Năm, Giá_trị)) +
geom_line(color = "darkblue", linewidth = 1) +
geom_point(color = "orange", size = 3) +
geom_vline(xintercept = max_point$Năm, linetype = "dashed", color = "gray60") +
geom_label_repel(
data = max_point,
aes(label = paste0("Giá trị cao nhất\n", round(Giá_trị, 0))),
nudge_x = -0.5, nudge_y = max_point$Giá_trị * 0.03,
size = 4.5, segment.color = "gray60",
box.padding = 0.5, point.padding = 0.3, force = 5
) +
labs(
title = "Xu hướng tài sản và điểm cực đại giá trị",
x = "Năm", y = "Giá trị (triệu VND)"
) +
scale_x_continuous(breaks = seq(min(data_ts$Năm), max(data_ts$Năm), 1)) +
coord_cartesian(clip = "off") +
theme_minimal(base_size = 14) +
theme(
plot.title = element_text(face = "bold", hjust = 0.5, size = 18),
axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1),
plot.margin = unit(c(1, 2, 1, 1), "cm")
)
Dữ liệu được vẽ theo chuỗi thời gian với geom_line() thể hiện xu hướng chung, geom_point() đánh dấu giá trị từng năm, geom_vline() chỉ ra năm có giá trị cực đại, và geom_label_repel() hiển thị nhãn thông minh cho giá trị cao nhất, slice_max() xác định điểm cực đại trong toàn bộ giai đoạn.
Kết quả:
2015–2018: Tăng trưởng ổn định (~90 - 210 tỷ)
2019–2021: Tăng mạnh, đạt đỉnh 457 tỷ vào 2021
2022–2024: Ổn định ở mức cao (~400 - 450 tỷ)
Điểm đáng chú ý: Tài sản cố định hữu hình năm 2024 đạt giá trị lớn nhất, phản ánh tập trung đầu tư dài hạn
Nhận xét: Biểu đồ minh họa rõ quá trình mở rộng quy mô và đầu tư dài hạn của Vingroup, cho thấy chiến lược phát triển bền vững và vị thế dẫn đầu ngành bất động sản – công nghiệp tại Việt Nam.
# 19. Mối tương quan giữa giá trị và năm
cor(data_long$Giá_trị, data_long$Năm)
[1] 0.5016219
Hệ số tương quan giữa Giá_trị và Năm là 0.502, cho thấy có mối quan hệ dương vừa phải. Điều này nghĩa là tổng giá trị các khoản mục tài chính của Vingroup tăng theo thời gian, phản ánh xu hướng mở rộng quy mô hoạt động và gia tăng đầu tư trong giai đoạn 2015–2024.
# Biểu đồ 9. Xu hướng tích lũy của từng khoản mục tài sản
ggplot(filter(data_long, Nhóm == "Tài sản"),
aes(x = Năm, y = Giá_trị, fill = Khoản_mục)) +
geom_area(alpha = 0.8) +
geom_line(color = "white", linewidth = 0.3) +
geom_vline(xintercept = 2020, linetype = "dashed", color = "gray40") +
labs(
title = "Diễn biến cơ cấu tài sản tích lũy (2015–2024)",
x = "Năm", y = "Giá trị (triệu VND)", fill = "Khoản mục"
) +
scale_x_continuous(breaks = 2015:2024) +
theme_minimal()
Biểu đồ sử dụng kỹ thuật area chart với geom_area() để thể hiện xu hướng tích lũy các khoản mục tài sản qua từng năm. Các đường viền trắng (geom_line()) giúp phân biệt rõ ranh giới giữa các thành phần, trong khi đường đứt nét tại năm 2020 (geom_vline()) đánh dấu mốc quan trọng trong quá trình phát triển.
Kết quả:
Tổng tài sản tăng mạnh từ ~90 tỷ (2015) lên ~450 tỷ (2024), tăng tốc rõ rệt từ 2020.
Tài sản cố định hữu hình tăng tỷ trọng liên tục, trở thành thành phần chủ đạo.
Hàng tồn kho giảm nhẹ tỷ trọng, chi phí XDCB dở dang duy trì mức cao.
Các khoản mục khác ổn định và nhỏ.
Nhận định: Biểu đồ cho thấy Vingroup tập trung vào đầu tư tài sản cố định, phản ánh chiến lược mở rộng quy mô dài hạn trong bất động sản và hạ tầng.
# Biểu đồ 10. Biểu đồ đường thể hiện xu hướng biến động giá trị theo từng nhóm (2015–2024)
ggplot(data_long, aes(Năm, Giá_trị, color = Khoản_mục)) +
geom_line(linewidth = 1) +
geom_point(size = 2) +
facet_wrap(~Nhóm, scales = "free_y") +
labs(
title = "Xu hướng biến động theo từng nhóm",
x = "Năm", y = "Giá trị (triệu VND)"
) +
scale_x_continuous(breaks = 2015:2024) +
theme_light(base_size = 14) +
theme(
plot.title = element_text(size = 18, face = "bold", hjust = 0.5),
axis.title = element_text(size = 14),
axis.text = element_text(size = 11),
legend.title = element_text(size = 13, face = "bold"),
legend.text = element_text(size = 11),
strip.text = element_text(size = 14, face = "bold"),
legend.position = "right"
)
Biểu đồ sử dụng kỹ thuật facet_wrap() để tách thành ba biểu đồ con theo nhóm Tài sản, Nguồn vốn và Kết quả kinh doanh, cho phép so sánh đồng thời xu hướng của từng khoản mục trong mỗi nhóm. Các đường màu (geom_line()) và điểm đánh dấu (geom_point()) thể hiện diễn biến giá trị qua các năm.
Kết quả cho thấy sự tăng trưởng đồng bộ giữa Tài sản và Nguồn vốn, trong đó các khoản mục chính như “Tài sản cố định hữu hình” và “Vay và nợ thuê tài chính dài hạn” có xu hướng tăng mạnh, phản ánh chiến lược mở rộng quy mô thông qua đòn bẩy tài chính. Ngược lại, nhóm Kết quả kinh doanh có biến động nhẹ và quy mô khiêm tốn hơn hẳn. Biểu đồ cung cấp cái nhìn toàn diện về sự vận động của các chỉ số tài chính trọng yếu qua từng giai đoạn phát triển của tập đoàn.
# 20. Phân tích cụm tương quan
library(cluster)
# Chuẩn bị dữ liệu cho phân tích cụm
cor_data <- data_long %>%
select(Năm, Khoản_mục, Giá_trị) %>%
pivot_wider(names_from = Khoản_mục, values_from = Giá_trị) %>%
select(-Năm) %>%
select(1:min(10, ncol(.))) # Lấy 10 khoản mục đầu
# Phân cụm K-means
set.seed(123)
kmeans_result <- kmeans(scale(cor_data), centers = 3)
kmeans_result
K-means clustering with 3 clusters of sizes 4, 3, 3
Cluster means:
Tiền và các khoản tương đương tiền Phải thu ngắn hạn của khách hàng
1 -0.01806044 -0.0274667
2 1.06626490 1.1767563
3 -1.04218431 -1.1401340
Hàng tồn kho Tài sản cố định hữu hình Bất động sản đầu tư
1 -0.2288612 0.1453825 0.5502505
2 1.2172494 1.0016713 0.2479910
3 -0.9121011 -1.1955146 -0.9816584
Chi phí xây dựng cơ bản dở dang Phải trả người bán ngắn hạn
1 -0.04776175 -0.2173725
2 1.18248940 1.3188775
3 -1.11880707 -1.0290474
Vay và nợ thuê tài chính dài hạn Vốn góp của chủ sở hữu
1 0.2054856 0.3276518
2 0.9864152 0.8653008
3 -1.2603960 -1.3021699
Lợi nhuận sau thuế chưa phân phối
1 -0.4231061
2 1.1112014
3 -0.5470600
Clustering vector:
[1] 3 3 3 1 1 1 1 2 2 2
Within cluster sum of squares by cluster:
[1] 6.182132 10.879724 1.913513
(between_SS / total_SS = 78.9 %)
Available components:
[1] "cluster" "centers" "totss" "withinss" "tot.withinss"
[6] "betweenss" "size" "iter" "ifault"
Phân cụm K-means được áp dụng để nhóm các khoản mục có xu hướng biến động tương đồng. Dữ liệu được chuẩn hóa (scale()) để đảm bảo tính so sánh, với số cụm xác định là 3 (centers = 3).
Kết quả:
Cụm 1 (4 khoản mục): Các khoản mục trung gian như Tài sản cố định hữu hình, Bất động sản đầu tư, có biến động quanh mức trung bình.
Cụm 2 (3 khoản mục): Nhóm thanh khoản cao (Phải thu ngắn hạn, Tiền mặt) với biến động mạnh và giá trị lớn.
Cụm 3 (3 khoản mục): Các khoản mục ổn định (Vốn chủ sở hữu) với biến động thấp.
Nhận định: Độ chính xác phân cụm đạt 78.9%, cho thấy sự phân nhóm rõ ràng theo mức độ biến động và tính thanh khoản. Kết quả này giúp nhận diện các nhóm khoản mục có hành vi tương tự, phục vụ công tác quản trị rủi ro và hoạch định chiến lược tài chính.
# Biểu đồ 11. Heatmap ma trận tương quan giữa các khoản mục tài chính
cor_long <- data_analysis %>%
select(where(is.numeric), -matches("Năm|Ma_Khoản_mục")) %>%
na.omit() %>%
cor(use = "complete.obs") %>%
as.data.frame() %>%
rownames_to_column("Var1") %>%
pivot_longer(-Var1, names_to = "Var2", values_to = "correlation")
ggplot(cor_long, aes(Var1, Var2, fill = correlation)) +
geom_tile(color = "white") +
geom_text(aes(label = round(correlation, 2)), color = "white", size = 3) +
scale_fill_gradient2(low = "red", mid = "white", high = "blue", midpoint = 0) +
labs(
title = "MA TRẬN TƯƠNG QUAN GIỮA CÁC KHOẢN MỤC TÀI CHÍNH",
fill = "Hệ số tương quan", x = NULL, y = NULL
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Heatmap được tạo ra từ ma trận tương quan (hàm cor()) để lượng hóa mối quan hệ tuyến tính giữa các khoản mục. Dữ liệu số được lọc và làm sạch trước khi tính toán. Thang màu gradient (scale_fill_gradient2) với điểm giữa là 0 (màu trắng) giúp trực quan hóa mức độ tương quan một cách trực quan.
Kết quả:
Nhóm tương quan cao (>0.9): Tập trung vào các cặp tài sản và nợ ngắn hạn và dài hạn (ví dụ: Phải thu ngắn hạn & Tài sản cố định hữu hình: 0.97), cho thấy sự gắn kết chặt chẽ trong cấu trúc tài chính.
Nhóm tương quan thấp/âm: “Bất động sản đầu tư” có quan hệ yếu hoặc âm với hầu hết các khoản mục, nổi bật là tương quan âm nhẹ (-0.27) với “Lợi nhuận sau thuế”.
Kết quả cho thấy sự phân nhóm rõ rệt: nhóm tài sản và nợ hoạt động chính có quan hệ chặt chẽ, trong khi “Bất động sản đầu tư” hoạt động khá độc lập. Điều này phản ánh tính chất đầu tư dài hạn, ít biến động theo hoạt động kinh doanh thường ngày của khoản mục này, cung cấp insight quan trọng cho việc quản trị rủi ro và ra quyết định đầu tư.
# Biểu đồ 12. Giá trị tài sản theo năm và khoản mục
heatmap_data <- data_long %>%
filter(Nhóm == "Tài sản") %>%
group_by(Năm, Khoản_mục) %>%
summarise(Giá_trị = sum(Giá_trị), .groups = "drop")
ggplot(heatmap_data, aes(x = Năm, y = Khoản_mục, fill = Giá_trị)) +
geom_tile(color = "white") +
scale_fill_viridis_c(option = "D") +
labs(title = "Heatmap giá trị tài sản theo năm và khoản mục",
x = "Năm", y = "Khoản mục", fill = "Giá trị") +
theme_minimal() +
scale_x_continuous(breaks = seq(2015, 2024, 1))
Biểu đồ heatmap sử dụng geom_tile(), trong đó mỗi ô đại diện cho một cặp (Năm, Khoản mục) và được tô màu theo giá trị tài sản. scale_fill_viridis_c() áp dụng thang màu liên tục Viridis từ thấp đến cao, giúp phân biệt trực quan các giá trị. Dữ liệu được lọc nhóm “Tài sản” và tổng hợp giá trị theo năm và khoản mục trước khi vẽ, đảm bảo heatmap phản ánh chính xác quy mô và phân bố các khoản mục theo thời gian.
Kết quả:
Màu sắc các ô thể hiện rõ sự chênh lệch giá trị giữa các khoản mục: “Tài sản cố định hữu hình” và “Chi phí xây dựng cơ bản dở dang” có màu đậm nhất, tương ứng giá trị cao nhất
Các khoản mục ngắn hạn như “Tiền và các khoản tương đương tiền” có màu nhạt hơn, thể hiện giá trị thấp hơn
Hầu hết các khoản mục duy trì màu sắc ổn định qua các năm, cho thấy giá trị không biến động mạnh
Nhận định: Heatmap cho thấy cơ cấu tài sản tập trung chủ yếu vào tài sản cố định và đầu tư dài hạn, phù hợp với đặc thù doanh nghiệp trong lĩnh vực xây dựng, sản xuất. Sự ổn định về màu sắc theo hàng ngang phản ánh tính bền vững trong cấu trúc tài sản qua thời gian. Việc trực quan hóa bằng heatmap giúp dễ dàng so sánh đồng thời cả theo thời gian và giữa các khoản mục.
# 21. Phân tích hồi quy hàng tồn kho - lợi nhuận
data_hồi_quy <- data_long %>%
filter(Khoản_mục %in% c("Hàng tồn kho", "Lợi nhuận sau thuế chưa phân phối")) %>%
select(Năm, Khoản_mục, Giá_trị) %>%
pivot_wider(names_from = Khoản_mục, values_from = Giá_trị)
lm_model <- lm(`Lợi nhuận sau thuế chưa phân phối` ~ `Hàng tồn kho`, data = data_hồi_quy)
# Tạo bảng kết quả hồi quy
reg_table <- tidy(lm_model)
kable(reg_table, digits = 3, caption = "Kết quả hồi quy: Hàng tồn kho và Lợi nhuận sau thuế")
| term | estimate | std.error | statistic | p.value |
|---|---|---|---|---|
| (Intercept) | -16352367.450 | 7894184.368 | -2.071 | 0.072 |
Hàng tồn kho |
0.379 | 0.107 | 3.535 | 0.008 |
# Biểu đồ 13. Mối quan hệ giữa hàng tồn kho và lợi nhuận.
library(ggrepel)
data_sub <- data_long %>%
filter(Khoản_mục %in% c("Hàng tồn kho", "Lợi nhuận sau thuế chưa phân phối")) %>%
select(Năm, Khoản_mục, Giá_trị) %>%
tidyr::pivot_wider(names_from = Khoản_mục, values_from = Giá_trị)
ggplot(data_sub, aes(x = `Hàng tồn kho`, y = `Lợi nhuận sau thuế chưa phân phối`)) +
geom_point(size = 3, color = "steelblue") + # Layer 1
geom_smooth(method = "lm", se = FALSE, color = "red") + # Layer 2
geom_text_repel(aes(label = Năm), size = 3, color = "black") + # Layer 3
geom_hline(yintercept = mean(data_sub$`Lợi nhuận sau thuế chưa phân phối`, na.rm = TRUE),
linetype = "dashed", color = "gray50") + # Layer 4
geom_vline(xintercept = mean(data_sub$`Hàng tồn kho`, na.rm = TRUE),
linetype = "dashed", color = "gray50") + # Layer 5
labs(title = "Mối quan hệ giữa Hàng tồn kho và Lợi nhuận sau thuế",
x = "Hàng tồn kho", y = "Lợi nhuận sau thuế") +
theme_minimal()
Biểu đồ sử dụng geom_point() thể hiện từng điểm dữ liệu theo năm, geom_smooth(method = “lm”) vẽ đường hồi quy tuyến tính để thấy xu hướng chung. geom_text_repel() gắn nhãn năm, tránh chồng chéo, trong khi geom_hline() và geom_vline() hiển thị trung bình của hai biến, hỗ trợ so sánh trực quan.
Kết quả:
Hệ số hồi quy 0.379 (p-value = 0.008) khẳng định mối quan hệ cùng chiều có ý nghĩa thống kê: khi hàng tồn kho tăng 1 đồng, lợi nhuận tăng trung bình 0.379 đồng
Các năm gần đây (2021-2023) thể hiện hiệu quả vượt trội khi nằm ở góc trên bên phải
Năm 2019 cho thấy sự bất thường khi đạt lợi nhuận rất cao với mức tồn kho trung bình
Nhận định: Mối quan hệ tích cực giữa hàng tồn kho và lợi nhuận phản ánh hiệu quả quản lý chuỗi cung ứng và dự báo nhu cầu của doanh nghiệp. Tuy nhiên, điểm bất thường năm 2019 cảnh báo về các nhân tố tác động ngoài mô hình (như hiệu quả sử dụng vốn, biến động giá nguyên liệu, hoặc yếu tố mùa vụ). Kết quả này nhấn mạnh sự cần thiết của việc kết hợp đa biến trong phân tích để nắm bắt toàn diện các động lực lợi nhuận.
# Biểu đồ 14. Quy mô và tỷ trọng tài sản
bubble_data <- data_long %>%
filter(Nhóm == "Tài sản") %>%
group_by(Năm) %>%
mutate(Tỷ_trọng = (Giá_trị / sum(Giá_trị)) * 100) %>%
ungroup()
ggplot(bubble_data, aes(x = Năm, y = Khoản_mục, size = Giá_trị, color = Tỷ_trọng)) +
geom_point(alpha = 0.7) +
scale_size(range = c(1, 10)) +
labs(title = "Quy mô và tỷ trọng tài sản",
x = "Năm", y = "Khoản mục",
size = "Giá trị", color = "Tỷ trọng (%)") +
scale_x_continuous(breaks = seq(2015, 2024, 1)) +
theme_light()
Biểu đồ sử dụng geom_point() tạo bubble, trong đó kích thước (size) thể hiện giá trị tuyệt đối của từng khoản mục, màu sắc (color) thể hiện tỷ trọng so với tổng tài sản trong năm. Tỷ trọng được tính bằng cách chuẩn hóa giá trị theo tổng tài sản của từng năm. scale_size() điều chỉnh kích thước bubble, scale_x_continuous() hiển thị đầy đủ các năm, và theme_light() giúp bố cục rõ ràng, dễ quan sát.
Kết quả:
Tài sản cố định hữu hình và Chi phí xây dựng cơ bản dở dang luôn có bubble lớn và màu đậm, chiếm tỷ trọng chủ đạo (>20%), phản ánh đầu tư dài hạn và trọng tâm vào dự án xây dựng.
Hàng tồn kho và Phải thu ngắn hạn duy trì tỷ trọng ổn định trung bình (10–20%), cho thấy quản lý dòng vốn lưu động tương đối ổn định.
Tiền và các khoản tương đương tiền có bubble nhỏ và màu nhạt (<10%), cảnh báo khả năng thanh khoản ngắn hạn thấp.
Nhìn chung, cơ cấu tài sản Vingroup tập trung vào đầu tư dài hạn, ổn định về tỷ trọng qua 10 năm, phù hợp với chiến lược phát triển bất động sản và hạ tầng.
# Biểu đồ 15. Cơ cấu tài sản năm 2024
library(treemapify)
data_2024 <- data_long %>%
filter(Năm == 2024, Nhóm == "Tài sản")
ggplot(data_2024, aes(area = Giá_trị, fill = Khoản_mục, label = Khoản_mục)) +
geom_treemap() +
geom_treemap_text(color = "black", place = "centre", grow = TRUE) +
labs(title = "Treemap: Cơ cấu tài sản năm 2024") +
theme(legend.position = "none") +
scale_fill_brewer(palette = "Set3")
Biểu đồ sử dụng geom_treemap() để trực quan hóa cơ cấu tài sản, trong đó diện tích mỗi ô vuông tỷ lệ thuận với giá trị từng khoản mục, phản ánh quy mô tương đối của các loại tài sản. Màu sắc (scale_fill_brewer) giúp phân biệt các khoản mục, còn geom_treemap_text() hiển thị tên trực tiếp trong từng ô, tăng khả năng nhận biết.
Kết quả:
Tài sản cố định hữu hình chiếm diện tích lớn nhất (~40%), là khoản mục chủ đạo.
Chi phí xây dựng cơ bản dở dang chiếm vị trí thứ hai (~25%), phản ánh các dự án đang triển khai.
Các tài sản ngắn hạn như Tiền mặt, Hàng tồn kho, Phải thu ngắn hạn chiếm khoảng 35%, diện tích nhỏ hơn, thể hiện tỷ trọng thấp.
Treemap cho thấy cơ cấu tài sản Vingroup tập trung mạnh vào tài sản dài hạn, phù hợp với đặc thù doanh nghiệp đầu tư xây dựng. Tỷ trọng cao của tài sản cố định và chi phí dở dang phản ánh quy mô dự án lớn và chiến lược đầu tư dài hạn, trong khi tỷ trọng tài sản ngắn hạn thấp cần theo dõi để đảm bảo khả năng thanh khoản.
# Biểu đồ 16. So sánh các khoản mục năm 2024
ggplot(data_long %>% filter(Năm == 2024),
aes(x = reorder(Khoản_mục, Giá_trị), y = Giá_trị, color = Nhóm)) +
geom_segment(aes(xend = Khoản_mục, y = 0, yend = Giá_trị), linewidth = 1) +
geom_point(size = 4) +
geom_text(
aes(label = scales::comma(Giá_trị)),
vjust = -0.5, size = 3.5, color = "black"
) +
coord_flip() +
labs(
title = "So sánh các khoản mục năm 2024",
y = "Giá trị (triệu VND)",
x = "",
color = "Nhóm"
) +
theme_bw() +
theme(
plot.title = element_text(hjust = 0.5, face = "bold", size = 16)
)
Biểu đồ sử dụng kỹ thuật Lollipop Chart kết hợp giữa đoạn thẳng (geom_segment) và điểm (geom_point) để so sánh giá trị các khoản mục:
geom_segment: Vẽ các đoạn thẳng từ trục y (giá trị 0) đến vị trí giá trị thực tế của từng khoản mục, tạo phần “thân” của biểu đồ
geom_point: Thêm điểm tròn tại cuối mỗi đoạn thẳng để nhấn mạnh vị trí giá trị
coord_flip(): Đảo trục để khoản mục hiển thị dọc theo trục y, giá trị hiển thị theo trục x, giúp đọc tên khoản mục dễ dàng hơn
reorder(): Sắp xếp các khoản mục theo thứ tự giá trị tăng dần từ trên xuống
geom_text: Hiển thị giá trị cụ thể với định dạng số (scales::comma)
Kết quả:
Tài sản cố định hữu hình có giá trị cao nhất (145.306 triệu)
4/5 khoản mục dẫn đầu thuộc nhóm Tài sản
Bất động sản đầu tư có giá trị thấp nhất (13.033 triệu)
Nhóm Nguồn vốn phân bổ khá đồng đều từ 38.786 - 129.042 triệu
Nhận định: Cơ cấu tài sản chiếm ưu thế với các khoản mục giá trị lớn, phù hợp với mô hình doanh nghiệp đầu tư dài hạn. Sự cân đối tương đối giữa nguồn vốn vay và vốn chủ sở hữu thể hiện cấu trúc vốn ổn định. Lợi nhuận sau thuế ở mức trung bình (44.498 triệu) cho thấy hiệu quả kinh doanh khả quan.
# Biểu đồ 17. Phân phối log(Giá trị)
ggplot(data_long, aes(x = log(Giá_trị + 1), fill = Nhóm)) +
geom_histogram(alpha = 0.7, bins = 30, color = "white") +
facet_wrap(~Nhóm, scales = "free_y") +
labs(title = "Phân phối giá trị theo nhóm", x = "Log(Giá trị)", y = "Tần suất") +
theme_minimal()
Biểu đồ sử dụng histogram (geom_histogram) với phép biến đổi logarithm (log(Giá_trị + 1)) để chuẩn hóa phân phối của dữ liệu có độ lệch lớn. Kỹ thuật facet_wrap(~Nhóm) được áp dụng để tách thành 3 biểu đồ con theo từng nhóm (Tài sản, Nguồn vốn, Kết quả kinh doanh), cho phép quan sát riêng biệt phân phối giá trị của từng nhóm. Tham số scales = “free_y” cho phép mỗi biểu đồ con có thang đo trục y độc lập.
Kết quả:
Nhóm Tài sản: Phân phối lệch phải, tập trung ở khoảng log(giá trị) 16-18, cho thấy đa số các khoản mục tài sản có giá trị ở mức trung bình đến cao
Nhóm Nguồn vốn: Phân phối tương đối cân đối, trải đều từ 14-19, phản ánh sự đa dạng về quy mô nguồn vốn
Nhóm Kết quả kinh doanh: Phân phối lệch trái rõ rệt, tập trung ở khoảng 14-16, thể hiện các khoản mục lợi nhuận có giá trị thấp hơn so với tài sản và nguồn vốn
Nhận định: Sự khác biệt về hình dạng phân phối giữa các nhóm phản ánh đặc thù cấu trúc tài chính: tài sản có xu hướng tích lũy giá trị lớn, trong khi kết quả kinh doanh thường có biên độ biến động thấp hơn. Việc sử dụng logarit giúp làm rõ hình dạng phân phối vốn bị che khuất bởi sự chênh lệch quá lớn về quy mô giữa các khoản mục.
# Biểu đồ 18. Phân phối mật độ giá trị theo nhóm
library(ggridges)
ggplot(data_long, aes(x = Giá_trị, y = Nhóm, fill = Nhóm)) +
geom_density_ridges(alpha = 0.8, scale = 1.2) + # Layer 1: density ridge
geom_vline(aes(xintercept = mean(Giá_trị, na.rm = TRUE)),
color = "red", linetype = "dashed") + # Layer 2: đường trung bình
labs(title = "Phân phối mật độ giá trị theo nhóm",
x = "Giá trị (triệu VND)", y = "Nhóm") + # Layer 3: tiêu đề
theme_minimal() + # Layer 4: theme
guides(fill = "none") # Layer 5: ẩn chú giải
Biểu đồ sử dụng kỹ thuật Ridge Plot (geom_density_ridges) để so sánh phân phối mật độ giá trị của 3 nhóm dữ liệu trên cùng một trục tọa độ. Mỗi “ridge” thể hiện đường mật độ (density curve) của một nhóm, với độ cao biểu thị tần suất xuất hiện của các giá trị. Đường thẳng đứng đứt đoạn (geom_vline) đánh dấu giá trị trung bình chung toàn bộ dữ liệu để so sánh.
Kết quả:
Nhóm Tài sản: Có phân phối rộng nhất, trải dài từ 0 đến >150 triệu, với đỉnh phân phối lệch phải, cho thấy đa số các khoản mục tài sản có giá trị trung bình đến cao
Nhóm Nguồn vốn: Có phân phối hẹp hơn, tập trung quanh khu vực giá trị trung bình
Nhóm Kết quả kinh doanh: Có phân phối hẹp và lệch trái mạnh, tập trung ở vùng giá trị thấp dưới 50 triệu
Nhận định: Sự khác biệt rõ rệt về hình dạng phân phối giữa các nhóm phản ánh đặc thù bản chất từng loại: tài sản có quy mô giá trị lớn và đa dạng, trong khi kết quả kinh doanh thường có biên độ hẹp hơn. Vị trí các đỉnh phân phối so với đường trung bình chung cho thấy nhóm tài sản đóng góp chính vào tổng giá trị, phù hợp với mô hình doanh nghiệp đầu tư dài hạn.
# Biểu đồ 19. Quan hệ giữa giá trị và tỷ trọng năm 2024
Tổng_tài_sản_2024 <- data_long %>%
filter(Năm == 2024, Nhóm == "Tài sản") %>%
summarise(Total_TS = sum(Giá_trị, na.rm = TRUE)) %>%
pull(Total_TS)
data_2024_final <- data_long %>%
filter(Năm == 2024) %>%
mutate(Tỷ_trọng = (Giá_trị / Tổng_tài_sản_2024) * 100)
ggplot(data_2024_final, aes(x = Giá_trị, y = Tỷ_trọng, color = Nhóm)) + geom_point(size = 3) + geom_smooth(method = "lm", se = FALSE, color = "gray50", linetype = "dashed") + geom_text_repel(aes(label = Khoản_mục), size = 3) + labs(title = "Quan hệ giữa giá trị và tỷ trọng năm 2024",
x = "Giá trị (triệu VND)", y = "Tỷ trọng (%)") + theme_minimal() +
guides(color = guide_legend(title = "Nhóm"))
Biểu đồ sử dụng scatter plot kết hợp đường xu hướng tuyến tính để phân tích mối quan hệ giữa giá trị tuyệt đối và tỷ trọng tương đối của các khoản mục:
geom_point(): Hiển thị các điểm, màu sắc phân biệt theo Nhóm
geom_smooth(method = “lm”): Vẽ đường xu hướng tổng quan
geom_text_repel(): Gắn nhãn tên khoản mục, tự động tránh chồng chéo
Tỷ trọng: % giá trị mỗi khoản mục so với tổng tài sản năm 2024
Kết quả:
Tài sản cố định hữu hình đứng ở góc trên phải, giá trị và tỷ trọng cao nhất
Các khoản mục Tài sản tập trung ở vùng giá trị lớn, tỷ trọng cao
Nhóm Nguồn vốn và Kết quả kinh doanh có giá trị thấp hơn
Đường xu hướng dốc lên, thể hiện mối quan hệ tích cực giữa giá trị và tỷ trọng
Nhận định: Cấu trúc tài chính tập trung vào tài sản dài hạn, với Tài sản cố định đóng vai trò chủ đạo. Mối quan hệ tuyến tính tích cực cho thấy các khoản mục giá trị lớn thường chiếm tỷ trọng cao trong tổng tài sản, phản ánh chiến lược ưu tiên đầu tư dài hạn của doanh nghiệp.
# Biểu đồ 20. Biểu đồ Heatmap đa chiều
data_heatmap <- data_long %>%
group_by(Năm, Nhóm, Khoản_mục) %>%
summarise(Giá_trị = sum(Giá_trị, na.rm = TRUE), .groups = 'drop') %>%
group_by(Năm) %>%
mutate(Tỷ_trọng = (Giá_trị / sum(Giá_trị)) * 100) %>%
ungroup() %>%
group_by(Nhóm, Khoản_mục) %>%
mutate(Tăng_trưởng = (Giá_trị / lag(Giá_trị) - 1) * 100) %>%
ungroup()
ggplot(data_heatmap, aes(x = Năm, y = Khoản_mục)) +
geom_tile(aes(fill = Giá_trị, alpha = Tỷ_trọng), color = "black") +
geom_text(aes(label = paste0(round(Tỷ_trọng, 0), "%")),
size = 3, color = "black", fontface = "bold") +
scale_fill_gradientn(
colors = c("#f7fbff", "#6baed6", "#08306b"),
name = "Giá trị (triệu VND)"
) + scale_alpha_continuous(
range = c(0.4, 1),
name = "Tỷ trọng (%)",
guide = guide_legend(override.aes = list(fill = "#2171b5"))
) + facet_grid(Nhóm ~ ., scales = "free_y", space = "free") +
labs( title = "Biểu đồ heatmap giá trị và tỷ trọng các khoản mục",x = "Năm", y = "Khoản mục" ) + theme_minimal() + scale_x_continuous(breaks = seq(2015, 2024, 1))
Biểu đồ sử dụng heatmap đa chiều để thể hiện cả giá trị tuyệt đối và tỷ trọng:
geom_tile(): Tạo ô heatmap, màu (fill) phản ánh giá trị, độ trong suốt (alpha) phản ánh tỷ trọng
scale_fill_gradientn(): Thang màu xanh lam từ nhạt đến đậm cho giá trị
scale_alpha_continuous(): Điều chỉnh độ trong suốt theo tỷ trọng (0.4–1)
facet_grid(Nhóm ~ .): Tách biểu đồ theo nhóm theo chiều dọc
geom_text(): Hiển thị tỷ trọng phần trăm trong từng ô
Kết quả:
Tài sản cố định hữu hình và Chi phí xây dựng cơ bản dở dang màu đậm, tỷ trọng cao (15–25%)
Vay và nợ thuê tài chính dài hạn ổn định (13–22%) trong cấu trúc nguồn vốn
Lợi nhuận sau thuế thấp (0–6%) và biến động mạnh
Các khoản mục ngắn hạn như Tiền mặt, Phải thu ngắn hạn duy trì tỷ trọng trung bình và ổn định
Nhận định: Heatmap thể hiện rõ cơ cấu tài chính tập trung vào tài sản dài hạn và nguồn vốn vay. Kết hợp màu sắc và độ trong suốt giúp đánh giá đồng thời quy mô tuyệt đối và vị trí tương đối của từng khoản mục.