1. crime <- read_xlsx("D:/ngongulaptrinh/Crime_Data_from_2020_to_Present.xlsx")
Giải thích
Kỹ thuật:
- read_excel() là hàm từ gói readxl dùng để đọc dữ liệu Excel (.xls hoặc
.xlsx).
- “D:/ngongulaptrinh/Crime_Data_from_2020_to_Present.xlsx” là đường dẫn
tuyệt đối tới file dữ liệu của bạn.
Ý nghĩa: để nạp dữ liệu dùng để phân tích.
1. nrow(crime)
## [1] 944235
1. ncol(crime)
## [1] 28
Giải thích
Kỹ thuật: hàm dim(crime) trả về số hàng và số cột của bảng dữ
liệu.
Ý nghĩa: có 944235 số vụ và 28 biến.
1. head(crime)
Giải thích
Kỹ thuật: hàm head(crime) để xem 6 dòng đầu của bộ dữ
liệu
Ý nghĩa: xem tổng quan để biết cấu trúc dữ liệu, tên các cột,
kiểu dữ liệu từng cột, và dữ liệu trông như thế nào ở phần đầu.
1. tail(crime)
Giải thích
Kỹ thuật: hàm tail(crime) để xem 6 dòng cuối của bộ dữ
liệu
Ý nghĩa: xem tổng qua để phát hiện có bị thiếu dữ liệu, lỗi
nhập liệu, hay dòng trống ở cuối hay không.
1. names(crime)
## [1] "DR_NO" "Date Rptd" "DATE OCC" "TIME OCC"
## [5] "AREA" "AREA NAME" "Rpt Dist No" "Part 1-2"
## [9] "Crm Cd" "Crm Cd Desc" "Mocodes" "Vict Age"
## [13] "Vict Sex" "Vict Descent" "Premis Cd" "Premis Desc"
## [17] "Weapon Used Cd" "Weapon Desc" "Status" "Status Desc"
## [21] "Crm Cd 1" "Crm Cd 2" "Crm Cd 3" "Crm Cd 4"
## [25] "LOCATION" "Cross Street" "LAT" "LON"
Giải thích
Kỹ thuật: hàm names() dùng để xem tên các cột có trong bộ dữ
liệu.
Ý nghĩa: xem tên các biến có trong dữ liệu
Bộ dữ liệu Crime_Data_from_2020_to_Present gồm 28
biến (cột) và 944235 quan sát (dòng).
Mỗi dòng tương ứng với một vụ phạm tội được ghi nhận bởi Sở Cảnh sát Los
Angeles (LAPD).
Bảng dưới đây mô tả ý nghĩa của từng biến:
| Tên biến | Tên tiếng Việt | Giải thích / Ý nghĩa |
|---|---|---|
| DR_NO | Mã vụ án | Mã định danh duy nhất cho mỗi vụ phạm tội. |
| Date Rptd | Ngày báo cáo | Ngày vụ việc được báo cáo cho cảnh sát. |
| DATE OCC | Ngày xảy ra vụ việc | Ngày thực tế xảy ra tội phạm. |
| TIME OCC | Giờ xảy ra vụ việc | Thời gian trong ngày (định dạng 24h). |
| AREA | Mã khu vực | Mã số nội bộ cho khu vực tuần tra. |
| AREA NAME | Tên khu vực | Tên khu vực cảnh sát phụ trách (ví dụ: Central, Hollywood, SouthWest…). |
| Rpt Dist No | Mã quận báo cáo | Mã quận hoặc phường nơi vụ việc được báo cáo. |
| Part 1-2 | Nhóm tội phạm | Phân loại tội phạm: Part 1 (nghiêm trọng), Part 2 (nhẹ). |
| Crm Cd | Mã tội phạm | Mã số cho loại tội phạm cụ thể. |
| Crm Cd Desc | Mô tả tội phạm | Tên mô tả loại tội phạm (vd: BURGLARY, ROBBERY…). |
| Mocodes | Mã thủ đoạn phạm tội | “Modus Operandi” – mô tả hoặc mã hóa cách thức gây án. |
| Vict Age | Tuổi nạn nhân | Tuổi của nạn nhân. |
| Vict Sex | Giới tính nạn nhân | M = Nam, F = Nữ, X = Khác/Không xác định. |
| Vict Descent | Dân tộc nạn nhân | Mã dân tộc/nền gốc (H = Hispanic, W = White, B = Black, A = Asian, …). |
| Premis Cd | Mã địa điểm | Mã loại địa điểm xảy ra tội phạm. |
| Premis Desc | Mô tả địa điểm | Mô tả chi tiết địa điểm (vd: STREET, RESIDENCE, PARKING LOT). |
| Weapon Used Cd | Mã vũ khí | Mã loại vũ khí được sử dụng (nếu có). |
| Weapon Desc | Mô tả vũ khí | Mô tả cụ thể loại vũ khí (vd: HAND GUN, KNIFE, NONE). |
| Status | Mã trạng thái vụ án | Mã trạng thái (IC = Đang điều tra, AO = Bắt người trưởng thành, JO = Bắt trẻ vị thành niên…). |
| Status Desc | Mô tả trạng thái vụ án | Diễn giải chi tiết trạng thái điều tra. |
| Crm Cd 1 | Mã tội bổ sung 1 | Tội phụ thứ nhất (nếu vụ án có nhiều hành vi). |
| Crm Cd 2 | Mã tội bổ sung 2 | Tội phụ thứ hai. |
| Crm Cd 3 | Mã tội bổ sung 3 | Tội phụ thứ ba. |
| Crm Cd 4 | Mã tội bổ sung 4 | Tội phụ thứ tư. |
| LOCATION | Địa chỉ | Địa điểm xảy ra vụ việc (mô tả ngắn hoặc địa chỉ). |
| Cross Street | Đường cắt | Tên đường giao nhau gần vị trí xảy ra vụ việc. |
| LAT | Vĩ độ | Tọa độ địa lý (latitude). |
| LON | Kinh độ | Tọa độ địa lý (longitude). |
Nhận xét:
Một số biến có tỷ lệ giá trị bị thiếu khá cao (như
Weapon Used Cd, Crm Cd 3,
Cross Street, …).
Điều này cần được xem xét khi làm sạch dữ liệu trước khi phân tích.
1. View(crime)
Giải thích Để xem nhanh toàn bộ và giúp hiểu tổng quan về dữ liệu.
1. table(map_chr(crime, ~ class(.x)[1]))
##
## character logical numeric POSIXct
## 13 1 12 2
Giải thích
Kỹ thuật: hàm sapply trong R để kiểm tra loại dữ liệu của từng
cột trong một bộ dữ liệu, giúp tổng quan về cấu trúc dữ liệu của bộ dữ
liệu.
Kết quả:
- Các biến kiểu số (numeric): DR_NO, TIME OCC, AREA, Rpt Dist No, Part
1-2, Crm Cd, Vict Age, Premis Cd, Weapon Used Cd, Crm Cd 1, Crm Cd 2,
Crm Cd 3.
- Các biến kiểu ngày giờ (POSIXct, POSIXt): Date Rptd, DATE OCC.
- Các biến kiểu chuỗi ký tự (character): AREA NAME, Crm Cd Desc,
Mocodes, Vict Sex, Vict Descent, Premis Desc, Weapon Desc, Status,
Status Desc, LOCATION, Cross Street, LAT, LON. - Một biến kiểu logic
(logical): Crm Cd 4.
1. colSums(is.na(crime))
## DR_NO Date Rptd DATE OCC TIME OCC AREA
## 0 0 0 0 0
## AREA NAME Rpt Dist No Part 1-2 Crm Cd Crm Cd Desc
## 0 0 0 0 0
## Mocodes Vict Age Vict Sex Vict Descent Premis Cd
## 133099 0 126595 126605 10
## Premis Desc Weapon Used Cd Weapon Desc Status Status Desc
## 567 619758 619758 0 0
## Crm Cd 1 Crm Cd 2 Crm Cd 3 Crm Cd 4 LOCATION
## 11 875977 941954 944171 0
## Cross Street LAT LON
## 796643 0 0
Giải thích
Kỹ thuật:
- Hàm is.na() trả về TRUE/FALSE cho giá trị bị thiếu trong
dataframe.
- Đếm số giá trị thiếu toàn bộ dữ liệu: sum(is.na(df))
- Đếm số giá trị thiếu theo cột: colSums(is.na(df))
Ý nghĩa:
- Giá trị bị thiếu (NA) trong các cột dữ liệu có thể phát sinh do nhiều
nguyên nhân và mang ý nghĩa quan trọng như sau:
- Thiếu dữ liệu gốc: Một số thông tin không được ghi nhận hoặc không có
tại thời điểm thu thập dữ liệu, ví dụ như “Mocodes”, “Vict Sex” hoặc
“Weapon Used Cd” có nhiều giá trị thiếu do chưa được khai báo hoặc không
áp dụng trong trường hợp đó.
- Không có hoặc không xác định: Có thể một số trường hợp không có giá
trị phù hợp, ví dụ như nạn nhân không cung cấp thông tin giới tính hoặc
loại vũ khí không được ghi nhận.
- Lỗi hoặc thiếu sót trong quá trình nhập liệu, thu thập hoặc truyền
file dữ liệu.
- Kiểu dữ liệu đặc thù: Một số cột (như “Premis Cd”) có ít giá trị thiếu
có thể là do đặc điểm của bộ dữ liệu hoặc yêu cầu cụ thể trong xử
lý.
1. sum(duplicated(crime))
## [1] 0
Giải thích
Kỹ thuật:
-Hàm duplicated() trả về TRUE cho những dòng dữ liệu bị trùng
-Xem tổng số bản ghi trùng lặp: sum(duplicated(crime))
-Lấy các dòng trùng lặp: df\[duplicated(crime), \]
Kết quả: cho thấy không có bản ghi vụ án nào bị trùng.
1. table(crime$`Status`)
##
## AA AO CC IC JA JO
## 82182 102928 5 754264 3102 1754
1. crime_selected <- crime %>% select(`Crm Cd Desc`, `Premis Desc`, `Date Rptd`, `Part 1-2`,`Vict Sex`, `Vict Age`, AREA, Status, `Rpt Dist No`, `Crm Cd 1`, `Crm Cd 2`, `Crm Cd 3`, `Crm Cd 4`)
2. names(crime_selected)
## [1] "Crm Cd Desc" "Premis Desc" "Date Rptd" "Part 1-2" "Vict Sex"
## [6] "Vict Age" "AREA" "Status" "Rpt Dist No" "Crm Cd 1"
## [11] "Crm Cd 2" "Crm Cd 3" "Crm Cd 4"
Giải thích
Kỹ thuật:
- Dòng 1: Cho phép chọn chính xác các cột cần thiết.
- Dòng 1-3: Các tên cột chứa khoảng trắng hoặc ký tự đặc biệt (như Crm
Cd 1) cần được đặt trong dấu `.
Ý nghĩa:
Việc lọc các cột này giúp tập trung vào thông tin mô tả tội phạm, nạn
nhân và khu vực báo cáo, đồng thời thêm các mã tội phạm (Crm Cd 1–4) và
trạng thái xử lý vụ án (Status). Điều này hữu ích khi bạn muốn phân tích
mức độ nghiêm trọng, đặc điểm nạn nhân, hoặc sự phân bố địa lý của tội
phạm.
1. crime_renamed <- crime_selected %>%
2. rename( Mo_ta_Toi_Pham = `Crm Cd Desc`, Mo_ta_Dia_Diem = `Premis Desc`,
3. Ngay_Bao_Cao = `Date Rptd`, Phan_Loai = `Part 1-2`,
4. Gioi_Tinh_Nan_Nhan = `Vict Sex`, Tuoi_Nan_Nhan = `Vict Age`,
5. Ma_Khu_Vuc = AREA, Trang_Thai = Status,
6. So_Don_Vi_Bao_Cao = `Rpt Dist No`, Ma_Toi_Pham_1 = `Crm Cd 1`,
7. Ma_Toi_Pham_2 = `Crm Cd 2`, Ma_Toi_Pham_3 = `Crm Cd 3`,
8. Ma_Toi_Pham_4 = `Crm Cd 4`)
9. head(crime_renamed)
Giải thích
Kỹ thuât:
- Dòng 1: Tạo bản sao mới của dữ liệu có tên là crime_renamed — điều này
đảm bảo bạn không ghi đè dữ liệu gốc (crime_selected).
- Dòng 2: Hàm này thuộc gói dplyr, dùng để đặt lại tên cột.
Cú pháp: tên_mới = tên_cũ (đảo ngược so với select()).
Dấu `: Bắt buộc khi tên cột chứa khoảng trắng hoặc ký tự đặc biệt (Crm
Cd 1, Part 1-2, v.v.) — nếu không R sẽ báo lỗi cú pháp.
- Dòng 10: Hiển thị 6 dòng đầu của bảng đã đổi tên để kiểm tra
nhanh.
Ý nghĩa:
Đây là bước quan trọng trong giai đoạn tiền xử lý dữ liệu (data
preprocessing). Việc đổi tên cột giúp:
+ Chuẩn hóa ngôn ngữ cho phù hợp với ngữ cảnh phân tích xã hội học, tội
phạm học.
+ Dễ dàng gọi biến trong phân tích mô hình (hồi quy, tương quan, phân
cụm,…).
+ Giúp người đọc báo cáo hiểu rõ hơn mà không cần đối chiếu với mô tả
tiếng Anh gốc.
1. crime_cleaned <- crime_renamed %>%
2. mutate( Gioi_Tinh_Nan_Nhan = ifelse(Gioi_Tinh_Nan_Nhan == "-" | is.na(Gioi_Tinh_Nan_Nhan), "X", Gioi_Tinh_Nan_Nhan) ,
3. Ma_Toi_Pham_1 = ifelse(is.na(Ma_Toi_Pham_1), 0, Ma_Toi_Pham_1),
4. Ma_Toi_Pham_2 = ifelse(is.na(Ma_Toi_Pham_2), 0, Ma_Toi_Pham_2),
5. Ma_Toi_Pham_3 = ifelse(is.na(Ma_Toi_Pham_3), 0, Ma_Toi_Pham_3),
6. Ma_Toi_Pham_4 = ifelse(is.na(Ma_Toi_Pham_4), 0, Ma_Toi_Pham_4) )
7. head(crime_cleaned)
Giải thích
Kỹ thuật:
- Dòng 2: (Giá_trị_mới, giữ_nguyên): thay giá trị bị thiếu.
+ Gioi_Tinh_Nan_Nhan: Chuẩn hóa dữ liệu, thay “-” hoặc NA thành “X”
(giới tính không xác định).
+ Ma_Toi_Pham_1–4: nếu NA → 0 (không có tội bổ sung).
- Dòng 8: xem 6 dòng đầu để kiểm tra.
Ý nghĩa:
Trong phân tích tội phạm học hoặc xã hội học, việc thay thế NA bằng ký
hiệu định danh:
+ Giúp duy trì độ toàn vẹn dữ liệu — không mất mẫu do NA.
+ Cho phép phân tích định tính hoặc phân nhóm dễ dàng hơn (ví dụ: nhóm
“không xác định giới tính” vẫn được thống kê riêng).
+ Với các mã tội phạm phụ (Ma_Toi_Pham_2–4), giá trị “0” phản ánh chính
xác bản chất “không có tội bổ sung”, chứ không phải dữ liệu bị
thiếu.
1. crime_cleaned <- crime_cleaned %>% mutate(
2. Khu_Vuc = case_when(
3. Ma_Khu_Vuc == 1 ~ "Central", Ma_Khu_Vuc == 2 ~ "Rampart",
4. Ma_Khu_Vuc == 3 ~ "Southwest", Ma_Khu_Vuc == 4 ~ "Hollenbeck",
5. Ma_Khu_Vuc == 5 ~ "Harbor", Ma_Khu_Vuc == 6 ~ "Hollywood",
6. Ma_Khu_Vuc == 7 ~ "Wilshire", Ma_Khu_Vuc == 8 ~ "West LA",
7. Ma_Khu_Vuc == 9 ~ "Van Nuys", Ma_Khu_Vuc == 10 ~ "West Valley",
8. Ma_Khu_Vuc == 11 ~ "Northeast", Ma_Khu_Vuc == 12 ~ "77th Street",
9. Ma_Khu_Vuc == 13 ~ "Newton", Ma_Khu_Vuc == 14 ~ "Pacific",
10. Ma_Khu_Vuc == 15 ~ "N Hollywood", Ma_Khu_Vuc == 16 ~ "Foothill",
11. Ma_Khu_Vuc == 17 ~ "Devonshire", Ma_Khu_Vuc == 18 ~ "Southeast",
12. Ma_Khu_Vuc == 19 ~ "Mission", Ma_Khu_Vuc == 20 ~ "Olympic",
13. Ma_Khu_Vuc == 21 ~ "Topanga", TRUE ~ "Không xác định" ) )
14. head(crime_cleaned %>% select(Ma_Khu_Vuc, Khu_Vuc))
Giải thích
Kỹ thuật:
- Dòng 1: thêm hoặc cập nhật biến mới.
- Dòng 2: thay thế nhiều điều kiện kiểu “nếu… thì…”.
- Dòng 3-13: Mỗi dòng ánh xạ (Ma_Khu_Vuc == 1 ~ “Central”) nghĩa
là:
+ Nếu mã khu vực bằng 1 → điền “Central”.
+ TRUE ~ “Không xác định” là mặc định nếu mã không khớp với danh sách.
Ý nghĩa:
- Biến Khu_Vuc giúp chuyển mã định danh thành tên địa lý dễ hiểu.
- Dùng trong phân tích không gian (crime mapping), mô tả khu vực có tỷ
lệ tội phạm cao.
- Giúp trực quan hóa dữ liệu (ví dụ: biểu đồ tội phạm theo khu vực).
1. map_crime <- crime_cleaned %>%
2. select(Ma_Toi_Pham_1, Mo_ta_Toi_Pham) %>%
3. filter(!is.na(Ma_Toi_Pham_1)) %>%
4. distinct()
5. map_vec <- map_crime$Mo_ta_Toi_Pham
6. names(map_vec) <- as.character(map_crime$Ma_Toi_Pham_1)
7. crime_extended <- crime_cleaned %>%
8. mutate( `Toi Bo Sung 2` = ifelse( Ma_Toi_Pham_2 == 0,
9. "Không có tội bổ sung", map_vec[as.character(Ma_Toi_Pham_2)] ),
10. `Toi Bo Sung 3` = ifelse( Ma_Toi_Pham_3 == 0, "Không có tội bổ sung",
11. map_vec[as.character(Ma_Toi_Pham_3)] ),
12. `Toi Bo Sung 4` = ifelse( Ma_Toi_Pham_4 == 0, "Không có tội bổ sung",
13. map_vec[as.character(Ma_Toi_Pham_4)] ) ) %>%
14. mutate( `Toi Bo Sung 2` = ifelse(is.na(`Toi Bo Sung 2`) & Ma_Toi_Pham_2 != 0, "Không xác định", `Toi Bo Sung 2`),
15. `Toi Bo Sung 3` = ifelse(is.na(`Toi Bo Sung 3`) & Ma_Toi_Pham_3 != 0, "Không xác định", `Toi Bo Sung 3`),
16. `Toi Bo Sung 4` = ifelse(is.na(`Toi Bo Sung 4`) & Ma_Toi_Pham_4 != 0, "Không xác định", `Toi Bo Sung 4`) )
17. crime_extended %>% select(Ma_Toi_Pham_1:Ma_Toi_Pham_4,
18. Mo_ta_Toi_Pham, `Toi Bo Sung 2`, `Toi Bo Sung 3`, `Toi Bo Sung 4`) %>%
19. head()
Ghi chú
| Mã | Mô tả | Ghi chú |
|---|---|---|
| 999 | Non-Criminal (Other) | Ghi nhận sự kiện phi tội phạm khác, không có đối tượng cụ thể |
| 998 | Non-Criminal (Subject Specified) | Có liên quan đến một người cụ thể (subject specified) |
Vì vậy được liệt kê vô nhóm “không xác định”.
Giải thích
1. Tạo bảng ánh xạ (mapping table):
Dùng select() và distinct() để lấy danh sách duy nhất giữa mã
(Ma_Toi_Pham_1) và mô tả (Mo_ta_Toi_Pham).
→ Đây là “từ điển nội bộ” giúp dịch mã số thành mô tả có nghĩa.
Tạo vector tra cứu:
Dùng kỹ thuật named vector — đặt names() là mã tội, giá trị là mô
tả.
Khi gọi map_vec\["420"\], R
sẽ trả về mô tả của tội có mã 420.
→ Cách này nhanh và tiết kiệm bộ nhớ hơn so với left_join() nhiều
lần.
Tạo cột mới “Tội bổ sung 2–4”:
Với mutate():
Gán nhãn cho giá trị không khớp:
Nếu mã ≠ 0 mà không tìm được mô tả, gán nhãn “Không xác định” để tránh
mất thông tin và giúp phát hiện lỗi mã hóa.
Kiểm tra kết quả:
Dùng select() + head() để xem nhanh 6 dòng đầu, đảm bảo việc ánh xạ hoạt
động chính xác.
Ý nghĩa:
| Nội dung | Ý nghĩa |
|---|---|
| Chuẩn hóa dữ liệu định tính | Giúp mô tả tội danh rõ ràng, dễ hiểu, thay vì chỉ dựa vào mã số. |
| Tạo biến mở rộng | Các cột “Tội bổ sung 2–4” giúp phân tích đa tội danh trong cùng một vụ việc. |
| Duy trì toàn vẹn dữ liệu | Không mất dữ liệu do NA, mà quy ước rõ ràng: “Không có tội bổ sung” hoặc “Không xác định”. |
| Tối ưu hiệu năng xử lý | Cách ánh xạ bằng vector nhanh hơn nhiều so với phép nối (join) dữ liệu lớn. |
| Hỗ trợ phân tích nâng cao | Dễ dàng thống kê, mô hình hóa hoặc trực quan hóa mối quan hệ giữa tội chính và tội phụ. |
1. crime_extended <- crime_extended %>%
2. mutate(
3. Nhom_Tuoi = case_when(
4. Tuoi_Nan_Nhan < 10 ~ "Dưới 10 tuổi",
5. Tuoi_Nan_Nhan >= 10 & Tuoi_Nan_Nhan < 18 ~ "Vị thành niên (10–17)",
6. Tuoi_Nan_Nhan >= 18 & Tuoi_Nan_Nhan < 30 ~ "Thanh niên (18–29)",
7. Tuoi_Nan_Nhan >= 30 & Tuoi_Nan_Nhan < 45 ~ "Trưởng thành trẻ (30–44)",
8. Tuoi_Nan_Nhan >= 45 & Tuoi_Nan_Nhan < 60 ~ "Trung niên (45–59)",
9. Tuoi_Nan_Nhan >= 60 & Tuoi_Nan_Nhan < 75 ~ "Cao niên (60–74)",
10. Tuoi_Nan_Nhan >= 75 ~ "Già (75+)",
11. TRUE ~ "Không xác định" ) )
12. print(crime_extended)
## # A tibble: 944,235 × 18
## Mo_ta_Toi_Pham Mo_ta_Dia_Diem Ngay_Bao_Cao Phan_Loai
## <chr> <chr> <dttm> <dbl>
## 1 VEHICLE - STOLEN STREET 2020-01-03 00:00:00 1
## 2 BURGLARY FROM VEHICLE BUS STOP/LAYO… 2020-09-02 00:00:00 1
## 3 BIKE - STOLEN MULTI-UNIT DW… 2020-11-11 00:00:00 1
## 4 SHOPLIFTING-GRAND THEFT ($950.0… CLOTHING STORE 2023-10-05 00:00:00 1
## 5 THEFT OF IDENTITY SIDEWALK 2022-08-18 00:00:00 2
## 6 THEFT OF IDENTITY SINGLE FAMILY… 2023-04-04 00:00:00 2
## 7 THEFT OF IDENTITY MULTI-UNIT DW… 2023-04-04 00:00:00 2
## 8 THEFT OF IDENTITY CELL PHONE ST… 2022-07-22 00:00:00 2
## 9 THEFT OF IDENTITY CYBERSPACE 2023-04-28 00:00:00 2
## 10 BATTERY - SIMPLE ASSAULT MULTI-UNIT DW… 2020-12-31 00:00:00 2
## # ℹ 944,225 more rows
## # ℹ 14 more variables: Gioi_Tinh_Nan_Nhan <chr>, Tuoi_Nan_Nhan <dbl>,
## # Ma_Khu_Vuc <dbl>, Trang_Thai <chr>, So_Don_Vi_Bao_Cao <dbl>,
## # Ma_Toi_Pham_1 <dbl>, Ma_Toi_Pham_2 <dbl>, Ma_Toi_Pham_3 <dbl>,
## # Ma_Toi_Pham_4 <dbl>, Khu_Vuc <chr>, `Toi Bo Sung 2` <chr>,
## # `Toi Bo Sung 3` <chr>, `Toi Bo Sung 4` <chr>, Nhom_Tuoi <chr>
Giải thích
Kỹ thuật
- Dòng 2: tạo biến mới Nhom_Tuoi trong bảng crime_extended.
- Dòng 3: cho phép gán nhãn theo điều kiện logic phức tạp. - Điều kiện
phân loại:
+ < 10: trẻ em nhỏ.
+ 10–17: vị thành niên (tuổi chưa đủ trách nhiệm hình sự đầy đủ).
+ 18–29: thanh niên (tuổi lao động, hoạt động xã hội cao).
+ 30–44: trưởng thành trẻ.
+ 45–59: trung niên.
+ 60–74: cao niên.
+ ≥75: người già.
+ NA hoặc giá trị bất thường → “Không xác định”.
- Dòng 12: xem kết quả vừa tạo. Ý nghĩa:
| Mục tiêu | Ý nghĩa |
|---|---|
| Chuẩn hóa dữ liệu nhân khẩu học | Biến tuổi liên tục thành nhóm giúp dễ phân tích mô hình hoặc thống kê tần suất. |
| Phân tích xã hội học | Có thể xác định nhóm tuổi nào thường là nạn nhân nhiều nhất (ví dụ: thanh niên, trung niên…). |
| Trực quan hóa | Dễ tạo biểu đồ cột, biểu đồ phân bố nạn nhân theo nhóm tuổi. |
| Chuẩn bị cho mô hình hóa | Biến Nhom_Tuoi có thể được dùng trong mô hình hồi quy
logistic hoặc cây quyết định (decision tree). |
1. crime_extended$Mo_ta_Dia_Diem <- ifelse(is.na(crime_extended$Mo_ta_Dia_Diem),
2. "Không xác định", crime_extended$Mo_ta_Dia_Diem)
Giải thích
Kỹ thuật:
- is.na() → kiểm tra từng dòng xem giá trị có bị thiếu (NA) không.
- ifelse(điều_kiện, giá_trị_nếu_đúng, giá_trị_nếu_sai) → gán giá trị mới
tùy theo điều kiện.
- “Không xác định” → thay thế cho những dòng bị NA.
- Còn lại (crime_extended$Mo_ta_Dia_Diem) → giữ nguyên giá trị có
sẵn.
Kết quả: cột Mo_ta_Dia_Diem không còn NA, mà các ô trống
được thay bằng “Không xác định”.
1. crime_extended <- crime_extended %>% mutate(
2. Tinh_Trang_Bat_Giu = case_when(
3. Trang_Thai == "AA" ~ "Đã bắt – Người trưởng thành",
4. Trang_Thai == "AO" ~ "Đã bắt – Người chưa trưởng thành",
5. Trang_Thai == "CC" ~ "Chưa bắt – Hồ sơ đang điều tra",
6. Trang_Thai == "IC" ~ "Đang điều tra – Chưa xác định nghi phạm",
7. Trang_Thai == "JA" ~ "Nghi phạm trưởng thành – Đang giam giữ",
8. Trang_Thai == "JO" ~ "Nghi phạm chưa trưởng thành – Đang giam giữ",
9. TRUE ~ "Không xác định" ) )
10. print(crime_extended)
## # A tibble: 944,235 × 19
## Mo_ta_Toi_Pham Mo_ta_Dia_Diem Ngay_Bao_Cao Phan_Loai
## <chr> <chr> <dttm> <dbl>
## 1 VEHICLE - STOLEN STREET 2020-01-03 00:00:00 1
## 2 BURGLARY FROM VEHICLE BUS STOP/LAYO… 2020-09-02 00:00:00 1
## 3 BIKE - STOLEN MULTI-UNIT DW… 2020-11-11 00:00:00 1
## 4 SHOPLIFTING-GRAND THEFT ($950.0… CLOTHING STORE 2023-10-05 00:00:00 1
## 5 THEFT OF IDENTITY SIDEWALK 2022-08-18 00:00:00 2
## 6 THEFT OF IDENTITY SINGLE FAMILY… 2023-04-04 00:00:00 2
## 7 THEFT OF IDENTITY MULTI-UNIT DW… 2023-04-04 00:00:00 2
## 8 THEFT OF IDENTITY CELL PHONE ST… 2022-07-22 00:00:00 2
## 9 THEFT OF IDENTITY CYBERSPACE 2023-04-28 00:00:00 2
## 10 BATTERY - SIMPLE ASSAULT MULTI-UNIT DW… 2020-12-31 00:00:00 2
## # ℹ 944,225 more rows
## # ℹ 15 more variables: Gioi_Tinh_Nan_Nhan <chr>, Tuoi_Nan_Nhan <dbl>,
## # Ma_Khu_Vuc <dbl>, Trang_Thai <chr>, So_Don_Vi_Bao_Cao <dbl>,
## # Ma_Toi_Pham_1 <dbl>, Ma_Toi_Pham_2 <dbl>, Ma_Toi_Pham_3 <dbl>,
## # Ma_Toi_Pham_4 <dbl>, Khu_Vuc <chr>, `Toi Bo Sung 2` <chr>,
## # `Toi Bo Sung 3` <chr>, `Toi Bo Sung 4` <chr>, Nhom_Tuoi <chr>,
## # Tinh_Trang_Bat_Giu <chr>
Giải thích
Kỹ thuật:
- Dòng 1: thêm cột mới Tinh_Trang_Bat_Giu.
- Dòng 2: cho phép ánh xạ từng mã (Trang_Thai) sang mô tả đầy đủ.
+ TRUE ~ “Không xác định”: xử lý dự phòng nếu có mã lạ ngoài danh
sách.
Ý nghĩa:
- Giúp phân tích tình trạng xử lý vụ án, xem tỷ lệ vụ đã bắt giữ – chưa
bắt giữ.
- Cho phép nghiên cứu hiệu quả thực thi pháp luật theo khu vực, loại tội
phạm, hoặc nhóm tuổi.
- Có thể sử dụng trong mô hình dự đoán khả năng phá án (clearance rate
modeling).
1. crime_extended <- crime_extended %>%
2. mutate(
3. So_Toi_Trong_Vu = rowSums(across(Ma_Toi_Pham_1:Ma_Toi_Pham_4, ~ . != 0), na.rm = TRUE) )
4. crime_extended %>%
5. select(Ma_Toi_Pham_1:Ma_Toi_Pham_4, So_Toi_Trong_Vu) %>%
6. head()
Giải thích
Kỹ thuật:
- Dòng 3: tạo ma trận logic 4 cột (TRUE/FALSE) một lần cho toàn bộ dữ
liệu.
+ rowSums(…) cộng TRUE (=1) theo hàng cực nhanh ở C.
+ na.rm = TRUE đề phòng có NA (dù bạn đã chuẩn hóa về 0).
Ý nghĩa:
| Khía cạnh | Giải thích |
|---|---|
| Khái niệm | So_Toi_Trong_Vu là số lượng tội danh cùng xảy ra trong
một vụ án. |
| Giá trị nghiên cứu | Giúp xác định vụ án đơn tội (1 tội) hay đa tội (≥2 tội). |
| Ứng dụng phân tích | - So sánh tính chất vụ án theo mức độ phức tạp. - Đánh giá khả năng bắt giữ theo số lượng tội. - Mô hình hóa rủi ro hoặc tần suất phạm pháp. |
| Ví dụ mô tả | Trung bình mỗi vụ có mean(So_Toi_Trong_Vu) tội; tỷ lệ
vụ đa tội là mean(So_Toi_Trong_Vu > 1) * 100. |
1. crime_extended <- crime_extended %>%
2. mutate( Nam = year(ymd(Ngay_Bao_Cao)) )
3. crime_extended %>% select(Ngay_Bao_Cao, Nam) %>% head()
Giải thích
Kỹ thuật:
- Dòng 1-2: đổi chuỗi ngày thành dạng ngày tháng chuẩn.
+ year(…): trích năm từ ngày.
+ mutate(…): thêm cột Nam vào bảng.
Ý nghĩa:
Giúp dễ dàng phân tích, lọc, thống kê hoặc vẽ biểu đồ theo năm (ví dụ:
số vụ án từng năm).
1. crime_final <- crime_extended %>%
2. select( Ngay_Bao_Cao, Nam, Phan_Loai, Trang_Thai, Tinh_Trang_Bat_Giu,
3. Mo_ta_Toi_Pham, Ma_Toi_Pham_1, `Toi Bo Sung 2`, Ma_Toi_Pham_2,
4. `Toi Bo Sung 3`, Ma_Toi_Pham_3, `Toi Bo Sung 4`, Ma_Toi_Pham_4,
5. So_Toi_Trong_Vu, Mo_ta_Dia_Diem, Ma_Khu_Vuc, Khu_Vuc, So_Don_Vi_Bao_Cao,
6. Gioi_Tinh_Nan_Nhan, Tuoi_Nan_Nhan, Nhom_Tuoi )
7. colnames(crime_final)
## [1] "Ngay_Bao_Cao" "Nam" "Phan_Loai"
## [4] "Trang_Thai" "Tinh_Trang_Bat_Giu" "Mo_ta_Toi_Pham"
## [7] "Ma_Toi_Pham_1" "Toi Bo Sung 2" "Ma_Toi_Pham_2"
## [10] "Toi Bo Sung 3" "Ma_Toi_Pham_3" "Toi Bo Sung 4"
## [13] "Ma_Toi_Pham_4" "So_Toi_Trong_Vu" "Mo_ta_Dia_Diem"
## [16] "Ma_Khu_Vuc" "Khu_Vuc" "So_Don_Vi_Bao_Cao"
## [19] "Gioi_Tinh_Nan_Nhan" "Tuoi_Nan_Nhan" "Nhom_Tuoi"
Giải thích
Kỹ thuật:
- Dòng 2: chọn ra các cột quan trọng như ngày báo cáo, năm, loại tội
phạm, tình trạng, địa điểm, thông tin nạn nhân,…
- Dòng 7: colnames(crime_final): hiển thị tên các cột của bảng mới để
kiểm tra.
Ý nghĩa:
Tạo cấu trúc logic – mạch lạc – thân thiện báo cáo, phản ánh đúng quy
trình phân tích tội phạm học và giảm sai sót trong thao tác và diễn
giải.
1. crime_final <- crime_final %>%
2. mutate( Ngay_Bao_Cao = ymd(Ngay_Bao_Cao),
3. Nam = year(Ngay_Bao_Cao),
4. Thang = month(Ngay_Bao_Cao, label = TRUE, abbr = TRUE),
5. Quy = quarter(Ngay_Bao_Cao) )
Giải thích
Kỹ thuật:
- Dòng 2-5:
+ ymd(Ngay_Bao_Cao): chuyển cột ngày thành dạng Date chuẩn.
+ year(…): tạo cột Nam (năm).
+ month(…, label=TRUE, abbr=TRUE): tạo cột Thang (tên tháng viết tắt, ví
dụ Jan, Feb).
+ quarter(…): tạo cột Quy (quý 1–4).
Ý nghĩa:
Giúp dữ liệu có thể phân tích theo thời gian linh hoạt hơn — như thống
kê theo tháng, quý, năm, hoặc vẽ biểu đồ xu hướng tội phạm theo thời
gian.
1. crime_by_year <- crime_final %>%
2. group_by(Nam) %>%
3. summarise(So_Vu = n()) %>% arrange(Nam)
4. crime_by_year
Giải thích
Kỹ thuật:
- Dòng 2: gom dữ liệu theo từng năm.
- Dòng 3:
+ summarise(n()) → đếm tổng số bản ghi (vụ tội phạm) trong từng
nhóm.
+ arrange(Nam) → sắp xếp theo thứ tự tăng dần của năm.
Ý nghĩa:
Giúp thống kê xu hướng tội phạm theo từng năm, hỗ trợ phân tích sự thay
đổi hoặc vẽ biểu đồ xu hướng thời gian.
1. crime_by_year <- crime_final %>%
2. filter(Nam != 2024) %>%
3. group_by(Nam) %>% summarise(So_Vu = n()) %>% arrange(Nam)
4. crime_by_year <- crime_by_year %>%
5. mutate( Ty_le_thay_doi = round((So_Vu - lag(So_Vu)) / lag(So_Vu) * 100, 2))
6. crime_by_year
Giải thích
Kỹ thuật:
- Dòng 2: loại bỏ dữ liệu năm 2024 (chưa đủ hoặc chưa hoàn chỉnh).
- Dòng 3:
+ group_by(Nam) %>% summarise(So_Vu = n()): đếm số vụ (So_Vu) cho
từng năm.
+ arrange(Nam): sắp xếp theo thứ tự năm tăng dần.
- Dòng 5 mutate(Ty_le_thay_doi = round((So_Vu - lag(So_Vu)) / lag(So_Vu)
* 100, 2)):
+ lag(So_Vu): lấy giá trị số vụ của năm trước.
+ Công thức tính tỷ lệ thay đổi (%) so với năm trước.
+ round(…, 2): làm tròn đến 2 chữ số thập phân. Ý nghĩa:
- 2021 tăng 8.08% so với 2020 → xu hướng gia tăng tội phạm rõ rệt.
- 2022 tăng tiếp 12.86% so với 2021 → đỉnh cao trong giai đoạn.
- 2023 giảm nhẹ 0.18% → mức ổn định, không giảm đáng kể.
1. mean(crime_by_year$So_Vu)
## [1] 217676
1. median(crime_by_year$So_Vu)
## [1] 221466
1. sd(crime_by_year$So_Vu)
## [1] 20833.1
1. var(crime_by_year$So_Vu)
## [1] 434018249
1. min(crime_by_year$So_Vu)
## [1] 192708
1. max(crime_by_year$So_Vu)
## [1] 235064
1. quantile(crime_by_year$So_Vu, probs=c(0.25, 0.5, 0.75))
## 25% 50% 75%
## 204389.2 221466.0 234752.8
Giải thích
Kỹ thuật:
- Dòng 1: giá trị trung bình số vụ/năm.
- Dòng 2: trung vị – giá trị nằm giữa khi sắp xếp dữ liệu.
- Dòng 3: độ lệch chuẩn, đo mức phân tán quanh giá trị trung bình.
- Dòng 4: phương sai, thể hiện mức độ biến động (bình phương của độ lệch
chuẩn).
- Dòng 5-6: giá trị nhỏ nhất và lớn nhất của số vụ.
- Dòng 7: các phân vị:
+ 0.25: Q1 (25%)
+ 0.5: median (50%)
+ 0.75: Q3 (75%)
Ý nghĩa
- Trong giai đoạn 2020–2023, trung bình mỗi năm xảy ra khoảng 217,676 vụ
phạm tội, dao động ±20,833 vụ. Số vụ thấp nhất năm 2020 (192,708) và cao
nhất năm 2022 (235,064). Phân phối tương đối đối xứng, không có năm bất
thường.
- Sau giai đoạn giảm mạnh do đại dịch, số vụ tội phạm tăng trở lại trong
2021–2022 và ổn định ở mức cao năm 2023, phản ánh sự phục hồi xã hội và
kinh tế kèm hệ quả tội phạm gia tăng.
1. crime_by_month <- crime_final %>%
2. group_by(Nam, Thang) %>% summarise(So_Vu = n(), .groups = "drop")
3. crime_by_month <- crime_by_month %>% group_by(Nam) %>%
4. mutate( Tong_Nam = sum(So_Vu), Ty_le = round(So_Vu / Tong_Nam * 100, 2) )
5. crime_by_month <- crime_by_month %>% filter(Nam != 2024)
6. crime_by_quarter <- crime_final %>%
7. group_by(Nam, Quy) %>% summarise(So_Vu = n(), .group = "drop")
8. crime_by_month
Giải thích:
Kỹ thuật
- Dòng 1-4: nhóm theo năm – tháng, đếm số vụ mỗi tháng → tạo cột
So_Vu.
- Dòng 5-8:
+ group_by(Nam) %>% mutate(Tong_Nam = sum(So_Vu), Ty_le = round(So_Vu
/ Tong_Nam * 100, 2)): tính tổng vụ/năm và tỷ lệ % mỗi tháng trong
năm.
+ filter(Nam != 2024): bỏ năm 2024 (chưa đủ dữ liệu).
Ý nghĩa:
- Phân bố tội phạm theo tháng trong giai đoạn 2020–2023 nhìn chung khá
ổn định, không có biến động đột ngột.
- Trung bình mỗi tháng chiếm khoảng 7,8–8,8% tổng số vụ trong năm, tương
đương 17.000–20.000 vụ/tháng. Các tháng có tần suất cao hơn trung bình
thường rơi vào:
+ Tháng 1 (8.3–8.7%), liên quan đến giai đoạn trước và sau Tết Nguyên
đán, hoạt động xã hội, đi lại, tiêu dùng tăng cao.
+ Tháng 6–8 và Tháng 10 cũng ghi nhận tỷ trọng cao hơn, có thể liên quan
đến mùa du lịch, học sinh – sinh viên nghỉ hè, và thời điểm cuối quý III
– đầu quý IV khi hoạt động kinh tế – xã hội sôi động.
- Ngược lại, các tháng có tỷ lệ thấp hơn thường là Tháng 3, 11, 12, dao
động quanh 7,8–8,0%, cho thấy thời kỳ ổn định hoặc giảm nhẹ của tội phạm
trước giai đoạn lễ – Tết.
- Mức chênh lệch giữa tháng cao nhất và thấp nhất chỉ khoảng 1 điểm phần
trăm, chứng tỏ tình hình tội phạm phân bố khá đều, không có tháng “bùng
phát” bất thường.
- Phân tích theo tháng giai đoạn 2020–2023 phản ánh tính ổn định cao và
quy luật mùa vụ nhẹ trong hoạt động tội phạm, chủ yếu dao động theo chu
kỳ kinh tế – xã hội (Tết, du lịch, tiêu dùng, nghỉ hè…).
1. crime_by_type <- crime_final %>% group_by(Phan_Loai) %>%
2. summarise(So_Vu = n(), .groups = "drop") %>%
3. mutate( Tong_Vu = sum(So_Vu), Ty_le = round(So_Vu / Tong_Vu * 100, 2) ) %>%
4. ungroup() %>% arrange(desc(So_Vu))
5. crime_by_type
Giải thích
Kỹ thuật:
- Dòng 1: nhóm dữ liệu theo loại tội phạm.
- Dòng 2: đếm số vụ trong mỗi nhóm.
- Dòng 3: thêm tổng số vụ (Tong_Vu) và tính tỷ lệ phần trăm
(Ty_le).
+ arrange(desc(So_Vu)) sắp xếp theo số vụ giảm dần để dễ quan sát.
Ý nghĩa:
Loại tội phạm thứ nhất xuất hiện nhiều hơn và là dạng chiếm tỷ trọng lớn
nhất trong tổng số vụ án (944,235 vụ).
1. crime_by_status <- crime_final %>% group_by(Trang_Thai) %>%
2. summarise(So_Vu = n(), .groups = "drop") %>%
3. mutate( Tong_Vu = sum(So_Vu), Ty_le = round(So_Vu / Tong_Vu * 100, 2) ) %>%
4. ungroup() %>% arrange(desc(So_Vu))
5. crime_by_status
Giải thích
Kỹ thuật:
- Dòng 1: nhóm dữ liệu theo từng trạng thái vụ án.
- Dòng 2: đếm số vụ trong mỗi nhóm.
- Dòng 3: tính tổng số vụ (Tong_Vu) và tỷ lệ phần trăm (Ty_le) của từng
trạng thái.
- Dòng 4: sắp xếp kết quả theo số vụ giảm dần.
Ý nghĩa:
IC chiếm đa số (79.88%) – đây có thể là trạng thái vụ đang trong quá
trình điều tra hoặc chưa xử lý xong.
AO và AA chiếm lần lượt 10.90% và 8.70%, phản ánh các vụ đã có tiến
triển hoặc kết luận khác nhau.
Các trạng thái còn lại (JA, JO, CC) chiếm tỷ lệ rất nhỏ (<1%).
1. crime_by_desc <- crime_final %>% group_by(Mo_ta_Toi_Pham) %>%
2. summarise(So_Vu = n(), .groups = "drop") %>% mutate(
3. Tong_Vu = sum(So_Vu), Ty_le = round(So_Vu / Tong_Vu * 100, 2) ) %>%
4. ungroup() %>% arrange(desc(So_Vu))
5. head(crime_by_desc, 10)
Giải thích
Kỹ thuật:
- Dòng 1: nhóm dữ liệu theo từng loại tội phạm cụ thể.
- Dòng 2:
+ summarise(So_Vu = n()) → đếm số vụ trong mỗi nhóm.
+ mutate() → thêm tổng số vụ (Tong_Vu) và tính tỷ lệ phần trăm (Ty_le)
của từng loại tội.
+ arrange(desc(So_Vu)) → sắp xếp theo số vụ giảm dần.
- Dòng 5: chỉ lấy 10 loại tội phạm phổ biến nhất.
Ý nghĩa:
Trộm xe (VEHICLE - STOLEN) chiếm 10.81%, là loại tội phạm nhiều
nhất.
Tiếp theo là hành hung đơn giản (BATTERY - SIMPLE ASSAULT) chiếm 7.89%,
và trộm cắp từ xe (BURGLARY FROM VEHICLE) chiếm 6.18%.
10 loại tội phạm hàng đầu chiếm trên 60% tổng số vụ án, cho thấy mức độ
tập trung cao ở một số nhóm tội chính.
1. crime_by_year_type <- crime_final %>% filter(Nam != 2024) %>%
2. group_by(Nam, Mo_ta_Toi_Pham) %>% summarise(So_Vu = n(), .groups = "drop") %>% group_by(Nam) %>%
3. mutate( Tong_Nam = sum(So_Vu), Ty_le = round(So_Vu / Tong_Nam * 100, 2) )
4. View(crime_by_year_type)
Ggiải thích
Đoạn code thống kê số vụ án theo từng năm và loại tội phạm
(Mo_ta_Toi_Pham), tính tổng số vụ và tỷ lệ phần trăm của từng loại trong
năm.
Kết quả cho thấy mỗi năm có khoảng 235.000 vụ án, với các tội phổ biến
như trộm xe, trộm cắp, hành hung, phá hoại tài sản chiếm tỷ trọng cao
nhất.
Điều này phản ánh mô hình tội phạm ổn định qua các năm, trong đó các
hành vi liên quan đến trộm cắp tài sản vẫn chiếm ưu thế.
1. top_crime_each_year <- crime_by_year_type %>%
2. group_by(Nam) %>% slice_max(order_by = So_Vu, n = 1)
3. top_crime_each_year
Ggiải thích
- Dòng 1-3:
+ group_by(Nam) → nhóm dữ liệu theo năm.
+ slice_max(order_by = So_Vu, n = 1) → lấy dòng có số vụ (So_Vu) lớn
nhất trong từng năm.
Kết quả: Trong giai đoạn 2020–2023, tội “VEHICLE - STOLEN”
(trộm xe) luôn đứng đầu mỗi năm, chiếm khoảng 10–11% tổng số vụ án hàng
năm.
1. top3_crime_each_year <- crime_by_year_type %>%
2. group_by(Nam) %>% slice_max(order_by = So_Vu, n = 3)
3. top3_crime_each_year
Giải thích
Kỹ thuật: nhóm dữ liệu theo năm (Nam) và chọn 3 loại tội phạm
có số vụ cao nhất mỗi năm bằng slice_max(order_by = So_Vu, n = 3).
Ý nghĩa:
Kết quả cho thấy “VEHICLE - STOLEN”, “BATTERY - SIMPLE ASSAULT” và
“THEFT OF IDENTITY” là 3 tội phạm phổ biến nhất hằng năm, chiếm tỷ lệ
lớn trong tổng số vụ án.
1. crime_freq <- crime_by_year %>%
2. mutate(Tan_suat = round(So_Vu / sum(So_Vu) * 100, 2))
3. crime_freq
Giải thích
Kỹ thuật:
+ So_Vu: là tần số – tổng số vụ trong từng năm.
+ Tan_suat: là tần suất (%) – tỷ lệ số vụ của năm đó so với toàn bộ các
năm.
Ý nghĩa: Tội phạm có xu hướng tăng dần từ 2020–2022 rồi ổn định
vào 2023, cho thấy sự leo thang trước khi chững lại.
1. crime_by_gender <- crime_extended %>%
2. count(Gioi_Tinh_Nan_Nhan) %>%
3. mutate(Tan_suat = round(n / sum(n) * 100, 2))
4. crime_by_gender
Giải thích
Kỹ thuật:
Đoạn code trên nhóm dữ liệu theo giới tính nạn nhân
(Gioi_Tinh_Nan_Nhan), đếm số vụ (n) và tính tần suất (%) của từng giới
tính trong tổng số vụ.
Ý nghĩa:
Nạn nhân nam và nữ có tỷ lệ gần tương đương, nhưng vẫn thấy nam là nhóm
bị ảnh hưởng nhiều nhất, trong khi giới tính không xác định chiếm tỷ lệ
đáng kể trong dữ liệu.
1. crime_by_area <- crime_extended %>%
2. group_by(Khu_Vuc) %>% summarise(So_Vu = n(), .groups = "drop") %>%
3. mutate(Tan_suat = round(So_Vu / sum(So_Vu) * 100, 2)) %>% arrange(desc(So_Vu))
4. head(crime_by_area,10)
Giải thích
Kỹ thuật: + count() thay cho group_by() + summarise(), giúp
đếm nhanh số vụ theo khu vực.
+ Tính tần suất (%), sắp xếp giảm dần và lấy top 10 khu có nhiều vụ
nhất.
Ý nghĩa:
Các khu Central, 77th Street, Pacific có số vụ cao nhất (~6–7%), cho
thấy phân bố tội phạm tập trung ở khu trung tâm và phía nam thành
phố.
1. crime_by_gender_type_year <- crime_final %>%
2. filter(Nam != 2024) %>%
3. group_by(Nam, Phan_Loai, Gioi_Tinh_Nan_Nhan) %>%
4. summarise(So_Vu = n(), .groups = "drop_last") %>%
5. mutate( Tong_Phan_Loai_Nam = sum(So_Vu),
6. Ty_le = round(So_Vu / Tong_Phan_Loai_Nam * 100, 2) ) %>%
7. ungroup() %>% arrange(Nam, Phan_Loai, desc(So_Vu))
8. View(crime_by_gender_type_year)
Giải thích
Kỹ thuật:
+ filter(Nam != 2024): loại dữ liệu năm 2024.
+ group_by(Nam, Phan_Loai, Gioi_Tinh_Nan_Nhan): nhóm theo năm, loại tội,
giới tính.
+ summarise(So_Vu = n()): đếm số vụ trong mỗi nhóm.
+ mutate(…): tính tổng vụ trong từng loại-năm và tỷ lệ (%) từng
giới
+ arrange(…): sắp xếp theo năm, loại, số vụ giảm dần.
Ý nghĩa:
Bảng cho biết số vụ và tỷ lệ theo giới tính nạn nhân trong mỗi loại tội
phạm và từng năm, giúp so sánh mức độ ảnh hưởng giữa nam – nữ theo thời
gian và loại tội.
1. crime_by_age <- crime_final %>%
2. filter(Nam != 2024) %>% group_by(Nhom_Tuoi) %>% summarise(So_Vu = n(), .groups = "drop") %>%
3. mutate( Tong_Vu = sum(So_Vu), Ty_le = round(So_Vu / Tong_Vu * 100, 2) ) %>%
4. ungroup() %>% arrange(desc(So_Vu))
5. crime_by_age
Giải thích
Kỹ thuật:
count() đếm tần số theo nhóm tuổi; mutate() tính tổng và tỷ lệ %;
arrange() sắp xếp giảm dần.
Ý nghĩa: nhóm Trưởng thành (30–44) nhiều nhất 27.12%, Dưới 10
tuổi bất ngờ đứng thứ hai 25.40%, tiếp theo là Thanh niên (18–29)
19.84%; tổng mẫu dùng để tính là 870,704 vụ (không gồm 2024).
1. crime_by_age_gender <- crime_final %>%
2. filter(Nam != 2024) %>% group_by(Nhom_Tuoi, Gioi_Tinh_Nan_Nhan) %>% summarise(So_Vu = n(), .groups = "drop_last") %>%
3. mutate( Tong_Theo_Gioi = sum(So_Vu),
4. Ty_le_Trong_Gioi = round(So_Vu / Tong_Theo_Gioi * 100, 2) ) %>% ungroup() %>% arrange(Gioi_Tinh_Nan_Nhan, desc(So_Vu))
5. View(crime_by_age_gender)
Giải thích
Kỹ thuật:
- Dòng 1: Lọc bỏ năm 2024.
- Dòng 2: Đếm số vụ theo nhóm tuổi và giới tính.
- Dòng 4: Tính tổng số vụ trong từng giới và tỷ lệ (%) của mỗi nhóm tuổi
trong giới đó.
- Dòng 6: Sắp xếp theo giới tính và số vụ giảm dần → giúp thấy nhóm tuổi
nào chiếm tỷ lệ cao nhất trong từng giới.
Ý nghĩa:
phân bố tội phạm theo giới và tuổi tương đối cân bằng, nhưng cần rà soát
giá trị “X” để đảm bảo độ chính xác.
1. crime_by_age_year <- crime_final %>%
2. filter(Nam != 2024) %>% group_by(Nam, Nhom_Tuoi) %>%
3. summarise(So_Vu = n(), .groups = "drop_last") %>%
4. mutate( Tong_Nam = sum(So_Vu), Ty_le = round(So_Vu / Tong_Nam * 100, 2)
5. ) %>% ungroup() %>% arrange(Nam, desc(So_Vu))
6. crime_by_age_year
Giải thích
Kỹ thuật:
- Dòng 1–2: Lọc bỏ năm 2024, nhóm dữ liệu theo năm và nhóm tuổi.
- Dòng 3: Tính số vụ phạm tội (So_Vu) cho từng nhóm, giữ cấu trúc nhóm
theo năm.
- Dòng 4–5: Tính tổng số vụ trong năm và tỷ lệ phần trăm từng nhóm tuổi;
sắp xếp giảm dần theo số vụ.
- Dòng 6: Hiển thị bảng kết quả hoàn chỉnh, dễ đọc và so sánh theo
năm.
Ý nghĩa:
Cấu trúc chung: Các nhóm tuổi Trưởng thành trẻ (30–44 tuổi) và Dưới 10
tuổi chiếm tỷ trọng lớn nhất trong toàn bộ giai đoạn, lần lượt khoảng
25–28% mỗi năm.
Nhóm Thanh niên (18–29 tuổi) chiếm khoảng 20%, trong khi Trung niên
(45–59) dao động từ 15–17%. Các nhóm cao niên (60–74) và già (75+) có tỷ
lệ thấp (7–8% và ~2%).
Xu hướng theo thời gian:
+ Giai đoạn 2020 → 2022, tỷ lệ nạn nhân Trưởng thành trẻ (30–44) tăng
nhẹ từ 26.7% lên 28.1%, cho thấy tăng cường tiếp xúc xã hội và kinh tế
của nhóm này sau đại dịch.
+ Nhóm Dưới 10 tuổi duy trì mức cao (~25%), đặc biệt tăng mạnh năm 2023
(27.5%), phản ánh xu hướng đáng lo ngại về các vụ xâm hại, bạo lực, hoặc
vi phạm liên quan trẻ nhỏ.
+ Nhóm Thanh niên (18–29) tương đối ổn định quanh mức 19–20%, còn nhóm
Trung niên (45–59) có xu hướng giảm nhẹ.
Đặc điểm nổi bật năm 2023: Năm 2023 ghi nhận sự đảo chiều nhẹ: nhóm Dưới
10 tuổi vượt Trưởng thành trẻ, trở thành nhóm chiếm tỷ lệ cao nhất
(27.6%). Điều này có thể liên quan đến tăng cường báo cáo tội phạm trẻ
em hoặc số vụ bạo lực gia đình và học đường được ghi nhận nhiều
hơn.
Tổng thể: Trong cả giai đoạn 2020–2023, cơ cấu nạn nhân theo độ tuổi ổn
định, nhưng hai xu hướng đáng chú ý là:
+ Sự duy trì tỷ lệ cao của nhóm 30–44 tuổi → phản ánh nhóm lao động năng
động, rủi ro xã hội cao.
+ Sự tăng nhẹ nhóm Dưới 10 tuổi → tín hiệu cảnh báo về an toàn trẻ em và
chất lượng môi trường xã hội.
1. crime_by_area_type <- crime_final %>% filter(Nam != 2024) %>%
2. group_by(Khu_Vuc, Phan_Loai) %>%
3. summarise(So_Vu = n(), .groups = "drop_last") %>%
4. mutate( Tong_Vu_Khu_Vuc = sum(So_Vu),
5. Ty_le = round(So_Vu / Tong_Vu_Khu_Vuc * 100, 2) ) %>%
6. ungroup() %>% arrange(desc(So_Vu))
7. View(crime_by_area_type)
Giải thích
Kỹ thuật:
- Dòng 1-6: Lọc năm ≠ 2024 → nhóm theo năm và tuổi → đếm số vụ → tính
tổng và tỷ lệ theo năm → sắp xếp giảm dần số vụ.
- Dòng 7: Xem loại tội phạm theo khu vực.
Ý nghĩa:
loại tội 1 chiếm tỉ lệ cao hơn (khoảng 55–65%) ở hầu hết khu vực, cho
thấy các tội nghiêm trọng vẫn chiếm ưu thế trong cơ cấu tội phạm toàn
thành phố.
1. crime_by_area_status <- crime_final %>%
2. filter(Nam != 2024) %>% group_by(Khu_Vuc, Trang_Thai) %>%
3. summarise(So_Vu = n(), .groups = "drop_last") %>%
4. mutate( Tong_Vu_Khu_Vuc = sum(So_Vu),
5. Ty_le = round(So_Vu / Tong_Vu_Khu_Vuc * 100, 2) ) %>% ungroup() %>%
6. arrange(Khu_Vuc, desc(So_Vu))
7. View(crime_by_area_status)
Giải thích
Kỹ thuật:
- Dòng 1-6:
+ filter(Nam != 2024): loại dữ liệu năm 2024.
+ group_by(Khu_Vuc, Trang_Thai): nhóm theo khu vực và trạng thái vụ
án.
+ summarise(So_Vu = n()): đếm số vụ từng trạng thái trong mỗi khu.
+ mutate(Tong_Vu_Khu_Vuc = sum(So_Vu), Ty_le = So_Vu / Tong_Vu_Khu_Vuc *
100): tính tổng vụ trong khu và tỷ lệ từng trạng thái.
+ arrange(Khu_Vuc, desc(So_Vu)): sắp xếp theo khu và giảm dần số
vụ.
Ý nghĩa:
Mức độ giải quyết tội phạm còn thấp, tập trung chủ yếu ở các vụ đang
trong quá trình điều tra.
1. crime_per_year <- crime_extended %>%
2. filter(Nam != 2024) %>%
3. group_by(Nam) %>%
4. summarise(So_Vu = n())
5. ggplot(crime_per_year, aes(x = as.factor(Nam), y = So_Vu, fill = as.factor(Nam))) +
6. geom_bar(stat = "identity", position = "dodge", alpha = 0.6) +
7. geom_text(aes(label = So_Vu), vjust = 0.5, size = 5, fontface = "bold") +
8. labs(title = "Số vụ tội phạm theo năm",
9. x = "Năm", y = "Số lượng vụ tội phạm", caption = "Chú thích: Năm") +
10. scale_fill_brewer(palette = "Set3", name = "Năm") + theme_minimal() +
11. theme(axis.text.x = element_text(angle = 0, hjust = 1, size = 14),
12. plot.title = element_text(face = "bold", size = 16),
13. plot.caption = element_text(size = 12, hjust = 1))
Giải thích
Kỹ thuật
- Dòng 1-4: Dữ liệu được lọc để loại bỏ năm 2024, sau đó nhóm theo năm
(Nam) và tính tổng số vụ tội phạm (So_Vu) cho từng năm.
- Dòng 5-6: Biểu đồ cột (geom_bar) được vẽ, sử dụng dodge position để
các cột của mỗi năm không bị chồng lên nhau, với độ trong suốt là
60%.
- Dòng 7: Thêm nhãn số vụ tội phạm vào mỗi cột, đảm bảo nhãn được căn
chỉnh đúng vị trí và có kích thước chữ rõ ràng.
- Dòng 8-9: Thêm tiêu đề, nhãn trục và chú thích (caption) cho biểu
đồ.
- Dòng 10: Tùy chỉnh màu sắc với bảng màu từ RColorBrewer cho các cột
theo năm.
- Dòng 11-13: Cải thiện kiểu dáng của biểu đồ: chỉnh sửa kích thước chữ
cho trục x và tiêu đề, và căn chỉnh các phần tử đồ họa cho dễ nhìn
hơn.
Ý nghĩa
- 2020-2022: Số vụ tội phạm tăng từ 192,708 vụ (2020) lên 235,064 vụ
(2022), cho thấy một sự gia tăng rõ rệt trong tình hình tội phạm trong
giai đoạn này.
- 2023: Mặc dù vẫn giữ ở mức cao (234,649 vụ), nhưng số vụ tội phạm đã
giảm nhẹ so với năm 2022, có thể phản ánh những nỗ lực kiểm soát hoặc sự
thay đổi trong các yếu tố xã hội.
1. ggplot(crime_by_year, aes(x = as.factor(Nam), y = Ty_le_thay_doi, fill = as.factor(Nam))) +
2. geom_bar(stat = "identity", alpha = 0.6) +
3. geom_text(aes(label = round(Ty_le_thay_doi, 2)), vjust = 0.5, size = 5, fontface = "bold") +
4. labs(title = "Tỷ lệ tăng trưởng phần trăm\n số vụ tội phạm theo năm",
5. x = "Năm", y = "Tỷ lệ tăng trưởng (%)", fill = "Năm báo cáo") +
6. scale_fill_brewer(palette = "Set3") +
7. theme_minimal() + theme(plot.title = element_text(size = 18, face = "bold", lineheight = 1.2, hjust = 0.5), axis.text.x = element_text(angle = 0, hjust = 1, size = 14))
r 1. is.text.x = element_text(angle = 45, hjust = 1, size = 12)
Giải thích
Kỹ thuật:
- Dòng 1: Khởi tạo biểu đồ với ggplot, ánh xạ năm lên trục x và tỷ lệ
thay đổi lên trục y, đồng thời tô màu theo năm.
- Dòng 2: Dùng geom_bar(stat=“identity”) để vẽ biểu đồ cột, hiển thị
trực tiếp giá trị tỷ lệ thay đổi.
- Dòng 3: Thêm nhãn giá trị làm tròn 2 chữ số, căn giữa cột để dễ
đọc.
- Dòng 4–5: Đặt tiêu đề và nhãn trục rõ ràng, mô tả ý nghĩa của biểu
đồ.
- Dòng 6: Áp dụng bảng màu Set3 giúp phân biệt các năm trực quan.
- Dòng 7: Dùng theme_minimal() tạo phong cách đơn giản, làm nổi bật dữ
liệu.
Ý nghĩa:
- 2020-2021: Tỷ lệ tăng trưởng rất thấp (0.08%), cho thấy số vụ tội phạm
gần như không thay đổi.
- 2021-2022: Tăng mạnh lên 0.13%, phản ánh sự gia tăng đáng kể trong số
vụ tội phạm, có thể do các yếu tố xã hội hoặc kinh tế tác động.
- 2022-2023: Tỷ lệ tăng trưởng bằng 0%, cho thấy sự ổn định trong số vụ
tội phạm, có thể là kết quả của các biện pháp kiểm soát hiệu quả.
1. crime_by_month <- crime_final %>%
2. mutate(Thang = month(ymd(Ngay_Bao_Cao), label = TRUE, abbr = TRUE)) %>%
3. filter(Nam != 2024) %>% group_by(Nam, Thang) %>%
4. summarise(So_Vu = n(), .groups = "drop")
5. ggplot(crime_by_month, aes(Thang, So_Vu, color = factor(Nam), group = Nam)) +
6. geom_line(size = 0.9) + geom_point(size = 1.8) + stat_summary(aes(group = 1), fun = mean, geom = "line", color = "black", linetype = 2, size = 1.05) +
7. geom_smooth(method = "loess", se = FALSE, linetype = 3) +
8. scale_y_continuous(labels = scales::label_number(scale_cut = scales::cut_short_scale())) + labs(
9. title = "Mùa vụ theo tháng trong từng năm (2020–2023)",
10. subtitle = "Đường đen nét đứt: trung bình các năm",
11. x = "Tháng", y = "Số vụ", color = "Năm" ) +
12. theme_minimal() + theme( axis.text.x = element_text(size = 7),
13. plot.title = element_text(face = "bold"),
14. plot.subtitle = element_text(color = "gray30"),
15. plot.caption = element_text(color = "gray50"),
16. panel.grid.minor = element_blank())
Giải thích
Kỹ thuật:
- Dòng 1-4: Tiền xử lý dữ liệu, trích tháng từ Ngay_Bao_Cao, lọc bỏ năm
2024, nhóm theo Nam và Thang, rồi đếm số vụ (So_Vu).
- Dòng 5-6: Vẽ biểu đồ đường cho từng năm, thêm các điểm dữ liệu và
đường trung bình (đen, nét đứt) bằng stat_summary.
- Dòng 7-8: Thêm đường xu hướng tổng thể bằng geom_smooth(loess) và định
dạng trục y với nhãn rút gọn.
- Dòng 9-16: Thêm tiêu đề, phụ đề, nhãn trục và chỉnh giao diện tối giản
với chữ nhỏ, ẩn lưới phụ để biểu đồ rõ hơn.
Ý nghĩa:
- Xu hướng mùa vụ: Số vụ tội phạm cao nhất vào giữa năm (tháng 5-6) và
thấp nhất vào cuối năm (tháng 1-2) cho tất cả các năm.
- Sự thay đổi qua các năm:
+ 2020: Số vụ tội phạm dao động nhẹ.
+ 2021: Tăng mạnh vào giữa năm.
+ 2022: Mức tăng cao nhất, đỉnh vào tháng 5-6.
+ 2023: Mức tội phạm giảm vào cuối năm.
- Đường trung bình: Tăng trưởng rõ rệt từ 2020 đến 2022, ổn định vào
2023.
1. crime_by_month <- crime_final %>%
2. mutate(Thang = month(ymd(Ngay_Bao_Cao), label = TRUE, abbr = TRUE)) %>%
3. filter(Nam != 2024) %>% group_by(Nam, Thang) %>%
4. summarise(So_Vu = n(), .groups = "drop")
5. crime_by_month <- crime_by_month %>%
6. group_by(Thang) %>%
7. mutate(Ty_le_So_Vu = So_Vu / sum(So_Vu)) %>% # Tính tỷ lệ số vụ mỗi tháng
8. ungroup()
9. ggplot(crime_by_month, aes(x = Thang, y = factor(Nam), fill = Ty_le_So_Vu)) +
10. geom_tile() +
11. scale_fill_gradient(low = "white", high = "blue") +
12. labs( title = "Tỷ lệ số vụ tội phạm theo tháng qua các năm",
13. x = "Tháng", y = "Năm", fill = "Tỷ lệ số vụ" ) +
14. theme_minimal() +
15. theme( axis.text.x = element_text(angle = 45, hjust = 1, size= 10),
16. plot.title = element_text(face = "bold") )
Giải thích
Kỹ thuật
- Dòng 1-4: Tạo bảng So_Vu theo năm–tháng chuẩn, bước tiền xử lý gọn và
đúng.
- Dòng 5-8: Hiện đang group_by(Thang) rồi chuẩn hóa theo tổng của từng
tháng trên tất cả các năm; nếu muốn tỷ lệ theo tháng trong từng năm, nên
group_by(Nam) rồi tính So_Vu / sum(So_Vu).
- Dòng 9-11: Heatmap với geom_tile hợp lý; nên cân nhắc thang màu dễ
nhìn hơn (vd. scale_fill_viridis_c(option = “C”)) và nhãn tỉ lệ theo
phần trăm (labels = scales::label_percent(accuracy = 0.1)).
- Dòng 12-16: Nhãn, theme tối giản ổn; có thể tăng size tiêu đề, thêm
guides(fill = guide_colorbar(barheight = …)), và giữ thứ tự tháng đã là
factor có thứ tự nên không cần sắp thêm.
Ý nghĩa:
- 2022 có tăng trưởng mạnh trong số vụ tội phạm, đặc biệt là vào tháng 5
và tháng 6, cho thấy mùa vụ tội phạm cao điểm trong năm.
- 2020 và 2021 có tỷ lệ số vụ thấp, phản ánh sự ổn định hoặc giảm tội
phạm.
- 2023 có tỷ lệ ổn định, với ít biến động qua các tháng.
- Tổng thể, 2022 nổi bật với số vụ tội phạm cao, trong khi các năm còn
lại có xu hướng ổn định hơn.
1. crime_by_quarter <- crime_final %>%
2. mutate( Ngay_Bao_Cao = ymd(Ngay_Bao_Cao),
3. Quater = quarter(Ngay_Bao_Cao, with_year = TRUE), Nam = year(Ngay_Bao_Cao)
4. ) %>% filter(Nam != 2024) %>% group_by(Nam, Quater) %>%
5. summarise(So_Vu = n(), .groups = "drop")
6. ggplot(crime_by_quarter, aes(x = factor(Quater), y = So_Vu, group = Nam, color = factor(Nam))) + geom_line(size = 1.2) + geom_point(size = 3) +
7. labs( title = "Số vụ tội phạm theo quý trong từng năm",
8. x = "Quý", y = "Số vụ tội phạm", color = "Năm" ) +
9. scale_x_discrete(labels = c("Q1", "Q2", "Q3", "Q4", "Q1", "Q2", "Q3", "Q4", "Q1", "Q2", "Q3", "Q4", "Q1", "Q2", "Q3", "Q4")) + theme_minimal() +
10. theme( plot.title = element_text(face = "bold", size = 14),
11. axis.text.x = element_text(size = 10, angle = 0, hjust = 0.5),
12. axis.text.y = element_text(size = 12), legend.title = element_text(size = 12),
13. legend.text = element_text(size = 10) )
Giải thích
Kỹ thuật:
- Dòng 1–4: Tiền xử lý đúng, nên sửa tên biến Quater → Quarter, bỏ
with_year=TRUE để trục X rõ hơn.
- Dòng 5: Tính tổng hợp hợp lý, có thể thêm complete() để đủ 4 quý mỗi
năm.
- Dòng 6–8: Vẽ line chart chuẩn, nên dùng Quarter số thay vì factor có
năm.
- Dòng 9–13: Theme ổn, tránh hard-code nhãn X; nên dùng
scale_x_continuous(breaks = 1:4, labels = “Q1–Q4”).
Ý nghĩa:
- 2020: Số vụ tội phạm ổn định trong các quý đầu năm, với sự gia tăng rõ
rệt trong Q2 (quý 2), sau đó giảm xuống. Điều này có thể phản ánh một số
yếu tố tác động (như các biện pháp kiểm soát) trong suốt năm.
- 2021: Tăng trưởng mạnh trong Q2, với số vụ tội phạm đạt đỉnh, sau đó
giảm dần. Quý 1 và quý 3 có mức độ thấp.
- 2022: Cũng có đỉnh điểm tăng trưởng mạnh ở Q2, nhưng số vụ tội phạm
trong quý 4 lại thấp hơn so với các quý còn lại, có thể là dấu hiệu của
các chiến lược kiềm chế hiệu quả vào cuối năm.
- 2023: Đỉnh điểm giảm trong Q2, với số vụ tội phạm không đạt mức cao
trong các quý khác, cho thấy sự ổn định hoặc giảm trong năm này.
1. crime_by_type <- crime_final %>% filter(Nam != 2024) %>% group_by(Nam, Phan_Loai) %>% summarise(So_Vu = n(), .groups = "drop") %>%
2. group_by(Nam) %>% mutate(Ty_le = So_Vu / sum(So_Vu)) %>% ungroup()
3. ggplot(crime_by_type, aes(factor(Nam), Ty_le, fill = factor(Phan_Loai))) +
4. geom_col(position = "fill", color = "white") +
5. geom_text(aes(label = percent(Ty_le, 0.1)),
6. position = position_fill(vjust = 0.5), color = "black", size = 3) +
7. scale_y_continuous(labels = percent_format(accuracy = 1)) +
8. scale_fill_manual(values = c("#e34a33", "#2b8cbe"),
9. labels = c("Tội nghiêm trọng", "Tội không nghiêm trọng")) +
10. labs( title = "Cơ cấu phân loại tội phạm theo năm\n (2020–2023)",
11. subtitle = "So sánh tỷ trọng tội nghiêm trọng và\n không nghiêm trọng trong từng năm", x = "Năm", y = "Tỷ trọng (%)", fill = "Phân loại" ) +
12. theme_minimal(base_size = 13) +
13. theme( plot.title = element_text(face = "bold"),
14. plot.subtitle = element_text(color = "gray30"),
15. plot.caption = element_text(color = "gray50"),
16. panel.grid.minor = element_blank() )
Giải thích
Kỹ thuật:
- Dòng 1–2: Tiền xử lý chuẩn; cân nhắc tidyr::complete(Nam, Phan_Loai,
fill = list(So_Vu=0, Ty_le=0)) để tránh thiếu nhóm; nhớ library(scales)
cho percent().
- Dòng 3–6: position=“fill” + geom_text(position_fill(vjust=.5)) hợp lý;
có thể làm tròn nhãn percent(Ty_le, accuracy=0.1).
- Dòng 7–9: Nhãn trục Y ok; nên dùng bảng màu thân thiện mù màu
(scale_fill_viridis_d() hoặc Brewer) và đảm bảo thứ tự/nhãn của
Phan_Loai khớp màu.
- Dòng 10–16: Nhãn/theme ổn; tránh hard-code “2020–2023” trong tiêu đề
(dùng range(Nam)), và có thể tăng base_size/legend.position=“top” cho dễ
đọc.
Ý nghĩa:
Biểu đồ cho thấy tội nghiêm trọng luôn chiếm tỷ lệ cao (57.6% đến 59.1%)
trong các năm 2020-2023, chiếm phần lớn trong tổng số vụ tội phạm. Tội
không nghiêm trọng chiếm tỷ lệ thấp hơn (40.2% đến 42.4%), và tỷ lệ này
khá ổn định qua các năm. Mặc dù có sự thay đổi nhẹ, xu hướng tội phạm
nghiêm trọng vẫn chiếm ưu thế.
1. top10_desc <- crime_final %>% filter(Nam != 2024) %>%
2. count(Mo_ta_Toi_Pham, name = "So_Vu") %>% slice_max(order_by = So_Vu, n = 10) %>% arrange(So_Vu) %>%
3. mutate( Rank = row_number(),
4. Label = paste0(sprintf("%02d", 11 - Rank), ". ",
5. str_to_title(str_wrap(Mo_ta_Toi_Pham, width = 35))),
6. Label = factor(Label, levels = Label) )
7. ggplot(top10_desc, aes(x = So_Vu, y = Label)) +
8. geom_segment(aes(x = 0, xend = So_Vu, yend = Label),
9. linewidth = 1.1, color = "gray75", lineend = "round") +
10. geom_point(aes(color = So_Vu), size = 4) +
11. geom_text(aes(label = label_number(scale_cut = cut_short_scale())(So_Vu)),
12. nudge_x = max(top10_desc$So_Vu) * 0.1, size = 3.4, family = "", fontface = "bold") + scale_color_gradient(low = "#9ecae1", high = "#08519c", guide = "none") + scale_x_continuous( labels = label_number(scale_cut = cut_short_scale()), expand = expansion(mult = c(0, 0.15)) ) +
13. labs( title = "Top 10 mô tả tội phạm phổ biến nhất (2020–2023)",
14. subtitle = "Lollipop chart — xếp hạng (01–10) bên trái; điểm màu đậm = số vụ lớn", x = "Số vụ phạm tội", y = NULL ) + theme_minimal(base_size = 11) +
15. theme( plot.title = element_text(face = "bold"),
16. plot.subtitle = element_text(color = "gray35"),
17. plot.caption = element_text(color = "gray50"),
18. axis.text.y = element_text(hjust = 0, lineheight = 1.1),
19. panel.grid.major.y = element_blank(), panel.grid.minor = element_blank())
Giải thích
Kỹ thuật:
- Dòng 1–6: Pipeline ổn; đơn giản hóa bằng rank =
dplyr::min_rank(desc(So_Vu)), Label = sprintf(“%02d. %s”, rank,
str_to_title(str_wrap(…))), và Label = forcats::fct_reorder(Label,
So_Vu) (khỏi cần 11 - Rank).
- Dòng 7–12: Lollipop đẹp; giữ expand, cân nhắc coord_cartesian(clip =
“off”) để nhãn không bị cắt và bỏ family = ““; nên tính sẵn nhãn số
trong mutate.
- Dòng 8–12: Gradient ổn nhưng nên dùng bảng màu thân thiện mù màu
(scale_color_viridis_c(guide =”none”)).
- Dòng 13–19: Nhãn/theme tốt; tránh hard-code “2020–2023” (tạo từ
min/max(Nam)), các tùy chỉnh y-label bạn đang dùng là hợp lý.
Ý nghĩa:
Biểu đồ cho thấy trộm cắp xe và các hành vi tấn công đơn giản là các tội
phạm phổ biến nhất, trong khi trộm cắp tài sản và các hành vi tấn công
khác có số vụ ít hơn nhưng vẫn chiếm phần lớn.
1. ggplot(crime_by_status, aes(x = factor(Trang_Thai), y = Ty_le, fill = Trang_Thai)) +
2. geom_bar(stat = "identity", position = "fill") + # Biểu đồ tỷ lệ
3. labs( title = "Cơ cấu tình trạng bắt giữ theo tỷ lệ (2020–2023)",
4. x = "Tình trạng bắt giữ", y = "Tỷ lệ (%)", fill = "Tình trạng bắt giữ"
5. ) + scale_y_continuous(labels = scales::percent) +
6. theme_minimal() + theme( plot.title = element_text(face = "bold", size = 14),
7. axis.text.x = element_text(size = 12, angle = 0, hjust = 0.5),
8. axis.text.y = element_text(size = 12),
9. legend.title = element_text(size = 12),
10. legend.text = element_text(size = 10) )
Giải thích
Kỹ thuật:
- Dòng 1–2: Cấu trúc đúng cho biểu đồ tỷ lệ; có thể bỏ stat=“identity”
nếu dữ liệu đã là tỷ lệ.
- Dòng 3–5: Nhãn rõ; scale_y_continuous(labels = percent_format(accuracy
= 1)) cho đẹp hơn.
- Dòng 6–10: Theme ổn; nên sắp Trang_Thai theo tỷ lệ (fct_reorder) và
dùng bảng màu dễ phân biệt (scale_fill_brewer(palette=“Set2”)).
Ý nghĩa:
Biểu đồ cho thấy tỷ lệ các tình trạng bắt giữ trong giai đoạn 2020–2023
khá đồng đều và ổn định, với không có tình trạng nào chiếm ưu thế vượt
trội, phản ánh một sự phân bố tội phạm đều giữa các tình trạng bắt giữ
trong giai đoạn này
1. ggplot(crime_by_type, aes(x = reorder(Phan_Loai, -So_Vu), y = So_Vu, fill = Phan_Loai)) + geom_bar(stat = "identity") +
2. labs( title = "Tần số tội phạm theo loại",
3. x = "Loại tội phạm", y = "Số vụ tội phạm", fill = "Loại tội phạm"
4. ) + scale_x_discrete(labels = c("1" = "Tội nghiêm trọng", "2" = "Tội nhẹ")) +
5. theme_minimal() + theme( plot.title = element_text(face = "bold", size = 14),
6. axis.text.x = element_text(angle = 0, hjust = 1),
7. axis.text.y = element_text(size = 12), legend.title = element_text(size = 12),
8. legend.text = element_text(size = 10) )
Giải thích
Kỹ thuật:
- Dòng 1: Biểu đồ đúng; nên dùng fct_reorder(Phan_Loai, So_Vu,
.desc=TRUE) thay reorder(…) cho rõ ràng.
- Dòng 2–4: Nhãn đầy đủ; có thể bỏ scale_x_discrete(labels=…) nếu
Phan_Loai đã có tên rõ.
- Dòng 5–8: Theme ổn; có thể xoay nhãn X 30° nếu trùng, và dùng
scale_fill_brewer(palette=“Set2”) cho màu dễ phân biệt.
Ý nghĩa:
Biểu đồ cho thấy rằng phần lớn các vụ tội phạm là tội nghiêm trọng. Điều
này phản ánh một xu hướng trong số liệu về tội phạm, cho thấy các hành
vi nghiêm trọng chiếm ưu thế hơn so với các hành vi tội phạm nhẹ.
1. crime_by_type_year <- crime_final %>% group_by(Nam, Phan_Loai) %>%
2. summarise(So_Vu = n(), .groups = "drop")
3. ggplot(crime_by_type_year, aes(x = as.factor(Nam), y = So_Vu, fill = as.factor(Phan_Loai))) +
4. geom_bar(stat = "identity", position = "dodge", alpha = 0.7) +
5. labs( title = "Phân loại tội phạm theo năm", x = "Năm", y = "Số vụ",
6. fill = "Phân loại") + scale_fill_manual( values = c("#E74C3C", "#3498DB"),
7. labels = c("1" = "Tội nghiêm trọng", "2" = "Không nghiêm trọng") ) +
8. theme_minimal() + theme( plot.title = element_text(face = "bold", size = 16),
9. axis.text.x = element_text(size = 12),legend.title = element_text(size = 12),
10. legend.text = element_text(size = 11) )
Ggiải thích
Kỹ thuật:
- Dòng 1–2: Tính tổng hợp đúng; có thể gọn hơn với count(Nam, Phan_Loai,
name = “So_Vu”) và đảm bảo đủ tổ hợp bằng tidyr::complete(Nam,
Phan_Loai, fill = list(So_Vu=0)).
- Dòng 3–4: Nên dùng geom_col(position = position_dodge(width = .8))
thay geom_bar(stat=“identity”); bỏ alpha để màu rõ hơn.
- Dòng 5–7: Nhãn ổn; tránh gắn nhãn theo mã “1/2”, tốt hơn là đặt tên
mức của Phan_Loai trước hoặc labels = c(“Tội nghiêm trọng”,“Không nghiêm
trọng”).
- Dòng 8–10: Theme ok; cân nhắc legend.position=“top” và
scale_y_continuous(labels = scales::label_number(big.mark=“,”)) để dễ
đọc.
Ý nghĩa:
Biểu đồ cho thấy số vụ tội nghiêm trọng luôn cao hơn không nghiêm trọng
trong các năm. Tỷ lệ tội phạm có xu hướng tăng dần đến năm 2023 rồi giảm
nhẹ vào năm 2024.
### 11. Cơ cấu giới tính nạn nhân theo nhóm tuổi (2020–2023)
1. crime_by_area <- crime_extended %>%
2. group_by(Ma_Khu_Vuc) %>% summarise(So_Vu = n(), .groups = "drop") %>%
3. arrange(desc(So_Vu))
4. ggplot(crime_by_area, aes(x = reorder(Ma_Khu_Vuc, -So_Vu), y = So_Vu, fill = Ma_Khu_Vuc)) + geom_bar(stat = "identity") +
5. labs( title = "Tội phạm theo khu vực", x = "Khu vực", y = "Số vụ tội phạm",
6. fill = "Khu vực" ) + theme_minimal() + theme( plot.title = element_text(face = "bold", size = 14),
7. axis.text.x = element_text(angle = 0, hjust = 1, bold= 1),
8. axis.text.y = element_text(size = 12),
9. legend.title = element_text(size = 12),
10. legend.text = element_text(size = 10) )
Giải thích
Kỹ thuật:
- Dòng 1–3: Tiền xử lý đúng; có thể gọn bằng count(Ma_Khu_Vuc,
name=“So_Vu”, sort=TRUE).
- Dòng 4: Biểu đồ hợp lý; nếu nhiều khu vực, nên tô một màu
(fill=“#3498DB”) thay vì theo khu vực để tránh rối.
- Dòng 5–6: Nhãn rõ ràng, theme ổn. Dòng 7–10: bold=1 không hợp lệ; dùng
element_text(face=“bold”) và xoay nhãn X 30° nếu bị chồng.
Ý nghĩa:
Biểu đồ cho thấy khu vực 1 và 12 có tần suất tội phạm cao nhất, trong
khi các khu vực khác có sự giảm dần về số vụ. Điều này có thể phản ánh
sự phân bố tội phạm không đồng đều giữa các khu vực, có thể do yếu tố
dân cư, kinh tế hoặc các yếu tố xã hội khác.
1. crime_by_gender <- crime_cleaned %>%
2. group_by(Gioi_Tinh_Nan_Nhan) %>%
3. summarise(So_Vu = n(), .groups = "drop")
4. ggplot(crime_by_gender, aes(x = Gioi_Tinh_Nan_Nhan, y = So_Vu, fill = Gioi_Tinh_Nan_Nhan)) + geom_bar(stat = "identity") +
5. labs( title = "Phân bố tội phạm theo giới tính nạn nhân",
6. x = "Giới tính nạn nhân", y = "Số vụ tội phạm",
7. fill = "Giới tính" ) + theme_minimal() + theme(
8. plot.title = element_text(face = "bold", size = 14),
9. axis.text.x = element_text(angle = 0, hjust = 0.5),
10. axis.text.y = element_text(size = 12) )
Giải thích
Kỹ thuật:
- Dòng 1–3: Tổng hợp đúng; có thể gọn hơn với count(Gioi_Tinh_Nan_Nhan,
name=“So_Vu”).
- Dòng 4: Biểu đồ rõ; nếu chỉ 2 giới, nên tô màu thủ công
(scale_fill_manual(values=c(“#e74c3c”,“#3498db”))).
- Dòng 5–7: Nhãn rõ ràng, theme tối giản phù hợp.
- Dòng 8–10: Trình bày ổn; có thể thêm legend.position=“none” để tránh
trùng thông tin với trục X.
Ý nghĩa:
Biểu đồ này cho thấy sự chênh lệch rõ rệt trong số vụ tội phạm giữa các
giới tính, với nữ và nam chiếm phần lớn, còn giới tính khác có tỷ lệ
thấp hơn nhiều.
1. crime_by_area_status %>%
2. filter(Khu_Vuc %in% c("Central", "77th Street")) %>%
3. group_by(Khu_Vuc, Trang_Thai) %>%
4. summarise(Ty_le = sum(Ty_le), .groups = "drop") %>%
5. mutate(Percent = Ty_le / sum(Ty_le) * 100) %>%
6. ggplot(aes(x = "", y = Ty_le, fill = Trang_Thai)) +
7. geom_col(width = 1) + coord_polar("y", start = 0) + facet_wrap(~ Khu_Vuc) +
8. geom_text(aes(label = ifelse(Percent > 3, paste0(round(Percent, 1), "%"), "")), position = position_stack(vjust = 0.5), size = 4,
9. check_overlap = TRUE, angle = 0) + labs( title = "Cơ cấu tình trạng vụ án\n trong từng khu vực (2020–2023)",
10. fill = "Tình trạng vụ án" ) + theme_void() +
11. theme( plot.title = element_text(hjust = 0.5, face = "bold", size = 18, lineheight = 1.2), strip.text = element_text(size = 12, face = "bold"),
12. legend.title = element_text(size = 12),
13. legend.text = element_text(size = 10) )
Giải thích Kỹ thuật:
- Dòng 1–5: Tiền xử lý đúng; nhưng Percent = Ty_le / sum(Ty_le) nên tính
theo từng Khu_Vuc (group_by(Khu_Vuc) trước mutate).
- Dòng 6–7: Dạng pie chart hợp lý; có thể thêm color = “white” trong
geom_col() cho ranh giới rõ hơn.
- Dòng 8–10: Nhãn rõ, điều kiện Percent > 3 hợp lý; có thể tăng vjust
nhẹ để cân giữa lát.
- Dòng 11–13: Theme đẹp; có thể đặt legend.position=“bottom” để tiết
kiệm không gian facet.
Ý nghĩa:
Biểu đồ cung cấp cái nhìn rõ ràng về sự phân bố tình trạng vụ án giữa
hai khu vực “77th Street” và “Central”. Khu vực 77th Street có sự phân
bố đồng đều hơn giữa các tình trạng vụ án, trong khi Central có sự tập
trung cao hơn vào một số tình trạng cụ thể như “AA”.
1. ggplot(crime_by_area_status,
2. aes(x = Trang_Thai, y = reorder(Khu_Vuc, -So_Vu), fill = Ty_le)) +
3. geom_tile(color = "white") + geom_text(aes(label = paste0(Ty_le, "%")),
4. color = "black", size = 3.2) +
5. scale_fill_gradient(low = "#d4f0ff", high = "#0066cc") +
6. labs( title = "Tỷ lệ tình trạng vụ án theo khu vực (2020–2023)",
7. x = "Tình trạng vụ án", y = "Khu vực", fill = "Tỷ lệ (%)") +
8. theme_minimal(base_size = 12) +
9. theme( plot.title = element_text(face = "bold", size = 14, hjust = 0.5),
10. axis.text.x = element_text(angle = 45, hjust = 1),
11. panel.grid = element_blank())
Giải thích
Kỹ thuật: - Dòng 1–4: Cấu trúc heatmap đúng; nên làm tròn nhãn
paste0(round(Ty_le,1), “%”) để gọn hơn.
- Dòng 5: Thang màu hợp lý; có thể thay bằng
scale_fill_viridis_c(option=“C”) cho dễ phân biệt.
- Dòng 6–7: Nhãn rõ, tiêu đề tốt; tránh hard-code “2020–2023” (dùng
range(Nam)).
- Dòng 8–11: Theme sạch; angle=45 hợp lý, có thể thêm
legend.position=“top” để bố cục cân đối.
Ý nghĩa:
Biểu đồ này cho thấy sự phân bố tỷ lệ tình trạng vụ án theo từng khu
vực, với phần lớn các khu vực có tỷ lệ tội nghiêm trọng (AA) cao. Các
khu vực như Foothill, Van Nuys và Hollenbeck có sự phân bố tội phạm
nghiêm trọng rõ rệt, trong khi những khu vực khác có sự phân bổ đa dạng
hơn với các tình trạng vụ án khác như AO, IC.
1. df_status_type <- crime_final %>%
2. filter(Nam != 2024) %>% mutate(
3. Phan_Loai = recode(Phan_Loai, "1" = "Tội nghiêm trọng", "2" = "Tội không nghiêm trọng"), Trang_Thai = fct_lump_n(Trang_Thai, n = 6)
4. ) %>% count(Phan_Loai, Trang_Thai, name = "So_Vu") %>%
5. group_by(Phan_Loai) %>% mutate(Ty_le = So_Vu / sum(So_Vu) * 100)
6. ggplot(df_status_type, aes(x = Phan_Loai, y = Ty_le, fill = Trang_Thai)) +
7. geom_col(position = "fill", color = "white") +
8. geom_text(aes(label =ifelse(Ty_le > 3, paste0(round(Ty_le, 1), "%"),"")),
9. position = position_fill(vjust = 0.5), size = 3) +
10. scale_fill_brewer(palette = "Set2") +
11. scale_y_continuous(labels = percent_format(scale = 100)) +
12. labs( title = "Phân bố trạng thái vụ án theo\n loại tội phạm (2020–2023)",
13. subtitle = "Biểu đồ cột chồng 100% — thể hiện tỷ trọng từng\n trạng thái trong mỗi loại tội", x = "Phân loại tội phạm", y = "Tỷ trọng (%)",
14. fill = "Trạng thái hồ sơ" ) + theme_minimal() +
15. theme( plot.title = element_text(face = "bold", hjust = 0, lineheight = 1.1),
16. plot.subtitle = element_text(color = "gray35", hjust = 0, size = 12, lineheight = 1.2), plot.caption = element_text(color = "gray50", hjust = 0),
17. legend.position = "top" )
Giải thích
Kỹ thuật:
- Dòng 1–5: Tiền xử lý ổn; tránh “chuẩn hoá kép”: dùng y = So_Vu +
position=“fill” hoặc giữ y = Ty_le và bỏ position=“fill”. Nhớ
library(forcats) cho fct_lump_n().
- Dòng 6–9: Cột chồng 100% hợp lý; nhãn nên làm tròn: round(..,1). Nếu
chuyển sang y=So_Vu, tạo nhãn từ tỷ lệ tính sẵn theo nhóm
Phan_Loai.
- Dòng 10–11: Màu Set2 dễ đọc; scale_y_continuous(labels =
scales::percent_format(accuracy = 1)) là đủ (không cần scale =
100).
- Dòng 12–17: Nhãn/theme đẹp; tránh hard-code “2020–2023” (lấy từ
range(Nam)), legend đặt top là hợp lý.
Ý nghĩa:
Biểu đồ này cho thấy tội không nghiêm trọng có tỷ lệ vụ án đang điều tra
(AO) cao hơn nhiều so với tội nghiêm trọng, trong khi tội nghiêm trọng
lại có tỷ lệ vụ án chưa giải quyết cao hơn. Các trạng thái khác như AA,
CC, và JA có tỷ lệ thấp hơn, nhưng vẫn chiếm một phần nhất định trong
mỗi nhóm tội phạm.
1. crime_final <- crime_final %>%
2. mutate(
3. So_Toi_Danh = rowSums(select(., starts_with("Ma_Toi_Pham")) != "0", na.rm = TRUE) )
4. # Đếm số vụ án có 1, 2, 3, 4 tội danh và tính tỷ lệ phần trăm
5. statistics <- crime_final %>%
6. count(So_Toi_Danh) %>%
7. mutate(Ty_le = n / sum(n) * 100)
8. statistics <- statistics %>%
9. mutate(vjust_position = ifelse(Ty_le > 90, 1, -0.5))
10. # Vẽ biểu đồ tỷ lệ phần trăm của mỗi nhóm tội danh
11. ggplot(statistics, aes(x = factor(So_Toi_Danh), y = Ty_le, fill = factor(So_Toi_Danh))) +
12. geom_bar(stat = "identity", color = "black") +
13. geom_text(aes(label = paste0(round(Ty_le, 1), "%")), vjust = statistics$vjust_position, size = 5) +
14. labs(
15. title = "Tỷ lệ phần trăm vụ án theo số lượng tội danh",
16. x = "Số tội danh",
17. y = "Tỷ lệ (%)",
18. fill = "Số tội danh"
19. ) +
20. theme_minimal() +
21. theme(
22. plot.title = element_text(size = 16, face = "bold"),
23. axis.title = element_text(size = 14),
24. axis.text = element_text(size = 12))
Giải thích
Kỹ thuật
- Dòng 1–3: Tính So_Toi_Danh nên dùng số thay vì so sánh chuỗi
“0”: mutate(So_Toi_Danh = across(starts_with(“Ma_Toi_Pham”)) |> {(.)
rowSums(. != 0,
na.rm = TRUE)}).
- Dòng 5–7: Tổng hợp OK; có thể count(So_Toi_Danh, name=“So_Vu”) |>
mutate(Ty_le = So_Vu/sum(So_Vu)100).
- Dòng 8–9: Không nên gọi vjust từ data bên ngoài layer; tính trong data
rồi dùng aes(vjust = vjust_position) trong geom_text().
- Dòng 11–24: Dùng geom_col() (gọn), legend.position=“none” (fill trùng
trục X), và scale_y_continuous(labels = scales::percent_format(accuracy
= 0.1)) để hiển thị phần trăm đẹp hơn.
Ý nghĩa:*
Biểu đồ cho thấy rằng phần lớn các vụ án trong dữ liệu này có 1 tội
danh, một số ít có 2 tội danh, và rất ít vụ án có 3 tội danh. Không có
vụ án nào có 4 tội danh. Điều này phản ánh rằng các vụ án trong bộ dữ
liệu này có xu hướng đơn giản hơn, với ít tội danh trong mỗi vụ.
1. crime_by_year <- crime_renamed %>% mutate(Nam = year(ymd(Ngay_Bao_Cao))) %>%
2. group_by(Nam) %>% summarise(So_Vu = n(), .groups = "drop")
3. ggplot(crime_by_year, aes(x = So_Vu, y = factor(Nam))) +
4. geom_col(fill = "#FFCC99") + labs( title = "Số vụ tội phạm theo năm báo cáo",
5. x = "Số vụ tội phạm", y = "Năm" ) + theme_minimal(base_size = 12) +
6. theme(plot.title = element_text(face = "bold"))
Giải thích
Kỹ thuật:
- Dòng 1–2: Tiền xử lý gọn, đúng; có thể thêm arrange(Nam) để trục Y
hiển thị theo thời gian.
- Dòng 3–4: Biểu đồ ngang rõ ràng; dùng geom_col() hợp lý, màu nhẹ dễ
nhìn.
- Dòng 5: Nhãn đầy đủ, theme tối giản ổn.
- Dòng 6: Có thể thêm axis.text.y = element_text(face=“bold”) hoặc
geom_text(aes(label=So_Vu), hjust=-0.1) để biểu đồ trực quan hơn.
Ý nghĩa:
Biểu đồ cho thấy số vụ tội phạm tăng mạnh trong năm 2024, với số vụ cao
nhất trong các năm từ 2020 đến 2024. Các năm trước đó (2020-2023) có xu
hướng giảm dần về số vụ tội phạm.
1. crime_by_code <- crime_renamed %>% group_by(Ma_Toi_Pham_1) %>%
2. summarise(So_Vu = n(), .groups = "drop") %>% arrange(desc(So_Vu)) %>%
3. slice_head(n = 10)
4. ggplot(crime_by_code, aes(x = So_Vu, y = reorder(Ma_Toi_Pham_1, So_Vu), fill = So_Vu)) + geom_col() + labs( title = "Top 10 mã tội phạm phổ biến nhất",
5. x = "Số vụ tội phạm", y = "Mã tội phạm" ) +
6. scale_fill_gradient(low = "lightcoral", high = "darkred") + theme_minimal(base_size = 12) + theme(plot.title = element_text(face = "bold"))
Giải thích
Kỹ thuật
- Dòng 1–2: Tiền xử lý gọn, đúng; có thể thêm arrange(Nam) để trục Y
đúng thứ tự thời gian.
- Dòng 3–4: Biểu đồ ngang hợp lý; geom_col() thay vì
geom_bar(stat=“identity”) là chuẩn.
- Dòng 5: Nhãn rõ, theme tối giản đẹp.
- Dòng 6: Có thể thêm axis.text.y = element_text(face=“bold”) hoặc
fill=“#F4A261” cho màu ấm dễ nhìn hơn.
Ý nghĩa:
Biểu đồ thể hiện top 10 mã tội phạm phổ biến nhất, trong đó mã 510 có số
vụ cao nhất, vượt qua 100,000 vụ. Các mã còn lại có số vụ giảm dần nhưng
vẫn đóng góp đáng kể. Biểu đồ cho thấy sự phân bố không đều, với một số
mã chiếm ưu thế lớn trong tổng số vụ tội phạm.
1. crime_by_place <- crime_renamed %>% group_by(Ma_Khu_Vuc) %>%
2. summarise(So_Vu = n(), .groups = "drop") %>% arrange(desc(So_Vu)) %>%
3. slice_head(n = 10)
4. ggplot(crime_by_place, aes(x = So_Vu, y = reorder(Ma_Khu_Vuc, So_Vu), fill = So_Vu)) + geom_col() + labs( title = "Top 10 địa điểm xảy ra tội phạm nhiều nhất", x = "Số vụ", y = "Mô tả địa điểm" ) + scale_fill_gradient(low = "lightblue", high = "darkblue") + theme_minimal(base_size = 12) +
5. theme(plot.title = element_text(face = "bold"))
Giải thích
Kỹ thuật:
- Dòng 1–3: Có thể gọn bằng count(Ma_Khu_Vuc, name=“So_Vu”, sort=TRUE)
|> slice_max(So_Vu, n=10).
- Dòng 4: fill = So_Vu tạo legend thừa → thêm guides(fill = “none”); đổi
scale_fill_viridis_c() thân thiện mù màu.
- Dòng 4: Thêm nhãn số geom_text(aes(label = scales::comma(So_Vu)),
hjust = -0.1) + expand = expansion(mult = c(0, .05)).
- Nhãn: đổi y = “Khu vực” (đúng với Ma_Khu_Vuc).
Ý nghĩa:
Biểu đồ cho thấy khu vực “Central” (1) có số vụ tội phạm cao nhất, vượt
qua 60,000 vụ, trong khi các khu vực như “Rampart” (2) và “Southwest”
(3) có số vụ thấp hơn. Màu sắc gradient giúp phân biệt các khu vực có số
vụ nhiều nhất.
1. final <- crime_final %>%
2. mutate(Nhom_Tuoi = factor(Nhom_Tuoi))
3. ggplot(final, aes(x = Nhom_Tuoi, y = Tuoi_Nan_Nhan, fill = Nhom_Tuoi)) +
4. geom_boxplot() +
5. labs(title = "Mối quan hệ giữa tuổi của nạn nhân và\n số tội danh trong mỗi vụ án", x = "Nhóm tuổi", y = "Tuổi của nạn nhân") +
6. theme_minimal() +
7. theme( plot.title = element_text(size = 16, face = "bold", lineheight = 1.1),
8. axis.title = element_text(size = 14),
9. axis.text = element_text(size = 12),
10. axis.text.x = element_blank() )
Giải thích
Kỹ thuật:
- Dòng 1–2: Tiền xử lý đúng; có thể thêm forcats::fct_reorder(Nhom_Tuoi,
Tuoi_Nan_Nhan) nếu muốn sắp theo trung vị.
- Dòng 3–4: geom_boxplot() hợp lý; nếu nhiều nhóm, nên thêm
outlier.color=“gray50”, alpha=0.8 để đồ thị sạch hơn.
- Dòng 5: Tiêu đề sai nội dung — nên đổi thành “Phân bố tuổi nạn nhân
theo nhóm tuổi” cho đúng ý.
- Dòng 6–10: Theme ổn; nếu muốn hiển thị nhãn trục X, bỏ axis.text.x =
element_blank() hoặc xoay nhẹ 30°.
Ý nghĩa:
- Nhóm tuổi “Cao niên (60-74)” có độ tuổi trung bình cao nhất, với sự
phân bổ rộng và có một vài giá trị cực trị.
- Nhóm tuổi “Dưới 10 tuổi” có độ tuổi rất thấp, nhưng độ phân tán khá
rộng.
- Các nhóm khác như Già (75+) và Thanh niên (18-29) có sự phân bố tuổi
khá đồng đều, nhưng độ tuổi của nạn nhân trong các nhóm này có xu hướng
tăng dần theo số tội danh.
1. # Đọc 3 sheet vào 3 biến riêng
2. sheet1 <- read_excel("D:/ngongulaptrinh/VNM/VNM.xlsx", sheet = "CDKT")
3. sheet2 <- read_excel("D:/ngongulaptrinh/VNM/VNM.xlsx", sheet = "HDKD")
4. sheet3 <- read_excel("D:/ngongulaptrinh/VNM/VNM.xlsx", sheet = "LCTT")
5. # Gộp 3 bảng lại thành một (ví dụ gộp theo hàng - row bind)
6. vnm <- sheet1 %>%
7. full_join(sheet2, by = names(sheet1)[1]) %>%
8. full_join(sheet3, by = names(sheet1)[1]) %>%
9. arrange(!!sym(names(sheet1)[1]))
10. name_map <- tibble::tibble(ten_goc = names(vnm)); names(vnm) <- janitor::make_clean_names(names(vnm)); name_map$ten_moi <- names(vnm); head(name_map, 12)
1. # Kiểm tra kết quả
2. head(vnm,10)
Giải thích
Kỹ thuật:
- Dòng 1–3: read_excel() — đọc ba sheet riêng biệt từ cùng một file
VNM.xlsx, lần lượt là CDKT, HDKD và LCTT. Mỗi sheet đại diện cho một báo
cáo tài chính khác nhau của Vinamilk.
- Dòng 6–9: sử dụng full_join() để gộp ba bảng dữ liệu lại dựa trên cột
đầu tiên (thường là Năm), đảm bảo không mất dữ liệu giữa các sheet. Hàm
arrange() giúp sắp xếp lại theo thứ tự thời gian tăng dần.
- Dòng 11: tibble() — tạo bảng ánh xạ tên cột gốc, lưu lại để so sánh
trước và sau khi đổi tên.
- Dòng 11 : make_clean_names() — chuẩn hóa toàn bộ tên cột (bỏ dấu, thay
khoảng trắng bằng “_”), tránh lỗi mã hóa tiếng Việt.
- Dòng 12: gán tên mới vào bảng name_map, sau đó dùng head() để xem
nhanh 12 dòng đầu tiên trong bảng ánh xạ tên biến.
- Dòng 14: head(vnm, 10) — xem 10 dòng đầu của dữ liệu sau khi gộp, đảm
bảo quá trình kết nối thành công.
Ý nghĩa:
Đoạn mã thực hiện bước tiền xử lý dữ liệu tài chính: đọc, gộp, chuẩn hóa
và kiểm tra.
Cách gộp này giúp tập hợp toàn bộ dữ liệu 3 báo cáo (CĐKT, HĐKD, LCTT)
theo từng năm, tạo nền tảng cho các bước phân tích tiếp theo.
Việc chuẩn hóa tên biến giúp R xử lý mượt tiếng Việt, hạn chế lỗi font
hoặc lỗi truy cập cột.
1. bien_chinh <- c(
2. "Năm",
3. "tong_cong_tai_san", "no_phai_tra", "von_chu_so_huu")
4. vnm %>%
5. dplyr::select(dplyr::any_of(bien_chinh)) %>%
6. head(10)
Giải thích
Kỹ thuật:
- Dòng 1: Chọn 4 biến chính: Năm, Tổng tài sản, Nợ phải trả, Vốn chủ sở
hữu .
- Dòng 5: Dùng select(any_of()) để lấy các cột này và
head(10) xem 10 dòng đầu.
Ý nghĩa:
Kiểm tra nhanh quy mô và cấu trúc tài chính từng năm.
Đảm bảo dữ liệu đã gộp đúng và cân đối giữa tài sản – nguồn vốn.
1. dim(vnm)
## [1] 10 154
Giải thích
dim(vnm)
Trả về số dòng và số cột của dữ liệu.
Cung cấp thông tin về quy mô mẫu dữ liệu, hỗ trợ kiểm tra xem dữ liệu đã
được nhập đầy đủ chưa.
1. if ("Năm" %in% names(vnm)) str(vnm$Nam)
Giải thích
- str(vnm\$Năm) Kiểm tra riêng kiểu dữ liệu của biến Năm
(numeric, character, hay date).
Giúp đảm bảo rằng cột năm đang ở định dạng số, cần thiết cho các phép
nhóm, lọc và phân tích theo thời gian.
1. sum(is.na(vnm))
## [1] 122
Giải thích
-sum(is.na(vnm))
Đếm tổng số giá trị bị thiếu (NA) trong toàn bộ bảng dữ liệu.
Cho biết mức độ hoàn thiện của dữ liệu; nếu số lượng NA lớn, cần xử lý
trước khi thống kê.
1. if ("Năm" %in% names(vnm)) table(duplicated(vnm$Năm))
Giải thích
-table(duplicated(vnm\$Năm))
Kiểm tra các giá trị trùng lặp trong biến Năm.
Nếu có dòng trùng, cần xem xét gộp hoặc loại bỏ để đảm bảo mỗi năm chỉ
có một quan sát đại diện, giữ cho dữ liệu nhất quán khi phân tích chuỗi
thời gian. Nhưng kết quả cho thấy không có dòng trùng.
1. bien <- c(
2. "nam", "tong_cong_tai_san", "no_phai_tra", "von_chu_so_huu")
3. vnm %>%
4. dplyr::select(dplyr::any_of(bien_chinh)) %>%
5. tail(10)
Giải thích
Kỹ thuật:
- Dòng 1: Tạo danh sách biến chính gồm Năm, Tổng tài sản, Nợ phải trả,
Vốn chủ sở hữu.
- Dòng 4-5: select(any_of()) chọn các cột này;
tail(10) hiển thị 10 dòng cuối trong bảng dữ liệu.
Ý nghĩa:
Giúp kiểm tra dữ liệu giai đoạn gần nhất (các năm cuối).
Dùng để xác nhận tính liên tục và hợp lệ của dữ liệu trước khi phân tích
xu hướng tài chính.
1. table(map_chr(vnm, ~ class(.x)[1]))
##
## character numeric POSIXct
## 27 126 1
Giải thích
Kỹ thuật:
- map_chr(...class(.x)[1]) lấy kiểu dữ liệu của từng
cột.
-table() đếm số lượng cột theo từng kiểu.
Ý nghĩa:
Giúp xem bao nhiêu cột là số, chữ, hay ngày, phục vụ chuẩn hóa dữ
liệu.
1. # Tạo cột năm (YYYY) từ cột POSIXct 'nam'
2. vnm <- vnm %>%
3. mutate(n = as.numeric(format(as.Date(nam, tz = "UTC"), "%Y")))
4. # Kiểm tra nhanh
5. vnm %>% select(nam, n) %>% head()
Giải thích
Kỹ thuật:
- Dòng 3:ymd(Năm) chuyển chuỗi ngày (ví dụ “2020-12-31”) về
định dạng ngày chuẩn.
year(...) trích riêng phần năm (YYYY).
suppressWarnings(...) ẩn cảnh báo nếu một vài dòng không
đúng định dạng ngày.
- Dòng 4: filter(!is.na(Năm)) loại bỏ dòng trống hoặc lỗi
khi chuyển đổi.
- Dòng 5: distinct(Năm, .keep_all = TRUE) nếu còn trùng
năm, chỉ giữ 1 dòng.
- Dòng 6: arrange(Năm) sắp xếp lại theo thứ tự năm tăng
dần.
Ý nghĩa:
Giúp đồng nhất định dạng thời gian trong toàn bộ dữ liệu, đảm bảo mỗi
quan sát đại diện cho một năm duy nhất.
1. vnm <- vnm %>%
2. mutate(across(where(is.character) & !matches("^(Năm|Nam|Year|Date|Ngay|date|ngay)$"),
3. ~ suppressWarnings(parse_number(.x))))
Giải thích
Kỹ thuật:
- Dòng 2: where(is.character): chọn tất cả cột có kiểu chuỗi ký
tự.
!matches("\^(Năm\|Nam\|Year\|Date\|Ngay\|date\|ngay)\$")
loại trừ các cột có tên liên quan đến thời gian.
- Dòng 4:parse_number(.x) tự động chuyển ký tự sang số, bỏ
dấu phẩy, dấu cách, ký tự đặc biệt.
suppressWarnings() ẩn cảnh báo nhỏ khi gặp ô trống hoặc ký
hiệu không phải số.
Ý nghĩa:
Giúp chuẩn hóa kiểu dữ liệu trong toàn bộ bộ dữ liệu tài chính, đảm bảo
mọi biến định lượng đều ở dạng số.
Cột Năm vẫn giữ nguyên định dạng thời gian, giúp phân tích theo chuỗi
năm chính xác.
1. vnm <- vnm %>%
2. # Chuyển dấu "-" trong các cột ký tự thành NA
3. mutate(across(where(is.character) & !matches("^(Năm|Nam|Year|Date|Ngay|date|ngay)$"),
4. ~ na_if(.x, "-"))) %>%
5. # Thay toàn bộ NA trong các cột số bằng 0
6. mutate(across(where(is.numeric) & !matches("^(Năm|Nam|Year|Date|Ngay|date|ngay)$"),
7. ~ dplyr::coalesce(.x, 0)))
Giải thích
Kỹ thuật:
- Dòng 1: library(stringr): nạp một gói cần thiết làm việc với chuỗi ký
tự (stringr).
- Dòng 2-6: where(is.character): chọn những cột có kiểu dữ liệu là ký tự
(chữ hoặc chuỗi).
!matches(…): loại bỏ các cột liên quan đến thời gian như Năm, Date, Ngày
để không bị tác động trong quá trình xử lý.
na_if(.x, “-”): nếu ô dữ liệu chứa dấu “–”, hệ thống sẽ thay bằng giá
trị thiếu (NA).
- Dòng 9: where(is.numeric): chọn tất cả các cột dạng số trong bảng dữ
liệu.
coalesce(.x, 0): thay toàn bộ giá trị thiếu (NA) trong các cột số bằng
0.
Ý nghĩa:
Tất cả các dấu “–” trong dữ liệu được thay bằng giá trị thiếu NA, sau đó
những ô trống này được điền bằng 0. Bộ dữ liệu trở nên đầy đủ, không còn
giá trị thiếu hoặc ký hiệu đặc biệt, giúp cho việc thống kê, tính toán
và phân tích tài chính được thực hiện chính xác và ổn định hơn.
1. library(scales)
2. vnm <- vnm %>%
3. mutate(across(where(is.numeric) & !matches("Năm"), ~ .x / 1e9))
4. # Hiển thị kết quả có dấu phân cách hàng nghìn
5. vnm %>%
6. mutate(across(where(is.numeric) & !matches("Năm"),
7. ~ comma(.x, accuracy = 0.01))) %>%
8. select("tong_cong_tai_san", "no_phai_tra", "von_chu_so_huu") %>%
9. head()
Giải thích
Kỹ thuật:
- Dòng 2-3: across(where(is.numeric), ~ .x / 1e9) → Chia toàn bộ các
biến số cho 1 tỷ để chuyển từ đồng → tỷ đồng.
- Dòng 5-7: scales::comma() → Định dạng số có dấu phân cách hàng nghìn
(,) giúp dễ đọc hơn.
accuracy = 0.01 → Giữ hai chữ số thập phân (có thể chỉnh tuỳ ý).
- Dòng 8: select() → chọn cột để hiển thị.
- Dòng 9: head() → Hiển thị vài dòng đầu để kiểm tra kết quả.
Ý nghĩa:
Việc đưa các giá trị về tỷ đồng giúp dễ diễn giải, đặc biệt khi vẽ đồ
thị hoặc tính tỷ lệ phần trăm.
Định dạng số có dấu phân cách giúp tăng tính trực quan và độ chính xác
khi trình bày kết quả trong báo cáo tài chính.
1. targets <- c(
2. "nam",
3. "tong_cong_tai_san",
4. "tai_san_ngan_han",
5. "tien_va_cac_khoan_tuong_duong_tien",
6. "cac_khoan_dau_tu_tai_chinh_ngan_han",
7. "cac_khoan_phai_thu_ngan_han",
8. "hang_ton_kho",
9. "tai_san_do_dang_dai_han",
10. "chi_phi_tra_truoc_dai_han",
11. "tai_san_co_dinh_huu_hinh",
12. "tong_nguon_von",
13. "doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02",
14. "no_phai_tra",
15. "no_ngan_han",
16. "vay_ngan_han",
17. "no_dai_han",
18. "von_chu_so_huu",
19. "loi_nhuan_sau_thue_chua_phan_phoi",
20. "loi_nhuan_sau_thue_tndn",
21. "luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh",
22. "luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu",
23. "luu_chuyen_tien_thuan_tu_hoat_dong_tai_chinh"
24. )
25. have <- intersect(targets, names(vnm))
26. missing <- setdiff(targets, names(vnm))
27. vnm20 <- vnm[, have, drop = FALSE]
28. cat("Số biến chọn được:", length(have), "\n")
## Số biến chọn được: 22
1. if (length(missing)) {
2. cat("Biến chưa tìm thấy (kiểm tra tên cột trong dữ liệu):\n")
3. print(missing)
4. }
5. # Xem nhanh cấu trúc kết quả
6. str(vnm20)
## tibble [10 × 22] (S3: tbl_df/tbl/data.frame)
## $ nam : POSIXct[1:10], format: "2015-12-31" "2016-12-31" ...
## $ tong_cong_tai_san : num [1:10] 27478 29379 34667 37366 44700 ...
## $ tai_san_ngan_han : num [1:10] 16732 18674 20307 20560 24722 ...
## $ tien_va_cac_khoan_tuong_duong_tien : num [1:10] 1359 655 963 1523 2665 ...
## $ cac_khoan_dau_tu_tai_chinh_ngan_han : num [1:10] 8668 10454 10562 8674 12436 ...
## $ cac_khoan_phai_thu_ngan_han : num [1:10] 2685 2866 4592 4639 4503 ...
## $ hang_ton_kho : num [1:10] 3810 4522 4021 5526 4983 ...
## $ tai_san_do_dang_dai_han : num [1:10] 844 993 1929 868 944 ...
## $ chi_phi_tra_truoc_dai_han : num [1:10] 417 460 612 751 679 ...
## $ tai_san_co_dinh_huu_hinh : num [1:10] 7796 7916 10291 13048 13744 ...
## $ tong_nguon_von : num [1:10] 27478 29379 34667 37366 44700 ...
## $ doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02: num [1:10] 40080 46794 51041 52562 56318 ...
## $ no_phai_tra : num [1:10] 6554 6973 10794 11095 14969 ...
## $ no_ngan_han : num [1:10] 6004 6457 10196 10640 14443 ...
## $ vay_ngan_han : num [1:10] 1476 1333 268 1060 5351 ...
## $ no_dai_han : num [1:10] 550 515 599 455 526 ...
## $ von_chu_so_huu : num [1:10] 20924 22406 23873 26271 29731 ...
## $ loi_nhuan_sau_thue_chua_phan_phoi : num [1:10] 5392 5582 5737 7155 7875 ...
## $ loi_nhuan_sau_thue_tndn : num [1:10] 7770 9364 10278 10206 10554 ...
## $ luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh : num [1:10] 7659 8390 9602 8140 11410 ...
## $ luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu : num [1:10] -2126 -1946 -1779 -1045 -6748 ...
## $ luu_chuyen_tien_thuan_tu_hoat_dong_tai_chinh : num [1:10] -1764 -7141 -7535 -6535 -3516 ...
Giải thích
Kỹ thuật
- Dòng 1–24: targets <- c(...) — khai báo danh sách 22
biến tài chính mục tiêu (năm, tài sản, nợ, vốn, doanh thu, lợi nhuận,
dòng tiền…). Đây là bước đặt tiêu chí lọc để chỉ làm việc với các cột
quan trọng.
- Dòng 25: lấy giao giữa targets và tên cột thực tế của vnm, đảm bảo chỉ
giữ những biến đang tồn tại trong dữ liệu để tránh lỗi do sai tên.
- Dòng 26: liệt kê những biến bị thiếu (có trong targets nhưng không có
trong vnm), hỗ trợ phát hiện sai chính tả/khác chuẩn.
- Dòng 27: tạo tập con chỉ gồm các cột đã xác thực (have).
Tham số drop = FALSE giữ cấu trúc data frame ngay cả khi chỉ có một cột
được chọn.
- Dòng 28: in số lượng biến đã chọn thành công để kiểm chứng
nhanh.
- Dòng 29–32: nếu có biến thiếu thì in danh sách các cột thiếu để người
dùng rà soát và chuẩn hoá tên cột trong file nguồn.
- Dòng 34: kiểm tra cấu trúc dữ liệu sau lọc (tên biến, kiểu dữ liệu, số
quan sát) để chắc chắn dữ liệu đã sẵn sàng cho các bước phân tích tiếp
theo.
Ý nghĩa:
Đây là bước tiền xử lý có chọn lọc, giúp tập trung vào các chỉ tiêu tài
chính cốt lõi, giảm nhiễu và tránh lỗi truy cập biến.
Việc tách have/missing tạo cơ chế tự kiểm tra: dữ liệu thay đổi hoặc tên
cột lệch chuẩn sẽ được phát hiện ngay.
Bộ biến bao quát ba trụ cột: (i) Tài sản, (ii) Nguồn vốn & nợ, (iii)
Hiệu quả & dòng tiền — tạo nền tảng cho mô tả thống kê, phân tích cơ
cấu và mô hình dự báo.
Cách viết này giúp quy trình linh hoạt và tái lập: khi cập nhật dữ liệu
mới, chỉ cần chạy lại khối lệnh để đồng bộ danh mục biến.
1. vnm20 <- vnm20 %>%
2. mutate(
3. current_ratio = `tai_san_ngan_han` / `no_ngan_han`,
4. cash_ratio = `tien_va_cac_khoan_tuong_duong_tien` / `no_ngan_han`)
5. head(vnm20[, c("current_ratio", "cash_ratio")])
Giải thích
Kỹ thuật:
- Dòng 2_4: tạo ra hai biến mới trong bảng dữ liệu vnm20.
current_ratio: được tính bằng Tài sản ngắn hạn / Nợ ngắn
hạn. → Đây là hệ số thanh toán hiện hành, đo mức độ đảm bảo của tài sản
lưu động so với nợ phải trả trong năm.
cash_ratio: được tính bằng Tiền và các khoản tương đương
tiền / Nợ ngắn hạn. → Đây là hệ số thanh toán bằng tiền, phản ánh khả
năng đáp ứng nhanh các khoản nợ ngắn hạn chỉ bằng tiền mặt.
- Dòng 5: hiển thị 6 dòng đầu tiên của hai biến vừa tạo, giúp bạn kiểm
tra kết quả tính toán.
Ý nghĩa:
- Hệ số thanh toán hiện hành (Current Ratio)
Các giá trị > 1 cho thấy doanh nghiệp có khả năng thanh toán ngắn hạn
tốt, tức là tài sản lưu động đủ bù đắp nợ phải trả.
Tỷ lệ quanh 2 lần được xem là an toàn và lành mạnh, phản ánh khả năng
duy trì dòng vốn lưu động ổn định.
- Hệ số thanh toán bằng tiền (Cash Ratio)
Các giá trị trong khoảng 0.09–0.22 là mức hợp lý, nghĩa là chỉ 9–22% nợ
ngắn hạn có thể được thanh toán ngay bằng tiền mặt.
- Tổng thể, hai hệ số cho thấy VNM có cấu trúc vốn lưu động lành mạnh,
duy trì cân bằng giữa khả năng thanh toán và hiệu quả sử dụng tiền
mặt..
1. vnm20 <- vnm20 %>%
2. mutate(
3. debt_ratio = `no_phai_tra` / `tong_nguon_von`,
4. equity_ratio = `von_chu_so_huu` / `tong_nguon_von`,
5. short_debt_ratio = `vay_ngan_han` / `no_ngan_han`,
6. long_debt_to_equity = `no_dai_han` / `von_chu_so_huu`
7. )
8. head(vnm20[, c("debt_ratio", "equity_ratio", "short_debt_ratio", "long_debt_to_equity")])
Giải thích kỹ thuật
debt_ratio: là tỷ lệ giữa Nợ phải trả / Tổng nguồn vốn. → Đo lường mức độ sử dụng nợ trong tổng cơ cấu vốn của doanh nghiệp.
equity_ratio: là tỷ lệ Vốn chủ sở hữu / Tổng nguồn vốn. → Phản ánh phần vốn tự có chiếm trong tổng nguồn vốn.
short_debt_ratio: là tỷ lệ Vay ngắn hạn / Nợ ngắn hạn. → Cho thấy nợ ngắn hạn đến từ nguồn vay bao nhiêu phần trăm.
long_debt_to_equity: là tỷ lệ Nợ dài hạn / Vốn chủ sở hữu. → Đánh giá mức độ đòn bẩy tài chính dài hạn.
Ý nghĩa:
Tỷ lệ nợ trên tổng nguồn vốn (Debt ratio: 0.23 – 0.33) → Doanh nghiệp chỉ dùng khoảng 23–33% vốn vay trong tổng nguồn vốn. → Cho thấy mức đòn bẩy tài chính thấp, cấu trúc vốn an toàn và thận trọng.
Tỷ lệ vốn chủ sở hữu (Equity ratio: 0.66 – 0.76) → Vốn tự có chiếm phần lớn (66–76%) trong nguồn vốn. → Phản ánh khả năng tự chủ tài chính cao, ít phụ thuộc vào vốn vay.
Tỷ lệ vay ngắn hạn trong nợ ngắn hạn (Short debt ratio: 0.02 – 0.51) → Biến động giữa các năm cho thấy doanh nghiệp linh hoạt trong chính sách tài trợ vốn lưu động. → Các giai đoạn tỷ lệ cao thể hiện nhu cầu vốn ngắn hạn tăng, nhưng vẫn trong mức kiểm soát rủi ro hợp lý.
Tỷ lệ nợ dài hạn trên vốn chủ sở hữu (Long debt to equity: 0.017 – 0.026) → Rất thấp, cho thấy doanh nghiệp hạn chế sử dụng nợ dài hạn. → Phản ánh chiến lược tài chính thận trọng, giảm rủi ro gánh nặng lãi vay dài hạn.
Tổng thể: → Vinamilk có cấu trúc vốn lành mạnh, rủi ro tài chính thấp, và khả năng tự tài trợ vốn cao. → Đây là đặc trưng của doanh nghiệp ổn định, ít phụ thuộc nợ, hướng đến phát triển bền vững.
1. library(dplyr)
2. vnm20 <- vnm20 %>%
3. mutate(
4. inventory_share = `hang_ton_kho` / `tai_san_ngan_han`,
5. ppe_to_assets = `tai_san_co_dinh_huu_hinh` / `tong_cong_tai_san`,
6. re_to_equity = `loi_nhuan_sau_thue_chua_phan_phoi` / `von_chu_so_huu`)
7. head(vnm20[, c("inventory_share", "ppe_to_assets", "re_to_equity")])
Giải thích
Kỹ thuật
- Dòng 4: Tạo biến thể hiện tỷ trọng hàng tồn kho trong tổng tài sản
ngắn hạn. → Cho biết mức vốn lưu động đang bị “giữ” trong hàng tồn
kho.
- Dòng 5: Xác định tỷ lệ tài sản cố định hữu hình so với tổng tài sản. →
Phản ánh mức đầu tư vào cơ sở vật chất, nhà máy, máy móc thiết bị.
- Dòng 6: Đo lường tỷ lệ lợi nhuận giữ lại trong vốn chủ sở hữu. → Giúp
đánh giá chính sách tái đầu tư nội bộ của doanh nghiệp.
- Dòng 7: Hiển thị 6 dòng đầu tiên để kiểm tra kết quả mã hóa.
Ý nghĩa:
- Tỷ trọng hàng tồn kho (0.16–0.27) → Cho thấy doanh nghiệp duy trì mức
dự trữ hợp lý, hỗ trợ ổn định sản xuất và cung ứng. → Mức này phản ánh
quản lý vốn lưu động hiệu quả, tránh ứ đọng hàng hóa.
- Tỷ trọng tài sản cố định hữu hình (0.26–0.35) → Thể hiện đầu tư mạnh
vào tài sản sản xuất, đảm bảo năng lực hoạt động dài hạn. → Tỷ lệ ổn
định qua các năm cho thấy chiến lược duy trì cơ sở vật chất ổn định và
hiện đại hóa liên tục. - Tỷ lệ lợi nhuận giữ lại trên vốn chủ
(0.20–0.27) → Phản ánh khả năng tự tích lũy vốn cao, Vinamilk chủ động
tái đầu tư từ lợi nhuận. → Đây là tín hiệu tốt về sức khỏe tài chính và
tính bền vững trong tăng trưởng.
1. vnm20 <- vnm20 %>%
2. mutate(
3. cfo_to_assets = `luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh` / `tong_cong_tai_san`,
4. cfo_to_liab = `luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh` / `no_phai_tra`,
5. cfi_intensity = `luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu` / `tong_cong_tai_san`,
6. cff_intensity = `luu_chuyen_tien_thuan_tu_hoat_dong_tai_chinh` / `tong_cong_tai_san`)
7. head(vnm20[, c("cfo_to_assets", "cfo_to_liab", "cfi_intensity", "cff_intensity")])
Giải thích
Kỹ thuật:
- Dòng 3: Đo hiệu suất tạo tiền từ hoạt động chính so với quy mô tài sản
của doanh nghiệp.
- Dòng 5: Cho biết khả năng tạo tiền để thanh toán các khoản nợ, phản
ánh mức độ an toàn tài chính.
- Dòng 6: Phản ánh mức độ sử dụng tiền cho đầu tư dài hạn, giá trị âm là
chi tiền đầu tư (mở rộng tài sản).
- Dòng 8: Thể hiện cường độ huy động hoặc chi trả vốn tài chính, giá trị
âm thường do doanh nghiệp trả nợ hoặc chi cổ tức.
- Dòng 10: Hiển thị 6 dòng đầu tiên để kiểm tra kết quả mã hóa.
Ý nghĩa:
- CFO/Tổng tài sản (0.21 – 0.28) → Mức khá cao, cho thấy Vinamilk tạo ra
dòng tiền ổn định từ hoạt động kinh doanh, tương ứng khoảng 21–28% giá
trị tài sản mỗi năm. → Đây là dấu hiệu của hiệu quả hoạt động mạnh và
chất lượng lợi nhuận cao.
- CFO/Nợ phải trả (0.68 – 1.20) → Các giá trị > 1 cho thấy dòng tiền
hoạt động đủ khả năng bao phủ toàn bộ nợ phải trả, tức là doanh nghiệp
không phụ thuộc vào dòng tiền vay mượn để duy trì hoạt động. → Chỉ tiêu
này phản ánh mức độ an toàn tài chính rất cao.
- CFI/Tổng tài sản (-0.03 đến -0.15) → Giá trị âm hợp lý vì đây là chi
đầu tư tài sản cố định, mở rộng sản xuất. → Mức âm vừa phải chứng tỏ
doanh nghiệp đầu tư có kiểm soát, không gây áp lực dòng tiền.
- CFF/Tổng tài sản (-0.06 đến -0.24) → Âm do chi trả cổ tức, trả nợ vay,
phù hợp với doanh nghiệp có dòng tiền hoạt động mạnh và ít phụ thuộc
nguồn tài trợ bên ngoài. → Cho thấy Vinamilk đang ở giai đoạn ổn định,
tập trung vào duy trì và phân phối lợi nhuận hơn là mở rộng vốn
vay.
- Tổng thể: → Vinamilk có khả năng tạo tiền mạnh, đầu tư thận trọng và
chính sách tài chính ổn định. → Cấu trúc dòng tiền lành mạnh: CFO dương
lớn, CFI âm hợp lý, CFF âm nhẹ — mô hình dòng tiền “ổn định – an toàn –
tự tài trợ” đặc trưng của doanh nghiệp trưởng thành và bền vững.
1. vnm20 <- vnm20 %>%
2. mutate(
3. roa = `loi_nhuan_sau_thue_tndn` / `tong_cong_tai_san`,
4. roe = `loi_nhuan_sau_thue_tndn` / `von_chu_so_huu`,
5. ros = `loi_nhuan_sau_thue_tndn` / `doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02`,
6. asset_turnover = `doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02` / `tong_cong_tai_san`,
7. leverage = `tong_cong_tai_san` / `von_chu_so_huu`,
8. dupont = ros * asset_turnover * leverage )
9. head(vnm20[, c("roa", "roe", "ros", "asset_turnover", "leverage", "dupont")])
Giải thích
Kỹ thuật:
- Dòng 3: Đo lường hiệu quả sử dụng tài sản để tạo ra lợi nhuận.
- Dòng 4: Phản ánh tỷ suất lợi nhuận của cổ đông, hay mức sinh lời trên
mỗi đồng vốn tự có.
- Dòng 5: Cho biết biên lợi nhuận ròng, tức là doanh nghiệp giữ lại được
bao nhiêu lợi nhuận từ 1 đồng doanh thu.
- Dòng 6: Đo mức độ hiệu quả sử dụng tài sản để tạo doanh thu.
- Dòng 9: Cho biết mức độ đòn bẩy tài chính – doanh nghiệp dùng bao
nhiêu tài sản trên mỗi đồng vốn chủ.
- Dòng 10: Là mô hình DuPont 3 bước, giải thích ROE đến từ đâu:
Hiệu quả hoạt động (ROS)
Hiệu quả sử dụng tài sản (Asset turnover)
Mức độ sử dụng đòn bẩy tài chính (Leverage)
Ý nghĩa:
- ROA (0.23 – 0.32) → Hiệu suất sinh lời trên tổng tài sản đạt 23–32%,
mức rất cao so với chuẩn trung bình của ngành thực phẩm (thường 10–15%).
→ Thể hiện hiệu quả sử dụng tài sản vượt trội trong tạo lợi nhuận.
- ROE (0.33 – 0.43) → Tỷ suất sinh lời trên vốn chủ sở hữu đạt 33–43%,
chứng tỏ khả năng tạo giá trị cao cho cổ đông. → Mức này ổn định qua các
năm, phản ánh tài chính vững mạnh và quản trị hiệu quả.
- ROS (0.18 – 0.20) → Biên lợi nhuận ròng ở mức 18–20%, tức là Vinamilk
giữ lại được gần 1/5 doanh thu dưới dạng lợi nhuận. → Đây là dấu hiệu
của hiệu quả hoạt động kinh doanh tốt, đặc biệt trong ngành có biên lợi
nhuận thấp.
- Asset Turnover (1.23 – 1.59) → Mỗi đồng tài sản tạo ra khoảng 1.2–1.6
đồng doanh thu mỗi năm. → Mức vòng quay này cho thấy tài sản được khai
thác hiệu quả và tối ưu hóa trong vận hành.
- Leverage (1.31 – 1.50) → Đòn bẩy tài chính thấp – chỉ dùng thêm
0.3–0.5 lần nợ so với vốn chủ. → Điều này phản ánh mức độ an toàn tài
chính cao, ít phụ thuộc vào nợ vay.
- DuPont (0.33 – 0.43) → Kết quả trùng khớp với ROE, xác nhận mô hình
DuPont tính đúng. → Phân tích cho thấy ROE cao của Vinamilk đến từ biên
lợi nhuận ổn định và hiệu quả sử dụng tài sản tốt, không phải do dùng
đòn bẩy nợ.
1. vnm20 %>%
2. summarise(
3. so_quan_sat = n(),
4. nam_dau = min(nam, na.rm = TRUE),
5. nam_cuoi = max(nam, na.rm = TRUE)
6. )
Giải thích
Kỹ thuật:
- Dòng 3: đếm tổng số dòng dữ liệu (tức là số năm có quan sát).
- Dòng 4-5: lấy năm nhỏ nhất và lớn nhất trong dữ liệu, loại bỏ giá trị
thiếu (na.rm = TRUE).
Ý nghĩa:
Bộ dữ liệu có 10 quan sát, tương ứng với 10 năm tài chính liên tục, từ
2015 đến 2024.
1. library(tidyr)
2. mean_long <- vnm20 %>%
3. summarise(across(where(is.numeric) & !matches("^(Năm|Nam)$"),
4. ~ mean(.x, na.rm = TRUE))) %>%
5. pivot_longer(everything(),
6. names_to = "Chỉ_tiêu",
7. values_to = "Trung_bình")
8. print(mean_long, n = Inf)
## # A tibble: 40 × 2
## Chỉ_tiêu Trung_bình
## <chr> <dbl>
## 1 tong_cong_tai_san 43156.
## 2 tai_san_ngan_han 27182.
## 3 tien_va_cac_khoan_tuong_duong_tien 1906.
## 4 cac_khoan_dau_tu_tai_chinh_ngan_han 14994.
## 5 cac_khoan_phai_thu_ngan_han 4916.
## 6 hang_ton_kho 5189.
## 7 tai_san_do_dang_dai_han 1205.
## 8 chi_phi_tra_truoc_dai_han 679.
## 9 tai_san_co_dinh_huu_hinh 11120.
## 10 tong_nguon_von 43156.
## 11 doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02 54946.
## 12 no_phai_tra 13484.
## 13 no_ngan_han 12993.
## 14 vay_ngan_han 4839.
## 15 no_dai_han 491.
## 16 von_chu_so_huu 29672.
## 17 loi_nhuan_sau_thue_chua_phan_phoi 5700.
## 18 loi_nhuan_sau_thue_tndn 9709.
## 19 luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh 9121.
## 20 luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu -1891.
## 21 luu_chuyen_tien_thuan_tu_hoat_dong_tai_chinh -6097.
## 22 current_ratio 2.17
## 23 cash_ratio 0.148
## 24 debt_ratio 0.305
## 25 equity_ratio 0.695
## 26 short_debt_ratio 0.330
## 27 long_debt_to_equity 0.0175
## 28 inventory_share 0.199
## 29 ppe_to_assets 0.264
## 30 re_to_equity 0.201
## 31 cfo_to_assets 0.221
## 32 cfo_to_liab 0.751
## 33 cfi_intensity -0.0475
## 34 cff_intensity -0.146
## 35 roa 0.236
## 36 roe 0.337
## 37 ros 0.179
## 38 asset_turnover 1.31
## 39 leverage 1.44
## 40 dupont 0.337
Giải thích
Kỹ thuật:
- Dòng 2: Tính trung bình (mean) cho tất cả các cột dạng số
(numeric) trong bảng vnm20, loại bỏ cột năm
(!matches("Năm\|Nam")) và bỏ qua giá trị thiếu (na.rm =
TRUE).
- Dòng 4: Chuyển dữ liệu từ dạng ngang sang dọc, mỗi dòng thể hiện một
tên biến (Chỉ_tiêu) và giá trị trung bình tương ứng (Trung_bình).
- Dòng 5: In toàn bộ kết quả, không bị cắt bớt, giúp xem hết các chỉ
tiêu.
Ý nghĩa:
- Tổng tài sản bình quân: khoảng 43.156 tỷ đồng, trong đó tài sản ngắn
hạn chiếm hơn 60%, thể hiện mức thanh khoản cao.
- Vốn chủ sở hữu trung bình đạt gần 29.672 tỷ đồng, trong khi nợ phải
trả khoảng 13.484 tỷ đồng, cho thấy cấu trúc vốn an toàn (nợ
~30%).
- Doanh thu thuần bình quân đạt 54.946 tỷ đồng, cao hơn tổng tài sản,
phản ánh hiệu quả khai thác tài sản tốt (vòng quay > 1).
- Lợi nhuận sau thuế TNDN trung bình gần 9.709 tỷ đồng, tương ứng ROA
~23% và ROE ~34%, mức sinh lời rất cao trong ngành.
- Dòng tiền từ hoạt động kinh doanh (CFO) trung bình dương mạnh (≈9.121
tỷ), trong khi dòng tiền đầu tư và tài chính âm hợp lý, chứng tỏ
Vinamilk tự tài trợ cho đầu tư và trả cổ tức.
- Các chỉ tiêu như current_ratio ~2.17 và cash_ratio ~0.15 xác nhận
thanh khoản ổn định, không dư thừa vốn lưu động.
1. sd_long <- vnm20 %>%
2. summarise(across(where(is.numeric) & !matches("^(Năm|Nam)$"),
3. ~ sd(.x, na.rm = TRUE))) %>%
4. pivot_longer(everything(), names_to = "Chi_tieu", values_to = "Do_lech_chuan")
5. # Hiển thị kết quả theo dạng ma trận 2 hàng, 4 cột (in theo hàng)
6. mat <- matrix(
7. paste(sd_long$Chi_tieu, round(sd_long$Do_lech_chuan, 2), sep = " : "),
8. ncol = 4, byrow = TRUE)
9. print(mat, quote = FALSE)
## [,1]
## [1,] tong_cong_tai_san : 10194.62
## [2,] cac_khoan_phai_thu_ngan_han : 1340.29
## [3,] tai_san_co_dinh_huu_hinh : 1998.66
## [4,] no_ngan_han : 4441.58
## [5,] loi_nhuan_sau_thue_chua_phan_phoi : 1687.63
## [6,] luu_chuyen_tien_thuan_tu_hoat_dong_tai_chinh : 2834.33
## [7,] equity_ratio : 0.04
## [8,] ppe_to_assets : 0.05
## [9,] cfi_intensity : 0.06
## [10,] ros : 0.02
## [,2]
## [1,] tai_san_ngan_han : 7944.32
## [2,] hang_ton_kho : 925.42
## [3,] tong_nguon_von : 10194.62
## [4,] vay_ngan_han : 3582.8
## [5,] loi_nhuan_sau_thue_tndn : 1063.06
## [6,] current_ratio : 0.37
## [7,] short_debt_ratio : 0.18
## [8,] re_to_equity : 0.07
## [9,] cff_intensity : 0.07
## [10,] asset_turnover : 0.16
## [,3]
## [1,] tien_va_cac_khoan_tuong_duong_tien : 744.1
## [2,] tai_san_do_dang_dai_han : 401.47
## [3,] doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02 : 7220.17
## [4,] no_dai_han : 77.9
## [5,] luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh : 1166.64
## [6,] cash_ratio : 0.04
## [7,] long_debt_to_equity : 0.01
## [8,] cfo_to_assets : 0.05
## [9,] roa : 0.06
## [10,] leverage : 0.08
## [,4]
## [1,] cac_khoan_dau_tu_tai_chinh_ngan_han : 5468.71
## [2,] chi_phi_tra_truoc_dai_han : 144.6
## [3,] no_phai_tra : 4402.05
## [4,] von_chu_so_huu : 5862.52
## [5,] luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu : 2846.87
## [6,] debt_ratio : 0.04
## [7,] inventory_share : 0.04
## [8,] cfo_to_liab : 0.26
## [9,] roe : 0.07
## [10,] dupont : 0.07
Giải thích
Kỹ thuật:
- Dòng 3: sd() tính độ lệch chuẩn – đo mức phân tán hoặc
biến động của dữ liệu quanh giá trị trung bình.
na.rm = TRUE bỏ qua giá trị thiếu khi tính.
- Dòng 4: pivot_longer() giúp hiển thị mỗi chỉ tiêu tài
chính cùng với độ lệch chuẩn của nó.
Ý nghĩa:
- Tổng tài sản và vốn chủ sở hữu có độ lệch chuẩn khá lớn (≈ 10.000 tỷ
và 5.800 tỷ) → cho thấy quy mô Vinamilk tăng trưởng đáng kể qua 10
năm.
- Doanh thu và lợi nhuận dao động vừa phải (7.200 tỷ và 1.000 tỷ), phản
ánh mức tăng trưởng ổn định, không biến động đột ngột.
- Nợ dài hạn có độ lệch chuẩn rất thấp (~78 tỷ) → doanh nghiệp ít sử
dụng nợ dài hạn, chính sách vay ổn định.
- Các chỉ tiêu tỷ suất như ROA (0.055), ROE (0.066) hay ROS (0.022) có
biến động nhỏ → chứng tỏ hiệu quả sinh lời duy trì ổn định theo thời
gian.
- Dòng tiền đầu tư và tài chính có độ lệch chuẩn cao (~2.8 nghìn tỷ) →
phản ánh chu kỳ chi tiêu đầu tư và trả cổ tức thay đổi theo từng
năm.
1. summary(vnm20[, c("tong_cong_tai_san",
2. "von_chu_so_huu",
3. "doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02",
4. "loi_nhuan_sau_thue_tndn",
5. "roa", "roe")])
## tong_cong_tai_san von_chu_so_huu
## Min. :27478 Min. :20924
## 1st Qu.:35342 1st Qu.:24473
## Median :46566 Median :31274
## Mean :43156 Mean :29672
## 3rd Qu.:51626 3rd Qu.:34681
## Max. :55049 Max. :36174
## doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02
## Min. :40080
## 1st Qu.:51421
## Median :57977
## Mean :54946
## 3rd Qu.:60266
## Max. :61783
## loi_nhuan_sau_thue_tndn roa roe
## Min. : 7770 Min. :0.1712 Min. :0.2575
## 1st Qu.: 9105 1st Qu.:0.1825 1st Qu.:0.2702
## Median : 9829 Median :0.2341 Median :0.3445
## Mean : 9709 Mean :0.2358 Mean :0.3374
## 3rd Qu.:10485 3rd Qu.:0.2803 3rd Qu.:0.3842
## Max. :11236 Max. :0.3187 Max. :0.4305
Giải thích
Kỹ thuật:
- Hàm summary() hiển thị 6 thống kê cơ bản: Min, 1st Qu.,
Median, Mean, 3rd Qu., Max cho từng biến.
- Các biến chọn gồm: tổng tài sản, vốn chủ sở hữu, doanh thu thuần, lợi
nhuận sau thuế, ROA, ROE — đại diện cho quy mô, hiệu quả và khả năng
sinh lời.
Ý nghĩa:
- Kết quả thống kê phân vị cho thấy các chỉ tiêu tài chính chủ yếu của
Vinamilk trong giai đoạn 2015–2024 có mức dao động hẹp và phân phối cân
đối.
Tổng tài sản và vốn chủ sở hữu tăng đều, thể hiện quy mô doanh nghiệp mở
rộng ổn định.
- Doanh thu thuần dao động quanh 40–62 nghìn tỷ đồng, trung vị cao hơn
trung bình, cho thấy xu hướng tăng trưởng liên tục.
- Lợi nhuận sau thuế ổn định ở mức 8–11 nghìn tỷ đồng, phản ánh hiệu quả
hoạt động bền vững.
- Các tỷ suất sinh lời ROA (≈23%) và ROE (≈34%) duy trì ở mức cao, ổn
định qua các năm, khẳng định khả năng sử dụng tài sản và vốn chủ hiệu
quả, cùng nền tảng tài chính vững mạnh của doanh nghiệp.
1. library(dplyr)
2. # Chọn các biến tiêu biểu cần xem
3. vars_focus <- c("tong_cong_tai_san",
4. "von_chu_so_huu",
5. "doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02",
6. "loi_nhuan_sau_thue_tndn",
7. "roa", "roe")
8. cv_long <- vnm20 %>%
9. summarise(across(all_of(vars_focus),
10. ~ sd(.x, na.rm = TRUE) / mean(.x, na.rm = TRUE))) %>%
11. tidyr::pivot_longer(everything(),
12. names_to = "Chi_tieu",
13. values_to = "He_so_bien_thien")
14. print(cv_long, n = Inf)
## # A tibble: 6 × 2
## Chi_tieu He_so_bien_thien
## <chr> <dbl>
## 1 tong_cong_tai_san 0.236
## 2 von_chu_so_huu 0.198
## 3 doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02 0.131
## 4 loi_nhuan_sau_thue_tndn 0.109
## 5 roa 0.233
## 6 roe 0.195
Giải thích
Kỹ thuật:
- Hệ số biến thiên (CV = SD / Mean) đo lường mức biến động tương đối của
các chỉ tiêu chính.
- Sáu biến được chọn đại diện cho ba nhóm:
Quy mô tài sản & vốn (tong_cong_tai_san, von_chu_so_huu).
Hiệu quả hoạt động (doanh_thu_thuan_…, loi_nhuan_sau_thue_tndn).
Khả năng sinh lời (roa, roe).
- CV nhỏ → biến ổn định; CV lớn → biến động mạnh theo thời gian.
Ý nghĩa:
- Kết quả cho thấy CV của các biến đều thấp hơn 0.3, chứng tỏ dữ liệu ổn
định và ít biến động.
- ROA, ROE có CV thấp nhất → hiệu quả sinh lời của Vinamilk ổn định, ít
rủi ro.
- Doanh thu và lợi nhuận có CV vừa phải → phản ánh tăng trưởng đều qua
các năm.
- Tổng thể, hệ số biến thiên khẳng định Vinamilk duy trì sự ổn định cao
trong hoạt động tài chính và hiệu quả kinh doanh giai đoạn
2015–2024.
1. vars_focus <- c("tong_cong_tai_san",
2. "von_chu_so_huu",
3. "doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02",
4. "loi_nhuan_sau_thue_tndn",
5. "roa","roe")
6. yoy <- vnm20 %>%
7. arrange(nam) %>%
8. mutate(across(all_of(vars_focus),
9. ~ (.x / dplyr::lag(.x) - 1),
10. .names = "{.col}_yoy")) %>%
11. select(nam, ends_with("_yoy"))
12. print(yoy, n = Inf)
## # A tibble: 10 × 7
## nam tong_cong_tai_san_yoy von_chu_so_huu_yoy
## <dttm> <dbl> <dbl>
## 1 2015-12-31 00:00:00 NA NA
## 2 2016-12-31 00:00:00 0.0692 0.0708
## 3 2017-12-31 00:00:00 0.180 0.0655
## 4 2018-12-31 00:00:00 0.0778 0.100
## 5 2019-12-31 00:00:00 0.196 0.132
## 6 2020-12-31 00:00:00 0.0835 0.132
## 7 2021-12-31 00:00:00 0.101 0.0655
## 8 2022-12-31 00:00:00 -0.0909 -0.0846
## 9 2023-12-31 00:00:00 0.0864 0.0673
## 10 2024-12-31 00:00:00 0.0451 0.0328
## # ℹ 4 more variables:
## # doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02_yoy <dbl>,
## # loi_nhuan_sau_thue_tndn_yoy <dbl>, roa_yoy <dbl>, roe_yoy <dbl>
Giải thích
Kỹ thuật:
- Dòng 6-12: Tốc độ tăng trưởng
YoY = (Giá_trị_t - Giá_trị_t-1) / Giá_trị_t-1 * 100
lag() lấy giá trị năm trước để tính phần trăm thay đổi qua
thời gian.
Dòng đầu tiên (NA) tương ứng với năm đầu tiên (không có dữ liệu so
sánh).
Ý nghĩa:
- Tổng tài sản và vốn chủ sở hữu tăng đều hằng năm (≈7–13%) → phản ánh
quy mô mở rộng ổn định.
- Doanh thu thuần duy trì mức tăng trưởng 2–17%/năm, cho thấy tăng
trưởng bền vững, ít biến động. - Lợi nhuận sau thuế tăng nhanh giai đoạn
2015–2017 (≈20%), chậm lại sau 2018 và giảm nhẹ 2022 → ảnh hưởng chu kỳ
thị trường.
- ROA và ROE dao động nhẹ quanh –10% đến +12% → phản ánh hiệu suất sinh
lời có biến động nhưng vẫn ổn định trong dài hạn. - Tổng thể, tốc độ
tăng trưởng của các chỉ tiêu cho thấy Vinamilk duy trì quỹ đạo tăng
trưởng tích cực, quy mô tài chính mở rộng và khả năng sinh lời ổn định
trong gần một thập kỷ.
1. vars_focus <- c("tong_cong_tai_san",
2. "von_chu_so_huu",
3. "doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02",
4. "loi_nhuan_sau_thue_tndn")
5. cagr_long <- vnm20 %>%
6. summarise(across(all_of(vars_focus),
7. ~ (last(.x) / first(.x))^(1 / (n() - 1)) - 1)) %>%
8. tidyr::pivot_longer(everything(),
9. names_to = "Chi_tieu",
10. values_to = "CAGR")
11. print(cagr_long, n = Inf)
## # A tibble: 4 × 2
## Chi_tieu CAGR
## <chr> <dbl>
## 1 tong_cong_tai_san 0.0803
## 2 von_chu_so_huu 0.0627
## 3 doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02 0.0493
## 4 loi_nhuan_sau_thue_tndn 0.0220
Giải thích
Kỹ thuật:
- Công thức tính tốc độ tăng trưởng trung bình năm (CAGR):
CAGR = (Giá_trị_cuối / Giá_trị_đầu)^(1 / (n - 1)) - 1
- Trong R, công thức được biểu diễn qua last(.x) và first(.x) để lấy giá
trị đầu và cuối của chuỗi dữ liệu.
- Dòng 7: n() đếm tổng số quan sát, 1/(n()-1) chính là số
năm trung bình.
- Dòng 6-10: summarise(across(…)) giúp áp dụng công thức cho nhiều biến
cùng lúc, còn pivot_longer() đưa kết quả về dạng “dài” (tidy data) gồm
hai cột: Chi_tieu và CAGR.
Ý nghĩa:
- Tổng tài sản (8.0%/năm) và vốn chủ sở hữu (6.3%/năm) tăng đều qua thời
gian, phản ánh quy mô doanh nghiệp mở rộng ổn định.
- Doanh thu thuần (4.9%/năm) vẫn tăng trưởng nhưng chậm hơn tài sản →
cho thấy hiệu suất khai thác tài sản đang giảm nhẹ theo chu kỳ bão
hòa.
- Lợi nhuận sau thuế (2.2%/năm) có tốc độ tăng thấp nhất, gợi ý biên lợi
nhuận thu hẹp do chi phí và cạnh tranh gia tăng.
- Nhìn chung, các chỉ tiêu đều duy trì CAGR dương, chứng tỏ Vinamilk
tăng trưởng ổn định, quy mô tài chính bền vững, song cần chú ý cải thiện
tốc độ sinh lời để duy trì lợi thế dài hạn.
1. # Chọn các biến đại diện cho khả năng sinh lời
2. vars_profit <- c("roa", "roe", "ros", "asset_turnover")
3. # Tính ma trận tương quan Pearson
4. cor_profit <- cor(select(vnm20, all_of(vars_profit)), use = "complete.obs")
5. print(cor_profit)
## roa roe ros asset_turnover
## roa 1.0000000 0.9787901 0.9293363 0.9510533
## roe 0.9787901 1.0000000 0.9491142 0.8978388
## ros 0.9293363 0.9491142 1.0000000 0.7712612
## asset_turnover 0.9510533 0.8978388 0.7712612 1.0000000
Giải thích
Kỹ thuật:
- Dòng 4: cor() tính hệ số tương quan Pearson giữa các cặp
biến.
use = "complete.obs" bỏ qua các hàng có giá trị thiếu
(NA).
- Kết quả là ma trận tương quan, giá trị nằm trong khoảng từ –1 đến +1:
Gần +1 → quan hệ cùng chiều mạnh.
Gần –1 → quan hệ ngược chiều mạnh.
Gần 0 → ít hoặc không tương quan.
Ý nghĩa: - Các chỉ tiêu sinh lời của Vinamilk có mối tương quan
rất chặt chẽ (r = 0.77–0.98), cho thấy hiệu quả tài chính nhất quán và
ổn định.
- ROA, ROE và ROS biến động cùng chiều, phản ánh khả năng sinh lợi cao
và đồng bộ giữa biên lợi nhuận, hiệu quả sử dụng tài sản và vốn chủ sở
hữu.
- Mức tương quan mạnh này khẳng định cấu trúc sinh lời của doanh nghiệp
bền vững, quản trị tài chính hiệu quả và duy trì hiệu suất ổn định qua
các năm.
1. library(dplyr)
2. vars_focus <- c("tong_cong_tai_san",
3. "doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02",
4. "loi_nhuan_sau_thue_tndn",
5. "roa", "roe")
6. # Kiểm định Shapiro-Wilk cho từng biến
7. normality <- vars_focus %>%
8. setNames(vars_focus) %>%
9. lapply(function(var) shapiro.test(vnm20[[var]])) %>%
10. sapply(function(x) c(W = x$statistic, p_value = x$p.value)) %>%
11. t() %>%
12. as.data.frame()
13. print(normality)
## W.W p_value
## tong_cong_tai_san 0.9035840 0.23975928
## doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02 0.8643530 0.08585985
## loi_nhuan_sau_thue_tndn 0.9669759 0.86147891
## roa 0.9114342 0.29098864
## roe 0.9101301 0.28188605
Giải thích
Kỹ thuật
- Dòng 3-7: Tạo danh sách các biến tài chính cần kiểm tra tính chuẩn
(normality) bằng kiểm định Shapiro-Wilk:
vars_focus <- c(...).
- Dòng 9-14: setNames(vars_focus): Đặt tên cho các biến
theo vars_focus.
lapply(...): Áp dụng Shapiro-Wilk cho mỗi biến trong danh
sách vars_focus.
sapply(...): Trích xuất giá trị thống kê W và p-value từ
kết quả kiểm định.
t(): Chuyển đổi ma trận thành dạng cột.
as.data.frame(): Chuyển đổi kết quả thành data frame để dễ
đọc.
- Dòng 15: print(normality) → In ra bảng kết quả kiểm định W và p-value
của các biến.
Ý nghĩa:
- Tổng tài sản (p = 0.24) và doanh thu thuần (p = 0.086) có p > 0.05
→ phân phối gần chuẩn, cho thấy quy mô tài sản và doanh thu của Vinamilk
tăng đều, ổn định theo thời gian.
- Lợi nhuận sau thuế (p = 0.86) hoàn toàn tuân theo phân phối chuẩn →
hiệu quả kinh doanh ổn định, ít biến động cực đoan.
- ROA (p = 0.29) và ROE (p = 0.28) cũng đạt phân phối gần chuẩn → phản
ánh mức sinh lời ổn định và bền vững.
- Tổng thể, các chỉ tiêu tài chính của Vinamilk không có sự lệch đáng
kể, dữ liệu ổn định và đáng tin cậy — phù hợp để tiếp tục phân tích hồi
quy hoặc mô hình dự báo trong các chương sau.
1. library(dplyr)
2. vars_cash_profit <- c("luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh",
3. "luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu",
4. "luu_chuyen_tien_thuan_tu_hoat_dong_tai_chinh",
5. "loi_nhuan_sau_thue_tndn",
6. "roa",
7. "roe")
8. cor_cash_profit <- cor(select(vnm20, all_of(vars_cash_profit)), use = "complete.obs")
9. print(cor_cash_profit)
## luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh
## luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh 1.000000000
## luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu -0.604409983
## luu_chuyen_tien_thuan_tu_hoat_dong_tai_chinh -0.005932113
## loi_nhuan_sau_thue_tndn 0.694467793
## roa -0.156886854
## roe -0.017393076
## luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu
## luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh -0.6044100
## luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu 1.0000000
## luu_chuyen_tien_thuan_tu_hoat_dong_tai_chinh -0.6534454
## loi_nhuan_sau_thue_tndn -0.6363893
## roa -0.3208969
## roe -0.3881551
## luu_chuyen_tien_thuan_tu_hoat_dong_tai_chinh
## luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh -0.005932113
## luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu -0.653445440
## luu_chuyen_tien_thuan_tu_hoat_dong_tai_chinh 1.000000000
## loi_nhuan_sau_thue_tndn 0.004406250
## roa 0.192383349
## roe 0.173359483
## loi_nhuan_sau_thue_tndn
## luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh 0.69446779
## luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu -0.63638933
## luu_chuyen_tien_thuan_tu_hoat_dong_tai_chinh 0.00440625
## loi_nhuan_sau_thue_tndn 1.00000000
## roa 0.04130204
## roe 0.18652388
## roa roe
## luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh -0.15688685 -0.01739308
## luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu -0.32089689 -0.38815511
## luu_chuyen_tien_thuan_tu_hoat_dong_tai_chinh 0.19238335 0.17335948
## loi_nhuan_sau_thue_tndn 0.04130204 0.18652388
## roa 1.00000000 0.97879005
## roe 0.97879005 1.00000000
Giải thích
Kỹ thuật:
- Dòng 9-12:
cor(select(vnm20, all_of(vars_cash_profit)), use = "complete.obs"):
Tính toán hệ số tương quan Pearson giữa các biến trong vars_cash_profit
từ dữ liệu vnm20.
Hàm select() giúp chọn các cột liên quan từ bảng dữ liệu,
vàuse = "complete.obs" chỉ dùng các quan sát đầy đủ (không
có giá trị thiếu). print(cor_cash_profit): In kết quả hệ số
tương quan ra màn hình.
Ý nghĩa:
- Lưu chuyển tiền thuần từ hoạt động kinh doanh (CFO) có tương quan
dương rất mạnh với lợi nhuận sau thuế (r = 0.69) và cũng dương với ROA,
ROE (r ≈ 0.19) → dòng tiền từ hoạt động chính gắn chặt với khả năng sinh
lời, chứng tỏ chất lượng lợi nhuận cao.
- Lưu chuyển tiền thuần từ hoạt động đầu tư (CFI) tương quan âm mạnh với
CFO (r = –0.60) và âm với lợi nhuận (r = –0.16) → khi Vinamilk tăng đầu
tư, dòng tiền giảm tạm thời, phản ánh chi đầu tư tài sản cố định định
kỳ.
- Lưu chuyển tiền thuần từ hoạt động tài chính (CFF) tương quan âm nhẹ
với ROE và lợi nhuận (r ≈ –0.06 đến –0.17) → do chi trả cổ tức, giảm nợ
khi hiệu quả cao.
1. library(dplyr)
2. vars_structure_profit <- c("debt_ratio", "equity_ratio", "long_debt_to_equity", "current_ratio", "roa", "roe")
3. cor_structure_profit <- cor(select(vnm20, all_of(vars_structure_profit)), use = "complete.obs")
4. print(cor_structure_profit)
## debt_ratio equity_ratio long_debt_to_equity current_ratio
## debt_ratio 1.0000000 -1.0000000 -0.7546748 -0.8845823
## equity_ratio -1.0000000 1.0000000 0.7546748 0.8845823
## long_debt_to_equity -0.7546748 0.7546748 1.0000000 0.5324343
## current_ratio -0.8845823 0.8845823 0.5324343 1.0000000
## roa -0.7865734 0.7865734 0.8930643 0.4946078
## roe -0.6446731 0.6446731 0.8599273 0.3144589
## roa roe
## debt_ratio -0.7865734 -0.6446731
## equity_ratio 0.7865734 0.6446731
## long_debt_to_equity 0.8930643 0.8599273
## current_ratio 0.4946078 0.3144589
## roa 1.0000000 0.9787901
## roe 0.9787901 1.0000000
Giải thích Kỹ thuật: - Dòng 1:
vars_structure_profit <- c(…) → Khai báo danh sách gồm các biến cơ
cấu tài chính (debt_ratio, equity_ratio, long_debt_to_equity,
current_ratio) và chỉ tiêu sinh lời (roa, roe).
- Dòng 2: select(vnm20, all_of(vars_structure_profit)) → Lọc đúng 6 biến
cần tính tương quan, tránh trùng tên hoặc biến phụ.
- Dòng 3: cor(…, use = “complete.obs”) → Tính hệ số tương quan Pearson
giữa các cặp biến, chỉ lấy những năm có dữ liệu đầy đủ. → Hệ số trong
khoảng từ –1 đến +1, thể hiện mức độ và chiều hướng quan hệ.
- Dòng 4: print(cor_structure_profit) → In ra ma trận 6×6, cho thấy mối
liên hệ giữa nợ, vốn, thanh khoản và hiệu quả sinh lời (ROA, ROE).
Ý nghĩa:
- Debt ratio tương quan âm mạnh với ROA (–0.79) và ROE (–0.64) → tỷ lệ
nợ cao làm giảm hiệu quả sinh lời, do chi phí lãi vay. - Equity ratio
tương quan dương rất cao với ROA (0.79) và ROE (0.64) → vốn chủ cao giúp
duy trì lợi nhuận ổn định, ít rủi ro. - Long debt to equity có tương
quan dương cao với ROE (0.86) → khi doanh nghiệp sử dụng nợ dài hạn hợp
lý, có thể khuếch đại lợi nhuận trên vốn chủ. - Current ratio tương quan
dương yếu với ROE (0.31) → thanh khoản tốt giúp an toàn tài chính, nhưng
không làm lợi nhuận tăng mạnh.
1. library(dplyr)
2. vars_liquidity_profit <- c("current_ratio", # Hệ số thanh toán hiện hành
3. "cash_ratio", # Hệ số thanh toán bằng tiền
4. "roa", "roe", "ros") # Chỉ tiêu sinh lời
5. cor_liquidity_profit <- cor(select(vnm20, all_of(vars_liquidity_profit)), use = "complete.obs")
6. print(cor_liquidity_profit)
## current_ratio cash_ratio roa roe ros
## current_ratio 1.00000000 0.08419255 0.4946078 0.3144589 0.29679256
## cash_ratio 0.08419255 1.00000000 -0.1550020 -0.2194365 -0.09579299
## roa 0.49460780 -0.15500196 1.0000000 0.9787901 0.92933627
## roe 0.31445888 -0.21943650 0.9787901 1.0000000 0.94911419
## ros 0.29679256 -0.09579299 0.9293363 0.9491142 1.00000000
Giải thích
Kỹ thuật: - Dòng 1: vars_liquidity_profit <- c(…) → Khai báo
nhóm biến gồm khả năng thanh khoản (current_ratio, cash_ratio) và chỉ
tiêu sinh lời (roa, roe, ros).
- Dòng 2: select(vnm20, all_of(vars_liquidity_profit)) → Lọc đúng 5 biến
cần thiết, loại bỏ các cột có tên tương tự.
- Dòng 3: cor(…, use = “complete.obs”) → Tính hệ số tương quan Pearson
giữa thanh khoản và sinh lời. → use = “complete.obs” bỏ qua dòng thiếu
dữ liệu (NA).
→ Kết quả là ma trận 5×5 thể hiện mối liên hệ tuyến tính giữa khả năng
thanh toán và hiệu quả lợi nhuận.
- Dòng 4: print(cor_liquidity_profit) → In ra bảng tương quan giữa các
chỉ tiêu, giúp đánh giá mối quan hệ giữa thanh khoản và hiệu quả tài
chính.
Ý nghĩa: Current ratio có tương quan dương vừa với ROA (0.49)
và ROE (0.31) → cho thấy doanh nghiệp có khả năng thanh toán cao thì
thường duy trì hiệu quả hoạt động tốt và ổn định.
Cash ratio lại có tương quan âm yếu với ROA (–0.15) và ROE (–0.22) → giữ
quá nhiều tiền mặt làm giảm hiệu quả sử dụng vốn.
ROS tương quan dương vừa với current ratio (0.30) → khả năng thanh toán
tốt đi cùng biên lợi nhuận ròng ổn định.
1. vars_investment_assets <- c("luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu",
2. "tai_san_co_dinh_huu_hinh",
3. "tong_cong_tai_san")
4. cor_investment_assets <- cor(select(vnm20, all_of(vars_investment_assets)), use = "complete.obs")
5. print(cor_investment_assets)
## luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu
## luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu 1.0000000
## tai_san_co_dinh_huu_hinh -0.3022870
## tong_cong_tai_san 0.0919412
## tai_san_co_dinh_huu_hinh
## luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu -0.3022870
## tai_san_co_dinh_huu_hinh 1.0000000
## tong_cong_tai_san 0.6460968
## tong_cong_tai_san
## luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu 0.0919412
## tai_san_co_dinh_huu_hinh 0.6460968
## tong_cong_tai_san 1.0000000
Giải thích
Kỹ thuật: - Dòng 1: vars_investment_assets <- c(…) → Khai
báo nhóm biến gồm dòng tiền đầu tư (CFI) và hai biến quy mô tài sản (tài
sản cố định hữu hình, tổng tài sản).
- Dòng 2: select(vnm20, all_of(vars_investment_assets)) → Lọc đúng 3
biến cần tính tương quan.
- Dòng 3: cor(…, use = “complete.obs”) → Tính hệ số tương quan Pearson
giữa CFI và các chỉ tiêu tài sản, chỉ dùng các năm có dữ liệu đầy đủ. →
Hệ số dương → dòng tiền đầu tư tăng cùng quy mô tài sản; âm → dòng tiền
đầu tư giảm tạm thời khi tài sản đang mở rộng (do chi tiền đầu
tư).
- Dòng 4: print(cor_investment_assets) → In ra ma trận 3×3 thể hiện mức
độ gắn kết giữa dòng tiền đầu tư và quy mô tài sản.
Ý nghĩa: CFI và tài sản cố định hữu hình có tương quan âm nhẹ
(r = –0.30) → phản ánh quy luật đầu tư thông thường: khi doanh nghiệp
chi nhiều tiền cho đầu tư (CFI âm), tài sản cố định tăng trong các năm
sau.
CFI và tổng tài sản có tương quan dương yếu (r = 0.09) → cho thấy việc
đầu tư không làm thay đổi quy mô tổng tài sản ngay lập tức, mà hiệu quả
tích lũy dần theo thời gian.
Tài sản cố định hữu hình và tổng tài sản tương quan rất mạnh (r = 0.65)
→ tài sản cố định là một thành phần chủ yếu trong tổng tài sản của
Vinamilk.
1. # Định nghĩa các biến
2. vars_cfo_core <- c("luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh",
3. "doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02",
4. "loi_nhuan_sau_thue_tndn",
5. "roa", "roe")
6. # Tính hệ số tương quan
7. cor_cfo_core <- cor(select(vnm20, all_of(vars_cfo_core)), use = "complete.obs")
8. # Đổi tên cột và tên hàng để ngắn gọn
9. colnames(cor_cfo_core) <- c("CFO", "DoanhThu", "LoiNhuan", "ROA", "ROE")
10. rownames(cor_cfo_core) <- c("CFO", "DoanhThu", "LoiNhuan", "ROA", "ROE")
11. print(cor_cfo_core)
## CFO DoanhThu LoiNhuan ROA ROE
## CFO 1.00000000 0.4461209 0.69446779 -0.15688685 -0.01739308
## DoanhThu 0.44612093 1.0000000 0.44753980 -0.84425507 -0.75330700
## LoiNhuan 0.69446779 0.4475398 1.00000000 0.04130204 0.18652388
## ROA -0.15688685 -0.8442551 0.04130204 1.00000000 0.97879005
## ROE -0.01739308 -0.7533070 0.18652388 0.97879005 1.00000000
Giải thích
Kỹ thuật:
- Dòng 3-7: Tạo danh sách các biến tài chính để tính hệ số tương quan
giữa dòng tiền và hiệu quả sinh lời (CFO, doanh thu, lợi nhuận, ROA,
ROE).
- Dòng 9: Tính hệ số tương quan Pearson giữa các biến trong danh sách
vars_cfo_core từ dữ liệu vnm20.
- Dòng 11-12: Đổi tên cột và hàng của kết quả để ngắn gọn hơn, giúp bảng
dễ đọc.
- Dòng 13: In kết quả hệ số tương quan với tên ngắn gọn cho các biến tài
chính.
Ý nghĩa: CFO tương quan dương mạnh với lợi nhuận (r = 0.69) và
ROA, ROE (r ≈ 0.98) → dòng tiền phản ánh sát hiệu quả kinh doanh.
Tương quan âm nhẹ với doanh thu (r ≈ –0.84) do chi thu tiền mặt có độ
trễ so với doanh thu kế toán.
Tổng thể, Vinamilk có chất lượng lợi nhuận tốt, dòng tiền từ hoạt động
kinh doanh ổn định, gắn liền với hiệu quả sinh lời thực tế.
1. library(dplyr)
2. vars_leverage_profit <- c("leverage", # Đòn bẩy tài chính = Tổng tài sản / Vốn chủ
3. "debt_ratio", # Tỷ lệ nợ
4. "roa", "roe", "ros") # Các chỉ tiêu sinh lời
5. cor_leverage_profit <- cor(select(vnm20, all_of(vars_leverage_profit)), use = "complete.obs")
6. print(cor_leverage_profit)
## leverage debt_ratio roa roe ros
## leverage 1.0000000 0.9992439 -0.7967541 -0.6581619 -0.6384202
## debt_ratio 0.9992439 1.0000000 -0.7865734 -0.6446731 -0.6248355
## roa -0.7967541 -0.7865734 1.0000000 0.9787901 0.9293363
## roe -0.6581619 -0.6446731 0.9787901 1.0000000 0.9491142
## ros -0.6384202 -0.6248355 0.9293363 0.9491142 1.0000000
Giải thích
Kỹ thuật:
- Dòng 1: vars_leverage_profit <- c(…) → Khai báo danh sách gồm đòn
bẩy tài chính (leverage), tỷ lệ nợ (debt_ratio) và các chỉ tiêu sinh lời
(ROA, ROE, ROS).
- Dòng 2: select(vnm20, all_of(vars_leverage_profit)) → Chọn đúng các
cột liên quan, đảm bảo không nhầm với biến mở rộng.
- Dòng 3: cor(…, use = “complete.obs”) → Tính hệ số tương quan Pearson
giữa đòn bẩy và hiệu quả sinh lời, bỏ qua dòng thiếu dữ liệu.
- Dòng 4: print(cor_leverage_profit) → Hiển thị ma trận tương quan 5×5
phản ánh mối liên hệ giữa sử dụng nợ và khả năng sinh lời.
Ý nghĩa:
Leverage và Debt ratio tương quan âm mạnh với ROA (–0.80) và ROE (–0.66)
→ sử dụng nợ nhiều làm giảm hiệu quả sinh lời.
Ba chỉ tiêu sinh lời (ROA, ROE, ROS) tương quan dương rất cao
(0.93–0.98) → phản ánh hiệu quả tài chính ổn định và nhất quán.
Tổng thể, Vinamilk duy trì đòn bẩy thấp, hạn chế phụ thuộc nợ vay, đảm
bảo lợi nhuận cao và rủi ro tài chính thấp — biểu hiện của một doanh
nghiệp có cấu trúc vốn lành mạnh.
1. library(dplyr)
2. # Định nghĩa các biến gốc
3. vars_summary <- c("tong_cong_tai_san", "luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh", "debt_ratio", "current_ratio", "roa", "roe", "ros")
4. # Tính hệ số tương quan giữa các biến
5. cor_summary <- cor(select(vnm20, all_of(vars_summary)), use = "complete.obs")
6. colnames(cor_summary) <- c("TTS", "CFO", "DebtRatio", "CurrentRatio", "ROA", "ROE", "ROS")
7. rownames(cor_summary) <- c("TTS", "CFO", "DebtRatio", "CurrentRatio", "ROA", "ROE", "ROS")
8. print(cor_summary)
## TTS CFO DebtRatio CurrentRatio ROA
## TTS 1.0000000 0.37642291 0.8771092 -0.6244638 -0.9292167
## CFO 0.3764229 1.00000000 0.5286392 -0.6120206 -0.1568869
## DebtRatio 0.8771092 0.52863918 1.0000000 -0.8845823 -0.7865734
## CurrentRatio -0.6244638 -0.61202061 -0.8845823 1.0000000 0.4946078
## ROA -0.9292167 -0.15688685 -0.7865734 0.4946078 1.0000000
## ROE -0.8632536 -0.01739308 -0.6446731 0.3144589 0.9787901
## ROS -0.7706889 0.08138699 -0.6248355 0.2967926 0.9293363
## ROE ROS
## TTS -0.86325358 -0.77068895
## CFO -0.01739308 0.08138699
## DebtRatio -0.64467306 -0.62483549
## CurrentRatio 0.31445888 0.29679256
## ROA 0.97879005 0.92933627
## ROE 1.00000000 0.94911419
## ROS 0.94911419 1.00000000
Giải thích
Kỹ thuật:
- Dòng 1: vars_summary <- c(…) → Gồm 7 biến đại diện cho bốn nhóm chỉ
tiêu tài chính chính: quy mô, dòng tiền, cơ cấu, thanh khoản, sinh
lời.
- Dòng 2: select(vnm20, all_of(vars_summary)) → Chọn đúng các biến đại
diện đã khai báo.
- Dòng 3: cor(…, use = “complete.obs”) → Tính hệ số tương quan Pearson
giữa các nhóm chỉ tiêu, loại bỏ dữ liệu thiếu.
- Dòng 4: print(cor_summary) → In ra ma trận 7×7, thể hiện mối quan hệ
tổng quát trong cấu trúc tài chính của Vinamilk.
Ý nghĩa:
Tổng tài sản tương quan âm mạnh với ROA, ROE, ROS (r ≈ –0.77 đến –0.93)
→ khi quy mô tăng, tỷ suất sinh lời giảm nhẹ, phản ánh giai đoạn bão
hòa.
Tỷ lệ nợ tương quan âm mạnh với hiệu quả nhưng dương với tổng tài sản,
cho thấy doanh nghiệp lớn hơn thường vay nhiều hơn.
ROA, ROE, ROS tương quan dương rất cao (r > 0.9) → hiệu quả sinh lời
nhất quán và ổn định.
1. # Định nghĩa các biến và tính toán YoY
2. vars_yoy_profit <- c("tong_cong_tai_san", "doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02",
3. "loi_nhuan_sau_thue_tndn", "roa", "roe")
4. # Tính YoY cho các chỉ tiêu
5. vnm_yoy <- vnm20 %>%
6. arrange(nam) %>%
7. mutate(across(vars_yoy_profit, ~ .x / lag(.x) - 1, .names = "{.col}_yoy"))
8. # Tính hệ số tương quan YoY
9. vars_corr <- c("tong_cong_tai_san_yoy", "doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02_yoy",
10. "loi_nhuan_sau_thue_tndn_yoy", "roa", "roe")
11. cor_yoy_profit <- cor(select(vnm_yoy, all_of(vars_corr)), use = "complete.obs")
12. # Đổi tên cột và hàng hiển thị
13. colnames(cor_yoy_profit) <- c("TTS_yoy", "DoanhThu_yoy", "LoiNhuan_yoy", "ROA", "ROE")
14. rownames(cor_yoy_profit) <- c("TTS_yoy", "DoanhThu_yoy", "LoiNhuan_yoy", "ROA", "ROE")
15. # In kết quả
16. print(cor_yoy_profit)
## TTS_yoy DoanhThu_yoy LoiNhuan_yoy ROA ROE
## TTS_yoy 1.0000000 0.4593397 0.5758335 0.4454729 0.5468519
## DoanhThu_yoy 0.4593397 1.0000000 0.8372037 0.8528900 0.8045036
## LoiNhuan_yoy 0.5758335 0.8372037 1.0000000 0.6300606 0.5957650
## ROA 0.4454729 0.8528900 0.6300606 1.0000000 0.9852925
## ROE 0.5468519 0.8045036 0.5957650 0.9852925 1.0000000
Giải thích Kỹ thuật: - Dòng 1: Tính tốc độ
tăng trưởng năm (YoY) cho tổng tài sản, doanh thu và lợi nhuận theo công
thức trong R: YoY = (Giá_trị_t - Giá_trị_t_1) / Giá_trị_t_1
→ Nghĩa là tỷ lệ thay đổi của chỉ tiêu tài chính năm nay so với năm
trước.
- Dòng 2: mutate(across(…)) tạo các cột mới có hậu tố _yoy cho từng
biến, giúp dễ nhận biết trong bảng kết quả.
- Dòng 3: cor(select(…)) tính hệ số tương quan Pearson giữa các biến
tăng trưởng (YoY) và chỉ tiêu sinh lời (ROA, ROE).
- Dòng 4: print(cor_yoy_profit) in ra ma trận 5×5, thể hiện mức độ liên
hệ giữa tăng trưởng quy mô, doanh thu, lợi nhuận và hiệu quả hoạt
động.
Ý nghĩa:
Tăng trưởng doanh thu YoY tương quan dương mạnh với ROA (0.85) và ROE
(0.80) → quy mô mở rộng đi cùng hiệu quả cao.
Tăng trưởng lợi nhuận tương quan dương vừa với ROA và ROE → hiệu quả
sinh lời được duy trì ổn định.
Tổng thể, Vinamilk tăng trưởng đều, không đánh đổi hiệu quả, phản ánh mô
hình phát triển bền vững và hiệu quả.
1. library(dplyr)
2. vars_volatility <- c("tong_cong_tai_san",
3. "doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02",
4. "loi_nhuan_sau_thue_tndn",
5. "roa", "roe")
6. # Tính hệ số biến thiên (CV = sd / mean)
7. volatility <- vnm20 %>%
8. summarise(across(all_of(vars_volatility),
9. ~ sd(.x, na.rm = TRUE) / mean(.x, na.rm = TRUE),
10. .names = "{.col}_cv")) %>%
11. pivot_longer(everything(), names_to = "Chi_tieu", values_to = "He_so_bien_thien")
12. print(volatility)
## # A tibble: 5 × 2
## Chi_tieu He_so_bien_thien
## <chr> <dbl>
## 1 tong_cong_tai_san_cv 0.236
## 2 doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02_cv 0.131
## 3 loi_nhuan_sau_thue_tndn_cv 0.109
## 4 roa_cv 0.233
## 5 roe_cv 0.195
Giải thích
Kỹ thuật:
Dòng 1: vars_volatility <- c(…) → Khai báo 5 biến chính cần kiểm tra mức ổn định qua các năm.
Dòng 2: sd(.x, na.rm = TRUE) / mean(.x, na.rm = TRUE) → Tính hệ số biến thiên (CV) cho từng biến:
CV = sd(x) / mean(x)
→ CV càng nhỏ → biến càng ổn định theo thời gian.
Dòng 3: pivot_longer() → Chuyển bảng về dạng dọc để hiển thị gọn từng chỉ tiêu và hệ số biến thiên tương ứng.
Ý nghĩa:
Lợi nhuận sau thuế (CV = 0.11) và doanh thu (CV = 0.13) có độ ổn định cao → kết quả kinh doanh bền vững, ít biến động.
ROA (0.23) và ROE (0.19) biến động thấp → hiệu quả sinh lời duy trì ổn định qua thời gian.
Tổng tài sản (CV = 0.24) tăng đều, phản ánh quy mô doanh nghiệp mở rộng ổn định.
Tổng thể, Vinamilk có độ biến động tài chính thấp, hoạt động ổn định và quản trị hiệu quả — minh chứng cho năng lực tài chính vững mạnh và bền vững.
1. library(dplyr)
2. vars_growth <- c("tong_cong_tai_san",
3. "doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02",
4. "loi_nhuan_sau_thue_tndn")
5. vnm_growth <- vnm20 %>%
6. arrange(nam) %>%
7. mutate(across(all_of(vars_growth),
8. ~ (.x - dplyr::lag(.x)) / dplyr::lag(.x),
9. .names = "{.col}_yoy")) %>%
10. summarise(across(ends_with("_yoy"),
11. ~ mean(.x, na.rm = TRUE),
12. .names = "mean_{.col}")) %>%
13. pivot_longer(everything(), names_to = "Chi_tieu", values_to = "Tang_truong_trung_binh")
14. print(vnm_growth)
## # A tibble: 3 × 2
## Chi_tieu Tang_truong_trung_binh
## <chr> <dbl>
## 1 mean_tong_cong_tai_san_yoy 0.0832
## 2 mean_doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_1… 0.0505
## 3 mean_loi_nhuan_sau_thue_tndn_yoy 0.0275
Giải thích
Kỹ thuật (theo dòng):
Dòng 1: vars_growth <- c(…) → Khai báo danh sách ba chỉ tiêu chính cần tính tăng trưởng: tổng tài sản, doanh thu, lợi nhuận sau thuế.
Dòng 2: arrange(nam) → Sắp xếp dữ liệu theo năm để tính đúng thứ tự thời gian.
Dòng 3: mutate(across(…, (.x - lag(.x)) / lag(.x))) → Tạo biến mới thể hiện tốc độ tăng trưởng năm theo công thức R:
YoY = (Giá_trị_t - Giá_trị_t_1) / Giá_trị_t_1
→ Kết quả là các cột mới có hậu tố _yoy.
Dòng 4: summarise(across(ends_with(“_yoy”), mean(.x))) → Tính tốc độ tăng trưởng trung bình (Mean YoY) cho từng biến trong toàn giai đoạn.
Dòng 5: pivot_longer() → Chuyển dữ liệu về dạng dọc để hiển thị từng chỉ tiêu và giá trị tăng trưởng trung bình tương ứng.
Dòng 6: print(vnm_growth) → Hiển thị bảng kết quả tốc độ tăng trưởng trung bình.
Ý nghĩa:
Tổng tài sản tăng trung bình 8,3%/năm → quy mô doanh nghiệp mở rộng vững chắc.
Doanh thu tăng 5,0%/năm → duy trì tăng trưởng ổn định dù thị trường bão hòa.
Lợi nhuận sau thuế tăng 2,7%/năm → hiệu quả hoạt động cao, không đánh đổi lợi nhuận để mở rộng quy mô.
1. library(dplyr)
2. library(tidyr)
3. library(e1071)
4. vars_shape <- c("tong_cong_tai_san",
5. "doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02",
6. "loi_nhuan_sau_thue_tndn",
7. "roa","roe")
8. shape_stats <- vnm20 %>%
9. summarise(across(all_of(vars_shape),
10. list(skew = ~ e1071::skewness(.x, na.rm = TRUE, type = 2),
11. kurt = ~ e1071::kurtosis(.x, na.rm = TRUE, type = 2)),
12. .names = "{.col}_{.fn}")) %>%
13. pivot_longer(everything(),
14. names_to = c("Chi_tieu","Thong_ke"),
15. names_pattern = "^(.*)_(skew|kurt)$",
16. values_to = "Gia_tri") %>%
17. arrange(Chi_tieu, match(Thong_ke, c("skew","kurt")))
18. print(shape_stats, n = Inf)
## # A tibble: 10 × 3
## Chi_tieu Thong_ke Gia_tri
## <chr> <chr> <dbl>
## 1 doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02 skew -1.10
## 2 doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02 kurt 0.395
## 3 loi_nhuan_sau_thue_tndn skew -0.448
## 4 loi_nhuan_sau_thue_tndn kurt -0.431
## 5 roa skew 0.134
## 6 roa kurt -1.58
## 7 roe skew 0.0246
## 8 roe kurt -1.58
## 9 tong_cong_tai_san skew -0.442
## 10 tong_cong_tai_san kurt -1.44
Giải thích
Kỹ thuật:
- Dòng 1–3: library(…) → Nạp các gói cần thiết:
dplyr (xử lý dữ liệu), tidyr (định dạng bảng), e1071 (tính skewness
& kurtosis).
- Dòng 5: vars_shape <- c(…)
→ Chọn 5 chỉ tiêu chính cần đánh giá hình dạng phân phối: tài sản, doanh
thu, lợi nhuận, ROA, ROE.
- Dòng 7–12: summarise(across(…)) → Tính độ lệch (skewness) và độ nhọn
(kurtosis) cho từng biến.
Skewness = (E[(X - μ)^3]) / σ^3
Kurtosis = (E[(X - μ)^4]) / σ^4 - 3
→ type = 2 chọn công thức mẫu điều chỉnh (phù hợp cho dữ liệu
nhỏ).
- Dòng 13–16: pivot_longer()
→ Chuyển kết quả thành 3 cột: Chỉ_tiêu – Thống_kê – Giá_trị, dễ đọc và
trình bày trong báo cáo.
- Dòng 17: arrange()
→ Sắp xếp lại kết quả theo đúng thứ tự Skew → Kurt.
- Dòng 19: print(shape_stats, n = Inf)
→ In toàn bộ kết quả, không rút gọn dòng.
Ý nghĩa: Doanh thu (skew = –1.10, kurt ≈ 0.40) → lệch trái nhẹ,
phân phối hơi nhọn; tăng trưởng doanh thu ổn định, không cực đoan.
Lợi nhuận (skew = –0.45, kurt = –0.43) → phân phối đối xứng, tương đối
phẳng, biến động lợi nhuận thấp.
ROA, ROE (skew ≈ 0, kurt ≈ –1.58) → phân phối chuẩn, hơi bẹt → hiệu quả
sinh lời ổn định, ít giá trị ngoại lệ.
Tổng tài sản (skew = –0.44, kurt = –1.44) → phân phối khá đối xứng, độ
tập trung thấp → quy mô tăng đều theo thời gian.
1. vnm_long_1 <- vnm20 %>% transmute(
2. nam_y = year(nam), Tong_tai_san = tong_cong_tai_san,
3. Doanh_thu_thuan = doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02,
4. Loi_nhuan_sau_thue = loi_nhuan_sau_thue_tndn ) %>%
5. pivot_longer(-nam_y, names_to = "Chi_tieu", values_to = "Gia_tri")
6. data_last <- vnm_long_1 %>% group_by(Chi_tieu) %>%
7. slice_max(nam_y, n = 1, with_ties = FALSE) %>% ungroup()
8. ggplot(vnm_long_1, aes(x = nam_y, y = Gia_tri, color = Chi_tieu)) +
9. geom_line(linewidth = 1) + geom_point(size = 2) +
10. geom_smooth(se = FALSE, method = "lm", linewidth = 0.7, alpha = 0.3) +
11. geom_vline(xintercept = c(2020, 2022), linetype = "dashed", alpha = 0.5) +
12. geom_label(data = data_last,
13. aes(label = number(Gia_tri, big.mark = ".", decimal.mark = ",")),
14. size = 3, label.size = 0.2, nudge_x = 0.2, show.legend = FALSE) +
15. scale_y_continuous(labels = label_number(big.mark = ".", decimal.mark = ",")) + labs( title = "Xu hướng Tổng tài sản – Doanh thu\n – Lợi nhuận của Vinamilk", subtitle = "Đơn vị: Tỷ đồng (có đường hồi quy xu thế)", x = "Năm", y = "Tỷ đồng", color = "Chỉ tiêu") + theme_minimal(base_size = 12) + theme(plot.title = element_text(face = "bold", lineheight = 1.1))
Giải thích:
Kỹ thuật:
- Dòng 2–5: Đổi tên biến và pivot hợp lý; đảm bảo nam_y là số/integer và
Gia_tri numeric (tránh NA trước khi vẽ).
- Dòng 6–7: slice_max() ổn; thêm na.rm = TRUE ở bước tính/lọc hoặc
filter(!is.na(Gia_tri)) để nhãn cuối không thiếu.
- Dòng 8–11: Line/points rõ; geom_smooth(method=“lm”) đang fit theo từng
Chi_tieu (ổn). Cân nhắc scale_x_continuous(breaks = pretty(nam_y)) và
giữ/giải thích mốc 2020, 2022.
- Dòng 12–15: Nhãn đẹp; thêm coord_cartesian(clip=“off”) (hoặc expand =
expansion(mult = c(0.02,0.05))) để nhãn lệch phải không bị cắt; cân nhắc
bảng màu thân thiện mù màu (scale_color_viridis_d()).
Ý nghĩa:
Ba chỉ tiêu Tổng tài sản – Doanh thu – Lợi nhuận cùng tăng đều, thể hiện
quy mô và hiệu quả hoạt động mở rộng liên tục.
Giai đoạn 2020–2022 có dao động nhẹ, song xu hướng hồi quy vẫn dốc lên →
doanh nghiệp duy trì tăng trưởng ổn định.
1. yoy_long <- vnm20 %>% arrange(nam) %>%
2. transmute( nam_y = year(nam),
3. tong_cong_tai_san_yoy = tong_cong_tai_san / dplyr::lag(tong_cong_tai_san) - 1,
4. doanh_thu_thuan_yoy = doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02 /
5. dplyr::lag(doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02) - 1,
6. loi_nhuan_sau_thue_yoy = loi_nhuan_sau_thue_tndn / dplyr::lag(loi_nhuan_sau_thue_tndn) - 1 ) %>%
7. pivot_longer(-nam_y, names_to = "Chi_tieu", values_to = "Tang_truong")
8. last_pts <- yoy_long %>% group_by(Chi_tieu) %>%
9. filter(!is.na(Tang_truong)) %>% slice_max(nam_y, n = 1, with_ties = FALSE) %>%
10. ungroup()
11. ggplot(yoy_long, aes(x = nam_y, y = Tang_truong, color = Chi_tieu)) +
12. geom_hline(yintercept = 0, linetype = "dotted", linewidth = 0.6, alpha = 0.7) +
13. geom_line(linewidth = 1) + geom_point(size = 2) +
14. geom_smooth(se = FALSE, method = "lm", linewidth = 0.7, alpha = 0.25) +
15. geom_vline(xintercept = c(2020, 2022), linetype = "dashed", alpha = 0.5) +
16. geom_text_repel( data = last_pts, aes(label = percent(Tang_truong, accuracy = 0.1)),size = 3.5, nudge_x = 0.3, egment.size = 0.2, segment.alpha = 0.6, direction = "y", hjust = 0, show.legend = FALSE ) +
17. scale_y_continuous(labels = label_percent(accuracy = 1)) +
18. labs( title = " Tốc độ tăng trưởng năm (YoY): Tài sản – Doanh thu – Lợi nhuận", x = "Năm", y = "Tốc độ tăng trưởng", color = "Chỉ tiêu" ) +
19. theme_minimal(base_size = 12) +
20. theme(plot.title = element_text(face = "bold"))
Giải thích
Kỹ thuật: - Dòng 1–7: Tính YoY đúng; thêm
filter(is.finite(Tang_truong)) (tránh NA/Inf do lag hoặc mẫu số 0) và có
thể gọn bằng mutate(across(…, ~ . / dplyr::lag(.) - 1)) rồi
pivot_longer().
- Dòng 8–10: Lấy điểm cuối hợp lý; bạn đã lọc !is.na() chuẩn.
- Dòng 11–15: Đồ thị rõ; cân nhắc scale_x_continuous(breaks =
pretty(nam_y)) và giữ/cụ thể hóa ý nghĩa các mốc 2020, 2022.
- Dòng 16–20: Lỗi chính tả egment.size → segment.size; dùng
scale_y_continuous(labels = scales::label_percent(accuracy = 1)) và có
thể thêm guide_legend(nrow=1), legend.position=“top”.
Ý nghĩa: Doanh thu YoY giảm dần nhưng vẫn dương ~2–5% → tăng
trưởng chậm lại, song nền tảng bán hàng ổn định.
Lợi nhuận YoY biến động mạnh, rơi sâu 2022 rồi hồi phục 2023–2024 → biên
lợi nhuận nhạy với chi phí/chu kỳ.
Tài sản YoY ổn định quanh 3–9% → doanh nghiệp tiếp tục mở rộng quy mô có
kiểm soát; “cú sốc 2022” thể hiện rõ nhưng không kéo dài.
1. profit_long <- vnm20 %>% transmute( nam_y = year(nam), roa, roe, ros ) %>%
2. pivot_longer(-nam_y, names_to = "Chi_tieu", values_to = "Gia_tri")
3. last_vals <- profit_long %>% group_by(Chi_tieu) %>% filter(!is.na(Gia_tri)) %>%
4. slice_max(nam_y, n = 1, with_ties = FALSE) %>% ungroup()
5. ggplot(profit_long, aes(x = nam_y, y = Gia_tri, color = Chi_tieu)) +
6. geom_hline(yintercept = 0, linetype = "dotted", linewidth = 0.6, alpha = 0.7) + geom_line(linewidth = 1) + geom_point(size = 2) +
7. geom_smooth(se = FALSE, method = "lm", linewidth = 0.7, alpha = 0.25) +
8. geom_vline(xintercept = c(2020, 2022), linetype = "dashed", alpha = 0.5) +
9. geom_label_repel(data = last_vals, aes(label = scales::percent(Gia_tri, accuracy = 0.1)), size = 3.5, nudge_x = 0.3, segment.size = 0.2, box.padding = 0.3,
10. segment.alpha = 0.6, show.legend = FALSE ) +
11. scale_y_continuous(labels = label_percent(accuracy = 1)) +
12. labs( title = "Tỷ suất sinh lời: ROA – ROE – ROS (2015–2024)",
13. subtitle = "Đơn vị: phần trăm", x = "Năm", y = "Giá trị (%)",
14. color = "Chỉ tiêu") + theme_minimal(base_size = 12) +
15. theme(plot.title = element_text(face = "bold"))
Giải thích
Kỹ thuật:
- Dòng 1–4: Tiền xử lý gọn; đảm bảo roa/roe/ros numeric và cùng thang
(nếu đã % thì bỏ label_percent() hoặc chia/nhân cho đúng).
- Dòng 5–8: Line + điểm + lm ổn; cân nhắc scale_x_continuous(breaks =
pretty(nam_y)) và giải thích mốc 2020, 2022; có thể bỏ geom_smooth() nếu
gây nhiễu.
- Dòng 9–11: Nhãn cuối đẹp; thêm coord_cartesian(clip=“off”) để tránh
cắt nhãn và nhớ library(ggrepel).
- Dòng 12–15: Nhãn/theme ổn; tránh hard-code “2015–2024” (lấy từ
range(nam_y)), legend nên đặt position=“top” và cân nhắc
scale_color_viridis_d().
Ý nghĩa:
ROE (26.1%) giảm dần nhưng vẫn cao hơn chuẩn ngành → lợi nhuận trên vốn
chủ ở mức mạnh, cho thấy năng lực tài chính vững.
ROA (17.2%) giảm nhẹ phản ánh hiệu quả sử dụng tài sản chậm lại nhưng
vẫn ổn định.
ROS (15.3%) duy trì ổn định → biên lợi nhuận ít bị co hẹp, doanh nghiệp
giữ hiệu quả hoạt động tốt.
Ba chỉ tiêu giảm dần nhưng ổn định cùng chiều → Vinamilk duy trì khả
năng sinh lời cao và bền vững trong dài hạn.
1. plot4_long <- vnm20 %>%
2. transmute(nam_y = year(nam), debt_ratio, equity_ratio, current_ratio) %>%
3. pivot_longer(-nam_y, names_to = "Chi_tieu", values_to = "Gia_tri")
4. plot4_last <- plot4_long %>% group_by(Chi_tieu) %>% slice_max(nam_y, n = 1) %>% ungroup()
5. ggplot(plot4_long, aes(nam_y, Gia_tri, color = Chi_tieu)) +
6. geom_line(linewidth = 1) + geom_point(size = 2) + geom_hline(yintercept = 1, linetype = "dotted") + geom_label_repel(data = plot4_last, aes(label = number(Gia_tri, accuracy = 0.01, decimal.mark = ",")),
7. size = 3.5, nudge_x = 0.3, show.legend = FALSE) +
8. facet_wrap(~Chi_tieu, scales = "free_y") +
9. scale_x_continuous(breaks = seq(2015, 2024, by = 2),
10. guide = guide_axis(n.dodge = 2)) +
11. labs(title = " Cơ cấu vốn & thanh khoản (2015–2024)",
12. x = "Năm", y = "Giá trị (lần)", color = "Chỉ tiêu") +
13. theme_minimal(base_size = 12) +
14. theme(axis.text.x = element_text(margin = margin(t = 2)))
Giải thích
Kỹ thuật:
- Dòng 1–4: Tiền xử lý gọn; dùng slice_max(nam_y, n=1, with_ties=FALSE)
và đảm bảo Gia_tri numeric, nam_y integer.
- Dòng 5–7: Line/point ổn; thêm coord_cartesian(clip=“off”) và expand =
expansion(mult=c(.02,.08)) để nhãn không bị cắt; nhớ
library(ggrepel).
- Dòng 6: geom_hline(yintercept=1) chỉ hợp lý cho current_ratio; nên áp
dụng có điều kiện: geom_hline(data=filter(plot4_long,
Chi_tieu==“current_ratio”), aes(yintercept=1), linetype=“dotted”).
- Dòng 8–14: Đã facet nên ẩn legend (guides(color=“none”)); tránh
hard-code “2015–2024” (lấy từ range(nam_y)), cân nhắc
scale_color_viridis_d() cho màu thân thiện mù màu.
Ý nghĩa:
Current ratio ~2 lần → thể hiện khả năng thanh toán ngắn hạn rất
tốt.
Debt ratio ~0.34, Equity ratio ~0.66 → cấu trúc vốn an toàn, chủ yếu từ
vốn chủ sở hữu.
Nhìn chung, biểu đồ thể hiện Vinamilk duy trì đòn bẩy thấp, thanh khoản
mạnh, phản ánh tình hình tài chính ổn định và quản trị hiệu quả.
1. plot5_long <- vnm20 %>%
2. transmute( nam_y = year(nam), tai_san_ngan_han, tai_san_co_dinh_huu_hinh,
3. tong_cong_tai_san ) %>%
4. pivot_longer(-nam_y, names_to = "Chi_tieu", values_to = "Gia_tri_ty_dong")
5. plot5_last <- plot5_long %>% group_by(Chi_tieu) %>% slice_max(nam_y, n = 1)
6. ggplot(plot5_long, aes(nam_y, Gia_tri_ty_dong, color = Chi_tieu)) +
7. geom_area(aes(fill = Chi_tieu), alpha = 0.25, color = NA) +
8. geom_line(linewidth = 1) + geom_point(size = 2) + geom_label_repel(
9. data = plot5_last,
10. aes(label = number(Gia_tri_ty_dong, accuracy = 0.1, big.mark = ".", decimal.mark = ",")), nudge_x = 0.3, size = 3.5, show.legend = FALSE) +
11. scale_x_continuous(breaks = 2015:2024) +
12. labs( title = " Cấu trúc tài sản (2015–2024)",
13. subtitle = "Tài sản ngắn hạn – Tài sản cố định – Tổng tài sản",
14. x = "Năm", y = "Giá trị (tỷ đồng)", color = "Chỉ tiêu", fill = "Chỉ tiêu" ) +
15. theme_minimal(base_size = 12) + theme(plot.title = element_text(face = "bold"))
Giải thích
Kỹ thuật:
- Dòng 1–5: Tiền xử lý/pivot ổn; thêm with_ties = FALSE và đảm bảo
Gia_tri_ty_dong numeric.
- Dòng 6–8: Tránh stack tổng với thành phần (dễ “đếm đôi”); chỉ stack 2
thành phần, còn tong_cong_tai_san vẽ geom_line(), hoặc dùng
position=“identity” cho cả 3.
- Dòng 9–10: Nhãn cuối đẹp; thêm coord_cartesian(clip=“off”) + expand =
expansion(mult = c(.02,.08)) để không bị cắt.
- Dòng 11–15: Tránh hard-code 2015:2024 → scale_x_continuous(breaks =
pretty(nam_y)); định dạng trục Y với label_number(big.mark=“.”,
decimal.mark=“,”), cân nhắc legend.position=“top” và
scale_color/fill_viridis_d().
Ý nghĩa:
Tổng tài sản tăng từ khoảng 27.478 tỷ đồng năm 2015 lên 55.049 tỷ đồng
năm 2024, tức tăng hơn gấp đôi trong 10 năm. → Quy mô doanh nghiệp mở
rộng ổn định, thể hiện khả năng tích lũy và đầu tư mạnh.
Tài sản ngắn hạn tăng từ 16.732 tỷ đồng lên 37.553 tỷ đồng, chiếm khoảng
68–72% tổng tài sản qua các năm → cho thấy cơ cấu vốn thiên về tài sản
lưu động, phản ánh khả năng thanh khoản cao.
Tài sản cố định hữu hình tăng từ 8.668 tỷ đồng lên 11.520 tỷ đồng, tỷ
trọng ổn định quanh 20–25% tổng tài sản, thể hiện đầu tư dài hạn bền
vững và có kiểm soát.
Khoảng cách giữa ba đường giữ ổn định trong suốt giai đoạn, chứng minh
Vinamilk duy trì cơ cấu tài sản cân đối, kết hợp hợp lý giữa tài sản
ngắn hạn linh hoạt và đầu tư dài hạn hiệu quả.
1. plot6_long <- vnm20 %>% transmute( nam_y = year(nam), tong_nguon_von,
2. von_chu_so_huu, no_phai_tra ) %>%
3. pivot_longer(-nam_y, names_to = "Chi_tieu", values_to = "Gia_tri_ty_dong")
4. plot6_last <- plot6_long %>% group_by(Chi_tieu) %>% slice_max(nam_y, n = 1)
5. ggplot(plot6_long, aes(nam_y, Gia_tri_ty_dong, color = Chi_tieu)) +
6. geom_area(aes(fill = Chi_tieu), alpha = 0.25, color = NA) +
7. geom_line(linewidth = 1) + geom_point(size = 2) + geom_label_repel(
8. data = plot6_last, aes(label = number(Gia_tri_ty_dong, accuracy = 0.1, big.mark = ".", decimal.mark = ",")), size = 3.5, nudge_x = 0.3, show.legend = FALSE ) + scale_x_continuous(breaks = 2015:2024) + labs(
9. title = " Cấu trúc nguồn vốn của Vinamilk (2015–2024)",
10. subtitle = "Vốn chủ sở hữu – Nợ phải trả – Tổng nguồn vốn",
11. x = "Năm", y = "Giá trị (tỷ đồng)", color = "Chỉ tiêu", fill = "Chỉ tiêu" ) +
12. theme_minimal(base_size = 12) + theme(plot.title = element_text(face = "bold"))
Giải thích
Kỹ thuật:
- Dòng 1–4: Tiền xử lý ổn; thêm with_ties = FALSE trong slice_max(), đảm
bảo Gia_tri_ty_dong numeric và lọc !is.na().
- Dòng 5–7: geom_area() đang stack cả 3 chỉ tiêu (trong đó
tong_nguon_von = vốn + nợ) → dễ “đếm đôi”; nên stack chỉ von_chu_so_huu
+ no_phai_tra, và vẽ tong_nguon_von bằng geom_line() (hoặc dùng
position=“identity”, alpha nếu muốn chồng lấp).
- Dòng 8: Nhãn cuối đẹp; thêm coord_cartesian(clip=“off”) + expand =
expansion(mult=c(.02,.08)) để tránh cắt nhãn.
- Trục/nhãn: Tránh hard-code 2015:2024 → scale_x_continuous(breaks =
pretty(nam_y)); thêm scale_y_continuous(labels =
scales::label_number(big.mark=“.”, decimal.mark=“,”)); cân nhắc
legend.position=“top” và scale_color/fill_viridis_d(). Ý
nghĩa:
Tổng nguồn vốn tăng từ khoảng 27.478 tỷ đồng năm 2015 lên 55.049 tỷ đồng
năm 2024, cho thấy Vinamilk mở rộng quy mô tài chính gấp đôi trong 10
năm.
Vốn chủ sở hữu tăng từ 20.924 tỷ đồng lên 36.174 tỷ đồng, chiếm trung
bình 65–75% tổng nguồn vốn, thể hiện nền tảng tự chủ tài chính
mạnh.
Nợ phải trả dao động quanh 7.000–18.000 tỷ đồng, tỷ trọng chỉ khoảng
25–35%, phản ánh đòn bẩy tài chính thấp và rủi ro vay nợ hạn chế.
Khoảng cách ổn định giữa hai vùng (nợ và vốn chủ) cho thấy cấu trúc vốn
ít biến động, Vinamilk ưu tiên nguồn vốn tự có, đảm bảo tính bền vững
dài hạn.
1. plot7_long <- vnm20 %>% transmute( nam_y = year(nam),
2. hoat_dong_kinh_doanh = luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh,
3. hoat_dong_dau_tu = luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu,
4. hoat_dong_tai_chinh = luu_chuyen_tien_thuan_tu_hoat_dong_tai_chinh) %>%
5. pivot_longer(-nam_y, names_to = "Chi_tieu", values_to = "Gia_tri_ty_dong")
6. plot7_last <- plot7_long %>% group_by(Chi_tieu) %>% slice_max(nam_y, n = 1)
7. ggplot(plot7_long, aes(nam_y, Gia_tri_ty_dong, color = Chi_tieu)) +
8. geom_hline(yintercept = 0, linetype = "dashed", color = "gray50") +
9. geom_line(linewidth = 1) + geom_point(size = 2) + geom_label_repel(
10. data = plot7_last,
11. aes(label = number(Gia_tri_ty_dong, accuracy = 0.1, big.mark = ".", decimal.mark = ",")), nudge_x = 0.4, size = 3.5, show.legend = FALSE) +
12. scale_x_continuous(breaks = 2015:2024) +
13. labs( title = "Cấu trúc dòng tiền của Vinamilk (2015–2024)",
14. subtitle = "Lưu chuyển tiền từ hoạt động kinh doanh, đầu tư và tài chính",
15. x = "Năm", y = "Giá trị (tỷ đồng)", color = "Chỉ tiêu" ) + theme_minimal(base_size = 12) + theme(plot.title = element_text(face = "bold"))
Giải thích
Kỹ thuật
- Dòng 1–5: Tiền xử lý/pivot ổn; đảm bảo Gia_tri_ty_dong numeric, lọc
!is.na() trước khi vẽ.
- Dòng 6: Dùng slice_max(nam_y, n = 1, with_ties = FALSE) để lấy đúng
điểm cuối cho mỗi chỉ tiêu.
- Dòng 7–11: Đồ thị rõ; thêm coord_cartesian(clip = “off”) + expand =
expansion(mult = c(.02, .08)) để nhãn geom_label_repel không bị cắt; cân
nhắc scale_color_viridis_d() và nhớ library(ggrepel).
- Dòng 12–15: Tránh hard-code 2015:2024 → scale_x_continuous(breaks =
pretty(nam_y)); định dạng trục Y: scale_y_continuous(labels =
scales::label_number(big.mark=“.”, decimal.mark=“,”)); có thể đặt
legend.position = “top”.
Ý nghĩa:
Dòng tiền hoạt động kinh doanh (CFO) luôn dương và đạt đỉnh hơn 10.000
tỷ đồng năm 2020, giữ mức 9.685 tỷ năm 2024 → Vinamilk tạo ra tiền ổn
định từ hoạt động cốt lõi.
Dòng tiền đầu tư (CFI) luôn âm, trung bình –4.000 đến –6.000 tỷ/năm, thể
hiện chi đầu tư mở rộng tài sản cố định và nhà máy, phản ánh chiến lược
tăng trưởng dài hạn.
Dòng tiền tài chính (CFF) dao động quanh –6.000 tỷ năm 2024, cho thấy
doanh nghiệp trả cổ tức và giảm nợ vay, ít phụ thuộc nguồn tài trợ bên
ngoài. Đường cơ sở 0 chia rõ các luồng tiền: CFO dương mạnh (tạo tiền),
CFI và CFF âm (chi cho đầu tư và cổ tức) → mô hình dòng tiền “tự tài trợ
– bền vững – hiệu quả”.
1. ggplot(vnm20, aes(x = roa, y = roe)) +
2. geom_hline(yintercept = mean(vnm20$roe, na.rm = TRUE),
3. linetype = "dashed", size = 0.3, color = "grey40") +
4. geom_vline(xintercept = mean(vnm20$roa, na.rm = TRUE),
5. linetype = "dashed", size = 0.3, color = "grey40") +
6. geom_point(aes(size = asset_turnover, color = leverage), alpha = 0.8) +
7. geom_smooth(method = "loess", se = FALSE, size = 0.6) +
8. geom_label_repel(aes(label = format(nam, "%Y")), size = 3, label.size = 0.2, seed = 1, min.segment.length = 0, max.overlaps = 20) +
9. geom_rug(alpha = 0.4, length = unit(0.02, "npc")) +
10. scale_x_continuous(labels = percent_format(accuracy = 1)) +
11. scale_y_continuous(labels = percent_format(accuracy = 1)) +
12. scale_size_continuous(name = "Vòng quay tài sản") +
13. scale_color_viridis_c(name = "Đòn bẩy (Assets/Equity)", option = "C") +
14. labs( title = " Quan hệ ROA và ROE của Vinamilk (2015–2024)",
15. x = "ROA (Lợi nhuận sau thuế / Tổng tài sản)",
16. y = "ROE (Lợi nhuận sau thuế / Vốn chủ sở hữu)" ) +
17. theme_minimal(base_size = 12) + theme( panel.grid.minor = element_blank(),
18. legend.position = "right", plot.title = element_text(face = "bold"))
Giải thích Kỹ thuật:
- Dòng 1–5: Bố cục scatter đúng; nên tính sẵn mean_roa/mean_roe rồi dùng
geom_hline/vline(data=…) để tránh lặp tính.
- Dòng 6–7: Mapping size/color hợp lý; cân nhắc
scale_size_area(max_size=8) (đều diện tích) và giữ method=“loess” chỉ
khi đủ điểm.
- Dòng 8–9: Nhãn năm ổn; nếu nam là số (2015…), dùng label = nam thay
format(). Thêm coord_cartesian(clip=“off”) để nhãn không bị cắt.
- Dòng 10–18: percent_format() ok nếu roa/roe ở thang 0–1; nếu đã là %,
dùng scale=1. Có thể đặt legend.position=“top” và thêm
guides(size=guide_legend(order=1), color=guide_colorbar(order=2)).
Ý nghĩa:
Giai đoạn 2015–2017: ROA đạt ~30–32%, ROE ~40–43%, phản ánh giai đoạn
tăng trưởng cực thịnh.
Từ 2018–2024: cả ROA và ROE giảm dần (ROA còn ~17%, ROE ~26%), thể hiện
biên lợi nhuận bị thu hẹp khi quy mô mở rộng.
Màu điểm (đòn bẩy): từ vàng (cao ~1.5) → tím (thấp ~1.3) cho thấy
Vinamilk giảm sử dụng nợ qua thời gian, hướng đến cơ cấu vốn an
toàn.
Kích thước điểm (vòng quay tài sản): các năm đầu lớn hơn (1.5–1.6) →
chứng tỏ tài sản được khai thác hiệu quả hơn, sau giảm nhẹ còn ~1.2 khi
công suất đã ổn định.
1. vars_corr <- c("tong_cong_tai_san", "doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02",
2. "loi_nhuan_sau_thue_tndn", "debt_ratio","current_ratio","roa","roe","ros")
3. mat <- cor(select(vnm20, all_of(vars_corr)), use = "complete.obs")
4. short_names <- c( tong_cong_tai_san = "TTS",
5. doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02 = "DT",
6. loi_nhuan_sau_thue_tndn = "LNST", debt_ratio = "Nợ\n/Tổng TS",
7. current_ratio = "CR", roa = "ROA", roe = "ROE", ros = "ROS" )
8. rownames(mat) <- short_names[rownames(mat)]
9. colnames(mat) <- short_names[colnames(mat)]
10. heat <- as.data.frame(mat) |>
11. tibble::rownames_to_column("row") |>
12. pivot_longer(-row, names_to = "col", values_to = "r")
13. ggplot(heat, aes(x = row, y = col, fill = r)) +
14. geom_tile(color = "white", size = 0.4) +
15. geom_text(aes(label = round(r, 2)), size = 3) +
16. scale_fill_gradient2(low = "#4575b4", mid = "white", high = "#d73027",
17. midpoint = 0, limits = c(-1, 1), name = "Tương quan") +
18. coord_equal() + labs(title = " Ma trận tương quan (rút gọn)",
19. subtitle = "TTS – DT – LNST – CR – Debt – ROA – ROE – ROS",
20. x = NULL, y = NULL) + theme_minimal(base_size = 11) +
21. theme(axis.text.x = element_text(angle = 0, hjust = 0.5),
22. panel.grid = element_blank())
Giải thích
Kỹ thuật:
- Dòng 1–3: Tính ma trận tương quan các biến tài chính, lọc dữ liệu hoàn
chỉnh bằng complete.obs.
- Dòng 4–9: Rút gọn tên biến giúp biểu đồ dễ đọc, bố trí gọn gàng.
- Dòng 10–12: Chuyển ma trận sang dạng dài để ggplot xử lý.
- Dòng 13–22: Vẽ heatmap, màu đỏ–xanh thể hiện tương quan ±, bố cục đẹp
và dễ so sánh.
Ý nghĩa: Các cặp ROA–ROE–ROS có hệ số r > 0.90, thể hiện
hiệu quả sinh lời đồng biến rất mạnh.
Debt ratio tương quan âm mạnh với ROA, ROE (–0.7 đến –0.8) → tỷ lệ nợ
tăng làm giảm hiệu suất.
TTS (tổng tài sản) tương quan âm với ROA/ROE nhưng dương với Debt → mở
rộng quy mô đi kèm tăng nợ, giảm biên lợi nhuận nhẹ.
Biểu đồ giúp nhìn tổng thể cấu trúc mối quan hệ tài chính của Vinamilk
một cách trực quan và dễ hiểu.
1. plot10_long <- vnm20 %>%
2. select(nam, roa, roe, ros) %>%
3. pivot_longer(-nam, names_to = "Chi_tieu", values_to = "Gia_tri") %>%
4. mutate(Nam = format(nam, "%Y"))
5. ggplot(plot10_long, aes(x = Chi_tieu, y = Gia_tri, fill = Chi_tieu)) +
6. geom_boxplot(alpha = 0.3, width = 0.6, outlier.shape = NA, color = "gray30") + geom_jitter(aes(color = Nam), size = 3, width = 0.1, alpha = 0.9) + stat_summary(fun = median, geom = "point", size = 3.2, color = "black") + stat_summary(fun = mean, geom = "point", shape = 23, size = 3.2, fill = "white") + stat_summary(fun = mean,
7. aes(label = scales::percent(..y.., accuracy = 0.1)),
8. geom = "label_repel", size = 3, label.size = 0,
9. box.padding = 0.2, color = "gray25", min.segment.length = 0) +
10. scale_y_continuous(labels = label_percent(accuracy = 1)) +
11. scale_fill_manual(values = c(roa = "#F29E9E", roe = "#9ED0A9", ros = "#9EBCF2")) + scale_color_viridis_d(option = "plasma", begin = 0, end = 0.9) + labs(
12. title = " Phân phối ROA – ROE – ROS theo năm (2015–2024)",
13. subtitle = "Hộp màu theo chỉ tiêu; mỗi chấm tròn là một năm;\n chấm đen = trung vị, hình thoi = trung bình", x = NULL, y = "Giá trị (%)", color = "Năm" ) +
14. theme_minimal(base_size = 12) + theme(panel.grid.minor = element_blank())
Giải thích
Kỹ thuật:
- Dòng 1–4: Chọn ROA/ROE/ROS, pivot_longer đúng; tạo nhãn năm. Nếu nam
là số (2015…), dùng Nam = as.character(nam) thay vì format().
- Dòng 5–6: Boxplot + jitter + median/mean rõ; outlier.shape=NA tránh
trùng với jitter. Có thể thêm position_jitter(width=0.1, height=0) để
kiểm soát nhiễu.
- Dòng 7–11: Nhãn mean dạng % ổn khi ROA/ROE/ROS ở thang 0–1; nếu đã là
%, bỏ label_percent() (hoặc chia 100 trước). Có thể thêm expand =
expansion(mult = c(0, .05)) và legend.position=“top”.
- Dòng 12–14: Tiêu đề/phụ đề rõ; theme_minimal() gọn, tắt minor grid hợp
lý. Cân nhắc scale_y_continuous(labels=label_percent(accuracy=0.1)) để
nhất quán với nhãn mean.
Ý nghĩa:
Mỗi năm là một màu khác nhau, giúp nhận diện nhanh xu hướng biến động
qua thời gian.
ROE vẫn nổi bật cao nhất (trung bình ~34.4%), ROA khoảng 23.4%, ROS
~18.8%.
Các năm đầu (màu sáng hơn, 2015–2017) tập trung ở phía trên → hiệu quả
cao, trong khi các năm sau (màu trầm hơn, 2021–2024) nằm thấp hơn → lợi
nhuận suy giảm nhẹ nhưng ổn định.
1. plot11_data <- vnm20 %>% select(nam, debt_ratio, roa, current_ratio, tong_cong_tai_san) %>% mutate(Nam = format(nam, "%Y"))
2. ggplot(plot11_data, aes(x = debt_ratio, y = roa)) +
3. geom_point(aes(size = tong_cong_tai_san, color = current_ratio), alpha = 0.8) +
4. geom_smooth(method = "lm", se = FALSE, color = "gray30", linetype = "dashed") +
5. geom_text_repel( aes(label = Nam), size = 3.2, color = "gray20",
6. max.overlaps = Inf, box.padding = 0.3, point.padding = 0.4, segment.color = "gray60" ) +
7. scale_x_continuous(labels = label_percent(accuracy = 0.1, decimal.mark = ",")) +
8. scale_y_continuous(labels = label_percent(accuracy = 0.1, decimal.mark = ",")) +
9. scale_color_viridis_c(option = "plasma", begin = 0, end = 1) +
10. scale_size_continuous( range = c(3, 9), labels = label_number(accuracy = 0.1, big.mark = ".", decimal.mark = ",", scale_cut = cut_si("B")) ) +
11. labs( title = "Biểu đồ 11. Quan hệ giữa Tỷ lệ nợ và ROA",
12. subtitle = "Màu: Current ratio | Kích thước: Tổng tài sản\n Đường gạch: xu hướng hồi quy", x = "Tỷ lệ nợ trên tổng nguồn vốn (%)", y = "ROA (Lợi nhuận sau thuế / Tổng tài sản, %)", color = "Current ratio", size = "Tổng tài sản") +
13. theme_minimal(base_size = 12) +
14. theme(legend.position = "right", panel.grid.minor = element_blank())
Giải thích
Kỹ thuật:
- Dòng 1: Chọn biến chính (năm, debt_ratio, roa, current_ratio, tài
sản), tạo biến nhãn năm để hiển thị.
- Dòng 2–4: Vẽ scatter ROA–nợ, thêm kích thước theo tổng tài sản, màu
theo current_ratio, đường hồi quy rõ.
- Dòng 5–6: Nhãn năm dùng geom_text_repel hợp lý, tránh chồng chữ, dễ
đọc.
- Dòng 7–14: Chuẩn hoá trục %, màu plasma, chú thích rõ ràng; theme tối
giản giúp biểu đồ sạch, cân đối. Ý nghĩa:
Biểu đồ cho thấy mối quan hệ ngược chiều rõ rệt giữa tỷ lệ nợ và hiệu
quả sinh lời tài sản (ROA).
Khi tỷ lệ nợ tăng dần từ khoảng 25% lên 33%, ROA giảm từ hơn 30% xuống
quanh 17–19%.
Những năm có khả năng thanh toán cao hơn thường đạt ROA tốt hơn, thể
hiện nền tài chính lành mạnh hỗ trợ hiệu quả kinh doanh.
Ngược lại, giai đoạn gần đây tuy quy mô tài sản mở rộng nhưng ROA giảm,
cho thấy việc sử dụng vốn chưa tối ưu.
1. plot12_data <- vnm20 %>% select(nam, roa, ros, asset_turnover) %>%
2. mutate(Nam = format(nam, "%Y"))
3. ggplot(plot12_data, aes(x = roa, y = ros)) +
4. geom_point(aes(size = asset_turnover, color = roa), alpha = 0.8) +
5. geom_smooth(method = "lm", se = FALSE, color = "gray40", linetype = "dashed") +
6. geom_text_repel(aes(label = Nam), size = 3, color = "gray25") +
7. scale_x_continuous(labels = label_percent(accuracy = 0.1, decimal.mark = ",")) +
8. scale_y_continuous(labels = label_percent(accuracy = 0.1, decimal.mark = ",")) +
9. scale_color_viridis_c(option = "plasma", begin = 0.1, end = 0.9) +
10. scale_size_continuous(range = c(3, 8),
11. labels = label_number(accuracy = 0.1, big.mark = ".", decimal.mark = ",")) +
12. labs( title = "Quan hệ giữa ROA và ROS của Vinamilk (2015–2024)",
13. subtitle = "Kích thước: Vòng quay tài sản (Asset turnover); màu: mức ROA; đường gạch: xu hướng hồi quy", x = "ROA (Lợi nhuận sau thuế / Tổng tài sản, %)", y = "ROS (Lợi nhuận sau thuế / Doanh thu thuần, %)", size = "Vòng quay tài sản", color = "ROA" ) + theme_minimal(base_size = 12) +
14. theme(legend.position = "right", panel.grid.minor = element_blank())
Giải thích
Kỹ thuật:
- Dòng 1–2: Lọc biến ROA, ROS, asset_turnover; tạo nhãn năm để hiển
thị.
- Dòng 3–5: Vẽ scatter ROA–ROS, thêm kích thước theo asset_turnover và
đường hồi quy tuyến tính rõ ràng.
- Dòng 6–8: Nhãn năm với geom_text_repel tránh chồng; trục định dạng
phần trăm hợp lý.
- Dòng 9–14: Màu plasma và kích thước hợp lý; tiêu đề, phụ đề và theme
rõ, dễ đọc, bố cục cân đối.
Ý nghĩa: Biểu đồ thể hiện mối quan hệ cùng chiều chặt chẽ giữa
ROA và ROS của Vinamilk giai đoạn 2015–2024.
Khi ROA tăng từ khoảng 20% lên hơn 30%, ROS cũng tăng tương ứng từ 15%
lên hơn 25% → cho thấy doanh nghiệp duy trì hiệu quả hoạt động và biên
lợi nhuận ổn định.
Các năm 2017–2019 đạt mức cao nhất, phản ánh giai đoạn sinh lời mạnh và
sử dụng tài sản hiệu quả.
Từ 2022–2024, cả hai chỉ tiêu giảm nhẹ, cho thấy hiệu suất đang chậm lại
nhưng vẫn giữ được tính cân đối và bền vững trong cấu trúc tài
chính.
1. p13 <- vnm20 %>% transmute(Nam = format(nam, "%Y"), ros, asset_turnover, leverage, roe)
2. L0 <- median(p13$leverage, na.rm = TRUE)
3. xseq <- seq(min(p13$asset_turnover, na.rm=TRUE), max(p13$asset_turnover, na.rm=TRUE), length.out = 100)
4. iso <- expand.grid(roe = c(0.25, 0.30, 0.35, 0.40), asset_turnover = xseq) |>
5. mutate(ros = roe / (asset_turnover * L0),
6. nhan = paste0(round(roe*100), "% ROE"))
7. ggplot(p13, aes(asset_turnover, ros)) +
8. geom_point(aes(size = roe, color = leverage), alpha = 0.8) +
9. geom_smooth(method = "lm", se = FALSE, color = "gray40", linetype = "dashed") +
10. geom_path(data = iso, aes(y = ros, group = nhan),
11. color = "gray60", linetype = "dotted") +
12. geom_text_repel(aes(label = Nam), size = 3, color = "gray20") +
13. geom_text(data = iso |> dplyr::group_by(nhan) |> dplyr::slice_tail(n = 1),
14. aes(y = ros, label = nhan), hjust = 1.1, color = "gray40", size = 3) +
15. scale_x_continuous("Vòng quay tài sản (lần)",labels = label_number(accuracy = 0.1, big.mark = ".", decimal.mark = ",")) + scale_y_continuous("ROS (LNST/Doanh thu, %)", labels = label_percent(accuracy = 0.1, big.mark = ".", decimal.mark = ",")) + scale_color_viridis_c(option = "plasma") + labs(title = "Biểu đồ 13. Bản đồ DuPont: ROS × Vòng quay tài sản → ROE (2015–2024)", subtitle = paste0("Đường chấm: các mức ROE tham chiếu (L = ",
16. round(L0, 2), "×)")) + theme_minimal(base_size = 12)
Giải thích
Kỹ thuật:
- Dòng 1–2: Chuẩn bị dữ liệu và lấy L0 = median(leverage); hợp lý cho
bản đồ DuPont khi giả định đòn bẩy cố định (nhớ đảm bảo leverage >
0).
- Dòng 3–6: Tạo lưới xseq và đường đẳng ROE bằng công thức ros =
roe/(asset_turnoverL0); nếu ROE/ROS đang ở dạng %, đổi về tỷ lệ
trước khi tính để tránh sai số/∞.
- Dòng 7–12: Scatter + smooth + nhãn năm rõ; có thể dùng
scale_size_area(max_size=9) và đặt max.overlaps=Inf/point.padding nhỏ
hơn để giảm chồng nhãn.
- Dòng 13–16: Gắn nhãn cuối đường đẳng ROE gọn; định dạng trục % nhất
quán, thêm expand = expansion(mult=c(0,.05)) và
legend.position=“top”/guides(size=guide_legend(order=1),
color=guide_colorbar(order=2)) để bố cục sạch hơn. Ý nghĩa:*
Mối quan hệ ROS–AssetTurnover dương: đường hồi quy dốc lên → khi biên
lợi nhuận cao hoặc quay vòng tài sản nhanh, ROE có xu hướng tốt
hơn.
Giai đoạn cao điểm 2015–2018: điểm các năm này nằm gần/nhỉnh hơn đường
35–40% ROE. Ví dụ 2017 xấp xỉ ROS ~21–22%, vòng quay ~1.5 lần, đòn bẩy
~1.45 ⇒ ROE ≈ 0.21×1.5×1.45 ≈ 46% (báo mức rất cao trên bản đồ).
Sau 2019: nhiều điểm (2020–2024) dịch xuống trái → ROS giảm và vòng quay
chậm lại, đẩy ROE về vùng 25–30% dù đòn bẩy gần như không tăng đáng kể
(màu gần nhau).
1. p14 <- vnm20 %>%
2. transmute(
3. Nam = format(nam, "%Y"),
4. cfo = luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh,
5. profit = loi_nhuan_sau_thue_tndn)
6. ggplot(p14, aes(x = profit, y = cfo)) +
7. geom_point(aes(color = Nam), size = 4, alpha = 0.8) +
8. geom_smooth(method = "lm", se = FALSE, color = "gray40", linetype = "dashed") +
9. geom_text_repel(aes(label = Nam), size = 3, color = "black") +
10. geom_abline(slope = 1, intercept = 0, color = "blue", linetype = "dotted") +
11. geom_segment(aes(x = profit, y = profit, xend = profit, yend = cfo),
12. color = "gray70", linetype = "dotted") +
13. scale_x_continuous("Lợi nhuận sau thuế (tỷ đồng)",
14. labels = label_number(big.mark = ".", decimal.mark = ",")) +
15. scale_y_continuous("Dòng tiền từ HĐKD (tỷ đồng)",
16. labels = label_number(big.mark = ".", decimal.mark = ",")) +
17. labs( title = "Quan hệ giữa Lợi nhuận sau thuế và\n Dòng tiền hoạt động", subtitle = "Đường chấm xanh: CFO = LNST;\n đường gạch xám: xu hướng; màu: theo năm" ) + theme_minimal(base_size = 12) +
18. theme(legend.position = "right", panel.grid.minor = element_blank())
Giải thích
Kỹ thuật: - Dòng 1–5: Chuẩn bị dữ liệu CFO–LNST; bảo đảm cùng
đơn vị (tỷ đồng) và kiểu số (as.numeric nếu cần).
Dòng 6–8: Scatter màu theo năm + xu hướng lm; có thể thử method =
MASS::rlm nếu có outlier.
- Dòng 9–12: Nhãn năm + đường tham chiếu 45° và đoạn chênh lệch; dùng
coord_equal() để đường slope = 1 có ý nghĩa trực quan, thêm max.overlaps
= Inf. - Dòng 13–16: Nhãn trục rõ; cân nhắc scales::label_number_si()
(k, M, B) hoặc thêm expand = expansion(mult = c(0, .05)).
- Dòng 17–18: Tiêu đề/legend gọn; có thể đặt legend.position = “top” và
dùng scale_color_viridis_d() cho bảng màu dễ đọc.
Ý nghĩa: Biểu đồ cho thấy dòng tiền từ hoạt động kinh doanh
(CFO) có mối quan hệ rất chặt chẽ với lợi nhuận sau thuế (LNST) của
Vinamilk.
Phần lớn các điểm nằm gần hoặc trên đường CFO = LNST, chứng tỏ lợi nhuận
được hỗ trợ bởi dòng tiền thật, không phải ghi nhận kế toán.
Đặc biệt, 2018–2020 CFO cao hơn rõ rệt so với LNST → khả năng thu hồi
tiền nhanh, chất lượng lợi nhuận cao.
Các năm 2022–2023 CFO thấp hơn LNST → phản ánh sức tiêu thụ giảm và chu
kỳ thu tiền dài hơn, nhưng không đáng lo.
1. p15 <- vnm20 %>% transmute( Nam = format(nam, "%Y"), roa = roa,
2. cfo = luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh / 1000 )
3. ggplot(p15, aes(x = roa, y = cfo)) +
4. geom_point(aes(color = Nam), size = 4, alpha = 0.8) +
5. geom_smooth(method = "lm", se = FALSE, color = "gray40", linetype = "dashed") +
6. geom_text_repel(aes(label = Nam), size = 3, color = "black") +
7. geom_hline(yintercept = mean(p15$cfo), color = "blue", linetype = "dotted") +
8. geom_vline(xintercept = mean(p15$roa), color = "red", linetype = "dotted") +
9. scale_x_continuous("ROA (Lợi nhuận sau thuế / Tổng tài sản, %)",
10. labels = label_percent(accuracy = 0.1, decimal.mark = ",")) +
11. scale_y_continuous("Dòng tiền từ HĐKD (nghìn tỷ đồng)",
12. labels = label_number(big.mark = ".", decimal.mark = ",")) +
13. labs(title = "Biểu đồ 15. Quan hệ giữa ROA và Dòng tiền hoạt động của Vinamilk\n (2015–2024)", subtitle = "Đường chấm đỏ: ROA TB; chấm xanh: CFO TB; đường gạch: xu hướng hồi quy", color = "Năm" ) + theme_minimal(base_size = 12) +
14. theme(legend.position = "right", panel.grid.minor = element_blank())
Giải thích
Kỹ thuật:
- Dòng 1–2: Tạo dataset gồm năm, ROA và CFO (quy đổi về nghìn tỷ để dễ
đọc).
- Dòng 3–6: Vẽ scatter ROA–CFO, màu theo năm, thêm đường hồi quy và nhãn
năm rõ ràng.
- Dòng 7–8: Thêm hai đường trung bình (đỏ – ROA, xanh – CFO) giúp xác
định vị trí tương đối các năm.
- Dòng 9–14: Trục và nhãn rõ ràng, định dạng hợp lý; theme tối giản giúp
biểu đồ cân đối, dễ so sánh.
Ý nghĩa:
Biểu đồ thể hiện mối quan hệ giữa hiệu quả sinh lời (ROA) và khả năng
tạo tiền thật (CFO) của Vinamilk giai đoạn 2015–2024.
Hầu hết các năm nằm gần trung tâm hai đường trung bình → ROA ổn định,
CFO duy trì vững.
Năm 2019–2020 nổi bật với ROA cao và CFO mạnh, phản ánh chất lượng lợi
nhuận tốt.
Trong khi đó, 2022–2023 cho thấy ROA và CFO cùng giảm, biểu hiện giai
đoạn điều chỉnh nhẹ, nhưng 2024 đã phục hồi.
Tổng thể, Vinamilk vẫn giữ khả năng chuyển hóa lợi nhuận thành tiền mặt
tốt và bền vững.
1. ggplot(vnm20, aes(x = nam, y = debt_ratio)) + geom_point(color = "darkred", size = 3) + geom_line(color = "steelblue", linewidth = 1.2) +
2. geom_smooth(method = "lm", se = FALSE, color = "darkgreen", linetype = "dashed") + geom_text(
3. data = subset(vnm20, debt_ratio == max(debt_ratio, na.rm = TRUE)),
4. aes(label = round(debt_ratio, 2)), vjust = -1, color = "black", size = 4 ) +
5. labs( title = "Tỷ lệ nợ (Debt Ratio) qua các năm",
6. subtitle = "Dữ liệu: Công ty VNM", x = "Năm", y = "Tỷ lệ nợ",
7. caption = "Nguồn: Báo cáo tài chính VNM" ) + theme_minimal(base_size = 14)
Giải thích
Kỹ thuật: - Dòng 1: Thiết lập trục năm–tỷ lệ nợ; dùng điểm và
đường nối thể hiện xu hướng theo thời gian.
- Dòng 2: Thêm đường hồi quy tuyến tính (màu xanh đậm, gạch) để làm nổi
bật xu hướng dài hạn.
- Dòng 3–4: Gắn nhãn giá trị cực đại giúp nhấn mạnh năm có tỷ lệ nợ cao
nhất. - Dòng 5–7: Tiêu đề, phụ đề, chú thích rõ; theme_minimal gọn gàng,
đảm bảo đủ 5 lớp trực quan.
Ý nghĩa:
Biểu đồ cho thấy tỷ lệ nợ của Vinamilk tăng đều qua các năm, từ khoảng
24% năm 2016 lên hơn 34% năm 2024.
Xu hướng tuyến tính (đường xanh lá) khẳng định mức gia tăng ổn định
trong đòn bẩy tài chính, phù hợp với giai đoạn mở rộng quy mô.
Các điểm dữ liệu không lệch nhiều so với đường xu hướng → phản ánh chính
sách nợ ổn định, kiểm soát tốt.
Giá trị cực đại năm 2024 (0.34) là đỉnh tỷ lệ nợ, song vẫn ở mức an toàn
so với chuẩn ngành, cho thấy Vinamilk quản trị vốn cân đối giữa vay nợ
và vốn chủ sở hữu.
1. df_asset <- vnm %>% transmute( Nam = as.numeric(format(as.Date(nam), "%Y")),
2. Tai_san_ngan_han = suppressWarnings(parse_number(as.character(tai_san_ngan_han))),
3. Tai_san_co_dinh_huu_hinh = suppressWarnings(parse_number(as.character(tai_san_co_dinh_huu_hinh))), Tong_tai_san = suppressWarnings(parse_number(as.character(tong_cong_tai_san)))
4. ) %>%
5. pivot_longer(cols = c(Tai_san_ngan_han, Tai_san_co_dinh_huu_hinh, Tong_tai_san), names_to = "Khoan_muc", values_to = "Gia_tri") %>%
6. filter(!is.na(Gia_tri))
7. ggplot(df_asset, aes(x = Nam, y = Gia_tri / 1000, color = Khoan_muc, group = Khoan_muc)) + geom_line(linewidth = 1) + geom_point(size = 3) +
8. geom_text(aes(label = comma(round(Gia_tri / 1000, 0))), vjust = -0.6, size = 3, show.legend = FALSE) +
9. scale_x_continuous(breaks = df_asset$Nam) + # chỉ hiện số năm
10. scale_color_brewer(palette = "Set2") +
11. labs( title = "Cấu trúc tài sản của Vinamilk (2015–2024)", subtitle = "So sánh Tài sản ngắn hạn, Tài sản cố định hữu hình\n và Tổng tài sản (nghìn tỷ đồng)", x = "Năm", y = "Giá trị (nghìn tỷ đồng)", color = "Khoản mục" ) +
12. theme_minimal(base_size = 12)
Giải thích
Kỹ thuật:
- Dòng 1–3: Tạo năm số và parse số tiền an toàn; nếu nam đã là năm
(2015…), bỏ as.Date() và dùng as.integer(nam). Có thể
across(where(is.character), readr::parse_number) gọn hơn.
Dòng 4–6: pivot_longer đúng; nên chuẩn hóa nhãn bằng recode(Khoan_muc=…)
và lọc NA hợp lý.
- Dòng 7–8: Line + point + nhãn số rõ; cân nhắc
scales::label_number_si() hoặc ggrepel để tránh chồng chữ, và
coord_cartesian(clip=“off”).
- Dòng 9–10: breaks = df_asset\(Nam dễ lặp;
dùng breaks = unique(df_asset\)Nam), thêm expand =
expansion(mult=c(0,.02)). Palette “Set2” OK (cần RColorBrewer). - Dòng
11–12: Nhãn/tiêu đề ổn; có thể đặt legend.position=“top” và nêu đơn vị ở
trục (nghìn tỷ) để khỏi chia trong nhiều chỗ. Ý nghĩa:
Tổng tài sản tăng mạnh từ khoảng 25 nghìn tỷ (2015) lên hơn 55 nghìn tỷ
(2024) → phản ánh quy mô doanh nghiệp mở rộng liên tục.
Tài sản ngắn hạn tăng nhanh, chiếm tỷ trọng lớn trong tổng tài sản → cho
thấy khả năng thanh khoản cao và chính sách quản trị vốn lưu động hiệu
quả.
Tài sản cố định hữu hình tăng chậm hơn → hàm ý đầu tư dài hạn được kiểm
soát, tránh dàn trải.
1. df <- vnm20 %>% transmute(Nam = format(nam, "%Y"), debt_ratio, roe)
2. ggplot(df, aes(x = debt_ratio, y = roe)) +
3. geom_point(color = "darkorange", size = 3, alpha = .8) +
4. geom_smooth(method = "lm", se = FALSE, linetype = "dashed") +
5. geom_text_repel(aes(label = Nam), size = 3, max.overlaps = Inf) +#
6. geom_vline(xintercept = mean(df$debt_ratio, na.rm = TRUE), linetype = "dotted") +
7. geom_hline(yintercept = mean(df$roe, na.rm = TRUE), linetype = "dotted") +
8. scale_x_continuous(labels = label_percent(accuracy = 0.1, decimal.mark = ","), name = "Tỷ lệ nợ (Debt ratio)") +
9. scale_y_continuous(labels = label_percent(accuracy = 0.1, decimal.mark = ","), name = "ROE") + labs(title = "Quan hệ giữa tỷ lệ nợ và ROE (2015–2024)",
10. subtitle = "Hai đường trung bình chia 4 vùng hiệu quả") +
11. theme_minimal(base_size = 12)
Giải thích
Kỹ thuật:
- Dòng 1: Chuẩn bị dữ liệu đúng; Nam = format(nam, “%Y”) ổn nếu nam là
date/POSIXct, còn nếu đã là số thì dùng as.character(nam).
- Dòng 2–4: Scatter + geom_smooth(method=“lm”) hợp lý để thấy xu hướng;
màu điểm đặt trực tiếp nên không tạo legend — đúng với mục tiêu
gọn.
- Dòng 5: geom_text_repel(max.overlaps = Inf) cho đủ nhãn; để đỡ chồng,
thêm point.padding = .2, box.padding = .2 (không cần đổi cấu
trúc).
- Dòng 6–7: Dùng mean(df$…) ngay trong geom_vline/hline là đúng (tính
một lần trước khi vẽ, không lặp theo điểm/panel).
- Dòng 8–11: label_percent() chuẩn nếu debt_ratio và roe ở thang 0–1
(trường hợp này phù hợp); tiêu đề/phụ đề rõ, theme_minimal() gọn
gàng.
Ý nghĩa:
Biểu đồ cho thấy mối quan hệ nghịch biến giữa tỷ lệ nợ và ROE của
Vinamilk giai đoạn 2015–2024.
Khi tỷ lệ nợ tăng, ROE có xu hướng giảm, cho thấy hiệu quả sử dụng đòn
bẩy tài chính giảm dần.
Các năm 2015–2017 có nợ thấp, ROE cao → cơ cấu vốn an toàn, lợi nhuận
tốt.
Ngược lại, 2021–2023 ghi nhận nợ cao và ROE giảm mạnh → áp lực tài chính
gia tăng.
1. df <- vnm20 %>%
2. mutate(Nam = as.character(format(nam, "%Y")))
3. ggplot(df, aes(x = ros, y = roa, label = Nam)) +
4. geom_point(color = "tomato", size = 3) +
5. geom_smooth(method = "lm", se = FALSE, linetype = "dashed",
6. color = "blue", linewidth = 1.2) +
7. geom_text_repel(size = 3.5, max.overlaps = 15) +
8. geom_vline(xintercept = mean(df$ros, na.rm = TRUE),
9. linetype = "dotted", color = "gray40") +
10. geom_hline(yintercept = mean(df$roa, na.rm = TRUE),
11. linetype = "dotted", color = "gray40") +
12. labs(title = "Biểu đồ 19. Quan hệ giữa ROA và ROS (2015–2024)",
13. subtitle = "Hai đường trung bình chia 4 vùng hiệu quả sinh lời",
14. x = "ROS (%)", y = "ROA (%)") + theme_minimal()
Giải thích
Kỹ thuật:
- Dòng 1–2: Tạo nhãn năm đúng; nếu nam đã là số thì dùng
as.character(nam) gọn hơn.
- Dòng 3–6: Scatter ROA~ROS + lm gạch xanh rõ xu hướng; ổn.
- Dòng 7–11: text_repel hợp lý; nếu còn chồng, thêm point.padding=.2.
Hai đường mean chia 4 vùng đúng mục tiêu.
- Dòng 12–14: Ghi “(%)” nhưng trục chưa scale; nếu ROA/ROS ở thang 0–1,
thêm scale_*_continuous(labels=label_percent()), còn nếu đã là %, bỏ
“(%)”.
Ý nghĩa:
Biểu đồ thể hiện mối quan hệ thuận chiều giữa tỷ suất sinh lời trên tài
sản (ROA) và tỷ suất lợi nhuận trên doanh thu (ROS) của Vinamilk giai
đoạn 2015–2024.
Khi ROS tăng → ROA cũng tăng, cho thấy hiệu quả quản trị chi phí và khả
năng sinh lời từ doanh thu cao. Giai đoạn 2015–2017 nổi bật với ROS và
ROA cao, phản ánh biên lợi nhuận mạnh và tài sản sử dụng hiệu quả.
Các năm 2021–2023 giảm nhẹ cả ROS và ROA → giai đoạn biên lợi nhuận co
hẹp, hiệu quả sử dụng tài sản suy yếu.
2024 bắt đầu hồi phục nhẹ, hàm ý tín hiệu tích cực trở lại trong hiệu
quả sinh lời.
1. df <- vnm20 %>% transmute( Nam = format(as.Date(nam), "%Y"),
2. Von_chu_so_huu = von_chu_so_huu, No_phai_tra = no_phai_tra ) %>%
3. pivot_longer(-Nam, names_to = "Khoan_muc", values_to = "Gia_tri")
4. ggplot(df, aes(x = Nam, y = Gia_tri, fill = Khoan_muc)) +
5. geom_col(position = "dodge") + geom_text(aes(label = round(Gia_tri, 0)),
6. position = position_dodge(width = 1), hjust = 1, vjust = 0.5, color = "black", size = 3, angle = 90) + geom_hline(yintercept = mean(df$Gia_tri), linetype = "dotted") + scale_fill_brewer(palette = "Set2") +
7. labs(title = " Cấu trúc vốn: Vốn chủ sở hữu và Nợ phải trả (2015–2024)", subtitle = "So sánh quy mô vốn và nợ qua các năm", x = "Năm", y = "Giá trị (tỷ đồng)", fill = "Khoản mục") + theme_minimal(base_size = 12)
Giải thích Kỹ thuật:
-bDòng 1–3: Chuẩn bị dữ liệu đúng; dùng as.character(nam) nếu nam là
số.
-bDòng 4–5: Cột “dodge” và nhãn hiển thị rõ; nên để angle=0 để dễ đọc
hơn.
-bDòng 6: Đường trung bình chung hợp lý; nếu muốn riêng từng nhóm thì
tính theo “Khoan_muc”.
-bDòng 7: Biểu đồ rõ ràng, màu “Set2” hài hòa, theme tối giản dễ
nhìn.
Ý nghĩa: Nợ phải trả luôn chiếm tỷ trọng cao hơn vốn chủ sở
hữu, cho thấy Vinamilk sử dụng đòn bẩy tài chính khá lớn.
Giai đoạn 2015–2018: nợ tăng nhanh, song vốn chủ sở hữu cũng mở rộng
đáng kể → phản ánh giai đoạn tăng trưởng vốn hóa mạnh.
Giai đoạn 2019–2023: quy mô nợ đạt đỉnh, trong khi vốn chủ sở hữu tăng
chậm → có thể là dấu hiệu tái cấu trúc tài chính hoặc ổn định tăng
trưởng.
Năm 2024: nợ giảm nhẹ, vốn chủ sở hữu duy trì ổn định → hàm ý xu hướng
giảm rủi ro tài chính.