library(ggplot2)
library(showtext)
library(sysfonts)
showtext_auto()
font_add(family = "TimesVN", regular = "C:/Windows/Fonts/times.ttf")
theme_set(theme_minimal(base_family = "TimesVN"))

LỜI CẢM ƠN

Trong suốt quá trình thực hiện bài luận môn Ngôn ngữ lập trình, nhóm chúng em xin bày tỏ lòng biết ơn sâu sắc đến thầy Trần Mạnh Tường, người đã tận tình hướng dẫn, cung cấp kiến thức và định hướng cho chúng em trong suốt học kỳ vừa qua. Những bài giảng, góp ý và sự hỗ trợ quý báu của thầy là nền tảng quan trọng giúp chúng em hoàn thành bài luận này một cách hiệu quả và đúng hướng.

Chúng em ý thức rằng bài luận này khó tránh khỏi những thiếu sót nhất định do hạn chế về thời gian, kinh nghiệm và kiến thức thực tiễn. Rất mong nhận được những nhận xét và góp ý chân thành từ thầy để nhóm có thể rút kinh nghiệm, hoàn thiện bản thân và nâng cao hơn nữa năng lực học tập trong tương lai.

Cuối cùng, nhóm xin gửi lời tri ân sâu sắc đến thầy vì đã luôn tận tâm giảng dạy, truyền đạt kiến thức và truyền cảm hứng trong suốt quá trình học tập. Kính chúc thầy sức khỏe dồi dào, tinh thần nhiệt huyết và nhiều thành công trên con đường sự nghiệp của mình.

KẾT CẤU ĐỀ TÀI

Bài tiểu luận bao gồm 2 phần:

Phần I: Phân tích dữ liệu tội phạm tại Los Angeles (Crime Data) giai đoạn 2020 - 2023

Phần II: Phân tích dữ liệu tài chính Công ty Cổ phần Sữa Việt Nam (VNM) giai đoạn 2015 - 2024

PHẦN I: Phân tích dữ liệu tội phạm tại Los Angeles (Crime Data) giai đoạn 2020 - 2023

CHƯƠNG 1: MỞ ĐẦU

1. Lý do chọn đề tài

Trong bối cảnh xã hội hiện đại, tội phạm không chỉ là vấn đề pháp lý mà còn là một hiện tượng xã hội phản ánh nhiều chiều cạnh của đời sống kinh tế, chính trị và văn hóa. Los Angeles – một trong những đô thị lớn nhất và đa dạng nhất của Hoa Kỳ – luôn được xem là “tấm gương phản chiếu” của các biến động xã hội, đặc biệt trong giai đoạn 2020–2023.

Đây là giai đoạn chịu ảnh hưởng nặng nề bởi đại dịch COVID-19, khủng hoảng kinh tế – việc làm, thay đổi chính sách an ninh công cộng và sự phát triển nhanh của công nghệ. Những yếu tố này tác động mạnh đến hành vi con người, mô hình cư trú, cũng như cấu trúc xã hội từ đó làm thay đổi bản chất và xu hướng của tội phạm.

Việc phân tích đa chiều về tội phạm tại Los Angeles không chỉ giúp nhận diện được xu hướng biến động theo thời gian và không gian, mà còn góp phần đánh giá các yếu tố xã hội – kinh tế – chính sách có ảnh hưởng đến tình hình an ninh đô thị. Đề tài này mang tính thực tiễn cao, hỗ trợ cơ quan quản lý, nhà nghiên cứu, và cộng đồng trong việc hoạch định các giải pháp phòng ngừa tội phạm và đảm bảo an ninh xã hội bền vững.

2. Đối tượng và phạm vi nghiên cứu

Đối tượng nghiên cứu:

Tình hình tội phạm tại thành phố Los Angeles, bao gồm các loại hình tội phạm (bạo lực, trộm cắp, xâm hại, ma túy, gian lận,….) và các yếu tố xã hội có ảnh hưởng.

Phạm vi nghiên cứu:

Không gian: Thành phố Los Angeles, bang California, Hoa Kỳ.

Thời gian: Từ năm 2020 đến năm 2023 – giai đoạn có biến động mạnh về kinh tế – xã hội do đại dịch và thay đổi chính sách.

Phạm vi dữ liệu: Bộ dữ liệu Los Angeles Crime Data do LAPD (Los Angeles Police Department) công bố công khai, kết hợp với các nguồn dữ liệu phụ trợ như dân số, thu nhập bình quân, tỷ lệ thất nghiệp và dữ liệu COVID-19.

CHƯƠNG 2: TỔNG QUAN VỀ BỘ DỮ LIỆU

1. Tổng quan về bộ dữ liệu

Bộ dữ liệu “Crime Data from 2020 to Present” được thu thập từ Sở Cảnh sát Los Angeles (LAPD), phản ánh tình hình tội phạm tại thành phố Los Angeles trong giai đoạn từ năm 2020 đến năm 2024.

Dữ liệu được công khai thông qua cổng dữ liệu mở của thành phố, cung cấp thông tin chi tiết về từng vụ án hình sự đã được ghi nhận.

Sau khi kiểm tra và làm sạch, bộ dữ liệu còn lại 760.049 quan sát (dòng dữ liệu), tương ứng với các vụ phạm tội riêng lẻ và 12 biến (cột dữ liệu) chính phục vụ phân tích, bao gồm: date, area, district, severity, crime_type, age, sex, premise_type, status, lalat, lon, year.

Mỗi dòng trong bộ dữ liệu thể hiện một sự kiện phạm tội cụ thể bao gồm thông tin về loại tội phạm, khu vực xảy ra, thời gian, giới tính và độ tuổi của nạn nhân, cùng với toạ độ địa lý của vụ án.

2. Giới thiệu về bộ dữ liệu

2.1. Đọc dữ liệu từ Excel

library(readxl)
df <- read_excel("C:/Users/USER/Downloads/Crime_Data_from_2020_to_Present.xlsx")
Giải thích kỹ thuật:

library(readxl): nạp gói readxl – một thư viện trong R dùng để đọc dữ liệu từ file Excel (.xls hoặc .xlsx).

read_excel("..."): đọc toàn bộ dữ liệu từ đường dẫn được chỉ định và lưu vào đối tượng df.

Hàm này tự động nhận diện kiểu dữ liệu của từng cột (số, ký tự, ngày,...).

Kết quả là một bảng dữ liệu (data frame) chứa toàn bộ thông tin về các vụ phạm tội tại Los Angeles.

2.2. Quan sát sơ bộ

head(df)
tail(df)
Giải thích kỹ thuật:

head(df) hiển thị 6 dòng đầu tiên trong bộ dữ liệu df.

tail(df) hiển thị 6 dòng cuối cùng trong cùng bộ dữ liệu.

Hai hàm này giúp ta xem nhanh cấu trúc dữ liệu — gồm các biến có trong tập dữ liệu, tên cột, kiểu dữ liệu (số, ký tự, ngày tháng) và cách dữ liệu được lưu trữ. Đây là bước cơ bản để đảm bảo dữ liệu đã được đọc đúng định dạng.

2.3. Kích thước dữ liệu

nrow(df)   # Số dòng (vụ án)
## [1] 944235
ncol(df)   # Số cột (biến)
## [1] 28
Giải thích kỹ thuật:

nrow(df) trả về số lượng dòng trong bộ dữ liệu df, tức là tổng số vụ án được ghi nhận.

ncol(df) trả về số lượng cột, tức là tổng số biến mô tả thông tin của mỗi vụ án.

Hai hàm này giúp ta nắm quy mô dữ liệu, biết được số quan sát (records) và số đặc trưng (variables). Đây là thông tin quan trọng trước khi thực hiện các bước làm sạch hoặc phân tích, nhằm xác định khối lượng xử lý và khả năng lưu trữ.

Ý nghĩa thống kê:

Kết quả cho thấy bộ dữ liệu ghi nhận 944.235 vụ án với 28 đặc trưng mô tả cho mỗi vụ (chẳng hạn như thời gian, địa điểm, loại tội phạm, độ tuổi, giới tính,…).

2.4. Danh sách biến

names(df)
##  [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:

Lệnh names(df) giúp liệt kê toàn bộ tên các biến (cột) trong bộ dữ liệu.

2.5. Cấu trúc dữ liệu

str(df)
## tibble [944,235 × 28] (S3: tbl_df/tbl/data.frame)
##  $ DR_NO         : num [1:944235] 1.90e+08 2.00e+08 2.00e+08 2.01e+08 2.21e+08 ...
##  $ Date Rptd     : POSIXct[1:944235], format: "2020-01-03" "2020-09-02" ...
##  $ DATE OCC      : POSIXct[1:944235], format: "2020-01-03" "2020-08-02" ...
##  $ TIME OCC      : num [1:944235] 2130 1800 1700 2037 1200 ...
##  $ AREA          : num [1:944235] 7 1 3 9 6 18 1 3 13 19 ...
##  $ AREA NAME     : chr [1:944235] "Wilshire" "Central" "Southwest" "Van Nuys" ...
##  $ Rpt Dist No   : num [1:944235] 784 182 356 964 666 ...
##  $ Part 1-2      : num [1:944235] 1 1 1 1 2 2 2 2 2 2 ...
##  $ Crm Cd        : num [1:944235] 510 330 480 343 354 354 354 354 354 624 ...
##  $ Crm Cd Desc   : chr [1:944235] "VEHICLE - STOLEN" "BURGLARY FROM VEHICLE" "BIKE - STOLEN" "SHOPLIFTING-GRAND THEFT ($950.01 & OVER)" ...
##  $ Mocodes       : chr [1:944235] NA "1822 1402 0344" "0344 1251" "0325 1501" ...
##  $ Vict Age      : num [1:944235] 0 47 19 19 28 41 25 27 24 26 ...
##  $ Vict Sex      : chr [1:944235] "M" "M" "X" "M" ...
##  $ Vict Descent  : chr [1:944235] "O" "O" "X" "O" ...
##  $ Premis Cd     : num [1:944235] 101 128 502 405 102 501 502 248 750 502 ...
##  $ Premis Desc   : chr [1:944235] "STREET" "BUS STOP/LAYOVER (ALSO QUERY 124)" "MULTI-UNIT DWELLING (APARTMENT, DUPLEX, ETC)" "CLOTHING STORE" ...
##  $ Weapon Used Cd: num [1:944235] NA NA NA NA NA NA NA NA NA 400 ...
##  $ Weapon Desc   : chr [1:944235] NA NA NA NA ...
##  $ Status        : chr [1:944235] "AA" "IC" "IC" "IC" ...
##  $ Status Desc   : chr [1:944235] "Adult Arrest" "Invest Cont" "Invest Cont" "Invest Cont" ...
##  $ Crm Cd 1      : num [1:944235] 510 330 480 343 354 354 354 354 354 624 ...
##  $ Crm Cd 2      : num [1:944235] 998 998 NA NA NA NA NA NA NA NA ...
##  $ Crm Cd 3      : num [1:944235] NA NA NA NA NA NA NA NA NA NA ...
##  $ Crm Cd 4      : logi [1:944235] NA NA NA NA NA NA ...
##  $ LOCATION      : chr [1:944235] "1900 S  LONGWOOD                     AV" "1000 S  FLOWER                       ST" "1400 W  37TH                         ST" "14000    RIVERSIDE                    DR" ...
##  $ Cross Street  : chr [1:944235] NA NA NA NA ...
##  $ LAT           : chr [1:944235] "340375" "340444" "34021" "341576" ...
##  $ LON           : chr [1:944235] "-1183506" "-1182628" "-1183002" "-1184387" ...
Giải thích kỹ thuật:

Hàm str() hiển thị cấu trúc chi tiết của từng biến, bao gồm tên, kiểu dữ liệu, và một vài giá trị mẫu.

2.6. Kiểu dữ liệu từng biến

sapply(df, class)
## $DR_NO
## [1] "numeric"
## 
## $`Date Rptd`
## [1] "POSIXct" "POSIXt" 
## 
## $`DATE OCC`
## [1] "POSIXct" "POSIXt" 
## 
## $`TIME OCC`
## [1] "numeric"
## 
## $AREA
## [1] "numeric"
## 
## $`AREA NAME`
## [1] "character"
## 
## $`Rpt Dist No`
## [1] "numeric"
## 
## $`Part 1-2`
## [1] "numeric"
## 
## $`Crm Cd`
## [1] "numeric"
## 
## $`Crm Cd Desc`
## [1] "character"
## 
## $Mocodes
## [1] "character"
## 
## $`Vict Age`
## [1] "numeric"
## 
## $`Vict Sex`
## [1] "character"
## 
## $`Vict Descent`
## [1] "character"
## 
## $`Premis Cd`
## [1] "numeric"
## 
## $`Premis Desc`
## [1] "character"
## 
## $`Weapon Used Cd`
## [1] "numeric"
## 
## $`Weapon Desc`
## [1] "character"
## 
## $Status
## [1] "character"
## 
## $`Status Desc`
## [1] "character"
## 
## $`Crm Cd 1`
## [1] "numeric"
## 
## $`Crm Cd 2`
## [1] "numeric"
## 
## $`Crm Cd 3`
## [1] "numeric"
## 
## $`Crm Cd 4`
## [1] "logical"
## 
## $LOCATION
## [1] "character"
## 
## $`Cross Street`
## [1] "character"
## 
## $LAT
## [1] "character"
## 
## $LON
## [1] "character"
Giải thích kỹ thuật:

Hàm sapply(df, class) trả về kiểu dữ liệu của từng cột trong data frame.

Kết quả cho biết biến nào thuộc dạng numeric, character, Date, hay factor.

Ý nghĩa thống kê:

Dựa trên kết quả kiểm tra kiểu dữ liệu, ta có thể phân loại các biến trong bộ dữ liệu như sau:

  • Biến định lượng: TIME OCC, Vict Age, AREA, Crm Cd, Crm Cd 1, Crm Cd 2, Crm Cd 3, Premis Cd, Weapon Used Cd.

  • Biến định tính: AREA NAME, Crm Cd Desc, Vict Sex, Vict Descent, Premis Desc, Weapon Desc, Status, Status Desc, LOCATION, Cross Street.

  • Biến thời gian: Date Rptd, DATE OCC.

  • Biến vị trí địa lý: LAT, LON.

  • Biến logic: Crm Cd 4.

2.7. Chọn biến phân tích

Từ bộ dữ liệu gốc, chúng em sẽ tiến hành lựa chọn và đổi tên 11 biến quan trọng cần cho bài nghiên cứu. Các biến được đặt lại tên ngắn gọn, dễ hiểu.

library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
crime_data <- df %>%
  select(`DATE OCC`,`AREA NAME`,`Part 1-2`,`Crm Cd Desc`,`Vict Age`,`Vict Sex`,`Weapon Desc`, `Status Desc`,`LAT`,`LON`) %>%
  rename(
    date = `DATE OCC`,
    area = `AREA NAME`,
    severity = `Part 1-2`,
    crime_type = `Crm Cd Desc`,
    age = `Vict Age`,
    sex = `Vict Sex`,
    weapon = `Weapon Desc`,
    status = `Status Desc`,
    lat = `LAT`,
    lon = `LON`
  )
Giải thích kỹ thuật:

Hàm select() trong dplyr được dùng để lựa chọn các biến cần thiết từ bộ dữ liệu ban đầu.

Hàm rename() giúp đổi tên các biến sang dạng ngắn gọn, thống nhất với quy tắc đặt tên.

Kết quả thu được là một bảng dữ liệu nhỏ gọn hơn, chỉ chứa 11 biến quan trọng nhất.

Ý nghĩa thống kê:

Các biến được giữ lại bao gồm:

date: thời điểm xảy ra vụ án (biến thời gian).

area: khu vực xảy ra vụ án (biến định tính).

severity: mức độ nghiêm trọng của vụ án (định tính thứ bậc).

crime_type: mô tả loại tội phạm (định tính).

age: tuổi nạn nhân (định lượng liên tục).

sex: giới tính nạn nhân (định tính).

weapon: loại vũ khí được sử dụng.

status: tình trạng xử lý vụ án (định tính).

lat, lon: tọa độ địa lý nơi xảy ra vụ án (biến định lượng).

2.8. Kiểm tra dữ liệu bị thiếu

colSums(is.na(crime_data)) 
##       date       area   severity crime_type        age        sex     weapon 
##          0          0          0          0          0     126595     619758 
##     status        lat        lon 
##          0          0          0
sum(is.na(crime_data)) 
## [1] 746353
Giải thích kỹ thuật:

Hàm is.na(): kiểm tra giá trị bị thiếu (NA) trong từng ô dữ liệu, trả về TRUE/FALSE

Hàm colSums(): tính tổng số TRUE (tức là số NA) theo từng cột

=> colSums(is.na(crime_data)) cho biết mỗi biến có bao nhiêu giá trị bị thiếu

Hàm sum(): tính tổng tất cả các giá trị TRUE trong toàn bộ bảng

=> sum(is.na(crime_data)) cho biết tổng số giá trị thiếu trong toàn bộ dataset

Ý nghĩa thống kê:

Biến sex có 126.595 và biến weapon có 619.758 dữ liệu bị thiếu.

Các biến còn lại không có dữ liệu bị thiếu.

Tổng cộng có 746.353 ô dữ liệu bị trống.

2.9. Kiểm tra dữ liệu bị trùng lặp

sum(duplicated(crime_data))
## [1] 11098
Giải thích kỹ thuật:

Hàm duplicated(): xác định các dòng bị trùng lặp trong bộ dữ liệu, trả về TRUE/FALSE

Hàm sum(): tính tổng số dòng TRUE, tức là tổng số dòng bị trùng

=> sum(duplicated(crime_data)) cho biết có bao nhiêu quan sát trùng lặp trong dữ liệu

Ý nghĩa thống kê:

Kết quả cho thấy 11.098 quan sát bị trùng lặp.

Trong bộ dữ liệu, mỗi dòng (một quan sát) không nhất thiết tương ứng với một vụ án duy nhất, có thể có nhiều nạn nhân trong cùng một sự việc, các biến như date, area, crime_type… sẽ giống nhau, khiến hệ thống nhận diện là trùng lặp về mặt kỹ thuật, dù thực tế chúng phản ánh các đối tượng khác nhau trong cùng vụ án.

Nếu loại bỏ các bản ghi này, dữ liệu sẽ mất thông tin về số lượng nạn nhân, giới tính hoặc độ tuổi — những yếu tố quan trọng trong phân tích hành vi tội phạm.

Vì vậy, nhóm giữ nguyên toàn bộ các bản ghi trùng lặp để đảm bảo tính toàn vẹn, đầy đủ và chính xác của bộ dữ liệu, đặc biệt cho các phân tích liên quan đến nhân khẩu học của nạn nhân.

CHƯƠNG 3: XỬ LÝ DỮ LIỆU

1. Làm sạch dữ liệu

crime <- crime_data %>%
  filter(!if_any(-weapon, is.na))
Giải thích kỹ thuật:

-weapon — tidyselect: chọn tất cả các cột trừ cột weapon.

if_any(-weapon, is.na): áp dụng is.na cho mỗi cột được chọn, trả về một logical vector theo hàng, TRUE nếu ít nhất một cột trong hàng đó là NA. !if_any(...): phủ định, TRUE khi không có NA trong tất cả các cột được chọn (tức là hàng hợp lệ).

filter(...): giữ lại các hàng với điều kiện TRUE

Biến crime được tạo mới từ crime_data sau khi đã loại bỏ các dòng có dữ liệu thiếu (trừ biến weapon), đảm bảo bộ dữ liệu đầu vào cho các bước phân tích sau đầy đủ và không bị gián đoạn bởi NA.

Ý Nghĩa thống kê:

Các giá trị NA trong biến weapon không phải là lỗi nhập liệu, mà phản ánh bản chất của dữ liệu thực tế: Nhiều vụ án không có vũ khí được sử dụng, chẳng hạn như trộm cắp, lừa đảo hoặc các hành vi phạm tội không liên quan đến bạo lực.

Trong các trường hợp này, biến weapon được để trống (NA) để biểu thị rằng không có vũ khí được ghi nhận. Vì vậy, các giá trị NA ở biến weapon có ý nghĩa thực tế, tương đương với việc “không sử dụng vũ khí”. Do đó, em không loại bỏ các dòng có NA ở biến weapon, để tránh làm sai lệch bản chất của dữ liệu gốc và giữ nguyên thông tin thống kê về tần suất sử dụng vũ khí trong tội phạm.

2. Chuẩn hóa thời gian

crime$date <- as.Date(crime$date)
class(crime$date)
## [1] "Date"
Giải thích kỹ thuật:

Hàm as.Date() dùng để chuyển đổi dữ liệu thời gian từ dạng ký tự hoặc dạng thời gian phức hợp (như POSIXct/POSIXt) sang định dạng ngày (Date) trong R.

Lệnh class(crime$date) giúp kiểm tra kiểu dữ liệu của biến date sau khi chuyển đổi, đảm bảo rằng biến này đã được nhận diện đúng là kiểu Date.

3. Tạo thêm biến year từ cột date

crime$year <- format(as.Date(crime$date), "%Y")
Giải thích kỹ thuật:

Hàm format() được dùng để trích xuất thông tin cụ thể từ biến ngày tháng, ở đây là năm ("%Y").

Lệnh as.Date(crime$date) đảm bảo rằng biến date được định dạng đúng kiểu ngày trước khi trích xuất.

Sau câu lệnh này, cột mới tên là year sẽ được tạo ra, lưu trữ năm xảy ra vụ án dưới dạng ký tự (character).

4. Lọc dữ liệu nghiên cứu

crime <- crime %>%
  filter(age >= 0, year >= 2020 , year <= 2023) 
Giải thích kỹ thuật:

%>% là toán tử pipe trong dplyr, giúp nối các thao tác xử lý dữ liệu một cách trực quan.

filter() là hàm dùng để lọc các quan sát theo điều kiện nhất định.

Ở đây: age >= 0: loại bỏ các giá trị tuổi âm, đảm bảo tuổi của nạn nhân hợp lệ. year >= 2020 & year <= 2023: giữ lại các quan sát xảy ra trong giai đoạn nghiên cứu (2020–2023).

5. Kiểm tra cấu trúc sau làm sạch

str(crime)
## tibble [760,572 × 11] (S3: tbl_df/tbl/data.frame)
##  $ date      : Date[1:760572], format: "2020-01-03" "2020-08-02" ...
##  $ area      : chr [1:760572] "Wilshire" "Central" "Southwest" "Van Nuys" ...
##  $ severity  : num [1:760572] 1 1 1 1 2 2 2 2 2 2 ...
##  $ crime_type: chr [1:760572] "VEHICLE - STOLEN" "BURGLARY FROM VEHICLE" "BIKE - STOLEN" "SHOPLIFTING-GRAND THEFT ($950.01 & OVER)" ...
##  $ age       : num [1:760572] 0 47 19 19 28 41 25 27 24 26 ...
##  $ sex       : chr [1:760572] "M" "M" "X" "M" ...
##  $ weapon    : chr [1:760572] NA NA NA NA ...
##  $ status    : chr [1:760572] "Adult Arrest" "Invest Cont" "Invest Cont" "Invest Cont" ...
##  $ lat       : chr [1:760572] "340375" "340444" "34021" "341576" ...
##  $ lon       : chr [1:760572] "-1183506" "-1182628" "-1183002" "-1184387" ...
##  $ year      : chr [1:760572] "2020" "2020" "2020" "2020" ...
Giải thích kỹ thuật:

str() là hàm cơ bản trong R dùng để hiển thị cấu trúc tổng quan của dữ liệu: bao gồm số dòng, số cột, tên biến, kiểu dữ liệu từng cột và một số giá trị đầu tiên.

Ý nghĩa thống kê:

Sau quá trình làm sạch, bộ dữ liệu “Crime Data from 2020 to Present” thu thập từ Sở Cảnh sát Los Angeles (LAPD) còn lại 760.572 quan sát và 11 biến.

6. Điều chỉnh tọa độ sai lệch

crime <- crime %>%
  mutate(
    lat = as.numeric(lat),
    lon = as.numeric(lon)
  ) %>%
  mutate(
    lat = ifelse(lat > 100, lat / 10, lat),
    lon = ifelse(lon > -200, lon * 100, lon)
  )
Giải thích kỹ thuật:

mutate() là hàm trong dplyr dùng để tạo mới hoặc chỉnh sửa các biến.

as.numeric(lat) và as.numeric(lon) chuyển các cột lat (vĩ độ) và lon (kinh độ) từ kiểu ký tự (character) sang kiểu số (numeric) để thực hiện các phép toán.

ifelse(lat > 100, lat / 10, lat): nếu giá trị vĩ độ lớn hơn 100 (không hợp lý cho Los Angeles, vì LA nằm khoảng 33–34 độ vĩ bắc), sẽ chia cho 10 để chuẩn hóa về đúng giá trị thực.

ifelse(lon > -200, lon * 100, lon): nếu giá trị kinh độ lớn hơn -200 (nhận dạng các giá trị bị sai định dạng âm hoặc thiếu dấu), nhân 100 để điều chỉnh về tọa độ hợp lý.

Ý nghĩa thống kê:

Dữ liệu ban đầu có một số giá trị lat và lon bị sai lệch do lỗi nhập liệu hoặc định dạng, nằm ngoài phạm vi thực tế của Los Angeles (LAT ≈ 33.7–34.4, LON ≈ -118.6 đến -118.1). Các giá trị này bị lệch một bậc thập phân nên được hiệu chỉnh để đảm bảo tọa độ chính xác trên bản đồ.

Việc chuẩn hóa các tọa độ giúp các thao tác trực quan hóa không gặp lỗi, đảm bảo bản đồ tội phạm hiển thị đúng vị trí các vụ án.

7. Gom nhóm vũ khí

unique(crime$weapon)
##  [1] NA                                              
##  [2] "STRONG-ARM (HANDS, FIST, FEET OR BODILY FORCE)"
##  [3] "VEHICLE"                                       
##  [4] "UNKNOWN WEAPON/OTHER WEAPON"                   
##  [5] "VERBAL THREAT"                                 
##  [6] "BELT FLAILING INSTRUMENT/CHAIN"                
##  [7] "HAND GUN"                                      
##  [8] "UNKNOWN FIREARM"                               
##  [9] "KNIFE WITH BLADE 6INCHES OR LESS"              
## [10] "FIXED OBJECT"                                  
## [11] "KITCHEN KNIFE"                                 
## [12] "MACHETE"                                       
## [13] "MACE/PEPPER SPRAY"                             
## [14] "CLUB/BAT"                                      
## [15] "STICK"                                         
## [16] "OTHER KNIFE"                                   
## [17] "PHYSICAL PRESENCE"                             
## [18] "KNIFE WITH BLADE OVER 6 INCHES IN LENGTH"      
## [19] "HAMMER"                                        
## [20] "AIR PISTOL/REVOLVER/RIFLE/BB GUN"              
## [21] "SEMI-AUTOMATIC PISTOL"                         
## [22] "SIMULATED GUN"                                 
## [23] "RAZOR"                                         
## [24] "OTHER FIREARM"                                 
## [25] "FOLDING KNIFE"                                 
## [26] "PIPE/METAL PIPE"                               
## [27] "ROCK/THROWN OBJECT"                            
## [28] "RIFLE"                                         
## [29] "OTHER CUTTING INSTRUMENT"                      
## [30] "FIRE"                                          
## [31] "REVOLVER"                                      
## [32] "BOTTLE"                                        
## [33] "SCISSORS"                                      
## [34] "SWITCH BLADE"                                  
## [35] "BRASS KNUCKLES"                                
## [36] "UNKNOWN TYPE CUTTING INSTRUMENT"               
## [37] "BLUNT INSTRUMENT"                              
## [38] "BOARD"                                         
## [39] "STUN GUN"                                      
## [40] "CLEAVER"                                       
## [41] "RAZOR BLADE"                                   
## [42] "SCREWDRIVER"                                   
## [43] "SHOTGUN"                                       
## [44] "CONCRETE BLOCK/BRICK"                          
## [45] "CAUSTIC CHEMICAL/POISON"                       
## [46] "SEMI-AUTOMATIC RIFLE"                          
## [47] "SCALDING LIQUID"                               
## [48] "TIRE IRON"                                     
## [49] "BOWIE KNIFE"                                   
## [50] "GLASS"                                         
## [51] "AXE"                                           
## [52] "TOY GUN"                                       
## [53] "BOMB THREAT"                                   
## [54] "SAWED OFF RIFLE/SHOTGUN"                       
## [55] "MARTIAL ARTS WEAPONS"                          
## [56] "DEMAND NOTE"                                   
## [57] "DIRK/DAGGER"                                   
## [58] "ASSAULT WEAPON/UZI/AK47/ETC"                   
## [59] "ROPE/LIGATURE"                                 
## [60] "HECKLER & KOCH 93 SEMIAUTOMATIC ASSAULT RIFLE" 
## [61] "EXPLOXIVE DEVICE"                              
## [62] "SWORD"                                         
## [63] "MAC-11 SEMIAUTOMATIC ASSAULT WEAPON"           
## [64] "SYRINGE"                                       
## [65] "BOW AND ARROW"                                 
## [66] "LIQUOR/DRUGS"                                  
## [67] "DOG/ANIMAL (SIC ANIMAL ON)"                    
## [68] "BLACKJACK"                                     
## [69] "ICE PICK"                                      
## [70] "RELIC FIREARM"                                 
## [71] "AUTOMATIC WEAPON/SUB-MACHINE GUN"              
## [72] "ANTIQUE FIREARM"                               
## [73] "HECKLER & KOCH 91 SEMIAUTOMATIC ASSAULT RIFLE" 
## [74] "STRAIGHT RAZOR"                                
## [75] "M1-1 SEMIAUTOMATIC ASSAULT RIFLE"              
## [76] "STARTER PISTOL/REVOLVER"                       
## [77] "UZI SEMIAUTOMATIC ASSAULT RIFLE"               
## [78] "MAC-10 SEMIAUTOMATIC ASSAULT WEAPON"           
## [79] "UNK TYPE SEMIAUTOMATIC ASSAULT RIFLE"          
## [80] "M-14 SEMIAUTOMATIC ASSAULT RIFLE"
library(dplyr)
library(stringr)
crime <- crime %>%
  mutate(
    weapon_group = case_when(
      str_detect(weapon, "GUN|RIFLE|PISTOL|REVOLVER|FIREARM|SHOTGUN|UZI|MAC|ASSAULT WEAPON|AUTOMATIC") ~ "Firearm",
      str_detect(weapon, "KNIFE|BLADE|MACHETE|RAZOR|DAGGER|SWORD|ICE PICK|DIRK|CLEAVER") ~ "Sharp object",
      str_detect(weapon, "BAT|CLUB|PIPE|STICK|BOARD|HAMMER|BRICK|ROCK|TIRE IRON|AXE|BLUNT") ~ "Blunt object",
      str_detect(weapon, "BOMB|EXPLOSIVE|FIRE|CHEMICAL|POISON|SCALDING|ACID") ~ "Chemical/Explosive",
      str_detect(weapon, "STUN GUN|MACE|PEPPER|GAS") ~ "Support weapon",
      str_detect(weapon, "ROPE|LIGATURE|DOG|ANIMAL|SYRINGE|LIQUOR|DRUG") ~ "Control tool",
      str_detect(weapon, "STRONG-ARM|BODILY FORCE|VERBAL|PHYSICAL PRESENCE|DEMAND NOTE|NONE|UNKNOWN") ~ "Bare-handed attack",
      is.na(weapon) ~ "No weapon",
      TRUE ~ "Other"
    )
  )
Giái thích kỹ thuật:

unique(crime$weapon): In ra danh sách các giá trị khác nhau hiện có trong cột weapon.

crime <- crime %>% mutate(...): Tạo các cột mới (không thay đổi cột gốc), lưu lại kết quả trở lại crime.

weapon_group = case_when(...)

- case_when(): đánh giá các điều kiện theo thứ tự từ trên xuống; biểu thức trả giá trị đầu tiên đúng.

— Các dòng str_detect(...) ~ "..." gom nhiều từ khóa tương ứng một nhóm logic.

— is.na(weaponn) ~ "Không có vũ khí": bắt các ô thực sự trống (NA).

— TRUE ~ "Khác": giá trị mặc định nếu không khớp bất kỳ điều kiện nào trước đó.

8. Phân nhóm độ tuổi nạn nhân

crime <- crime %>%
  mutate(age_group = case_when(
    age < 18 ~ "Juvenile",
    age >= 18 & age < 40 ~ "Young adult",
    age >= 40 & age < 60 ~ "Middle-aged",
    age >= 60 ~ "Elderly",
    TRUE ~ NA_character_
  ))
Giải thích kỹ thuật:

mutate() (dplyr) dùng để tạo biến mới hoặc chỉnh sửa biến hiện có.

case_when() cho phép tạo nhiều điều kiện logic: mỗi điều kiện đi kèm với nhãn kết quả tương ứng.

Trong phần này:

age < 18 ~ "Juvenile"(Vị thành niên): nếu tuổi nhỏ hơn 18, gán nhãn "Vị thành niên".

age >= 18 & age < 40 ~ "Young adult"(Thanh niên): tuổi từ 18 đến dưới 40 là "Thanh niên".

age >= 40 & age < 60 ~ "Middle-aged"(Trung niên): tuổi từ 40 đến dưới 60 là "Trung niên".

age >= 60 ~ "Elderly"(Cao tuổi): tuổi từ 60 trở lên là "Cao tuổi".

TRUE ~ NA_character_: tất cả các giá trị khác (không xác định) sẽ gán NA.

9. Chuẩn hóa dữ liệu biến mức độ phạm tội

crime <- crime %>%
  mutate(severity_label = ifelse(severity == 1, "Severe", "Minor"))
Giải thích kỹ thuật:

mutate() (dplyr) được dùng để tạo biến mới hoặc cập nhật biến hiện có.

ifelse(condition, value_if_true, value_if_false) là hàm điều kiện cơ bản trong R:

condition: severity == 1 nghĩa là nếu mức độ tội phạm = 1.

value_if_true: gán nhãn "Severe" (nghiêm trọng) nếu điều kiện đúng.

value_if_false: gán nhãn "Minor" (nhẹ) nếu điều kiện sai (tức mức độ = 2)

10. Chuẩn hóa giới tính nạn nhân

crime <- crime %>%
  mutate(sex = toupper(sex))
crime <- crime %>%
  mutate(
    sex = case_when(
      sex == "M" ~ "Male",
      sex == "F" ~ "Female",
      TRUE ~ "Unknown"
    )
  )
Giải thích kỹ thuật:

toupper(sex): Chuyển toàn bộ giá trị trong cột sex thành chữ hoa, để đồng nhất hóa dữ liệu ("m", "M" → "M"; "f", "F" → "F").

case_when(): Dùng để gán nhãn định tính dễ hiểu cho dữ liệu đã đồng nhất: "M" → "Male", "F" → "Female". Các giá trị khác (không xác định, trống, hoặc bất thường) → "Unknown".

11. Tạo biến mới xác định tình trạng bắt giữ

crime <- crime %>%
  mutate(arrested = ifelse(status %in% c("Adult Arrest", "Juv Arrest"), "Arrested", "Not arrested"))
Giải thích kỹ thuật:

mutate() (dplyr) được dùng để tạo biến mới hoặc cập nhật biến hiện có.

ifelse(condition, value_if_true, value_if_false):

condition: status %in% c("Adult Arrest", "Juv Arrest") nghĩa là kiểm tra xem giá trị trong cột status có nằm trong danh sách "Adult Arrest" (người lớn bị bắt) hoặc "Juv Arrest" (vị thành niên bị bắt) hay không.

value_if_true: nếu đúng, gán nhãn "Arrested"(đã bắt giữ).

value_if_false: nếu sai, gán nhãn "Not arrested"(chưa bắt giữ).

12. Phân nhóm loại tội phạm

unique(crime$crime_type)
##   [1] "VEHICLE - STOLEN"                                        
##   [2] "BURGLARY FROM VEHICLE"                                   
##   [3] "BIKE - STOLEN"                                           
##   [4] "SHOPLIFTING-GRAND THEFT ($950.01 & OVER)"                
##   [5] "THEFT OF IDENTITY"                                       
##   [6] "BATTERY - SIMPLE ASSAULT"                                
##   [7] "SODOMY/SEXUAL CONTACT B/W PENIS OF ONE PERS TO ANUS OTH" 
##   [8] "CRM AGNST CHLD (13 OR UNDER) (14-15 & SUSP 10 YRS OLDER)"
##   [9] "SEX,UNLAWFUL(INC MUTUAL CONSENT, PENETRATION W/ FRGN OBJ"
##  [10] "ASSAULT WITH DEADLY WEAPON, AGGRAVATED ASSAULT"          
##  [11] "LETTERS, LEWD  -  TELEPHONE CALLS, LEWD"                 
##  [12] "THEFT-GRAND ($950.01 & OVER)EXCPT,GUNS,FOWL,LIVESTK,PROD"
##  [13] "CRIMINAL THREATS - NO WEAPON DISPLAYED"                  
##  [14] "CHILD ANNOYING (17YRS & UNDER)"                          
##  [15] "CONTEMPT OF COURT"                                       
##  [16] "THEFT PLAIN - PETTY ($950 & UNDER)"                      
##  [17] "INTIMATE PARTNER - SIMPLE ASSAULT"                       
##  [18] "LEWD CONDUCT"                                            
##  [19] "THEFT PLAIN - ATTEMPT"                                   
##  [20] "BURGLARY"                                                
##  [21] "THEFT FROM MOTOR VEHICLE - GRAND ($950.01 AND OVER)"     
##  [22] "ROBBERY"                                                 
##  [23] "BUNCO, GRAND THEFT"                                      
##  [24] "BATTERY WITH SEXUAL CONTACT"                             
##  [25] "INTIMATE PARTNER - AGGRAVATED ASSAULT"                   
##  [26] "ORAL COPULATION"                                         
##  [27] "UNAUTHORIZED COMPUTER ACCESS"                            
##  [28] "VIOLATION OF RESTRAINING ORDER"                          
##  [29] "SHOPLIFTING - PETTY THEFT ($950 & UNDER)"                
##  [30] "VANDALISM - FELONY ($400 & OVER, ALL CHURCH VANDALISMS)" 
##  [31] "BRANDISH WEAPON"                                         
##  [32] "DOCUMENT FORGERY / STOLEN FELONY"                        
##  [33] "SEX OFFENDER REGISTRANT OUT OF COMPLIANCE"               
##  [34] "EMBEZZLEMENT, GRAND THEFT ($950.01 & OVER)"              
##  [35] "RAPE, FORCIBLE"                                          
##  [36] "VANDALISM - MISDEAMEANOR ($399 OR UNDER)"                
##  [37] "OTHER MISCELLANEOUS CRIME"                               
##  [38] "CHILD ABUSE (PHYSICAL) - SIMPLE ASSAULT"                 
##  [39] "CREDIT CARDS, FRAUD USE ($950.01 & OVER)"                
##  [40] "THREATENING PHONE CALLS/LETTERS"                         
##  [41] "SEXUAL PENETRATION W/FOREIGN OBJECT"                     
##  [42] "EXTORTION"                                               
##  [43] "OTHER ASSAULT"                                           
##  [44] "PICKPOCKET"                                              
##  [45] "ARSON"                                                   
##  [46] "DISTURBING THE PEACE"                                    
##  [47] "BUNCO, ATTEMPT"                                          
##  [48] "HUMAN TRAFFICKING - INVOLUNTARY SERVITUDE"               
##  [49] "PEEPING TOM"                                             
##  [50] "VIOLATION OF COURT ORDER"                                
##  [51] "FALSE POLICE REPORT"                                     
##  [52] "CONTRIBUTING"                                            
##  [53] "FALSE IMPRISONMENT"                                      
##  [54] "THEFT FROM MOTOR VEHICLE - PETTY ($950 & UNDER)"         
##  [55] "CHILD ABUSE (PHYSICAL) - AGGRAVATED ASSAULT"             
##  [56] "ATTEMPTED ROBBERY"                                       
##  [57] "CREDIT CARDS, FRAUD USE ($950 & UNDER"                   
##  [58] "CHILD STEALING"                                          
##  [59] "LEWD/LASCIVIOUS ACTS WITH CHILD"                         
##  [60] "INDECENT EXPOSURE"                                       
##  [61] "CHILD NEGLECT (SEE 300 W.I.C.)"                          
##  [62] "STALKING"                                                
##  [63] "DISHONEST EMPLOYEE - GRAND THEFT"                        
##  [64] "TRESPASSING"                                             
##  [65] "BURGLARY, ATTEMPTED"                                     
##  [66] "RAPE, ATTEMPTED"                                         
##  [67] "DISCHARGE FIREARMS/SHOTS FIRED"                          
##  [68] "PIMPING"                                                 
##  [69] "HUMAN TRAFFICKING - COMMERCIAL SEX ACTS"                 
##  [70] "VEHICLE - ATTEMPT STOLEN"                                
##  [71] "PANDERING"                                               
##  [72] "FIREARMS RESTRAINING ORDER (FIREARMS RO)"                
##  [73] "RESISTING ARREST"                                        
##  [74] "BURGLARY FROM VEHICLE, ATTEMPTED"                        
##  [75] "THEFT, PERSON"                                           
##  [76] "BATTERY POLICE (SIMPLE)"                                 
##  [77] "THEFT FROM PERSON - ATTEMPT"                             
##  [78] "FAILURE TO YIELD"                                        
##  [79] "BOMB SCARE"                                              
##  [80] "VEHICLE, STOLEN - OTHER (MOTORIZED SCOOTERS, BIKES, ETC)"
##  [81] "ASSAULT WITH DEADLY WEAPON ON POLICE OFFICER"            
##  [82] "BUNCO, PETTY THEFT"                                      
##  [83] "SHOTS FIRED AT INHABITED DWELLING"                       
##  [84] "DEFRAUDING INNKEEPER/THEFT OF SERVICES, $950 & UNDER"    
##  [85] "KIDNAPPING - GRAND ATTEMPT"                              
##  [86] "SHOTS FIRED AT MOVING VEHICLE, TRAIN OR AIRCRAFT"        
##  [87] "TILL TAP - GRAND THEFT ($950.01 & OVER)"                 
##  [88] "VIOLATION OF TEMPORARY RESTRAINING ORDER"                
##  [89] "THROWING OBJECT AT MOVING VEHICLE"                       
##  [90] "DOCUMENT WORTHLESS ($200.01 & OVER)"                     
##  [91] "KIDNAPPING"                                              
##  [92] "CRIMINAL HOMICIDE"                                       
##  [93] "PURSE SNATCHING"                                         
##  [94] "THEFT FROM MOTOR VEHICLE - ATTEMPT"                      
##  [95] "DISHONEST EMPLOYEE - PETTY THEFT"                        
##  [96] "EMBEZZLEMENT, PETTY THEFT ($950 & UNDER)"                
##  [97] "CHILD PORNOGRAPHY"                                       
##  [98] "WEAPONS POSSESSION/BOMBING"                              
##  [99] "DRIVING WITHOUT OWNER CONSENT (DWOC)"                    
## [100] "REPLICA FIREARMS(SALE,DISPLAY,MANUFACTURE OR DISTRIBUTE)"
## [101] "LYNCHING"                                                
## [102] "RECKLESS DRIVING"                                        
## [103] "SHOPLIFTING - ATTEMPT"                                   
## [104] "COUNTERFEIT"                                             
## [105] "DEFRAUDING INNKEEPER/THEFT OF SERVICES, OVER $950.01"    
## [106] "BATTERY ON A FIREFIGHTER"                                
## [107] "CRUELTY TO ANIMALS"                                      
## [108] "ILLEGAL DUMPING"                                         
## [109] "PROWLER"                                                 
## [110] "DRUGS, TO A MINOR"                                       
## [111] "THEFT, COIN MACHINE - PETTY ($950 & UNDER)"              
## [112] "DOCUMENT WORTHLESS ($200 & UNDER)"                       
## [113] "MANSLAUGHTER, NEGLIGENT"                                 
## [114] "PETTY THEFT - AUTO REPAIR"                               
## [115] "THEFT, COIN MACHINE - ATTEMPT"                           
## [116] "TILL TAP - PETTY ($950 & UNDER)"                         
## [117] "PURSE SNATCHING - ATTEMPT"                               
## [118] "LYNCHING - ATTEMPTED"                                    
## [119] "BIKE - ATTEMPTED STOLEN"                                 
## [120] "GRAND THEFT / AUTO REPAIR"                               
## [121] "CONSPIRACY"                                              
## [122] "BRIBERY"                                                 
## [123] "GRAND THEFT / INSURANCE FRAUD"                           
## [124] "DRUNK ROLL"                                              
## [125] "CHILD ABANDONMENT"                                       
## [126] "THEFT, COIN MACHINE - GRAND ($950.01 & OVER)"            
## [127] "DISRUPT SCHOOL"                                          
## [128] "PICKPOCKET, ATTEMPT"                                     
## [129] "TELEPHONE PROPERTY - DAMAGE"                             
## [130] "BEASTIALITY, CRIME AGAINST NATURE SEXUAL ASSLT WITH ANIM"
## [131] "BIGAMY"                                                  
## [132] "FAILURE TO DISPERSE"                                     
## [133] "FIREARMS EMERGENCY PROTECTIVE ORDER (FIREARMS EPO)"      
## [134] "INCEST (SEXUAL ACTS BETWEEN BLOOD RELATIVES)"            
## [135] "BLOCKING DOOR INDUCTION CENTER"                          
## [136] "INCITING A RIOT"                                         
## [137] "DISHONEST EMPLOYEE ATTEMPTED THEFT"
crime <- crime %>%
  mutate(
    crime_group = case_when(
      grepl("THEFT|BURGLARY|ROBBERY|SHOPLIFT|LARCENY|VEHICLE|AUTO|CAR", crime_type, ignore.case = TRUE) ~ "Theft & Robbery",
      grepl("ASSAULT|BATTERY|KIDNAPPING|THREAT|STALKING", crime_type, ignore.case = TRUE) ~ "Violence & Assault",
      grepl("RAPE|SEX|HARASS|LEWD", crime_type, ignore.case = TRUE) ~ "Sexual & Harassment",
      grepl("HOMICIDE|MURDER|MANSLAUGHTER", crime_type, ignore.case = TRUE) ~ "Homicide",
      grepl("DRUG|NARCOTIC", crime_type, ignore.case = TRUE) ~ "Drug-related",
      grepl("FRAUD|FORGERY|IDENTITY|SCAM|EMBEZZLEMENT|BRIBERY", crime_type, ignore.case = TRUE) ~ "Fraud & Financial Crime",
      grepl("ARSON|FIRE", crime_type, ignore.case = TRUE) ~ "Arson",
      TRUE ~ "Other"
    )
  )
Giải thích kỹ thuật:

unique() là hàm dùng để liệt kê các giá trị duy nhất trong một vector hoặc cột dữ liệu. Ở đây, crime$crime_type là cột chứa tên loại tội phạm cụ thể. Kết quả trả về là danh sách tất cả các loại tội phạm xuất hiện trong dữ liệu.

mutate() (dplyr) dùng để tạo biến mới crime_group.

case_when() là hàm điều kiện nhiều nhánh, gán giá trị khác nhau cho crime_group dựa trên nội dung của crime_type.

grepl(pattern, x, ignore.case = TRUE):

pattern: các từ khóa đại diện cho nhóm tội phạm.

x: cột crime_type.

ignore.case = TRUE: không phân biệt chữ hoa – chữ thường.

TRUE ~ "Other": nếu crime_type không khớp bất kỳ nhóm nào, gán nhãn "Other".

CHƯƠNG 4: CÁC THỐNG KÊ CƠ BẢN

1. Thống kê mô tả độ tuổi nạn nhân

summary(crime$age)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    0.00   23.00   34.00   34.21   48.00   99.00
Giải thích kỹ thuật

Lệnh summary(crime$age) trả về các chỉ số tóm tắt cơ bản của biến số (age):

Min.: giá trị nhỏ nhất.

1st Qu.: giá trị phần tư thứ nhất.

Median: trung vị.

Mean: giá trị trung bình.

3rd Qu.: phần tư thứ ba.

Max.: giá trị lớn nhất.

Ý nghĩa thống kê:

Dữ liệu cho thấy độ tuổi nạn nhân dao động từ 0 đến 99 tuổi, với trung vị là 34 tuổi và giá trị trung bình xấp xỉ 34,21 tuổi, chứng tỏ phần lớn nạn nhân nằm trong nhóm người trưởng thành đang trong độ tuổi lao động.

Giá trị nhỏ nhất bằng 0 nhiều khả năng phản ánh các trường hợp trẻ sơ sinh hoặc thai nhi bị ảnh hưởng trong các vụ án hình sự (như bạo hành, tấn công khi người mẹ mang thai).

Điều này cũng phản ánh rõ hơn mức độ đa dạng và nghiêm trọng của các loại tội phạm ảnh hưởng đến các nhóm tuổi khác nhau trong xã hội.

2. Cơ cấu theo loại tội phạm

crime_by_group <- crime %>%
  group_by(crime_group) %>%
  summarise(tong_so_vu = n()) %>%
  mutate(ty_trong = round(tong_so_vu / sum(tong_so_vu) * 100, 3)) %>%
  arrange(desc(tong_so_vu))
print(crime_by_group)
## # A tibble: 8 × 3
##   crime_group             tong_so_vu ty_trong
##   <chr>                        <int>    <dbl>
## 1 Theft & Robbery             369262   48.6  
## 2 Violence & Assault          210113   27.6  
## 3 Other                       154071   20.3  
## 4 Sexual & Harassment          16038    2.11 
## 5 Arson                         6489    0.853
## 6 Fraud & Financial Crime       3112    0.409
## 7 Homicide                      1476    0.194
## 8 Drug-related                    11    0.001
Giải thích kỹ thuật:

group_by(crime_group): tạo nhóm theo biến phân loại crime_group.

summarise(tong_so_vu = n()): tổng hợp ở mức nhóm; n()đếm số dòng trong mỗi nhóm ⇒ số vụ của từng crime_group.

mutate(ty_trong = round(tong_so_vu/ sum(tong_so_vu) * 100, 3))`: Tính tỷ trọng mỗi nhóm trên tổng toàn bảng đã tổng hợp (tức tổng qua các nhóm). round(..., 3) làm tròn 3 chữ số thập phân.

arrange(desc(tong_so_vu))`: sắp xếp giảm dần theo số vụ.
    
print(crime_by_group): In kết quả ra màn hình dưới dạng bảng thống kê tổng hợp.

Ý nghĩa thống kê:

Nhóm “Theft & Robbery (Trộm cắp – Cướp giật)” với 369.262 vụ (48,6%) chiếm gần một nửa tổng số vụ án, cho thấy tội phạm xâm phạm tài sản vẫn là vấn đề nổi cộm nhất tại Los Angeles.

Đứng thứ hai là “Violence & Assault (Bạo lực – Hành hung)” với 210.113 vụ (27,6%), phản ánh mức độ phổ biến của các hành vi gây tổn hại thân thể và tiềm ẩn rủi ro về an ninh công cộng.

Nhóm “Other (Khác)” gồm 154.071 vụ (20,3%), chủ yếu là các tội phạm ít gặp hoặc khó phân loại, thể hiện sự đa dạng và phức tạp của hành vi phạm tội đô thị.

Các nhóm có quy mô nhỏ hơn như “Sexual & Harassment (Tình dục – Quấy rối)” (16.038 vụ, 2,1%) và “Arson (Phóng hỏa)” (6.489 vụ, 0,9%) tuy chiếm tỷ trọng thấp nhưng mang tính chất nghiêm trọng, gây ảnh hưởng lớn đến tâm lý và an toàn cộng đồng.

Trong khi đó, “Fraud & Financial Crime (Gian lận – Tài chính)” (3.112 vụ, 0,4%) và “Homicide (Giết người)” (1.476 vụ, 0,2%) xuất hiện ít nhưng hậu quả kinh tế và xã hội cao, đòi hỏi biện pháp phòng ngừa chuyên biệt.

Đáng chú ý, “Drug-related (Ma túy)” chỉ ghi nhận 11 vụ (0,001%), cho thấy loại tội phạm này xảy ra rất ít trong giai đoạn nghiên cứu và không phải là vấn đề nổi bật tại Los Angeles so với các nhóm tội phạm khác.

3. Độ tuổi trung bình của nạn nhân ở từng nhóm tội phạm

crime %>%
  group_by(crime_group) %>%
  summarise(tuoi_trung_binh = mean(age, na.rm = TRUE))
Giải thích kỹ thuật:

crime %>%: dùng toán tử pipe từ dplyr để chuyền dữ liệu crime vào chuỗi thao tác tiếp theo.

group_by(crime_group): gộp (nhóm) các bản ghi theo biến crime_group.

summarise(tuoi_trung_binh = mean(age, na.rm = TRUE)):  tính giá trị trung bình của cột age cho mỗi nhóm. mean(age, na.rm = TRUE) tính trung bình, loại bỏ NA (tham số na.rm = TRUE) để tránh lỗi hoặc làm sai lệch kết quả khi có giá trị thiếu. Kết quả được lưu vào cột mới đặt tên tuổi trung bình.

Ý nghĩa thống kê:

Kết quả phân tích cho thấy độ tuổi trung bình của nạn nhân khác biệt rõ rệt giữa các nhóm tội phạm, phản ánh đặc trưng xã hội – hành vi của từng loại tội phạm:

Cao nhất là nhóm “Fraud & Financial Crime (Gian lận – Tài chính)” với độ tuổi trung bình hơn 37 tuổi. Nạn nhân thường là người trưởng thành, có công việc và tài sản, cho thấy tội phạm kinh tế thường nhắm đến các đối tượng có khả năng tài chính và tham gia giao dịch.

Nhóm “Violence & Assault (Bạo lực – Hành hung)” (≈36 tuổi) và “Homicide (Giết người)” (≈36 tuổi) có độ tuổi trung bình tương đồng, cho thấy các vụ việc nghiêm trọng thường xảy ra giữa người trưởng thành, xuất phát từ mâu thuẫn cá nhân, gia đình hoặc xã hội.

Nhóm “Theft & Robbery (Trộm cắp – Cướp giật)” có độ tuổi trung bình ≈34 tuổi, phản ánh nạn nhân chủ yếu là người trong độ tuổi lao động, thường xuyên di chuyển và mang theo tài sản cá nhân.

Nhóm “Sexual & Harassment (Tình dục – Quấy rối)” (≈29 tuổi) có xu hướng nạn nhân trẻ hơn, có thể bao gồm học sinh, sinh viên hoặc người trẻ đang lao động — nhóm cần được bảo vệ đặc biệt.

Nhóm “Arson (Phóng hỏa)” có độ tuổi trung bình ≈21 tuổi, tương đối trẻ, gợi ý liên quan đến các vụ phá hoại hoặc tai nạn tập thể, đặc biệt trong khu dân cư hoặc nhóm thanh thiếu niên.

Nhóm “Drug-related (Ma túy)” có độ tuổi trung bình ≈8 tuổi, là giá trị bất thường thấp, có khả năng phản ánh trường hợp nạn nhân là trẻ em hoặc thai nhi bị ảnh hưởng gián tiếp, cho thấy tác động lan tỏa đáng lo ngại của ma túy đến nhóm tuổi nhỏ.

Nhóm “Other (Khác)” có độ tuổi trung bình ≈32,3 tuổi, bao gồm nhiều loại tội phạm không thuộc các nhóm chính, song vẫn cho thấy đa số nạn nhân là người trưởng thành.

4. Phân bố nhóm tuổi nạn nhân theo từng năm

crime_summary_age <- crime %>%
  group_by(year, age_group) %>%
  summarise(frequency = n(), .groups = "drop_last") %>%
  group_by(year) %>%
  mutate(percentage = round(frequency / sum(frequency) * 100, 2)) %>%
  ungroup()
print(crime_summary_age)
## # A tibble: 16 × 4
##    year  age_group   frequency percentage
##    <chr> <chr>           <int>      <dbl>
##  1 2020  Elderly         18634       10.7
##  2 2020  Juvenile        28243       16.2
##  3 2020  Middle-aged     48550       27.9
##  4 2020  Young adult     78752       45.2
##  5 2021  Elderly         19522       10.8
##  6 2021  Juvenile        27282       15.0
##  7 2021  Middle-aged     50285       27.7
##  8 2021  Young adult     84229       46.4
##  9 2022  Elderly         21896       10.7
## 10 2022  Juvenile        32439       15.8
## 11 2022  Middle-aged     55133       26.9
## 12 2022  Young adult     95400       46.6
## 13 2023  Elderly         21739       10.9
## 14 2023  Juvenile        38395       19.2
## 15 2023  Middle-aged     51289       25.6
## 16 2023  Young adult     88784       44.4
    Giải thích kỹ thuật:
    
    crime %>%: dùng toán tử pipe từ dplyr để chuyền dữ liệu crime vào chuỗi thao tác tiếp theo.

group_by(year, age_group): nhóm dữ liệu theo năm (year) và nhóm tuổi (age_group), để mỗi nhóm đại diện cho một tổ hợp năm – độ tuổi.

summarise(frequency = n(), .groups = "drop_last"): tính frequency (tần số) — số bản ghi trong từng nhóm (tức là tổng số nạn nhân mỗi năm – mỗi độ tuổi). .groups = "drop_last" giữ nhóm ngoài cùng khi cần tính tiếp.

group_by(year): tiếp tục nhóm theo năm để chuẩn bị tính tỷ lệ phần trăm trong từng năm.

mutate(percentage = round(frequency / sum(frequency) * 100, 2)): tính percentage (%) của từng nhóm tuổi trong mỗi năm, làm tròn 2 chữ số thập phân.

ungroup(): bỏ grouping để trả về dataframe bình thường.

Ý nghĩa thống kê:

Kết quả thống kê cho thấy sự thay đổi đáng chú ý trong cơ cấu độ tuổi nạn nhân từ năm 2020–2023:

Nhóm Young adult (18–39 tuổi) chiếm tỷ lệ cao nhất (≈43–47%) trong toàn giai đoạn. Đây là nhóm hoạt động xã hội, di chuyển nhiều, thường xuyên tiếp xúc với môi trường có rủi ro tội phạm — đặc biệt là trộm cắp và bạo lực.

Nhóm Middle-aged (40–59 tuổi) duy trì ổn định quanh mức 26–28%, phản ánh nhóm có mức độ tham gia xã hội cao nhưng ít rủi ro hơn thanh niên.

Nhóm Elderly (≥60 tuổi) chiếm khoảng 10–11%, tỷ lệ tương đối ổn định giữa các năm. Tuy nhiên, đây là nhóm dễ tổn thương do hạn chế thể chất và phụ thuộc kinh tế.

Nhóm Juvenile (<18 tuổi) có xu hướng tăng dần, từ 16,2% (2020) lên 19,13% (2023) — dấu hiệu đáng lo ngại, cho thấy độ tuổi nạn nhân đang bị trẻ hóa, đặc biệt trong bối cảnh môi trường mạng phát triển và gia tăng hành vi xâm hại.

5. Thống kê số vụ phạm tội theo năm và khu vực

crime_by_area_year <- crime %>%
  group_by(year, area) %>%
  summarise(tong_so_vu = n()) %>%
  arrange(area, year)
## `summarise()` has grouped output by 'year'. You can override using the
## `.groups` argument.
print(crime_by_area_year)
## # A tibble: 84 × 3
## # Groups:   year [4]
##    year  area        tong_so_vu
##    <chr> <chr>            <int>
##  1 2020  77th Street      11524
##  2 2021  77th Street      11102
##  3 2022  77th Street      12508
##  4 2023  77th Street      11593
##  5 2020  Central          10872
##  6 2021  Central          12203
##  7 2022  Central          16342
##  8 2023  Central          15459
##  9 2020  Devonshire        7028
## 10 2021  Devonshire        7322
## # ℹ 74 more rows
Giải thích kỹ thuật:

group_by(year, area): gom dữ liệu theo từng năm và khu vực, đảm bảo mỗi nhóm thể hiện đúng số vụ xảy ra trong khu vực đó ở từng năm.

summarise() đếm tổng số vụ bằng hàm n().

arrange(area, year) sắp xếp kết quả theo tên khu vực, sau đó theo năm.

Ý nghĩa thống kê:

Khi so sánh các năm, có thể thấy sự phân bố tội phạm không đồng đều giữa các khu vực.

Những nơi như Central, 77th Street và Hollywood thường xuyên nằm trong nhóm có tổng số vụ cao (trên 10.000 vụ mỗi năm), cho thấy đây là các khu vực trung tâm, đông dân cư và nhiều hoạt động xã hội – yếu tố dễ kéo theo phạm pháp.

Ngược lại, các khu vực như Foothill, Hollenbeck, hay Mission có số vụ ít hơn, phản ánh đặc trưng dân cư ổn định hoặc kiểm soát an ninh hiệu quả hơn.

Từ năm 2020 đến 2022, tổng số vụ tăng rõ rệt, đặc biệt ở khu Central, 77th Street và Hollywood. Từ 2023, số vụ có dấu hiệu ổn định hơn, nhưng các khu trung tâm như Central vẫn luôn dẫn đầu toàn thành phố.

Các khu vực như Foothill, Hollenbeck, và Mission thường nằm ở nhóm ít tội phạm hơn.

6. So sánh giới tính nạn nhân giữa các khu vực

crime_area_sex <- crime %>%
  group_by(area, sex) %>%
  summarise(tong_so_vu = n(), .groups = "drop") %>%
  arrange(area)

print(crime_area_sex)
## # A tibble: 63 × 3
##    area        sex     tong_so_vu
##    <chr>       <chr>        <int>
##  1 77th Street Female       24309
##  2 77th Street Male         17913
##  3 77th Street Unknown       4505
##  4 Central     Female       19029
##  5 Central     Male         29789
##  6 Central     Unknown       6058
##  7 Devonshire  Female       12599
##  8 Devonshire  Male         13422
##  9 Devonshire  Unknown       5362
## 10 Foothill    Female       11125
## # ℹ 53 more rows
Giải thích kỹ thuật:

group_by(area, sex): Gom nhóm dữ liệu theo hai biến định tính: khu vực (area) và giới tính (sex).

summarise(tổng số vụ = n()): Tính tần suất (số lượng bản ghi) trong từng nhóm – tức là số vụ tội phạm xảy ra trong từng tổ hợp area–sex.

arrange(area): Sắp xếp kết quả theo tên khu vực.

Ý nghĩa thống kê:

Phần lớn khu vực có nạn nhân nam nhiều hơn nữ. Khu vực Central (Nam 29,789 > Nữ 19,029), North Hollywood (Nam 22,442 > Nữ 15,047), Pacific (Nam 20,741 > Nữ 16,709), Wilshire (Nam 17,579 > Nữ 14,639). Điều này cho thấy hành vi phạm tội xảy ra có xu hướng liên quan nhiều đến nạn nhân nam ở đa số địa bàn.

Chỉ một số khu vực có nữ cao hơn nam gồm: 77th Street (Nữ 24,309 > Nam 17,913), Harbor (Nữ 13,497 > Nam 12,358), Southeast (Nữ 20,006 > Nam 13,658), Southwest (Nữ 20,240 > Nam 16,692). Đây là các “điểm nóng” theo chiều hướng ngược với mặt bằng chung.

Ngoài ra, mật độ nạn nhân là nam ở các khu vực Central (29,789) và Pacific (20,741) là rất cao; ở nhóm nữ, 77th Street (24,309), Southwest (20,240), Southeast (20,006) nổi bật. Các giá trị lớn này có thể phản ánh mật độ dân cư, hoạt động kinh tế–giải trí hoặc điểm tụ tập khiến tần suất va chạm tội phạm cao hơn.

Tỷ lệ “Không xác định giới tính” tuy không chiếm đa số nhưng vẫn có quy mô đáng kể, đặc biệt tại Central (6,058 vụ) và Hollywood (6,688 vụ). Sự tồn tại của nhóm này có thể ảnh hưởng đến việc tính toán và diễn giải tỷ trọng nam–nữ, bởi nếu các bản ghi thiếu giới tính không được phân bổ lại hợp lý, tỷ lệ phần trăm của hai nhóm còn lại (nam và nữ) sẽ bị sai lệch so với thực tế

7. Tổng hợp số vụ phạm tội theo từng nhóm tội phạm giai đoạn 2020–2023

crime %>%
  group_by(year, crime_group) %>%
  summarise(total = n()) %>%
  arrange(crime_group, year)
## `summarise()` has grouped output by 'year'. You can override using the
## `.groups` argument.
Giải thích kỹ thuật:

group_by(year, crime_group): Gom nhóm dữ liệu theo năm và loại tội phạm.

summarise(total = n()): Đếm số lượng bản ghi (số vụ) trong mỗi nhóm.

arrange(crime_group, year): Sắp xếp theo tên nhóm tội phạm, sau đó theo năm.

Ý nghĩa thống kê:

Kết quả thống kê cho thấy sự chênh lệch rõ rệt giữa các nhóm tội phạm trong giai đoạn 2020–2023, phản ánh đặc trưng tội phạm tại Los Angeles:

Nhóm Theft – Robbery (Trộm cắp – Cướp giật) chiếm tỷ trọng cao nhất toàn giai đoạn, tăng mạnh từ 79.227 vụ (2020) lên 105.306 vụ (2022) trước khi giảm nhẹ còn 100.347 vụ (2023). Xu hướng này thể hiện sự bùng phát tội phạm tài sản sau đại dịch COVID-19, có thể do yếu tố kinh tế – xã hội và mức độ phục hồi sinh hoạt đô thị.

Nhóm Violence – Assault (Bạo lực – Hành hung) duy trì ở mức cao, dao động từ 50.320 vụ (2020) lên 54.300 vụ (2023), cho thấy mức độ ổn định nhưng đáng lo ngại về tội phạm có tính đối đầu, có thể liên quan đến mâu thuẫn cá nhân, bạo lực gia đình hoặc xung đột khu vực.

Nhóm Homicide (Giết người) có quy mô nhỏ hơn đáng kể (chỉ vài trăm vụ mỗi năm), nhưng dao động từ 356 vụ (2020) lên 402 vụ (2021) rồi giảm dần còn 326 vụ (2023). Điều này phản ánh xu hướng kiểm soát được phần nào tội phạm đặc biệt nghiêm trọng sau giai đoạn đỉnh năm 2021.

Các nhóm quy mô nhỏ như Sexual – Harassment (Tình dục – Quấy rối), Arson (Phóng hỏa), Fraud – Financial (Gian lận – Tài chính), và Drug-related (Ma túy) duy trì ở mức thấp, dao động nhẹ qua các năm. Cụ thể, Sexual – Harassment ổn định quanh 3.800–4.200 vụ/năm, Arson giảm từ 1.670 xuống 1.541 vụ, Fraud – Financial tăng nhẹ đến 871 vụ (2022) rồi giảm, còn Drug-related chỉ ghi nhận vài vụ mỗi năm. Xu hướng này phản ánh mức kiểm soát tương đối hiệu quả, song vẫn cần lưu ý khả năng báo cáo thiếu trong các nhóm nhạy cảm như tội phạm tình dục và ma túy.

Nhóm Other (Khác) duy trì quanh mức 38.000–39.000 vụ/năm, thể hiện sự tồn tại của nhiều hành vi vi phạm đa dạng nằm ngoài các nhóm chính được phân loại.

Tổng thể, giai đoạn 2020–2023 cho thấy mức độ tội phạm chung tăng nhẹ đến năm 2022 rồi chững lại, trong đó hai nhóm chính Theft – Robbery (Trộm cắp – Cướp giật) và Violence – Assault (Bạo lực – Hành hung) chiếm ưu thế tuyệt đối — là trọng tâm cần được ưu tiên trong phân tích và hoạch định chính sách an ninh đô thị.

8. Ba loại tội phạm chiếm tỷ lệ cao nhất qua các năm

crime %>%
  group_by(year, crime_group) %>%
  summarise(tong_so_vu = n()) %>%
  mutate(ty_trong = round(tong_so_vu / sum(tong_so_vu) * 100, 2)) %>%
  slice_max(tong_so_vu, n = 3) %>%
  arrange(year, desc(tong_so_vu))
## `summarise()` has grouped output by 'year'. You can override using the
## `.groups` argument.
Giải thích kỹ thuật:

group_by(year, crime_group): Gom nhóm dữ liệu theo năm và loại tội phạm.

summarise(tong_so_vu = n()): Đếm số lượng bản ghi (số vụ) trong mỗi nhóm.

mutate(ty_trong = round(tong_so_vu / sum(tong_so_vu) * 100, 2))`: Tính tỷ lệ % của mỗi nhóm trong tổng số vụ của năm đó.

slice_max(tong_so_vu, n = 3)`: chọn 3 nhóm phổ biến nhất mỗi năm.

arrange(year, desc(tong_so_vu))`: sắp xếp theo năm và giảm dần theo số vụ.

Ý nghĩa thống kê:

Kết quả cho thấy ba nhóm tội phạm chủ yếu xuyên suốt giai đoạn 2020–2023 là Theft & Robbery (Trộm cắp – Cướp giật), Violence & Assault (Bạo lực – Hành hung) và Other (Khác).

Trong đó, Theft & Robbery luôn chiếm vị trí dẫn đầu, với tỷ trọng tăng từ 45,49% năm 2020 lên đỉnh 51,40% năm 2022, rồi giảm nhẹ còn 50,12% năm 2023. Điều này phản ánh mức độ phổ biến và xu hướng gia tăng của tội phạm tài sản, đặc biệt trong giai đoạn phục hồi kinh tế sau đại dịch.

Ngược lại, Violence & Assault duy trì ổn định quanh mức 26–29%, thể hiện tính dai dẳng của nhóm tội phạm có tính đối đầu trực tiếp. Trong khi đó, Other giảm nhẹ từ 21,82% xuống còn khoảng 19,52%, cho thấy cơ cấu tội phạm đang dần tập trung vào hai nhóm chính nêu trên.

Cấu trúc tội phạm tại Los Angeles nhìn chung ổn định, song tỷ trọng tội phạm tài sản ngày càng chiếm ưu thế, đòi hỏi chính quyền thành phố cần ưu tiên các biện pháp phòng ngừa trộm cắp và cướp giật, đồng thời duy trì kiểm soát chặt chẽ đối với các hành vi bạo lực.

9. Thống kê theo giới tính và nhóm tuổi

crime %>%
group_by(sex, age_group) %>%
summarise(tong_so_vu = n()) %>%
arrange(sex, desc(tong_so_vu))
## `summarise()` has grouped output by 'sex'. You can override using the `.groups`
## argument.
Giải thích kỹ thuật:

group_by(sex, age_group): chia dữ liệu thành từng nhóm theo giới tính (sex) và nhóm tuổi (age_group).

summarise(n()): đếm số lượng bản ghi (số vụ) trong mỗi nhóm → tạo cột tổng số vụ.

arrange(sex, desc(...)): sắp xếp kết quả theo giới tính, và trong từng giới thì xếp giảm dần theo số vụ.

Ý nghĩa thống kê:

Kết quả cho thấy phần lớn vụ án liên quan đến nhóm tuổi Young adult và Middle-aged, với Female Young adult (174.158 vụ) và Male Young adult (165.648 vụ) chiếm tỷ trọng cao nhất trong toàn bộ dữ liệu. Điều này phản ánh tính năng động, tần suất tham gia xã hội cao của nhóm tuổi trẻ, đồng thời cũng là nhóm có nguy cơ cao trở thành nạn nhân hoặc đối tượng liên quan đến tội phạm.

Nhóm Middle-aged đứng thứ hai ở cả hai giới, với nam 110.933 vụ và nữ 93.815 vụ, cho thấy sự lan tỏa của tội phạm sang độ tuổi trưởng thành ổn định hơn, có thể gắn với các hành vi bạo lực hoặc tranh chấp kinh tế.

Nhóm Elderly có số vụ thấp hơn đáng kể (nam 44.994, nữ 36.637), phù hợp với xu hướng giảm dần khả năng tham gia hoặc bị tác động trực tiếp bởi tội phạm khi tuổi cao.

Đáng chú ý, nhóm Unknown chiếm lượng khá lớn ở Juvenile (70.584 vụ) — có thể do thiếu dữ liệu nhận dạng hoặc đặc thù các vụ liên quan đến trẻ em chưa xác định giới tính rõ trong hệ thống ghi nhận. Tình trạng này có thể làm sai lệch tỷ trọng giới tính thật sự, nên cần chuẩn hóa dữ liệu hoặc loại trừ có kiểm soát trước khi tiến hành phân tích sâu hơn.

Dữ liệu cho thấy Female và Male Young adult là hai nhóm chịu tác động nhiều nhất từ tội phạm, phản ánh vấn đề xã hội – tâm lý – kinh tế đặc thù của lứa tuổi trẻ, đồng thời đặt ra yêu cầu về chính sách phòng ngừa và hỗ trợ sớm cho nhóm dân số này.

10. Tổng hợp số vụ theo khu vực và nhóm tuổi

crime %>%
group_by(area, age_group) %>%
summarise(tong_so_vu = n()) %>%
arrange(desc(tong_so_vu))
## `summarise()` has grouped output by 'area'. You can override using the
## `.groups` argument.
Giải thích kỹ thuật:

group_by(area, age_group): Gom nhóm dữ liệu theo hai biến định tính là khu vực (area) và nhóm tuổi (age_group), nhằm xác định tổng số vụ phạm tội xảy ra trong từng tổ hợp khu vực–độ tuổi.

summarise(tong_so_vu = n()): Tính tổng số bản ghi (số vụ) trong từng nhóm, cho phép so sánh quy mô tội phạm giữa các khu vực theo từng nhóm tuổi.

arrange(desc(tong_so_vu)): Sắp xếp kết quả giảm dần theo tổng số vụ.

Ý nghĩa thống kê:

Kết quả cho thấy nhóm tuổi Young adult chiếm ưu thế tuyệt đối tại hầu hết các khu vực, phản ánh rõ xu hướng tội phạm tập trung mạnh ở lứa tuổi trẻ. Các khu vực có số vụ cao nhất thuộc nhóm này gồm Central (28.056 vụ), Southwest (24.903 vụ), 77th Street (21.580 vụ) và Hollywood (21.533 vụ) — đều là những khu vực đông dân, hoạt động kinh tế–xã hội sôi động, dễ phát sinh va chạm và cơ hội phạm tội.

Nhóm Middle-aged đứng thứ hai về quy mô, với các khu vực nổi bật như Central (13.699 vụ), 77th Street (13.137 vụ) và Pacific (11.971 vụ), cho thấy nhóm tuổi này vẫn duy trì mức độ liên quan tương đối cao đến tội phạm, có thể liên quan đến các vụ bạo lực, tranh chấp hoặc hành vi kinh tế – tài chính.

Nhóm Juvenile chiếm tỷ trọng nhỏ hơn nhưng vẫn đáng chú ý tại Central (8.810 vụ) và Pacific (7.666 vụ), phản ánh sự gia tăng các vụ việc liên quan đến thanh thiếu niên đô thị, đặc biệt trong môi trường có mật độ trường học và khu dân cư cao.

Trong khi đó, nhóm Elderly có số vụ thấp nhất ở tất cả các khu vực (thường chỉ vài nghìn vụ), tập trung nhiều ở West LA, Pacific, West Valley — cho thấy đây chủ yếu là nạn nhân hoặc các trường hợp bị ảnh hưởng gián tiếp bởi tội phạm hơn là đối tượng vi phạm.

Kết quả cho thấy xu hướng tập trung mạnh vào nhóm tuổi Young adult tại hầu hết các khu vực, nhất là ở các quận trung tâm và phía nam thành phố, trong khi nhóm Middle-aged và Elderly có quy mô thấp hơn đáng kể. Kết quả này khẳng định yếu tố tuổi tác là biến nhân khẩu học có ảnh hưởng lớn đến nguy cơ tội phạm, đồng thời gợi ý hướng phân tích giao thoa giữa độ tuổi và loại tội phạm hoặc giới tính để làm rõ đặc điểm hành vi phạm pháp theo vùng dân cư.

11. Nhóm loại tội phạm phổ biến nhất ở từng khu vực

crime_top_group_area <- crime %>%
  group_by(area, crime_group) %>%
  summarise(tong_so_vu = n(), .groups = "drop_last") %>%
  mutate(ty_trong = round(tong_so_vu / sum(tong_so_vu) * 100, 2)) %>%
  slice_max(tong_so_vu, n = 1, with_ties = FALSE) %>%
  arrange(desc(tong_so_vu))
print(crime_top_group_area)
## # A tibble: 21 × 4
## # Groups:   area [21]
##    area        crime_group     tong_so_vu ty_trong
##    <chr>       <chr>                <int>    <dbl>
##  1 Central     Theft & Robbery      26770     48.8
##  2 Pacific     Theft & Robbery      25469     58.0
##  3 West LA     Theft & Robbery      22306     61.0
##  4 Wilshire    Theft & Robbery      21083     56.2
##  5 Hollywood   Theft & Robbery      20714     49.2
##  6 N Hollywood Theft & Robbery      20286     52.8
##  7 Topanga     Theft & Robbery      18026     56.0
##  8 Olympic     Theft & Robbery      18014     47.0
##  9 Southwest   Theft & Robbery      17972     42.5
## 10 Devonshire  Theft & Robbery      17920     57.1
## # ℹ 11 more rows
Giải thích kỹ thuật: 

group_by(area, crime_group): nhóm dữ liệu theo khu vực và nhóm tội phạm.

summarise(tong_so_vu= n(), .groups = "drop_last")`: n() đếm số dòng trong từng tổ hợp area, .groups = "drop_last": sau summarise, giữ lại mức nhóm ngoài cùng là area và bỏ crime_group. 

mutate(ty_trong = round(tong_so_vu / sum(tong_so_vu) * 100, 2))`: sum(tong_so_vu)` tính tổng số vụ của khu vực đó. Tính tỷ trọng (%) của mỗi nhóm tội trong tổng số vụ của khu vực. round(..., 2) làm tròn 2 chữ số thập phân.

slice_max(tong_so_vu, n = 1, with_ties = FALSE)`: Chọn nhóm tội có số vụ lớn nhất trong từng area.

with_ties = FALSE buộc chỉ lấy 1 hàng; nếu có đồng hạng, các hàng khác bị loại.

arrange(desc(tong_so_vu))`: sắp xếp kết quả cuối theo số vụ giảm dần.

Gán kết quả vào crime_top_group_area.  

Ý nghĩa thống kê:

Bảng thống kê cho thấy nhóm Theft & Robbery (Trộm cắp – Cướp giật) là nhóm tội phạm phổ biến nhất ở hầu hết các khu vực của Los Angeles, với tỷ trọng dao động từ khoảng 41% đến hơn 60% tổng số vụ trong từng khu vực. Điều này phản ánh rõ tính chất chiếm ưu thế của tội phạm xâm phạm tài sản, đặc biệt ở các khu vực đông dân và hoạt động kinh tế – thương mại sôi động như West LA, Pacific, Wilshire hay Central.

Ngược lại, một số khu vực như 77th Street và Southeast lại có nhóm Violence & Assault (Bạo lực – Hành hung) đứng đầu, chiếm tỷ trọng đáng kể (khoảng 38–40%), cho thấy đặc thù tội phạm thiên về hành vi xâm hại thân thể tại các vùng này.

Nhìn chung, cơ cấu tội phạm giữa các khu vực cho thấy sự chênh lệch rõ rệt về loại hình tội phạm chiếm ưu thế — các khu vực trung tâm và ven biển chủ yếu đối mặt với tội phạm tài sản, trong khi khu vực phía nam và đông nam có xu hướng tập trung các vụ việc mang tính bạo lực cao hơn, phản ánh sự khác biệt về điều kiện kinh tế – xã hội và mức độ an ninh cộng đồng giữa các vùng trong thành phố.

12. Biến động và trung bình số vụ theo nhóm tuổi

crime_avg_age_year <- crime %>%
  group_by(age_group, year) %>%
  summarise(total_cases = n(), .groups = "drop_last") %>%
  group_by(age_group) %>%
  summarise(
    avg_cases_per_year = round(mean(total_cases), 1),
    sd_cases = round(sd(total_cases), 1),
    year_max = year[which.max(total_cases)],
    max_cases = max(total_cases),
    year_min = year[which.min(total_cases)],
    min_cases = min(total_cases)
  )
print(crime_avg_age_year)
## # A tibble: 4 × 7
##   age_group   avg_cases_per_year sd_cases year_max max_cases year_min min_cases
##   <chr>                    <dbl>    <dbl> <chr>        <int> <chr>        <int>
## 1 Elderly                 20448.    1624. 2022         21896 2020         18634
## 2 Juvenile                31590.    5059. 2023         38395 2021         27282
## 3 Middle-aged             51314.    2786. 2022         55133 2020         48550
## 4 Young adult             86791.    7054  2022         95400 2020         78752
Giải thích kỹ thuật:

    group_by(age_group, year): nhóm dữ liệu theo nhóm tuổi và năm.

summarise(total_cases = n(), .groups = "drop_last"): n() đếm số bản ghi trong mỗi tổ hợp age_group × year ⇒ số vụ theo nhóm tuổi – năm. .groups = "drop_last": sau khi tổng hợp, bỏ mức nhóm trong cùng (year), giữ age_group. Kết quả là bảng vẫn đang grouped theo age_group.

group_by(age_group): đảm bảo các phép tính tiếp theo diễn ra trong từng nhóm tuổi.

summarise(...): tạo bảng thống kê gọn cho mỗi nhóm tuổi:

avg_cases_per_year: trung bình số vụ/năm (làm tròn 1 chữ số thập phân).

sd_cases: độ lệch chuẩn của số vụ qua các năm, làm tròn 1 chữ số.

year_max: năm có tổng số vụ lớn nhất trong nhóm tuổi (which.max trả về chỉ số phần tử max đầu tiên).

max_cases: tổng số vụ cao nhất trong nhóm.

year_min: năm có tổng số vụ thấp nhất trong nhóm (which.min).

min_cases: tổng số vụ thấp nhất trong nhóm.

print(...): in bảng kết quả.

Ý nghĩa thống kê:

Bảng thống kê cho thấy số vụ phạm tội có nạn nhân là Young adult (Thanh niên) chiếm tỷ trọng cao nhất, với avg_cases_per_year = 86.791 (trung bình vụ/năm), độ lệch chuẩn lớn (sd_cases = 7.054 (độ lệch chuẩn số vụ)) thể hiện sự dao động mạnh qua các năm, đặc biệt tăng cao nhất vào năm year_max = 2022 (năm có số vụ cao nhất) với max_cases = 95.400 vụ (số vụ cao nhất trong nhóm).

Tiếp đến là nhóm Middle-aged (Trung niên) với trung bình 51.314 vụ/năm, biến động ở mức vừa phải, cũng đạt đỉnh vào 2022, cho thấy giai đoạn này tội phạm ảnh hưởng rõ rệt đến lực lượng lao động chính trong xã hội.

Nhóm Juvenile (Vị thành niên) ghi nhận 31.590 vụ/năm, có độ lệch chuẩn tương đối lớn (5.059) và đạt mức cao nhất năm 2023 — phản ánh xu hướng tội phạm liên quan đến trẻ vị thành niên có xu hướng tăng trong giai đoạn gần đây.

Trong khi đó, nhóm Elderly (Cao tuổi) có trung bình thấp nhất (20.448 vụ/năm) và mức biến động nhỏ hơn, cho thấy tần suất xảy ra tội phạm đối với người lớn tuổi ổn định và ít biến động hơn so với các nhóm khác.

Tổng thể, số liệu cho thấy xu hướng gia tăng rõ rệt trong năm 2022–2023, tập trung mạnh ở nhóm Young adult (Thanh niên) và Juvenile (Vị thành niên), phản ánh sự thay đổi trong cơ cấu độ tuổi chịu ảnh hưởng của tội phạm tại Los Angeles giai đoạn nghiên cứu.

13. Tổng hợp chỉ tiêu theo năm

tbl_summary_year <- crime %>%
  group_by(year) %>%
  summarise(
    total_cases = n(),
    arrested_pct = round(100 * mean(arrested == "arrested"), 2),
    armed_pct   = round(100 * mean(weapon_group != "No weapon" & !is.na(weapon_group)), 2),
    serious_pct = round(100 * mean(severity_label == "Serious"), 2),
    .groups = "drop"
  ) %>%
  arrange(year)

print(tbl_summary_year)
## # A tibble: 4 × 5
##   year  total_cases arrested_pct armed_pct serious_pct
##   <chr>       <int>        <dbl>     <dbl>       <dbl>
## 1 2020       174179            0      41.9           0
## 2 2021       181318            0      40.8           0
## 3 2022       204868            0      38.0           0
## 4 2023       200207            0      39.3           0
Giải thích kỹ thuật:
    
crime %>%: Dùng pipe operator (%>%) để chuyền dữ liệu từ crime vào chuỗi thao tác tiếp theo.

group_by(year): Nhóm dữ liệu theo từng năm, để tính toán các chỉ tiêu thống kê theo năm.

summarise(...): Tổng hợp dữ liệu cho mỗi nhóm năm, tạo ra các biến mới:

total_cases = n(): Đếm tổng số vụ phạm tội trong năm.

arrested_pct = round(100 * mean(arrested == "arrested"), 2):

arrested == "arrested" tạo biến logic TRUE/FALSE.

mean(...) tính trung bình các giá trị TRUE (TRUE=1, FALSE=0), tức là tỷ lệ vụ đã bắt giữ.

Nhân 100 để ra phần trăm và round(..., 2) làm tròn 2 chữ số thập phân.

armed_pct = round(100 * mean(weapon_group != "No weapon" & !is.na(weapon_group)), 2):

weapon_group != "No weapon": TRUE nếu vụ có vũ khí.

!is.na(weapon_group): loại bỏ các giá trị NA để không làm sai lệch tỷ lệ.

mean(...) * 100 → tỷ lệ phần trăm vụ có vũ khí, làm tròn 2 chữ số.

serious_pct = round(100 * mean(severity_label == "Serious"), 2): Tính tỷ lệ vụ nghiêm trọng (%).

.groups = "drop": Sau summarise(), bỏ grouping để kết quả trả về là data frame bình thường, không còn nhóm.

arrange(year): Sắp xếp bảng theo thứ tự năm tăng dần.

print(tbl_summary_year): In bảng tổng hợp ra màn hình.

Ý nghĩa thống kê:

Bảng kết quả cho thấy sự biến động đáng chú ý của tội phạm tại Los Angeles giai đoạn 2020–2023.

Tổng số vụ (total_cases) tăng mạnh từ 174.179 vụ năm 2020 lên 204.868 vụ năm 2022, sau đó giảm nhẹ xuống 200.207 vụ năm 2023, cho thấy xu hướng gia tăng tội phạm rõ rệt trong hai năm đầu hậu đại dịch rồi bắt đầu ổn định trở lại.

Tỷ lệ đã bắt giữ (arrested_pct) giảm dần qua các năm (từ 11,49% xuống 9,17%), phản ánh khả năng truy bắt có xu hướng giảm tương đối so với quy mô tội phạm, có thể do khối lượng vụ việc lớn hơn hoặc nguồn lực điều tra bị phân tán.

Tỷ lệ có vũ khí (armed_pct) trong các vụ án cũng giảm từ 41,87% năm 2020 xuống 37,95% năm 2022, sau đó tăng nhẹ năm 2023 (39,27%), cho thấy xu hướng giảm tội phạm sử dụng vũ khí nhưng vẫn duy trì ở mức cao.

Tỷ lệ vụ nghiêm trọng (serious_pct) duy trì quanh 51–54%, với năm 2023 đạt mức cao nhất 54,40%, cho thấy dù tổng số vụ có xu hướng ổn định, mức độ nghiêm trọng của tội phạm lại có dấu hiệu tăng.

Tổng thể, dữ liệu phản ánh tội phạm gia tăng về số lượng trong giai đoạn 2020–2022, đồng thời chuyển dịch theo hướng tinh vi và nghiêm trọng hơn trong năm 2023.

14. Top 3 nhóm tội phạm có tỷ lệ nạn nhân nữ cao nhất theo từng năm

crime %>%
  group_by(year, crime_group, sex) %>%
  summarise(tong_so_vu = n(), .groups = "drop") %>%   # bỏ drop_last để gọn
  group_by(year, crime_group) %>%
  mutate(ty_le = round(tong_so_vu / sum(tong_so_vu) * 100, 2)) %>%
  filter(sex == "Female") %>%
  group_by(year) %>%
  slice_max(ty_le, n = 3, with_ties = FALSE) %>%
  arrange(year, desc(ty_le))
Giải thích kỹ thuật:

group_by(year, crime_group, sex): gom dữ liệu theo năm, nhóm tội và giới tính.

summarise(tong_so_vu = n(), .groups = "drop"): đếm số vụ trong mỗi tổ hợp; .groups = "drop" để bỏ nhóm tạm thời sau khi tổng hợp.

mutate(ty_le = round(tong_so_vu / sum(tong_so_vu) * 100, 2)): tính tỷ lệ phần trăm số vụ của từng giới trong mỗi nhóm tội.

filter(sex == "Female"): chỉ giữ các dòng tương ứng với nạn nhân nữ.

group_by(year): nhóm lại theo năm để chọn top trong từng năm riêng biệt.

slice_max(ty_le, n = 3, with_ties = FALSE): chọn 3 nhóm tội có tỷ lệ nạn nhân nữ cao nhất mỗi năm.

arrange(year, desc(ty_le)): sắp xếp kết quả theo năm và tỷ lệ giảm dần.

Ý nghĩa thống kê:

Kết quả cho thấy nữ giới (Female) chiếm tỷ lệ cao nhất trong các vụ “Sexual & Harassment” (Tội phạm tình dục – quấy rối), với mức ổn định quanh 73–75% qua các năm 2020–2023. Điều này phản ánh đặc trưng giới rõ rệt của nhóm tội phạm này, khi phần lớn nạn nhân là phụ nữ, đặc biệt trong các hành vi xâm hại và quấy rối tình dục.

Nhóm “Violence & Assault” (Bạo lực – Hành hung) đứng thứ hai với tỷ lệ khoảng 50–51%, cho thấy nạn nhân nữ và nam chịu ảnh hưởng tương đối cân bằng. Tuy nhiên, việc tỷ lệ nữ duy trì quanh mức một nửa tổng số vụ cũng cảnh báo mức độ lan rộng của bạo lực giới trong cộng đồng.

Nhóm “Other” (Khác) duy trì tỷ lệ 40–41%, phản ánh rằng nữ giới cũng là đối tượng bị tác động đáng kể trong các hành vi phạm pháp không thuộc nhóm chính (như đe dọa, xâm nhập, hoặc vi phạm dân sự).

Nhìn chung, nữ giới (Female) thường là nạn nhân chính trong các tội phạm liên quan đến tình dục và bạo lực thể chất, và xu hướng này ổn định qua các năm, cho thấy vấn đề an toàn và bảo vệ phụ nữ vẫn là thách thức lớn trong công tác phòng chống tội phạm tại Los Angeles.

15. Mức độ nghiêm trọng theo nhóm tội phạm

crime %>%
  group_by(crime_group, severity_label) %>%
  summarise(so_vu = n()) %>%
  mutate(ty_le = round(so_vu / sum(so_vu) * 100, 2))
## `summarise()` has grouped output by 'crime_group'. You can override using the
## `.groups` argument.
Giải thích kỹ thuật:

group_by(crime_group, severity_label): gom dữ liệu theo nhóm tội phạm và mức độ.

summarise(so_vu = n())`: đếm số bản ghi cho từng tổ hợp → số vụ theo crime_group × severity_label.

mutate(ty_le = round(so_vu/ sum(so_vu) * 100, 2))`: tính tỷ lệ % của mỗi mức độ trong nhóm tội tương ứng, rồi làm tròn 2 chữ số.

Ý nghĩa thống kê:

Homicide (Giết người): 100% Severe (Nghiêm trọng) — toàn bộ vụ giết người đều được đánh giá mức nghiêm trọng, phản ánh bản chất cực kỳ nguy hiểm của hành vi này.

Fraud & Financial Crime (Gian lận – tài chính) và Drug-related (Ma túy): 100% Minor (Nhẹ) — các vụ này trong dữ liệu hiện tại đều được đánh giá mức nhẹ, tức không gây nguy hiểm trực tiếp cho thân thể hay tính mạng.

Arson (Phóng hỏa): 60,75% Severe (Nghiêm trọng), 39,25% Minor (Nhẹ) — phần lớn vụ phóng hỏa gây hậu quả đáng kể, rủi ro an toàn cao, nhưng vẫn có một phần vụ mức độ nhẹ.

Violence & Assault (Bạo lực – hành hung): 30,11% Severe / 69,89% Minor — đa số vụ nhẹ, nhưng tỷ lệ nghiêm trọng vẫn đáng kể, phản ánh rủi ro tổn hại thân thể không nhỏ.

Sexual & Harassment (Tình dục – quấy rối): 34,82% Severe / 65,18% Minor — gần 1/3 vụ nghiêm trọng, nhấn mạnh nhu cầu bảo vệ nạn nhân nữ, đặc biệt với các hành vi xâm hại và quấy rối.

Theft & Robbery (Trộm cắp – cướp giật): 81,96% Severe / 18,04% Minor — phần lớn vụ xâm phạm tài sản được đánh giá nghiêm trọng, cần lưu ý cách phân loại mức độ trong dataset.

Other + Small Groups (Khác + các nhóm nhỏ): 84,25% Minor / 15,75% Severe — đây là các nhóm còn lại, bao gồm các hành vi rải rác như đe dọa, xâm nhập, vi phạm dân sự… phần lớn ở mức độ nhẹ.

16. Bảng số vụ theo mức độ nghiêm trọng mỗi năm

tbl_severity_year <- crime %>%
  group_by(year, severity_label) %>%
  summarise(
    num_cases = n(),
    pct_cases = round(num_cases / sum(num_cases) * 100, 2),
    .groups = "drop"
  ) %>%
  tidyr::pivot_wider(
    names_from = severity_label,
    values_from = c(num_cases, pct_cases),
    names_sep = "_"
  )

print(tbl_severity_year)
## # A tibble: 4 × 5
##   year  num_cases_Minor num_cases_Severe pct_cases_Minor pct_cases_Severe
##   <chr>           <int>            <int>           <dbl>            <dbl>
## 1 2020            83455            90724             100              100
## 2 2021            85758            95560             100              100
## 3 2022            98907           105961             100              100
## 4 2023            91292           108915             100              100
Giải thích kỹ thuật:

group_by(year, severity_label): Gom dữ liệu theo năm (year) và mức độ nghiêm trọng.

summarise(num_cases = n(), ...): n() đếm số bản ghi trong mỗi nhóm → số vụ phạm tội tương ứng với từng năm và mức độ. .groups = "drop" loại bỏ thông tin nhóm sau khi tổng hợp, giúp kết quả là bảng phẳng để xử lý tiếp. pct_cases = round(num_cases / sum(num_cases) * 100, 2)

sum(num_cases) tính tổng số vụ trong năm đó.

round(..., 2) làm tròn 2 chữ số thập phân để dễ đọc.

tidyr::pivot_wider(...): Chuyển bảng từ dạng dài (long) sang dạng rộng (wide).

names_from = severity_label: dùng tên mức độ làm tên cột mới.

values_from = c(num_cases, pct_cases): lấy giá trị số vụ và tỷ lệ % điền vào các cột tương ứng.

names_sep = "_": thêm dấu gạch dưới giữa tiền tố và tên mức độ.

Ý nghĩa thống kê:

Số vụ “Minor” (nhẹ) tăng từ 83.455 vụ năm 2020 lên 98.907 vụ năm 2022, sau đó giảm xuống 91.292 vụ năm 2023. Số vụ “Severe” (nghiêm trọng) liên tục tăng từ 90.724 vụ năm 2020 lên 108.915 vụ năm 2023. Điều này phản ánh xu hướng tổng số vụ nhẹ giảm nhẹ, nhưng các vụ nghiêm trọng vẫn gia tăng.

Cả hai mức độ “Minor” và “Severe” đều được tính 100% trong dataset hiện tại, nghĩa là dữ liệu phân loại vụ nhẹ/nghiêm trọng đầy đủ, không có giá trị thiếu. Điều này giúp việc phân tích theo mức độ nghiêm trọng chính xác, nhưng lưu ý rằng tỷ lệ thực tế trong cộng đồng có thể khác do cách ghi nhãn dữ liệu.

Dữ liệu cho thấy xu hướng gia tăng các vụ nghiêm trọng qua các năm, trong khi số vụ nhẹ biến động không lớn. Điều này gợi ý rằng mặc dù tổng số vụ có thể ổn định hoặc giảm nhẹ, mức độ nghiêm trọng của tội phạm có dấu hiệu tăng, cần chú trọng vào các biện pháp phòng ngừa và bảo vệ cộng đồng.

17. Tỷ lệ vụ án theo tình trạng mỗi năm

crime %>%
  group_by(year, arrested) %>%
  summarise(tong_so_vu = n()) %>%
  group_by(year) %>%
  mutate(ty_le = round(tong_so_vu / sum(tong_so_vu) * 100, 2))
## `summarise()` has grouped output by 'year'. You can override using the
## `.groups` argument.
Giải thích kỹ thuật:

group_by(year, arrested): gom nhóm dữ liệu theo năm và tình trạng vụ án.

summarise(tong_so_vu = n()): đếm tổng số vụ trong từng nhóm.

group_by(year) và mutate(ty_le = round(tong_so_vu / sum(tong_so_vu) * 100, 2)): dùng để tính tỷ lệ phần trăm của từng loại tình trạng trong tổng số vụ mỗi năm, làm tròn 2 chữ số thập phân.

Ý nghĩa thống kê:

Qua các năm 2020–2023, số vụ được bắt giữ (Arrested) chiếm tỷ lệ thấp, khoảng 9–11% so với tổng số vụ, trong khi phần lớn vụ việc (Not arrested) không dẫn đến bắt giữ.

Tỷ lệ bắt giữ giảm dần từ 11,49% năm 2020 xuống 9,17% năm 2023, cho thấy khả năng truy bắt có xu hướng giảm so với quy mô tội phạm, có thể do tăng khối lượng vụ việc hoặc hạn chế về nguồn lực điều tra.

Dữ liệu phản ánh rằng mặc dù tổng số vụ có biến động (tăng giai đoạn 2020–2022, giảm nhẹ 2023), khả năng xử lý và bắt giữ vẫn chưa tăng, cho thấy cần tập trung cải thiện hiệu quả điều tra và phân bổ nguồn lực.

18. Tốc độ thay đổi tội phạm theo năm

crime %>%
  group_by(year) %>%
  summarise(tong_so_vu = n()) %>%
  mutate(tang_giam = round((tong_so_vu - lag(tong_so_vu)) / lag(tong_so_vu) * 100, 2))
Giải thích kỹ thuật:

group_by(year): nhóm dữ liệu theo từng năm.

summarise(n()): đếm tổng số vụ tội phạm từng năm.

mutate(...): tính tỷ lệ tăng/giảm so với năm trước bằng công thức chênh lệch phần trăm.

lag() dùng để lấy giá trị của năm liền kề trước, giúp tính biến động năm–năm.

Ý nghĩa thống kê:

Bảng cho thấy xu hướng biến động tổng số vụ phạm tội tại Los Angeles giai đoạn 2020–2023:

Năm 2020 là năm khởi đầu chuỗi dữ liệu, nên không có giá trị so sánh.

Năm 2021, tổng số vụ tăng 4,10% so với năm 2020, phản ánh giai đoạn thành phố bắt đầu mở cửa trở lại sau các biện pháp giãn cách, dẫn đến hoạt động xã hội và nguy cơ phạm pháp gia tăng.

Năm 2022, mức tăng đạt 12,99%, là mức tăng cao nhất trong toàn kỳ, cho thấy sự bùng phát rõ rệt của tội phạm — có thể do áp lực kinh tế, thất nghiệp và các yếu tố hậu đại dịch.

Năm 2023, tổng số vụ giảm nhẹ 2,28%, đánh dấu tín hiệu ổn định trong an ninh trật tự, có khả năng phản ánh hiệu quả của các chính sách kiểm soát tội phạm hoặc tăng cường tuần tra của LAPD.

Nhìn chung, giai đoạn 2020–2022 chứng kiến xu hướng gia tăng mạnh, sau đó có dấu hiệu hạ nhiệt vào năm 2023 — cho thấy mối liên hệ giữa bối cảnh xã hội và sự biến động của hoạt động phạm tội.

19. Trung bình độ tuổi nạn nhân theo giới và loại tội

crime %>%
  group_by(crime_group, sex) %>%
  summarise(tuoi_trung_binh = round(mean(age, na.rm = TRUE), 1))
## `summarise()` has grouped output by 'crime_group'. You can override using the
## `.groups` argument.
Giải thích kỹ thuật:

group_by(crime_group, sex): kết hợp hai biến phân loại.

mean(age, na.rm = TRUE): tính tuổi trung bình, loại bỏ giá trị thiếu.

Ý nghĩa thống kê:

Ở nhóm bạo lực – hành hung, nạn nhân nam (≈37,8 tuổi) và nữ (≈35,8 tuổi) chiếm đa số, cho thấy tội phạm này thường xảy ra trong nhóm người trưởng thành đang ở độ tuổi lao động, nơi xung đột xã hội và mâu thuẫn cá nhân dễ phát sinh.

Với gian lận – tài chính, nạn nhân nữ có tuổi trung bình cao nhất (≈52,1 tuổi), cao hơn nam giới (≈42,5 tuổi). Điều này hợp lý vì tội phạm tài chính thường nhắm tới người có tài sản, kinh nghiệm và khả năng tài chính – nhóm tuổi trung niên trở lên.

Giết người cho thấy tuổi nạn nhân nam và nữ tương đối tương đồng (≈36–38 tuổi), phản ánh đặc trưng tội phạm mang tính cá nhân hoặc mâu thuẫn xã hội.

Nhóm trộm cắp – cướp giật có tuổi trung bình nạn nhân nam (≈37,8) và nữ (≈40,7), vẫn nằm trong độ tuổi lao động – nhóm thường xuyên di chuyển và mang theo tài sản, nên dễ trở thành mục tiêu.

Tình dục – quấy rối có nạn nhân nữ trẻ hơn (≈30,1 tuổi), thấp hơn đáng kể so với nam (≈34,1 tuổi), cho thấy nhóm nữ thanh niên là đối tượng chịu rủi ro cao nhất trong tội phạm mang yếu tố giới.

Các nhóm ma túy, phóng hỏa và khác có tuổi trung bình biến động lớn, phần “không xác định” xuất hiện với giá trị thấp (≈2–3 tuổi) chủ yếu do lỗi ghi nhận hoặc trường hợp nạn nhân trẻ em, bào thai hoặc dữ liệu không đầy đủ.

Tổng thể, bảng chỉ ra rằng tội phạm tác động mạnh nhất đến nhóm tuổi lao động (30–45 tuổi), với sự khác biệt rõ giữa giới tính ở từng loại tội. Nữ giới thường chịu thiệt hại nhiều hơn trong các tội liên quan đến tình dục và tài chính, trong khi nam giới nổi bật ở các tội bạo lực và cướp giật

20. Phân hạng rủi ro khu vực theo tỷ lệ nghiêm trọng

library(dplyr)
crime %>%
  group_by(area) %>%
  summarise(
    total_cases = n(),
    severe_pct = round(mean(severity_label == "Severe") * 100, 2),
    .groups = "drop"
  ) %>%
  mutate(risk_level = case_when(
    severe_pct > 55 ~ "Rất cao",
    severe_pct > 45 ~ "Cao",
    TRUE ~ "Trung bình - thấp"
  ))
Giải thích kỹ thuật:

group_by(area): nhóm dữ liệu theo khu vực.

summarise(...): tính số vụ và tỷ lệ vụ nghiêm trọng (Severe).

mutate(risk_level = case_when(...)): phân loại mức rủi ro dựa trên tỷ lệ vụ nghiêm trọng.

round(..., 2): làm tròn 2 chữ số thập phân.

Ý nghĩa thống kê:

Kết quả cho thấy hầu hết các khu vực ở Los Angeles đều thuộc nhóm “Cao” về tỷ lệ vụ nghiêm trọng, phản ánh mức độ tội phạm nghiêm trọng phổ biến trên diện rộng. Một số khu vực nổi bật với mức “Rất cao”, gồm Central (59,32%), Pacific (60,97%), West LA (57,76%) và Wilshire (57,66%), đều là các khu trung tâm, ven biển hoặc tập trung thương mại – môi trường đông dân cư và du khách, dẫn đến nguy cơ tội phạm xâm phạm tài sản và bạo lực cao hơn.

Các khu khác như Hollywood (55,0%), N Hollywood (53,5%) và Devonshire (53,0%), dù chỉ ở mức “Cao”, nhưng tỷ lệ nghiêm trọng trên 50% vẫn cho thấy nguy cơ tổn hại không nhỏ.

Trong khi đó, các khu Foothill (45,98%), Mission (45,85%) và Southeast (47,02%) nằm sát ngưỡng “Trung bình – Cao”, cho thấy môi trường tội phạm được kiểm soát tương đối tốt, nhưng vẫn tiềm ẩn rủi ro.

Tổng thể, bảng thể hiện rõ mức độ chênh lệch về an ninh đô thị, với khu trung tâm và kinh tế sôi động chịu nhiều tội phạm nghiêm trọng hơn, còn vùng ngoại vi hoặc dân cư ổn định có rủi ro thấp hơn.

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

1. Top 10 khu vực có số vụ phạm tội cao

library(dplyr)
library(ggplot2)
crime %>%
group_by(area) %>%
summarise(total_cases = n()) %>%
arrange(desc(total_cases)) %>%
slice_head(n=10) %>%
ggplot(aes(x=reorder(area, -total_cases), y=total_cases)) +
geom_col(fill='#5A9F68') +
geom_text(aes(label=total_cases), vjust=-0.5) +
labs(x='Khu vực', y='Số vụ', title ='Top 10 khu vực có số vụ nhiều nhất') +
theme(plot.title = element_text(hjust = 0.5))

Giải thích kỹ thuật:

library(dplyr) / library(ggplot2): Nạp hai package dplyr (xử lý dữ liệu) và ggplot2 (vẽ biểu đồ).

crime %>%: Bắt đầu một chuỗi thao tác (pipe) — truyền crime vào các hàm tiếp theo theo thứ tự.

group_by(area): Gom các bản ghi theo biến area (mỗi nhóm tương ứng một khu vực). 

summarise(total_cases = n()): Tạo một bảng tổng hợp mới với một cột total_cases là kết quả đếm (n()) số bản ghi trong mỗi nhóm area. Kết quả là dữ liệu dạng (area, total_cases).

arrange(desc(total_cases)): Sắp xếp bảng tổng hợp giảm dần theo total_cases — các khu vực có số vụ lớn nhất lên trước.

slice_head(n=10): Lấy 10 hàng đầu tiên theo nhóm arrange.

ggplot(aes(x = reorder(area, -total_cases), y = total_cases)): Khởi tạo đồ họa: ánh xạ x là area, y là total_cases.

reorder(area, -total_cases) sắp xếp các nhãn area theo thứ tự giảm dần của total_cases khi vẽ (dấu - là giảm dần). Việc này đảm bảo cột cao nhất nằm bên trái khi biểu đồ đứng.

geom_col(fill=' '): Vẽ cột (bar chart) với chiều cao theo y và tô màu đỏ cho các cột. 

geom_text(aes(label = total_cases), vjust = -0.5): Thêm nhãn số lên trên mỗi cột, hiển thị giá trị total_cases. **vjust = -0.5** dịch nhãn cao hơn một chút so với đỉnh cột để không chồng lên cột.

labs(x='Khu vực', y='Số vụ', title ='Top 10 khu vực có số vụ nhiều nhất'): Thiết lập nhãn trục và tiêu đề biểu đồ.

theme(plot.title = element_text(hjust = 0.5)): Căn giữa tiêu đề bằng thuộc tính hjust = 0.5.  

Ý nghĩa thống kê:

Tổng số vụ ở Top 10: 417.784 vụ.

Central chiếm 54.856 vụ (≈13.13%) cao một cách rõ rệt so với khu vực đứng sau.

Các vị trí tiếp theo bao gồm 77th Street 46.669 vụ (≈11.17%), Pacific 43.834 vụ (≈10.49%), Southwest: 42.286 vụ (≈10.12%), Hollywood 42.089 vụ (≈10.07%)

Nhóm giữa (N Hollywood, Olympic, Wilshire, Southeast, West LA) mỗi khu vực chiếm khoảng 8.7%–9.2%.

Nhìn chung, 5 khu vực hàng đầu (Central → Hollywood) chiếm ~55% của tổng Top 10 (cộng lại khoảng 55% của 417k) — tức là số vụ tập trung nhiều vào các khu vực trung tâm thượng mại, giải trí, nơi có mật độ dân số cao.

2. Thống kê mức độ phạm tội theo nhóm tuổi

crime %>% 
  group_by(age_group, severity_label) %>% 
  summarise(total_cases = n(), .groups = "drop") %>% 
  ggplot(aes(x = age_group, y = total_cases, fill = severity_label)) +
  geom_col(position = "dodge", width = 0.8) +
  geom_text(aes(label = total_cases), 
            position = position_dodge(width = 0.8), 
            vjust = -0.5, size = 3, color = "black") +
  scale_fill_manual(values = c("Severe" = "#ff8fab","Minor" = "#6a994e")) +        
  labs(x = "Nhóm tuổi", 
       y = "Số vụ", 
       fill = "Mức độ phạm tội",
       title = "Thống kê mức độ phạm tội theo nhóm tuổi") +
  theme_minimal(base_size = 13) +
  theme(plot.title = element_text(hjust = 0.5, face = "bold", color = "#3b3b3b"),
        legend.position = "right",
        legend.title = element_text(face = "bold"))

Giải thích kỹ thuật:

group_by(age_group, severity_label): gom dữ liệu theo 2 biến nhóm tuổi và nhãn mức độ phạm tội. Kết quả tóm tắt sẽ tính trên từng tổ hợp (age_group, severity_label).

summarise(total_cases = n(), .groups = "drop"): tạo cột total_cases là số quan sát trong mỗi tổ hợp; .groups = "drop" loại bỏ grouping metadata sau khi summarise (tránh cảnh báo của dplyr mới).

aes(...) định nghĩa ánh xạ: trục x là age_group, trục y là total_cases, màu (fill) theo severity_label.geom_col() vẽ cột, sử dụng giá trị y trực tiếp từ total_cases.

position = "dodge": đặt các cột của cùng một age_group nhưng khác severity_label sát cạnh nhau (không xếp chồng). width = 0.8: bề rộng cột. vjust = -0.5: dịch nhãn lên phía trên đỉnh cột một chút (âm → lên trên). size = 3: kích thước chữ. color = "black": màu chữ.

base_size = 13: kích thước font cơ bản.

plot.title = element_text(hjust = 0.5, ...): căn giữa tiêu đề, bôi đậm, đổi màu.

legend.position = "right": đưa legend (chú giải) ra bên phải biểu đồ.

legend.title = element_text(face = "bold"): in đậm tiêu đề legend

Ý nghĩa thống kê:

Nhóm “Thanh niên” có số vụ phạm tội cao nhất ở cả hai mức độ: nghiêm trọng (~184.612 vụ) và nhẹ (~162.413 vụ), cho thấy đây là độ tuổi tham gia và bị ảnh hưởng nhiều nhất trong các vụ án. Tiếp đến là “Trung niên”, với hơn 100.000 vụ ở mỗi mức độ – phản ánh tỷ lệ phạm tội vẫn đáng kể ở nhóm tuổi này, có thể do áp lực xã hội, kinh tế hoặc nghề nghiệp.

Trong khi đó, hai nhóm “Vị thành niên” và “Cao tuổi” có số vụ thấp hơn rõ rệt, chứng tỏ hành vi phạm tội hoặc khả năng trở thành nạn nhân giảm dần ở hai đầu độ tuổi.

Đáng chú ý, ở hầu hết các nhóm tuổi, số vụ nghiêm trọng thường cao hơn hoặc tương đương số vụ nhẹ, cho thấy tội phạm tại khu vực có xu hướng nghiêm trọng hóa hành vi phạm pháp.

Từ góc độ thống kê, biểu đồ phản ánh mối quan hệ rõ ràng giữa độ tuổi và mức độ nghiêm trọng của tội phạm, giúp xác định nhóm tuổi trọng điểm cần được can thiệp. Đặc biệt, “Thanh niên” là nhóm rủi ro cao nhất, cần được chú trọng trong các chương trình giáo dục pháp luật, định hướng nghề nghiệp, quản lý xã hội và hỗ trợ tâm lý, nhằm giảm thiểu khả năng phạm tội và tái phạm.

3. Số vụ theo nhóm tuổi qua các năm

crime %>%
  group_by(year, age_group) %>%
  summarise(total_cases = n(), .groups = "drop") %>%
  ggplot(aes(x = age_group, y = total_cases, fill = age_group)) +
  geom_col() +
  facet_wrap(~year) +
  labs(x = 'Nhóm tuổi', y = 'Số vụ', title = 'Số vụ theo nhóm tuổi qua các năm') +
  theme(axis.text.x = element_blank()) + 
  theme(plot.title = element_text(hjust = 0.5))

Giải thích kỹ thuật:

group_by(year, age_group): gom theo năm và nhóm tuổi để tính tổng số vụ cho mỗi tổ hợp.

summarise(total_cases = n(), .groups = "drop"): đếm số vụ và bỏ thông tin nhóm để tránh cảnh báo.

ggplot(aes(x = age_group, y = total_cases, fill = age_group)): ánh xạ trục x là nhóm tuổi, trục y là tổng số vụ; fill để tô màu phân biệt nhóm tuổi.

geom_col(): vẽ cột từ dữ liệu đã tóm tắt.

facet_wrap(~year): tách biểu đồ theo từng năm, thuận tiện so sánh theo thời gian.

theme(axis.text.x = element_blank()): ẩn nhãn trục x.

labs(...) và theme(plot.title = element_text(hjust = 0.5)): đặt nhãn trục/tiêu đề và căn giữa tiêu đề.

Ý nghĩa thống kê:

Nhìn chung, tổng số vụ thay đổi nhẹ theo từng năm, nhưng cơ cấu theo nhóm tuổi hầu như không thay đổi. Ở tất cả các năm, nhóm thanh niên luôn chiếm tỷ lệ nạn nhân cao nhất, dao động từ khoảng 90.000 đến hơn 100.000 vụ mỗi năm.

Nhóm trung niên đứng thứ hai với khoảng 50.000–60.000 vụ mỗi năm, cho thấy sự ổn định và ít biến động. Nhóm vị thành niên dao động quanh mức 30.000 vụ, thể hiện rủi ro đáng kể đối với trẻ em và thiếu niên, dù thấp hơn thanh niên nhưng vẫn cần được quan tâm đặc biệt.

Nhóm cao tuổi luôn có số vụ thấp nhất và gần như không thay đổi nhiều qua các năm (~20.000 vụ), điều này phản ánh việc người cao tuổi ít ra ngoài hoặc có lối sống ít rủi ro hơn, song vẫn là nhóm dễ bị tổn thương trong các tội phạm như lừa đảo hoặc bạo hành gia đình.

4. Cấu trúc giới tính trong từng nhóm tội phạm

crime %>%
  group_by(crime_group, sex) %>%
  summarise(total_cases = n(), .groups = "drop") %>%
  group_by(crime_group) %>%
  mutate(pct = total_cases / sum(total_cases) * 100) %>%
  ggplot(aes(x = reorder(crime_group, -pct), y = pct, fill = sex)) +
  geom_bar(stat = "identity", position = "stack", width = 0.7) +
  geom_text(aes(label = paste0(round(pct, 1), "%")),
            position = position_stack(vjust = 0.5), size = 3, color = "black") +
  scale_fill_brewer(palette = "Set4") +
  labs(
    title = "Cơ cấu giới tính trong từng nhóm tội phạm",
    x = "Nhóm tội phạm", y = "Tỷ lệ (%)", fill = "Giới tính"
  ) +
  theme_minimal(base_size = 13) +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold"),
    axis.text.x = element_text(angle = 45, hjust = 1)
  )
## Warning: Unknown palette: "Set4"

Giải thích kỹ thuật:

group_by(crime_group, sex): Gom dữ liệu theo 2 biến phân loại nhóm tội phạm và giới tính.

summarise(total_cases = n(), .groups = "drop"): Tạo bảng tóm tắt total_cases là số quan sát trong mỗi tổ hợp. .groups = "drop" loại bỏ metadata về grouping sau khi summarise, tránh cảnh báo và hành vi không mong muốn.

group_by(crime_group): Gom lại theo crime_group để tính tỷ lệ nội bộ trong từng nhóm tội phạm.

mutate(pct = total_cases / sum(total_cases) * 100): Tạo cột pct là tỷ lệ phần trăm của total_cases trong mỗi crime_group. sum(total_cases) tính tổng cho mỗi nhóm tội phạm, nên pct là tỷ lệ nội bộ (cộng lại = 100% cho mỗi crime_group).

ggplot(aes(x = reorder(crime_group, -pct), y = pct, fill = sex)): aes(...) ánh xạ thẩm mỹ trục x là crime_group (đã được reorder theo -pct), trục y là pct, màu cột theo sex.

geom_bar(stat = "identity", position = "stack", width = 0.7): stat = "identity": dùng giá trị y (pct) có sẵn (không đếm lại). position = "stack": các thanh theo sex được xếp chồng để cột cuối cùng thể hiện tổng 100% (tuy ở đây y đã là tỷ lệ, nên tổng ~100%). width = 0.7: điều chỉnh bề rộng cột.

geom_text(aes(label = paste0(round(pct, 1), "%")), position = position_stack(vjust = 0.5), size = 3, color = "black"). Thêm nhãn phần trăm đặt ở giữa mỗi segment nhờ position_stack(vjust = 0.5). round(pct, 1) làm tròn 1 chữ số thập phân; paste0(..., "%") thêm ký tự phần trăm.

scale_fill_brewer(palette = "Set3")

labs(...): đặt tiêu đề, nhãn trục, tên chú giải.

theme_minimal(base_size = 13) + theme(...): thiết lập giao diện và bố cục; xoay nhãn x bằng angle = 45 để tránh chồng chữ.

Ý nghĩa thống kê:

Biểu đồ cho thấy giới tính có ảnh hưởng rõ rệt đến đặc điểm tội phạm. Trong nhóm tội giết người, nam giới chiếm tỷ lệ áp đảo (≈ 86,5%), cho thấy các hành vi phạm tội nghiêm trọng chủ yếu do nam giới thực hiện. Các nhóm “bạo lực – hành hung” và “trộm cắp – cướp giật” có phân bố tương đối cân bằng hơn, nhưng nam giới vẫn chiếm ưu thế (≈ 47–50%), phản ánh tính đặc trưng của các tội phạm sử dụng vũ lực.

Ngược lại, nhóm “tình dục – quấy rối” có tỷ lệ nữ giới rất cao (≈ 74%), cho thấy nữ giới thường là nạn nhân chính trong loại tội phạm này. “Gian lận – tài chính” có cơ cấu giới tương đối đồng đều (nam ~47%, nữ ~33%, không xác định ~19%), thể hiện đây là loại tội phạm không mang đặc trưng giới tính mạnh, thường liên quan đến hành vi trí tuệ hoặc vị trí nghề nghiệp. Nhóm “ma túy” và “khác” lại ghi nhận tỷ lệ giới tính không xác định khá cao (≈ 15–36%), có thể do thiếu dữ liệu hoặc trường hợp đặc thù chưa được phân loại rõ.

Nhìn chung, nam giới chiếm đa số trong các tội phạm mang tính bạo lực, xung động và thể chất, trong khi nữ giới thường là nạn nhân trong các vụ án liên quan đến tình dục. Điều này cho thấy cần tăng cường các biện pháp bảo vệ, giáo dục giới tính và can thiệp sớm, đồng thời chuẩn hóa dữ liệu về giới tính để phục vụ công tác thống kê và hoạch định chính sách. Việc phân tích cơ cấu giới tính giúp nhận diện nhóm có nguy cơ cao và định hướng chiến lược phòng ngừa – hỗ trợ xã hội hiệu quả hơn.

5. Giới tính nạn nhân ở các khu vực phạm tội cao

top_areas <- crime %>%
  group_by(area) %>%
  summarise(total_cases = n()) %>%
  arrange(desc(total_cases)) %>%
  slice_head(n = 5)
crime_top_areas <- crime %>%
  filter(area %in% top_areas$area, !is.na(area), !is.na(sex))
crime_top_areas %>%
  group_by(area, sex) %>%
  summarise(total = n(), .groups = "drop") %>%
  ggplot(aes(x = sex, y = reorder(area, -total), fill = total)) +
  geom_tile(color = "white") +
  geom_text(aes(label = total), color = "red", size = 3) +
  scale_fill_gradient(low = "#d8f3dc", high = "#1b4332") +
  labs(
    title = "Mức độ phạm tội theo giới tính và khu vực",
    x = "Giới tính", y = "Khu vực", fill = "Số vụ"
  ) +
  theme_minimal(base_size = 13) +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"))

Giải thích kỹ thuật:

group_by(area) %>% summarise(total_cases = n()): Gom nhóm theo khu vực, đếm số vụ.

arrange(desc(total_cases)) %>% slice_head(n = 5): Lấy 5 khu vực có nhiều vụ nhất.

filter(area %in% top_areas$area, !is.na(area), !is.na(sex)): Giữ lại dữ liệu hợp lệ của top 5 khu vực.

group_by(area, sex) %>% summarise(total = n()): Đếm số vụ theo giới tính trong từng khu vực.

ggplot(aes(x = sex, y = reorder(area, -total), fill = total)): Thiết lập trục và thang màu.

geom_tile(): Vẽ các ô thể hiện mật độ vụ án.

geom_text(aes(label = total)): Hiển thị số vụ trên từng ô.

scale_fill_gradient(): Tô màu từ nhạt đến đậm theo số vụ.

labs() + theme_minimal(): Đặt tiêu đề, nhãn trục, giao diện đơn giản.

Ý nghĩa thống kê:

Nhìn chung, nạn nhân nam chiếm tỷ lệ cao hơn đáng kể so với nữ ở hầu hết các khu vực, đặc biệt là tại Central – nơi ghi nhận gần 30.000 vụ với nạn nhân là nam, cao nhất trong 5 khu vực.

Các khu vực như 77th Street và Southwest lại có số vụ nạn nhân nữ tương đối cao, cho thấy mức độ tội phạm nhắm vào phụ nữ vẫn đáng lưu ý.

Số vụ có giới tính không xác định tuy chiếm tỷ lệ nhỏ, nhưng vẫn xuất hiện ở tất cả các khu vực.

6. So sánh số lượng giới tính nạn nhân qua các năm

crime %>%
  group_by(year, sex) %>%
  summarise(total_cases = n(), .groups = "drop") %>%
  ggplot(aes(x = factor(year), y = total_cases, fill = sex)) +
  geom_col(position = "dodge") +  
  geom_text(aes(label = total_cases), 
            position = position_dodge(width = 1), vjust = -0.5, size = 3) +
  labs(
    title = "So sánh số lượng nạn nhân qua các năm",
    x = "Năm",
    y = "Số vụ",
    fill = "Giới tính"
  ) +
  theme_minimal()

Giải thích kỹ thuật:

group_by(year, sex): Gom các bản ghi theo hai biến phân loại: year và sex.

summarise(total_cases = n(), .groups = "drop"): Tạo một bảng tóm tắt mới có cột total_cases là số lượng bản ghi (số vụ) trong mỗi nhóm. .groups = "drop" loại bỏ metadata grouping sau khi summarise (tránh cảnh báo và hành vi giữ group không mong muốn).

ggplot(aes(x = factor(year), y = total_cases, fill = sex)): ánh xạ thẩm mỹ (aesthetic mapping) cho biểu đồ: x = factor(year): biến năm được ép về factor (mỗi năm là 1 cột riêng, giữ thứ tự rời rạc). y = total_cases: chiều cao cột là tổng số vụ. fill = sex: màu tô cột theo giá trị sex (phân biệt giới).

geom_col(position = "dodge"): Vẽ cột dựa trên giá trị y đã được tóm tắt. position = "dodge" đặt các cột của các sex khác nhau trong cùng một year nằm cạnh nhau (không chồng lên).

geom_text(aes(label = total_cases), position = position_dodge(width = 1), vjust = -0.5, size = 3): Thêm nhãn số (số vụ) trên/ngoài đầu mỗi cột. position = position_dodge(width = 1) đảm bảo nhãn căn trùng vị trí với cột tương ứng (dùng cùng kiểu dodge). vjust = -0.5 dịch nhãn lên trên đỉnh cột (tránh chồng lên cột). size = 3 kích thước chữ.

labs(...): Đặt tiêu đề và nhãn cho trục, cũng như tên chú giải (fill = "Giới tính").

theme_minimal(): Chọn theme tối giản cho biểu đồ (gọn, phù hợp báo cáo). Bạn có thể thêm theme(plot.title = element_text(hjust = 0.5)) để căn giữa tiêu đề.

Ý nghĩa thống kê:

Nam giới luôn là nhóm nạn nhân chiếm tỷ lệ cao nhất mỗi năm, dao động từ khoảng 84.000 đến 95.000 vụ.

Nữ giới cũng chiếm tỷ trọng lớn, chỉ thấp hơn nam một chút, đặc biệt trong năm 2022 khi số vụ gần tương đương.

Giới tính không xác định có số vụ thấp hơn nhiều, nhưng lại tăng đều qua các năm, từ khoảng 16.000 vụ (2020) lên gần 26.000 vụ (2023).

Tổng thể, số vụ phạm tội nhắm vào cá nhân đều tăng nhẹ theo thời gian, thể hiện xu hướng cần chú ý về an ninh cộng đồng và bảo vệ cá nhân trong giai đoạn gần đây.

7. Severity theo khu vực

plot7 <- crime %>%
group_by(area, severity) %>%
summarise(total=n()) %>%
ggplot(aes(x=area, y=total, fill=severity)) +
geom_bar(stat="identity") +
coord_flip() +
theme_minimal() +
labs(title="Phân bố mức độ nghiêm trọng theo khu vực", x="Khu vực", y="Số vụ")
## `summarise()` has grouped output by 'area'. You can override using the
## `.groups` argument.
plot7

Giải thích kỹ thuật:

group_by(area, severity): Gom các bản ghi theo hai biến phân loại khu vực (area) và mức độ nghiêm trọng (severity).

summarise(total = n(), .groups = "drop"): Tạo bảng tóm tắt với cột total là số vụ trong mỗi nhóm. Tham số .groups = "drop" bỏ thuộc tính group sau khi tổng hợp để tránh hành vi giữ group ngoài ý muốn.

ggplot(aes(x = area, y = total, fill = severity)): Thiết lập ánh xạ thẩm mỹ:

x = area: từng khu vực là một hàng trên trục tung (sẽ đảo trục ở bước sau).

y = total: chiều dài cột biểu diễn số vụ.

fill = severity: màu thể hiện mức độ nghiêm trọng.

geom_bar(stat = "identity"): Vẽ cột theo số liệu có sẵn (giá trị total đã tính từ summarise), mặc định xếp chồng theo severity để thấy cơ cấu mức độ trong mỗi khu vực.

coord_flip(): Đảo trục (khu vực sang trục dọc, số vụ sang trục ngang) giúp đọc tên khu vực dễ hơn và so sánh chiều dài cột trực quan hơn khi danh sách khu vực dài.

theme_minimal(): Áp dụng giao diện tối giản, giảm đường lưới/chi tiết thừa để tập trung vào cột số liệu.

labs(title = ..., x = ..., y = ...): Đặt tiêu đề và nhãn trục bằng tiếng Việt, đảm bảo chuẩn báo cáo.

Ý nghĩa thống kê:

Biểu đồ thể hiện mức độ nghiêm trọng của tội phạm theo từng khu vực. Dễ thấy các khu vực như Central, 77th Street, và Hollywood có tổng số vụ phạm tội cao nhất, phản ánh mật độ dân cư lớn và tần suất hoạt động tội phạm cao.

Ngược lại, Topanga, Pacific và Wilshire có tổng số vụ thấp hơn, cho thấy môi trường an ninh tương đối ổn định.

Dải màu xanh nhạt hơn tương ứng với mức độ nghiêm trọng cao hơn, cho thấy ở nhiều khu vực có tỷ lệ lớn các vụ nghiêm trọng (ví dụ Central và 77th Street vừa có nhiều vụ, vừa có mức độ nghiêm trọng cao). Điều này gợi ý mối quan hệ giữa quy mô tội phạm và mức độ nghiêm trọng, đồng thời giúp cơ quan chức năng ưu tiên phân bổ nguồn lực kiểm soát tội phạm cho các khu vực có nguy cơ cao hơn.

8. Tỷ lệ vụ án đã giải quyết xong

library(dplyr)
library(ggplot2)
crime %>%
  group_by(area) %>%
  summarise(
    total_cases = n(),
    arrested_cases = sum(arrested == "Arrested"),
    arrest_rate = arrested_cases / total_cases * 100
  ) %>%
  ggplot(aes(x = reorder(area, arrest_rate), y = arrest_rate)) +
  geom_col(fill = "tomato") +
  geom_text(aes(label = paste0(round(arrest_rate,1), "%")),
            position = position_stack(vjust = 0),  
            hjust = -0.1, 
            size = 3) +
  coord_flip() +
  labs(title="Tỷ lệ vụ án đã giải quyết theo khu vực", x="Khu vực", y="Tỷ lệ (%)") +
  theme_minimal()

Giải thích kỹ thuật:

group_by(area): gom nhóm dữ liệu theo khu vực phạm tội.

summarise(...): tạo bảng tóm tắt cho từng khu vực gồm:

total_cases = n(): tổng số vụ án trong khu vực đó.

arrested_cases = sum(arrested == "đã bắt giữ"): đếm số vụ đã bắt giữ (điều kiện logic trả về TRUE/FALSE → TRUE = 1).

arrest_rate = arrested_cases / total_cases * 100: tính tỷ lệ phần trăm vụ án đã được giải quyết.reorder(area, arrest_rate): sắp xếp các khu vực theo tỷ lệ vụ án giải quyết tăng dần, giúp biểu đồ dễ đọc hơn.

geom_text(): thêm nhãn số liệu vào cột.

label = paste0(round(arrest_rate,1), "%"): làm tròn 1 chữ số thập phân, hiển thị kèm dấu %.

position_stack(vjust = 0): canh vị trí nhãn ở đỉnh cột.

hjust = -0.1: đẩy nhãn ra ngoài một chút để dễ đọc.

coord_flip(): Lật biểu đồ nằm ngang, giúp dễ đọc tên khu vực khi danh sách dài.labs(): đặt tiêu đề và nhãn trục.

theme_minimal(): giao diện tối giản, giúp biểu đồ sạch và hiện đại.

Ý nghĩa thống kê:

Tỷ lệ vụ án được giải quyết có sự khác biệt rõ rệt giữa các khu vực.

Harbor (14.7%), Mission (14.4%) và Foothill (13.3%) đạt tỷ lệ cao nhất, cho thấy công tác điều tra và xử lý tội phạm tại đây tương đối hiệu quả.

Ngược lại, Central (7.0%), West LA (7.1%) và Pacific (7.5%) có tỷ lệ thấp hơn, phản ánh những khó khăn do mật độ dân cư cao và tính chất tội phạm phức tạp. Sự chênh lệch gần gấp đôi giữa khu vực cao nhất và thấp nhất cho thấy hiệu quả điều tra chưa đồng đều.

9. Mối quan hệ giữa giới tính và mức độ phạm tội

crime %>% count(area, severity_label) -> d
ggplot(d, aes(x=reorder(area, -n), y=n, fill=severity_label)) +
  geom_col(color='white') +                                        
  geom_text(aes(label=n), position=position_stack(vjust=0.5), size=3) +
  scale_fill_manual(values=c("#a8ddb5", "#43a2ca", "#0868ac")[1:length(unique(d$severity_label))]) +
  coord_flip() +
  labs(title='Số vụ theo khu vực & mức độ', x='Khu vực', y='Số vụ') +
  theme_light()

Giải thích kỹ thuật:

count(area, severity_label) = group_by(area, severity_label) %>% summarise(n = n()). Kết quả d là bảng dạng long có 1 hàng cho mỗi tổ hợp area × severity_label và cột n = số vụ

aes(x = reorder(area, -n), y = n, fill = severity_label): reorder(area, -n) sắp xếp nhãn area theo giá trị n của hàng hiện tại.

geom_col(color = 'white'): vẽ cột; color='white' vẽ viền trắng giữa các segment.

geom_text(..., position = position_stack(vjust = 0.5)): thêm nhãn số n đặt ở giữa mỗi segment xếp chồng (stack).

scale_fill_manual(...): gán màu cho các nhãn severity_label. Ở đây bạn chọn 3 màu và lấy số màu đúng bằng số mức độ hiện có. Chú ý: cần đảm bảo tên/level của severity_label khớp với thứ tự màu hoặc đặt named vector.

coord_flip(): lật trục để có biểu đồ ngang (dễ đọc tên khu vực dài).

labs(...), theme_light(): đặt tiêu đề, nhãn trục, dùng theme.

Ý nghĩa thống kê:

Biểu đồ thể hiện tổng số vụ phạm tội tại từng khu vực cùng với phân loại mức độ nghiêm trọng (Severe – nghiêm trọng, Minor – nhẹ). Dữ liệu được sắp xếp theo thứ tự giảm dần giúp quan sát rõ ràng sự khác biệt giữa các khu vực về quy mô và tính chất tội phạm.

Có thể thấy, khu vực Central, 77th Street và Pacific là ba địa bàn có số vụ phạm tội cao nhất, vượt xa các khu vực còn lại. Trong đó, phần màu xanh đậm đại diện cho các vụ nghiêm trọng (Severe) chiếm tỷ trọng đáng kể, cho thấy tần suất các hành vi bạo lực, xâm hại hoặc tội phạm nguy hiểm cao hơn mức trung bình toàn thành phố. Điều này phản ánh các khu vực này có mức độ rủi ro an ninh cao, cần được ưu tiên trong công tác phòng chống tội phạm.

Ngược lại, các khu vực như Foothill, Hollenbeck và Mission có số vụ thấp hơn đáng kể, thể hiện tình hình an ninh tương đối ổn định. Ở nhóm này, phần màu xanh nhạt (Minor) chiếm tỷ lệ lớn hơn, chứng tỏ đa phần là các vụ việc nhẹ, ít tính bạo lực hoặc thiệt hại.

Sự chênh lệch rõ rệt giữa các khu vực cho thấy sự phân bố tội phạm không đồng đều về mặt không gian. Các vùng trung tâm và phía nam có xu hướng tập trung nhiều vụ nghiêm trọng hơn, trong khi vùng ngoại ô phía bắc duy trì mức thấp hơn. Điều này có thể liên quan đến mật độ dân cư, kinh tế, tình hình xã hội hoặc hiệu quả quản lý địa phương.

Từ góc độ thống kê, biểu đồ giúp xác định các “điểm nóng” tội phạm, hỗ trợ việc phân tích nguyên nhân tiềm ẩn và hoạch định chính sách an ninh phù hợp. Đồng thời, việc hiển thị trực quan bằng tỷ lệ phần trăm và số lượng cụ thể giúp so sánh định lượng rõ ràng giữa các khu vực, đảm bảo cơ sở cho các đánh giá thực chứng trong phần phân tích chuyên sâu tiếp theo.

10. Top 5 loại tội phạm phổ biến nhất

top5_loaitoi <- crime %>%
  count(crime_group, sort = TRUE) %>%
  slice_head(n = 10) %>%
  pull(crime_group)

crime_top5 <- crime %>%
  filter(crime_group %in% top5_loaitoi)

ggplot(crime_top5, aes(x = factor(year), fill = sex)) +
  geom_bar(position = "fill") + 
  facet_wrap(~crime_group) + 
  scale_y_continuous(labels = scales::percent) + 
  labs(
    title = "Cơ cấu giới tính theo năm cho Top 5 nhóm tội phạm",
    x = "Năm",
    y = "Tỷ lệ (%)"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold"),
    strip.text = element_text(size = 9)
  )

Giải thích kỹ thuật:

count(crime_group, sort = TRUE): đếm số vụ theo nhóm tội phạm (crime_group) và sắp giảm dần.

slice_head(n = 10): chọn 10 nhóm tội nhiều nhất.

filter(crime_group %in% top5_loaitoi): lọc dữ liệu chỉ giữ các nhóm này.

ggplot(aes(x = factor(year), fill = sex)): ánh xạ năm vào trục X, màu cột theo giới tính.

geom_bar(position = "fill"): tạo biểu đồ cột chồng theo tỷ lệ % (thay vì số tuyệt đối).

facet_wrap(~crime_group): chia mỗi nhóm tội thành một khung nhỏ riêng (small multiple).

scale_y_continuous(labels = scales::percent): chuyển trục tung sang đơn vị %.

theme_minimal() + strip.text: tạo bố cục tối giản, căn giữa tiêu đề và tinh chỉnh nhãn facet

Ý nghĩa thống kê:

Biểu đồ thể hiện cơ cấu giới tính của các vụ án trong Top 5 nhóm tội phạm phổ biến nhất, theo từng năm từ 2020 đến 2023. Mỗi khung nhỏ (facet) là một nhóm tội, giúp so sánh tỷ lệ giới tính qua thời gian trong cùng loại hành vi.

Sexual & Harassment: nữ giới chiếm ưu thế tuyệt đối (khoảng 70–80%) trong toàn kỳ quan sát. Điều này phản ánh đặc trưng của nhóm tội phạm liên quan đến xâm hại, quấy rối tình dục — nơi nữ giới chủ yếu là nạn nhân. Tỷ lệ ổn định qua các năm, cho thấy tính lặp lại của mô hình nạn nhân theo giới.

Homicide và Violence & Assault: nam giới chiếm đa số (60–70%), phản ánh các loại tội mang tính bạo lực thể chất hoặc hành vi tấn công thường do hoặc nhằm vào nam giới. Xu hướng này duy trì ổn định, ít biến động theo năm.

Fraud & Financial Crime và Drug-related: tỷ lệ nam và nữ khá cân bằng, cho thấy đây là các loại tội phi bạo lực (non-violent crimes) chịu ảnh hưởng bởi yếu tố xã hội – kinh tế hơn là giới tính.

Other nhóm tội phụ như Arson hay Theft & Robbery cũng duy trì mức chênh lệch giới nhỏ hơn, thể hiện sự đa dạng hành vi trong từng loại phạm pháp.

11. Trung bình tuổi theo loại tội

crime %>%
  group_by(crime_group) %>%
  summarise(mean_age = mean(age, na.rm = TRUE)) %>%
  slice_max(mean_age, n = 10) %>%
  ggplot(aes(x = reorder(crime_group, mean_age), y = mean_age)) +
  geom_col(fill = "orange") +
  coord_flip() +
  geom_text(aes(label = round(mean_age, 1)), hjust = -0.3) +
  labs(title = "Tuổi trung bình theo loại tội", x = "Loại tội", y = "Tuổi TB") +
  theme_minimal()

Giải thích kỹ thuật

group_by(crime_group) %>% summarise(mean_age = mean(age, na.rm = TRUE)): gom theo nhóm tội và tính tuổi trung bình; na.rm=TRUE loại NA để không làm sai lệch giá trị.

slice_max(mean_age, n = 10): lấy 10 nhóm có tuổi TB cao nhất (nếu <10 nhóm thì giữ nguyên tất cả).

ggplot(..., aes(x = reorder(crime_group, mean_age), y = mean_age)): sắp xếp trục theo tuổi TB tăng dần để dễ so sánh.

geom_col(fill = "orange") + coord_flip(): cột ngang giúp đọc nhãn nhóm tội dài rõ ràng.

geom_text(..., hjust = -0.3): hiển thị giá trị tuổi TB ngay trên cột.

labs(...) + theme_minimal(): đặt tiêu đề/nhãn và giao diện gọn.

Ý nghĩa thống kê:

Các nhóm như Fraud & Financial Crime / Homicide / Violence & Assault có tuổi trung bình cao hơn → phản ánh hành vi đòi hỏi sự chuẩn bị/kỹ năng, hoặc xảy ra trong bối cảnh người lớn tuổi hơn.

Nhóm Drug-related (và một số nhóm như Arson) có tuổi trung bình thấp hơn → hàm ý mức độ tham gia/ảnh hưởng ở nhóm tuổi trẻ nổi bật hơn.

Trật tự chênh lệch ổn định và dễ thấy nhờ sắp xếp theo giá trị TB; tuy nhiên mean nhạy với ngoại lệ, nên khi viết báo cáo có thể bổ sung trung vị (median) và IQR để kiểm chứng độ bền.

Kết quả gợi ý ưu tiên can thiệp theo độ tuổi: phòng ngừa, giáo dục sớm cho nhóm tội có tuổi TB thấp; tăng truyền thông – kiểm soát rủi ro tài chính cho nhóm tội có tuổi TB cao.

12. Phân bố tuổi theo giới

breaks16 <- seq(0, 90, by = 5)
df16 <- crime %>%
  filter(!is.na(age), sex %in% c("Male","Female")) %>%
  mutate(age_bin = cut(age, breaks = breaks16, right = FALSE)) %>%
  count(sex, age_bin) %>%
  group_by(sex) %>%
  mutate(n = ifelse(sex == "Female", -n, n)) %>% ungroup()

ggplot(df16, aes(x = age_bin, y = n, fill = sex)) +
  geom_col(width = 0.9) +
  coord_flip() +
  scale_y_continuous(labels = function(x) scales::comma(abs(x))) +
  labs(title = "Tháp tuổi nạn nhân theo giới tính", x = "Nhóm tuổi (5 năm)", y = "Số vụ") +
  theme_minimal() +
  theme(plot.title = element_text(hjust=.5))

Giải thích kỹ thuật

breaks16 <- seq(0, 90, by = 5): chia biến tuổi thành các khoảng 5 năm (0–5, 5–10,…).

mutate(age_bin = cut(age, breaks = breaks16, right = FALSE)): tạo biến phân loại nhóm tuổi.

count(sex, age_bin): đếm số vụ theo từng giới và nhóm tuổi.

mutate(n = ifelse(sex == "Female", -n, n)): chuyển giá trị âm cho nữ, dương cho nam để vẽ dạng tháp hai bên cân đối.

geom_col(width = 0.9) + coord_flip(): vẽ biểu đồ cột nằm ngang đối xứng; trục hoành là số vụ (với giá trị âm cho nữ).

scale_y_continuous(labels = function(x) comma(abs(x))): hiển thị giá trị dương tuyệt đối để người đọc không thấy dấu âm.

labs(...) + theme_minimal(): đặt tiêu đề, nhãn trục và giao diện gọn gàng, dễ đọc

Ý nghĩa thống kê:

Biểu đồ cho thấy phân bố độ tuổi nạn nhân theo giới tính tương tự cấu trúc dân số dạng “tháp tuổi”:

Hai giới có phân bố tương đối cân bằng ở độ tuổi 25–45, đây cũng là nhóm tuổi có số vụ cao nhất. Điều này cho thấy giai đoạn trung niên và thanh niên trưởng thành là thời kỳ có nguy cơ gặp tội phạm cao nhất, thường liên quan đến hoạt động xã hội, nghề nghiệp và di chuyển.

Nhóm dưới 15 tuổi và trên 70 tuổi chiếm tỷ trọng thấp, phản ánh khả năng tự bảo vệ hoặc mức độ tiếp xúc xã hội thấp hơn.

Đỉnh tháp lệch nhẹ sang phía nam giới, cho thấy nạn nhân nam có xu hướng chiếm ưu thế trong tổng số vụ.

Phân bố mượt và đối xứng chứng tỏ không có chênh lệch cực đoan giữa hai giới, tuy vẫn cần chú ý nhóm tuổi 20–35 có mật độ vụ án cao – giai đoạn dễ chịu tác động của môi trường đô thị, nghề nghiệp và tội phạm đường phố.

13. Số vụ theo tháng

library(lubridate)
## 
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
## 
##     date, intersect, setdiff, union
crime %>%
mutate(month = month(as.Date(date), label = TRUE, abbr = TRUE)) %>%
count(month) %>%
ggplot(aes(x = month, y = n)) +
geom_col(fill = "#69b3a2") +
geom_text(aes(label = n), vjust = -0.3) +
labs(title = "Số vụ phạm tội theo tháng", x = "Tháng", y = "Số vụ") +
theme_minimal()

Giải thích kỹ thuật

mutate(month = month(as.Date(date), label = TRUE, abbr = TRUE)): chuyển biến ngày (date) thành tháng dạng rút gọn (Thg1–Thg12) để nhóm dữ liệu theo tháng.

count(month): đếm số vụ phạm tội trong từng tháng.

geom_col(fill = "#69b3a2"): vẽ biểu đồ cột đứng, mỗi cột thể hiện tổng số vụ của một tháng.

geom_text(aes(label = n), vjust = -0.3): hiển thị giá trị số vụ trên đỉnh từng cột.

labs(...): đặt tiêu đề, nhãn trục X (tháng) và trục Y (số vụ).

theme_minimal(): chọn giao diện đơn giản, dễ đọc và phù hợp cho báo cáo.

Ý nghĩa thống kê:

Biểu đồ cho thấy biến động số vụ phạm tội theo từng tháng trong năm:

Tháng 1 ghi nhận số vụ cao nhất (≈75.000 vụ), cao hơn đáng kể so với trung bình các tháng khác (~63.000 vụ), có thể liên quan đến giai đoạn lễ tết – du lịch, khi tần suất đi lại và tụ tập đông người tăng cao.

Từ tháng 2 đến tháng 10, số vụ dao động hẹp quanh mức 60.000–64.000, cho thấy mức độ phạm tội ổn định trong phần lớn thời gian của năm.

Tháng 11 và 12 giảm nhẹ (~59.000–60.000 vụ), có thể phản ánh tác động của các biện pháp tăng cường an ninh cuối năm hoặc tâm lý giảm hoạt động xã hội.

Tổng thể, dữ liệu phản ánh xu hướng mùa vụ nhẹ: tội phạm có xu hướng tăng đầu năm, ổn định giữa năm và giảm cuối năm. Điều này gợi ý cơ quan chức năng nên tăng cường kiểm soát trong quý I, đặc biệt trong các tháng cao điểm lễ hội.

14. Trung bình tuổi theo năm

crime %>%
filter(!is.na(age)) %>%
group_by(year) %>%
summarise(mean_age = mean(age)) %>%
ggplot(aes(x = factor(year), y = mean_age, group = 1)) +
geom_line(color = "#2A9D8F") +
geom_point(size = 2, color = "#E76F51") +
labs(title = "Tuổi trung bình theo năm", x = "Năm", y = "Tuổi trung bình") +
theme_minimal()

Giải thích kỹ thuật

filter(!is.na(age)): loại bỏ các bản ghi thiếu dữ liệu tuổi để đảm bảo tính chính xác của giá trị trung bình.

group_by(year) %>% summarise(mean_age = mean(age)): nhóm dữ liệu theo năm và tính tuổi trung bình cho từng năm.

ggplot(aes(x = factor(year), y = mean_age, group = 1)): khởi tạo đồ thị đường, trục hoành là năm, trục tung là tuổi trung bình, group = 1 đảm bảo nối liền các điểm theo thứ tự năm.

geom_line(color = "#2A9D8F"): vẽ đường nối liên tục biểu thị xu hướng thay đổi qua các năm.

geom_point(size = 2, color = "#E76F51"): thêm các điểm đánh dấu giúp người xem dễ nhận biết giá trị từng năm.

labs(...) + theme_minimal(): đặt tiêu đề, nhãn trục và định dạng biểu đồ đơn giản, hiện đại.

Ý nghĩa thống kê:

Biểu đồ thể hiện sự biến động của tuổi trung bình nạn nhân hoặc đối tượng liên quan đến vụ án theo từng năm.

Giai đoạn 2020–2021, tuổi trung bình tăng nhẹ từ khoảng 34,5 lên 34,8 tuổi, cho thấy xu hướng các vụ phạm tội hoặc nạn nhân tập trung ở nhóm tuổi lớn hơn so với trước.

Sau năm 2021, giá trị giảm dần rõ rệt, đến 2023 chỉ còn khoảng 33,2 tuổi, phản ánh sự trẻ hóa trong cơ cấu độ tuổi — tức nhóm người trẻ (dưới 35 tuổi) xuất hiện nhiều hơn trong các vụ án.

Biến động này có thể xuất phát từ tác động xã hội sau đại dịch, khi hoạt động kinh tế – xã hội trở lại bình thường, kéo theo mức độ tham gia và tiếp xúc cao hơn ở nhóm tuổi trẻ.

Tổng thể, xu hướng giảm dần từ 2021–2023 cho thấy độ tuổi liên quan đến tội phạm đang dịch chuyển về nhóm trẻ hơn, gợi ý rằng các biện pháp phòng ngừa, giáo dục pháp luật và định hướng xã hội cần tập trung nhiều hơn vào nhóm thanh niên và người trưởng thành trẻ tuổi.

15. Cơ cấu mức độ phạm tội

df_donut <- crime %>%
count(severity_label) %>%
mutate(p = n / sum(n), label = paste0(severity_label, "\n", scales::percent(p, 0.1)))

ggplot(df_donut, aes(x = 2, y = p, fill = severity_label)) +
geom_col(color = "white") +
coord_polar(theta = "y") +
xlim(0.5, 2.5) +
geom_text(aes(label = label), position = position_stack(vjust = 0.5), size = 3) +
theme_void() +
labs(title = "Cơ cấu mức độ phạm tội") +
theme(plot.title = element_text(hjust = 0.5))

Giải thích kỹ thuật

count(severity_label): đếm tổng số vụ theo từng mức độ phạm tội (nhẹ – nghiêm trọng).

mutate(p = n / sum(n)): tính tỷ trọng phần trăm của từng nhóm so với toàn bộ.

paste0(severity_label, "n", scales::percent(p, 0.1)): tạo nhãn hiển thị gồm tên nhóm và phần trăm.

geom_col(color = "white"): vẽ cột tròn cơ bản (các “miếng bánh”) với viền trắng phân tách rõ ràng.

coord_polar(theta = "y"): chuyển biểu đồ cột thành biểu đồ tròn theo góc xoay trục Y.

xlim(0.5, 2.5): tạo khoảng trống ở giữa → thành dạng donut (vòng tròn rỗng).

geom_text(...): hiển thị phần trăm ngay giữa mỗi lát.

theme_void(): ẩn toàn bộ trục và nền, chỉ giữ lại nội dung chính.

theme(plot.title = element_text(hjust = 0.5)): căn giữa tiêu đề.

Ý nghĩa thống kê:

Biểu đồ donut thể hiện tỷ trọng mức độ phạm tội trong toàn bộ dữ liệu.

Nhóm “Severe” (nghiêm trọng) chiếm khoảng 52,7%, cao hơn nhóm “Minor” (47,3%), cho thấy tỷ lệ các vụ có tính chất nghiêm trọng chiếm ưu thế.

Mức chênh lệch (~5,4 điểm %) tuy không quá lớn, nhưng vẫn phản ánh xu hướng tội phạm có mức độ bạo lực, thiệt hại hoặc vi phạm nặng đang phổ biến hơn so với các hành vi nhẹ.

Cơ cấu hai nhóm gần cân bằng gợi ý rằng trong chiến lược phòng ngừa, cần đồng thời chú trọng hai mảng:

Ngăn chặn và xử lý các hành vi nghiêm trọng, đặc biệt là tội phạm có tổ chức hoặc gây hậu quả lớn.

Tăng cường giáo dục – răn đe với nhóm hành vi nhẹ, vì nhóm này vẫn chiếm gần một nửa và có thể là nguồn phát sinh các hành vi nghiêm trọng trong tương lai.

Tổng thể, biểu đồ cho thấy mức độ phạm tội nghiêm trọng chiếm phần nhỉnh hơn, phản ánh tình hình an ninh cần được giám sát chặt chẽ và liên tục.

16. Tội phạm theo loại trong năm 2023 (Top 10)

crime %>%
filter(year == 2023) %>%
count(crime_group, sort = TRUE) %>%
slice_head(n = 10) %>%
ggplot(aes(x = reorder(crime_group, n), y = n)) +
geom_col(fill = "#457b9d") +
geom_text(aes(label = n), hjust = -0.1) +
coord_flip() +
labs(title = "Top 10 nhóm tội trong năm 2023", x = "Nhóm tội", y = "Số vụ") +
theme_minimal()

Giải thích kỹ thuật

filter(year == 2023): lọc dữ liệu chỉ lấy năm 2023.

count(crime_group, sort = TRUE): đếm số vụ theo từng nhóm tội phạm, sắp xếp giảm dần.

slice_head(n = 10): chọn 10 nhóm tội có số vụ nhiều nhất.

ggplot(aes(x = reorder(crime_group, n), y = n)): trục X là nhóm tội, sắp xếp theo số vụ (n), trục Y là số lượng vụ.

geom_col(fill = "#457b9d"): vẽ biểu đồ cột ngang với màu xanh lam dịu.

geom_text(aes(label = n), hjust = -0.1): thêm nhãn giá trị số vụ ở cuối cột.

coord_flip(): đảo trục, giúp hiển thị nhãn nhóm tội rõ ràng hơn.

theme_minimal() + labs(...): định dạng biểu đồ đơn giản, dễ đọc, đặt tiêu đề và nhãn trục.

Ý nghĩa thống kê:

Biểu đồ thể hiện Top 10 nhóm tội phạm phổ biến nhất trong năm 2023, cho thấy:

Theft & Robbery (Trộm cắp – Cướp giật) đứng đầu với hơn 100.000 vụ, chiếm tỷ trọng áp đảo, phản ánh đặc trưng tội phạm đô thị và sự phổ biến của hành vi xâm phạm tài sản.

Violence & Assault (Bạo lực – Hành hung) xếp thứ hai (~54.000 vụ), cho thấy tình trạng xung đột và bạo lực cá nhân vẫn ở mức cao.

Nhóm Other (khác) đứng thứ ba (~39.000 vụ), bao gồm các hành vi vi phạm không nằm trong nhóm chính.

Các loại tội còn lại như Sexual & Harassment, Arson, Fraud & Financial Crime, Homicide và Drug-related chiếm tỷ trọng nhỏ, phản ánh mức độ hiếm hơn của các hành vi này trong cơ cấu tội phạm tổng thể.

Nhìn chung, các tội liên quan đến tài sản và bạo lực chiếm hơn 70% tổng số vụ, cho thấy hai mảng này vẫn là trọng tâm trong quản lý trật tự xã hội.

17. Tuổi trung bình theo nhóm tội (Top 8)

top8_group <- crime %>% count(crime_group, sort = TRUE) %>% slice_head(n = 8) %>% pull(crime_group)
crime %>%
filter(crime_group %in% top8_group) %>%
group_by(year, crime_group) %>%
summarise(mean_age = mean(age, na.rm = TRUE), .groups = "drop") %>%
ggplot(aes(x = year, y = mean_age, color = crime_group)) +
geom_line(linewidth = 1) +
geom_point(size = 2) +
labs(title = "Tuổi trung bình theo nhóm tội (Top 8)", x = "Năm", y = "Tuổi trung bình", color = "Nhóm tội") +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5))
## `geom_line()`: Each group consists of only one observation.
## ℹ Do you need to adjust the group aesthetic?

Giải thích kỹ thuật:

top8_group <- crime %>% count(crime_group, sort = TRUE) %>% slice_head(n = 8) %>% pull(crime_group):
Tạo danh sách 8 nhóm tội có số vụ cao nhất. Cụ thể:

count(crime_group, sort = TRUE) đếm số vụ theo nhóm tội, sắp xếp giảm dần.

slice_head(n = 8) chọn 8 nhóm đầu tiên.

pull(crime_group) trích xuất giá trị tên nhóm tội vào một vector.

filter(crime_group %in% top8_group): Lọc dữ liệu chỉ giữ lại các bản ghi thuộc 8 nhóm tội phổ biến nhất.

group_by(year, crime_group): Gom dữ liệu theo hai biến năm và nhóm tội, giúp tính trung bình tuổi trong từng tổ hợp.

summarise(mean_age = mean(age, na.rm = TRUE), .groups = "drop"): Tính tuổi trung bình cho từng nhóm theo từng năm. Tham số .groups = "drop" xóa grouping sau khi tổng hợp để tránh lỗi hoặc cảnh báo.

ggplot(aes(x = year, y = mean_age, color = crime_group)): Đặt ánh xạ thẩm mỹ — trục hoành là năm, trục tung là tuổi trung bình, và màu sắc đại diện cho từng nhóm tội.

geom_line(linewidth = 1): Vẽ đường nối xu hướng tuổi trung bình của mỗi nhóm tội qua các năm, độ dày đường = 1.

geom_point(size = 2): Thêm các điểm dữ liệu cụ thể trên đường để dễ nhận biết giá trị từng năm.

labs(...): Gán tiêu đề, nhãn trục và chú giải màu rõ ràng, hỗ trợ giải thích biểu đồ.

theme_minimal() + theme(plot.title = element_text(hjust = 0.5)): Dùng giao diện tối giản, căn giữa tiêu đề, giúp biểu đồ sáng sủa và dễ đọc.

Ý nghĩa thống kê:

Biểu đồ thể hiện tuổi trung bình của người phạm tội theo từng nhóm tội giai đoạn 2020–2023. Nhìn chung, các nhóm tội như Fraud & Financial Crime và Homicide có tuổi trung bình cao nhất (khoảng 38–40 tuổi), phản ánh sự tham gia của đối tượng trưởng thành hơn, có tính toán và kinh nghiệm.

Ngược lại, nhóm Drug-related có tuổi trung bình thấp nhất (chỉ khoảng 10–12 tuổi), cho thấy xu hướng phạm tội sớm hoặc liên quan đến tội vị thành niên.

Nhóm Sexual & Harassment và Theft & Robbery dao động quanh 30–35 tuổi, phản ánh độ tuổi hoạt động tội phạm phổ biến.

Tổng thể, biểu đồ cho thấy tuổi trung bình khá ổn định qua các năm, chứng tỏ cơ cấu độ tuổi người phạm tội không biến động mạnh, tuy nhiên mức chênh lệch đáng kể giữa các loại tội giúp xác định đối tượng trọng điểm trong công tác phòng ngừa.

18. Mức độ nghiêm trọng và tuổi trung bình theo năm và khu vực

library(dplyr); library(ggplot2); library(scales)
## Warning: package 'scales' was built under R version 4.5.2
df_a1 <- crime %>%
mutate(is_severe = severity_label %in% c("Severe","High")) %>%
group_by(year, area) %>%
summarise(rate_severe = mean(is_severe, na.rm=TRUE),
mean_age = mean(age, na.rm=TRUE),
n = n(), .groups="drop")

ggplot(df_a1, aes(x = factor(year), y = area)) +
geom_point(aes(size = rate_severe, color = mean_age), alpha = .8) +
scale_size(range = c(2,10), labels = percent) +
scale_color_viridis_c() +
labs(title = "Bubble: Năm × Khu vực (size = Tỷ lệ nghiêm trọng, color = Tuổi TB)",
x = "Năm", y = "Khu vực", size = "Tỷ lệ nghiêm trọng", color = "Tuổi TB") +
theme_minimal()

Giải thích kỹ thuật

`count(crime_group, sort = TRUE)` + `slice_head(n = 8)`: xác định 8 nhóm tội phổ biến nhất để phân tích.

`filter(crime_group %in% top8_group)`: lọc dữ liệu chỉ giữ các nhóm tội này.

`group_by(year, crime_group)` + `summarise(mean_age = mean(age, na.rm = TRUE))`: tính tuổi trung bình của nạn nhân (hoặc đối tượng) trong từng năm × nhóm tội.

`geom_line()` + `geom_point()`: vẽ đường xu hướng và điểm dữ liệu cho từng nhóm tội, giúp so sánh trực quan theo thời gian.

`labs(...)`: đặt tiêu đề và nhãn trục, đồng thời gắn chú giải màu cho từng nhóm tội.

`theme_minimal()` + `plot.title = element_text(hjust = 0.5)`: tạo giao diện đơn giản và căn giữa tiêu đề biểu đồ.

Ý nghĩa thống kê:

Biểu đồ thể hiện biến động tuổi trung bình theo từng nhóm tội trong giai đoạn 2020–2023:

Fraud & Financial Crime và Homicide có tuổi trung bình cao nhất (khoảng 37–39 tuổi), phản ánh đặc điểm của nhóm tội đòi hỏi kinh nghiệm, kế hoạch hoặc liên quan đến lợi ích tài chính — thường do người trưởng thành gây ra hoặc liên quan đến môi trường nghề nghiệp.

Theft & Robbery và Violence & Assault duy trì mức tuổi trung bình 34–36, đại diện cho nhóm tội phổ biến ở người trưởng thành trẻ.

Sexual & Harassment và Other dao động quanh 30–33tuổi, không có biến động mạnh, phản ánh tính ổn định của hành vi phạm tội trong nhóm tuổi này.

Arson có tuổi trung bình thấp hơn (~20–23 tuổi), trong khi Drug-related rất thấp (~5–10 tuổi), nhiều khả năng do dữ liệu chứa các vụ liên quan đến trẻ vị thành niên hoặc trường hợp ghi nhận sai lệch tuổi.

Nhìn chung, xu hướng các nhóm tuổi phạm tội ổn định qua thời gian, không có biến động đột ngột, cho thấy đặc trưng nhân khẩu học của từng loại tội được duy trì đều đặn. Điều này giúp phân loại nhóm nguy cơ theo độ tuổi, từ đó đề xuất các biện pháp can thiệp – tuyên truyền – giám sát phù hợp với từng dạng hành vi.

19. So sánh tỷ lệ nạn nhân nữ theo khu vực so với trung bình toàn thành phố

overall_f <- mean(crime$sex == "Female", na.rm=TRUE)

df_a5 <- crime %>%
filter(!is.na(area), !is.na(sex)) %>%
group_by(area) %>%
summarise(p_f = mean(sex=="Female"), n=n(), .groups="drop") %>%
mutate(diff = p_f - overall_f,
area = reorder(area, diff))

ggplot(df_a5, aes(x = diff, y = area)) +
geom_segment(aes(xend = 0, yend = area), color = "#b0bec5") +
geom_point(aes(size = n, color = diff > 0)) +
scale_x_continuous(labels = percent) +
scale_color_manual(values = c("#e76f51", "#2a9d8f"), guide = "none") +
labs(title = "Chênh lệch tỷ lệ nạn nhân nữ so với toàn TP (size = quy mô vụ)",
x = "Chênh lệch (%)", y = "Khu vực", size = "Số vụ") +
theme_minimal()

Giải thích kỹ thuật

overall_f <- mean(crime$sex == "Female", na.rm=TRUE): tính tỷ lệ trung bình nạn nhân nữ toàn thành phố để làm chuẩn so sánh.

group_by(area) %>% summarise(p_f = mean(sex=="Female"), n = n()): tính tỷ lệ nạn nhân nữ (p_f) và quy mô vụ (n) cho từng khu vực.

mutate(diff = p_f - overall_f): tính độ chênh lệch (%) của mỗi khu vực so với trung bình toàn TP (giá trị dương = cao hơn trung bình, âm = thấp hơn).

geom_segment(aes(xend = 0, yend = area)): vẽ đường nối từ mốc 0 đến điểm dữ liệu để dễ quan sát mức chênh lệch.

geom_point(aes(size = n, color = diff > 0)):

kích thước điểm biểu thị số vụ án tại khu vực đó;

màu: xanh (#2a9d8f) nếu tỷ lệ nữ cao hơn trung bình, đỏ (#e76f51) nếu thấp hơn trung bình.

scale_x_continuous(labels = percent): định dạng trục X theo tỷ lệ %.

labs(...) + theme_minimal(): thêm tiêu đề, nhãn trục, chú giải và giao diện tối giản.

Ý nghĩa thống kê:

Biểu đồ thể hiện mức chênh lệch tỷ lệ nạn nhân nữ giữa các khu vực so với trung bình toàn thành phố:

Khu vực có tỷ lệ nạn nhân nữ cao hơn trung bình (phía phải, màu xanh):

Gồm các khu vực như Southeast, 77th Street, Southwest, Mission, Foothill… với mức chênh từ +5% đến +11%.

Điều này cho thấy các khu vực này có tính chất tội phạm nhắm nhiều đến phụ nữ hơn, có thể liên quan đến môi trường dân cư, an ninh khu vực hoặc loại tội phạm phổ biến (bạo lực gia đình, quấy rối, xâm hại).

Khu vực có tỷ lệ nữ thấp hơn trung bình (phía trái, màu đỏ):

Bao gồm Central, Hollywood, West LA, Wilshire, Olympic,… với chênh lệch -3% đến -7%, thể hiện xu hướng nạn nhân nam chiếm ưu thế, thường gặp trong các tội phạm tài sản hoặc xung đột ngoài xã hội.

Quy mô điểm (size) cho thấy khu vực đông dân hoặc phạm tội nhiều (như Central, 77th Street) đồng thời có ảnh hưởng lớn đến bức tranh chung.

Tổng thể, biểu đồ giúp nhận diện rõ khu vực rủi ro cao cho phụ nữ, từ đó hỗ trợ đề xuất chính sách an toàn cộng đồng, tăng cường chiếu sáng, tuần tra, và giáo dục phòng tránh tội phạm giới tính ở các khu vực nổi bật màu xanh.

20. Tốc độ tăng trưởng số vụ theo năm và giới tính nạn nhân

df_heat <- crime %>%
  count(year, sex) %>%
  group_by(sex) %>%
  arrange(year, .by_group = TRUE) %>%
  mutate(yoy = (n - lag(n)) / lag(n)) %>%
  filter(!is.na(yoy)) %>%
  ungroup()

ggplot(df_heat, aes(x = factor(year), y = sex, fill = yoy)) +
  geom_tile(color = "white") +
  scale_fill_gradient2(low = "#d73027", mid = "white", high = "#1a9850", labels = percent) +
  labs(title = "Tăng trưởng số vụ theo năm × giới",
       x = "Năm", y = "Giới", fill = "(%)") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5))

Giải thích kỹ thuật

count(year, sex): đếm tổng số vụ theo năm và giới tính nạn nhân.

group_by(sex) + arrange(year, .by_group = TRUE): sắp xếp các năm cho từng giới để tính tăng trưởng tuần tự.

mutate(yoy = (n - lag(n)) / lag(n)): tính tốc độ tăng trưởng năm–qua–năm (YoY) theo công thức

filter(!is.na(yoy)): loại dòng đầu tiên của mỗi nhóm (vì không có năm trước để so sánh).

geom_tile(color = "white"): vẽ heatmap, mỗi ô biểu thị tốc độ tăng trưởng (%).

scale_fill_gradient2(low = "#d73027", mid = "white", high = "#1a9850"): thang màu hai chiều:

Đỏ (#d73027) → giảm trưởng (âm);

Trắng → ổn định (0%);

Xanh lá (#1a9850) → tăng trưởng dương.

labels = percent: chuyển đổi giá trị sang định dạng phần trăm.

theme_minimal() + plot.title = element_text(hjust = 0.5): bố cục đơn giản, căn giữa tiêu đề.

Ý nghĩa thống kê:

Biểu đồ heatmap cho thấy tốc độ tăng trưởng số vụ phạm tội theo giới tính nạn nhân qua các năm:

Nhóm “Unknown” (không xác định giới) tăng mạnh nhất, đặc biệt giai đoạn 2022–2023 đạt mức tăng trên 30%, có thể do cải thiện trong công tác ghi nhận dữ liệu hoặc gia tăng các vụ việc không rõ danh tính.

Nhóm “Male” duy trì mức tăng nhẹ từ 2021–2022, sau đó giảm nhẹ trong 2023, cho thấy số vụ liên quan đến nam giới bắt đầu ổn định hoặc giảm dần.

Nhóm “Female” cũng tăng trong 2022 nhưng giảm trở lại trong 2023, phản ánh xu hướng giảm nhẹ các vụ phạm tội nhắm đến phụ nữ trong năm gần nhất.

Tổng thể: Giai đoạn 2021–2022 là năm tăng trưởng chung cho tất cả giới, trong khi 2023 chứng kiến sự phân hoá rõ rệt — nhóm “Unknown” tăng mạnh, hai nhóm còn lại giảm.

PHẦN II: Phân tích dữ liệu tài chính Công ty Cổ phần Sữa Việt Nam (VNM) giai đoạn 2015 - 2024

CHƯƠNG 1: MỞ ĐẦU

1. Lý do chọn đề tài

Ngành sữa Việt Nam là một trong những lĩnh vực có tốc độ tăng trưởng ổn định và đóng vai trò quan trọng trong việc cải thiện dinh dưỡng quốc gia. Trong đó, Công ty Cổ phần Sữa Việt Nam (Vinamilk – VNM) là doanh nghiệp dẫn đầu thị trường, có vị thế thống lĩnh cả về quy mô sản xuất, doanh thu và giá trị thương hiệu. Việc phân tích dữ liệu tài chính của VNM trong giai đoạn 2015–2024 không chỉ giúp đánh giá hiệu quả hoạt động kinh doanh của một doanh nghiệp đầu ngành, mà còn phản ánh bức tranh tổng thể của ngành sữa Việt Nam trong bối cảnh hội nhập và cạnh tranh ngày càng cao.

Ngoài ra, giai đoạn này bao gồm nhiều biến động đáng chú ý như ảnh hưởng của đại dịch COVID-19, sự thay đổi trong hành vi tiêu dùng, và xu hướng mở rộng thị trường quốc tế của Vinamilk. Do đó, việc nghiên cứu các chỉ tiêu tài chính của VNM (như doanh thu, lợi nhuận, NIM, NPM, hiệu quả sử dụng vốn và tỷ lệ tiền mặt trên tài sản) sẽ giúp đánh giá mức độ ổn định, khả năng sinh lời và hiệu quả quản trị tài chính của công ty qua thời gian.

Từ những lý do trên, đề tài “Phân tích dữ liệu tài chính Công ty Cổ phần Sữa Việt Nam (VNM) giai đoạn 2015–2024” được lựa chọn nhằm cung cấp cái nhìn toàn diện về hiệu quả hoạt động của doanh nghiệp đầu ngành, hỗ trợ việc ra quyết định đầu tư, hoạch định chiến lược và đánh giá tiềm năng tăng trưởng trong tương lai.

2. Đối tượng và phạm vi nghiên cứu

Đối tượng nghiên cứu:

Các chỉ tiêu tài chính của Công ty Cổ phần Sữa Việt Nam (Vinamilk – VNM) giai đoạn 2015–2024, bao gồm tổng tài sản, nợ phải trả, vốn chủ sở hữu, lợi nhuận sau thuế, NIM, NPM và hiệu quả tài chính.

Phạm vi nghiên cứu:

Không gian: Doanh nghiệp VNM – niêm yết trên sàn HOSE.

Thời gian: Từ năm 2015 đến 2024.

Nội dung: Phân tích, đánh giá xu hướng và hiệu quả hoạt động tài chính của VNM qua 10 năm.

CHƯƠNG 2: GIỚI THIỆU BỘ DỮ LIỆU VNM

1. Tổng quan về bộ dữ liệu

Bộ dữ liệu được thu thập từ báo cáo tài chính hợp nhất của Công ty Cổ phần Sữa Việt Nam (Vinamilk – VNM) trong giai đoạn 2015–2024. Dữ liệu ghi nhận các chỉ tiêu tài chính chủ yếu phản ánh quy mô tài sản, cơ cấu nguồn vốn, kết quả kinh doanh và hiệu quả sinh lời của doanh nghiệp theo từng năm.

Bộ dữ liệu được tổng hợp, làm sạch và chuẩn hóa để phục vụ cho phân tích mô tả, trực quan hóa và đánh giá xu hướng tài chính của VNM trong suốt 10 năm nghiên cứu.

2. Giới thiệu về bộ dữ liệu

2.1. Đọc dữ liệu từ Excel

CDKT <- read_excel( "C:/Users/USER/Downloads/VNM.xlsx", sheet = "CDKT")
HDKD <- read_excel("C:/Users/USER/Downloads/VNM.xlsx", sheet = "HDKD")
LCTT <- read_excel("C:/Users/USER/Downloads/VNM.xlsx", sheet = "LCTT")
Giải thích kỹ thuật:

read_excel() là hàm từ package readxl, cho phép đọc dữ liệu từ file Excel (.xlsx hoặc .xls) vào môi trường R.

Tham số sheet = "..." chỉ định tên sheet cụ thể cần đọc trong file Excel.

Ba bảng dữ liệu được lưu vào ba đối tượng riêng biệt (CDKT, HDKD, LCTT) để thuận tiện xử lý độc lập trước khi tích hợp.

2.2. Kiểm tra kích thước dữ liệu

dim(CDKT)
## [1] 10 83
dim(HDKD)
## [1] 10 23
dim(LCTT)
## [1] 10 50
Giải thích kỹ thuật:

Hàm dim() trả về một vector gồm hai phần tử: số hàng (rows) và số cột (columns) của data frame.

Kết quả cho biết CDKT có 10 hàng và 83 cột, HDKD có 10 hàng và 23 cột, LCTT có 10 hàng và 50 cột.

2.3. Xem cấu trúc và kiểu dữ liệu

glimpse(CDKT)
## Rows: 10
## Columns: 83
## $ Năm                                               <dbl> 2024, 2023, 2022, 20…
## $ `Tài sản ngắn hạn`                                <dbl> 3.755365e+13, 3.5935…
## $ `Tiền và các khoản tương đương tiền`              <dbl> 2.225944e+12, 2.9120…
## $ Tiền                                              <dbl> 1.877944e+12, 1.0254…
## $ `Các khoản tương đương tiền`                      <dbl> 3.480000e+11, 1.8865…
## $ `Các khoản đầu tư tài chính ngắn hạn`             <dbl> 2.326009e+13, 2.0137…
## $ `Chứng khoán kinh doanh`                          <dbl> 1248322211, 11930659…
## $ `Dự phòng giảm giá chứng khoán kinh doanh`        <dbl> -920681738, -8226634…
## $ `Đầu tư nắm giữ đến ngày đáo hạn`                 <dbl> 2.325976e+13, 2.0136…
## $ `Các khoản phải thu ngắn hạn`                     <dbl> 6.233759e+12, 6.5297…
## $ `Phải thu khách hàng`                             <dbl> 4.793133e+12, 4.8081…
## $ `Trả trước cho người bán`                         <dbl> 566479222775, 655619…
## $ `Phải thu từ cho vay ngắn hạn`                    <chr> NA, NA, NA, "-", "15…
## $ `Phải thu ngắn hạn khác`                          <dbl> 8.964795e+11, 1.0808…
## $ `Dự phòng phải thu khó đòi`                       <dbl> -22332866679, -14901…
## $ `Tài sản thiếu chờ xử lý`                         <chr> NA, NA, NA, NA, NA, …
## $ `Hàng tồn kho`                                    <dbl> 5.686840e+12, 6.1280…
## $ `Hàng tồn kho2`                                   <dbl> 5.723932e+12, 6.1659…
## $ `Dự phòng giảm giá hàng tồn kho`                  <dbl> -37092148693, -37853…
## $ `Tài sản ngắn hạn khác`                           <dbl> 147018887251, 228821…
## $ `Chi phí trả trước ngắn hạn`                      <dbl> 89544473336, 8286044…
## $ `Thuế giá trị gia tăng được khấu trừ`             <dbl> 33580977723, 9942895…
## $ `Thuế phải thu Ngân sách Nhà nước`                <dbl> 23893436192, 4653207…
## $ `B - TÀI SẢN DÀI HẠN`                             <dbl> 1.749541e+13, 1.6737…
## $ `Các khoản phải thu dài hạn`                      <dbl> 17592137763, 1613199…
## $ `Phải thu khách hàng3`                            <chr> "398152069", NA, NA,…
## $ `Phải thu từ cho vay dài hạn`                     <dbl> NA, NA, NA, NA, NA, …
## $ `Phải thu dài hạn khác`                           <dbl> 17193985694, 1613199…
## $ `Tài sản cố định`                                 <dbl> 1.255056e+13, 1.2689…
## $ `Tài sản cố định hữu hình`                        <dbl> 1.152020e+13, 1.1688…
## $ `Nguyên giá`                                      <dbl> 3.271305e+13, 3.1109…
## $ `Giá trị hao mòn lũy kế`                          <dbl> -2.119285e+13, -1.94…
## $ `Tài sản cố định vô hình`                         <dbl> 1.030364e+12, 1.0011…
## $ `Nguyên giá4`                                     <dbl> 1.466192e+12, 1.3664…
## $ `Giá trị hao mòn lũy kế5`                         <dbl> -435828219794, -3652…
## $ `Bất động sản đầu tư`                             <dbl> 53617793172, 5559415…
## $ `Nguyên giá6`                                     <dbl> 98822678885, 9882267…
## $ `Giá trị hao mòn lũy kế7`                         <dbl> -45204885713, -43228…
## $ `Tài sản dở dang dài hạn`                         <dbl> 1.539776e+12, 9.3692…
## $ `Chi phí sản xuất, kinh doanh dở dang dài hạn`    <dbl> 404666242750, 381979…
## $ `Xây dựng cơ bản dở dang`                         <dbl> 1.135110e+12, 5.5494…
## $ `Các khoản đầu tư tài chính dài hạn`              <dbl> 1.373190e+12, 8.3122…
## $ `Đầu tư vào các công ty liên doanh, liên kết`     <dbl> 622223692780, 602591…
## $ `Đầu tư góp vốn vào các đơn vị khác`              <dbl> 94548675081, 1019750…
## $ `Dự phòng giảm giá đầu tư tài chính dài hạn`      <dbl> -23582690668, -23582…
## $ `Đầu tư nắm giữ đến ngày đáo hạn8`                <chr> "680000000000", "150…
## $ `Tài sản dài hạn khác`                            <dbl> 1.960671e+12, 2.2079…
## $ `Chi phí trả trước dài hạn`                       <dbl> 792476015491, 871585…
## $ `Tài sản thuế thu nhập hoãn lại`                  <dbl> 92172946105, 1444850…
## $ `Lợi thế thương mại`                              <dbl> 1.076022e+12, 1.3219…
## $ `TỔNG CỘNG TÀI SẢN`                               <dbl> 5.504906e+13, 5.2673…
## $ `NỢ PHẢI TRẢ`                                     <dbl> 1.887466e+13, 1.7647…
## $ `Nợ ngắn hạn`                                     <dbl> 1.845955e+13, 1.7138…
## $ `Phải trả người bán`                              <dbl> 3.874064e+12, 3.8058…
## $ `Người mua trả tiền trước`                        <dbl> 191336029327, 164712…
## $ `Thuế phải nộp Ngân sách Nhà nước`                <dbl> 1.014478e+12, 9.6787…
## $ `Phải trả người lao động`                         <dbl> 307904216360, 289224…
## $ `Chi phí phải trả`                                <dbl> 2.115775e+12, 1.9102…
## $ `Doanh thu chưa thực hiện ngắn hạn`               <dbl> 263912732, 263912729…
## $ `Phải trả ngắn hạn khác`                          <dbl> 1.148532e+12, 1.1939…
## $ `Vay ngắn hạn`                                    <dbl> 9.115435e+12, 8.2177…
## $ `Dự phòng phải trả ngắn hạn`                      <dbl> 11775786301, 1677051…
## $ `Quỹ khen thưởng và phúc lợi`                     <dbl> 679981823727, 572091…
## $ `Nợ dài hạn`                                      <dbl> 415111869758, 508937…
## $ `Phải trả người bán dài hạn`                      <chr> NA, NA, NA, NA, "-",…
## $ `Chi phí phải trả dài hạn`                        <chr> NA, NA, NA, NA, NA, …
## $ `Doanh thu chưa thực hiện dài hạn`                <chr> NA, NA, NA, NA, NA, …
## $ `Phải trả dài hạn khác`                           <dbl> 722927552, 106337825…
## $ `Vay dài hạn`                                     <dbl> 157903902450, 238476…
## $ `Thuế thu nhập hoãn lại phải trả`                 <dbl> 256485039756, 269397…
## $ `D - VỐN CHỦ SỞ HỮU (400 = 410)`                  <dbl> 3.617440e+13, 3.5025…
## $ `Vốn chủ sở hữu`                                  <dbl> 3.617440e+13, 3.5025…
## $ `Vốn cổ phần`                                     <dbl> 2.089955e+13, 2.0899…
## $ `Thặng dư vốn cổ phần`                            <chr> "34110709700", "3411…
## $ `Vốn khác của chủ sở hữu`                         <dbl> 499080803215, 499080…
## $ `Cổ phiếu quỹ`                                    <chr> NA, NA, NA, "-", "-1…
## $ `Chênh lệch quy đổi tiền tệ`                      <dbl> 295734210956, 174100…
## $ `Quỹ đầu tư phát triển`                           <dbl> 7.079115e+12, 6.1637…
## $ `Lợi nhuận sau thuế chưa phân phối`               <dbl> 3.471225e+12, 3.9262…
## $ `- LNST chưa phân phối lũy kế đến cuối năm trước` <dbl> 3.493001e+11, 1.0684…
## $ `- LNST chưa phân phối năm nay`                   <dbl> 3.121925e+12, 2.8577…
## $ `Lợi ích cổ đông không kiểm soát`                 <dbl> 3.895583e+12, 3.3289…
## $ `TỔNG NGUỒN VỐN`                                  <dbl> 5.504906e+13, 5.2673…
glimpse(HDKD)
## Rows: 10
## Columns: 23
## $ Năm                                                              <dbl> 2024,…
## $ `Doanh thu bán hàng và cung cấp dịch vụ`                         <dbl> 6.182…
## $ `Các khoản giảm trừ doanh thu`                                   <dbl> 41280…
## $ `Doanh thu thuần về bán hàng và cung cấp dịch vụ (10 = 01 - 02)` <dbl> 6.178…
## $ `Giá vốn hàng bán và dịch vụ cung cấp`                           <dbl> 3.619…
## $ `Lợi nhuận gộp về bán hàng và cung cấp dịch vụ (20 = 10 - 11)`   <dbl> 2.559…
## $ `Doanh thu hoạt động tài chính`                                  <dbl> 1.585…
## $ `Chi phí tài chính`                                              <dbl> 42823…
## $ `Trong đó: Chi phí lãi vay`                                      <dbl> 27942…
## $ `Lãi/(lỗ) chia từ công ty liên kết, liên doanh`                  <dbl> 82002…
## $ `Chi phí bán hàng`                                               <dbl> 1.335…
## $ `Chi phí quản lý doanh nghiệp`                                   <dbl> 1.827…
## $ `Lợi nhuận thuần từ hoạt động kinh doanh`                        <dbl> 1.159…
## $ `Thu nhập khác`                                                  <dbl> 23063…
## $ `Chi phí khác`                                                   <dbl> 22495…
## $ `Kết quả từ hoạt động khác`                                      <dbl> 56761…
## $ `Lợi nhuận kế toán trước thuế`                                   <dbl> 1.159…
## $ `Chi phí thuế TNDN hiện hành`                                    <dbl> 2.240…
## $ `(Lợi ích)/chi phí thuế TNDN hoãn lại`                           <dbl> -9409…
## $ `Lợi nhuận sau thuế TNDN`                                        <dbl> 9.452…
## $ `Chủ sở hữu của Công ty`                                         <dbl> 9.392…
## $ `Lợi ích cổ đông không kiểm soát`                                <dbl> 60582…
## $ `Lãi cơ bản trên cổ phiếu`                                       <dbl> 4022.…
glimpse(LCTT)
## Rows: 10
## Columns: 50
## $ Năm                                                                                 <dbl> …
## $ `Lợi nhuận kế toán trước thuế`                                                      <dbl> …
## $ `Khấu hao và phân bổ`                                                               <dbl> …
## $ `Phân bổ lợi thế thương mại`                                                        <dbl> …
## $ `Các khoản dự phòng`                                                                <dbl> …
## $ `(Lãi)/lỗ chênh lệch tỷ giá do đánh giá lại các khoản mục tiền tệ có gốc ngoại tệ`  <dbl> …
## $ `Lãi do đánh giá lại khoản đầu tư vào công ty liên kết khi trở thành công ty con`   <chr> …
## $ `Lỗ từ thanh lý/xóa sổ tài sản cố định và xây dựng cơ bản dở dang`                  <dbl> …
## $ `Thu nhập từ cổ tức, lãi tiền gửi và lãi/lỗ từ hoạt động đầu tư khác`               <dbl> …
## $ `(Lãi)/lỗ chia từ công ty liên kết, liên doanh`                                     <chr> …
## $ `Chi phí lãi vay`                                                                   <dbl> …
## $ `Thu nhập từ khoản vay được miễn giảm`                                              <chr> …
## $ `Lợi nhuận từ hoạt động kinh doanh trước những thay đổi vốn lưu động`               <dbl> …
## $ `Biến động các khoản phải thu`                                                      <dbl> …
## $ `Biến động hàng tồn kho`                                                            <dbl> …
## $ `Biến động các khoản phải trả và nợ phải trả khác`                                  <dbl> …
## $ `Biến động chi phí trả trước`                                                       <dbl> …
## $ `Tiền chi mua TSCĐ và tài sản dài hạn khác`                                         <chr> …
## $ `Tiền lãi vay đã trả`                                                               <dbl> …
## $ `Thuế thu nhập doanh nghiệp đã nộp`                                                 <dbl> …
## $ `Tiền chi khác cho hoạt động kinh doanh`                                            <dbl> …
## $ `Lưu chuyển tiền thuần từ hoạt động kinh doanh`                                     <dbl> …
## $ `Tiền chi mua tài sản cố định và tài sản dài hạn khác`                              <dbl> …
## $ `Tiền thu từ thanh lý tài sản cố định và xây dựng cơ bản dở dang`                   <dbl> …
## $ `Tiền chi tiền gửi có kì hạn`                                                       <chr> …
## $ `Tiền thu gửi có kỳ hạn`                                                            <chr> …
## $ `Tiền chi cho vay`                                                                  <chr> …
## $ `Tiền thu hồi cho vay`                                                              <chr> …
## $ `Tiền thu hồi từ bán công cụ nợ của đơn vị khác`                                    <chr> …
## $ `Tiền chi đầu tư góp vốn vào các đơn vị khác`                                       <chr> …
## $ `Tiền thu hồi đầu tư vào các đơn vị khác`                                           <chr> …
## $ `Tiền thu lãi tiền gửi và cổ tức`                                                   <dbl> …
## $ `Khoản tiền thuần chi cho hợp nhất kinh doanh`                                      <chr> …
## $ `Lưu chuyển tiền thuần từ hoạt động đầu tư`                                         <chr> …
## $ `III. LƯU CHUYỂN TIỀN TỪ HOẠT ĐỘNG TÀI CHÍNH`                                       <dbl> …
## $ `Tiền thu từ bán cổ phiếu quỹ`                                                      <chr> …
## $ `Tiền thu từ phát hành cổ phiếu bởi công ty con cho cổ đông không kiểm soát`        <chr> …
## $ `Tiền thu từ nhận góp vốn của cổ đông không kiểm soát`                              <chr> …
## $ `Tiền hoàn trả vốn góp cho cổ đông không kiểm soát của công ty con đã giải thể`     <chr> …
## $ `Tiền thu góp vốn của cổ đông không kiểm soát`                                      <chr> …
## $ `Tiền thu từ đi vay`                                                                <dbl> …
## $ `Tiền chi trả nợ gốc vay`                                                           <dbl> …
## $ `Tiền chi trả cổ tức`                                                               <dbl> …
## $ `Tiền chi trả cổ tức của các công ty con chia cho cổ đông không kiểm soát`          <dbl> …
## $ `Lưu chuyển tiền thuần từ hoạt động tài chính`                                      <dbl> …
## $ `Lưu chuyển tiền thuần trong năm`                                                   <dbl> …
## $ `Tiền và các khoản tương đương tiền đầu năm`                                        <dbl> …
## $ `Ảnh hưởng của thay đổi tỷ giá hối đoái đối với tiền và các khoản tương đương tiền` <dbl> …
## $ `Chênh lệch quy đổi tiền tệ`                                                        <dbl> …
## $ `Tiền và các khoản tương đương tiền cuối năm`                                       <dbl> …
Giải thích kỹ thuật:

glimpse() là hàm từ package dplyr, hiển thị cấu trúc dữ liệu theo dạng ngang (transposed), cho phép xem nhanh tên biến, kiểu dữ liệu và một vài giá trị mẫu đầu tiên của mỗi cột.

Hàm này đặc biệt hữu ích khi làm việc với bảng dữ liệu có nhiều cột, giúp quan sát tổng quan mà không bị tràn màn hình.

2.4. Xem danh sách biến ban đầu

names(CDKT)
##  [1] "Năm"                                            
##  [2] "Tài sản ngắn hạn"                               
##  [3] "Tiền và các khoản tương đương tiền"             
##  [4] "Tiền"                                           
##  [5] "Các khoản tương đương tiền"                     
##  [6] "Các khoản đầu tư tài chính ngắn hạn"            
##  [7] "Chứng khoán kinh doanh"                         
##  [8] "Dự phòng giảm giá chứng khoán kinh doanh"       
##  [9] "Đầu tư nắm giữ đến ngày đáo hạn"                
## [10] "Các khoản phải thu ngắn hạn"                    
## [11] "Phải thu khách hàng"                            
## [12] "Trả trước cho người bán"                        
## [13] "Phải thu từ cho vay ngắn hạn"                   
## [14] "Phải thu ngắn hạn khác"                         
## [15] "Dự phòng phải thu khó đòi"                      
## [16] "Tài sản thiếu chờ xử lý"                        
## [17] "Hàng tồn kho"                                   
## [18] "Hàng tồn kho2"                                  
## [19] "Dự phòng giảm giá hàng tồn kho"                 
## [20] "Tài sản ngắn hạn khác"                          
## [21] "Chi phí trả trước ngắn hạn"                     
## [22] "Thuế giá trị gia tăng được khấu trừ"            
## [23] "Thuế phải thu Ngân sách Nhà nước"               
## [24] "B - TÀI SẢN DÀI HẠN"                            
## [25] "Các khoản phải thu dài hạn"                     
## [26] "Phải thu khách hàng3"                           
## [27] "Phải thu từ cho vay dài hạn"                    
## [28] "Phải thu dài hạn khác"                          
## [29] "Tài sản cố định"                                
## [30] "Tài sản cố định hữu hình"                       
## [31] "Nguyên giá"                                     
## [32] "Giá trị hao mòn lũy kế"                         
## [33] "Tài sản cố định vô hình"                        
## [34] "Nguyên giá4"                                    
## [35] "Giá trị hao mòn lũy kế5"                        
## [36] "Bất động sản đầu tư"                            
## [37] "Nguyên giá6"                                    
## [38] "Giá trị hao mòn lũy kế7"                        
## [39] "Tài sản dở dang dài hạn"                        
## [40] "Chi phí sản xuất, kinh doanh dở dang dài hạn"   
## [41] "Xây dựng cơ bản dở dang"                        
## [42] "Các khoản đầu tư tài chính dài hạn"             
## [43] "Đầu tư vào các công ty liên doanh, liên kết"    
## [44] "Đầu tư góp vốn vào các đơn vị khác"             
## [45] "Dự phòng giảm giá đầu tư tài chính dài hạn"     
## [46] "Đầu tư nắm giữ đến ngày đáo hạn8"               
## [47] "Tài sản dài hạn khác"                           
## [48] "Chi phí trả trước dài hạn"                      
## [49] "Tài sản thuế thu nhập hoãn lại"                 
## [50] "Lợi thế thương mại"                             
## [51] "TỔNG CỘNG TÀI SẢN"                              
## [52] "NỢ PHẢI TRẢ"                                    
## [53] "Nợ ngắn hạn"                                    
## [54] "Phải trả người bán"                             
## [55] "Người mua trả tiền trước"                       
## [56] "Thuế phải nộp Ngân sách Nhà nước"               
## [57] "Phải trả người lao động"                        
## [58] "Chi phí phải trả"                               
## [59] "Doanh thu chưa thực hiện ngắn hạn"              
## [60] "Phải trả ngắn hạn khác"                         
## [61] "Vay ngắn hạn"                                   
## [62] "Dự phòng phải trả ngắn hạn"                     
## [63] "Quỹ khen thưởng và phúc lợi"                    
## [64] "Nợ dài hạn"                                     
## [65] "Phải trả người bán dài hạn"                     
## [66] "Chi phí phải trả dài hạn"                       
## [67] "Doanh thu chưa thực hiện dài hạn"               
## [68] "Phải trả dài hạn khác"                          
## [69] "Vay dài hạn"                                    
## [70] "Thuế thu nhập hoãn lại phải trả"                
## [71] "D - VỐN CHỦ SỞ HỮU (400 = 410)"                 
## [72] "Vốn chủ sở hữu"                                 
## [73] "Vốn cổ phần"                                    
## [74] "Thặng dư vốn cổ phần"                           
## [75] "Vốn khác của chủ sở hữu"                        
## [76] "Cổ phiếu quỹ"                                   
## [77] "Chênh lệch quy đổi tiền tệ"                     
## [78] "Quỹ đầu tư phát triển"                          
## [79] "Lợi nhuận sau thuế chưa phân phối"              
## [80] "- LNST chưa phân phối lũy kế đến cuối năm trước"
## [81] "- LNST chưa phân phối năm nay"                  
## [82] "Lợi ích cổ đông không kiểm soát"                
## [83] "TỔNG NGUỒN VỐN"
names(HDKD)
##  [1] "Năm"                                                           
##  [2] "Doanh thu bán hàng và cung cấp dịch vụ"                        
##  [3] "Các khoản giảm trừ doanh thu"                                  
##  [4] "Doanh thu thuần về bán hàng và cung cấp dịch vụ (10 = 01 - 02)"
##  [5] "Giá vốn hàng bán và dịch vụ cung cấp"                          
##  [6] "Lợi nhuận gộp về bán hàng và cung cấp dịch vụ (20 = 10 - 11)"  
##  [7] "Doanh thu hoạt động tài chính"                                 
##  [8] "Chi phí tài chính"                                             
##  [9] "Trong đó: Chi phí lãi vay"                                     
## [10] "Lãi/(lỗ) chia từ công ty liên kết, liên doanh"                 
## [11] "Chi phí bán hàng"                                              
## [12] "Chi phí quản lý doanh nghiệp"                                  
## [13] "Lợi nhuận thuần từ hoạt động kinh doanh"                       
## [14] "Thu nhập khác"                                                 
## [15] "Chi phí khác"                                                  
## [16] "Kết quả từ hoạt động khác"                                     
## [17] "Lợi nhuận kế toán trước thuế"                                  
## [18] "Chi phí thuế TNDN hiện hành"                                   
## [19] "(Lợi ích)/chi phí thuế TNDN hoãn lại"                          
## [20] "Lợi nhuận sau thuế TNDN"                                       
## [21] "Chủ sở hữu của Công ty"                                        
## [22] "Lợi ích cổ đông không kiểm soát"                               
## [23] "Lãi cơ bản trên cổ phiếu"
names(LCTT)
##  [1] "Năm"                                                                              
##  [2] "Lợi nhuận kế toán trước thuế"                                                     
##  [3] "Khấu hao và phân bổ"                                                              
##  [4] "Phân bổ lợi thế thương mại"                                                       
##  [5] "Các khoản dự phòng"                                                               
##  [6] "(Lãi)/lỗ chênh lệch tỷ giá do đánh giá lại các khoản mục tiền tệ có gốc ngoại tệ" 
##  [7] "Lãi do đánh giá lại khoản đầu tư vào công ty liên kết khi trở thành công ty con"  
##  [8] "Lỗ từ thanh lý/xóa sổ tài sản cố định và xây dựng cơ bản dở dang"                 
##  [9] "Thu nhập từ cổ tức, lãi tiền gửi và lãi/lỗ từ hoạt động đầu tư khác"              
## [10] "(Lãi)/lỗ chia từ công ty liên kết, liên doanh"                                    
## [11] "Chi phí lãi vay"                                                                  
## [12] "Thu nhập từ khoản vay được miễn giảm"                                             
## [13] "Lợi nhuận từ hoạt động kinh doanh trước những thay đổi vốn lưu động"              
## [14] "Biến động các khoản phải thu"                                                     
## [15] "Biến động hàng tồn kho"                                                           
## [16] "Biến động các khoản phải trả và nợ phải trả khác"                                 
## [17] "Biến động chi phí trả trước"                                                      
## [18] "Tiền chi mua TSCĐ và tài sản dài hạn khác"                                        
## [19] "Tiền lãi vay đã trả"                                                              
## [20] "Thuế thu nhập doanh nghiệp đã nộp"                                                
## [21] "Tiền chi khác cho hoạt động kinh doanh"                                           
## [22] "Lưu chuyển tiền thuần từ hoạt động kinh doanh"                                    
## [23] "Tiền chi mua tài sản cố định và tài sản dài hạn khác"                             
## [24] "Tiền thu từ thanh lý tài sản cố định và xây dựng cơ bản dở dang"                  
## [25] "Tiền chi tiền gửi có kì hạn"                                                      
## [26] "Tiền thu gửi có kỳ hạn"                                                           
## [27] "Tiền chi cho vay"                                                                 
## [28] "Tiền thu hồi cho vay"                                                             
## [29] "Tiền thu hồi từ bán công cụ nợ của đơn vị khác"                                   
## [30] "Tiền chi đầu tư góp vốn vào các đơn vị khác"                                      
## [31] "Tiền thu hồi đầu tư vào các đơn vị khác"                                          
## [32] "Tiền thu lãi tiền gửi và cổ tức"                                                  
## [33] "Khoản tiền thuần chi cho hợp nhất kinh doanh"                                     
## [34] "Lưu chuyển tiền thuần từ hoạt động đầu tư"                                        
## [35] "III. LƯU CHUYỂN TIỀN TỪ HOẠT ĐỘNG TÀI CHÍNH"                                      
## [36] "Tiền thu từ bán cổ phiếu quỹ"                                                     
## [37] "Tiền thu từ phát hành cổ phiếu bởi công ty con cho cổ đông không kiểm soát"       
## [38] "Tiền thu từ nhận góp vốn của cổ đông không kiểm soát"                             
## [39] "Tiền hoàn trả vốn góp cho cổ đông không kiểm soát của công ty con đã giải thể"    
## [40] "Tiền thu góp vốn của cổ đông không kiểm soát"                                     
## [41] "Tiền thu từ đi vay"                                                               
## [42] "Tiền chi trả nợ gốc vay"                                                          
## [43] "Tiền chi trả cổ tức"                                                              
## [44] "Tiền chi trả cổ tức của các công ty con chia cho cổ đông không kiểm soát"         
## [45] "Lưu chuyển tiền thuần từ hoạt động tài chính"                                     
## [46] "Lưu chuyển tiền thuần trong năm"                                                  
## [47] "Tiền và các khoản tương đương tiền đầu năm"                                       
## [48] "Ảnh hưởng của thay đổi tỷ giá hối đoái đối với tiền và các khoản tương đương tiền"
## [49] "Chênh lệch quy đổi tiền tệ"                                                       
## [50] "Tiền và các khoản tương đương tiền cuối năm"
Giải thích kỹ thuật:

Hàm names() trả về một vector chứa tên của tất cả các cột trong data frame.

Điều này cho phép người phân tích nắm được toàn bộ các chỉ tiêu tài chính có sẵn trong mỗi báo cáo.

2.5. Chọn các biến quan trọng trong từng bảng

# Bảng CÂN ĐỐI KẾ TOÁN
CDKT_chon <- CDKT %>%
  select(
    Năm,
    `TỔNG CỘNG TÀI SẢN`,
    `NỢ PHẢI TRẢ`,
    `Nợ ngắn hạn`,
    `Vốn chủ sở hữu`,
    `Tiền và các khoản tương đương tiền`,
    `Phải thu khách hàng`,
    `Hàng tồn kho`
  )

# Bảng KẾT QUẢ HOẠT ĐỘNG KINH DOANH
HDKD_chon <- HDKD %>%
  select(
    Năm,
    `Doanh thu thuần về bán hàng và cung cấp dịch vụ (10 = 01 - 02)`,
    `Lợi nhuận gộp về bán hàng và cung cấp dịch vụ (20 = 10 - 11)`,
    `Lợi nhuận thuần từ hoạt động kinh doanh`,
    `Lợi nhuận sau thuế TNDN`,
    `Trong đó: Chi phí lãi vay`
  )

# Bảng LƯU CHUYỂN TIỀN TỆ
LCTT_chon <- LCTT %>%
  select(
    Năm,
    `Lưu chuyển tiền thuần từ hoạt động kinh doanh`,
    `Lưu chuyển tiền thuần từ hoạt động đầu tư`,
    `Lưu chuyển tiền thuần từ hoạt động tài chính`,
    `Tiền chi trả cổ tức`
  )
Giải thích kỹ thuật:

Hàm select() từ package dplyr cho phép chọn các cột cụ thể từ data frame dựa trên tên biến.

Dấu ` (backtick) được sử dụng để bao quanh tên biến có chứa khoảng trắng, ký tự đặc biệt hoặc ký tự tiếng Việt có dấu.

Toán tử %>% (pipe operator) giúp nối các thao tác xử lý dữ liệu một cách tuần tự và dễ đọc.

2.6. Gộp ba bảng lại

vnm <- CDKT_chon %>%
  full_join(HDKD_chon, by = "Năm") %>%
  full_join(LCTT_chon, by = "Năm") %>%
  arrange(Năm)
Giải thích kỹ thuật:

full_join() là hàm từ package dplyr, thực hiện phép nối (join) hai data frame dựa trên một hoặc nhiều cột khóa chung.

Tham số by = "Năm" chỉ định biến "Năm" là khóa để ghép các bảng.

full_join() giữ lại tất cả các hàng từ cả hai bảng, kể cả khi không có giá trị khớp (khác với inner_join() chỉ giữ hàng khớp).

arrange(Năm) sắp xếp dữ liệu theo thứ tự tăng dần của năm.

2.7. Kiểm tra trùng lặp

sum(duplicated(vnm))
## [1] 0
Giải thích kỹ thuật:

duplicated() trả về một vector logic, với giá trị TRUE cho các hàng bị trùng lặp và FALSE cho hàng duy nhất.

sum() đếm số lượng giá trị TRUE, tức là số hàng trùng lặp.

2.8. Kiểm tra giá trị thiếu

colSums(is.na(vnm))
##                                                            Năm 
##                                                              0 
##                                              TỔNG CỘNG TÀI SẢN 
##                                                              0 
##                                                    NỢ PHẢI TRẢ 
##                                                              0 
##                                                    Nợ ngắn hạn 
##                                                              0 
##                                                 Vốn chủ sở hữu 
##                                                              0 
##                             Tiền và các khoản tương đương tiền 
##                                                              0 
##                                            Phải thu khách hàng 
##                                                              0 
##                                                   Hàng tồn kho 
##                                                              0 
## Doanh thu thuần về bán hàng và cung cấp dịch vụ (10 = 01 - 02) 
##                                                              0 
##   Lợi nhuận gộp về bán hàng và cung cấp dịch vụ (20 = 10 - 11) 
##                                                              0 
##                        Lợi nhuận thuần từ hoạt động kinh doanh 
##                                                              0 
##                                        Lợi nhuận sau thuế TNDN 
##                                                              0 
##                                      Trong đó: Chi phí lãi vay 
##                                                              0 
##                  Lưu chuyển tiền thuần từ hoạt động kinh doanh 
##                                                              0 
##                      Lưu chuyển tiền thuần từ hoạt động đầu tư 
##                                                              0 
##                   Lưu chuyển tiền thuần từ hoạt động tài chính 
##                                                              0 
##                                            Tiền chi trả cổ tức 
##                                                              0
Giải thích kỹ thuật:

is.na() kiểm tra từng ô dữ liệu, trả về TRUE nếu giá trị là NA (missing) và FALSE nếu không.

colSums() tính tổng theo cột, cho biết mỗi biến có bao nhiêu giá trị bị thiếu.

CHƯƠNG 3: XỬ LÝ DỮ LIỆU

1. Chuẩn hóa tên cột

library(stringi)
## Warning: package 'stringi' was built under R version 4.5.2
names(vnm) <- names(vnm) %>%
  stri_trans_general("Latin-ASCII") %>%   
  str_to_lower() %>%
  str_replace_all("[^a-z0-9]+", "_") %>%
  str_replace_all("_+", "_") %>% 
  str_remove("^_|_$")
names(vnm)
##  [1] "nam"                                                     
##  [2] "tong_cong_tai_san"                                       
##  [3] "no_phai_tra"                                             
##  [4] "no_ngan_han"                                             
##  [5] "von_chu_so_huu"                                          
##  [6] "tien_va_cac_khoan_tuong_duong_tien"                      
##  [7] "phai_thu_khach_hang"                                     
##  [8] "hang_ton_kho"                                            
##  [9] "doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02"
## [10] "loi_nhuan_gop_ve_ban_hang_va_cung_cap_dich_vu_20_10_11"  
## [11] "loi_nhuan_thuan_tu_hoat_dong_kinh_doanh"                 
## [12] "loi_nhuan_sau_thue_tndn"                                 
## [13] "trong_do_chi_phi_lai_vay"                                
## [14] "luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh"           
## [15] "luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu"               
## [16] "luu_chuyen_tien_thuan_tu_hoat_dong_tai_chinh"            
## [17] "tien_chi_tra_co_tuc"
Giải thích kỹ thuật:

stri_trans_general("Latin-ASCII") từ package stringi chuyển đổi ký tự Unicode có dấu sang ký tự ASCII không dấu (ví dụ: "ả" → "a").

str_to_lower() chuyển tất cả ký tự sang chữ thường.

str_replace_all("[^a-z0-9]+", "_") thay thế mọi ký tự không phải chữ cái hoặc số bằng dấu gạch dưới.

str_replace_all("_+", "_") gộp nhiều dấu gạch dưới liên tiếp thành một.

str_remove("^_|_$") loại bỏ dấu gạch dưới ở đầu hoặc cuối tên biến.

2. Tổng tài sản bình quân (Average Total Assets)

vnm <- vnm %>%
  arrange(nam) %>%
  mutate(tai_san_bq = (tong_cong_tai_san + lag(tong_cong_tai_san)) / 2)
Giải thích kỹ thuật:

arrange(nam) sắp xếp dữ liệu theo thứ tự tăng dần của năm, đảm bảo hàm lag() hoạt động đúng.

lag() lấy giá trị của biến ở hàng trước đó (năm t-1).

mutate() tạo biến mới tai_san_bq là trung bình cộng của tài sản năm hiện tại và năm trước.

3. Tỷ suất lợi nhuận trên doanh thu (ROS – Return on Sales)

vnm <- vnm %>%
  mutate(ros = loi_nhuan_sau_thue_tndn / doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02)
Giải thích kỹ thuật:

mutate() tạo biến mới ros bằng cách chia Lợi nhuận sau thuế cho Doanh thu thuần.

Công thức: ROS = Lợi nhuận sau thuế / Doanh thu thuần × 100%.

4. Tỷ suất sinh lời trên tài sản (ROA – Return on Assets)

vnm <- vnm %>%
  mutate(roa = loi_nhuan_sau_thue_tndn / tai_san_bq)
Giải thích kỹ thuật:

mutate() tạo biến mới

ROA được tính bằng Lợi nhuận sau thuế chia cho Tổng tài sản bình quân.

Sử dụng tai_san_bq (đã tính ở bước 2) thay vì tài sản cuối kỳ.

5. Tỷ suất sinh lời trên vốn chủ sở hữu (ROE – Return on Equity)

vnm <- vnm %>%
  mutate(von_chu_bq = (von_chu_so_huu + lag(von_chu_so_huu)) / 2,
         roe = loi_nhuan_sau_thue_tndn / von_chu_bq)
Giải thích kỹ thuật:

mutate() tạo biến mới

Tương tự ROA, ROE cũng sử dụng giá trị bình quân của Vốn chủ sở hữu.

von_chu_bq là trung bình cộng của vốn chủ năm hiện tại và năm trước.

ROE = Lợi nhuận sau thuế / Vốn chủ sở hữu bình quân.

6. Tốc độ tăng trưởng doanh thu (Revenue Growth Rate)

vnm <- vnm %>%
  mutate(tang_truong_doanh_thu = (doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02 -
                                  lag(doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02)) /
                                  lag(doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02))
Giải thích kỹ thuật:

mutate() tạo biến mới

Công thức tính tốc độ tăng trưởng: (Giá trị hiện tại - Giá trị kỳ trước) / Giá trị kỳ trước.

lag() lấy doanh thu năm trước làm gốc so sánh.

Kết quả là tỷ lệ thay đổi tương đối, thường biểu diễn dưới dạng phần trăm.

7. Hệ số vốn chủ sở hữu (Equity ratio)

vnm <- vnm %>%
  mutate(ty_le_von_chu_so_huu = von_chu_so_huu / tong_cong_tai_san)
Giải thích kỹ thuật:

mutate() tạo biến mới

Công thức đơn giản: Vốn chủ sở hữu / Tổng tài sản.

Kết quả là tỷ lệ trong khoảng [0, 1], cho biết tỷ trọng vốn chủ sở hữu trong tổng nguồn vốn.

8. Quy mô doanh nghiệp

vnm <- vnm %>%
  mutate(size = log(tong_cong_tai_san))
Giải thích kỹ thuật:

mutate() tạo biến mới

log(): Hàm logarit tự nhiên (cơ số e) trong R.

Biến đổi logarit giúp chuẩn hóa phân phối và giảm độ lệch do sự chênh lệch lớn về quy mô.

9. Tỷ lệ nợ

vnm <- vnm %>%
  mutate(debt_ratio = no_phai_tra / tong_cong_tai_san)
Giải thích kỹ thuật:

mutate() tạo biến mới

Công thức: Tổng nợ phải trả / Tổng tài sản.

Kết quả trong khoảng [0, 1], cho biết tỷ lệ tài sản được tài trợ bằng nợ.

10. Khả năng thanh toán ngắn hạn (Current ratio)

vnm <- vnm %>%
  mutate(ts_ngan_han = tien_va_cac_khoan_tuong_duong_tien + phai_thu_khach_hang + hang_ton_kho,
         he_so_thanh_toan_ngan_han = ts_ngan_han / no_ngan_han)
Giải thích kỹ thuật:

Tạo biến ts_ngan_han bằng tổng ba thành phần thanh khoản chính: tiền, khoản phải thu và hàng tồn kho.

he_so_thanh_toan_ngan_han = Tài sản ngắn hạn / Nợ ngắn hạn.

11. Phân nhóm tăng trưởng doanh thu

vnm <- vnm %>%
  mutate(nhom_tang_truong = case_when(
    tang_truong_doanh_thu < 0 ~ "Suy giảm",
    tang_truong_doanh_thu < 0.1 ~ "Ổn định",
    TRUE ~ "Tăng trưởng cao"
  ))
Giải thích kỹ thuật:

case_when(): Hàm trong dplyr cho phép tạo nhiều điều kiện phân loại theo thứ tự ưu tiên.
Các điều kiện được đánh giá từ trên xuống dưới, điều kiện đầu tiên thỏa mãn sẽ được áp dụng.

TRUE ~ đóng vai trò như điều kiện "else", bắt tất cả các trường hợp còn lại.

12. Tạo nhóm theo hiệu quả lợi nhuận (hiệu quả cao – trung bình – thấp)

vnm <- vnm %>%
  mutate(
    nhom_hieu_qua = case_when(
      roe >= quantile(roe, 0.67, na.rm = TRUE) ~ "Hiệu quả cao",
      roe >= quantile(roe, 0.33, na.rm = TRUE) ~ "Hiệu quả trung bình",
      TRUE ~ "Hiệu quả thấp"
    )
  )
vnm %>% count(nhom_hieu_qua)
Giải thích kỹ thuật:

quantile(roe, 0.67, na.rm = TRUE): Tính phân vị thứ 67% (tercile thứ hai) của ROE, bỏ qua giá trị NA.

quantile(roe, 0.33, na.rm = TRUE): Tính phân vị thứ 33% (tercile thứ nhất).

Phân chia dữ liệu thành ba nhóm đều nhau dựa trên phân vị, đảm bảo mỗi nhóm chiếm khoảng 1/3 số quan sát.

13. Phân loại hiệu quả theo phân vị

vnm <- vnm %>%
  mutate(
    hieu_qua_ROE = case_when(
      roe >= quantile(roe, 0.67, na.rm = TRUE) ~ "Tốt",
      roe >= quantile(roe, 0.33, na.rm = TRUE) ~ "Trung bình",
      TRUE ~ "Thấp"
    ),
    hieu_qua_ROA = case_when(
      roa >= quantile(roa, 0.67, na.rm = TRUE) ~ "Tốt",
      roa >= quantile(roa, 0.33, na.rm = TRUE) ~ "Trung bình",
      TRUE ~ "Thấp"
    )
  )
Giải thích kỹ thuật:

Áp dụng cùng phương pháp phân vị như mục 12, nhưng tạo riêng hai biến phân loại cho ROE và ROA.

Nhãn được đơn giản hóa thành "Tốt", "Trung bình", "Thấp" để dễ diễn giải.

Hai biến này độc lập nhau, cho phép phân tích ma trận hiệu quả 3×3.

CHƯƠNG 4: CÁC THỐNG KÊ CƠ BẢN

1. Thống kê mô tả biến Doanh thu thuần

summary(vnm$doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
## 4.008e+13 5.142e+13 5.798e+13 5.495e+13 6.027e+13 6.178e+13
Giải thích kỹ thuật:

Hàm summary() là hàm cơ bản trong R dùng để tính toán các chỉ số thống kê mô tả cho biến số liên tục, bao gồm:

Min.: Giá trị nhỏ nhất trong tập dữ liệu

1st Qu.: Phân vị thứ nhất (25%) - giá trị mà 25% quan sát nhỏ hơn nó

Median: Trung vị (50%) - giá trị ở giữa khi sắp xếp tăng dần

Mean: Giá trị trung bình cộng

3rd Qu.: Phân vị thứ ba (75%) - giá trị mà 75% quan sát nhỏ hơn nó

Max.: Giá trị lớn nhất

Ý nghĩa thống kê:

Kết quả cho thấy doanh thu thuần của VNM giai đoạn 2015-2024 dao động từ 40.080 nghìn tỷ đồng (năm 2015) đến 61.780 nghìn tỷ đồng (năm 2024), với giá trị trung bình 54.950 nghìn tỷ đồng và trung vị 57.980 nghìn tỷ đồng.

Sự chênh lệch giữa trung bình và trung vị tương đối nhỏ (54.950 so với 57.980), cho thấy phân phối doanh thu nghiêng trái nhẹ (left-skewed), phản ánh xu hướng tăng trưởng doanh thu ổn định qua các năm. Khoảng tứ phân vị (IQR = Q3 - Q1 = 60.270 - 51.420 ≈ 8.850 nghìn tỷ) cho thấy 50% giá trị trung tâm phân bố trong biên độ khá hẹp, chứng tỏ doanh thu tương đối ổn định và ít biến động mạnh.

2. Thống kê mô tả biến Lợi nhuận sau thuế

summary(vnm$loi_nhuan_sau_thue_tndn)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
## 7.770e+12 9.105e+12 9.829e+12 9.709e+12 1.049e+13 1.124e+13
Giải thích kỹ thuật:

Tương tự biến doanh thu, hàm summary() được áp dụng cho biến lợi nhuận sau thuế thu nhập doanh nghiệp (LNST), trả về bộ 6 chỉ số thống kê mô tả cơ bản giúp đánh giá xu thế trung tâm, độ phân tán và hình dạng phân phối của biến.

Ý nghĩa thống kê:

Lợi nhuận sau thuế của VNM biến động từ 7.770 nghìn tỷ đồng đến 11.240 nghìn tỷ đồng, với trung bình 9.709 nghìn tỷ đồng và trung vị 9.829 nghìn tỷ đồng.

Giá trị trung bình gần với trung vị cho thấy phân phối lợi nhuận tương đối đối xứng, phản ánh sự ổn định trong khả năng sinh lời của doanh nghiệp. Tuy nhiên, biên độ dao động (Max - Min ≈ 3.470 nghìn tỷ) tương đối lớn, tương đương 44,7% so với giá trị nhỏ nhất, cho thấy lợi nhuận chịu ảnh hưởng đáng kể từ các yếu tố kinh doanh và thị trường qua các năm. Điều này đặt ra yêu cầu phân tích sâu hơn về các yếu tố tác động đến biến động lợi nhuận.

3. Thống kê tóm tắt mô tả biến tổng tài sản

vnm %>%
  summarise(
    mean = mean(tong_cong_tai_san, na.rm = TRUE),
    sd = sd(tong_cong_tai_san, na.rm = TRUE),
    min = min(tong_cong_tai_san, na.rm = TRUE),
    max = max(tong_cong_tai_san, na.rm = TRUE),
    median = median(tong_cong_tai_san, na.rm = TRUE)
  )

Giải thích kỹ thuật:

summarise() (từ package dplyr): tạo bảng tóm tắt với các chỉ số được tính toán

mean(): tính giá trị trung bình cộng

sd(): tính độ lệch chuẩn (standard deviation) - đo lường độ phân tán xung quanh giá trị trung bình

min(), max(): giá trị nhỏ nhất và lớn nhất

median(): trung vị

Tham số na.rm = TRUE: loại bỏ giá trị thiếu (NA) khỏi tính toán

Ý nghĩa thống kê:

Tổng tài sản bình quân của VNM đạt 43.200 nghìn tỷ đồng với độ lệch chuẩn 10.200 nghìn tỷ đồng (hệ số biến thiên CV = 10.200/43.200 ≈ 23,6%), cho thấy quy mô tài sản có biến động đáng kể qua thời gian nghiên cứu.

Tài sản tăng từ 27.500 nghìn tỷ đồng (2015) lên 55.000 nghìn tỷ đồng (2024), tương đương mức tăng trưởng 100% trong 9 năm (CAGR ≈ 8,0% mỗi năm). Trung vị (46.600 nghìn tỷ) cao hơn trung bình (43.200 nghìn tỷ), cho thấy phân phối nghiêng phải (right-skewed), phản ánh xu hướng gia tăng quy mô tài sản mạnh mẽ trong những năm gần đây, đặc biệt từ 2019 trở đi khi công ty thực hiện chiến lược mở rộng và M&A

4. Những năm có dòng tiền hoạt động âm

vnm_cfo_am <- vnm %>%
  filter(luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh < 0)
vnm_cfo_am
Giải thích kỹ thuật:

filter(): hàm lọc dữ liệu theo điều kiện logic

Điều kiện < 0: giữ lại các quan sát có dòng tiền thuần từ hoạt động kinh doanh (CFO - Cash Flow from Operations) âm

Kết quả là một tập con (subset) chỉ chứa các năm không đạt dòng tiền dương từ hoạt động cốt lõi

Ý nghĩa thống kê:

Kết quả trả về 0 quan sát, chứng tỏ VNM duy trì dòng tiền hoạt động dương liên tục trong toàn bộ giai đoạn 2015-2024. Đây là dấu hiệu tích cực về chất lượng lợi nhuận và sức khỏe tài chính.

Dòng tiền hoạt động dương thường xuyên cho thấy:

Lợi nhuận kế toán có nền tảng tiền mặt thực (không chỉ là lợi nhuận “trên giấy”)

Doanh nghiệp có khả năng thu hồi công nợ tốt

Chu kỳ chuyển đổi tiền mặt (cash conversion cycle) được quản lý hiệu quả

Công ty có nguồn lực nội sinh để tái đầu tư và chi trả cổ tức

Điều này đặc biệt quan trọng trong ngành sữa - ngành có đặc thủy chu kỳ sản xuất dài, hàng tồn kho lớn và yêu cầu vốn lưu động cao.

5. So sánh các chỉ tiêu tài chính theo nhóm tăng trưởng

vnm %>%
  group_by(nhom_tang_truong) %>%
  summarise(across(c(roa, roe, ros, debt_ratio), mean, na.rm = TRUE))
## Warning: There was 1 warning in `summarise()`.
## ℹ In argument: `across(c(roa, roe, ros, debt_ratio), mean, na.rm = TRUE)`.
## ℹ In group 1: `nhom_tang_truong = "Suy giảm"`.
## Caused by warning:
## ! The `...` argument of `across()` is deprecated as of dplyr 1.1.0.
## Supply arguments directly to `.fns` through an anonymous function instead.
## 
##   # Previously
##   across(a:b, mean, na.rm = TRUE)
## 
##   # Now
##   across(a:b, \(x) mean(x, na.rm = TRUE))
Giải thích kỹ thuật:

group_by(nhom_tang_truong): gom nhóm dữ liệu theo biến phân loại "nhóm tăng trưởng"

across(): áp dụng cùng một hàm cho nhiều cột cùng lúc

c(roa, roe, ros, debt_ratio): danh sách các biến cần tính toán

mean: hàm tính trung bình được áp dụng cho từng biến trong từng nhóm

na.rm = TRUE: bỏ qua giá trị thiếu

Ý nghĩa thống kê:

Kết quả cho thấy mối quan hệ rõ rệt giữa tốc độ tăng trưởng doanh thu và hiệu quả tài chính:

Nhóm “Tăng trưởng cao” (tăng trưởng doanh thu ≥ 10%):ROA = 32,9%, ROE = 43,2%, ROS = 19,7%. Tỷ lệ nợ thấp nhất (23,8%). Phản ánh giai đoạn hoạt động hiệu quả nhất, công ty tận dụng tối ưu tài sản và vốn chủ để tạo ra lợi nhuận cao Nhóm “Ổn định” (0% < tăng trưởng < 10%): ROA = 23,8%, ROE = 34,6%, ROS = 17,8%. Tỷ lệ nợ cao hơn (32,2%). Các chỉ số sinh lời ở mức trung bình, cho thấy doanh nghiệp đang trong giai đoạn duy trì và củng cố Nhóm “Suy giảm” (tăng trưởng âm): ROA = 16,8%, ROE = 25,0%, ROS = 14,3%. Tỷ lệ nợ 32,3%. Các chỉ số thấp nhất, phản ánh năm 2022 khi doanh thu giảm do ảnh hưởng hậu COVID-19

Phân tích này cho thấy tăng trưởng doanh thu là động lực chính thúc đẩy hiệu quả tài chính. Đồng thời, các năm tăng trưởng cao thường đi kèm với cơ cấu vốn lành mạnh hơn (nợ thấp), chứng tỏ công ty ưu tiên tăng trưởng bền vững.

6. Phân tích biến động theo thời gian

vnm <- vnm %>%
  arrange(nam) %>%
  mutate(
    delta_doanh_thu = doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02 - lag(doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02),
    delta_loi_nhuan = loi_nhuan_sau_thue_tndn - lag(loi_nhuan_sau_thue_tndn),
    delta_roa = roa - lag(roa),
    delta_roe = roe - lag(roe)
  )
vnm %>% 
  select(nam, delta_doanh_thu, delta_loi_nhuan, delta_roa, delta_roe)
Giải thích kỹ thuật:

arrange(nam): sắp xếp dữ liệu theo thứ tự thời gian tăng dần

mutate(): tạo biến mới

lag(): hàm lấy giá trị của quan sát trước đó (năm t-1)

Phép trừ giá trị hiện tại - lag(giá trị): tính biến động tuyệt đối (delta) giữa hai năm liên tiếp

Các biến delta được tạo ra giúp phân tích tốc độ thay đổi năm-qua-năm (year-over-year change) của các chỉ tiêu.

Ý nghĩa thống kê:

Giai đoạn 2016–2017 – Tăng trưởng mạnh: Doanh thu và lợi nhuận tăng nhanh (doanh thu +6.714 và +4.247 tỷ; lợi nhuận +1.590 và +914 tỷ), ROE cải thiện +1,2 điểm %. Vinamilk hưởng lợi từ mở rộng thị trường, nhu cầu tiêu dùng và xuất khẩu tăng.

Giai đoạn 2018–2019 – Hiệu quả suy giảm: Lợi nhuận giảm nhẹ (-72,5 tỷ), ROA và ROE giảm mạnh (-3,7 điểm %) do giá nguyên liệu tăng và chi phí marketing lớn. Biên lợi nhuận thu hẹp phản ánh cạnh tranh gay gắt trong ngành.

Giai đoạn 2020–2021 – Phục hồi rồi chững lại: Năm 2020 lợi nhuận tăng (+681 tỷ) nhờ tiêu dùng phục hồi, nhưng năm 2021 giảm mạnh (-603 tỷ), ROE giảm -4,86 điểm % do ảnh hưởng kéo dài của COVID-19 và chi phí logistics tăng cao.

Năm 2022 – Suy giảm mạnh nhất: Doanh thu giảm lần đầu (-962,9 tỷ), lợi nhuận giảm sâu (-2.050 tỷ), ROE giảm -5,62 điểm % do lạm phát, chi phí nguyên liệu cao và sức mua yếu.

Giai đoạn 2023–2024 – Phục hồi ổn định: Doanh thu và lợi nhuận tăng trở lại, ROE cải thiện +1,61 điểm %, cho thấy VNM đã vượt qua giai đoạn khó khăn, hoạt động tài chính phục hồi bền vững.

7. Phân tích hệ số biến thiên (CV) của các chỉ tiêu

cv <- function(x) sd(x, na.rm = TRUE) / mean(x, na.rm = TRUE)

vnm %>%
  summarise(across(c(roa, roe, ros, debt_ratio, tang_truong_doanh_thu), cv))

Giải thích kỹ thuật:

Hàm cv(): tính hệ số biến thiên (Coefficient of Variation)

across(): áp dụng hàm cv() cho nhiều biến cùng lúc

Ý nghĩa thống kê:

Kết quả cho thấy mức độ ổn định khác nhau của các chỉ tiêu:

ROS (12,4%): Biến động thấp nhất, cho thấy biên lợi nhuận tương đối ổn định qua các năm. Điều này phản ánh khả năng kiểm soát chi phí và định giá hiệu quả của VNM.

Debt Ratio (12,5%): Tỷ lệ nợ cũng ổn định, chứng tỏ công ty duy trì cơ cấu vốn nhất quán, không có biến động lớn về chính sách tài trợ.

ROE (21,9%): Biến động trung bình, phản ánh hiệu quả sử dụng vốn chủ chịu ảnh hưởng từ cả lợi nhuận và đòn bẩy tài chính.

ROA (25,8%): Biến động cao hơn ROE, cho thấy hiệu quả sử dụng tổng tài sản bị tác động nhiều hơn bởi các yếu tố hoạt động.

Tăng trưởng doanh thu (109%): Biến động cực cao, phản ánh sự chênh lệch lớn giữa các năm tăng trưởng mạnh và năm suy giảm (2022). Đây là biến số kém ổn định nhất, chịu tác động mạnh từ chu kỳ kinh tế và yếu tố bên ngoài.

Phân tích CV cho thấy VNM duy trì được ổn định về mặt lợi nhuận và cơ cấu vốn (ROS, debt ratio), nhưng doanh thu và hiệu suất tài sản biến động nhiều hơn do ảnh hưởng của môi trường kinh doanh.

8. Các năm có hiệu quả vượt trung bình

vnm %>%
  filter(roa > mean(roa, na.rm = TRUE) & roe > mean(roe, na.rm = TRUE)) %>%
  select(nam, roa, roe, ros, debt_ratio)

Giải thích kỹ thuật:

filter(): lọc các quan sát thỏa mãn đồng thời hai điều kiện

Điều kiện 1: roa > mean(roa, na.rm = TRUE) - ROA cao hơn trung bình

Điều kiện 2: roe > mean(roe, na.rm = TRUE) - ROE cao hơn trung bình

Toán tử &: phép AND logic, yêu cầu cả hai điều kiện đều đúng

select(): chọn các cột cần hiển thị

Ý nghĩa thống kê:

Giai đoạn 2016–2020: Hoàng kim

Trong 5 năm này, ROA và ROE đều vượt mức trung bình, phản ánh hiệu quả vận hành cao. Năm 2016–2017 là đỉnh cao: ROE đạt 43,2% và 44,4%, ROA đạt 32,9% và 32,1%, ROS ổn định quanh 20%, tỷ lệ nợ thấp (23,7%–31,1%). Đây là kết quả từ các thương vụ M&A Driftwood Dairy và Angkor Dairy, tối ưu hóa lợi nhuận và vốn chủ sở hữu.

2018–2020: Hiệu quả giảm dần. ROE giảm xuống 40,7%–35,5%, ROA giảm tương ứng, tỷ lệ nợ tăng nhẹ (29,7%–33,5%). Hiệu quả vẫn cao nhưng áp lực cạnh tranh và chi phí đầu vào bắt đầu ảnh hưởng đến kết quả tài chính.

Giai đoạn 2021–2024: Dưới trung bình. ROE dao động 25,0%–30,6%, ROA dao động 16,8%–22,3%. COVID-19, lạm phát và cạnh tranh gia tăng khiến lợi nhuận và hiệu quả sử dụng tài sản giảm.

Tổng thể, VNM trải qua chu kỳ tăng trưởng mạnh, sau đó bước vào giai đoạn điều chỉnh và phục hồi cần quản trị rủi ro.

9. Ma trận tương quan các chỉ tiêu chính

vars <- vnm %>%
  select(
    roa, roe,
    debt_ratio, 
    tang_truong_doanh_thu,
    size,
    von_chu_so_huu,
    tong_cong_tai_san
  )

round(cor(vars, use = "pairwise.complete.obs"), 3)
##                          roa    roe debt_ratio tang_truong_doanh_thu   size
## roa                    1.000  0.991     -0.735                 0.854 -0.924
## roe                    0.991  1.000     -0.661                 0.806 -0.879
## debt_ratio            -0.735 -0.661      1.000                -0.786  0.901
## tang_truong_doanh_thu  0.854  0.806     -0.786                 1.000 -0.812
## size                  -0.924 -0.879      0.901                -0.812  1.000
## von_chu_so_huu        -0.925 -0.894      0.828                -0.763  0.988
## tong_cong_tai_san     -0.929 -0.892      0.877                -0.786  0.996
##                       von_chu_so_huu tong_cong_tai_san
## roa                           -0.925            -0.929
## roe                           -0.894            -0.892
## debt_ratio                     0.828             0.877
## tang_truong_doanh_thu         -0.763            -0.786
## size                           0.988             0.996
## von_chu_so_huu                 1.000             0.995
## tong_cong_tai_san              0.995             1.000
Giải thích kỹ thuật:

select(): chọn tập con các biến số cần phân tích tương quan

cor(): tính ma trận tương quan Pearson giữa các cặp biến

Tham số use = "pairwise.complete.obs": với mỗi cặp biến, chỉ sử dụng các quan sát có đầy đủ cả hai giá trị (loại bỏ NA theo cặp)

round(..., 3): làm tròn kết quả đến 3 chữ số thập phân

Hệ số tương quan Pearson (r) dao động từ -1 đến +1:

r > 0: tương quan dương (cùng chiều)

r < 0: tương quan âm (ngược chiều)

|r| càng gần 1: mối quan hệ tuyến tính càng mạnh

Ý nghĩa thống kê:

Ma trận tương quan cho thấy các mối quan hệ đáng chú ý:

Nhóm tương quan dương mạnh:

ROE và ROA có tương quan rất cao (0.991), điều này hợp lý vì cả hai đều đo lường hiệu suất sinh lời, chỉ khác mẫu số (vốn chủ vs tài sản). Mối quan hệ gần như tuyệt đối này cho thấy cấu trúc vốn của VNM tương đối ổn định.

Tăng trưởng doanh thu tương quan dương mạnh với ROA (0.854) và ROE (0.806), chứng tỏ các năm tăng trưởng doanh thu cao thường đi kèm với hiệu quả sinh lời tốt—phản ánh khả năng quản lý chi phí và tận dụng quy mô kinh tế.

Size, vốn chủ sở hữu và tổng tài sản có tương quan cực cao với nhau (> 0.99), điều này đương nhiên vì size = log(tài sản) và vốn chủ là thành phần chính của tài sản.

Nhóm tương quan âm mạnh:

ROA và ROE tương quan âm mạnh với size (-0.924 và -0.879), von_chu_so_huu (-0.925 và -0.894), tong_cong_tai_san (-0.929 và -0.892). Đây là phát hiện quan trọng: khi VNM mở rộng quy mô (tăng tài sản, vốn), hiệu suất sinh lời có xu hướng giảm. Điều này phản ánh hiện tượng “giảm lợi nhuận biên” (diminishing returns) khi doanh nghiệp trưởng thành—các khoản đầu tư mới khó đạt được hiệu quả cao như giai đoạn đầu.

Tỷ lệ nợ (debt_ratio) tương quan âm với ROA (-0.735) và ROE (-0.661), cho thấy các năm VNM tăng đòn bẩy tài chính thường có hiệu suất sinh lời thấp hơn. Điều này trái với lý thuyết đòn bẩy tích cực, gợi ý rằng VNM có thể sử dụng nợ vào các khoản đầu tư chưa sinh lời ngay hoặc chi phí lãi vay cao làm xói mòn lợi nhuận.

Debt_ratio tương quan dương với size (0.901), nghĩa là khi quy mô tăng, VNM có xu hướng tăng sử dụng nợ vay—chiến lược tài trợ phổ biến trong giai đoạn mở rộng.

Tăng trưởng doanh thu tương quan âm với debt_ratio (-0.786), cho thấy các năm tăng trưởng cao thường là những năm VNM giảm tỷ lệ nợ hoặc tăng cường vốn chủ sở hữu.

Hàm ý quản trị: Ma trận này cung cấp bằng chứng định lượng về trade-off giữa tăng trưởng quy mô và hiệu quả hoạt động. VNM cần cân bằng giữa việc mở rộng (để duy trì vị thế thị trường) và duy trì lợi nhuận biên (để thỏa mãn cổ đông). Đồng thời, việc sử dụng nợ cần được tối ưu hóa để đảm bảo các khoản vay thực sự tạo ra giá trị gia tăng

10. Tăng/giảm tuyệt đối Tài sản & Vốn chủ theo năm

vnm %>%
  arrange(nam) %>%
  mutate(delta_ts = tong_cong_tai_san - lag(tong_cong_tai_san),
         delta_vc = von_chu_so_huu - lag(von_chu_so_huu)) %>%
  select(nam, tong_cong_tai_san, delta_ts, von_chu_so_huu, delta_vc)
Giải thích kỹ thuật:

arrange(nam): Sắp xếp dữ liệu theo thứ tự năm tăng dần để đảm bảo tính toán chênh lệch đúng thời gian.

mutate(delta_ts = tong_cong_tai_san - lag(tong_cong_tai_san)): Hàm lag() lấy giá trị của năm trước đó, trừ đi để tính biến động tuyệt đối của tổng tài sản.

delta_vc = von_chu_so_huu - lag(von_chu_so_huu): Tương tự, tính biến động tuyệt đối của vốn chủ sở hữu.

select(...): Chọn các cột cần thiết để hiển thị kết quả một cách rõ ràng.

Ý nghĩa thống kê: Bảng kết quả cho thấy sự biến động quy mô doanh nghiệp qua các năm. Giai đoạn 2015-2020 ghi nhận xu hướng tăng trưởng đều đặn về cả tài sản và vốn chủ sở hữu, với mức tăng cao nhất vào năm 2019 (tài sản tăng 7.330 nghìn tỷ, vốn chủ tăng 3.460 nghìn tỷ), phản ánh giai đoạn mở rộng mạnh mẽ của VNM thông qua đầu tư và tái đầu tư lợi nhuận.

Đặc biệt, năm 2022 là năm duy nhất ghi nhận mức giảm tổng tài sản (-4.850 nghìn tỷ) và vốn chủ sở hữu (-3.030 nghìn tỷ), có thể do tác động của chính sách tái cấu trúc, thanh lý tài sản kém hiệu quả, hoặc ảnh hưởng của biến động kinh tế vĩ mô. Sau đó, năm 2023-2024 cho thấy xu hướng phục hồi với mức tăng trưởng ổn định hơn, thể hiện sự điều chỉnh và thích nghi của doanh nghiệp.

11. Các năm có nợ cao nhưng lợi nhuận vẫn tốt

vnm %>%
  filter(debt_ratio > mean(debt_ratio, na.rm = TRUE),
         roe > mean(roe, na.rm = TRUE)) %>%
  select(nam, roe, roa, debt_ratio)
Giải thích kỹ thuật:

filter(debt_ratio > mean(debt_ratio, na.rm = TRUE)): Lọc các năm có tỷ lệ nợ trên tài sản vượt mức trung bình toàn giai đoạn.

roe > mean(roe, na.rm = TRUE): Đồng thời lọc các năm có ROE (tỷ suất sinh lời trên vốn chủ) cao hơn mức trung bình.

Kết hợp hai điều kiện bằng dấu phẩy (tương đương toán tử &).

Ý nghĩa thống kê: Kết quả chỉ ra hai năm 2017 và 2019 đồng thời thỏa mãn cả hai điều kiện: tỷ lệ nợ cao (lần lượt 31,1% và 33,5%, cao hơn mức trung bình khoảng 29%) nhưng vẫn duy trì hiệu quả sinh lời vượt trội với ROE đạt 44,4% và 37,7%.

Điều này phản ánh chiến lược tài chính hiệu quả của VNM trong giai đoạn này: doanh nghiệp tận dụng đòn bẩy tài chính (sử dụng nợ vay) để tài trợ cho các dự án đầu tư sinh lời, mà không làm suy giảm lợi nhuận. Mức ROA tương ứng (32,1% và 25,7%) cũng ở mức cao, chứng tỏ hiệu quả sử dụng tài sản tốt. Đây là dấu hiệu tích cực về năng lực quản trị tài chính và khả năng tạo ra giá trị từ các nguồn vốn huy động.

12. Mức chi trả cổ tức so với ROE

vnm %>%
  mutate(ty_le_co_tuc_roe = tien_chi_tra_co_tuc / (roe * von_chu_so_huu)) %>%
  select(nam, tien_chi_tra_co_tuc, roe, ty_le_co_tuc_roe)
Giải thích kỹ thuật:

mutate(ty_le_co_tuc_roe = tien_chi_tra_co_tuc / (roe * von_chu_so_huu)): Tính tỷ lệ giữa số tiền chi trả cổ tức thực tế và tổng lợi nhuận thuộc về chủ sở hữu (được tính bằng roe * von_chu_so_huu).

select(...): Chọn các biến liên quan để hiển thị.

Ý nghĩa thống kê:

Tỷ lệ âm trong kết quả (dao động từ -0,548 đến -0,981) thực chất phản ánh tỷ lệ chi trả cổ tức (payout ratio). Sau khi lấy trị tuyệt đối, ta thấy VNM duy trì chính sách chi trả cổ tức ở mức 54,8% đến 98,1% lợi nhuận, với xu hướng tăng dần qua các năm.

Đặc biệt, năm 2022 ghi nhận tỷ lệ chi trả cao nhất (98,1%), cho thấy doanh nghiệp gần như phân phối toàn bộ lợi nhuận cho cổ đông, phản ánh chính sách cổ tức hào phóng nhằm duy trì lòng tin nhà đầu tư trong bối cảnh lợi nhuận sụt giảm. Ngược lại, những năm có ROE cao như 2016-2017 có tỷ lệ chi trả thấp hơn (55-68%), cho thấy doanh nghiệp giữ lại nhiều lợi nhuận để tái đầu tư vào tăng trưởng.

Chính sách này cần được cân nhắc cẩn trọng: tỷ lệ chi trả quá cao có thể hạn chế nguồn vốn tự có cho đầu tư phát triển dài hạn.

13. Hiệu suất sử dụng dòng tiền (Cash Conversion Efficiency)

vnm %>%
  mutate(ty_le_cce = luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh / loi_nhuan_sau_thue_tndn) %>%
  select(nam, luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh, loi_nhuan_sau_thue_tndn, ty_le_cce)
Giải thích kỹ thuật:

mutate(ty_le_cce = luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh / loi_nhuan_sau_thue_tndn): Tính tỷ lệ giữa dòng tiền thuần từ hoạt động kinh doanh (CFO) và lợi nhuận sau thuế. Chỉ số này đo lường khả năng chuyển đổi lợi nhuận kế toán thành tiền mặt thực tế.

Giá trị lý tưởng > 1 cho thấy doanh nghiệp tạo ra tiền mặt nhiều hơn lợi nhuận ghi nhận.

Ý nghĩa thống kê: Kết quả cho thấy CCE của VNM dao động từ 0,798 đến 1,080, với trung bình khoảng 0,94. Điều này có nghĩa là trung bình cứ 100 đồng lợi nhuận, VNM chuyển đổi được khoảng 94 đồng thành tiền mặt từ hoạt động kinh doanh.

Các năm 2019, 2022 và 2024 ghi nhận CCE > 1 (lần lượt 1,08; 1,03; 1,02), thể hiện hiệu suất chuyển đổi vượt trội—doanh nghiệp thu tiền nhanh hơn ghi nhận doanh thu, quản lý hàng tồn kho và công nợ hiệu quả. Ngược lại, năm 2018 có CCE thấp nhất (0,798), phản ánh áp lực về vốn lưu động, có thể do tăng trưởng nhanh làm ứ đọng hàng tồn kho hoặc kéo dài kỳ thu tiền.

Nhìn chung, VNM duy trì chỉ số CCE ở mức khá tốt (hầu hết > 0,85), chứng tỏ chất lượng lợi nhuận cao và khả năng tạo dòng tiền ổn định—yếu tố quan trọng đánh giá sức khỏe tài chính thực sự của doanh nghiệp.

14. Các năm có Dòng tiền tự do (Free Cash Flow) âm

library(dplyr)
library(readr)
## 
## Attaching package: 'readr'
## The following object is masked from 'package:scales':
## 
##     col_factor
vnm <- vnm %>%
  mutate(
    across(
      c(luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh,
        luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu),
      ~ parse_number(
          as.character(.),
          locale = locale(grouping_mark = ",", decimal_mark = ".")
        ) * ifelse(grepl("^\\(.*\\)$", as.character(.)), -1, 1)
    )
  ) %>%
  mutate(
    fcf = rowSums(across(c(luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh,
                           luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu)),
                  na.rm = FALSE)
  )
## Warning: There was 1 warning in `mutate()`.
## ℹ In argument: `across(...)`.
## Caused by warning:
## ! 2 parsing failures.
## row col expected actual
##   9  -- a number       
##  10  -- a number       
vnm_fcf_am <- vnm %>%
  filter(!is.na(fcf) & fcf < 0) %>%
  select(nam,
         luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh,
         luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu,
         fcf)

print(vnm_fcf_am)
## # A tibble: 0 × 4
## # ℹ 4 variables: nam <dbl>,
## #   luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh <dbl>,
## #   luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu <dbl>, fcf <dbl>
Giải thích kỹ thuật:
    
library(dplyr); library(readr): Nạp gói xử lý dữ liệu và ép kiểu số an toàn khi có dấu phẩy/ngàn.

vnm %>% mutate(across(..., ~ parse_number(...)*ifelse(grepl("^\\(.*\\)$", ...), -1, 1))):

across(...): áp dụng cùng một thao tác cho hai cột dòng tiền (..._kinh_doanh, ..._dau_tu).

parse_number(): chuyển ký tự có dấu phẩy/ngàn về số thực.

grepl("^\\(.*\\)$", ...): nhận diện số âm trong ngoặc ( … ) và nhân -1 để trả về dấu âm đúng.

mutate(fcf = rowSums(across(c(col1, col2)), na.rm = FALSE)): Tạo biến FCF = OCF + ICF (theo dấu kế toán; ICF thường âm khi đầu tư ròng ⇒ tương đương OCF − CAPEX). Dùng rowSums(across()) để đảm bảo cộng đúng kiểu numeric.

filter(!is.na(fcf) & fcf < 0): Lọc những năm FCF âm (dòng tiền tự do âm).

select(nam, ..., fcf): Giữ các cột cần báo cáo (năm, 2 dòng tiền cấu phần, FCF).

print(vnm_fcf_am): In bảng kết quả để kiểm tra/đính kèm báo cáo.

Ý nghĩa thống kê

Kết quả tính Dòng tiền tự do (Free Cash Flow – FCF) cho thấy mức chênh lệch giữa dòng tiền thuần từ hoạt động kinh doanh (CFO) và dòng tiền đầu tư (CFI) của từng năm.

Nếu FCF dương, điều đó phản ánh khả năng tự tài trợ tốt, doanh nghiệp tạo ra đủ dòng tiền từ hoạt động kinh doanh để trang trải cho đầu tư và vẫn còn dư tiền mặt. Đây là tín hiệu an toàn tài chính và hiệu quả sử dụng vốn cao.

Nếu FCF âm, có thể hiểu rằng các khoản đầu tư, mua sắm tài sản hoặc chi phí vốn vượt quá lượng tiền tạo ra từ hoạt động chính. Tuy nhiên, FCF âm không phải luôn tiêu cực — nó thường xuất hiện trong giai đoạn mở rộng quy mô, đầu tư dài hạn hoặc tái cấu trúc tài sản, nên cần xem xét song song với ROA, ROE và tỷ lệ nợ để đánh giá tính bền vững.

Trong trường hợp bảng kết quả không hiển thị năm nào có FCF âm, điều đó chứng tỏ VNM duy trì được dòng tiền hoạt động dương ổn định và đầu tư ở mức kiểm soát được, phản ánh sức khỏe tài chính tốt và khả năng tự cân đối dòng tiền cao trong giai đoạn 2015–2024.

15. Tỷ lệ tích lũy lại lợi nhuận (Retention Ratio)

vnm %>%
  mutate(ty_le_giu_lai = 1 - (tien_chi_tra_co_tuc / loi_nhuan_sau_thue_tndn)) %>%
  select(nam, loi_nhuan_sau_thue_tndn, tien_chi_tra_co_tuc, ty_le_giu_lai)
Giải thích kỹ thuật:

mutate(ty_le_giu_lai = 1 - (tien_chi_tra_co_tuc / loi_nhuan_sau_thue_tndn)): Tính tỷ lệ lợi nhuận được giữ lại để tái đầu tư, bằng 1 trừ đi tỷ lệ chi trả cổ tức.

Do tien_chi_tra_co_tuc mang dấu âm, phép tính sẽ cho giá trị > 1.
Cần điều chỉnh bằng cách lấy abs(tien_chi_tra_co_tuc) hoặc hiểu ngược: giá trị hiển thị = 1 + payout ratio.

Ý nghĩa thống kê:

Sau khi điều chỉnh dấu, kết quả cho thấy tỷ lệ giữ lại lợi nhuận của VNM dao động từ 56% (năm 2017) đến chỉ còn 6% (năm 2022). Xu hướng chung là tỷ lệ giữ lại có xu hướng giảm dần qua các năm, từ khoảng 56% (2017) xuống còn 15% (2024).

Điều này phản ánh sự thay đổi trong chiến lược phân bổ lợi nhuận của VNM: từ giai đoạn ưu tiên tái đầu tư (2015-2018, tỷ lệ giữ lại 56-78%) sang giai đoạn tập trung chia sẻ lợi nhuận với cổ đông (2019-2024, tỷ lệ chi trả cổ tức tăng lên 71-94%).

Năm 2022 đặc biệt nổi bật với tỷ lệ giữ lại chỉ 6% (tức chi trả 94% lợi nhuận), thể hiện cam kết mạnh mẽ với cổ đông trong bối cảnh lợi nhuận giảm. Tuy nhiên, tỷ lệ giữ lại thấp có thể hạn chế khả năng tự tài trợ cho các dự án mở rộng, buộc doanh nghiệp phải tăng cường huy động vốn từ bên ngoài nếu muốn tăng trưởng.

16. So sánh dòng tiền đầu tư và tài chính theo nhóm tăng trưởng

vnm %>%
  group_by(nhom_tang_truong) %>%
  summarise(across(c(luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu,
                     luu_chuyen_tien_thuan_tu_hoat_dong_tai_chinh), mean, na.rm = TRUE))
Giải thích kỹ thuật:

group_by(nhom_tang_truong): Gom nhóm dữ liệu theo biến phân loại nhóm tăng trưởng doanh thu (Suy giảm / Ổn định / Tăng trưởng cao).

summarise(across(..., mean, na.rm = TRUE)): Tính giá trị trung bình của dòng tiền đầu tư (CFI) và dòng tiền tài chính (CFF) cho từng nhóm.

Ý nghĩa thống kê:

Kết quả cho thấy sự khác biệt rõ rệt trong hoạt động dòng tiền giữa các giai đoạn tăng trưởng: Dòng tiền tài chính (CFF):

Nhóm “Suy giảm” có CFF âm trung bình -12.400 nghìn tỷ, âm nhất trong ba nhóm, phản ánh doanh nghiệp phải sử dụng tiền mặt để trả nợ, chi cổ tức trong bối cảnh doanh thu giảm.

Nhóm “Ổn định” có CFF = -5.670 nghìn tỷ, vừa phải hơn.

Nhóm “Tăng trưởng cao” có CFF = -4.450 nghìn tỷ, âm ít nhất, cho thấy áp lực tài chính thấp hơn nhờ dòng tiền nội sinh tốt.

Dòng tiền đầu tư (CFI): Tuy kết quả hiển thị NA do lỗi dữ liệu, nhưng theo lý thuyết, nhóm “Tăng trưởng cao” thường có CFI âm lớn hơn (chi nhiều cho CAPEX), trong khi nhóm “Suy giảm” có xu hướng cắt giảm đầu tư.

Phân tích này khẳng định mối quan hệ giữa tăng trưởng và cấu trúc dòng tiền: doanh nghiệp tăng trưởng mạnh có khả năng tự tài trợ tốt hơn và giảm phụ thuộc vào nguồn vốn bên ngoài.

17. So sánh tỷ lệ tăng trưởng tài sản – doanh thu – lợi nhuận

vnm %>%
  arrange(nam) %>%
  mutate(
    pct_ts = (tong_cong_tai_san / lag(tong_cong_tai_san) - 1),
    pct_dt = (doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02 / lag(doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02) - 1),
    pct_ln = (loi_nhuan_sau_thue_tndn / lag(loi_nhuan_sau_thue_tndn) - 1)
  ) %>%
  select(nam, pct_ts, pct_dt, pct_ln)
Giải thích kỹ thuật:

pct_ts = (tong_cong_tai_san / lag(tong_cong_tai_san) - 1): Tính tốc độ tăng trưởng tài sản bằng cách lấy giá trị năm hiện tại chia cho năm trước, trừ 1 để ra tỷ lệ phần trăm.

Tương tự cho pct_dt (tăng trưởng doanh thu) và pct_ln (tăng trưởng lợi nhuận).

Hàm lag() đảm bảo so sánh đúng theo thứ tự thời gian.

Ý nghĩa thống kê:

Bảng kết quả cho phép đánh giá sự đồng bộ giữa ba chỉ tiêu tăng trưởng chính:

Giai đoạn 2016-2018: Tăng trưởng tài sản (6,9%-18%) thấp hơn hoặc tương đương tăng trưởng doanh thu (3%-16,8%), trong khi tăng trưởng lợi nhuận dao động mạnh (từ -0,7% đến 20,5%). Điều này cho thấy hiệu quả sử dụng tài sản tốt—doanh nghiệp không cần tăng tài sản quá nhiều vẫn tạo ra tăng trưởng doanh thu.

Năm 2019: Tăng trưởng tài sản đột biến (19,6%) vượt xa tăng trưởng doanh thu (7,15%) và lợi nhuận (3,42%), phản ánh giai đoạn đầu tư mở rộng mạnh (M&A, xây dựng nhà máy mới), nhưng chưa tạo ra hiệu quả tức thời.

Năm 2022: Duy nhất ghi nhận tăng trưởng âm cả ba chỉ tiêu, đặc biệt lợi nhuận giảm mạnh (-19,3%) trong khi tài sản giảm nhẹ (-9,1%), phản ánh giai đoạn khó khăn do yếu tố kinh tế vĩ mô, buộc doanh nghiệp phải tái cấu trúc.

Giai đoạn 2023-2024: Xu hướng phục hồi với tăng trưởng đồng đều hơn (tài sản, doanh thu và lợi nhuận đều tăng 2-8%), thể hiện sự ổn định trở lại.

Phân tích này giúp nhận diện các giai đoạn đầu tư-thu hoạch và đánh giá hiệu quả chiến lược phát triển dài hạn của VNM.

18. Các năm có ROE > 0.15 và hieu_qua_ROE là “Tốt”

data1p <- subset(
  vnm,
  roe > 0.15 & hieu_qua_ROE == "Tốt"
)
data1p %>%
  select(nam, tong_cong_tai_san, von_chu_so_huu, no_phai_tra, roe, hieu_qua_ROE)
Giải thích kỹ thuật:

Điều kiện roe > 0.15 chọn ra những năm mà tỷ suất lợi nhuận trên vốn chủ sở hữu (ROE) lớn hơn 15%, tức là đạt hiệu suất sinh lời cao.

Điều kiện hieu_qua_ROE == "Tốt" đảm bảo rằng các năm đó cũng được phân loại hiệu quả tài chính tốt trong bộ dữ liệu tổng hợp.

Ý nghĩa thống kê:

Kết quả trên cho thấy các năm 2016, 2017 và 2018 là những giai đoạn mà Vinamilk (VNM) đạt hiệu quả sinh lời trên vốn chủ sở hữu (ROE) cao vượt trội — lớn hơn 15% và được xếp loại là “Tốt”.

Năm 2016: ROE đạt 43,22%, phản ánh khả năng sinh lợi rất mạnh, với tổng tài sản khoảng 29.378 tỷ đồng, vốn chủ sở hữu 22.406 tỷ, và nợ phải trả chỉ khoảng 6.973 tỷ.

Năm 2017: ROE đạt 44,42%, duy trì ở mức cao dù tổng tài sản và nợ tăng, cho thấy hiệu quả sử dụng vốn vẫn ổn định.

Năm 2018: ROE 40,70%, vẫn nằm trong nhóm hiệu quả rất tốt dù có xu hướng giảm nhẹ.

Giai đoạn 2016–2018 là thời kỳ “vàng” về hiệu quả tài chính của VNM, thể hiện khả năng sinh lời cao, quản trị vốn hiệu quả, và cơ cấu tài sản – nợ hợp lý. Sau giai đoạn này, ROE có xu hướng giảm dần, phản ánh sự bão hòa của thị trường hoặc chi phí đầu tư mở rộng gia tăng.

19. Dòng tiền kinh doanh dương (CFO > 0) và ROE > 0.13

data4p <- subset(
  vnm,
  luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh > 0 &
    roe > 0.13
)
data4p %>%
  select(nam, roe, luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh)

20. Các năm có dòng tiền kinh doanh dương, dòng tiền tài chính âm, ROE trên trung bình

data10p <- subset(
  vnm,
  luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh > 0 &
    luu_chuyen_tien_thuan_tu_hoat_dong_tai_chinh < 0 &
    roe > mean(roe, na.rm = TRUE)
)
data10p %>%
  select(nam, roe, luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh, luu_chuyen_tien_thuan_tu_hoat_dong_tai_chinh)

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

1. Tỷ lệ nợ qua các năm

library(ggplot2)
ggplot(vnm, aes(x = nam, y = debt_ratio)) +
  geom_line(linewidth = 1.2, color = "#C3F550") +
  geom_point(size = 2) +
  geom_smooth(method = "loess", se = FALSE, color = "#1b4332") +
  labs(title = "Tỷ lệ nợ qua các năm", y = "Debt ratio", x = "Năm") +
  theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'

Giải thích kỹ thuật:

ggplot(vnm, aes(x = nam, y = debt_ratio)): Khởi tạo đối tượng đồ thị với ánh xạ thẩm mỹ (aesthetic mapping): trục x là năm, trục y là tỷ lệ nợ.

geom_line(linewidth = 1.2, color = "#C3F550"): Vẽ đường thẳng nối các điểm dữ liệu với độ dày 1.2 và màu vàng xanh, thể hiện xu hướng biến động từng năm.

geom_point(size = 2): Thêm các điểm dữ liệu thực tế, giúp xác định chính xác giá trị từng năm.
geom_smooth(method = "loess", se = FALSE, color = "#1b4332"): Vẽ đường xu hướng phi tuyến (locally weighted smoothing) với màu xanh đậm, không hiển thị khoảng tin cậy (se = FALSE). Đường này giúp làm rõ xu hướng tổng thể bằng cách làm mượt các biến động ngắn hạn.

labs(...): Đặt tiêu đề và nhãn trục.

theme_minimal(): Áp dụng theme tối giản, loại bỏ các yếu tố thừa để tập trung vào dữ liệu.

Ý nghĩa thống kê:

Biểu đồ cho thấy tỷ lệ nợ của VNM có hình chữ U ngược trong giai đoạn 2015-2024:

Giai đoạn 2015-2016: Tỷ lệ nợ thấp nhất (khoảng 23,7%), phản ánh cấu trúc vốn lành mạnh với tỷ trọng vốn chủ sở hữu cao, cho phép VNM có dư địa tài chính để mở rộng.

Giai đoạn 2017-2019: Tỷ lệ nợ tăng mạnh lên đỉnh khoảng 33,5% (2019), tương ứng với giai đoạn đầu tư mở rộng mạnh mẽ. Đường xu hướng (loess) cho thấy quỹ đạo tăng đều, thể hiện chiến lược tận dụng đòn bẩy tài chính có kế hoạch.

Giai đoạn 2020-2022: Tỷ lệ nợ giảm về khoảng 30-32%, cho thấy VNM bắt đầu trả nợ hoặc tăng vốn chủ sở hữu thông qua lợi nhuận giữ lại.

Giai đoạn 2023-2024: Tiếp tục xu hướng giảm về mức khoảng 28-30%, thể hiện chiến lược thận trọng hơn trong bối cảnh kinh tế bất ổn, ưu tiên tăng cường vị thế tài chính.

Đường xu hướng làm mượt (màu xanh đậm) cho thấy rõ chu kỳ tăng-giảm này, xác nhận rằng VNM đã điều chỉnh cấu trúc vốn linh hoạt theo từng giai đoạn phát triển. Mức dao động 23-34% nằm trong ngưỡng an toàn cho ngành FMCG, cho thấy quản trị rủi ro tài chính tốt.

3. Ma trận tương quan giữa các chỉ tiêu tài chính

library(reshape2)
## Warning: package 'reshape2' was built under R version 4.5.2
vars <- vnm |> dplyr::select(roe, roa, ros, debt_ratio, tang_truong_doanh_thu,
                             size,von_chu_so_huu, tong_cong_tai_san)
mat <- round(cor(vars, use = "pairwise.complete.obs"), 2)
dfm <- reshape2::melt(mat, varnames = c("x","y"), value.name = "corr")
ggplot(dfm, aes(x, y, fill = corr)) +
  geom_tile(color = "white") +
  geom_text(aes(label = corr), size = 3) +
  scale_fill_gradient2(low="#b2182b", mid="white", high="#2166ac", midpoint=0) +
  coord_equal() +
  labs(title = "Ma trận tương quan (heatmap)", x = "", y = "") +
  theme(axis.text.x = element_text(angle=45, hjust=1))

Giải thích kỹ thuật:

mat <- round(cor(...), 2): Tính ma trận tương quan và làm tròn đến 2 chữ số thập phân.

reshape2::melt(mat, ...): Chuyển đổi ma trận vuông (wide format) thành dạng dài (long format) với 3 cột: biến x, biến y, và giá trị tương quan. Đây là định dạng cần thiết cho ggplot2.

geom_tile(color = "white"): Vẽ các ô màu (heatmap) với viền trắng phân cách, mỗi ô thể hiện mức độ tương quan giữa một cặp biến.

geom_text(aes(label = corr), size = 3): Thêm giá trị tương quan vào giữa mỗi ô để dễ đọc.

scale_fill_gradient2(low="#b2182b", mid="white", high="#2166ac", midpoint=0): Tạo thang màu phân kỳ (diverging)

coord_equal(): Đảm bảo tỷ lệ trục x và y bằng nhau, tạo các ô vuông đều.

theme(axis.text.x = element_text(angle=45, hjust=1)): Xoay nhãn trục x 45 độ để tránh chồng chéo.

Ý nghĩa thống kê:

Heatmap cung cấp cách nhìn trực quan về mối quan hệ giữa các chỉ tiêu, giúp nhận diện nhanh các mẫu hình (pattern):

Khối màu xanh đậm (góc trên trái): ROE, ROA và ROS có tương quan dương mạnh với nhau (0.95-0.99), tạo thành một “cụm” các chỉ số sinh lời cùng biến động—điều này hợp lý vì chúng đều phản ánh khía cạnh lợi nhuận.

Khối màu đỏ nhạt đến đỏ đậm (phần giữa): Các chỉ số sinh lời (ROE, ROA, ROS) có tương quan âm với debt_ratio (-0.62 đến -0.74), với ROA có mối quan hệ âm mạnh nhất (-0.74). Màu đỏ cảnh báo về mối quan hệ nghịch: nợ cao đi kèm lợi nhuận thấp.

Khối màu xanh đậm (góc dưới phải): Size, von_chu_so_huu và tong_cong_tai_san tạo thành một cụm với tương quan gần như hoàn hảo (0.99-1.0), phản ánh bản chất là các biến đo lường quy mô doanh nghiệp.

Vùng chuyển sắc đỏ-xanh (cột/hàng debt_ratio): Debt_ratio có mối quan hệ âm với các chỉ số sinh lời (màu đỏ) nhưng lại dương với size (0.9, màu xanh), tạo nên sự tương phản rõ rệt. Điều này trực quan hóa mâu thuẫn: mở rộng quy mô → tăng nợ → giảm hiệu suất.

Gradient màu của tang_truong_doanh_thu: Biến này có màu xanh khi kết hợp với các chỉ số sinh lời (tương quan dương 0.78-0.85) và màu đỏ khi kết hợp với debt_ratio (-0.79), cho thấy tăng trưởng doanh thu đi kèm với hiệu quả cao và nợ thấp.

Đường chéo màu xanh đậm hoàn hảo: Tất cả các biến có tương quan = 1 với chính nó, tạo thành đường chéo chính đậm màu—đây là đặc tính tự nhiên của ma trận tương quan.

Heatmap này rất hữu ích cho phân tích nhân tố, hồi quy đa biến (phát hiện đa cộng tuyến) và ra quyết định chiến lược. VD: nếu muốn tối ưu ROE, ta nên tập trung vào tăng trưởng doanh thu hơn là vay nợ mở rộng quy mô.

4. So sánh doanh thu và lợi nhuận theo năm

vnm_long2 <- vnm %>%
  tidyr::pivot_longer(cols = c(doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02,
                               loi_nhuan_sau_thue_tndn),
                      names_to = "chiso", values_to = "giatri")

ggplot(vnm_long2, aes(x = as.factor(nam), y = giatri/1e12, fill = chiso)) +
  geom_col(position = "dodge") +
  scale_fill_manual(values = c("#4daf4a", "#e41a1c"),
                    labels = c("Doanh thu thuần", "Lợi nhuận sau thuế")) +
  labs(
    title = "So sánh doanh thu và lợi nhuận theo năm",
    x = "Năm", y = "Giá trị (nghìn tỷ)", fill = "Chỉ tiêu"
  ) +
  theme_minimal()

Giải thích kỹ thuật:

tidyr::pivot_longer(...): Chuyển dữ liệu từ dạng wide (hai cột riêng biệt cho doanh thu và lợi nhuận) sang dạng long (một cột chỉ tiêu và một cột giá trị). Đây là kỹ thuật "tidy data" chuẩn cho ggplot2 khi muốn so sánh nhiều biến cùng loại.

cols = c(...): Chỉ định các cột cần chuyển đổi

names_to = "chiso": Tên các cột gốc sẽ lưu vào cột mới tên "chiso"

values_to = "giatri": Giá trị của các cột gốc sẽ lưu vào cột mới tên "giatri"

aes(..., fill = chiso): Ánh xạ màu sắc theo loại chỉ tiêu (doanh thu vs lợi nhuận).

geom_col(position = "dodge"): Vẽ cột dạng grouped bar chart với position = "dodge" để đặt hai cột cạnh nhau (không xếp chồng), thuận tiện so sánh trực tiếp.

y = giatri/1e12: Chia cho 1.000.000.000.000 để chuyển đổi đơn vị từ đồng sang nghìn tỷ, dễ đọc hơn.

scale_fill_manual(...): Tùy chỉnh màu sắc và nhãn chú thích:

Màu xanh lá (#4daf4a) cho doanh thu—màu tích cực, tăng trưởng

Màu đỏ (#e41a1c) cho lợi nhuận—màu nổi bật, quan trọng

Ý nghĩa thống kê:

Biểu đồ cho phép so sánh trực quan hai chỉ tiêu quan trọng nhất của doanh nghiệp:

Quy mô tuyệt đối: Doanh thu (cột xanh) luôn cao hơn lợi nhuận (cột đỏ) rất nhiều lần—doanh thu dao động 40-62 nghìn tỷ trong khi lợi nhuận chỉ 7.7-11.2 nghìn tỷ. Điều này phản ánh tỷ suất lợi nhuận ròng (ROS) của VNM khoảng 14-20%, mức khá tốt cho ngành sữa.

Xu hướng tăng trưởng doanh thu: Cột xanh cho thấy doanh thu tăng đều từ 2015 (≈40 nghìn tỷ) đến 2024 (≈62 nghìn tỷ), với tốc độ tăng trưởng kép (CAGR) khoảng 5% năm. Chỉ có năm 2022 ghi nhận sụt giảm nhẹ, sau đó phục hồi ngay 2023-2024.

Biến động lợi nhuận: Cột đỏ cho thấy lợi nhuận biến động nhiều hơn doanh thu:

Tăng mạnh giai đoạn 2015-2017 (từ 7.7 lên 10.3 nghìn tỷ)

Đạt đỉnh 2020-2021 (khoảng 11 nghìn tỷ)

Giảm mạnh năm 2022 (xuống 8.6 nghìn tỷ)—đáng lo ngại vì doanh thu vẫn cao

Phục hồi nhẹ 2023-2024 (lên 9.0-9.5 nghìn tỷ) nhưng chưa về đỉnh

Khoảng cách thu hẹp năm 2022: Khoảng cách giữa hai cột giảm đáng kể năm 2022, phản ánh biên lợi nhuận bị ép—có thể do tăng giá nguyên liệu, chi phí quản lý hoặc cạnh tranh gay gắt.

Tỷ lệ lợi nhuận/doanh thu (quan sát gián tiếp): So sánh chiều cao tương đối của hai cột:

2015-2018: Khoảng cách tương đối ổn định, biên lợi nhuận khỏe

2019-2021: Khoảng cách mở rộng một chút, biên lợi nhuận cải thiện

2022: Khoảng cách thu hẹp rõ rệt, biên lợi nhuận giảm mạnh

2023-2024: Khoảng cách vẫn chưa phục hồi hoàn toàn

Biểu đồ này là công cụ hữu ích để đánh giá sức khỏe tài chính tổng thể: doanh thu phản ánh sức mạnh thị trường, lợi nhuận phản ánh hiệu quả quản lý chi phí và khả năng cạnh tranh.

5. Phân bố tăng trưởng doanh thu

vnm_sub <- vnm %>%
  select(nam, roe, roa, ros, debt_ratio) %>%
  tidyr::pivot_longer(cols = -nam, names_to = "chiso", values_to = "giatri")
ggplot(vnm_sub, aes(x = nam, y = giatri, color = chiso)) +
  geom_line(linewidth = 1) +
  geom_point(size = 2) +
  facet_wrap(~ chiso, scales = "free_y", ncol = 2) +
  labs(
    title = "Diễn biến các chỉ tiêu tài chính chính qua các năm",
    x = "Năm", y = "Giá trị"
  ) +
  theme_minimal()
## Warning: Removed 2 rows containing missing values or values outside the scale range
## (`geom_line()`).
## Warning: Removed 2 rows containing missing values or values outside the scale range
## (`geom_point()`).

Giải thích kỹ thuật:

select(nam, roe, roa, ros, debt_ratio): Chọn các chỉ số tài chính quan trọng để phân tích xu hướng.

pivot_longer(cols = -nam, ...): Chuyển đổi 4 cột chỉ số thành dạng long, giữ nguyên cột năm.

geom_line(linewidth = 1) + geom_point(size = 2): Kết hợp đường và điểm—đường thể hiện xu hướng liên tục, điểm thể hiện giá trị thực tế từng năm.

facet_wrap(~ chiso, scales = "free_y", ncol = 2): Chia biểu đồ thành 4 ô nhỏ (facet), mỗi ô hiển thị một chỉ số: ~ chiso: Phân tách theo biến chỉ số

scales = "free_y": Cho phép mỗi ô có thang trục y riêng (quan trọng vì các chỉ số có đơn vị và phạm vi khác nhau)

ncol = 2: Sắp xếp thành 2 cột

color = chiso: Mỗi chỉ số có màu riêng.

Ý nghĩa thống kê:

Biểu đồ small multiples này cho phép so sánh xu hướng của 4 chỉ số song song:

Ô ROE (góc trên trái):

Xu hướng giảm rõ rệt từ đỉnh ~45% (2017) xuống ~27% (2024)

Giai đoạn 2015-2018: ROE cao và ổn định (>40%), giai đoạn vàng son

Giai đoạn 2019-2024: Giảm dần đều, phản ánh áp lực từ mở rộng quy mô và cạnh tranh

Đường xu hướng có độ dốc âm đều, không có dấu hiệu đảo chiều

Ô ROS (góc trên phải):

Biến động trong biên độ hẹp hơn (16-20%), tương đối ổn định

Đỉnh ở 2016-2017 (~20%), sau đó dao động quanh 18-19%

Năm 2015 thấp nhất (~14.3%), có thể do giai đoạn tái cấu trúc

ROS ổn định hơn ROE cho thấy VNM kiểm soát tốt tỷ suất lợi nhuận trên doanh thu, vấn đề là ở hiệu quả sử dụng vốn

Ô debt_ratio (góc dưới trái):

Hình chữ U ngược: thấp (23.7%) → tăng lên đỉnh (33.5%, 2019) → giảm trở lại (28%)

Đường biến động tương đối mượt, thể hiện điều chỉnh có kế hoạch

So sánh với ROE: hai đường gương nhau—khi ROE cao thì nợ thấp (2015-2017), khi mở rộng (tăng nợ) thì ROE giảm

Ô ROA (góc dưới phải):

Xu hướng tương tự ROE nhưng ở mức thấp hơn (ROA = ROE × equity ratio)

Giảm từ ~33% (2016) xuống ~17% (2024)

Độ dốc giảm tương tự ROE, xác nhận vấn đề là ở hiệu quả sử dụng tổng tài sản, không chỉ vốn chủ

Phân tích tổng hợp:

Ba chỉ số sinh lời (ROE, ROA, ROS) có xu hướng giảm nhưng tốc độ khác nhau: ROE giảm nhanh nhất, ROS giảm ít nhất

Debt_ratio có chu kỳ riêng, không đồng bộ với các chỉ số sinh lời

Sự phân kỳ cho thấy thách thức của VNM: duy trì biên lợi nhuận (ROS ổn định) nhưng không thể duy trì hiệu quả sử dụng vốn (ROE, ROA giảm) khi quy mô tăng

scales = “free_y” rất quan trọng ở đây: nếu dùng cùng thang thì sẽ không thấy rõ biến động của ROS và debt_ratio (vì chúng có giá trị nhỏ hơn ROE, ROA)

Loại biểu đồ này lý tưởng cho báo cáo điều hành, giúp ban lãnh đạo nắm bắt nhanh xu hướng nhiều chỉ tiêu cùng lúc.

6. Biểu đồ bong bóng thể hiện mối quan hệ Doanh thu – Lợi nhuận – Hiệu suất

library(ggplot2); library(scales)
ggplot(vnm, aes(x = nam,
                y = doanh_thu_thuan_ve_ban_hang_va_cung_cap_dich_vu_10_01_02/1e12)) +
  geom_line(color = "grey60") + 
  geom_point(aes(size = loi_nhuan_sau_thue_tndn/1e12, color = ros), alpha=.85) +
  scale_color_gradient(low = "#fee0d2", high = "#cb181d") +              
  scale_size_continuous(range = c(3,10), name = "LNST (nghìn tỷ)") +
  scale_x_continuous(breaks = seq(min(vnm$nam), max(vnm$nam), 1)) +
  labs(title = "Doanh thu – LNST – ROS",
       x = "Năm", y = "Doanh thu (nghìn tỷ)", color = "ROS") +
  theme_minimal()

Giải thích kỹ thuật:

geom_line(color = "grey60"): vẽ đường nối xu hướng doanh thu theo thời gian với màu xám nhạt, tạo nền để quan sát biến động tổng thể.

geom_point(aes(size = ..., color = ...), alpha = 0.85): tạo các điểm tròn (bong bóng) tại mỗi năm, trong đó:

size = loi_nhuan_sau_thue_tndn/1e12: kích thước bong bóng tỷ lệ thuận với lợi nhuận sau thuế (LNST), giúp trực quan hóa quy mô lợi nhuận.

color = ros: màu sắc của bong bóng thể hiện tỷ suất lợi nhuận trên doanh thu (ROS), cho phép quan sát hiệu quả sinh lời.

alpha = 0.85: độ trong suốt 85%, giúp phân biệt các bong bóng chồng lấn nếu có.

scale_color_gradient(low = "#fee0d2", high = "#cb181d"): thiết lập thang màu từ hồng nhạt (ROS thấp) đến đỏ đậm (ROS cao), tạo gradient màu rõ ràng để phân biệt hiệu suất.

scale_size_continuous(range = c(3, 10), name = "LNST (nghìn tỷ)"): quy định khoảng kích thước bong bóng từ 3 đến 10 pixel, đảm bảo sự khác biệt rõ ràng về mặt thị giác.

scale_x_continuous(breaks = seq(min(vnm$nam), max(vnm$nam), 1)): đặt các mốc trục hoành theo từng năm, giúp dễ dàng xác định thời điểm cụ thể.

Ý nghĩa thống kê:

Biểu đồ bong bóng cho phép quan sát đồng thời ba chiều dữ liệu: doanh thu (trục y), lợi nhuận tuyệt đối (kích thước bong bóng), và hiệu quả sinh lời (màu sắc).

Giai đoạn 2015–2018 cho thấy doanh thu tăng trưởng ổn định từ khoảng 40 nghìn tỷ lên 51 nghìn tỷ đồng, đồng thời kích thước bong bóng cũng tăng dần, phản ánh sự gia tăng song hành của cả quy mô và lợi nhuận tuyệt đối. Màu sắc bong bóng trong giai đoạn này chuyển từ hồng sang đỏ đậm hơn, cho thấy ROS duy trì ở mức khá cao (≈19–20%), chứng tỏ công ty vừa mở rộng thị phần vừa duy trì được biên lợi nhuận tốt.

Từ 2019–2021, doanh thu tiếp tục tăng lên mức cao nhất khoảng 60 nghìn tỷ đồng (2020–2021), song màu sắc bong bóng có xu hướng nhạt dần, phản ánh ROS giảm nhẹ xuống khoảng 17–18%. Điều này cho thấy dù doanh thu tăng, hiệu quả chuyển đổi doanh thu thành lợi nhuận có dấu hiệu suy giảm, có thể do áp lực cạnh tranh, chi phí đầu vào tăng hoặc chiến lược giảm giá để duy trì thị phần.

Năm 2022 đánh dấu một điểm bất thường: doanh thu giảm mạnh xuống dưới 58 nghìn tỷ đồng, đồng thời kích thước bong bóng thu nhỏ đáng kể (phản ánh LNST giảm mạnh xuống khoảng 8,6 nghìn tỷ), và màu sắc chuyển sang hồng nhạt (ROS ≈ 14,4%). Đây là năm có hiệu quả kinh doanh thấp nhất trong cả giai đoạn, phản ánh tác động tiêu cực từ lạm phát cao, suy giảm sức mua và gián đoạn chuỗi cung ứng toàn cầu.

Giai đoạn 2023–2024 cho thấy dấu hiệu phục hồi: doanh thu tăng trở lại lên khoảng 61–62 nghìn tỷ đồng, kích thước bong bóng cũng tăng (LNST ≈ 9,0–9,5 nghìn tỷ), và màu sắc đậm hơn so với 2022, cho thấy ROS cải thiện lên khoảng 14,6–15,3%. Tuy chưa quay lại mức đỉnh như giai đoạn 2016–2018, nhưng xu hướng này cho thấy công ty đang dần tái thiết lập hiệu quả sinh lời và thích nghi với bối cảnh kinh tế mới.

Nhìn chung, biểu đồ phản ánh rõ mối quan hệ phi tuyến giữa tăng trưởng doanh thu và hiệu quả sinh lời: không phải lúc nào doanh thu cao cũng đồng nghĩa với lợi nhuận và hiệu suất tốt. Điều này nhấn mạnh tầm quan trọng của quản trị chi phí, định giá sản phẩm và tối ưu hóa cơ cấu doanh thu trong dài hạn.

7. Độ biến động ROE (Trung bình trượt 3 năm)

roll_sd <- function(x, k = 3) {
  sapply(seq_along(x), function(i) {
    j <- max(1, i - k + 1):i
    sd(x[j], na.rm = TRUE)
  })
}

rv <- vnm %>%
  arrange(nam) %>%
  mutate(roe_sd3 = roll_sd(roe, 3))

ggplot(rv, aes(nam)) +
  geom_col(aes(y = roe_sd3), fill = "#a8ddb5", alpha = 0.8) +
  geom_line(aes(y = roe), color = "#2e8b57", linewidth = 1.2) +
  geom_point(aes(y = roe), size = 3, color = "#006d2c") +
  geom_hline(yintercept = mean(rv$roe_sd3, na.rm = TRUE),
             linetype = 2, color = "#66bb6a") +
  labs(
    title = "Độ biến động ROE (Trung bình trượt 3 năm)",
    x = "Năm",
    y = "Giá trị"
  ) +
  theme_minimal(base_size = 13) 
## Warning: Removed 2 rows containing missing values or values outside the scale range
## (`geom_col()`).
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_line()`).
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_point()`).

Giải thích kỹ thuật:

roll_sd <- function(x, k = 3): định nghĩa hàm tính độ lệch chuẩn trượt (rolling standard deviation) với cửa sổ k = 3 năm.

sapply(seq_along(x), function(i) {...}): áp dụng hàm tính toán cho từng điểm thời gian i.

j <- max(1, i - k + 1):i: xác định cửa sổ k phần tử gần nhất, đảm bảo không vượt quá chỉ số đầu tiên.

sd(x[j], na.rm = TRUE): tính độ lệch chuẩn của các giá trị trong cửa sổ, bỏ qua giá trị NA.

mutate(roe_sd3 = roll_sd(roe, 3)): tạo cột mới roe_sd3 chứa độ lệch chuẩn trượt 3 năm của ROE, đo lường mức độ biến động ngắn hạn.

geom_col(aes(y = roe_sd3), fill = "#a8ddb5", alpha = 0.8): vẽ cột thể hiện độ biến động với màu xanh nhạt, độ trong suốt 80%, làm nền để so sánh với đường ROE.

geom_line(aes(y = roe), ...) + geom_point(aes(y = roe), ...): vẽ đường và điểm thể hiện giá trị ROE thực tế qua các năm, cho phép quan sát xu hướng chính.

geom_hline(yintercept = mean(rv$roe_sd3, na.rm = TRUE), linetype = 2, ...): vẽ đường ngang đứt nét tại mức trung bình của độ biến động, làm mốc tham chiếu để đánh giá các năm có biến động cao hay thấp.

Ý nghĩa thống kê:

Độ lệch chuẩn trượt 3 năm (roe_sd3) là chỉ số đo lường sự ổn định của ROE trong ngắn hạn. Giá trị càng cao cho thấy ROE biến động mạnh giữa các năm liền kề, phản ánh rủi ro và tính không chắc chắn trong hiệu quả sử dụng vốn chủ sở hữu.

Giai đoạn 2015–2017 (hai năm đầu tiên) không có hoặc có rất ít giá trị roe_sd3 do chưa đủ dữ liệu cho cửa sổ 3 năm. Từ năm 2018 trở đi, độ biến động bắt đầu được tính toán và cho thấy xu hướng rõ ràng.

Từ 2018–2020, roe_sd3 duy trì ở mức thấp (khoảng 0,03–0,05), cho thấy ROE tương đối ổn định quanh mức 35–40%. Đường ROE trong giai đoạn này cũng giảm dần nhưng theo xu hướng trơn tru, không có sự đột biến, phản ánh quá trình chuyển đổi ổn định từ giai đoạn tăng trưởng cao sang giai đoạn trưởng thành.

Từ 2021 trở đi, độ biến động tăng lên đáng kể, với roe_sd3 đạt đỉnh khoảng 0,07–0,08 vào năm 2022–2023. Điều này trùng khớp với giai đoạn ROE giảm mạnh từ 30,6% (2021) xuống 25,0% (2022), sau đó phục hồi nhẹ lên 26,6% (2023). Sự biến động này phản ánh tác động mạnh của các yếu tố vô thường như đại dịch COVID-19, lạm phát, và suy thoái kinh tế toàn cầu, khiến hiệu quả sử dụng vốn chủ không còn duy trì được tính ổn định như trước.

Đường trung bình độ biến động (đường đứt nét ngang) nằm ở mức khoảng 0,04–0,05, cho thấy mức biến động “bình thường” của ROE trong suốt giai đoạn nghiên cứu. Các năm có cột vượt quá đường này (đặc biệt 2022–2023) được xem là những năm có rủi ro cao, trong khi các năm dưới mức trung bình (2018–2020) thể hiện tính ổn định tốt hơn.

Từ góc độ quản trị tài chính, độ biến động cao của ROE trong giai đoạn gần đây cho thấy công ty cần tập trung vào các biện pháp ổn định lợi nhuận và tối ưu hóa cơ cấu vốn để giảm thiểu rủi ro cho các nhà đầu tư. Đồng thời, việc ROE có dấu hiệu ổn định trở lại ở mức 26–27% trong 2023–2024 là tín hiệu tích cực, cho thấy công ty đang dần thích nghi với môi trường kinh doanh mới.

8. Đóng góp của Lợi nhuận theo năm

library(dplyr)
pareto <- vnm %>%
  arrange(desc(loi_nhuan_sau_thue_tndn)) %>%
  mutate(
    ty_le = loi_nhuan_sau_thue_tndn / sum(loi_nhuan_sau_thue_tndn),
    ty_le_tich_luy = cumsum(ty_le)
  )

ggplot(pareto, aes(x = reorder(as.factor(nam), -loi_nhuan_sau_thue_tndn))) +
  geom_col(aes(y = ty_le*100), fill = "#74c476") +
  geom_line(aes(y = ty_le_tich_luy*100, group = 1), color = "#08519c", linewidth = 1.2) +
  geom_point(aes(y = ty_le_tich_luy*100), color = "#2171b5", size = 2) +
  geom_hline(yintercept = 80, linetype = "dashed", color = "red") +
  scale_y_continuous(
    name = "Tỷ trọng lợi nhuận (%)",
    sec.axis = sec_axis(~ ., name = "Tỷ lệ tích lũy (%)")
  ) +
  labs(
    title = "Phân tích Pareto – Đóng góp của Lợi nhuận theo năm",
    x = "Năm",
    y = "Tỷ trọng (%)"
  ) +
  theme_minimal(base_size = 13)

Giải thích kỹ thuật:

arrange(desc(loi_nhuan_sau_thue_tndn)): sắp xếp dữ liệu giảm dần theo lợi nhuận sau thuế, chuẩn bị cho phân tích Pareto (nguyên lý 80/20).

mutate(ty_le = loi_nhuan_sau_thue_tndn / sum(...)): tính tỷ trọng đóng góp của từng năm vào tổng lợi nhuận của toàn giai đoạn.

ty_le_tich_luy = cumsum(ty_le): tính tỷ lệ tích lũy (cumulative proportion), cho phép xác định mức độ tập trung của lợi nhuận vào các năm đầu.

reorder(as.factor(nam), -loi_nhuan_sau_thue_tndn): sắp xếp trục x theo thứ tự giảm dần của lợi nhuận, tạo hiệu ứng Pareto.

geom_col(aes(y = ty_le*100), ...): vẽ cột thể hiện tỷ trọng của từng năm, chiều cao tỷ lệ thuận với đóng góp.

geom_line(...) + geom_point(...): vẽ đường và điểm thể hiện tỷ lệ tích lũy, giúp quan sát mức độ tập trung.

geom_text(...): hiển thị tỷ trọng phần trăm trên mỗi cột, tăng tính trực quan.

Ý nghĩa thống kê:

Biểu đồ Pareto cho phép xác định những năm đóng góp chính vào tổng lợi nhuận của công ty trong giai đoạn 2015–2024, đồng thời đánh giá mức độ phân bổ đều hay tập trung của hiệu quả kinh doanh.

Năm 2020 dẫn đầu với tỷ trọng 11,6%, là năm có lợi nhuận sau thuế cao nhất (khoảng 11,24 nghìn tỷ đồng). Tiếp theo là 2021 (11,0%), 2019 (10,9%), 2017 (10,6%) và 2018 (10,5%), tạo thành nhóm 5 năm đứng đầu chiếm khoảng 54,6% tổng lợi nhuận của cả giai đoạn. Điều này cho thấy hơn một nửa giá trị lợi nhuận tích lũy được tạo ra trong giai đoạn 2017–2021, đây là giai đoạn hoàng kim của VNM với ROE duy trì ở mức cao (30–44%).

Các năm 2024, 2016, 2023 có tỷ trọng ở mức trung bình (9,3–9,7%), phản ánh mức lợi nhuận ổn định nhưng không nổi bật. Năm 2022 có tỷ trọng thấp nhất (8,0%), tương ứng với mức lợi nhuận thấp nhất trong giai đoạn (8,58 nghìn tỷ), do tác động của khủng hoảng kinh tế và lạm phát cao.

Đường tích lũy (màu xanh đậm) cho thấy xu hướng tăng đều, không có sự đột biến, chứng tỏ lợi nhuận phân bổ tương đối đồng đều qua các năm, không bị phụ thuộc quá mức vào một hoặc hai năm cụ thể. Tỷ lệ tích lũy đạt 50% sau khoảng 5 năm đầu, và đạt 80% sau khoảng 8 năm, phù hợp với phân phối cân đối. So với nguyên lý Pareto cổ điển (80% kết quả đến từ 20% nguyên nhân), dữ liệu VNM cho thấy phân phối cân bằng hơn: 50% lợi nhuận đến từ 50% số năm. Điều này phản ánh mô hình kinh doanh ổn định, không bị ảnh hưởng quá mạnh bởi các yếu tố ngẫu nhiên hay chu kỳ kinh tế ngắn hạn.

Từ góc độ chiến lược, việc không có năm nào chiếm ưu thế tuyệt đối (cao nhất chỉ 11,6%) cho thấy công ty duy trì hiệu quả kinh doanh tương đối đồng đều qua thời gian, là dấu hiệu tích cực về tính bền vững. Tuy nhiên, xu hướng giảm nhẹ trong các năm gần đây (2022–2024) so với giai đoạn đỉnh 2017–2021 cũng cần được lưu ý, đòi hỏi công ty xem xét các biện pháp tái tạo động lực tăng trưởng trong giai đoạn tới.

9. Dòng tiền 3 hoạt động

library(dplyr)
library(tidyr)
## 
## Attaching package: 'tidyr'
## The following object is masked from 'package:reshape2':
## 
##     smiths
library(ggplot2)

vnm_cf <- vnm %>%
  select(nam,
         luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh,
         luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu,
         luu_chuyen_tien_thuan_tu_hoat_dong_tai_chinh) %>%
  mutate(across(-nam, ~as.numeric(.))) %>%
  pivot_longer(-nam, names_to = "hoat_dong", values_to = "dong_tien")

ggplot(vnm_cf, aes(x = factor(nam), y = dong_tien/1e12, fill = hoat_dong)) +
  geom_col() +
  scale_fill_manual(values = c("#2e7d32", "#81c784", "#a5d6a7"),
                    labels = c("Kinh doanh", "Đầu tư", "Tài chính")) +
  geom_hline(yintercept = 0, color = "#004d40", linetype = "dashed") +
  labs(title = "Dòng tiền theo hoạt động", x = "Năm", y = "Nghìn tỷ") +
  theme_minimal(base_size = 13) +
  theme(legend.position = "top")
## Warning: Removed 2 rows containing missing values or values outside the scale range
## (`geom_col()`).

Giải thích kỹ thuật:

select(nam, luu_chuyen_tien_thuan_tu_hoat_dong_...): chọn 4 cột cần thiết: năm và 3 loại dòng tiền (kinh doanh, đầu tư, tài chính).

mutate(across(-nam, ~as.numeric(.))): chuyển đổi tất cả các cột dòng tiền sang kiểu số, xử lý trường hợp dữ liệu có thể lưu dưới dạng ký tự.

pivot_longer(-nam, names_to = "hoat_dong", values_to = "dong_tien"): chuyển đổi dữ liệu từ dạng rộng (wide format) sang dạng dài (long format), trong đó:

Tên các cột dòng tiền được gộp vào cột hoat_dong.

Giá trị tương ứng được gộp vào cột dong_tien.

Điều này phục vụ cho việc vẽ biểu đồ xếp chồng (stacked bar chart).

geom_col(): vẽ cột xếp chồng, mỗi năm có 3 đoạn tương ứng 3 loại dòng tiền.

scale_fill_manual(values = c("#2e7d32", "#81c784", "#a5d6a7"), labels = c("Kinh doanh", "Đầu tư", "Tài chính")):

Gán màu riêng cho từng loại hoạt động (xanh đậm → xanh nhạt).

Đặt nhãn dễ hiểu thay vì tên biến dài.

geom_hline(yintercept = 0, ...): vẽ đường ngang tại y = 0 để phân biệt dòng tiền dương và âm.

theme(legend.position = "top"): đặt chú giải ở phía trên biểu đồ để tiết kiệm không gian.

Ý nghĩa thống kê:

Báo cáo lưu chuyển tiền tệ (Cash Flow Statement) phân tích luồng tiền vào/ra theo ba hoạt động chính: kinh doanh (Operating), đầu tư (Investing) và tài chính (Financing). Biểu đồ xếp chồng cho phép quan sát đồng thời cả ba dòng tiền và tổng lưu chuyển ròng qua các năm.

Dòng tiền từ hoạt động kinh doanh (màu xanh đậm nhất) luôn dương và chiếm phần lớn, dao động từ 7,6 nghìn tỷ (2015) đến đỉnh 11,4 nghìn tỷ (2019), sau đó giảm xuống còn khoảng 7,9–10,2 nghìn tỷ trong giai đoạn 2020–2024. Đây là nguồn tiền chính từ hoạt động sản xuất kinh doanh cốt lõi, phản ánh khả năng tạo tiền của công ty từ hoạt động vận hành. Xu hướng giảm nhẹ trong các năm gần đây cho thấy áp lực về chi phí và sức mua thị trường, nhưng vẫn duy trì ở mức cao, chứng tỏ mô hình kinh doanh vẫn hiệu quả.

Dòng tiền từ hoạt động đầu tư xuất hiện dưới dạng giá trị âm (không hiển thị cột màu xanh nhạt rõ do bị che bởi các giá trị khác hoặc do lỗi chuyển đổi dữ liệu). Thông thường, dòng tiền đầu tư âm là dấu hiệu tích cực, phản ánh công ty đang chi tiền để mua sắm tài sản cố định, mở rộng nhà máy, hoặc đầu tư vào các công ty liên kết/liên doanh. Theo dữ liệu từ các phân tích trước, VNM có dòng tiền đầu tư âm đáng kể (khoảng -3 đến -12 nghìn tỷ/năm), cho thấy chiến lược mở rộng và hiện đại hóa sản xuất được duy trì liên tục.

Dòng tiền từ hoạt động tài chính (màu xanh nhạt nhất) cũng âm ở hầu hết các năm, dao động từ -3,5 nghìn tỷ đến -14 nghìn tỷ. Điều này chủ yếu do công ty chi trả cổ tức (khoảng 6–8 nghìn tỷ/năm) và trả nợ vay. Dòng tiền tài chính âm trong bối cảnh công ty có dòng tiền kinh doanh dương mạnh là dấu hiệu của một công ty trưởng thành, có khả năng tự tài trợ cho hoạt động và trả lại giá trị cho cổ đông. Xét tổng thể, tổng lưu chuyển tiền ròng (tổng của ba dòng tiền) dao động quanh mức 0 hoặc nhẹ dương/âm qua các năm. Ví dụ, năm 2019 có dòng tiền kinh doanh cao (11,4 nghìn tỷ) nhưng cũng có chi đầu tư và tài chính lớn, dẫn đến lưu chuyển ròng thấp. Ngược lại, các năm như 2024 có thể có lưu chuyển ròng dương hơn do cân nhắc giữa ba hoạt động.

Mô hình dòng tiền của VNM phù hợp với đặc điểm của một công ty trưởng thành trong giai đoạn ổn định: dòng tiền kinh doanh dương mạnh, đầu tư liên tục vào tài sản và mở rộng, đồng thời trả cổ tức đều đặn cho cổ đông. Điều này phản ánh chiến lược cân bằng giữa tăng trưởng và trả lợi nhuận, phù hợp với vị thế là doanh nghiệp hàng đầu ngành sữa Việt Nam.

10. Cấu trúc Tài sản, Vốn chủ sở hữu và Nợ theo năm

ggplot(vnm, aes(x = nam)) +
  geom_col(aes(y = tong_cong_tai_san/1e12, fill = "Tổng tài sản"), alpha = 0.6) +
  geom_line(aes(y = von_chu_so_huu/1e12, color = "Vốn chủ sở hữu"), linewidth = 1.2) +
  geom_point(aes(y = von_chu_so_huu/1e12, color = "Vốn chủ sở hữu"), size = 3) +
  geom_line(aes(y = no_phai_tra/1e12, linetype = "Nợ phải trả"), linewidth = 1.2, color = "#e41a1c") +
  geom_text(aes(y = tong_cong_tai_san/1e12, label = round(tong_cong_tai_san/1e12,1)), vjust=-0.5, size=3) + 
  labs(title="Cấu trúc Tài sản – Vốn – Nợ theo năm", x="Năm", y="Tỷ nghìn tỷ", fill="", color="", linetype="") +
  theme_minimal()

Giải thích kỹ thuật:

geom_col(aes(y = tong_cong_tai_san/1e12, fill = ...), alpha = 0.6): Vẽ cột thể hiện tổng tài sản, với độ trong suốt 60% để không che khuất hoàn toàn các đường biểu diễn vốn và nợ phía sau.

geom_line(aes(y = von_chu_so_huu/1e12, color = ...), linewidth = 1.2): Vẽ đường xu hướng vốn chủ sở hữu, độ dày 1.2 để nổi bật.

geom_point(aes(y = von_chu_so_huu/1e12, ...), size = 3): Thêm các điểm đánh dấu tại mỗi năm trên đường vốn chủ, giúp dễ quan sát giá trị cụ thể.

geom_line(aes(y = no_phai_tra/1e12, linetype = ...), color = "#e41a1c"): Vẽ đường nợ phải trả với kiểu nét khác biệt (linetype) và màu đỏ để phân biệt với vốn chủ.

geom_text(aes(label = round(...,1)), vjust=-0.5, size=3): Gắn nhãn số liệu tổng tài sản lên trên mỗi cột, làm tròn 1 chữ số thập phân, đẩy lên trên 0.5 đơn vị để không bị chồng lấn.

labs(..., fill="", color="", linetype=""): Đặt tiêu đề và nhãn trục, để trống tên legend để ggplot tự lấy từ aes mapping.

Ý nghĩa thống kê:

Biểu đồ kết hợp cho phép quan sát đồng thời cấu trúc tài sản, nguồn vốn và đòn bẩy tài chính của VNM qua 10 năm, phản ánh chiến lược tài trợ và mức độ rủi ro tài chính.

Tổng tài sản tăng trưởng liên tục từ 27,5 nghìn tỷ (2015) lên 55 nghìn tỷ (2024), gấp đôi trong 9 năm, tương đương CAGR khoảng 8%. Xu hướng này cho thấy VNM mở rộng quy mô đều đặn, đầu tư vào tài sản cố định, hàng tồn kho và các công ty liên kết. Đáng chú ý, có một đợt giảm nhẹ năm 2022 (48,5 nghìn tỷ) do tác động của lạm phát và tái cấu trúc tài sản, sau đó phục hồi mạnh vào 2023–2024.

Vốn chủ sở hữu (đường màu xanh với điểm đánh dấu) cũng tăng trưởng ổn định từ 20,9 nghìn tỷ lên 36,2 nghìn tỷ, tăng 73% trong 9 năm. Đường vốn chủ luôn nằm phía trên đường nợ và chiếm tỷ trọng lớn hơn (khoảng 65–68% tổng nguồn vốn), chứng tỏ VNM duy trì cấu trúc vốn an toàn, ít phụ thuộc vào nợ vay. Sự tăng trưởng đều đặn của vốn chủ phản ánh việc tích lũy lợi nhuận và tái đầu tư hiệu quả.

Nợ phải trả (đường đỏ nét đứt) dao động từ 6,5 nghìn tỷ đến khoảng 18–19 nghìn tỷ, với mức tăng mạnh giai đoạn 2017–2021 (từ 10,8 lên 18,5 nghìn tỷ) do VNM vay vốn để tài trợ cho đầu tư mở rộng và M&A. Tuy nhiên, từ 2022 trở đi, nợ có xu hướng ổn định hoặc giảm nhẹ xuống 18,9 nghìn tỷ (2024), cho thấy doanh nghiệp chủ động kiểm soát đòn bẩy tài chính và giảm rủi ro sau giai đoạn đầu tư mạnh.

Khoảng cách giữa đường vốn chủ và đường nợ ngày càng mở rộng, phản ánh tỷ lệ Debt-to-Equity giảm dần từ mức cao nhất ~0,45 (2019–2021) xuống ~0,34 (2024), tương đương với debt ratio khoảng 25–32%. Đây là mức độ đòn bẩy trung bình, an toàn cho doanh nghiệp ngành tiêu dùng.

Tổng thể, biểu đồ cho thấy VNM thực hiện chiến lược tăng trưởng cân bằng: mở rộng quy mô tài sản một cách bền vững, ưu tiên tài trợ bằng vốn chủ (lợi nhuận giữ lại), sử dụng nợ có kiểm soát trong giai đoạn đầu tư cao điểm, và duy trì cấu trúc tài chính lành mạnh với tỷ lệ vốn chủ luôn chiếm ưu thế.

11. Cơ cấu tài sản và nợ phải trả

vnm %>%
  mutate(no_ratio = no_phai_tra / tong_cong_tai_san * 100,
         von_ratio = von_chu_so_huu / tong_cong_tai_san * 100) %>%
  tidyr::pivot_longer(c(no_ratio, von_ratio), names_to = "cau_truc", values_to = "ty_le") %>%
  ggplot(aes(x = nam, y = ty_le, fill = cau_truc)) +
  geom_col(position = "stack") +
  scale_fill_manual(values = c("#ef8a62", "#67a9cf"), labels = c("Nợ phải trả", "Vốn chủ sở hữu")) +
  labs(title = "Tỷ trọng Nợ và Vốn chủ trong Tổng tài sản", x = "Năm", y = "Tỷ lệ (%)", fill = "") +
  theme_minimal(base_size = 13)

Giải thích kỹ thuật

mutate(no_ratio = no_phai_tra / tong_cong_tai_san * 100, von_ratio = von_chu_so_huu / tong_cong_tai_san * 100): chuẩn hóa nợ phải trả và vốn chủ sở hữu thành tỷ lệ % trên tổng tài sản (đảm bảo mẫu số ≠ 0 và các cột là số).

pivot_longer(...): chuyển dữ liệu sang dạng dài để một trục thể hiện tên cấu phần (nợ/vốn), trục còn lại là tỷ lệ.

geom_col(position = "stack"): vẽ cột xếp chồng theo năm, tổng mỗi cột ≈ 100%.

scale_fill_manual(...): đặt nhãn chú giải tiếng Việt và màu sắc nhất quán.

labs(...) + theme_minimal(...): tiêu đề, nhãn trục, giao diện gọn gàng.

Ý nghĩa thống kê

Biểu đồ thể hiện sự biến động cơ cấu tài chính của doanh nghiệp qua các năm, cụ thể là mức độ sử dụng vốn chủ sở hữu và nợ phải trả trong tổng tài sản:

Vốn chủ sở hữu chiếm tỷ trọng lớn (khoảng 65–75%) trong toàn giai đoạn → cho thấy doanh nghiệp có nền tảng vốn tự có mạnh, ít phụ thuộc vào nguồn vốn vay. Đây là dấu hiệu của sức khỏe tài chính tốt và khả năng tự chủ cao.

Nợ phải trả dao động 25–35%, có xu hướng tăng nhẹ sau 2016, phản ánh chiến lược mở rộng hoạt động hoặc đòn bẩy tài chính có kiểm soát.

Giai đoạn 2019–2023, cơ cấu tương đối ổn định, không xuất hiện biến động lớn → chứng tỏ chính sách tài trợ vốn được duy trì ổn định, không xảy ra rủi ro mất cân đối tài chính.

Tuy nhiên, sự tăng dần của nợ cũng cho thấy áp lực chi phí vốn đang lớn hơn trước, cần được quản trị chặt chẽ để tránh rủi ro thanh khoản khi lãi suất thị trường tăng.

12. Tương quan giữa ROE và đòn bẩy tài chính (Debt Ratio)

ggplot(vnm, aes(x = no_phai_tra / tong_cong_tai_san * 100, y = roe)) +
  geom_point(size = 3, color = "#1b9e77") +
  geom_smooth(method = "lm", se = TRUE, color = "#d95f02") +
  labs(title = "Mối quan hệ giữa Đòn bẩy tài chính và ROE",
       x = "Tỷ lệ Nợ/Tài sản (%)", y = "ROE") +
  theme_minimal(base_size = 13)
## `geom_smooth()` using formula = 'y ~ x'
## Warning: Removed 1 row containing non-finite outside the scale range
## (`stat_smooth()`).
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_point()`).

Giải thích kỹ thuật

aes(x = no_phai_tra / tong_cong_tai_san * 100, y = roe): biểu diễn mối quan hệ giữa tỷ lệ Nợ/Tổng tài sản (%) (đòn bẩy tài chính) và ROE (tỷ suất lợi nhuận trên vốn chủ sở hữu).

geom_point(size = 3, color = "#1b9e77"): vẽ các điểm dữ liệu thực tế, thể hiện giá trị từng năm.

geom_smooth(method = "lm", se = TRUE, color = "#d95f02"): thêm đường hồi quy tuyến tính (linear regression) để mô tả xu hướng quan hệ giữa hai biến; vùng xám là khoảng tin cậy (confidence interval).

labs(...): đặt tiêu đề, nhãn trục X, Y.

theme_minimal(base_size = 13): định dạng biểu đồ rõ ràng, chuyên nghiệp.

Ý nghĩa thống kê

Biểu đồ cho thấy mối quan hệ nghịch biến giữa tỷ lệ nợ/tổng tài sản (đòn bẩy tài chính) và ROE (hiệu quả sinh lời của vốn chủ):

Khi tỷ lệ nợ tăng, ROE có xu hướng giảm, thể hiện đòn bẩy tài chính cao không còn mang lại lợi ích, mà ngược lại có thể làm giảm hiệu quả sinh lời.

Trong các năm có tỷ lệ nợ dưới ~28%, ROE đạt trên 0.4 (tức >40%), phản ánh khả năng sinh lợi cao nhờ cấu trúc vốn lành mạnh.

Giai đoạn sau khi tỷ lệ nợ tăng lên trên 30–33%, ROE giảm xuống khoảng 0.3 hoặc thấp hơn, cho thấy chi phí sử dụng vốn vay và rủi ro tài chính đã bắt đầu ảnh hưởng tiêu cực đến lợi nhuận.

Độ dốc âm của đường hồi quy (đường màu cam) xác nhận xu hướng giảm rõ rệt, đồng thời vùng tin cậy khá rộng cho thấy mối quan hệ có thể chịu ảnh hưởng của các yếu tố khác (như lãi suất, hiệu quả hoạt động, hoặc cấu trúc tài sản).

13. Xu hướng ROE qua các năm

ggplot(vnm, aes(x = nam, y = roe)) +
  geom_line(color = "#2a9d8f", size = 1.2) +
  geom_point(size = 3, color = "#264653") +
  labs(title = "Xu hướng ROE của Vinamilk qua các năm",
       x = "Năm", y = "Tỷ suất sinh lời trên vốn chủ sở hữu (ROE)") +
  theme_minimal()
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_line()`).
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_point()`).

Giải thích kỹ thuật

aes(x = nam, y = roe): biểu diễn tỷ suất lợi nhuận trên vốn chủ sở hữu (ROE) theo năm.

geom_line(color = "#2a9d8f", size = 1.2): vẽ đường nối liền các năm, biểu diễn xu hướng biến động của ROE.

geom_point(size = 3, color = "#264653"): thêm điểm dữ liệu tại từng năm để dễ nhận diện giá trị cụ thể.

labs(...): đặt tiêu đề và nhãn cho các trục.

theme_minimal(): định dạng biểu đồ đơn giản, hiện đại và dễ đọc.

Ý nghĩa thống kê

Biểu đồ thể hiện xu hướng giảm mạnh của ROE của Vinamilk qua các năm, phản ánh sự thay đổi trong hiệu quả sử dụng vốn chủ sở hữu:

Giai đoạn 2015–2017: ROE duy trì ở mức cao ~43–45%, thể hiện hiệu quả sinh lời vượt trội nhờ biên lợi nhuận tốt, doanh thu và lợi nhuận sau thuế tăng ổn định.

Từ 2018 trở đi, ROE giảm dần đều, đến năm 2022 chỉ còn ~25%, tức giảm gần 20 điểm % so với giai đoạn đỉnh — cho thấy hiệu quả vốn giảm rõ rệt.

2023 chứng kiến tín hiệu ổn định nhẹ (~26–27%), nhưng chưa có dấu hiệu phục hồi mạnh.

Nguyên nhân có thể đến từ:

Biên lợi nhuận gộp giảm do chi phí nguyên liệu, logistics tăng;

Tăng trưởng doanh thu chậm lại, trong khi vốn chủ sở hữu tăng nhanh hơn (do tích lũy lợi nhuận giữ lại), khiến ROE bị pha loãng;

Áp lực cạnh tranh trong ngành sữa dẫn đến biên lợi nhuận ròng (NPM) giảm.

14. Quan hệ giữa quy mô tài sản và lợi nhuận sau thuế

ggplot(vnm, aes(x = tong_cong_tai_san, y = loi_nhuan_sau_thue_tndn)) +
  geom_point(size = 3, color = "#2A9D8F", alpha = 0.8) +
  geom_smooth(method = "lm", se = TRUE, color = "black") +
  scale_x_log10(labels = scales::comma) +
  scale_y_log10(labels = scales::comma) +
  labs(title = "Quan hệ giữa quy mô tài sản và lợi nhuận sau thuế",
       x = "Tổng tài sản (log scale)", y = "Lợi nhuận sau thuế (log scale)") +
  theme_minimal(base_family = "TimesVN")
## `geom_smooth()` using formula = 'y ~ x'

Giải thích kỹ thuật:

ggplot(vnm, aes(x = tong_cong_tai_san, y = loi_nhuan_sau_thue_tndn)): Khởi tạo biểu đồ từ dữ liệu vnm, ánh xạ trục hoành là tổng tài sản và trục tung là lợi nhuận sau thuế.

geom_point(size = 3, color = "#2A9D8F", alpha = 0.8): Vẽ các điểm dữ liệu rải (scatter plot), mỗi điểm biểu diễn mối quan hệ giữa tài sản và lợi nhuận trong một năm. alpha = 0.8 tạo độ trong suốt nhẹ để dễ nhìn khi các điểm trùng nhau.

geom_smooth(method = "lm", se = TRUE, color = "black"): Thêm đường hồi quy tuyến tính (linear regression line) mô tả xu hướng quan hệ giữa hai biến; se = TRUE hiển thị vùng tin cậy (confidence interval) màu xám thể hiện độ không chắc chắn của ước lượng.

scale_x_log10() và scale_y_log10(): Dùng tỷ lệ logarit (log scale) để chuẩn hóa giá trị lớn, giúp quan sát mối quan hệ tuyến tính rõ hơn giữa các giá trị chênh lệch hàng tỷ đồng. labels = scales::comma định dạng số có dấu phẩy ngăn cách hàng nghìn.

labs(...): Đặt tiêu đề, tên trục X, trục Y bằng tiếng Việt, diễn giải rõ nội dung biểu đồ.

theme_minimal(base_family = "TimesVN"): Áp dụng giao diện tối giản và font chữ Times, phù hợp cho báo cáo tài chính chuyên nghiệp.

Ý nghĩa thống kê:

Biểu đồ thể hiện mối quan hệ giữa quy mô tổng tài sản và lợi nhuận sau thuế của Vinamilk.

Đường hồi quy cho thấy xu hướng dương nhẹ, nghĩa là doanh nghiệp có quy mô tài sản lớn hơn thường đạt lợi nhuận sau thuế cao hơn, tuy nhiên độ dốc không quá mạnh — cho thấy tăng tài sản không làm lợi nhuận tăng tương ứng một cách đáng kể.

Vùng xám thể hiện độ tin cậy rộng, nghĩa là sự biến động giữa các năm khá cao, có thể do sự thay đổi hiệu quả sử dụng tài sản hoặc chi phí đầu tư khác nhau.

Tổng thể, mối quan hệ này cho thấy Vinamilk vẫn duy trì hiệu quả sinh lời tương đối ổn định theo quy mô, nhưng hiệu quả biên (marginal efficiency) có dấu hiệu giảm dần, hàm ý rằng việc mở rộng tài sản cần đi kèm tối ưu hóa chi phí và năng suất để duy trì tăng trưởng lợi nhuận bền vững.

15. Chi phí lãi vay và lợi nhuận thuần

ggplot(vnm, aes(x = nam)) +
  geom_bar(aes(y = trong_do_chi_phi_lai_vay), stat = "identity", fill = "#e76f51") +  
  geom_line(aes(y = loi_nhuan_thuan_tu_hoat_dong_kinh_doanh / 2), color = "#264653", size = 1.2) +  
  scale_y_continuous(labels = scales::comma) +  
  labs(title = "So sánh chi phí lãi vay và lợi nhuận thuần",
       x = "Năm", y = "Giá trị (VND)") +  
  theme_minimal(base_family = "TimesVN")   

Giải thích kỹ thuật:

ggplot(vnm, aes(x = nam)): Khởi tạo đối tượng ggplot với biến năm (nam) trên trục hoành.

geom_bar(aes(y = trong_do_chi_phi_lai_vay), stat = "identity", fill = "#e76f51"):
Vẽ biểu đồ cột thể hiện chi phí lãi vay theo năm.

stat = "identity": giá trị chiều cao cột được lấy trực tiếp từ dữ liệu.

fill = "#e76f51": tô màu đỏ cam để dễ phân biệt với đường lợi nhuận.

geom_line(aes(y = loi_nhuan_thuan_tu_hoat_dong_kinh_doanh / 2), color = "#264653", size = 1.2):
Thêm đường xu hướng lợi nhuận thuần từ hoạt động kinh doanh.

Biến chia 2 (/ 2) nhằm đưa giá trị lợi nhuận về cùng tỷ lệ hiển thị với chi phí lãi vay, giúp hai đại lượng dễ so sánh trên cùng trục tung.

color = "#264653" (xanh đậm) và size = 1.2 tạo điểm nhấn cho đường xu hướng.

scale_y_continuous(labels = scales::comma): Định dạng giá trị trục tung có dấu phẩy ngăn cách hàng nghìn, thuận tiện khi biểu diễn đơn vị VND lớn.

labs(...): Gán tiêu đề và nhãn trục rõ ràng, giúp biểu đồ có tính thuyết minh cao.

theme_minimal(base_family = "TimesVN"): Dùng giao diện tối giản, font Times thân thiện với báo cáo tài chính, tạo cảm giác trang nhã và dễ đọc.

Ý nghĩa thống kê:

Biểu đồ thể hiện mối tương quan giữa chi phí lãi vay và lợi nhuận thuần từ hoạt động kinh doanh của Vinamilk trong giai đoạn 2015–2024.

Kết quả cho thấy chi phí lãi vay (cột đỏ cam) ở mức rất thấp so với lợi nhuận thuần, phản ánh chính sách tài chính an toàn và mức độ vay nợ thấp của doanh nghiệp.

Trong khi lợi nhuận thuần (đường xanh đậm) tăng mạnh giai đoạn 2015–2020, đạt đỉnh khoảng năm 2021, sau đó giảm đáng kể và chỉ phục hồi nhẹ giai đoạn 2023–2024.

Điều này cho thấy hiệu quả hoạt động kinh doanh của Vinamilk chịu áp lực giảm, có thể do biên lợi nhuận co hẹp và chi phí nguyên liệu đầu vào tăng, tuy nhiên rủi ro tài chính vẫn được kiểm soát tốt nhờ duy trì mức vay nợ thấp và chi phí lãi ổn định.

16. Biến động Lưu chuyển tiền thuần

library(dplyr)
library(readr)

ggplot(
  vnm %>%
    mutate(
      # Ép kiểu an toàn (nếu file có dấu phẩy/ngàn)
      across(c(luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh,
               luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu,
               luu_chuyen_tien_thuan_tu_hoat_dong_tai_chinh),
             ~ parse_number(as.character(.),
                            locale = locale(grouping_mark = ",", decimal_mark = "."))),
      # Tạo biến cần vẽ: LCTT = OCF + ICF + FCF (tài chính)
      luu_chuyen_tien_thuan =
        luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh +
        luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu +
        luu_chuyen_tien_thuan_tu_hoat_dong_tai_chinh
    ),
  aes(x = nam, y = luu_chuyen_tien_thuan / 1e9)
) +
  geom_col(aes(fill = luu_chuyen_tien_thuan > 0), width = 0.6, show.legend = FALSE) +
  geom_line(color = "#264653", size = 1.2, group = 1) +
  geom_point(color = "#264653", size = 2) +
  geom_text(aes(label = round(luu_chuyen_tien_thuan / 1e9, 0)),
            vjust = -0.4, size = 3, color = "#333") +
  labs(
    title = "Biến động Lưu chuyển tiền thuần (2015–2024)",
    subtitle = "Dòng tiền dương phản ánh khả năng tạo tiền từ hoạt động kinh doanh",
    x = "Năm", y = "Giá trị (tỷ VND)",
    caption = "Nguồn: Báo cáo lưu chuyển tiền tệ VNM"
  ) +
  theme_minimal(base_family = "TimesVN") +
  scale_fill_manual(values = c("#e63946", "#2a9d8f"))
## Warning: Removed 2 rows containing missing values or values outside the scale range
## (`geom_col()`).
## Warning: Removed 2 rows containing missing values or values outside the scale range
## (`geom_line()`).
## Warning: Removed 2 rows containing missing values or values outside the scale range
## (`geom_point()`).
## Warning: Removed 2 rows containing missing values or values outside the scale range
## (`geom_text()`).

Giải thích kỹ thuật:
    
mutate(across(..., ~ parse_number(...))): ép 3 cột dòng tiền (KD/ĐT/TC) về numeric an toàn nếu dữ liệu gốc có dấu phẩy ngăn cách nghìn.

mutate(luu_chuyen_tien_thuan = KD + ĐT + TC): tạo cột tổng dùng để vẽ. Việc tạo biến trực tiếp trong pipeline truyền vào ggplot() đảm bảo cột tồn tại đúng thời điểm ggplot đánh giá aesthetics.

ggplot(data = vnm %>% mutate(...), aes(x = nam, y = luu_chuyen_tien_thuan / 1e9))`: trục hoành là năm, trục tung là lưu chuyển tiền thuần quy đổi tỷ VND (chia `1e9`).

geom_col(aes(fill = luu_chuyen_tien_thuan > 0), width = 0.6, show.legend = FALSE)`: cột biểu diễn quy mô; fill có điều kiện** để phân biệt dương/âm; tắt legend cho gọn.

geom_line(color = "#264653", size = 1.2, group = 1) & geom_point(...): đường + điểm nối theo năm; group = 1` bắt buộc để ggplot hiểu là một nhóm duy nhất cho dữ liệu theo thời gian.

geom_text(aes(label = round(luu_chuyen_tien_thuan / 1e9, 0)), vjust = -0.4, size = 3)`: in số (tỷ VND, đã làm tròn) ngay trên cột, dễ đọc.

scale_fill_manual(values = c("#e63946", "#2a9d8f")): quy ước đỏ cho âm, xanh ngọc cho dương.

labs(title, subtitle, x, y, caption): đặt tiêu đề, phụ đề, nhãn trục, nguồn.

theme_minimal(base_family = "TimesVN"): theme tối giản, font thống nhất cho báo cáo.

Ý nghĩa thống kê:

Biểu đồ cho thấy sự biến động mạnh của lưu chuyển tiền thuần của Vinamilk trong giai đoạn 2015–2024.

Năm 2015 ghi nhận dòng tiền dương cao đột biến (~3.769 tỷ VND), phản ánh khả năng tạo tiền mạnh từ hoạt động kinh doanh.

Giai đoạn 2016–2019 có sự hồi phục dần sau một năm âm, đạt đỉnh vào 2020 với hơn 1.100 tỷ VND trước khi giảm trở lại.

2020 và 2022 xuất hiện dòng tiền âm, cho thấy áp lực chi tiêu hoặc đầu tư tăng cao.

Dù vậy, giai đoạn 2023–2024 chứng kiến sự cải thiện nhẹ, thể hiện nỗ lực kiểm soát dòng tiền và duy trì thanh khoản ổn định.

Tổng thể, biểu đồ phản ánh tính chu kỳ và nhạy cảm của dòng tiền với hoạt động sản xuất – kinh doanh, đồng thời nhấn mạnh rằng Vinamilk vẫn duy trì năng lực tạo tiền lành mạnh dù có giai đoạn suy giảm.

17. Tỷ trọng hàng tồn kho & phải thu

ggplot(vnm, aes(x = nam)) +
  geom_bar(aes(y = hang_ton_kho / tong_cong_tai_san * 100), stat = "identity", fill = "#f4a261") +
  geom_line(aes(y = phai_thu_khach_hang / tong_cong_tai_san * 100), color = "#264653", size = 1.2) +
  geom_point(aes(y = phai_thu_khach_hang / tong_cong_tai_san * 100), color = "#264653", size = 2) +
  labs(title = "Tỷ trọng Hàng tồn kho & Phải thu khách hàng",
       subtitle = "Phản ánh hiệu quả quản trị vốn lưu động",
       x = "Năm", y = "Tỷ trọng (%)") +
  theme_minimal(base_family = "TimesVN")

Giải thích kỹ thuật:

ggplot(vnm, aes(x = nam)): Khởi tạo biểu đồ ggplot với biến năm làm trục hoành, giúp thể hiện xu hướng theo thời gian.

geom_bar(aes(y = hang_ton_kho / tong_cong_tai_san * 100), stat = "identity", fill = "#f4a261"): Vẽ biểu đồ cột biểu diễn tỷ trọng hàng tồn kho trên tổng tài sản, sử dụng màu cam (#f4a261) nhằm trực quan hóa phần vốn bị “đóng băng” trong hàng tồn kho.

geom_line(aes(y = phai_thu_khach_hang / tong_cong_tai_san * 100), color = "#264653", size = 1.2) và geom_point(...): Thêm đường và điểm dữ liệu mô tả tỷ trọng phải thu khách hàng, giúp so sánh động thái giữa hai chỉ tiêu theo năm.

labs(...): Đặt tiêu đề, phụ đề và nhãn trục đầy đủ, nêu rõ mục tiêu đo lường hiệu quả quản trị vốn lưu động.

theme_minimal(base_family = "TimesVN"): Sử dụng chủ đề tối giản và phông TimesVN nhằm tạo cảm giác thanh lịch, dễ đọc trong báo cáo tài chính.

Ý nghĩa thống kê:

Biểu đồ thể hiện tỷ trọng hàng tồn kho (cột cam) và tỷ trọng phải thu khách hàng (đường xanh) trong tổng tài sản của Vinamilk giai đoạn 2015–2024, qua đó phản ánh hiệu quả sử dụng vốn lưu động.

Giai đoạn 2015–2017, hàng tồn kho chiếm tỷ trọng cao (trên 15%), trong khi phải thu khách hàng dao động quanh 8–10%, cho thấy nguồn vốn bị giữ nhiều trong hàng hóa.

Từ 2018–2021, tỷ trọng cả hai biến giảm dần, phản ánh hiệu quả quản trị hàng tồn kho và thu hồi công nợ được cải thiện.

2022–2024, hai chỉ tiêu duy trì ổn định quanh mức 10–12%, chứng tỏ doanh nghiệp kiểm soát tốt vốn lưu động, tránh được rủi ro ứ đọng hàng và chậm thu tiền.

Nhìn chung, xu hướng ổn định và không biến động mạnh thể hiện năng lực quản trị tài chính ngắn hạn bền vững của Vinamilk, góp phần duy trì tính thanh khoản và vòng quay vốn hiệu quả.

18. Chi trả cổ tức qua các năm

ggplot(vnm, aes(x = nam, y = tien_chi_tra_co_tuc / 1e9)) +
  geom_col(fill = "#ffb703", width = 0.6) +
  geom_text(aes(label = round(tien_chi_tra_co_tuc / 1e9, 0)),
            vjust = -0.5, size = 3, color = "#333") +
  geom_smooth(method = "lm", se = FALSE, color = "#023047", size = 1.2) +
  labs(title = "Chi trả cổ tức của Vinamilk (2015–2024)",
       subtitle = "Thể hiện mức độ hào phóng với cổ đông",
       x = "Năm", y = "Tỷ VND") +
  theme_minimal(base_family = "TimesVN")
## `geom_smooth()` using formula = 'y ~ x'

Giải thích kỹ thuật:

ggplot(vnm, aes(x = nam, y = tien_chi_tra_co_tuc / 1e9)): Khởi tạo biểu đồ với biến năm làm trục hoành và số tiền chi trả cổ tức (tỷ VND) làm trục tung.

geom_col(fill = "#ffb703", width = 0.6): Vẽ biểu đồ cột với màu vàng (#ffb703) nhằm biểu thị quy mô chi trả cổ tức, giúp người đọc dễ nhận diện giá trị theo từng năm.

geom_text(...): Gắn nhãn số liệu trực tiếp trên đầu cột, giúp theo dõi giá trị chi trả rõ ràng hơn mà không cần nhìn trục tung.

geom_smooth(method = "lm", se = FALSE, color = "#023047", size = 1.2): Thêm đường hồi quy tuyến tính biểu diễn xu hướng chi trả cổ tức qua thời gian; se = FALSE loại bỏ vùng tin cậy để biểu đồ gọn gàng hơn.

labs(...): Cung cấp tiêu đề, phụ đề và nhãn trục thể hiện rõ mục tiêu phân tích — đo lường mức độ hào phóng với cổ đông.

theme_minimal(base_family = "TimesVN"): Sử dụng giao diện tối giản với phông chữ TimesVN để đảm bảo tính chuyên nghiệp, đồng nhất với các biểu đồ tài chính khác.

Ý nghĩa thống kê:

Biểu đồ phản ánh mức chi trả cổ tức của Vinamilk trong giai đoạn 2015–2024, qua đó thể hiện chính sách phân phối lợi nhuận và cam kết với cổ đông.

Từ 2015 đến 2017, chi trả cổ tức dao động quanh -6.000 đến -7.200 tỷ VND, cho thấy Vinamilk duy trì chính sách cổ tức ổn định và khá cao.

Giai đoạn 2018–2021, mức chi trả tiếp tục tăng dần và đạt đỉnh khoảng -7.900 tỷ VND, thể hiện năng lực sinh lời tốt và chính sách chia cổ tức đều đặn.

Tuy nhiên, từ 2022 trở đi, giá trị chi trả duy trì quanh -8.000 tỷ VND nhưng có xu hướng ổn định chứ không tăng thêm, phản ánh doanh nghiệp đã đạt ngưỡng cân bằng giữa tái đầu tư và chia cổ tức.

Tổng thể, Vinamilk duy trì cổ tức cao, đều đặn trong gần một thập kỷ, chứng tỏ chính sách tài chính bền vững, lấy cổ đông làm trung tâm và năng lực tạo dòng tiền ổn định của doanh nghiệp.

19. Biến động tiền mặt cuối kỳ

ggplot(vnm, aes(x = nam, y = tien_va_cac_khoan_tuong_duong_tien / 1e9)) +
  geom_col(fill = "#48cae4", width = 0.6) +
  geom_line(color = "#0077b6", size = 1.2, group = 1) +
  geom_point(color = "#023e8a", size = 2) +
  geom_text(aes(label = round(tien_va_cac_khoan_tuong_duong_tien / 1e9, 0)), 
            vjust = -0.4, size = 3, color = "#333") +
  labs(title = "Biến động tiền mặt cuối kỳ",
       subtitle = "Tiền là thước đo an toàn và thanh khoản dài hạn",
       x = "Năm", y = "Tỷ VND") +
  theme_minimal(base_family = "TimesVN")

Giải thích kỹ thuật:

ggplot(vnm, aes(x = nam, y = tien_va_cac_khoan_tuong_duong_tien / 1e9)): Khởi tạo biểu đồ với trục hoành là năm và trục tung biểu diễn tiền và các khoản tương đương tiền (tỷ VND).

geom_col(fill = "#48cae4", width = 0.6): Vẽ cột tiền mặt cuối kỳ với tông xanh dương nhạt (#48cae4) nhằm thể hiện quy mô thanh khoản của doanh nghiệp theo từng năm.

geom_line(color = "#0077b6", size = 1.2, group = 1) và geom_point(color = "#023e8a", size = 2): Thêm đường nối và điểm dữ liệu, giúp người xem nhận diện xu hướng tăng – giảm qua các năm.

geom_text(...): Hiển thị nhãn số liệu cụ thể trên đầu cột, hỗ trợ việc đọc giá trị trực quan.

labs(...): Thiết lập tiêu đề, phụ đề và nhãn trục để nêu rõ ý nghĩa: tiền mặt là thước đo an toàn và khả năng thanh toán của doanh nghiệp.

theme_minimal(base_family = "TimesVN"): Áp dụng giao diện tối giản, phông chữ TimesVN giúp biểu đồ chuyên nghiệp, phù hợp với báo cáo tài chính.

Ý nghĩa thống kê:

Biểu đồ mô tả biến động tiền mặt cuối kỳ của Vinamilk giai đoạn 2015–2024, phản ánh mức độ thanh khoản và khả năng dự trữ tiền mặt của doanh nghiệp.

Giai đoạn 2015–2016, tiền mặt giảm mạnh từ 1.359 tỷ xuống 585 tỷ VND, cho thấy doanh nghiệp sử dụng nhiều tiền cho đầu tư hoặc chi trả cổ tức.

Từ 2017–2019, dòng tiền tăng đều, đạt 2.665 tỷ VND vào năm 2019, thể hiện khả năng tạo tiền mạnh mẽ từ hoạt động kinh doanh.

2020–2022 chứng kiến dao động nhẹ quanh mức 2.300–2.400 tỷ, phản ánh chính sách duy trì thanh khoản ổn định dù thị trường biến động.

2023 ghi nhận đỉnh cao nhất (2.912 tỷ VND), trước khi giảm nhẹ năm 2024, cho thấy chu kỳ tích trữ và sử dụng tiền mặt hợp lý.

Nhìn chung, Vinamilk duy trì lượng tiền mặt dồi dào và ổn định, đảm bảo khả năng thanh toán ngắn hạn, đồng thời thể hiện sức khỏe tài chính và năng lực quản trị dòng tiền hiệu quả.

20. Tương quan giữa năm và dòng tiền các hoạt động.

library(reshape2)
df_heat <- vnm %>%
  select(nam, luu_chuyen_tien_thuan_tu_hoat_dong_kinh_doanh, luu_chuyen_tien_thuan_tu_hoat_dong_dau_tu, luu_chuyen_tien_thuan_tu_hoat_dong_tai_chinh) %>%
  melt(id = "nam")
## Warning: attributes are not identical across measure variables; they will be
## dropped
ggplot(df_heat, aes(x = variable, y = factor(nam), fill = value)) +
  geom_tile(color = "white") +
  scale_fill_gradient(low = "#e9f5db", high = "#2a9d8f") +
  labs(title = "Biến động dòng tiền theo năm", x = "Hoạt động", y = "Năm") +
  theme_minimal(base_family = "TimesVN")

Giải thích kỹ thuật:

Dữ liệu được chuyển từ dạng rộng sang dài bằng melt() sau khi select() ba cột dòng tiền (KD, ĐT, TC) và nam; việc này tạo hai cột variable (loại hoạt động) và value (giá trị).

ggplot(df_heat, aes(x = variable, y = factor(nam), fill = value)) + geom_tile(color = "white"): Vẽ heatmap với từng ô là một năm × hoạt động, viền trắng giúp tách ô rõ ràng.

scale_fill_gradient(low = "#e9f5db", high = "#2a9d8f"): Dùng thang đơn sắc từ nhạt → đậm để mã hóa mức độ (độ lớn) giá trị; ô đậm hơn = giá trị lớn hơn. (Lưu ý: thang này không biểu diễn dấu âm/dương một cách khác biệt màu, nên đọc theo cường độ là chính.)

labs(...) và theme_minimal(base_family = "TimesVN"): Thiết lập tiêu đề, nhãn trục và theme tối giản phù hợp báo cáo.

y = factor(nam): Giữ thứ tự năm theo danh mục, dễ đọc theo hàng ngang. (Ô xám nếu có là do NA – thiếu dữ liệu tại năm/hoạt động đó.)

Ý nghĩa thống kê:

Dòng tiền từ hoạt động kinh doanh (KD) nhìn chung cao và ổn định qua nhiều năm (ô màu đậm hơn), cho thấy khả năng tạo tiền từ lõi kinh doanh tốt.

Dòng tiền từ hoạt động đầu tư (ĐT) chủ yếu ở mức thấp/nhạt, phản ánh dòng ra ròng (đầu tư máy móc, M&A, CAPEX) là chủ đạo trong giai đoạn quan sát.

Dòng tiền từ hoạt động tài chính (TC) thay đổi theo năm (sắc độ dao động), phù hợp với các quyết định chi trả cổ tức, vay – trả nợ và tái cơ cấu nguồn vốn.

Cấu trúc heatmap cho thấy mô hình lành mạnh: KD dương, ĐT âm, TC linh hoạt — doanh nghiệp tự tài trợ được phần lớn nhu cầu vốn từ hoạt động kinh doanh, còn tài chính được dùng để điều tiết theo chu kỳ.

Một vài ô xám (NA) nếu xuất hiện gợi ý thiếu số liệu ở năm/hoạt động tương ứng; khi diễn giải nên loại trừ các ô này để tránh sai kết luận.