1 Bộ dữ liệu UK Accidents 2005 - 2015

1.1 Giới thiệu bộ dữ liệu

Bộ dữ liệu [Accidents0515] là tập hợp thống kê chi tiết về các vụ tai nạn giao thông xảy ra trên lãnh thổ Vương quốc Anh trong giai đoạn 2005–2015. Dữ liệu được công bố bởi Bộ giao thông Anh, bao gồm thông tin đầy đủ về đặc điểm sự kiện, địa điểm, mức độ nghiêm trọng, số phương tiện tham gia và nhiều yếu tố liên quan đến môi trường, kết cấu hạ tầng giao thông tại thời điểm xảy ra tai nạn. Đây là nguồn dữ liệu tiêu chuẩn, thường dùng cho các nghiên cứu phân tích về an toàn giao thông, đánh giá rủi ro và hoạch định chính sách.

Các biến chính được lựa chọn để phân tích gồm: giới hạn tốc độ (Speed_limit), loại đường (Road_Type), mức độ nghiêm trọng của vụ tai nạn (Accident_Severity), số phương tiện tham gia (Number_of_Vehicles), số nạn nhân (Number_of_Casualties), thông tin thời gian (Date, Day_of_Week, Time), và điều kiện môi trường (Weather_Conditions, Light_Conditions, Road_Surface_Conditions). Những biến này phản ánh rõ cả yếu tố cấu trúc hạ tầng, hoàn cảnh xảy ra và hậu quả sự kiện.

Giải thích ý nghĩa các biến được sử dụng Accident_Severity: Là chỉ số quan trọng phản ánh mức độ nghiêm trọng của hậu quả tai nạn, mã hóa theo 3 mức: 1 = tử vong, 2 = nghiêm trọng, 3 = nhẹ. Đây là biến mục tiêu trong các phân tích đánh giá rủi ro, tìm nguyên nhân.

Speed_limit: Thể hiện giới hạn tốc độ (mph) tại địa điểm xảy ra vụ tai nạn – là nhân tố then chốt tác động đến nguy cơ và hậu quả va chạm.

Road_Type: Phân loại hạ tầng giao thông tại điểm xảy ra tai nạn (ví dụ: “Hai chiều”, “Một chiều”, “Vòng xuyến”, “Đường nhánh”, …), giúp xác định nguy cơ đặc thù từng loại đường.

Number_of_Vehicles: Tổng số phương tiện tham gia trong mỗi vụ tai nạn; cho phép phân tích quy mô vụ việc và đánh giá tính chất va chạm (đa phương tiện hay chỉ va chạm nhỏ).

Number_of_Casualties: Tổng số nạn nhân (bao gồm cả người bị thương và tử vong) – là chỉ tiêu về mức độ ảnh hưởng tới con người.

Date, Day_of_Week, Time: Chuỗi thông tin ngày, thứ và giờ giúp nghiên cứu xu hướng theo thời gian, xác định các khung giờ, ngày trong tuần hoặc mùa vụ có nguy cơ cao về tai nạn.

Longitude, Latitude: Tọa độ không gian của vụ việc, phục vụ trực quan hóa trên bản đồ, phát hiện điểm nóng hoặc phân tích nguy cơ theo khu vực.

Weather_Conditions, Light_Conditions, Road_Surface_Conditions: Phản ánh điều kiện môi trường (thời tiết, ánh sáng, tình trạng mặt đường) tại thời điểm xảy ra tai nạn – các yếu tố nền tảng tác động mạnh đến xác suất và mức độ nghiêm trọng của va chạm giao thông.

Như vậy, việc chọn lọc các biến trên giúp tập trung vào các khía cạnh cốt lõi nhất tác động đến an toàn giao thông, đảm bảo kết quả phân tích vừa cụ thể tình huống thực tế vừa có giá trị ứng dụng trong xây dựng giải pháp hoặc chính sách cải thiện tình hình tai nạn đường bộ.

1.1.1 Cài đặt & gọi thư viện cần thiết

library(dplyr)
library(readr)
library(ggplot2)
library(lubridate)

Đây là các dòng lệnh được chạy trước khi thao tác dữ liệu. Trong ngôn ngữ lập trình R, để thực hiện các chức năng tổng hợp, phân tích số liệu, và trực quan hóa dữ liệu, ta phải sử dụng các “thư viện” (packages) mở rộng đã được cộng đồng phát triển sẵn từ trước.

Thư viện dplyr giúp xử lý, biến đổi, tổng hợp và phân tích nhóm dữ liệu cực kỳ nhanh gọn, tiện lợi.

Thư viện readr hỗ trợ đọc dữ liệu từ các file .csv, .xls vào môi trường R một cách chính xác, tự động nhận diện kiểu dữ liệu cho từng cột. Điều này rất quan trọng khi làm việc với dữ liệu thực tế, vì file thật thường có rất nhiều biến với đủ loại định dạng (số, chuỗi, ngày tháng…).

Thư viện ggplot2 dùng cho việc vẽ các loại biểu đồ hiện đại, trực quan hóa dữ liệu để phát hiện xu hướng, bất thường, mẫu hình trong dữ liệu dễ dàng, hiện đại nhất trong hệ sinh thái R.

Thư viện lubridate đặc biệt mạnh về chuyển đổi và xử lý biến Ngày/Thời gian – đây là dạng dữ liệu rất hay gặp khi phân tích số liệu hành chính, xã hội, kinh tế…

Chạy các dòng này giúp R “nạp” các công cụ vào bộ nhớ, để các lệnh nâng cao bên dưới có thể hoạt động mà không phải lập trình lại mọi hàm từ đầu như các ngôn ngữ cổ điển.

1.1.2 Đọc file csv

dulieu <- read_csv("C:/TL/BCTC/Accidents0515.csv")

Dòng lệnh này có chức năng “nhập” toàn bộ dữ liệu tai nạn giao thông Anh quốc (giai đoạn 2005–2015) từ file .csv trên máy mình vào biến tên là dulieu, để toàn bộ thao tác về sau đều sẽ thực hiện trên khối dữ liệu này.

Đầu ra sau khi chạy lệnh cho thấy file này bao gồm tới 1.780.653 dòng dữ liệu (tương ứng hơn 1,7 triệu vụ việc được ghi nhận trên toàn quốc) với32 cột (tức là 32 biến mô tả tính chất, bối cảnh, hậu quả, … của từng vụ).

Có thể thấy kiểu dữ liệu khá đa dạng: một số cột là dạng chuỗi ký tự (chr – ví dụ: mã sự kiện, ngày tháng, mã địa phương), số còn lại là kiểu số thực (dbl), hoặc kiểu thời gian (time) dùng cho giờ xảy ra tai nạn.

Việc check kỹ cấu trúc kiểu này giúp người xử lý luôn nhận biết file đã được đọc lên đúng dạng chưa, giúp hạn chế lỗi sai khi chạy tiếp các thao tác xử lý (vì nếu file lớn, sai kiểu dữ liệu sẽ cực khó sửa thủ công từng dòng).

1.1.3 Xem qua số dòng/cột

dim(dulieu) 
## [1] 1780653      32

Lệnh này xuất ra kết quả gồm 2 số: 1.780.653 (dòng) và 32 (cột). Đây là bước đầu tiên luôn cần thực hiện khi khám phá tập dữ liệu mới, giúp người làm có hình dung về quy mô khối dữ liệu mình cần xử lý – trong trường hợp này là dữ liệu cực lớn, đầy đủ, có thể phục vụ phân tích chuyên sâu và suy luận thống kê ở quy mô quốc gia.

1.1.4 Kiểm tra tên biến trong bộ dữ liệu

names(dulieu)[1:7]
## [1] "Accident_Index"         "Location_Easting_OSGR"  "Location_Northing_OSGR"
## [4] "Longitude"              "Latitude"               "Police_Force"          
## [7] "Accident_Severity"

Việc liệt kê toàn bộ tên biến giúp nhanh chóng xác định các thông tin quan trọng mà tập dữ liệu cung cấp. Với các bộ dữ liệu thực tế, việc quen thuộc với tất cả tên biến, biết rõ chính tả, kiểu đặt tên (có dấu “_” hay không, dùng tiếng Anh ra sao…) là điều rất quan trọng để tránh nhầm lẫn khi truy xuất, phân tích các biến cụ thể, nhất là với các lệnh filter, summary, group_by… Việc kiểm đếm tên biến cũng giúp phát hiện có thừa thiếu cột nào, hoặc có ký tự đặc biệt khiến code bị báo lỗi hay không.

1.1.5 Xem nhanh 5 dòng đầu tiên và cuối cùng của bảng dữ liệu

head(dulieu, 5)
## # A tibble: 5 × 32
##   Accident_Index Location_Easting_OSGR Location_Northing_OSGR Longitude Latitude
##   <chr>                          <dbl>                  <dbl>     <dbl>    <dbl>
## 1 200501BS00001                 525680                 178240    -0.191     51.5
## 2 200501BS00002                 524170                 181650    -0.212     51.5
## 3 200501BS00003                 524520                 182240    -0.206     51.5
## 4 200501BS00004                 526900                 177530    -0.174     51.5
## 5 200501BS00005                 528060                 179040    -0.157     51.5
## # ℹ 27 more variables: Police_Force <dbl>, Accident_Severity <dbl>,
## #   Number_of_Vehicles <dbl>, Number_of_Casualties <dbl>, Date <chr>,
## #   Day_of_Week <dbl>, Time <time>, `Local_Authority_(District)` <dbl>,
## #   `Local_Authority_(Highway)` <chr>, `1st_Road_Class` <dbl>,
## #   `1st_Road_Number` <dbl>, Road_Type <dbl>, Speed_limit <dbl>,
## #   Junction_Detail <dbl>, Junction_Control <dbl>, `2nd_Road_Class` <dbl>,
## #   `2nd_Road_Number` <dbl>, `Pedestrian_Crossing-Human_Control` <dbl>, …
tail(dulieu, 5)
## # A tibble: 5 × 32
##   Accident_Index Location_Easting_OSGR Location_Northing_OSGR Longitude Latitude
##   <chr>                          <dbl>                  <dbl>     <dbl>    <dbl>
## 1 2015984139115                 312087                 570791     -3.38     55.0
## 2 2015984139715                 320671                 569791     -3.24     55.0
## 3 2015984140215                 311731                 586343     -3.39     55.2
## 4 2015984140515                 328273                 570137     -3.12     55.0
## 5 2015984141415                 314050                 579638     -3.35     55.1
## # ℹ 27 more variables: Police_Force <dbl>, Accident_Severity <dbl>,
## #   Number_of_Vehicles <dbl>, Number_of_Casualties <dbl>, Date <chr>,
## #   Day_of_Week <dbl>, Time <time>, `Local_Authority_(District)` <dbl>,
## #   `Local_Authority_(Highway)` <chr>, `1st_Road_Class` <dbl>,
## #   `1st_Road_Number` <dbl>, Road_Type <dbl>, Speed_limit <dbl>,
## #   Junction_Detail <dbl>, Junction_Control <dbl>, `2nd_Road_Class` <dbl>,
## #   `2nd_Road_Number` <dbl>, `Pedestrian_Crossing-Human_Control` <dbl>, …

Hai lệnh này lần lượt dùng để quan sát nhanh các dòng trên cùng và các dòng dưới cùng của bảng dữ liệu. Điều này cực kỳ có ích để kiểm tra dữ liệu đặc biệt, phát hiện dòng dữ liệu bất thường (ví dụ mã sự kiện rỗng, ký tự lạ ở cuối file), đánh giá tính nhất quán cách nhập, các trường hợp bị cắt phai hoặc xuất hiện dòng header thừa nếu file xuất từ nhiều nguồn. Thao tác này là cách quan sát dữ liệu sơ bộ, luôn nên thực hiện đầu tiên sau khi đọc bất kỳ tập dữ liệu nào.

1.1.6 Tổng hợp kiểu dữ liệu từng biến

paste(names(dulieu), sapply(dulieu, class), sep = ":", collapse = "    |    ")
## [1] "Accident_Index:character    |    Location_Easting_OSGR:numeric    |    Location_Northing_OSGR:numeric    |    Longitude:numeric    |    Latitude:numeric    |    Police_Force:numeric    |    Accident_Severity:numeric    |    Number_of_Vehicles:numeric    |    Number_of_Casualties:numeric    |    Date:character    |    Day_of_Week:numeric    |    Time:c(\"hms\", \"difftime\")    |    Local_Authority_(District):numeric    |    Local_Authority_(Highway):character    |    1st_Road_Class:numeric    |    1st_Road_Number:numeric    |    Road_Type:numeric    |    Speed_limit:numeric    |    Junction_Detail:numeric    |    Junction_Control:numeric    |    2nd_Road_Class:numeric    |    2nd_Road_Number:numeric    |    Pedestrian_Crossing-Human_Control:numeric    |    Pedestrian_Crossing-Physical_Facilities:numeric    |    Light_Conditions:numeric    |    Weather_Conditions:numeric    |    Road_Surface_Conditions:numeric    |    Special_Conditions_at_Site:numeric    |    Carriageway_Hazards:numeric    |    Urban_or_Rural_Area:numeric    |    Did_Police_Officer_Attend_Scene_of_Accident:numeric    |    LSOA_of_Accident_Location:character"

Khi dữ liệu có nhiều biến, thao tác này giúp ta xác định được biến nào là số thực, đoạn mã, ký tự, hoặc kiểu đặc biệt (như thời gian, kiểu hms/difftime). Đây là thông tin không thể thiếu để xác định cách áp dụng các hàm tính toán phù hợp (ví dụ: tính trung bình, cộng, trừ chỉ hợp với số, còn so sánh chuỗi chỉ dùng cho các biến ký tự). Nếu lẫn lộn kiểu dữ liệu, R sẽ báo lỗi khi thao tác. Vậy nên việc xác thực lại kiểu dữ liệu trước các thao tác chuyển đổi, mã hóa hay phân tổ là cực kỳ quan trọng, giúp quá trình làm sạch hoặc viết hàm thống kê/phân tích về sau hiệu quả và mượt mà hơn.

1.1.7 Đếm số lượng mã vụ tai nạn duy nhất

length(unique(dulieu$Accident_Index)) 
## [1] 1780653

Lệnh này trả về số sự kiện có mã “Accident_Index” là duy nhất. Giá trị này bằng với số dòng thực tế của bảng, chứng tỏ mỗi vụ việc là duy nhất, không bị trùng mã (không có dòng lặp). Đây là điều kiện quan trọng để yên tâm rằng dữ liệu không bị lỗi trùng lặp, tránh tính toán bị sai lệch hoặc bị gộp nhầm khi phân tích theo từng ca tai nạn thực tế.

1.1.8 Kiểm tra số dòng bị thiếu dữ liệu theo từng biến (missing value)

colSums(is.na(dulieu))[colSums(is.na(dulieu)) > 0]
##     Location_Easting_OSGR    Location_Northing_OSGR                 Longitude 
##                       138                       138                       138 
##                  Latitude                      Time LSOA_of_Accident_Location 
##                       138                       151                    129471

Nhiều biến có số giá trị thiếu là 0; một số biến có NA, ví dụ:

Location_Easting_OSGR, Location_Northing_OSGR, Longitude, Latitude: 138

Time: 151

LSOA_of_Accident_Location: 129471

Lệnh này chỉ ra rõ ràng ở mỗi cột có bao nhiêu giá trị bị để trống hoặc bị lỗi (NA). Với dữ liệu thực tế, chuyện có giá trị thiếu (chuẩn bị cho việc xử lý làm sạch) là rất phổ biến. Biết được số lượng, vị trí thiếu giúp ta đánh giá nhanh mức độ ảnh hưởng của thiếu dữ liệu tới phân tích. Ví dụ, các trường vị trí như tọa độ địa lý thường thiếu nhiều, group theo khu vực nếu thiếu sẽ gây sai; hoặc nếu biến thời gian (Time) bị thiếu, phân tích về giao thông giờ cao điểm sẽ không còn chính xác. Đối với biến có tỷ lệ fehl nhỏ, có thể cân nhắc loại bỏ dòng thiếu hoặc thay thế; còn nếu biến thiếu quá nhiều, có thể loại trừ biến khỏi phân tích tiếp theo.

1.1.9 Tính tần số xuất hiện cho một số biến mã hóa

table(dulieu$Accident_Severity)
## 
##       1       2       3 
##   22998  242080 1515575
table(dulieu$Day_of_Week) 
## 
##      1      2      3      4      5      6      7 
## 195326 253270 266706 268390 267494 291359 238108
table(dulieu$Road_Type)
## 
##       1       2       3       6       7       9 
##  119472   36755  262950 1332384   18647   10445
table(dulieu$Weather_Conditions)
## 
##      -1       1       2       3       4       5       6       7       8       9 
##     161 1423144  210489   12400   23313   25855    2309    9699   39165   34118

Việc tổng hợp tần suất xuất hiện của các giá trị trong các biến “mức độ nghiêm trọng”, “ngày trong tuần”, “loại đường” và “điều kiện thời tiết” cho phép người phân tích nhanh chóng vẽ nên “bức tranh tổng quát” của bộ dữ liệu. Nhìn vào kết quả phân tích, có thể rút ra các điểm quan trọng sau:

Về mức độ nghiêm trọng, phần lớn các vụ tai nạn trong 11 năm là mức độ nhẹ (hơn 1,5 triệu vụ: 22998 ; 242080 ; 1515575, chiếm tuyệt đại đa số), số vụ tử vong chiếm tỷ lệ rất nhỏ. Điều này phản ánh tính phổ biến của các tai nạn “không nghiêm trọng”, và chỉ ra rằng các chính sách an toàn giao thông cần song song chú ý đến giảm tỷ lệ tử vong lẫn hạn chế những thương tích nhẹ nhằm bảo vệ sức khỏe và giảm tải cho hệ thống y tế.

Số tai nạn xảy ra chủ yếu trên những loại đường phổ biến nhất (Road_Type = 6 là “Single carriageway”(1332384) – tức là đường một chiều không dải phân cách giữa), cho thấy các biện pháp kiểm soát an toàn, điều chỉnh tốc độ có thể tập trung đầu tư mạnh vào nhóm đường này sẽ sinh hiệu quả giảm thiểu thương vong rõ rệt nhất. Đối với các loại đường còn lại (vòng xuyến, hai chiều…), tỷ lệ tai nạn tuy thấp hơn nhưng thường tiềm ẩn nguy cơ nặng do đặc thù giao cắt hoặc tốc độ cao.

Điều kiện thời tiết là một yếu tố nền quan trọng: đa số tai nạn xảy ra khi thời tiết tốt(1:1423144), đơn giản vì lúc đó mật độ phương tiện tham gia giao thông cao nhất. Tuy nhiên, sự xuất hiện của hàng chục nghìn vụ trong điều kiện mưa, sương mù, gió mạnh hoặc có dầu tràn, nước ngập… là chỉ báo phải đặc biệt lưu ý nhóm tai nạn “hiếm gặp nhưng nguy hiểm” này. Việc tách riêng các nhóm điều kiện thời tiết xấu để phân tích sâu hơn sẽ giúp xác định vùng có nguy cơ, nguyên nhân tiềm tàng, từ đó đề xuất nhắc nhở hoặc cải thiện hệ thống cảnh báo thời tiết cho các cung đường trọng điểm.

1.2 Xử lí dữ liệu thô, mã hóa dữ liệu

1.2.1 Tạo bản sao để xử lý, giữ nguyên dữ liệu gốc

dulieu2 <- dulieu

Tạo một bản sao hoàn chỉnh của toàn bộ dữ liệu gốc bằng cách gán dữ liệu từ biến dulieu sang biến dulieu2. Trong lập trình R, thao tác này giúp xây dựng một “bản nháp dữ liệu” để thực hiện mọi hoạt động tiền xử lý, phân tổ, kiểm tra chất lượng, mã hóa hoặc phân tích sâu mà không lo ảnh hưởng đến tập dữ liệu đầu vào ban đầu. Việc làm việc trên bản sao dữ liệu là nguyên tắc chuẩn trong xử lý dữ liệu lớn, nhất là khi phải thực hiện hàng loạt thao tác xóa dòng, loại trường, chuyển đổi kiểu biến hoặc thử nghiệm phân nhóm.

1.2.2 Kiểm tra và thống kê số lượng dòng bị thiếu ở biến Speed_limit

sum(is.na(dulieu2$Speed_limit))
## [1] 0

Lệnh này sử dụng kết hợp hai hàm cực kỳ phổ biến trong xử lý dữ liệu với R: is.na() và sum(). Trước tiên, is.na(dulieu2$Speed_limit) sẽ kiểm tra tất cả các giá trị trong cột Speed_limit của bảng dữ liệu dulieu2, trả ra một vector logic (mỗi vị trí là TRUE nếu là NA, FALSE nếu không thiếu). Khi truyền vào hàm sum(), R sẽ tự động coi TRUE là 1 và FALSE là 0, nên kết quả cuối cùng là tổng số dòng đang bị thiếu dữ liệu ở trường này.

Trong ngữ cảnh phân tích dữ liệu lớn, việc kiểm tra số lượng NA ở Speed_limit cực kỳ quan trọng, vì những dòng này sẽ gây sai lệch khi phân tổ, mã hóa, hoặc tính toán thống kê. Kết quả bằng 0 khẳng định toàn bộ cột Speed_limit đã đầy đủ—không có dòng nào thiếu thông tin tốc độ. Đây là điều kiện lý tưởng giúp mọi phép chuyển đổi, phân nhóm, mô hình hóa với biến tốc độ đều diễn ra chính xác tuyệt đối, không cần các bước xử lý ngoại lệ, thay thế/loại bỏ dòng NA trong khối lệnh tiếp theo..

1.2.3 Loại bỏ các dòng dữ liệu bị thiếu ở Speed_limit

dulieu2 <- dulieu2[!is.na(dulieu2$Speed_limit), ] 

Lệnh này thực hiện thao tác lọc để loại bỏ hoàn toàn các dòng dữ liệu bị thiếu giá trị ở trường Speed_limit. Hàm is.na(dulieu2$Speed_limit) trả về một mảng giá trị logic (TRUE với dòng bị thiếu tốc độ, FALSE với dòng đầy đủ), và toán tử phủ định ! đảo lại để chọn chỉ những dòng có thông tin hợp lệ (không phải NA).

Khi áp dụng lên khung dữ liệu dulieu2 trong ngoặc vuông [ ], R sẽ giữ nguyên tất cả bản ghi đúng (đầy đủ giá trị tốc độ), đồng thời loại toàn bộ dòng rỗng hoặc sai sót do không nhập dữ liệu hoặc lỗi nhập liệu ban đầu. Đây là bước làm sạch dữ liệu rất quan trọng, giúp bảng dữ liệu đạt chuẩn chất lượng về mặt thông tin đầu vào – mọi phép phân tổ, phân nhóm, mã hóa hoặc phân tích thống kê sau này đều chính xác và không gặp lỗi do dòng thiếu số liệu.

Kết quả là dữ liệu sau khi làm sạch chỉ còn lại những dòng có giá trị tốc độ hợp lệ, ready để phân tổ hoặc phân loại, không còn dòng rác, đảm bảo tính đại diện và toàn vẹn cho bộ dữ liệu.

1.2.4 Loại bỏ trường hợp bị trùng lặp mã vụ việc nếu có

dulieu2 <- dulieu2[!duplicated(dulieu2$Accident_Index), ] 

Lệnh này có vai trò loại bỏ các dòng dữ liệu bị trùng lặp ở cột Accident_Index—nghĩa là nếu có nhiều dòng mang cùng một giá trị mã định danh vụ việc, chỉ giữ lại dòng đầu tiên và loại bỏ tất cả các dòng còn lại bị trùng. Trong kỹ thuật R, hàm duplicated() kiểm tra xem giá trị của từng dòng đã từng xuất hiện ở những dòng trước đó chưa; nếu đã xuất hiện thì trả về TRUE, chưa thì FALSE. Kết hợp với phủ định !, R chỉ giữ lại các dòng đầu tiên với mỗi mã.

Bước này đảm bảo độ chính xác tuyệt đối về cấu trúc dữ liệu sự kiện, chuẩn bị nền cho các phép phân nhóm, thống kê, vẽ đồ thị và phân tích mô hình hóa phía sau. Nếu không thực hiện loại trùng, mọi phép so sánh sẽ mất tính khách quan và không phản ánh đúng thực trạng dữ liệu nguồn.

1.2.5 Chuẩn hóa kiểu dữ liệu cho biến Speed_limit về dạng số nguyên

dulieu2$Speed_limit <- as.integer(dulieu2$Speed_limit)

Lệnh này chuyển dữ liệu trong cột Speed_limit thành dạng số nguyên (integer) bằng cách sử dụng hàm as.integer(). Trong thực tiễn xử lý dữ liệu lớn, trường tốc độ trong file nguồn thường có thể lưu dưới dạng số thực (double) hoặc thậm chí là chuỗi nếu nhập từ file CSV. Nếu không chuẩn hóa, các phép chia nhóm (bằng cut), kiểm tra logic (ví dụ, so sánh >= 50), hoặc các phép thống kê mô tả sẽ dễ bị lỗi do R phân biệt rất rõ khái niệm kiểu dữ liệu.

Chuyển sang integer còn giúp tiết kiệm bộ nhớ, tăng tốc độ xử lý và giảm nguy cơ phát sinh lỗi so sánh, đồng thời đảm bảo khi phân nhóm tốc độ sẽ không bị “lệch nhóm” vì tồn tại các giá trị lẻ không ý nghĩa (ví dụ: 29.7mph sẽ thành 29). Sau bước này, mọi thao tác phân tổ hoặc kiểm tra điều kiện tốc độ đều mạch lạc, nhất quán và phù hợp tiêu chuẩn phân tích thống kê khoa học dữ liệu.

1.2.6 Mã hóa biến Speed_limit thành phân loại: thấp/vừa/cao

dulieu2$Speed_cat <- cut(
  dulieu2$Speed_limit,  
  breaks = c(0, 30, 40, 60, Inf), 
  labels = c("Thấp", "Vừa", "Cao", "Rất cao"),
  right = FALSE)                                 

Lệnh này sử dụng hàm cut() để biến cột số liên tụcSpeed_limit thành một biến phân loại (factor) gồm 4 nhóm tốc độ rõ ràng — Thấp, Vừa, Cao, Rất cao. Việc mã hóa này rất quan trọng khi muốn phân tích, so sánh mức độ nguy cơ giữa các nhóm tốc độ hoặc trình bày kết quả cho người đọc không quen với dạng số kỹ thuật.

Đối số breaks = c(0, 30, 40, 60, Inf) chính là thiết lập các mức ngắt phân lớp: <30mph vào nhóm “Thấp”, từ 30 đến <40mph là “Vừa”, 40 đến <60mph là “Cao”, còn ≥60mph là “Rất cao”. Chọn các mốc này dựa trên thực tiễn luật giao thông và ngữ cảnh phân tích ở Anh.

Đối số labels = c("Thấp", "Vừa", "Cao", "Rất cao") giúp kết quả phân loại được trình bày bằng tiếng Việt dễ nhớ thay cho các mức số thô, từ đó bảng tần suất, biểu đồ hay phân tổ sẽ rõ nghĩa hơn cho người xem.

Đối số right = FALSE đảm bảo rằng giá trị đúng mốc dưới sẽ vào nhóm đó (ví dụ: 30mph nằm trong nhóm “Vừa” chứ không phải “Thấp”).

Sau lệnh này, mỗi dòng dữ liệu sẽ được gán vào một trong bốn nhóm tốc độ, tạo điều kiện thuận lợi cho mọi phép phân nhóm, thống kê, trực quan hóa hoặc phân tích chuyên sâu về rủi ro giao thông từng cấp tốc độ.

Việc chuyển thành categorical/factor qua cut là thao tác tiền đề cho bước so sánh tỷ lệ, xác suất, vẽ đồ thị group, hoặc phân tổ đa chiều trong nghiên cứu thực nghiệm và thống kê khoa học.

1.2.7 Kiểm tra phân bố tần suất từng mức tốc độ giới hạn sau khi mã hóa

table(dulieu2$Speed_cat)
## 
##    Thấp     Vừa     Cao Rất cao 
##   22053 1141609  204797  412194

Lệnh này sử dụng hàm table() để đếm tổng số dòng dữ liệu thuộc về mỗi nhóm tốc độ được mã hóa ở biến Speed_cat. Khi gọi table lên một biến dạng phân loại (factor), R sẽ tự động duyệt qua toàn bộ các giá trị trong cột đó, gom nhóm và trả về một bảng tần suất cho từng nhãn (nhóm) đã gắn trước đó.

Trong ngữ cảnh dữ liệu tai nạn giao thông, ý nghĩa của bảng này cực kỳ quan trọng:

Kết quả bảng tần suất cho biết rõ số lượng vụ tai nạn phát sinh ở từng cấp tốc độ, từ đó giúp nhận biết nhóm tốc độ nào phổ biến nhất, nhóm nào tiềm ẩn nguy cơ rủi ro cao, phục vụ cho phân tích tỷ lệ, đánh giá mức độ ảnh hưởng của các biến khác (loại đường, ánh sáng…) hoặc xây dựng chính sách giao thông.

Nhìn vào kết quả cụ thể, có thể thấy tốc độ “Vừa” (từ 30 đến dưới 40mph) chiếm đa số tuyệt đối các vụ tai nạn(1141609), tiếp theo là nhóm “Rất cao”, “Cao”, và cuối cùng nhóm “Thấp” là ít nhất. Đây là bằng chứng thực tế phản ánh mạng lưới tốc độ đường bộ của Anh quốc, cũng như cách phân bố hành vi tham gia giao thông.

Kết quả này còn là dữ liệu nền để các bước phân nhóm phức tạp hơn (phân tổ chéo, lọc nhóm theo loại đường, ánh sáng, hoặc điều kiện môi trường) được tiến hành hiệu quả, logic và minh bạch trên một bảng đã phân loại chuẩn khoa học. Việc liệt kê rõ từng nhóm tốc độ kèm số lượng vụ giúp làm nổi bật củng cố các kết luận về xu hướng, rủi ro và các nhóm cần kiểm soát, can thiệp hoặc nghiên cứu tiếp theo.

1.2.8 Chuẩn hóa biến Road_Type về dạng factor có nhãn

dulieu2$Road_Type <- factor(
  dulieu2$Road_Type,
  levels = c(1, 2, 3, 6, 7, 9), 
  labels = c("Vòng xuyến", "Một chiều", "Hai chiều", "Một chiều không dải", "Đường nhánh", "Khác")
)             

Đối số levels = c(1, 2, 3, 6, 7, 9) quy định cụ thể những giá trị mã loại đường thực tế sẽ dùng cho phân tích/báo cáo, loại bỏ các trường hợp mã số lạ hoặc nhập lỗi.

Đối số labels sẽ gán cho từng mã số một nhãn tiếng Việt đúng nghĩa (ví dụ: mã 1 là “Vòng xuyến”, 3 là “Hai chiều”…), tránh nhầm lẫn đối với người đọc, giúp phân tích và trình bày kết quả trở nên trực quan, dễ hiểu hơn.

Sau khi chuyển về dạng factor, mọi phép nhóm, bảng tần suất, trực quan hóa (biểu đồ cột, bảng chéo, v.v) với biến Road_Type sẽ trả về kết quả bằng tên nhãn, không còn dạng mã số khô khan. Điều này rất cần thiết để đảm bảo tính minh bạch, tạo thuận lợi tối đa về báo cáo cũng như các thao tác kiểm tra, lọc, so sánh khi phân tích nhóm đường trong dữ liệu thực tế. Nhìn vào nhãn sẽ nhận biết ngay đặc điểm cấu trúc hạ tầng, giúp phát hiện xu hướng hoặc đặc thù về nguy cơ, phân tích sâu mối liên hệ tốc độ - loại đường một cách khoa học, logic và dễ kiểm chứng.

1.2.9 Kiểm tra phân phối loại đường kết hợp với tốc độ

table(dulieu2$Speed_cat, dulieu2$Road_Type)
##          
##           Vòng xuyến Một chiều Hai chiều Một chiều không dải Đường nhánh   Khác
##   Thấp           634      1871       485               18354          88    621
##   Vừa          73686     33377     74192              947606        5064   7684
##   Cao          23476       708     62279              114714        3022    598
##   Rất cao      21676       799    125994              251710       10473   1542

Mỗi hàng của bảng tương ứng với một nhóm tốc độ (Thấp, Vừa, Cao, Rất cao); mỗi cột là một loại đường (“Vòng xuyến”, “Một chiều”, “Hai chiều”, “Một chiều không dải”, “Đường nhánh”, “Khác”).

Giá trị tại ô giao nhau thể hiện tổng số vụ tai nạn xuất hiện đúng tổ hợp tốc độ + loại đường đó trong toàn bộ dữ liệu, ví dụ “Rất cao - Hai chiều” có tới 125.994 vụ.

Giá trị phân tích dữ liệu này:

Nhìn tổng thể, số vụ tai nạn ở tốc độ “Vừa” tập trung rất lớn tại “Một chiều không dải” và “Hai chiều”, phản ánh thói quen di chuyển của người dân trên các loại đường phổ biến tại Anh. Với nhóm “Rất cao”, số lượng lớn nhất lại thuộc về “Một chiều không dải” và “Hai chiều”, chứng tỏ các loại đường này vừa có luồng giao thông lớn, vừa tiềm ẩn nguy cơ tốc độ cao.

Loại đường “Vòng xuyến” và “Đường nhánh” xuất hiện ít ở tất cả các nhóm tốc độ, cho thấy hai loại này mặc định kiểm soát giao thông an toàn (do đặc thù thiết kế).

Bảng này không chỉ để xem tần suất mà còn là cơ sở cho mọi phân tích tiếp theo về mức độ nguy cơ, tần suất tai nạn từng môi trường, điều kiện; đồng thời phục vụ xây dựng chính sách ưu tiên quản lý, sửa đổi thiết kế hoặc phân bổ tuyến kiểm tra ở từng loại đường, từng nhóm tốc độ một cách khoa học, có cơ sở thực chứng rõ ràng.

Kết quả này giúp chỉ rõ nhóm tốc độ/cấu trúc hạ tầng nào nguy hiểm nhất và là tiền đề cho phân tích sâu về nguyên nhân, giải pháp hạn chế nguy cơ tai nạn giao thông đường bộ trên phạm vi quốc gia.

1.2.10 Phân tổ số vụ tai nạn theo nhóm tốc độ và điều kiện ánh sáng

library(dplyr)
tonghop1 <- dulieu2 %>%
  group_by(Speed_cat, Light_Conditions) %>% 
  summarise(So_vu = n()) %>%
  arrange(desc(So_vu))
tonghop1
## # A tibble: 20 × 3
## # Groups:   Speed_cat [4]
##    Speed_cat Light_Conditions  So_vu
##    <fct>                <dbl>  <int>
##  1 Vừa                      1 840634
##  2 Rất cao                  1 297990
##  3 Vừa                      4 273273
##  4 Cao                      1 148999
##  5 Rất cao                  6  77815
##  6 Cao                      4  40620
##  7 Rất cao                  4  31140
##  8 Thấp                     1  16851
##  9 Vừa                      7  13091
## 10 Cao                      6  11623
## 11 Vừa                      6   9351
## 12 Vừa                      5   5260
## 13 Thấp                     4   4695
## 14 Rất cao                  7   3547
## 15 Cao                      7   2445
## 16 Rất cao                  5   1702
## 17 Cao                      5   1110
## 18 Thấp                     7    236
## 19 Thấp                     6    158
## 20 Thấp                     5    113

Khối lệnh này tổng hợp dữ liệu theo hai chiều: nhóm tốc độ (Speed_cat) và điều kiện ánh sáng (Light_Conditions) tại thời điểm xảy ra tai nạn. Việc sử dụng thư viện dplyr cùng các hàm group_by(), summarise(), và arrange() cho phép chia nhỏ toàn bộ kho dữ liệu thành các nhóm con (mỗi tổ hợp tốc độ – ánh sáng), rồi đếm chính xác số vụ từng nhóm.

Hàm group_by(Speed_cat, Light_Conditions)chia dữ liệu thành từng tổ hợp; ví dụ “Cao - Ban ngày”, “Thấp - Đêm có đèn”, “Rất cao - Đêm không đèn” v.v.

Hàm summarise(So_vu = n())sẽ đếm số vụ tai nạn trong mỗi tổ hợp, tạo ra một bảng tổng hợp số lượng cụ thể.

Hàm arrange(desc(So_vu))sắp xếp bảng mới theo số vụ tai nạn từ lớn tới nhỏ, giúp nhận diện các điều kiện, nhóm tốc độ nào có nguy cơ xảy ra nhiều nhất.

Bảng kết quả hiển thị tổng số vụ tai nạn cho từng tổ hợp nhóm tốc độ (Speed_cat) cùng điều kiện ánh sáng (Light_Conditions) tại hiện trường.

Cột Speed_cat phân nhóm tốc độ (“Thấp”, “Vừa”, “Cao”, “Rất cao”) dựa trên dữ liệu giới hạn tốc độ được mã hóa ban đầu.

Cột Light_Conditions (dưới dạng số: 1, 4, 5, 6, 7…) tương ứng với các điều kiện ánh sáng đặc thù(đã giải thích ở phần giới thiệu bộ dữ liệu).

Cột So_vu cho biết số lượng vụ từng tổ hợp vừa nêu.

Nhóm tốc độ “Vừa” (Speed_cat = “Vừa”) trong điều kiện ban ngày (Light_Conditions = 1) có số lượng vụ lớn vượt trội:840.634 vụ — đây là tổ hợp nguy cơ phổ biến, chỉ ra rằng giao thông trong nhóm tốc độ vừa vào ban ngày phát sinh nhiều sự kiện nhất trong toàn bộ dữ liệu.

Nhóm “Rất cao - Ban ngày” cũng đứng thứ hai với 297.990 vụ, cho thấy tốc độ lớn không đồng nghĩa với điều kiện ánh sáng nguy hiểm nhất nhưng vẫn phát sinh nhiều sự kiện do đặc thù nhóm đường lớn, khu vực đô thị, hoặc hành vi lái xe chủ quan ban ngày.

Những tổ hợp có số lượng ít như “Thấp - Đêm không đèn” (chỉ113 vụ) hoặc “Rất cao - Đêm chưa rõ ánh sáng” (3.547 vụ) cho thấy các điều kiện kết hợp này hiếm xảy ra hoặc môi trường/đường loại đó kiểm soát giao thông tốt.

Việc sắp xếp bảng theo số vụ giảm dần (arrange desc) giúp nhận diện nhóm nguy cơ cao nhất, phục vụ trực quan hóa (vẽ đồ thị nhóm/tổ hợp) và cung cấp căn cứ cho các dự báo, chiến lược kiểm soát hoặc truyền thông cảnh báo giao thông định hướng.

1.2.11 Tính tỷ lệ vụ trong từng nhóm tốc độ

tonghop1 <- tonghop1 %>%
  group_by(Speed_cat) %>%         
  mutate(Ty_le = round(So_vu / sum(So_vu) * 100, 2))
tonghop1
## # A tibble: 20 × 4
## # Groups:   Speed_cat [4]
##    Speed_cat Light_Conditions  So_vu Ty_le
##    <fct>                <dbl>  <int> <dbl>
##  1 Vừa                      1 840634 73.6 
##  2 Rất cao                  1 297990 72.3 
##  3 Vừa                      4 273273 23.9 
##  4 Cao                      1 148999 72.8 
##  5 Rất cao                  6  77815 18.9 
##  6 Cao                      4  40620 19.8 
##  7 Rất cao                  4  31140  7.55
##  8 Thấp                     1  16851 76.4 
##  9 Vừa                      7  13091  1.15
## 10 Cao                      6  11623  5.68
## 11 Vừa                      6   9351  0.82
## 12 Vừa                      5   5260  0.46
## 13 Thấp                     4   4695 21.3 
## 14 Rất cao                  7   3547  0.86
## 15 Cao                      7   2445  1.19
## 16 Rất cao                  5   1702  0.41
## 17 Cao                      5   1110  0.54
## 18 Thấp                     7    236  1.07
## 19 Thấp                     6    158  0.72
## 20 Thấp                     5    113  0.51

Trong nhóm “Vừa”, tai nạn xảy ra ban ngày (Light_Conditions = 1) chiếm 73,6% tổng số vụ của nhóm này, tức là phần lớn tai nạn ở nhóm tốc độ vừa diễn ra vào ban ngày; chỉ có23,9% vào đêm có đèn (Light_Conditions = 4), còn các điều kiện khác hoặc cực kỳ thấp, chỉ trên dưới 1%.

Ở nhóm “Rất cao”, ban ngày chiếm 72,3%, đêm không rõ ánh sáng (6) chiếm 18,9%, còn lại các điều kiện khác chỉ 7%, 0.8%… Điều này cho thấy phần lớn sự cố vẫn xảy ra ban ngày kể cả với tốc độ cực cao, nguy cơ về đêm chịu ảnh hưởng mạnh từ yếu tố chiếu sáng hoặc tầm nhìn.

Các tổ hợp tốc độ/ánh sáng còn lại đều có tỷ lệ rất nhỏ, củng cố nhận định rằng ban ngày là thời điểm tập trung phần lớn rủi ro do lượng phương tiện đông, hoạt động liên tục, mặc dù vào ban đêm hoặc đêm không đủ sáng vẫn tiềm ẩn nguy cơ đặc biệt với nhóm tốc độ rất cao.

Qua đó có thể nhận diện kiểu phân bố nguy cơ dựa trên tỷ lệ chứ không chỉ dựa trên số lượng tuyệt đối, nhờ vậy các đề xuất kiểm soát, cảnh báo hay tối ưu nguồn lực (giám sát vào thời điểm nào, nhóm lái xe nào) đều được căn cứ trên thực tế, tránh bỏ sót các điểm nóng nhỏ nhưng nguy hiểm bất thường trong từng nhóm. Bảng tỷ lệ cũng đặc biệt hữu ích khi trực quan hóa dữ liệu, cho phép trình bày bằng biểu đồ phần trăm, biểu đồ chồng cột hoặc sơ đồ tròn để dễ nhận diện xu thế và điểm nhấn về nguy cơ tai nạn giao thông.

1.2.12 Tạo biến xác định nhóm tốc độ cao

dulieu2$is_high_speed <- dulieu2$Speed_limit >= 50
table(dulieu2$is_high_speed)
## 
##   FALSE    TRUE 
## 1309954  470699

Phép phân loại này cho thấy phần lớn các vụ tai nạn giao thông tại Anh xảy ra trên tuyến đường có tốc độ nhỏ hơn 50 mph. Nhóm tốc độ cao có quy mô nhỏ hơn, nhưng do đặc thù về vận tốc lớn nên các vụ này thường nghiêm trọng hơn, nguy cơ rủi ro cao hơn.

Việc chia nhóm như trên là đầu vào cho các phân tích sâu hơn: xác suất rủi ro, kiểm soát hành vi vi phạm, xác định các biện pháp điều chỉnh chính sách giao thông, hoặc vẽ trực quan hóa nguy cơ từng nhóm mà không cần xây dựng lại biến phân loại phức tạp nhiều lần. Kết quả trực tiếp còn giúp điều hướng nguồn lực cảnh sát/giám sát vào nhóm nguy cơ cao hoặc phân tích điểm nóng theo vùng – thời gian..

1.2.13 Thống kê tỷ lệ vụ tốc độ cao/thấp

prop.table(table(dulieu2$is_high_speed))
## 
##     FALSE      TRUE 
## 0.7356593 0.2643407

Lệnh này cho biết tỷ lệ phần trăm các vụ tai nạn xảy ra ở nhóm đường tốc độ cao (Speed_limit ≥ 50mph, TRUE) so với tổng số vụ, và ngược lại là các vụ ở nhóm tốc độ thấp hơn (FALSE). Hàm table() tạo bảng tần suất số vụ TRUE/FALSE trong cộtis_high_speed, sau đó hàmprop.table() chuyển đổi số lượng tuyệt đối thành xác suất (tỷ lệ) trên toàn bộ dữ liệu, giúp đánh giá mức độ phổ biến của mỗi nhóm.

Kết quả trên thể hiện:

Khoảng26,4%vụ tai nạn xảy ra trên các tuyến đường có giới hạn tốc độ từ 50mph trở lên,

73,6%vụ còn lại thuộc về nhóm tốc độ thấp/vừa.

Thông tin này hỗ trợ phân tích và báo cáo xác định rõ mức độ rủi ro và quy mô nhóm cần tập trung các biện pháp kiểm soát về tốc độ, làm căn cứ khoa học cho lập chính sách hoặc ưu tiên quản lý giao thông đường bộ thực tiễn.

1.2.14 Lấy mẫu ngẫu nhiên từ nhóm tốc độ cao

dulieu2_highspeed <- dulieu2[dulieu2$is_high_speed == TRUE, ][sample(sum(dulieu2$is_high_speed), 1000), ] 

Câu lệnh này tạo ra một tập mẫu mới gồm 1000 vụ tai nạn được chọn ngẫu nhiên từ nhóm các sự kiện có giới hạn tốc độ từ 50mph trở lên (nhóm tốc độ cao). Phần lọc dulieu2[dulieu2$is_high_speed == TRUE, ]trước tiên chọn toàn bộ các bản ghi có tốc độ cao; tiếp đó, sample(sum(dulieu2$is_high_speed), 1000) giúp rút ra một lượng nhỏ để xử lý, kiểm chứng, mô phỏng hoặc vẽ trực quan hóa mà không bị quá tải như sử dụng toàn bộ bản ghi gốc.

Kỹ thuật lấy mẫu ngẫu nhiên này đặc biệt cần thiết khi tập dữ liệu thực tế quá lớn, không thuận tiện để phân tích chi tiết, thử nghiệm thuật toán hoặc minh hoạ kết quả bằng biểu đồ báo cáo. Tập con này đại diện cho đặc tính của toàn bộ biến nhóm tốc độ cao, dùng làm minh chứng cho kiểm tra chất lượng, kiểm nghiệm giả thuyết hoặc nội suy kết quả vào các kịch bản chuyên sâu về an toàn giao thông thực tế.

1.2.15 Chuẩn hóa trường ngày/tháng cho phân tích thời gian

library(lubridate)
dulieu2$Date <- dmy(dulieu2$Date)
head(dulieu2$Date)    
## [1] "2005-01-04" "2005-01-05" "2005-01-06" "2005-01-07" "2005-01-10" "2005-01-11"

Hai lệnh trên chuẩn hóa trường Date sang kiểu ngày (Date) chuẩn của R bằng cách dùng hàm dmy() từ package lubridate. Dữ liệu thực tế khi nhập thường ở dạng chuỗi ký tự kiểu “dd/mm/yyyy”, không phải kiểu ngày thật nên không thể thao tác tách tháng, năm hoặc phân nhóm thời gian đúng chuẩn. Sau khi chạy lệnh này, mọi dòng dữ liệu đều hóa thành kiểu ngày chuẩn hóa — cho phép dùng các hàm R nhưmonth(),year(), so sánh, cắt chuỗi thời gian, hoặc vẽ các biểu đồ biến động theo tháng/năm rất thuận tiện và chính xác kỹ thuật.

Việc này là điều kiện tiên quyết khi phân tích dữ liệu biến động theo thời gian, xây dựng chuỗi thời gian hoặc phân tổ số liệu theo các kỳ, mùa, hoặc phục vụ cho việc xử lý dữ liệu thời gian lớn, nâng cấp chất lượng phân tích báo cáo lên chuẩn chuyên nghiệp.

1.2.16 Phân tổ số vụ theo năm và nhóm tốc độ

dulieu2 %>%
  mutate(Month = month(Date), Year = year(Date)) %>% 
  group_by(Year, Speed_cat) %>% 
  summarise(Count = n()) 
## # A tibble: 44 × 3
## # Groups:   Year [11]
##     Year Speed_cat  Count
##    <dbl> <fct>      <int>
##  1  2005 Thấp         977
##  2  2005 Vừa       125689
##  3  2005 Cao        20849
##  4  2005 Rất cao    51220
##  5  2006 Thấp         996
##  6  2006 Vừa       118610
##  7  2006 Cao        20719
##  8  2006 Rất cao    48836
##  9  2007 Thấp        1154
## 10  2007 Vừa       114292
## # ℹ 34 more rows

Khối lệnh trên tạo bảng tổng hợp số vụ tai nạn đã được phân tổ theo từng năm và từng cấp độ phân loại tốc độ (Speed_cat).

Hàm mutate() trước hết tạo hai cột mới từ trường Date đã chuẩn hóa, tách ra năm (Year) và tháng (Month) từ ngày xảy ra tai nạn.

Sau đó, hàm group_by(Year, Speed_cat) gom tất cả các bản ghi lại theo từng năm rồi tiếp tục chia nhỏ theo từng nhóm tốc độ.

Cuối cùng, summarise(Count = n()) cho biết chính xác mỗi năm có bao nhiêu vụ tai nạn thuộc từng nhóm tốc độ cụ thể.

Kết quả hiển thị dạng bảng như trên hình đã gửi:

Dễ dàng nhận biết số vụ thay đổi theo từng năm ở các cấp tốc độ “Thấp”, “Vừa”, “Cao”, “Rất cao”, ví dụ năm 2005 có 977 vụ tốc độ thấp, 125689 vụ tốc độ vừa ,20849 vụ tốc độ cao và51220 vụ tốc độ rất cao.

Qua đó, có thể so sánh biến động, xu hướng tăng giảm giữa các năm, đánh giá hiệu quả chính sách, thời điểm hay loại đường “nóng”, truy vết nhóm nguy cơ có dấu hiệu bất thường liên quan đến tốc độ.

Bảng này là nền tảng cho vẽ biểu đồ đường, đồ thị cột phân tổ theo năm, giúp trực quan hóa biến động và hỗ trợ xây dựng các nhận định khoa học, đề xuất giải pháp phòng chống tai nạn giao thông một cách đầy đủ và có cơ sở thực tiễn.

1.3 Thực hiện các thống kê cơ bản

1.3.1 Thống kê mô tả biến tốc độ

mean(dulieu2$Speed_limit) 
## [1] 39.02833
range(dulieu2$Speed_limit)
## [1]  0 70
var(dulieu2$Speed_limit)  
## [1] 200.4652
median(dulieu2$Speed_limit) 
## [1] 30

Lệnh mean(dulieu2$Speed_limit) trả kết quả [1] 39.02833, nghĩa là tốc độ trung bình (mean) của toàn bộ sự kiện tai nạn giao thông trong dữ liệu là khoảng39 mph. Giá trị này cho thấy phần lớn các vụ tai nạn tập trung ở các tuyến có giới hạn tốc độ vừa phải, phản ánh thực trạng giao thông Anh chủ yếu xảy ra trên mạng lưới đường bộ đô thị và vùng ngoại ô không quá nhanh hoặc quá chậm. Dùng để đối chiếu với chính sách giới hạn, xác định điểm cần tập trung kiểm soát hay nghiên cứu sâu.

Lệnh range(dulieu2$Speed_limit) trả kết quả [1] 0 70, thể hiện trong dữ liệu có vụ xảy ra tại tốc độ nhỏ nhất là0 mph (có thể là nhập liệu lỗi, xe dừng/xếp hàng/lỗi địa hình), và lớn nhất là 70 mph (đường cao tốc hoặc ngoại ô). Việc xác định phạm vi này cho phép phát hiện giá trị cực trị, kiểm tra lại dữ liệu biến lỗi, đồng thời định hướng các phân tích rủi ro cho vùng vận tốc thấp/rất cao, củng cố phân chia nhóm tốc độ cho báo cáo/chính sách.

Lệnh var(dulieu2$Speed_limit) trả về [1] 200.4652, nghĩa là độ phân tán (phương sai) của biến tốc độ trên toàn tập dữ liệu. Số phương sai càng lớn thì mức độ chênh lệch, đa dạng về hành vi tốc độ càng cao, báo hiệu các nguy cơ tiềm ẩn hoặc các nhóm đặc biệt cần kiểm soát kỹ (ví dụ, nhiều loại đường và hành vi lái xe không đồng đều).

Lệnh median(dulieu2$Speed_limit) trả giá trị [1] 30, tức là tốc độ điển hình nhất, nằm ở giữa toàn bộ các giá trị khi sắp xếp dãy tốc độ là30 mph. Trong thực tế Anh, nhiều tuyến đường chính giới hạn ở30 mph. Kết quả này cho thấy phần lớn vụ tai nạn tập trung vào nhóm đường phổ biến nhất, là căn cứ để xây dựng hoặc rà soát chính sách kiểm soát ở tốc độ trung bình.

Các lệnh này vừa giúp kiểm tra chất lượng đầu vào, vừa xác định đặc điểm hành vi giao thông thực tế, cung cấp dữ liệu đại diện cho việc phân tích xu hướng, tối ưu hóa nguồn lực kiểm soát và đề xuất giải pháp quản lý an toàn giao thông dựa trên bằng chứng định lượng.

1.3.2 Độ lệch chuẩn số lượng vụ tai nạn mỗi năm

dulieu2$Year <- year(dulieu2$Date)
dulieu_theonam <- dulieu2 %>% group_by(Year) %>% summarise(Count = n())   
sd(dulieu_theonam$Count)
## [1] 20683.81

Dòng đầu thực hiện chuẩn hóa, tạo biến số Year từ Date (đã định dạng chuẩn kiểu ngày tháng), giúp dễ dàng phân nhóm dữ liệu theo từng năm mà không thao tác chuỗi ký tự thủ công. Tiếp theo sử dụng quy trình dplyr: group_by(Year) sẽ chia nhỏ dữ liệu thành từng nhóm theo giá trị năm, và summarise(Count = n()) sẽ tạo một bảng mới, trong đó ở mỗi dòng là một năm cùng số vụ tai nạn xảy ra trong năm đó. Cuối cùng dùng hàm sd để tính toán độ lệch chuẩn của chuỗi số vụ tai nạn từng năm (Count) vừa tổng hợp được. Độ lệch chuẩn này phản ánh mức dao động của số vụ giữa các năm – giá trị càng lớn chứng tỏ số vụ tai nạn các năm thay đổi mạnh, còn giá trị nhỏ chứng tỏ mỗi năm số vụ gần như tương tự nhau, ít biến động qua thời gian.

Kết quả trả về ở đây là20683.81, nghĩa là số vụ tai nạn mỗi năm có mức độ dao động khá lớn, gợi ý cần tiếp tục phân tích để xác định các năm bất thường hoặc tác động của chính sách, biến động xã hội cụ thể từng giai đoạn. Đây là một thông số quan trọng để quản lý vĩ mô, đánh giá xu hướng hoặc đặt nền cho các phân tích sâu về nguyên nhân – hệ quả thời gian.

1.3.3 Hệ số biến thiên tốc độ

sd(dulieu2$Speed_limit) / mean(dulieu2$Speed_limit)
## [1] 0.3627768

Dòng lệnh này tính hệ số biến thiên tốc độ bằng cách chia độ lệch chuẩn cho giá trị trung bình của biến Speed_limit. Kết quả [1] 0.3627768 cho biết mức độ phân tán tương đối của tốc độ trên toàn bộ dữ liệu: hệ số này càng nhỏ thì tốc độ càng đồng đều, càng lớn thể hiện tốc độ thay đổi nhiều giữa các tuyến. Trong thực tiễn, hệ số này giúp so sánh mức độ “ổn định” hành vi tốc độ giữa các vùng, nhóm đường hoặc đánh giá mức rủi ro vận hành của hệ thống giao thông, hỗ trợ quản trị và ra quyết định về an toàn, quy hoạch điều tiết tốc độ so với trung bình của toàn mạng lưới. Nếu đem so với dữ liệu khác (ví dụ, các quốc gia/vùng khác), hệ số biến thiên là chỉ báo rất quan trọng về tính phân tán động học của phương tiện giao thông.

1.3.4 Tổng số vụ tai nạn theo từng nhóm tốc độ cao/thấp

table(dulieu2$is_high_speed)
## 
##   FALSE    TRUE 
## 1309954  470699

Dòng lệnh này dùng hàm table để tổng hợp số lượng các vụ tai nạn được phân thành hai nhóm theo biến logic is_high_speed:

FALSE (không phải tốc độ cao – tức < 50 mph): 1,309,954 vụ.

TRUE (tốc độ cao – tức ≥ 50 mph): 470,699 vụ.

Kết quả giúp xác định quy mô từng nhóm nguy cơ trong cơ sở dữ liệu, từ đó làm nền cho các phân tích sâu hơn về phần trăm, xác suất rủi ro hoặc xây dựng báo cáo nhóm/cảnh báo cho chính sách kiểm soát tốc độ, ưu tiên nguồn lực cho nhóm tốc độ cao nếu tỷ lệ này lớn hoặc gây hậu quả nghiêm trọng hơn. Đây là bước tổng hợp cực kỳ quan trọng trước mọi phân tích so sánh về hành vi hoặc mức độ ảnh hưởng của tốc độ đến an toàn giao thông thực tế.

1.3.5 Thống kê tần suất loại đường

table(dulieu2$Road_Type)
## 
##          Vòng xuyến           Một chiều           Hai chiều Một chiều không dải 
##              119472               36755              262950             1332384 
##         Đường nhánh                Khác 
##               18647               10445

Các kết quả thống kê loại đường cho thấy “Một chiều không dải” và “Hai chiều” là hai nhóm quan trọng nhất, vì số vụ tai nạn tại đây chiếm đa số toàn bộ dữ liệu.

“Một chiều không dải” có tới 1.332.384 vụ, chứng tỏ khu vực này, dù mật độ phương tiện lớn hay thiết kế chưa tối ưu, chính là điểm nóng hoặc tuyến giao thông nội đô, luồng xe lớn, va chạm cao.

“Hai chiều” cũng rất đáng chú ý với hơn 262.950 vụ, tiềm ẩn xung đột do xe đi ngược chiều hoặc điểm giao nhau phức tạp.

Các nhóm còn lại (Vòng xuyến, Một chiều, Đường nhánh, Khác) xuất hiện ít hơn, phản ánh tuyến nhỏ, kiểm soát tốt, hoặc ngoại ô, tuyến phụ.

Việc thống kê này giúp phát hiện loại hình đường có nguy cơ hoặc số vụ phát sinh lớn nhất (“Một chiều không dải”, “Hai chiều” có số vụ vượt trội). Các loại đường ít vụ (như “Một chiều”, “Khác”, “Đường nhánh”) thường là tuyến nhỏ, khả năng kiểm soát cao hoặc mật độ xe thấp. Số liệu này là nền tảng cho phân tích sâu, ưu tiên kiểm tra, điều chỉnh hạ tầng, xây dựng các chính sách giảm thiểu rủi ro từng loại đường cụ thể dựa trên thực tế.

1.3.6 Số nhóm tốc độ đã thiết lập

length(unique(dulieu2$Speed_cat))
## [1] 4

Số nhóm tốc độ đã thiết lập là 4, thể hiện dữ liệu đã được chia thành 4 phân nhóm tốc độ chính: Thấp, Vừa, Cao và Rất cao. Việc phân loại này rất quan trọng để phục vụ đối chiếu, phân tích rủi ro và kiểm soát nguy cơ riêng từng nhóm thay vì xử lý dữ liệu tốc độ thô tuyệt đối. Chia nhóm đúng chuẩn sẽ giúp phát hiện điểm nóng hành vi, đánh giá tác động hạ tầng, hoặc tối ưu các biện pháp quản lý, cảnh báo phù hợp cho từng vùng tốc độ trên toàn hệ thống giao thông.

1.3.7 Tổng số bản ghi không phải tốc độ cao

sum(dulieu2$is_high_speed == FALSE) 
## [1] 1309954

Lệnh này giúp kiểm tra nhanh số bản ghi trong dữ liệu đáp ứng điều kiện tốc độ thấp/vừa, dùng khi muốn tách nhóm hoặc phân tích riêng các tuyến nguy cơ thấp.

1.3.8 Đếm số vụ tai nạn ban ngày và ban đêm

table(dulieu2$Light_Conditions) 
## 
##       1       4       5       6       7 
## 1304474  349728    8185   98947   19319

Kết quả bảng đếm số vụ theo điều kiện ánh sáng cho thấy phần lớn các tai nạn giao thông trong dữ liệu đều xảy ra vào ban ngày(1:1304474); tiếp theo là ban đêm có đèn cũng chiếm một tỷ trọng khá lớn(4:349728`). Các điều kiện như ban đêm không đèn, ánh sáng yếu hoặc không rõ, và các trường hợp đặc biệt, chiếm tỷ lệ thấp hơn nhiều so với ban ngày.

Điều này phản ánh đặc thù thực tiễn rằng nguy cơ tai nạn không chỉ đến từ việc thiếu sáng mà còn bị ảnh hưởng bởi mật độ giao thông, thói quen lái xe hoặc điều kiện môi trường trong các khung giờ sáng và tối. Khi báo cáo hay phân tích, nên lưu ý rằng các thời điểm ban ngày vẫn là thời gian trọng điểm cần kiểm soát an toàn giao thông, không nên chủ quan dù ánh sáng tốt.

1.3.9 Số vụ trên từng tháng riêng biệt

dulieu2$Month <- month(dulieu2$Date)
dulieu2 %>% group_by(Month) %>% summarise(vu_thang = n())
## # A tibble: 12 × 2
##    Month vu_thang
##    <dbl>    <int>
##  1     1   142169
##  2     2   131666
##  3     3   142483
##  4     4   136953
##  5     5   150590
##  6     6   151173
##  7     7   156624
##  8     8   145814
##  9     9   153354
## 10    10   161181
## 11    11   162784
## 12    12   145862

Số vụ tai nạn theo từng tháng thường dao động quanh mức 130.000–162.000 vụ/tháng, trong đó các tháng cao điểm nhất là tháng 10, tháng 11 và tháng 7. Các tháng thấp điểm hơn là tháng 2, tháng 4, tháng 1.

Dao động này có thể liên quan đến mùa lễ tết, kỳ nghỉ hè, hoặc thời điểm hoạt động giao thông tăng đột biến. Nhìn chung, số vụ không quá chênh lệch giữa các tháng, nhưng các tháng mùa hè và cận cuối năm cho thấy xu hướng cao hơn, cần tăng cường kiểm soát và phòng ngừa trong thời gian đó.

1.3.10 Kiểm tra khoảng thời gian có vụ lớn nhất

dulieu2 %>%
  group_by(Year, Month) %>% 
  summarise(vu = n(), .groups = "drop") %>% 
  arrange(desc(vu)) 
## # A tibble: 132 × 3
##     Year Month    vu
##    <dbl> <dbl> <int>
##  1  2005    11 18747
##  2  2005    10 17533
##  3  2006    11 17397
##  4  2006    10 17124
##  5  2005    12 17095
##  6  2005     5 17032
##  7  2005     7 16889
##  8  2005     6 16874
##  9  2006     9 16785
## 10  2005     9 16720
## # ℹ 122 more rows

Lệnh này giúp xác định nhanh tháng và năm nào có số vụ tai nạn lớn nhất trong toàn bộ dữ liệu. Các tháng 10–11 của năm 2005–2006 nổi bật về số vụ( khoảng 17000-18000 vụ), phản ánh hiệu ứng thời vụ, cao điểm vận chuyển, kì nghỉ lễ hoặc các yếu tố đặc biệt gây tăng nguy cơ tai nạn. Việc xác định này rất quan trọng khi cần cảnh báo thời điểm rủi ro hoặc nghiên cứu sâu tác động thời gian đối với an toàn giao thông.

1.3.11 Số vụ tai nạn theo loại đường và nhóm tốc độ

table(dulieu2$Speed_cat, dulieu2$Road_Type)
##          
##           Vòng xuyến Một chiều Hai chiều Một chiều không dải Đường nhánh   Khác
##   Thấp           634      1871       485               18354          88    621
##   Vừa          73686     33377     74192              947606        5064   7684
##   Cao          23476       708     62279              114714        3022    598
##   Rất cao      21676       799    125994              251710       10473   1542

Số vụ tai nạn tập trung chủ yếu ở các mức tốc độ “Vừa”, “Cao”, “Rất cao” trên các loại đường “Một chiều không dải” và “Hai chiều”, phản ánh đây là những tuyến đường/phân khúc nguy cơ cao (ví dụ: “Cao, Một chiều không dải”: 114.714 vụ; “Rất cao, Hai chiều”: 125.994 vụ; “Vừa, Một chiều không dải”:947.606 vụ).

Các loại đường như “Vòng xuyến”, “Đường nhánh”, “Khác” có số vụ tai nạn thấp hơn rất nhiều ở mọi nhóm tốc độ, cho thấy yếu tố hạ tầng và thiết kế đường tác động lớn đến tần suất và mức độ nguy cơ.

Kết quả này thích hợp để nhận diện các điểm nóng và nhóm đối tượng ưu tiên trong các đề xuất quản lý tốc độ, kiểm soát hạ tầng hoặc tăng cường giám sát an toàn giao thông trên các tuyến đường trọng điểm.

1.3.12 Tính hệ số tương quan giữa tốc độ và số lượng xe tham gia

cor(dulieu2$Speed_limit, dulieu2$Number_of_Vehicles)
## [1] 0.08451867

Giá trị hệ số tương quan Pearson là 0.08451867, nằm gần về phía 0; điều này thể hiện rằng giữa giới hạn tốc độ và số lượng xe tham gia trong vụ tai nạn hầu như không có mối quan hệ tuyến tính rõ rệt.

Hay nói cách khác, khi tốc độ giới hạn thay đổi, số lượng xe liên quan đến tai nạn không thay đổi theo chiều hướng cùng chiều hoặc ngược chiều một cách thống kê đáng kể.

Thông tin này cho thấy các yếu tố quản lý tốc độ không phải là nguyên nhân chủ yếu ảnh hưởng đến số lượng xe liên quan trong một vụ tai nạn, mà có thể còn phụ thuộc mạnh vào các yếu tố khác như loại đường, mật độ giao thông, điều kiện môi trường, hoặc kiểu tai nạn thực tế xảy ra trên mỗi tuyến đường. ### Kiểm tra giá trị tối đa của số lượng xe

max(dulieu2$Number_of_Vehicles)
## [1] 67

Số lượng xe liên quan tối đa trong một vụ tai nạn giao thông là 67 xe. Đây là một con số đặc biệt lớn đối với một vụ tai nạn, thường xảy ra ở các vụ va chạm trên cao tốc, đường nhiều làn, giao lộ lớn, hoặc trong điều kiện thời tiết/xuất hiện bất ngờ dẫn đến chuỗi va chạm.

Trong phân tích mô tả, kết quả này giúp nhận diện các trường hợp ngoại lệ về quy mô vụ tai nạn, từ đó xác định các sự kiện cần phân tích sâu hoặc loại bỏ khi làm thống kê trung bình vì có thể gây lệch kết quả (nên kiểm tra thêm phân bố, tần suất từng mức số xe).

Biết được giá trị cực đại này, bạn có thể đặt ra các phân tổ hoặc rà soát các vụ đặc biệt nghiêm trọng, so sánh giữa trung vị, trung bình và tối đa để bình luận về mức độ đa dạng quy mô vụ việc trong suốt hơn 1,7 triệu quan sát.

1.3.13 Tóm tắt mô tả về tháng có số vụ cao nhất

dulieu2 %>% group_by(Month) %>% summarise(vu = n()) %>% arrange(desc(vu)) %>% head(1)
## # A tibble: 1 × 2
##   Month     vu
##   <dbl>  <int>
## 1    11 162784

%>%: Toán tử pipe trong R, nối chuyển dữ liệu qua các thao tác tiếp theo. group_by(Month): Nhóm tất cả quan sát theo biến tháng (Month). summarise(vu = n()): Tính tổng số vụ tai nạn (n()) cho từng nhóm tháng, lưu vào biến mới tên là vu. arrange(desc(vu)): Sắp xếp bảng kết quả theo số vụ tai nạn giảm dần. head(1): Lấy dòng đầu tiên, tức là tháng có nhiều vụ tai nạn nhất. Tháng 11 là tháng có số vụ tai nạn giao thông cao nhất trong giai đoạn 2005–2015 tại UK(162784), vượt trội so với các tháng còn lại.

Điều này phản ánh các yếu tố mùa vụ, thời tiết cuối năm (thường nhiều mưa, sương mù, tối sớm…), hoặc sự kiện, kỳ nghỉ đông có thể làm gia tăng lưu lượng giao thông, dẫn đến nguy cơ tai nạn tăng mạnh.

Kết quả này giúp xác định điểm nóng về thời gian, góp phần hoạch định chính sách, tăng cường cảnh báo an toàn, bố trí nguồn lực kiểm soát giao thông vào những tháng có rủi ro cao nhất trong năm.

1.3.14 Tính giá trị trung vị (median) và quartile tốc độ

median(dulieu2$Speed_limit) 
## [1] 30
quantile(dulieu2$Speed_limit, probs = c(0.25, 0.75))
## 25% 75% 
##  30  50

Trung vị (Median = 30 mph): median(…): Tính giá trị trung vị, tức là tốc độ giới hạn chia toàn bộ tập dữ liệu thành hai nửa—một nửa nhỏ hơn hoặc bằng 30 mph, và một nửa lớn hơn hoặc bằng 30 mph.

Phần lớn các vụ tai nạn xảy ra trên các đoạn đường có giới hạn tốc độ từ 30 mph trở xuống—đây là mức rất phổ biến ở khu đô thị, khu dân cư hoặc nơi kiểm soát tốc độ nghiêm ngặt. Điều này phản ánh ngay cả trong vùng thấp tốc độ, rủi ro tai nạn vẫn rất cao (chưa chắc tốc độ cao mới nhiều tai nạn).

Giá trị tứ phân vị (Q1 = 30, Q3 = 50): quantile(…, probs = c(0.25, 0.75)): Trả về ngưỡng mà25% số quan sát có tốc độ≤ 30 mph (Q1) và 75% số quan sát có tốc độ ≤ 50 mph (Q3).

Phân nửa tập dữ liệu có giới hạn tốc độ dưới hoặc bằng 30 mph, ba phần tư có tốc độ dưới hoặc bằng 50 mph. Việc này cho phép chia nhóm phân tích—tìm đặc điểm/tác động của nhóm có giới hạn tốc độ thấp (Q1), nhóm phổ biến (Q1–Q3), và nhóm tốc độ cao (trên Q3). Các chỉ tiêu này làm cơ sở xác định phân khúc nguy cơ hoặc lập bảng so sánh nhanh về tần suất và mức độ nghiêm trọng giữa từng nhóm tốc độ.

1.3.15 Thống kê số lượng các giá trị tốc độ duy nhất trong tập dữ liệu

length(unique(dulieu2$Speed_limit))
## [1] 9

Dòng lệnh (1) tạo một vector gồm tất cả các giá trị tốc độ duy nhất xuất hiện trong toàn bộ dữ liệu. length(...) để đếm số phần tử trong vector này, tức là có bao nhiêu mức tốc độ giới hạn khác nhau được ghi nhận trong bộ dữ liệu.

Trong hơn 1,7 triệu vụ tai nạn, tập dữ liệu ghi nhận tổng cộng 9 mức giới hạn tốc độ khác nhau.

Điều này phản ánh sự đa dạng về cơ sở hạ tầng giao thông của Anh trong giai đoạn 2005–2015: tốc độ giới hạn được chia thành nhiều cấp độ để phù hợp với thực tế sử dụng đường (ví dụ: 20, 30, 40, 50, 60, 70 mph…), từ khu nội đô đến cao tốc.

Khi mô tả, đối chiếu hoặc phân tổ rủi ro, việc biết rõ số nhóm tốc độ giúp bạn chia nhóm, xây dựng bảng/biểu đồ hợp lý và phân tích sâu về đặc thù từng nhóm tốc độ, kiểm tra xu hướng hoặc nguy cơ tương ứng giữa các cấp độ giới hạn tốc độ.

1.3.16 Tính khoảng tứ phân vị của tốc độ

IQR(dulieu2$Speed_limit)
## [1] 20

Dòng lệnh (1) là hàm trong R tính khoảng tứ phân vị, tức là lấy Q3 (tứ phân vị thứ 3, giá trị ở mức 75%) trừ đi Q1 (tứ phân vị thứ nhất, giá trị ở mức 25%).

Với dữ liệu này, IQR = 50 – 30 = 20 (cùng kết quả với khi tự lấy Q3 – Q1).

IQR (20 mph) thể hiện mức độ phân tán của phân nửa trung tâm nhất trong tập dữ liệu tốc độ—tức là 50% các vụ tai nạn tập trung trong khoảng giới hạn tốc độ từ 30 đến 50 mph.

IQR phản ánh rõ ràng đặc trưng phổ biến của dữ liệu mà không bị ảnh hưởng bởi các biến dị/cực trị (như một số giới hạn tốc độ rất thấp hoặc rất cao trong bộ dữ liệu).

Chỉ số này rất quan trọng khi phân tích các thước đo biến thiên, phát hiện hoặc loại trừ ngoại lệ (outlier) và làm cơ sở cho việc chia nhóm so sánh nguy cơ hoặc mức độ nghiêm trọng giữa các nhóm tốc độ khác nhau.

1.3.17 Phân phối số vụ trong năm/tháng

dulieu2$Year <- year(dulieu2$Date) 
dulieu2$Month <- month(dulieu2$Date)
dulieu2 %>% group_by(Year) %>% summarise(n = n()) 
## # A tibble: 11 × 2
##     Year      n
##    <dbl>  <int>
##  1  2005 198735
##  2  2006 189161
##  3  2007 182115
##  4  2008 170591
##  5  2009 163554
##  6  2010 154414
##  7  2011 151474
##  8  2012 145571
##  9  2013 138660
## 10  2014 146322
## 11  2015 140056
dulieu2 %>% group_by(Month) %>% summarise(n = n())
## # A tibble: 12 × 2
##    Month      n
##    <dbl>  <int>
##  1     1 142169
##  2     2 131666
##  3     3 142483
##  4     4 136953
##  5     5 150590
##  6     6 151173
##  7     7 156624
##  8     8 145814
##  9     9 153354
## 10    10 161181
## 11    11 162784
## 12    12 145862

Lệnh đầu tiên sẽ đếm và trả về số lượng các vụ tai nạn giao thông trong từng năm, giúp đánh giá xu hướng tổng thể: các vụ tai nạn tăng, giảm hay giữ ổn định theo thời gian.

Kết quả phân tích từng tháng năm cho thấy, số vụ tai nạn không phân bổ đều các tháng mà có tính chu kỳ rõ ràng—tháng 11, 10, 7, 9 là những tháng có số vụ cao nhất (đỉnh điểm là tháng 11 với 162.784 vụ); các tháng thấp điểm là 2, 4. Đánh giá này phản ánh hiệu ứng mùa vụ, thời tiết, các kỳ nghỉ hay lưu lượng giao thông đặc biệt của từng thời điểm trong năm.

Các phân tích như vậy rất quan trọng khi xây dựng cảnh báo an toàn giao thông, điều phối nguồn lực kiểm soát hoặc đề xuất chiến lược giảm thiểu nguy cơ vào các tháng cao điểm. Ngoài ra, hiệu suất phòng ngừa hoặc can thiệp có thể được đo lường qua so sánh các năm hoặc tháng.

1.3.18 Kiểm tra phân phối kết hợp loại đường và tốc độ

table(dulieu2$Speed_cat, dulieu2$Road_Type)
##          
##           Vòng xuyến Một chiều Hai chiều Một chiều không dải Đường nhánh   Khác
##   Thấp           634      1871       485               18354          88    621
##   Vừa          73686     33377     74192              947606        5064   7684
##   Cao          23476       708     62279              114714        3022    598
##   Rất cao      21676       799    125994              251710       10473   1542

Bảng này giúp trực quan hóa sự khác biệt về tần suất tai nạn giữa các nhóm tốc độ trên từng loại đường. Có thể thấy số vụ lớn nhất xảy ra ở nhóm tốc độ “Vừa” trên các loại đường “Một chiều không dải” (947.606 vụ), tiếp đến là “Hai chiều” ở nhóm “Rất cao” (125.994 vụ) và “Một chiều không dải” ở nhóm “Rất cao” (251.710 vụ).

Điều này phản ánh phần lớn các vụ tai nạn tập trung vào các tuyến đường giao thông chính, nơi lưu lượng phương tiện lớn và tốc độ được phép trung bình/cao, cho thấy các yếu tố nguy cơ liên quan đến cả hạ tầng (loại đường) và tốc độ vận hành.

Kết quả này rất hữu ích để ưu tiên kiểm soát, quy hoạch an toàn cho các tuyến đường chiếm tỷ trọng vụ việc cao; đồng thời đánh giá tác động của chính sách hạn chế tốc độ hoặc nâng cấp về hạ tầng giao thông.

1.3.19 Hệ số tương quan giữa tốc độ và số xe

cor(dulieu2$Speed_limit, dulieu2$Number_of_Vehicles, use="complete.obs")
## [1] 0.08451867

Hệ số tương quan 0.08là rất nhỏ, cho thấy tốc độ giới hạn và số lượng xe tham gia trong các vụ tai nạn giao thông gần như không có quan hệ tuyến tính đáng kể: tức là việc thay đổi giới hạn tốc độ trên các tuyến đường không làm thay đổi số lượng xe có mặt tại một vụ tai nạn một cách rõ ràng.

Trong thang đo ý nghĩa thống kê, giá trị này nằm dưới ngưỡng 0.1, được xem là tương quan rất yếu, tức là gần như độc lập.

Kết quả này gợi ý khi phân tích nguy cơ tăng số xe liên quan, cần chú ý các yếu tố khác như loại đường, thời điểm trong năm hay điều kiện môi trường, thay vì chỉ tập trung vào tốc độ giới hạn.

1.4 Trực quan hóa dữ liệu

1.4.1 Biểu đồ số vụ tai nạn giao thông từng năm theo loại đường

library(dplyr)
dulieu2_subset <- dulieu2 %>%
  filter(Road_Type %in% c("Hai chiều", "Một chiều không dải"))
ggplot(dulieu2_subset, aes(x=Year, fill=factor(Accident_Severity))) + 
  geom_bar(position="dodge", color="black") + 
  facet_wrap(~Road_Type) +
  labs(
    title="Số vụ theo năm & RoadType cụ thể",
    y="Số vụ", 
    fill="Mức độ") +
  theme_minimal() +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Biểu đồ minh họa số vụ tai nạn giao thông từng năm đối với 2 loại đường điển hình: Hai chiều và Một chiều không dải, giúp so sánh xu hướng theo thời gian trên từng loại hạ tầng. Các cột được chia màu theo mức độ nghiêm trọng tai nạn (ví dụ: ít nghiêm trọng, vừa, nghiêm trọng), làm rõ đặc điểm từng nhóm vụ việc trên mỗi loại đường từng năm.

Việc tách riêng từng loại đường bằng facet giúp dễ dàng nhận biết nhóm đường nào có số vụ cao hơn, hoặc nhóm năm nào có diễn biến thái cực (tăng/giảm đột biến).

Phân tích này giúp nêu bật xu hướng tai nạn theo thời gian cũng như nhận diện hạ tầng nguy cơ (kiểm tra điểm nóng), từ đó đưa ra khuyến nghị hoặc tập trung kiểm soát tại các đường đông tai nạn nhất, từng giai đoạn cụ thể

1.4.2 Biểu đồ tần suất mức độ nghiêm trọng theo loại đường

dulieu2$Speed_cat <- factor(dulieu2$Speed_cat, 
                            levels = c("Thấp","Vừa","Cao","Rất cao")) 
ggplot(dulieu2, aes(x=Road_Type, fill=Speed_cat)) + 
  geom_bar(color="black", position="stack") + 
  facet_wrap(~Speed_cat) +  
  labs(title="Tổng số vụ theo Road_Type – nhóm tốc độ", y="Số vụ") +
  theme_minimal() +         # Giao diện tối giản, dễ nhìn (layer 4)
  scale_fill_manual(values=c("Thấp"="#7fc97f", "Vừa"="#beaed4", "Cao"="#fdc086", "Rất cao"="#ffff99")) +  
  theme(axis.text.x = element_text(angle = 30, hjust = 1)) +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Biểu đồ giúp phân tích tổng số vụ tai nạn trên từng loại đường, đồng thời trực quan hóa sự phân bố số vụ này theo 4 nhóm tốc độ (Thấp, Vừa, Cao, Rất cao) đã được mã hóa trước đó. Mỗi panel (facet) thể hiện rõ đặc trưng từng nhóm tốc độ, cho phép quan sát loại đường nào chiếm tỷ trọng lớn trong từng nhóm (ví dụ: hầu hết vụ tai nạn tốc độ “Vừa” xảy ra ở loại “Một chiều không dải”, còn tốc độ “Rất cao” tập trung ở nhóm nào, v.v.).

Việc so sánh giữa các panel cho phép nhận diện hạ tầng nguy cơ tương ứng từng nhóm tốc độ—ví dụ một số loại đường chỉ xuất hiện số vụ cao ở tốc độ “Rất cao”, nhưng thấp ở các nhóm còn lại. Từ đó, kết quả này hỗ trợ hoạch định chính sách an toàn giao thông, xác định nhóm mục tiêu truyền thông hoặc can thiệp kỹ thuật ở những đoạn đường và tốc độ có số vụ nổi bật.

1.4.3 Biểu đồ heatmap số vụ theo loại đường và nhóm tốc độ

library(reshape2) 
table_heat <- as.data.frame(table(dulieu2$Road_Type, dulieu2$Speed_cat))
ggplot(table_heat, aes(Var1, Var2, fill=Freq)) +
  geom_tile(color="white") +
  geom_text(aes(label=Freq), size=6) +
  labs(title="Heatmap số vụ theo Road_Type và nhóm tốc độ", 
       x="RoadType", y="Nhóm tốc độ") +
  scale_fill_gradient(low="yellow", high="red") + 
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 30, hjust = 1)) +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Biểu đồ heatmap này trực quan hóa số vụ tai nạn cho từng tổ hợp loại đường và nhóm tốc độ, giúp bạn dễ dàng nhìn thấy ở nhóm nào nguy cơ cao. Ô càng đậm (từ vàng sang đỏ) nghĩa là số vụ càng lớn, các số ngay giữa ô giúp đối chiếu số liệu chính xác. Đỉnh nổi bật là ô “Một chiều không dải – Vừa”, cho thấy nhóm này có số vụ rất lớn (947.606 vụ).

Các tổ hợp có số nhỏ dễ nhận biết nhờ màu vàng nhạt và số thấp trong ô, nổi bật các nhóm cần chú ý quản lý tài nguyên, cảnh báo, kiểm soát an toàn. So với biểu đồ bar, heatmap cho cái nhìn toàn diện hơn về mối liên hệ đồng thời giữa hai yếu tố hạ tầng và tốc độ trong phân tích rủi ro tai nạn giao thông. Đây là công cụ đắc lực để tổng hợp, ưu tiên kiểm soát và ra quyết định dựa trên đa biến từ dữ liệu lớn.

1.4.4 Histogram phân phối tốc độ

ggplot(dulieu2, aes(x=Speed_limit, fill=factor(Accident_Severity))) +
  geom_histogram(binwidth=5, position="identity", alpha=0.7, color="black") + 
  facet_wrap(~Road_Type, labeller = as_labeller(c("Vòng xuyến"="Vòng xuyến","Một chiều"="Một chiều","Hai chiều"="Hai chiều","Một chiều không dải"="Một chiều không dải","Đường nhánh" = "Nhánh","Khác"="Khác"))) +  
  labs(title="Phân phối tốc độ và mức độ nghiêm trọng", y="Số vụ (nghìn)") +
  theme_minimal() + 
  scale_y_continuous(labels = function(x) x / 1000) +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  ) 

Biểu đồ cho ta thấy phân phối số vụ tai nạn ứng với các mức giới hạn tốc độ khác nhau trên từng nhóm loại đường (panel). Cột màu thể hiện sự phân bố số vụ theo mức độ nghiêm trọng tại mỗi mức giới hạn tốc độ, giúp nhận diện nhanh tốc độ/loại đường nào hay gặp vụ nặng hay nhẹ nhiều nhất.

Từ histogram này sẽ thấy phần lớn số vụ tập trung ở một vài mức tốc độ đặc trưng (đặc biệt là 30mph và 50mph trên loại “Một chiều không dải”). Sử dụng đơn vị “nghìn” ở trục tung giúp biểu diễn số liệu lớn, trực quan và dễ đối chiếu giữa các nhóm (giảm bớt số 0 trên trục dọc). Việc tách panel loại đường giúp đánh giá ảnh hưởng của đặc điểm hạ tầng tới mối quan hệ giữa tốc độ và hậu quả tai nạn—vừa trực quan vừa giải thích được tại sao nên quản lý đặc biệt ở một số loại đường/tốc độ cụ thể.

1.4.5 Pie chart phân loại hạ tầng đường

road_tab <- as.data.frame(table(dulieu2$Road_Type))
road_tab$Var1_lab <- paste0(road_tab$Var1, " (", road_tab$Freq, ")")
ggplot(road_tab, aes(x="", y=Freq, fill=Var1_lab)) +
  geom_bar(width=1, stat="identity", color="grey4") + 
  coord_polar("y") +
  labs(title="Tỉ lệ từng RoadType") + 
  theme_void() +
  guides(fill=guide_legend(title="Loại đường (số vụ)", 
        label.position = "right")) +
  scale_fill_manual(values=c(
    "#A3A1FB", "#64B5F6", "#FFC3A0", "#FF8A80", "#F9D5E5", "#FEB144"
  )) +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    axis.text       = element_blank(),   # ẩn số trục
    axis.title      = element_blank(),   # ẩn tiêu đề trục
    axis.ticks      = element_blank(),   # ẩn vạch trục
    panel.grid      = element_blank(),   # ẩn đường chia
    plot.title      = element_text(size = 36, hjust = 0.5),
    legend.title    = element_text(size = 28),
    legend.text     = element_text(size = 28),
    strip.text      = element_text(size = 30)
  )

Biểu đồ tròn này trực quan hóa tỉ lệ các loại hạ tầng đường bộ trong toàn bộ số vụ tai nạn, giúp ta nhận biết nhóm loại đường có số vụ cao nhất chỉ với một cái nhìn. Nhãn rõ ràng [Loại đường (số vụ)] ở chú thích (legend) giúp không cần gắn trực tiếp số vào từng lát tròn (giảm rối, tránh đè lên nhau), rất phù hợp báo cáo khoa học hoặc trình bày.

Màu pastel dịu và có sự tách biệt giúp phân biệt từng loại rõ ràng, dễ đọc ngay cả khi in màu hoặc dùng trong slide. Đường “Một chiều không dải” chiếm tỷ lệ vượt trội, phản ánh đặc điểm đô thị, lưu lượng hoặc cơ sở hạ tầng là yếu tố chính ảnh hưởng đến quy mô tai nạn.

1.4.6 Bản đồ phân bố địa lý các vụ tai nạn (nếu có toạ độ)

library(maps) 
library(ggmap)
ggplot(dulieu2, aes(x=Longitude, y=Latitude, color=Accident_Severity)) + 
  geom_point(alpha=0.5, size=0.9) + 
  facet_wrap(~Road_Type, labeller = as_labeller(c("Vòng xuyến"="Vòng xuyến","Một chiều"="Một chiều","Hai chiều"="Hai chiều","Một chiều không dải"="Một chiều không dải","Đường nhánh" = "Nhánh","Khác"="Khác"))) +  
  labs(title="Bản đồ phân bố địa lý vụ tai nạn", y="Vĩ độ", x="Kinh độ") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Biểu đồ này phân bố các vụ tai nạn trên bản đồ nước Anh theo kinh độ và vĩ độ, giúp quan sát vị trí không gian của từng vụ việc. Sử dụng màu sắc/thang màu để biểu thị mức độ nghiêm trọng (Accident_Severity), vừa tổng quát vừa nổi bật các cụm nguy hiểm, dễ phát hiện điểm nóng.

Dùng facet_wrap theo từng loại đường giúp nhận biết loại hạ tầng nào phân bố tai nạn ở khu vực nào (ví dụ: “Một chiều không dải” tập trung ở thành phố lớn?), hỗ trợ hoạch định giao thông hoặc kiểm tra đặc điểm vùng nguy cơ. Dạng scattermap này rất trực quan để báo cáo với phòng quản lý đô thị, cơ quan chức năng hoặc khi phân tích nguyên nhân không gian của tai nạn giao thông thực tế.

1.4.7 Violin plot phân phối tốc độ từng loại đường

ggplot(dulieu2, aes(x=factor(Road_Type), y=Speed_limit, fill=Road_Type)) +
  geom_violin(trim=FALSE, alpha=0.6) +
  geom_jitter(height=0, width=0.08, size=0.4, alpha=0.3) +
  facet_wrap(~Speed_cat) + 
  labs(title="Violin plot tốc độ từng RoadType") +  
  theme_linedraw() + 
  theme(axis.text.x = element_text(angle = 30, hjust = 1)) +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Biểu đồ violin cung cấp thông tin trực quan về phân phối giá trị giới hạn tốc độ trên từng loại hạ tầng, qua hình dáng và độ dày mỗi violin (rộng/thấp). Từng panel (facet) là một nhóm phân loại tốc độ (Thấp, Vừa, Cao, Rất cao), từ đó thấy rõ loại đường nào chiếm ưu thế trong từng nhóm tốc độ được mã hóa. Lớp jitter giúp hiển thị chi tiết mức độ tập trung hoặc phân tán các giá trị thực (giới hạn tốc độ), không bị che giấu khi chỉ nhìn bằng violin.

Đồ thị này giúp so sánh nhanh các loại đường nào áp dụng phổ biến các mức tốc độ nào, loại nào thường có tốc độ cao/thấp, từ đó nhận diện nguy cơ tiềm ẩn về an toàn giao thông theo hạ tầng.

1.4.8 Line plot biến động số vụ theo tháng

dulieu_m <- dulieu2 %>% group_by(Month) %>% summarise(Vụ=n())
ggplot(dulieu_m, aes(x=Month, y=Vụ)) +
  geom_line(color="red", size=1) +
  geom_point(size=2, color="blue3") +
  labs(title="Biến động số vụ theo tháng") + 
  theme_light() +           
  scale_x_continuous(breaks=1:12) +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Biểu đồ line này phản ánh sự biến động số vụ tai nạn giao thông qua các tháng trong năm, cung cấp góc nhìn tổng quan về xu hướng mùa vụ/quý. Các điểm xanh và nhãn số trên đồ thị giúp dễ nhận biết tháng cao điểm, thấp điểm về số vụ, phục vụ so sánh nhanh giữa các thời kỳ.

Đường nối đỏ cho thấy rõ các tháng liên tiếp có tăng/giảm đột biến hay tương đối ổn định, từ đó xác định các yếu tố mùa vụ/ngày lễ hoặc thời tiết có thể tác động lên tần suất tai nạn. Qua biểu đồ này, báo cáo có thể chỉ ra tháng 11 là tháng cao điểm nhất về số vụ, nhấn mạnh nhu cầu tập trung nguồn lực kiểm soát/giám sát an toàn hoặc xây dựng chính sách đặc thù cho từng giai đoạn trong năm.

1.4.9 Point-range biểu diễn trung vị và tứ phân vị

library(dplyr)
tbl_speed <- dulieu2 %>% 
  group_by(Road_Type) %>% 
  summarise(
    med = median(Speed_limit),
    Q1 = quantile(Speed_limit, 0.25), 
    Q3 = quantile(Speed_limit, 0.75) 
  )
ggplot(tbl_speed, aes(x=Road_Type, y=med)) +
  geom_point(size=3, color="purple") + 
  geom_errorbar(aes(ymin=Q1, ymax=Q3), width=0.3) + 
  labs(title="Trung vị và tứ phân vị tốc độ từng RoadType") +
  theme(axis.text.x = element_text(angle = 30, hjust = 1)) +
  scale_x_discrete(labels = c("Đường nhánh" = "Nhánh")) +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30) 
  )

Biểu đồ này minh họa trung vị tốc độ trên từng loại đường (chấm tím) và khoảng tứ phân vị (Q1-Q3) thể hiện độ phân tán các giá trị tốc độ. Dễ dàng nhận biết loại đường nào có tốc độ lớn nhỏ nhất (trung vị thấp nhất/cao nhất) và biến động tốc độ trên từng hạ tầng (khoảng tứ phân vị dài/rộng).

Những loại đường như Hai chiều hoặc Đường nhánh có khoảng tứ phân vị rất rộng, phản ánh hoặc có nhiều mức tốc độ được áp dụng, hoặc dữ liệu nhiều ngoại lệ hơn. Đồ thị này rất hữu ích để so sánh đặc trưng phân phối tốc độ giữa các loại đường (đo lường mức độ đồng nhất hay đa dạng về giới hạn tốc độ của các tuyến đường).

1.4.10 Biểu đồ bubble: Số vụ, số xe, mức độ nghiêm trọng

ggplot(dulieu2, aes(x=Road_Type, y=Number_of_Vehicles, size=Speed_limit, color=factor(Accident_Severity))) +
  geom_point(alpha=0.5) +
  facet_wrap(~Speed_cat) +
  labs(title="Bubble số xe - RoadType - mức độ nghiêm trọng") +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 30, hjust = 1)) +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Mỗi bubble biểu diễn một vụ tai nạn; trục x là loại đường, trục y là số lượng xe liên quan. Kích thước bubble tỷ lệ với giới hạn tốc độ (Speed_limit), cho thấy mức tốc độ cao thì kích thước tròn lớn, dễ nhận diện. Màu bubble phản ánh mức độ nghiêm trọng của vụ tai nạn (Accident_Severity)—giúp phân biệt vụ nặng/nhẹ trực quan.

Facet theo nhóm tốc độ (Speed_cat), cho phép đánh giá nhanh sự khác biệt về hành vi tai nạn giữa các nhóm tốc độ.

Nhờ hình thức này,có thể đưa ra loại đường nào thường xảy ra tai nạn có nhiều xe cùng lúc, ở mức độ nghiêm trọng nào và trong nhóm tốc độ nào—từ đó giúp phát hiện những “điểm nóng” nguy cơ cao và gợi ý biện pháp kiểm soát hoặc truyền thông phù hợp nhất.

1.4.11 Histogram độ dài đoạn đường giới hạn tốc độ

ggplot(dulieu2, aes(x=Speed_limit, fill=Speed_cat)) +
  geom_histogram(binwidth=5, color='black', alpha=0.7) +
  facet_wrap(~Accident_Severity) +
  labs(title="Histogram tốc độ phân tầng mức độ nghiêm trọng") +
  theme_classic() + 
  scale_fill_brewer(palette="Set2") +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Biểu đồ giúp đánh giá phân phối số vụ theo từng mức giới hạn tốc độ, đồng thời so sánh cụ thể ở từng nhóm mức độ nghiêm trọng tai nạn (panel 1, 2, 3). Nhóm tốc độ (Thấp, Vừa, Cao, Rất cao) được tô màu, vừa dễ so màu vừa cho thấy ở từng mức nghiêm trọng, nhóm tốc độ nào thường gặp nhiều vụ nhất.

Có thể thấy: đa số vụ mức nghiêm trọng nhẹ tập trung ở tốc độ vừa và thấp; tai nạn nghiêm trọng cao thì phân tán ở các mức “Cao” đến “Rất cao”. Phù hợp để phát hiện rủi ro tiềm tàng về tốc độ trên từng cấp độ hậu quả—khi cần truyền thông, áp chính sách kiểm soát tốc độ hoặc can thiệp kỹ thuật từng nhóm đối tượng nguy cơ.

1.4.12 Barplot số vụ theo thứ trong tuần

ggplot(dulieu2, aes(x=factor(Day_of_Week), fill=Road_Type)) +
  geom_bar(color="black") +
  facet_wrap(~Speed_cat) +      
  labs(title="Số vụ tai nạn theo thứ trong tuần", x="Thứ", y="Số vụ") + 
  theme_minimal() +   
  scale_fill_brewer(palette="Dark2",
    labels = c("Đường nhánh" = "Nhánh")) +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Barplot này giúp so sánh số vụ tai nạn từng ngày trong tuần (thứ 1 - 7), trên từng nhóm tốc độ (Thấp, Vừa, Cao, Rất cao). Mỗi cột là tổng số vụ thuộc từng loại đường, nhờ phối hợp màu trong legend giúp nhận ra loại hạ tầng nào “đóng góp” tỷ trọng lớn nhất ở mỗi ngày/thứ.

Việc tách panel cho từng nhóm tốc độ cho phép nhận ra xu thế nguy cơ theo từng phân khúc tốc độ—vd: nhóm “Vừa” thường tập trung vụ ở ngày thường/cuối tuần, nhóm “Rất cao” có biến động thế nào theo từng ngày. Đây là biểu đồ mạnh về khám phá dữ liệu dạng thời gian tuần, hết sức phù hợp để báo cáo phân tích rủi ro theo lịch/lịch làm việc, phục vụ đề xuất chính sách hoặc truyền thông cảnh báo vào những ngày nguy cơ nhất định.

1.4.13 Violin plot số lượng xe theo nhóm tốc độ

ggplot(dulieu2, aes(x=Speed_cat, y=Number_of_Vehicles, fill=Speed_cat)) +
  geom_boxplot(alpha=0.7, outlier.color="red", outlier.size=1.2) +
  geom_jitter(width=0.2, alpha=0.2, color='black', size=0.7) +
  labs(title="Boxplot số lượng xe từng nhóm tốc độ", x="Nhóm tốc độ", y="Số lượng xe") +
  theme_classic() +
  scale_fill_brewer(palette="Accent") +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Boxplot cho từng nhóm tốc độ (Thấp, Vừa, Cao, Rất cao) cho phép so sánh trực tiếp phân bố số lượng xe liên quan đến tai nạn giữa các nhóm. Dải hộp (box) thể hiện khoảng tứ phân vị (50% dữ liệu trung tâm), vạch ngang trong box là trung vị, “râu” hai bên lan rộng tới giá trị biên hợp lý.

Các điểm đỏ phía trên là các ngoại lai (rất nhiều xe tham gia trong 1 vụ)—đáng chú ý, nhóm tốc độ càng cao thì càng có nhiều ngoại lai lớn. Các điểm jitter màu đen (chấm phân tán) thể hiện mật độ dữ liệu thực tế – phần lớn vụ chỉ có 1–5 xe, số vụ nhiều xe khá hiếm. So sánh giữa các nhóm, các nhóm tốc độ cao (Cao/Rất cao) có hộp box dày hơn, nhiều điểm ngoại lai—gợi ý: tốc độ càng lớn càng nhiều khả năng xảy ra va chạm liên đới nhiều phương tiện, nhưng phần lớn vẫn là các vụ quy mô nhỏ.

1.4.14 Bar chart số vụ theo nhóm tốc độ

ggplot(dulieu2, aes(x=Speed_cat, fill=Speed_cat)) +
  geom_bar(color="black") + 
  facet_wrap(~Accident_Severity) +
  labs(title="Số vụ theo nhóm tốc độ", y="Số vụ") +        
  theme_bw() +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Biểu đồ này thể hiện số vụ tai nạn phân theo nhóm tốc độ (Speed_cat) và đồng thời phân tách theo từng mức độ nghiêm trọng (Accident_Severity), thuận tiện so sánh nhanh từng khía cạnh. Hiển thị số cụ thể trên đầu từng cột giúp người xem nhận ra ngay nhóm nào có nhiều/ít vụ nhất, lượng hoá từng nhóm mà không cần ước lượng bằng mắt.

Sự phân chia panel giúp làm rõ: ở mức nghiêm trọng nhẹ (1), số vụ rất thấp, chủ yếu ở nhóm tốc độ vừa/nhỏ; càng lên các mức nghiêm trọng 2-3, số vụ ở nhóm tốc độ “Vừa” và “Rất cao” bùng nổ. Dạng trình bày này làm nổi bật tầm quan trọng của kiểm soát tốc độ hợp lý—bất kỳ sự thiên lệch nào về tốc độ đều kéo theo thay đổi đáng kể về quy mô và hậu quả tai nạn.

1.4.15 Pie chart tỉ lệ mức độ nghiêm trọng

pie_tab <- as.data.frame(table(dulieu2$Accident_Severity))
ggplot(pie_tab, aes(x="", y=Freq, fill=as.factor(Var1))) +
  geom_bar(width=1, stat="identity") +
  coord_polar("y") +
  labs(title="Tỉ lệ mức độ nghiêm trọng") + 
  theme_void() +
  scale_fill_brewer(palette="Set3") +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    axis.text       = element_blank(),  
    axis.title      = element_blank(),  
    axis.ticks      = element_blank(),   
    panel.grid      = element_blank(),   
    plot.title      = element_text(size = 36, hjust = 0.5),
    legend.title    = element_text(size = 28),
    legend.text     = element_text(size = 28),
    strip.text      = element_text(size = 30)
  )

Biểu đồ này giúp trực quan hóa cơ cấu tỷ lệ các mức độ nghiêm trọng trong toàn bộ tập tai nạn, thể hiện bằng từng lát tròn và màu sắc khác nhau. Nhìn vào kích thước lát tròn và số liệu hiển thị, ta biết ngay đa số tai nạn thuộc mức nhẹ (số ‘3’ áp đảo), còn mức nghiêm trọng và rất nghiêm trọng chỉ chiếm tỷ lệ nhỏ.

Đây là cách tổng kết dữ liệu định tính dễ tiếp cận, phù hợp với mọi báo cáo, nhất là để tạo điểm nhấn hoặc kết luận về tính phân bố mức độ hậu quả trong tập dữ liệu thực tế. Nên trình bày cùng nhận xét lý giải vì sao mức nhẹ quá lớn, số vụ nghiêm trọng ít (do nguyên nhân phòng ngừa hiệu quả, hay do đặc thù báo cáo/số hóa?).

1.4.16 Biểu đồ scatter cơ bản: tương quan giữa tốc độ và số xe

ggplot(dulieu2, aes(x=Speed_limit, y=Number_of_Vehicles)) +
  geom_point(alpha=0.3, color="blue", size=2) + 
  geom_smooth(method='lm', color="red") + 
  labs(title="Tương quan giữa tốc độ và số lượng xe", 
       x="Giới hạn tốc độ", y="Số lượng xe") +           
  theme_minimal() +             
  theme(panel.grid.major = element_line(color = "gray90")) +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Mỗi điểm biểu thị một vụ tai nạn: tọa độ x là giới hạn tốc độ, y là số lượng xe liên quan trong vụ đó. Đường thẳng đỏ hồi quy giúp nhận diện xu hướng tổng thể liệu số xe liên quan có tăng rõ theo tốc độ không; trong bộ dữ liệu này có thể thấy tương quan rất yếu (độ dốc gần như nằm ngang).

Đa số điểm tập trung ở các mức tốc độ đặc trưng (30, 40, 50, 60…), phản ánh thực tế có một số chuẩn giới hạn_speed phổ biến trên đường bộ Anh. Biểu đồ này hữu ích để kiểm chứng trực quan: giới hạn tốc độ cao không kéo theo nhiều xe hơn tham gia va chạm, nghĩa là yếu tố tốc độ và số xe có thể độc lập hoặc bị ảnh hưởng mạnh bởi các yếu tố khác (loại đường, thời gian, cấu trúc giao thông…).

1.4.17 Biểu đồ line tốc độ trung bình theo năm (chỉ lấy năm chẵn)

library(dplyr)
dulieu2_even <- dulieu2 %>%
  filter(Year %% 2 == 0) %>%  
  group_by(Year) %>%
  summarise(avg_speed = mean(Speed_limit, na.rm=TRUE))
ggplot(dulieu2_even, aes(x=Year, y=avg_speed)) +
  geom_line(size=1, color="darkgreen") +   
  geom_point(size=2, color="black") +   
  geom_text(aes(label=round(avg_speed,1)), vjust=-1) +
  labs(title="Tốc độ trung bình từng năm chẵn", x="Năm", y="Tốc độ trung bình") +
  theme_classic() + 
  scale_y_continuous(expand=expansion(mult = c(0, 0.18))) +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Đây là biểu đồ đường thể hiện xu hướng tốc độ trung bình trên toàn quốc qua từng năm chẵn trong bộ dữ liệu, giúp kiểm tra sự thay đổi (tăng/giảm) theo thời gian. Số liệu được tính riêng cho các năm chẵn để loại bỏ nhiễu hoặc đáp ứng yêu cầu đặc thù bài toán/phân tích. Các nhãn số giá trị cụ thể ở mỗi năm giúp nhận diện chi tiết sự chênh lệch nhỏ qua từng giai đoạn.

Biểu đồ này chỉ rõ xu hướng tốc độ trung bình giảm dần qua các năm chẵn, có thể là dấu hiệu của các chính sách an toàn giao thông, nâng cường kiểm soát tốc độ, hoặc thay đổi hạ tầng, biển báo… mang lại hiệu quả thực sự trên dữ liệu thực tế. Đây là kiểu biểu đồ rất trực quan, dễ đưa vào báo cáo, slide trình bày nhằm dân giải xu hướng tổng thể một cách dễ nhớ và thuyết phục.

1.4.18 Histogram tốc độ nhóm theo mức độ nghiêm trọng

ggplot(dulieu2, aes(x=Speed_limit, fill=factor(Accident_Severity))) +
  geom_histogram(binwidth=5, color="black", alpha=0.6, position="identity") + 
  labs(title="Phân phối tốc độ theo mức độ nghiêm trọng",
       x="Giới hạn tốc độ", y="Số vụ") +              
  theme_bw() +        
  facet_wrap(~Accident_Severity) + 
  scale_fill_brewer(palette="Pastel1") +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Biểu đồ này cho thấy phân phối số vụ tai nạn ở từng mức tốc độ, chia theo từng loại mức độ nghiêm trọng (1, 2, 3). Mỗi panel đại diện cho một nhóm mức độ nghiêm trọng, trong đó cột màu thể hiện số vụ tương ứng từng mức giới hạn tốc độ.

Có thể nhận thấy tai nạn mức nghiêm trọng thấp (3) tập trung cực lớn ở giới hạn tốc độ khoảng 30mph, mức nghiêm trọng nặng hơn (2, 1) có số lượng nhỏ hơn và xuất hiện tại các tốc độ cao hơn, phân tán hơn. Đây là kiểu trực quan hóa phù hợp khi muốn tìm hiểu mức độ rủi ro của từng nhóm tốc độ, nhận diện mức độ nghiêm trọng nào phổ biến ở mức tốc độ nào—qua đó rút ra biện pháp kiểm soát, hướng truyền thông hoặc ưu tiên kiểm tra thực tế một cách khoa học và hiệu quả.

1.4.19 Biểu đồ tốc độ trung bình theo loại đường

library(dplyr)
dulieu_rt <- dulieu2 %>%
  group_by(Road_Type) %>%
  summarise(avg_speed = mean(Speed_limit, na.rm=TRUE))
ggplot(dulieu_rt, aes(x=Road_Type, y=avg_speed, fill=Road_Type)) +
  geom_bar(stat="identity", color="black", alpha=0.8) +    
  labs(title="Tốc độ TB theo RoadType", y="Tốc độ trung bình", x="Loại đường") +
  theme_minimal() + 
  theme(axis.text.x = element_text(angle=30, hjust=1)) + 
  scale_y_continuous(expand=expansion(mult=c(0,0.15))) +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Biểu đồ bar này so sánh tốc độ trung bình của từng loại hạ tầng đường trong toàn bộ dữ liệu tai nạn giao thông. Các thanh cột với màu sắc riêng biệt, nhãn ngay trên đầu, giúp nhận diện nhanh loại đường nào cho phép/chủ yếu có tốc độ di chuyển cao, loại nào thấp nhất.

Kết quả cho thấy Đường nhánh và Hai chiều thường có tốc độ trung bình lớn, còn Một chiều và Một chiều không dải thấp nhất—phù hợp với đặc điểm thực địa, quy chuẩn giao thông. Việc trình bày tốc độ TB qua cột giúp nhấn mạnh và so sánh rõ ràng giữa các nhóm hạ tầng, hữu ích cho đánh giá hiệu quả kiểm soát tốc độ, xây dựng chính sách quy định tốc độ cũng như truyền thông nâng cao nhận thức an toàn trên từng loại đường cụ thể.

1.4.20 Barplot phân phối tần suất giới hạn tốc độ

library(ggplot2)
ggplot(dulieu2, aes(x=factor(Speed_limit), fill=factor(Speed_limit))) +
  geom_bar(color="black", alpha=0.8) +    
  labs(title="Phân phối các mức giới hạn tốc độ", 
       x="Giới hạn tốc độ (mph)", y="Số vụ", fill="Giới hạn tốc độ") +
  theme_minimal() +                
  scale_fill_brewer(palette="Pastel2") +    
  scale_y_continuous(expand=expansion(mult=c(0,0.13))) +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Biểu đồ thể hiện tần suất số vụ tai nạn ứng với từng mức giới hạn tốc độ (mph) trong toàn bộ dữ liệu, giúp nhận ra mức nào phổ biến nhất. Số trên đầu mỗi cột giúp dễ dàng đọc chính xác từng con số, thuận lợi cho báo cáo, trình bày hoặc nêu nhận xét so sánh giữa các mức tốc độ.

Kết quả cho thấy giới hạn tốc độ 30mph áp đảo toàn bộ về số vụ tai nạn, các mức 20, 40, 50, 60 và 70mph có tần suất nhỏ hơn rất nhiều. Cách trình bày này phù hợp để nêu bật các nhóm đối tượng trọng tâm (ví dụ: đô thị - 30mph, quốc lộ - 60/70mph), làm căn cứ đề xuất chính sách hạn tốc hoặc điều chỉnh cơ sở hạ tầng với từng nhóm vận tốc/địa hình cụ thể.

2 Bộ dữ liệu Báo Cáo Tài Chính

2.1 Giới thiệu bộ dữ liệu

2.1.1 Đọc dữ liệu

data<-read.csv("C:/TL/BCTC/dulieu1BCTC.csv",fileEncoding = "UTF-8")

Dòng (1) dùng để đọc bộ dữ liệu báo cáo tài chính từ file CSV có sẵn trên máy, lưu trữ vào biến data. Thao tác này đảm bảo toàn bộ bảng số liệu được tải đầy đủ vào môi trường làm việc R.

Tham số fileEncoding = "UTF-8" giúp dữ liệu được đọc chính xác, tránh lỗi font hoặc mất ký tự.

Sau thao tác đọc dữ liệu, mọi phân tích, kiểm tra trực tiếp đều thực hiện trên bảng dữ liệu này, giúp đảm bảo tính nhất quán và chính xác cho toàn bộ quá trình phân tích thống kê tiếp theo.

2.1.2 Hiển thị kết quả các dòng đầu

head(data[, 1:10])
##   Time       TSNH100   TVCKTDT110   TVCKTDT111     CKTDT112   CKDTTCNH120
## 1 2015 2221373030144 420712811918  89510544052 331000000000  508000000000
## 2 2016 2747174092202 603188961343 270265069467 333000000000  704000000000
## 3 2017 2939184938924 549777216585  88442815647 461000000000  931000000000
## 4 2018 3147636450849  75835597431  75330296062    505301369 1460000000000
## 5 2019 3133924348700  70328408693  66628408693   3700000000 1770000000000
## 6 2020 3480799873619  73054473018  73054473018           NA 2070000000000
##     DTNGDNDH123    CKPTNH130   PTNHCKH131  TTCNBNH132
## 1  508000000000 644064122343 570830701600 23308107672
## 2  704000000000 692280925032 622748103096 23277764848
## 3  931000000000 799556214859 739281053856 21016649661
## 4 1460000000000 669787225237 618503855955 26841394656
## 5 1770000000000 560791995735 510101306774 42468675491
## 6 2070000000000 496020199024 414158635702 69081209633

Dòng lệnh (1) để hiển thị các giá trị dữ liệu của những dòng đầu tiên trong bảng dữ liệu vừa nhập.

Kết quả trả về gồm một bảng nhỏ với các cột: “Time”, “TSNH100”, “TVCKTĐT110”, “TVCKTĐT111”, “CKTĐT112”, … và các giá trị đi kèm.

Việc kiểm tra dữ liệu đầu bảng giúp xác thực dữ liệu đã nhập đúng định dạng, giá trị các biến đã hiển thị đúng và bảng dữ liệu không gặp lỗi kỹ thuật trong quá trình chuyển định dạng. Đây là thao tác giúp kiểm tra trực quan dữ liệu trước khi đi vào các phân tích chi tiết tiếp theo.

2.1.3 Hiển thị tổng số cột

ncol(data)
## [1] 131

Dòng lệnh (1) để hiện tổng số cột có trong bảng dữ liệu vừa nhập vào môi trường R.

Kết quả trả về là [10] 131, xác nhận bảng dữ liệu hiện tại có 1 cột là thời gian và 130 biến tài chính khác nhau đã được nhập thành công. Việc này giúp nhanh chóng kiểm tra tính đầy đủ của dữ liệu, phục vụ tốt cho các yêu cầu phân tích tiếp theo.

2.1.4 Hiển thị tổng số quan sát

nrow(data)
## [1] 10

Dòng lệnh (1) để hiển thị tổng số hàng trong bảng dữ liệu đã nhập vào R.

Kết quả trả về là [1] 10 , xác nhận bộ dữ liệu gồm 10 quan sát, mỗi quan sát tương ứng với một năm tài chính từ 2015 đến 2024. Việc kiểm tra số lượng hàng đảm bảo đúng quy mô chuỗi thời gian, giúp xác thực dữ liệu đầy đủ các năm cần nghiên cứu, từ đó thuận tiện cho các thao tác thống kê hơn.

2.1.5 Kiểm tra kiểu dữ liệu của từng biến

str(data, vec.len = 1)
## 'data.frame':    10 obs. of  131 variables:
##  $ Time            : int  2015 2016 ...
##  $ TSNH100         : num  2221373030144 ...
##  $ TVCKTDT110      : num  420712811918 ...
##  $ TVCKTDT111      : num  89510544052 ...
##  $ CKTDT112        : num  331000000000 ...
##  $ CKDTTCNH120     : num  508000000000 ...
##  $ DTNGDNDH123     : num  508000000000 ...
##  $ CKPTNH130       : num  644064122343 ...
##  $ PTNHCKH131      : num  570830701600 ...
##  $ TTCNBNH132      : num  23308107672 ...
##  $ PTVCVNH135      : num  37688828113 ...
##  $ PTNHK136        : num  23223854477 ...
##  $ DPPTNHKD137     : num  -10987369519 ...
##  $ HTK140          : num  639320555977 ...
##  $ HTK141          : num  642331928161 ...
##  $ DPGGHTK149      : num  -3011372184 ...
##  $ TSNHK150        : num  9670439906 ...
##  $ CPTTNH151       : num  3968455036 ...
##  $ TGDKT152        : num  4327687627 ...
##  $ TVCKKPTNN153    : num  1374297243 ...
##  $ TSNHK155        : int  0 NA ...
##  $ TSDH200         : num  1140000000000 ...
##  $ CKPTDH210       : num  0 ...
##  $ PTVCVDH215      : num  NA ...
##  $ PTDHK216        : int  0 NA ...
##  $ TSCD220         : num  1070000000000 ...
##  $ TSCDHH221       : num  811000000000 ...
##  $ NG222           : num  1250000000000 ...
##  $ GTHMLK223       : num  -441000000000 ...
##  $ TSCDVH227       : num  256000000000 ...
##  $ NG228           : num  274000000000 ...
##  $ GTHMLK229       : num  -17551971267 ...
##  $ BDSDT230        : num  NA NA ...
##  $ NG231           : num  NA NA ...
##  $ GTHMLK232       : num  NA NA ...
##  $ TSDDDH240       : num  15722551016 ...
##  $ CPXDCBDD242     : num  15722551016 ...
##  $ DTTCDH250       : num  15932055542 ...
##  $ DTVCTLK252      : chr  "4 523885342" ...
##  $ DTGVVDVK253     : chr  "27908170200" ...
##  $ DPDTTCDH254     : num  -16500000000 ...
##  $ TSDHK260        : num  42396882691 ...
##  $ CPTTDH261       : num  28312322417 ...
##  $ TSTTNHL262      : num  14084560274 ...
##  $ TTS270          : num  3360000000000 ...
##  $ NPT300          : num  842000000000 ...
##  $ NNH310          : num  780000000000 ...
##  $ PTNBNH311       : num  225000000000 ...
##  $ NMTTTNH312      : num  7079129950 ...
##  $ TVCKPNNSNN313   : num  13343506157 ...
##  $ PTNLD314        : chr  "128000000000" ...
##  $ CPPTNH315       : num  21683931132 ...
##  $ DTCTHNH318      : num  7747880222 ...
##  $ PTNHK319        : num  15300462313 ...
##  $ VNH320          : num  271000000000 ...
##  $ QKTPL322        : num  90763619653 ...
##  $ NDH330          : num  62330344795 ...
##  $ DPPTDH342       : num  31323948748 ...
##  $ QPTKHVCN343     : chr  "31 n06396047" ...
##  $ VCSH400         : num  2520000000000 ...
##  $ VCSH410         : num  2520000000000 ...
##  $ VGCCSH411       : num  872000000000 ...
##  $ CPPTCQBQ411a    : num  NA NA ...
##  $ TDVCP412        : chr  "" ...
##  $ QDTPT418        : num  1040000000000 ...
##  $ LNSTCPP421      : num  606000000000 ...
##  $ LCPPLKDCNT421a  : num  164000000000 ...
##  $ LCPPNN421b      : num  441000000000 ...
##  $ LÍCCDKKS429     : num  20323225971 ...
##  $ TNV440          : num  3360000000000 ...
##  $ DTBHVCCDV1      : num  4150000000000 ...
##  $ CKGTDT2         : num  -544000000000 ...
##  $ DTTVBHVCCDV10   : num  3610000000000 ...
##  $ GV11            : num  -2190000000000 ...
##  $ LNGVBHVCCDV20   : num  1410000000000 ...
##  $ DTHDTC21        : num  34338648064 ...
##  $ CPTC22          : num  -89481890058 ...
##  $ TDCPLV23        : num  -8730565082 ...
##  $ PLTCTLK24       : int  -910388172 -187904291 ...
##  $ CPBH25          : num  -458000000000 ...
##  $ CPQLDN26        : num  -262000000000 ...
##  $ LNTTHDKD30      : num  637000000000 ...
##  $ TNK31           : num  84857448081 ...
##  $ CPK32           : num  -20438693513 ...
##  $ LK40            : num  64418754568 ...
##  $ TLNKTTT50       : num  701000000000 ...
##  $ CPTTNDNHH51     : num  -109000000000 ...
##  $ CPTTNDNHL52     : num  66576717 ...
##  $ LNSTTNDN60      : num  593000000000 ...
##  $ LNSTCCTM61      : num  589000000000 ...
##  $ LNSTCCDKKS62    : num  3984211763 ...
##  $ LCBTCP70        : int  5748 6993 ...
##  $ LSGTCP71        : int  5748 NA ...
##  $ LNTT1           : num  701000000000 ...
##  $ KHTSCD2         : num  89670281622 ...
##  $ CKDP3           : chr  "3353858898" ...
##  $ LCLTGHDDDGL4    : num  NA ...
##  $ DTLTSCD5        : num  -2618121272 ...
##  $ TNTLVCT5        : num  -30674059705 ...
##   [list output truncated]

Kết quả dòng lệnh (1) hiển thị thông tin chi tiết về cấu trúc 10 biến đầu tiên. Lệnh list.len = 10 giúp giới hạn số biến hiển thị ở đầu kết quả, tránh hiển thị toàn bộ 131 biến gây dài và khó kiểm soát.

Mỗi biến đều hiển thị tên, kiểu dữ liệu ( “Time” là số nguyênint, các biến tài sản/tài chính là ký tự num được lưu dạng số) và một số giá trị mẫu đầu tiên của biến đó.

Thao tác này giúp nhanh chóng kiểm tra tên biến, phát hiện xem các giá trị trong biến có lỗi định dạng để lên kế hoạch chuyển đổi kiểu. Ta thấy, các biến số đều có dạng số nên không cần thực hiện chuyển đổi trước khi thực hiện các bước thống kê nữa.

2.1.6 Kiểm tra các giá trị duy nhất của biến thời gian

unique(data$Time)
##  [1] 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024

Dòng lệnh (1) dùng để liệt kê các giá trị duy nhất xuất hiện trong biến “Time” của bảng dữ liệu.

Kết quả trả về, xác nhận toàn bộ dữ liệu gồm 10 năm liên tiếp, không bị trùng lặp hoặc thiếu năm nào. Thao tác này giúp nhanh chóng kiểm tra tính đầy đủ, liên tục của biến thời gian, đóng vai trò kiểm soát logic dữ liệu đầu vào, đảm bảo chính xác về mặt chuỗi năm nghiên cứu khi thống kê và trực quan hóa sau này.

2.1.7 Kiểm tra dung lượng bộ nhớ dữ liệu sử dụng

object.size(data)
## 36408 bytes

Dòng lệnh (1) sử dụng để đo kích thước bộ nhớ mà bảng dữ liệu đang chiếm dụng trong môi trường làm việc R.

Kết quả trả về là 36408 bytes , tức gần 35.5 KB; điều này cho biết bảng dữ liệu tương đối nhỏ gọn, thuận tiện cho các thao tác xử lý, phân tích thống kê hoặc trực quan hoá mà không lo ngại các vấn đề về giới hạn bộ nhớ.

2.1.8 Kiểm tra có trùng lặp dòng không

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

Dòng lệnh này để kiểm tra số lượng dòng trùng lặp trong bảng dữ liệu.

Kết quả trả về xác nhận toàn bộ dữ liệu không có dòng nào trùng lặp. Mỗi năm là duy nhất, không bị lặp thông tin giữa các năm. Việc kiểm tra này giúp đảm bảo tính toàn vẹn dữ liệu, đồng thời đảm bảo tất cả các phân tích đều thực hiện trên các quan sát độc lập.

2.1.9 Kiểm tra biến thiếu dữ liệu

colSums(is.na(data))[colSums(is.na(data)) > 0]
##        CKTDT112    TVCKKPTNN153        TSNHK155      PTVCVDH215        PTDHK216 
##               5               1               9               7               1 
##        BDSDT230           NG231       GTHMLK232    CPPTCQBQ411a     LÍCCDKKS429 
##               3               3               3               2               3 
##       PLTCTLK24      LNSTCCTM61    LNSTCCDKKS62        LSGTCP71    LCLTGHDDDGL4 
##               4               3               3               7               1 
##        DTLTSCD5        TNTLVCT5         LTCTLK5   LTTLCKDTVCTC5         LTHDDT5 
##               7               8               8               9               3 
##      TQPTKHVCN7    TCDTGVVDVK25    THDTGVVDVK26     TTTNVGCCD31     TMLCPLCPQ32 
##               9               8               6               9               9 
## AHCTDTGHDQDNT61 
##               1

Dòng lệnh (1) dùng để xác định tổng số giá trị bị thiếu trên từng biến của bảng dữ liệu.

Kết quả xuất ra cho thấy đa số các biến không có giá trị thiếu, một số ít biến có số lượng NA (ví dụ biến CKTT112 dòng 1 có 5 giá trị NA), phản ánh rõ ràng biến đó không có số liệu đủ cho các năm hoặc chỉ xuất hiện ở một số năm nhất định.

Thao tác này giúp nhận diện chính xác các biến gặp vấn đề về missing value để lập kế hoạch xử lý dữ liệu thô: loại bỏ, nội suy, hoặc thay thế trước khi thực hiện các phân tích thống kê sau này.

2.1.10 Tra cứu ý nghĩa tên biến chính

  • TSNH100 : Tổng tài sản ngắn hạn

  • TVCKTDT110 : Tiền và các khoản tương đương tiền

  • TVCKTDT111 : Tiền gửi ngân hàng, tiền mặt

  • CKDTTCNH120 : Các khoản đầu tư tài chính ngắn hạn

  • CKPTNH130 : Các khoản phải thu ngắn hạn

  • PTNHCKH131 : Phải thu ngắn hạn của khách hàng

  • TTCNBNH132 : Trả trước cho người bán ngắn hạn

  • HTK140 : Hàng tồn kho, biến chính cần phân tích

  • DPGGHTK149 : Dự phòng giảm giá hàng tồn kho (rủi ro ứ đọng)

  • TSNHK150 : Tài sản ngắn hạn khác

2.2 Xử lý dữ liệu thô, mã hoá dữ liệu

2.2.1 Tạo vector và dataframe trên 10 biến đã chọn

cols_selected <- c("Time","TSNH100","TVCKTDT110","TVCKTDT111","CKDTTCNH120",
"CKPTNH130","PTNHCKH131","TTCNBNH132", "HTK140", "DPGGHTK149","TSNHK150")
data_10var <- data[, cols_selected]

Dòng lệnh (1) tạo ra một vector chứa tên 11 biến đã chọn từ bảng dữ liệu, bao gồm biến thời gian và 10 biến tài sản ngắn hạn, phải thu, trả trước, hàng tồn kho, dự phòng. Sử dụng hàm c() trong R để gom chung các tên biến lại thành một vector tên cols_selected.

Dòng lệnh (3) tạo ra một bảng dữ liệu mới tên là data_10var chỉ chứa 11 biến trên bằng cú pháp chọn cột theo tên vector cols_selected.

Việc tạo vector và dataframe mới giúp tổ chức xử lý dữ liệu có hệ thống, tập trung vào nhóm biến liên quan đến hàng tồn kho. Cách làm này giúp giảm sai sót khi làm việc với bộ dữ liệu lớn, đồng thời đảm bảo tính nhất quán, dễ kiểm soát chất lượng và thuận tiện cho các bước phân tích, chuyển đổi hay thống kê sau này.

2.2.2 Xóa dòng dữ liệu thiếu ở biến chính (hàng tồn kho - HTK130)

data_10var <- data_10var[!is.na(data_10var$HTK140), ]

Dòng lệnh (1) thực hiện bước loại bỏ tất cả các dòng dữ liệu bị thiếu giá trị ở biến Hàng tồn kho khỏi bảngdata_10var. Biểu thức !is.na(data_10var$HTK140), trả về TRUE cho những dòng mà biến HTK140 không bị thiếu, các dòng bị thiếu ở HTK140 sẽ bị loại bỏ hoàn toàn khỏi phân tích.

Việc lọc như vậy đảm bảo mọi phân tích, so sánh hoặc trực quan hóa tiếp theo về Hàng tồn kho đều được thực hiện trên dữ liệu đầy đủ, tránh sai sót thống kê hoặc gây sai lệch kết quả do giá trị khuyết.

2.2.3 Kiểm tra và loại dòng trùng lặp

data_10var <- data_10var[!duplicated(data_10var), ]

Dòng lệnh (1) thực hiện loại bỏ mọi dòng dữ liệu bị trùng trong bảng data_10var , chỉ giữ lại những dòng đầu tiên duy nhất cho mỗi trường hợp trùng lặp.

Kết quả là bảng dữ liệu sau khi xử lý chỉ còn các dòng duy nhất về tổ hợp 11 biến đã chọn (thời gian và các chỉ tiêu tài sản ngắn hạn/hàng tồn kho), đảm bảo chất lượng dữ liệu cho việc phân tích, thống kê tiếp theo.

Bước loại lỗi trùng lặp này rất quan trọng nhằm đảm bảo các kết quả phân tích không bị sai lệch do lặp thông tin, giúp tăng tính chính xác, độ tin cậy của các phép kiểm tra, so sánh giữa các thời điểm hoặc các biến trong báo cáo tài chính.

2.2.4 Tạo bản sao cho bước phân tổ

data_10var1 <- data_10var

Dòng lệnh này dùng để tạo một bản sao của bảng dữ liệu data_10var và gán cho tên mới là data_10var1.

Việc sao chép bảng này giúp thực hiện các phân tổ, chuyển đổi hoặc thao tác khác trên dữ liệu hoàn toàn tách biệt, đảm bảo dữ liệu gốc vẫn được giữ nguyên để đối chiếu hoặc kiểm soát chất lượng. Thuận tiện cho lưu trữ kết quả trung gian, đối chiếu hoặc quay lại so sánh ở các bước sau mà không làm thay đổi bảng dữ liệu gốc.

2.2.5 Tính tỷ lệ hàng tồn kho trên tổng tài sản ngắn hạn

data_10var1$TyLe_HTK_TSNH <- data_10var1$HTK140 / data_10var1$TSNH100

Dòng lệnh này tính toán tỷ lệ giữa giá trị Hàng tồn kho HTK130 so với Tổng tài sản ngắn hạn TSNH cho từng năm, tạo ra một cột mới tên là TyLe_HTK_TSNH trong bảng dữ liệu phân tích.

Cột này giúp đo lường mức độ hàng tồn kho chiếm bao nhiêu phần trăm trong tổng tài sản ngắn hạn của doanh nghiệp từng kỳ, đóng vai trò chỉ báo về hiệu quả quản trị chu chuyển vốn lưu động, khả năng kiểm soát tồn kho và rủi ro về hàng hóa ứ đọng.

2.2.6 Chuẩn hóa giá trị số Scale

num_cols <- sapply(data_10var1, is.numeric)
data_10var_scaled <- as.data.frame(scale(data_10var1[, num_cols]))

Dòng (1) để kiểm tra từng biến của bảng dữ liệu, trả về một vector logic (TRUE/FALSE) chỉ rõ cột nào là số học, cột nào là ký tự hoặc dạng khác. Dòng (2) để chuẩn hóa tất cả các cột số học đã xác định ở bước trên, chuyển về đơn vị chuẩn hóa (trung bình bằng 0, độ lệch chuẩn bằng 1).

Kết quả trả về là bảng dữ liệu mới gồm các biến số học sau khi chuẩn hóa, dưới dạng dataframe R, thuận tiện cho phân tích đa biến, so sánh tỷ lệ, hoặc trực quan hóa mà không bị ảnh hưởng bởi chênh lệch đơn vị gốc ban đầu giữa các biến.

2.2.7 Phân nhóm tồn kho theo 3 mức bằng nhau

data_10var1$HTK_Level <- cut(data_10var1$HTK140, breaks = 3, 
                             labels = c("Thap","Trung_Binh","Cao"))
data_10var1$HTK_Level
##  [1] Thap       Thap       Thap       Thap       Thap       Thap       Trung_Binh
##  [8] Cao        Cao        Trung_Binh
## Levels: Thap Trung_Binh Cao

Dòng lệnh (1) để phân nhóm giá trị hàng tồn kho thành 3 mức độ bằng nhau về mặt giá trị và tạo ra một biến phân loại mới tên HTK_Level trong bảng dữ liệu. Tham số breaks = 3 chia toàn bộ dải giá trị của hàng tồn kho thành 3 khoảng giá trị đều nhau.

Các nhóm được gán nhãn lần lượt là “Thap”, “Trung_Binh”, và “Cao”, giúp dễ dàng nhận diện các năm mà doanh nghiệp có mức tồn kho thấp, trung bình hoặc cao nhất tương đối với toàn bộ giai đoạn phân tích. Việc phân nhóm này thuận tiện cho việc so sánh, trực quan hóa xu hướng tồn kho, hoặc phân tích tác động của tồn kho theo mốc giá trị cụ thể.

2.2.8 Phân tổ hàng tồn kho theo nhóm tiền tương đương cao/thấp

library(dplyr)
median_TVCKTDT_val <- median(data_10var1$TVCKTDT110, na.rm = TRUE)
data_10var1 <- data_10var1 %>%
  mutate(Group_TVCKTDT = ifelse(data_10var1$TVCKTDT110 > median_TVCKTDT_val, 
                                "Cao", "Thap")) 
summary_table <- data_10var1 %>%
  group_by(Group_TVCKTDT) %>%
  summarise(Tonkho_median = median(HTK140, na.rm = TRUE), 
            Tonkho_max = max(HTK140, na.rm = TRUE))
summary_table
## # A tibble: 2 × 3
##   Group_TVCKTDT Tonkho_median    Tonkho_max
##   <chr>                 <dbl>         <dbl>
## 1 Cao            732860670514 1534636314655
## 2 Thap          1072605509022 1250833919138

Phần này để phân nhóm các năm theo giá trị tiền và các khoản tương đương tiền TVCKTDT110 cao hoặc thấp hơn trung vị, sau đó tổng hợp số liệu thống kê về hàng tồn kho HTK140 trong từng nhóm.

Dòng (2) tính giá trị trung vị của biến TVCKTDT110.Tại dòng (3), dữ liệu được gán thêm biến phân nhóm mới Group_TVCKTDT: năm nào có giá trị lớn hơn trung vị sẽ xếp vào nhóm “Cao”, ngược lại là nhóm “Thap”. Việc phân nhóm như vậy hỗ trợ soi chiếu tác động của lượng tiền và tương đương tiền đến chỉ tiêu tài chính khác. Cuối cùng, dòng (6) tính riêng cho mỗi nhóm giá trị trung vị và giá trị lớn nhất của hàng tồn kho, bảng summary_table.

Kết quả thống kê này cho phép nhận diện mối liên hệ giữa lượng tiền/khoản tương đương tiền và hàng tồn kho trong từng nhóm đảm bảo đầu ra minh bạch, thuận tiện cho việc phân tích sâu, trực quan hóa dữ liệu ở các bước tiếp theo.

2.2.9 Phân tổ kiểu ngưỡng nhiều biến thành chỉ tiêu “tam giác tài chính”

data_10var1$Triad <- ifelse(
  (data_10var1$HTK140 > median(data_10var1$HTK140, na.rm = TRUE)) &
  (data_10var1$TVCKTDT110 > median(data_10var1$TVCKTDT110, na.rm = TRUE)) &
  (data_10var1$DPGGHTK149 < median(data_10var1$DPGGHTK149, na.rm = TRUE)),
  "Tam_Giac_Tot","Khac")
table(data_10var1$Triad)
## 
##         Khac Tam_Giac_Tot 
##            9            1

Dòng lệnh này tạo biến phân loại Triad cho mỗi dòng trong bảng dữ liệu, dựa trên ba điều kiện kết hợp từ các chỉ tiêu tài chính:

  • Giá trị hàng tồn kho (HTK140) lớn hơn trung vị toàn bộ.

  • Giá trị tiền và các khoản tương đương tiền (TVCKTDT110) lớn hơn trung vị toàn bộ.

  • Giá trị dự phòng giảm giá hàng tồn kho (DPGGHTK149) nhỏ hơn trung vị toàn bộ.

Nếu tất cả các điều kiện này cùng đúng, năm đó được phân loại là “Tam_Giac_Tot”; ngược lại, gán nhãn “Khac”. Kết quả cho phép dễ dàng nhận diện những năm công ty vừa có hàng tồn kho lớn, vừa đảm bảo an toàn dòng tiền, vừa kiểm soát tốt rủi ro tồn kho.

Việc sử dụng hàm table(data_10var1$Triad) để đếm tần suất mỗi loại nhóm giúp kiểm tra phân bố các trường hợp đạt bộ ba điều kiện tốt so với các trường hợp khác, kết quả cho thấy chỉ có 1 năm đạt điều kiện.

Nhóm “Tam_Giac_Tot” phản ánh năm tài chính mà doanh nghiệp duy trì cùng lúc mức tồn kho cao, tiềm lực tài chính mạnh và kiểm soát tốt các khoản dự phòng tồn kho – được xem là trạng thái lý tưởng và ổn định về mặt quản trị vốn lưu động.

2.2.10 Phân tổ theo chỉ tiêu quay vòng vốn (TurnoverGroup)

data_10var1 <- data_10var1 %>% 
  mutate(turnover_proxy = HTK140 / TVCKTDT110) 
turnover_quart <- quantile(data_10var1$turnover_proxy, probs = seq(0, 1, 0.25), 
                           na.rm = TRUE)
data_10var1 <- data_10var1 %>% 
  mutate(TurnoverGroup = cut(turnover_proxy,breaks = turnover_quart, 
                             include.lowest = TRUE,
                             labels = c("VeryLow","Low","High","VeryHigh")))
  • Dòng (1) tính và tạo ra một biến mới turnover_proxy cho mỗi năm, bằng giá trị hàng tồn kho chia cho giá trị tiền và các khoản tương đương tiền. Đây là chỉ số tự xây dựng, dùng để tạm thời phản ánh mối quan hệ giữa lượng tồn kho doanh nghiệp với luồng tiền ngắn hạn.

  • Dòng lệnh (3) tính các giá trị phân vị tứ phân cho biến này bằng quantile(), chia dải giá trị thành 4 nhóm bằng nhau dùng cho phân tích phân tầng.

  • Sau đó, dòng (6) phân bổ các quan sát về 4 nhóm (VeryLow, Low, High, VeryHigh) tuỳ vào tỷ lệ tồn kho trên tiền mặt của từng năm.

Cách chia nhóm này giúp nhận diện những năm nào doanh nghiệp duy trì mức tồn kho cao vượt trội so với tiền, những năm nào cân bằng tốt, hay những năm tồn kho cực thấp so với thanh khoản. Việc phân nhóm theo chỉ số này thuận tiện cho việc mô tả, so sánh, trực quan hoá xu hướng vận động cơ cấu tài sản ngắn hạn, cảnh báo các năm doanh nghiệp có thể đối mặt với rủi ro “đọng vốn” tồn kho quá lớn, hỗ trợ các bước phân tích sau.

2.3 Thực hiện các thống kê cơ bản

2.3.1 Kiểm tra cấu trúc và loại dữ liệu của datafram mới

str(data_10var)
## 'data.frame':    10 obs. of  11 variables:
##  $ Time       : int  2015 2016 2017 2018 2019 2020 2021 2022 2023 2024
##  $ TSNH100    : num  2221373030144 2747174092202 2939184938924 3147636450849 3133924348700 ...
##  $ TVCKTDT110 : num  420712811918 603188961343 549777216585 75835597431 70328408693 ...
##  $ TVCKTDT111 : num  89510544052 270265069467 88442815647 75330296062 66628408693 ...
##  $ CKDTTCNH120: num  508000000000 704000000000 931000000000 1460000000000 1770000000000 ...
##  $ CKPTNH130  : num  644064122343 692280925032 799556214859 669787225237 560791995735 ...
##  $ PTNHCKH131 : num  570830701600 622748103096 739281053856 618503855955 510101306774 ...
##  $ TTCNBNH132 : num  23308107672 23277764848 21016649661 26841394656 42468675491 ...
##  $ HTK140     : num  639320555977 732860670514 633807876593 891486976436 725438891568 ...
##  $ DPGGHTK149 : num  -3011372184 -1696412765 -2456156179 -814326232 -1091103288 ...
##  $ TSNHK150   : num  9670439906 15112535313 25428487796 50804651745 9365052704 ...

Dòng lệnh dùng để kiểm tra nhanh cấu trúc tổng quan và kiểu dữ liệu của toàn bộ các biến trong data frame.

Bảng dữ liệu data_10var gồm 10 dòng ứng với 10 năm quan sát và 11 biến tài chính chính yếu, mỗi biến là một cột. Biến “Time” kiểu số nguyên, các biến tài sản, tiền, hàng tồn kho, dự phòng đều là kiểu số thực. Việc kiểm tra này xác nhận các biến đều đã đúng định dạng số, thuận tiện để thực hiện các phân tích thống kê, biểu đồ hoặc các thao tác tính toán số liệu mà không cần chuyển đổi kiểu dữ liệu nữa.

2.3.2 Thống kê giá trị bị thiếu

colSums(is.na(data_10var))
##        Time     TSNH100  TVCKTDT110  TVCKTDT111 CKDTTCNH120   CKPTNH130  PTNHCKH131 
##           0           0           0           0           0           0           0 
##  TTCNBNH132      HTK140  DPGGHTK149    TSNHK150 
##           0           0           0           0

Lệnh này kiểm tra số lượng bị thiếu ở mỗi biến của dataframe.

Tất cả các biến quan sát đều trả về giá trị 0 , đồng nghĩa không có bất kỳ giá trị thiếu nào (NA) trong dataframe data_10var. Điều này xác nhận dữ liệu đã đầy đủ, không cần thực hiện các thao tác xử lý missing value như loại bỏ, nội suy hay thay thế.

2.3.3 Tính giá trị lớn nhất và nhỏ nhất từng năm của biến hàng tồn kho

max(data_10var$HTK140, na.rm=TRUE)
## [1] 1534636314655
min(data_10var$HTK140, na.rm=TRUE)
## [1] 633807876593

Lệnh dòng (1) và dòng (2) lần lượt trả về giá trị lớn nhất và nhỏ nhất của biến hàng tồn kho trong toàn bộ dữ liệu. Đối số na.rm=TRUE giúp hàm bỏ qua các giá trị thiếu để chỉ tính trên các giá trị thực tế có trong dữ liệu.

Trong giai đoạn phân tích, giá trị hàng tồn kho của doanh nghiệp dao động mạnh: năm thấp nhất khoảng 634 tỷ đồng, năm cao nhất lên tới hơn 1.534 tỷ đồng. Sự chênh lệch này phản ánh rõ biến động về hoạt động sản xuất, tiêu thụ và khả năng kiểm soát hàng hóa lưu kho từng năm. Nếu liên tiếp xuất hiện các năm tồn kho cao, doanh nghiệp cần rà soát lại chính sách dự trữ, tiêu thụ để hạn chế rủi ro chiếm dụng vốn, trong khi các năm tồn kho thấp cho thấy hiệu quả thu hồi, tiêu thụ và kiểm soát kho hàng.

2.3.4 Tính giá trị trung bình cộng của biến hàng tồn kho

mean(data_10var$HTK140, na.rm=TRUE)
## [1] 942300540987

Dòng lệnh có chức năng tính giá trị trung bình cộng của biến hàng tồn kho trên toàn bộ mẫu dữ liệu báo cáo tài chính. Đối số na.rm=TRUE đảm bảo các giá trị thiếu sẽ bị bỏ qua, kết quả chỉ tính trên các năm có số liệu thực tế về tồn kho.

Giá trị kết quả là khoảng 942,3 tỷ đồng, thể hiện mức tồn kho bình quân của doanh nghiệp trong giai đoạn phân tích. Lệnh này giúp đánh giá quy mô tồn kho điển hình, làm cơ sở so sánh từng năm với mức bình quân chung hoặc các mốc mục tiêu quản lý hàng hóa, vốn lưu động theo chuẩn ngành hoặc chính sách doanh nghiệp.

2.3.5 Thống kê mô tả riêng cho biến HTK140

summary(data_10var$HTK140)
##          Min.       1st Qu.        Median          Mean       3rd Qu.          Max. 
##  633807876593  727294336304  859036203206  942300540987 1104723326748 1534636314655

Dòng lệnh dùng để tính toán và hiển thị thống kê mô tả cơ bản cho biến Hàng tồn kho (HTK140) trong bảng dữ liệu đã chọn. Đây là công cụ thống kê, cho phép tóm tắt nhanh các giá trị đặc trưng của một biến số: giá trị nhỏ nhất, tứ phân vị thứ nhất, số trung vị , trung bình cộng, tứ phân vị thứ ba, và giá trị lớn nhất.

Các chỉ số này cho thấy doanh nghiệp đã duy trì mức tồn kho từ khá thấp tới rất cao trong các năm qua. Có sự biến động về vận hành kho: Nếu duy trì mức quanh trung vị/1st Quartile là tối ưu về chi phí, hiệu quả sử dụng vốn. Những năm vượt hẳn giá trị trung bình hoặc max thì cần cảnh báo, vì tồn kho quá cao gây lãng phí vốn, phát sinh chi phí lưu kho, làm giảm hiệu quả kinh doanh. Quản trị hàng tồn kho tốt sẽ giúp doanh nghiệp linh hoạt trước biến động thị trường, tối ưu hóa hiệu suất sử dụng vốn, tăng khả năng sinh lời và giảm nguy cơ tài chính lâu dài.

2.3.6 Tính độ lệch chuẩn của từng biến

sapply(data_10var[, -1], sd, na.rm = TRUE)
##      TSNH100   TVCKTDT110   TVCKTDT111  CKDTTCNH120    CKPTNH130   PTNHCKH131 
## 807225528779 227055903063  66339203964 759356167348 101399339263 128628983671 
##   TTCNBNH132       HTK140   DPGGHTK149     TSNHK150 
##  52940182964 295679648195   1079365376  18250642439

Dòng lệnh thực hiện tính độ lệch chuẩn cho từng chỉ tiêu tài chính trong bảng dữ liệu. Độ lệch chuẩn phản ánh mức độ phân tán của các giá trị quanh trung bình; SD càng cao thì biến động càng mạnh qua các năm.

Hàng tồn kho có độ lệch chuẩn 295,679,648,195 đồng. Đây là mức biến động rất mạnh, cho thấy lượng hàng tồn kho trong các năm khác biệt lớn so với giá trị trung bình. Sự lên xuống này có thể đến từ chính sách nhập hàng, vận hành sản xuất hoặc biến động thị trường và cần quản trị chặt chẽ để kiểm soát rủi ro lưu động vốn.

Dự phòng giảm giá hàng tồn kho có độ lệch chuẩn 1,079,365,376 đồng. Biến này dao động rất nhỏ qua các năm, phản ánh doanh nghiệp duy trì dự phòng tương đối ổn định và không phát sinh biến động đột ngột. Điều này thường cho thấy khâu kiểm soát giá trị hàng tồn kho được thực hiện đều đặn và đúng quy định kế toán.

Việc chú ý các biến có SD cao cảnh báo về sự bất ổn hoặc rủi ro tiềm tàng, còn các biến SD thấp lại cho thấy tính ổn định và dự toán chính xác của doanh nghiệp.

2.3.7 Tính hệ số biến thiên (CV = SD/Mean)

cv <- sapply(data_10var[, -1], function(x) sd(x, na.rm=TRUE)/mean(x, na.rm=TRUE))
round(cv, 3)
##     TSNH100  TVCKTDT110  TVCKTDT111 CKDTTCNH120   CKPTNH130  PTNHCKH131  TTCNBNH132 
##       0.232       1.120       0.739       0.450       0.161       0.244       0.833 
##      HTK140  DPGGHTK149    TSNHK150 
##       0.314      -0.564       0.731

Lệnh dùng để tính hệ số biến thiên cho từng biến tài chính trong bảng dữ liệu.

Hệ số biến thiên là tỷ lệ giữa độ lệch chuẩn và giá trị trung bình của một biến; nó phản ánh mức độ biến động tương đối so với giá trị trung tâm của biến đó, thường dùng để so sánh mức ổn định hoặc rủi ro giữa các chỉ tiêu khác nhau có đơn vị hoặc quy mô khác nhau.

Hệ số biến thiên hàng tồn kho đạt 0.314 cho thấy mức độ biến động của tồn kho so với giá trị trung bình là vừa phải. Giá trị này phản ánh hàng tồn kho có dao động, nhưng vẫn bám quanh trung bình qua các năm, nên việc kiểm soát tồn kho nhìn chung khá ổn định.

Hệ số biến thiên rất cao, lên tới 1.120, điều này cho thấy giá trị tiền và các khoản tương đương tiền biến động mạnh so với trung bình, các năm có sự chênh lệch lớn. Đây là tín hiệu cho thấy doanh nghiệp có các kỳ với lượng tiền mặt, các khoản tương đương tiền tăng giảm mạnh, tiềm ẩn rủi ro về quản lý dòng tiền trong chu kỳ hoạt động.

Các biến có CV cao (>0.5) thể hiện mức thu nhập, tài sản, hoặc dòng tiền thiếu ổn định qua các năm, cần quan sát kỹ để quản lý rủi ro hoặc có chính sách cân đối phù hợp. Các biến có CV thấp (<0.4), như HTK140, thể hiện mức độ biến động thấp, quản trị hoặc vận hành ổn định qua chu kỳ kinh doanh doanh nghiệp. Lệnh này giúp các nhà phân tích nhanh chóng nhận diện các chỉ tiêu có xu hướng biến động mạnh về mặt giá trị tương đối, so sánh mức ổn định tài chính giữa các nhóm biến, hỗ trợ ra quyết định quản lý và cảnh báo rủi ro tài chính.

2.3.8 Tính ma trận tương quan giữa các biến tài chính

cor_matrix <- cor(data_10var[, -1], use = "complete.obs")
round(cor_matrix, 2)
##             TSNH100 TVCKTDT110 TVCKTDT111 CKDTTCNH120 CKPTNH130 PTNHCKH131
## TSNH100        1.00      -0.68      -0.41        0.92     -0.14      -0.41
## TVCKTDT110    -0.68       1.00       0.73       -0.86      0.63       0.68
## TVCKTDT111    -0.41       0.73       1.00       -0.58      0.41       0.47
## CKDTTCNH120    0.92      -0.86      -0.58        1.00     -0.44      -0.60
## CKPTNH130     -0.14       0.63       0.41       -0.44      1.00       0.90
## PTNHCKH131    -0.41       0.68       0.47       -0.60      0.90       1.00
## TTCNBNH132     0.63      -0.56      -0.45        0.63     -0.48      -0.80
## HTK140         0.90      -0.61      -0.31        0.75     -0.10      -0.42
## DPGGHTK149    -0.20      -0.28      -0.12        0.09     -0.59      -0.41
## TSNHK150       0.49      -0.25      -0.09        0.25      0.48       0.25
##             TTCNBNH132 HTK140 DPGGHTK149 TSNHK150
## TSNH100           0.63   0.90      -0.20     0.49
## TVCKTDT110       -0.56  -0.61      -0.28    -0.25
## TVCKTDT111       -0.45  -0.31      -0.12    -0.09
## CKDTTCNH120       0.63   0.75       0.09     0.25
## CKPTNH130        -0.48  -0.10      -0.59     0.48
## PTNHCKH131       -0.80  -0.42      -0.41     0.25
## TTCNBNH132        1.00   0.71       0.12     0.19
## HTK140            0.71   1.00      -0.34     0.66
## DPGGHTK149        0.12  -0.34       1.00    -0.39
## TSNHK150          0.19   0.66      -0.39     1.00

Dòng (1) để tính ma trận tương quan giữa các biến số tài chính trong bảng dữ liệu. Lệnh round(cor_matrix, 2) làm tròn các hệ số tương quan đến 2 chữ số thập phân để dễ đọc.

TSNH100 và CKĐTTCNH120: 0.92 – Tài sản ngắn hạn và đầu tư tài chính ngắn hạn cùng biến động mạnh, tức là khi tài sản ngắn hạn tăng thì đầu tư tài chính cũng tăng (do cùng thuộc nhóm vốn lưu động).

TSNH100 và TVCKTĐT110: -0.68 – Tài sản ngắn hạn và tiền mặt/tiền gửi biến động ngược nhau, chứng tỏ khi tăng đầu tư tài sản ngắn hạn thì tiền mặt giảm (dồn vốn đi đầu tư hoặc nhập hàng).

HTK140 và TSNH100: 0.90 – Tồn kho và tài sản ngắn hạn tăng giảm cùng chiều, cho thấy tồn kho chiếm tỷ trọng lớn trong vốn lưu động.

Hệ số dương thể hiện hai biến cùng tăng/giảm với nhau, hệ số âm nghĩa là một biến tăng thì biến kia giảm. Giúp xác định các chỉ tiêu tài chính nào có xu hướng vận động cùng chiều/quanh nhau qua từng năm, hoặc ngược chiều, từ đó hỗ trợ kiểm tra các giả thuyết, loại bỏ biến trùng lặp và lựa chọn biến khi xây dựng mô hình phân tích sâu.

2.3.9 Bảng tần suất tham chiếu các nhóm Turnover

table(data_10var1$TurnoverGroup)
## 
##  VeryLow      Low     High VeryHigh 
##        3        2        2        3

Dòng lệnh này để đếm số lần xuất hiện của từng mức trong biến phân loại TurnoverGroup của bảng dữ liệu data_10var1. Hàm table() trả về một bảng tần suất cho từng nhóm định tính trong biến, giúp biết có bao nhiêu năm ở mỗi mức hiệu suất quay vòng tồn kho: “VeryLow”, “Low”, “High”, “VeryHigh”. Kết quả cho thấy có 3 năm ở mức VeryLow, 2 năm ở mức Low, 2 năm ở mức High, 3 năm ở mức VeryHigh.

Điều này cho thấy doanh nghiệp trải đều các trạng thái hiệu quả sử dụng tồn kho — có những năm kiểm soát tồn kho và dòng tiền tốt, xen kẽ với những năm tồn kho trên tiền và khoản tương đương tiền tăng mạnh, cảnh báo về rối loạn chu kỳ quay vòng hoặc chính sách dự trữ biến động tùy chính sách sản xuất kinh doanh.

2.3.10 Bảng tần suất loại Triad

table(data_10var1$Triad)
## 
##         Khac Tam_Giac_Tot 
##            9            1

Dòng lệnh này để đếm số lần xuất hiện của từng loại trong biến phân loại Triad – nơi đã mã hoá năm tài chính nào thỏa đồng thời ba điều kiện “tam giác tài chính tốt” (hàng tồn kho cao, tiền/khoản tương đương tiền cao, dự phòng giảm giá tồn kho thấp).

Lệnh trả về một bảng tần suất, 9 năm thuộc nhóm “Khac” (không đạt đủ 3 điều kiện cùng lúc), chỉ 1 năm được phân loại là “Tam_Giac_Tot” (đáp ứng trạng thái lý tưởng).

Việc kiểm tra tần suất này cho phép đánh giá doanh nghiệp hiếm khi hội tụ đồng thời ba yếu tố mạnh về vốn lưu động. Kết quả nhấn mạnh rằng trạng thái lý tưởng là hiếm, còn đa phần các năm còn lại thiếu ít nhất một yếu tố chủ chốt trong quản trị tài chính toàn diện.

2.3.11 Tính hệ số biến thiên cho tỷ lệ

(sd(data_10var1$TyLe_HTK_TSNH, na.rm=TRUE)/mean(data_10var1$TyLe_HTK_TSNH, na.rm=TRUE))*100
## [1] 13.29062

Dòng lệnh này tính hệ số biến thiên cho tỷ lệ hàng tồn kho trên tổng tài sản ngắn hạn TyLe_HTK_TSNH trong bảng dữ liệu. Hệ số biến thiên đo mức độ dao động tương đối của một biến số, bằng tỉ lệ độ lệch chuẩn trên trung bình, rồi nhân 100 để ra %.

Cụ thể, giá trị CV trả về là khoảng 13,3% cho biết độ phân tán của tỷ lệ tồn kho so với giá trị trung bình của nó trong toàn bộ mẫu các năm. Nếu hệ số biến thiên thấp, dữ liệu ổn định, tỷ lệ tồn kho biến động ít; còn giá trị cao cho thấy tỷ lệ này biến động mạnh qua các kỳ tài chính. Trong trường hợp này, CV nằm ở mức thấp cho thấy quản trị tỷ lệ tồn kho trên tổng tài sản ngắn hạn của doanh nghiệp là ổn định, ít dao động qua các kỳ tài chính.

Lệnh này rất quan trọng khi phân tích tài chính vì nó giúp đánh giá mức độ ổn định hay biến động của các chỉ số quản trị quan trọng, hỗ trợ nhận diện xu hướng và kiểm soát rủi ro về quản trị vốn lưu động của doanh nghiệp.

2.3.12 So sánh trung bình hàng tồn kho theo nhóm tiền mặt (Group_TVCKTDT)

data_10var1 %>%
  group_by(Group_TVCKTDT) %>% 
  summarise(mean_HTK = mean(HTK140, na.rm=TRUE),
            median_HTK = median(HTK140, na.rm=TRUE),
            sd_HTK = sd(HTK140, na.rm=TRUE))
## # A tibble: 2 × 4
##   Group_TVCKTDT      mean_HTK    median_HTK        sd_HTK
##   <chr>                 <dbl>         <dbl>         <dbl>
## 1 Cao           886422478835   732860670514 377048257059.
## 2 Thap          998178603139. 1072605509022 216190285573.

Dòng lệnh này giúp so sánh số liệu hàng tồn kho (HTK140) giữa hai nhóm doanh nghiệp theo mức tiền và các khoản tương đương tiền (TVCKTĐT110) thấp hay cao hơn trung vị. Cụ thể, lệnh group_by(Group_TVCKTĐT) chia nhỏ các năm thành hai nhóm: ‘Cao’ khi giá trị tiền/khoản tương đương tiền lớn hơn trung vị toàn bộ giai đoạn; ‘Thap’ cho trường hợp ngược lại.

Kết quả cho thấy nhóm có nhiều tiền/khoản tương đương tiền Cao thì giá trị tồn kho trung bình và trung vị đều thấp hơn nhóm Thap. Điều này cho thấy doanh nghiệp kiểm soát dòng tiền tốt thường duy trì tồn kho ở mức khiêm tốn hơn, qua đó giảm nguy cơ vốn bị ứ đọng ở hàng hóa chậm luân chuyển. Độ lệch chuẩn ở nhóm Cao cao hơn so với nhóm Thap, hàm ý các năm trong nhóm này mức biến động tồn kho lớn hơn, khả năng do doanh nghiệp chủ động điều chỉnh tồn kho phù hợp dòng tiền mỗi giai đoạn.

Phân tích như vậy giúp đánh giá mối quan hệ giữa sự an toàn dòng tiền và chính sách quản trị tồn kho, là căn cứ quan trọng cho các quyết định tài chính hoặc sản xuất ở doanh nghiệp.

2.3.13 So sánh chênh lệch bằng kiểm định t-test

t.test(HTK140 ~ Group_TVCKTDT, data = data_10var1)
## 
##  Welch Two Sample t-test
## 
## data:  HTK140 by Group_TVCKTDT
## t = -0.57496, df = 6.3735, p-value = 0.585
## alternative hypothesis: true difference in means between group Cao and group Thap is not equal to 0
## 95 percent confidence interval:
##  -580693012274  357180763666
## sample estimates:
##  mean in group Cao mean in group Thap 
##       886422478835       998178603139

Dòng lệnh (1) thực hiện phép kiểm định t-test độc lập giữa hai nhóm Group_TVCKTĐT để kiểm tra xem trung bình HTK140 giữa hai nhóm có khác biệt có ý nghĩa thống kê hay không.

Giá trị p-value = 0.585 > 0.05, cho thấy không có sự khác biệt có ý nghĩa thống kê giữa trung bình hàng tồn kho của hai nhóm tiền mặt cao và thấp. Tuy nhiên, chênh lệch vẫn có thể mang ý nghĩa kinh tế – phản ánh xu hướng nhưng chưa đủ mạnh để kết luận về mặt xác suất.

2.3.14 Thống kê theo nhóm quay vòng vốn (TurnoverGroup)

data_10var1 %>%
  group_by(TurnoverGroup) %>%
  summarise(mean_HTK = mean(HTK140, na.rm=TRUE),
            median_HTK = median(HTK140, na.rm=TRUE),
            max_HTK = max(HTK140, na.rm=TRUE))
## # A tibble: 4 × 4
##   TurnoverGroup mean_HTK median_HTK       max_HTK
##   <fct>            <dbl>      <dbl>         <dbl>
## 1 VeryLow        6.69e11    6.39e11  732860670514
## 2 Low            7.76e11    7.76e11  826585429976
## 3 High           1.21e12    1.21e12 1534636314655
## 4 VeryHigh       1.15e12    1.12e12 1250833919138

Dòng lệnh này tổng hợp số liệu hàng tồn kho (HTK140) cho từng nhóm tốc độ quay vòng vốn (TurnoverGroup) – nhóm được tạo dựa trên tỷ lệ tồn kho so với tiền/khoản tương đương tiền, nhằm phục vụ phân tích, đánh giá hiệu quả quản trị hàng tồn kho ở nhiều mức độ khác nhau. Dòng lệnh (2) chia các dòng dữ liệu thành 4 nhóm (VeryLow, Low, High, VeryHigh) tùy vào tỷ lệ quay vòng vốn. Dòng (3) tính trung bình, trung vị, giá trị lớn nhất của hàng tồn kho trong từng nhóm.

  • Trong nhóm VeryLow, trung bình tồn kho 668 tỷ, trung vị 639 tỷ, giá trị lớn nhất733 tỷ. Doanh nghiệp năm này có hiệu quả quay vòng vốn thấp, vốn lưu động bị “chôn” ở kho, duy trì tồn kho ở mức thấp nhất, ít linh hoạt vốn nhưng rủi ro mất cơ hội kinh doanh.

  • Trong nhóm Low, trung bình/trung vị cùng 776 tỷ, lớn nhất 827 tỷ. Nhóm này bắt đầu cải thiện vận động vốn, tồn kho tăng hơn nhóm VeryLow nhưng vẫn kiểm soát tốt.

  • Trong nhóm High, trung bình/trung vị 1,213 tỷ, lớn nhất tới 1,534 tỷ. Năm thuộc nhóm này DN vận hành kho lớn, vốn quay vòng nhanh, chấp nhận rủi ro tồn kho cao để tối ưu vận động sản xuất hoặc đáp ứng hợp đồng lớn.

  • Trong nhóm VeryHigh, trung bình 1,146 tỷ, trung vị 1,115 tỷ, lớn nhất 1,250 tỷ. Các năm này DN đạt hiệu quả quay vòng vốn tốt nhất, dám duy trì mức tồn kho lớn với khả năng thanh toán linh hoạt, tăng tốc sản xuất và bán hàng.

Kết quả cho thấy: Hiệu quả quay vòng vốn càng cao thì DN càng dám “ôm kho” lớn, nhằm đáp ứng thị trường hoặc tận dụng tối đa lợi thế vận động vốn. Nhóm thấp lại an toàn, nhưng bỏ lỡ cơ hội bán hàng hoặc mở rộng quy mô. Dùng số liệu này để xác định chiến lược tồn kho cho từng năm, phòng ngừa rủi ro hoặc thúc đẩy tăng trưởng khi có điều kiện tài chính thuận lợi.

2.3.15 Tính tỷ lệ Dự phòng/Hàng tồn kho

data_10var1 <- data_10var1 %>%
  mutate(Ratio_DPG_HTK = DPGGHTK149 / HTK140)
summary(data_10var1$Ratio_DPG_HTK)
##       Min.    1st Qu.     Median       Mean    3rd Qu.       Max. 
## -0.0047103 -0.0026152 -0.0018342 -0.0021614 -0.0012715 -0.0008629

Dòng lệnh này tính tỷ lệ giữa khoản dự phòng giảm giá hàng tồn kho (DPGGHTK149) trên tổng giá trị hàng tồn kho (HTK140) cho từng năm trong bảng dữ liệu và lưu vào biến mới Ratio_DPG_HTK.

Dòng (3) sẽ thống kê phân vị, giá trị tối thiểu, tối đa cũng như số lượng giá trị thiếu (NA) của tỷ lệ này, cho cái nhìn tổng quan về mức độ dự phòng đối với từng giai đoạn.

Tất cả các giá trị đều là số âm, tức doanh nghiệp chủ yếu hoàn nhập dự phòng hoặc điều chỉnh giảm dự phòng so với tồn kho thực tế (thường xuất hiện khi bán hàng tồn lâu năm, hoặc ghi nhận hoàn nhập do hàng tồn bán được, không phải ghi nhận tăng dự phòng thêm). Giá trị tuyệt đối khá nhỏ, trung bình -0.0022, nghĩa là tỷ lệ dự phòng so với tồn kho trong từng năm là rất thấp (~0.2%). DN kiểm soát rủi ro giảm giá tồn kho rất tốt, không gây ảnh hưởng lớn tới chất lượng kho hàng hoặc hiệu quả vận hành chu kỳ vốn lưu động. Giá trị Min là năm đặc biệt phải hoàn nhập hoặc điều chỉnh dự phòng nhiều hơn các năm khác, giá trị Max là năm trích lập ít nhất (gần bằng không).

Nhìn chung, doanh nghiệp vận hành hàng tồn kho an toàn, ít rủi ro phải dự phòng giảm giá trị kho và giữ cho chi phí ghi nhận liên quan tới dự phòng ở mức thấp ổn định xuyên suốt cả chuỗi thời gian.

2.3.16 Tính tốc độ tăng trưởng hàng tồn kho qua các năm

data_10var1 <- data_10var1 %>%
  arrange(Time) %>%
  mutate(Growth_HTK = (HTK140 - lag(HTK140)) / lag(HTK140) * 100)
summary(data_10var1$Growth_HTK)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##  -27.32  -13.52   14.63    8.76   22.69   40.66       1

Dòng (2) sắp xếp dữ liệu theo thứ tự năm và tạo biến mới Growth_HTK,dòng(3) tính tỷ lệ tăng trưởng của hàng tồn kho (HTK140) so với năm trước đó. Dòng (3) trình bày các thống kê cơ bản (min, các phân vị, max, số lượng NA) cho biến tăng trưởng này, cho phép nhận diện mức tăng/giảm hàng tồn kho từng năm và xu hướng biến động tổng thể.

Kết quả cho thấy có năm doanh nghiệp giảm tồn kho rất lớn (giảm tới27%), thể hiện giai đoạn bán hàng mạnh, giải phóng vốn lưu động hoặc tối ưu quản trị kho. Nhiều năm ghi nhận tốc độ tăng trưởng âm, thể hiện kiểm soát nhập hàng hoặc bán hàng thuận lợi, doanh nghiệp không giữ quá nhiều vốn trong kho. Tuy nhiên, trung vị và trung bình lại dương (+14.6%, +8.8%), tức đa phần các năm doanh nghiệp có xu hướng tăng tồn kho dần – phát triển mở rộng quy mô hoặc tích trữ hàng để chuẩn bị cho sản xuất/ bán hàng. Những năm tăng vọt hơn22-40% cho thấy DN chủ động tích trữ lớn, dự báo chu kỳ kinh doanh thuận lợi hoặc chuẩn bị cho hợp đồng lớn.

Tốc độ tăng trưởng tồn kho qua các năm phản ánh chiến lược vận hành kho dài hạn, khả năng thích ứng thị trường, và cảnh báo năm cần kiểm soát để tránh rủi ro ‘ôm’ hàng quá nhiều hoặc thiếu nguồn cung cho sản xuất.

2.3.17 Hồi quy tuyến tính đơn giản giữa tồn kho và tiền mặt

model <- lm(HTK140 ~ TVCKTDT110, data=data_10var1)
summary(model)
## 
## Call:
## lm(formula = HTK140 ~ TVCKTDT110, data = data_10var1)
## 
## Residuals:
##           Min            1Q        Median            3Q           Max 
## -321919526333 -146128926033  -14626862679   96777314783  506167433476 
## 
## Coefficients:
##                       Estimate         Std. Error t value   Pr(>|t|)    
## (Intercept) 1103163357612.4468  107986363119.5538  10.216 0.00000724 ***
## TVCKTDT110             -0.7935             0.3651  -2.174     0.0615 .  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 248700000000 on 8 degrees of freedom
## Multiple R-squared:  0.3713, Adjusted R-squared:  0.2927 
## F-statistic: 4.724 on 1 and 8 DF,  p-value: 0.06148

Dòng lệnh (1) để xây dựng mô hình hồi quy tuyến tính đơn giản trong R, với biến phụ thuộc là hàng tồn kho (HTK140) và biến độc lập là tiền và các khoản tương đương tiền (TVCKTDT110).

Kết quả cho thấy khi tiền và tương đương tiền tăng thêm 1 đơn vị, hàng tồn kho giảm trung bình khoảng 0.79 đơn vị – nghĩa là quan hệ ngược chiều.

Mức ý nghĩa 0.06 cho thấy mối quan hệ này gần như có ý nghĩa thống kê, nhưng chưa đạt ngưỡng 5%.

R² ≈ 37% nghĩa là tiền mặt giải thích được khoảng 1/3 biến động hàng tồn kho, phần còn lại do các yếu tố khác (như khoản phải thu, sản lượng, nhu cầu thị trường…).

Vậy doanh nghiệp cần phải điều chỉnh tiền mặt và hàng tồn kho theo hướng ngược nhau để cân bằng vốn lưu động.

2.3.18 Phân phối dữ liệu kiểm tra bằng skewness và kurtosis

library(moments)
skewness(data_10var$HTK140, na.rm=TRUE)
## [1] 0.7537488
kurtosis(data_10var$HTK140, na.rm=TRUE)
## [1] 2.506124

Dòng lệnh (1), Skewness đo mức độ lệch của phân phối dữ liệu so với phân phối chuẩn.

  • Nếu > 0 → phân phối lệch phải (có một số năm tồn kho rất cao).

  • Nếu < 0 → phân phối lệch trái (một vài năm tồn kho rất thấp).

  • Nếu ≈ 0 → phân phối đối xứng.

Trong trường hợp này, Skewness = 0.75, tức là lệch phải nhẹ — tồn kho phần lớn tập trung quanh trung bình, nhưng có một vài năm giá trị cao đột biến.

Kurtosis (độ nhọn) đo mức độ tập trung của phân phối quanh trung bình.

  • Nếu ≈ 3 → tương tự phân phối chuẩn.

  • Nếu > 3 → phân phối nhọn hơn chuẩn (nhiều giá trị cực trị).

  • Nếu < 3 → phân phối dẹt hơn chuẩn (dữ liệu phân tán đều hơn).

Ở đây, Kurtosis = 2.51, nhỏ hơn 3 → phân phối hơi dẹt, nghĩa là tồn kho phân bố khá đều, không có quá nhiều giá trị cực trị.

Biến HTK140 có phân phối gần chuẩn, hơi lệch phải và dẹt nhẹ. Điều này cho thấy mức tồn kho của doanh nghiệp qua các năm không biến động quá cực đoan, nên có thể sử dụng các phương pháp thống kê tham số (như hồi quy tuyến tính) mà không cần biến đổi log. Tuy nhiên, do có chút lệch phải, khi dùng để so sánh hoặc mô hình hóa nâng cao, có thể chuẩn hóa hoặc log-transform để giảm ảnh hưởng của các năm có tồn kho quá lớn.

2.3.19 Hồi quy đa biến (tồn kho phụ thuộc vào nhiều biến)

model2 <- lm(HTK140 ~ TSNH100 + CKPTNH130 + TVCKTDT110 + DPGGHTK149, data=data_10var)
summary(model2)
## 
## Call:
## lm(formula = HTK140 ~ TSNH100 + CKPTNH130 + TVCKTDT110 + DPGGHTK149, 
##     data = data_10var)
## 
## Residuals:
##             1             2             3             4             5             6 
##    6190759731   93260134773 -104875315249  104351398129  -97257336114  -99168304228 
##             7             8             9            10 
##   51615830734  124653134303  110090882038 -188861184116 
## 
## Coefficients:
##                      Estimate        Std. Error t value Pr(>|t|)  
## (Intercept)  -3331521601.0986 475978440974.3438  -0.007   0.9947  
## TSNH100                0.2727            0.1063   2.566   0.0503 .
## CKPTNH130             -0.1680            0.8122  -0.207   0.8443  
## TVCKTDT110            -0.1862            0.4567  -0.408   0.7004  
## DPGGHTK149           -72.1290           63.6263  -1.134   0.3084  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 152300000000 on 5 degrees of freedom
## Multiple R-squared:  0.8527, Adjusted R-squared:  0.7348 
## F-statistic: 7.236 on 4 and 5 DF,  p-value: 0.02608

Dòng lệnh này xây dựng mô hình hồi quy đa biến, xem xét đồng thời ảnh hưởng của tổng tài sản ngắn hạn, khoản phải thu, tiền mặt và dự phòng đến tồn kho.

  • R² = 0.8527 ⇒ mô hình giải thích 85% biến động tồn kho, rất cao chứng minh mô hình phù hợp

  • TSNH100 có hệ số dương (0.2727), có ý nghĩa ở mức 10% → khi tổng tài sản ngắn hạn tăng, tồn kho cũng tăng.

  • TVCKTĐT110 âm (-0.1862), không ý nghĩa thống kê → mối quan hệ âm nhưng chưa chắc chắn.

  • DPGGHTK149 âm mạnh (-72.13) nhưng p = 0.3084 → tác động ngược chiều nhưng không đủ bằng chứng thống kê.

Mô hình cho thấy tổng tài sản ngắn hạn là yếu tố chính chi phối tồn kho, trong khi tiền mặt và dự phòng ảnh hưởng thứ yếu.

2.3.20 Tính tần suất xuất hiện các mức tồn kho

table(cut(data_10var$HTK140, breaks = 3, dig.lab = 13))
## 
## (632907048154.9,934084022613.7]  (934084022613.7,1234360168634] 
##                               6                               2 
##   (1234360168634,1535537143093] 
##                               2

Dòng lệnh này để phân chia giá trị hàng tồn kho (HTK140) thành 3 khoảng (buckets) đều nhau theo giá trị và dùng table() để tính tần suất xuất hiện các mức tồn kho trong toàn bộ dữ liệu của bảng tài chính.

Kết quả cho thấy phần lớn các năm, doanh nghiệp duy trì tồn kho ở mức thấp (6/10 năm dưới 934 tỷ), phản ánh hiệu quả kiểm soát, tiêu thụ ổn định và an toàn dòng vốn lưu động. Chỉ có 2 năm tồn kho ở mức trung bình và 2 năm ở mức cao (từ 934 tỷ đến hơn 1.530 tỷ), thường gắn với mở rộng sản xuất, biến động thị trường hoặc tích lũy đầu vào. Nhóm tồn kho cao cần giám sát thêm để hạn chế nguy cơ ứ đọng, giảm giá trị hàng hóa chưa tiêu thụ.

Bảng tần suất này là công cụ quan trọng hỗ trợ xây dựng mô hình dự báo, kiểm định hiệu quả quản trị kho và đánh giá sức khỏe tài chính doanh nghiệp.

2.4 Trực quan hoá dữ liệu

2.4.1 Violin plot + boxplot gộp (phân bố HTK140 theo nhóm thanh khoản)

Cho thấy cả hình dạng phân phối và giá trị trung vị của HTK140 trong từng nhóm.

library(ggplot2)
library(dplyr)
library(scales)
ggplot(data_10var1, aes(x = Group_TVCKTDT, y = HTK140 / 1e12, fill = Group_TVCKTDT)) +    
  geom_violin(trim = FALSE, alpha = 0.6) +                                                
  geom_boxplot(width = 0.1, fill = "white", outlier.color = "red") +                      
  labs(title = "Phân bố HTK140 theo nhóm thanh khoản (Violin + Boxplot)",                 
       x = "Nhóm thanh khoản", y = "Hàng tồn kho (nghìn tỷ đồng)") +
  scale_y_continuous(labels = scales::comma) +                                            
  theme_minimal(base_size = 14) +                                                         
  theme(plot.title = element_text(hjust = 0)) +
  scale_fill_manual(values = c("Cao" = "#FF3399", "Thap" = "#1E88E5")) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Dòng lệnh (5) thể hiện hình dạng toàn bộ phân phối hàng tồn kho từng nhóm thanh khoản bằng đường mật độ, giúp thấy dữ liệu dày đặc ở đâu, phân phối rộng/hẹp. Dòng lệnh (6) chèn boxplot bên trong violin; cho biết giá trị trung vị, tứ phân vị, và phát hiện điểm ngoại lệ mỗi nhóm. Dòng lệnh(12) thể hiện màu phân biệt nhóm “Cao” và “Thap” rõ ràng trên biểu đồ.

Biểu đồ violin cho thấy phân bố dày đặc của giá trị hàng tồn kho (HTK140) trong từng nhóm. Vùng phình rộng chứng tỏ nhiều năm có tồn kho ở mức đó, vùng thắt là năm ít xuất hiện ở mức giá trị đó. Boxplot ở giữa giúp xác định vị trí trung vị, tứ phân vị trên dưới và nhận biết sự phân tán, cũng như phát hiện giá trị bất thường (outlier đỏ).

So sánh hai nhóm “Cao” và “Thap”:

  • Nhóm “Thap” (thanh khoản thấp): đa số các năm duy trì tồn kho cao hơn, boxplot cao, giá trị trung vị/mean lớn và phân bố hẹp (ít dao động).

  • Nhóm “Cao” (thanh khoản tốt): tồn kho trung bình thường thấp hơn (“bụng violin” nằm thấp hơn), đoạn phân bố rộng hơn — tức là linh hoạt điều chỉnh tồn kho từng năm.

Xuất hiện giá trị ngoại lệ nổi bật ở nhóm “Cao”, cho thấy có năm DN tăng mạnh tồn kho để chuẩn bị cho yêu cầu đặc biệt, tạo outlier đỏ và “đuôi dài”. Biểu đồ cũng nhấn mạnh rủi ro hoặc sự linh hoạt tích cực trong chiến lược vốn lưu động — kho năm nào nguy hiểm.

2.4.2 Biểu đồ bar phân nhóm: Trung bình tồn kho theo nhóm HTK_Level

ggplot(data_10var1, aes(x = HTK_Level, y = HTK140/ 1e12, fill = HTK_Level)) +
  stat_summary(fun = mean, geom = "bar") +
  labs(title = "Trung bình tồn kho theo nhóm HTK_Level", y = "Trung bình tồn kho (nghìn tỷ đồng)", x = "Nhóm HTK") +
  theme_minimal() +
  scale_fill_brewer(palette = "Pastel2") +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Dòng lệnh (1) để vẽ biểu đồ cột so sánh giá trị trung bình hàng tồn kho (HTK140) giữa các nhóm đã phân loại (‘Thap’, ‘Trung_Binh’, ‘Cao’) theo HTK_Level. Trục x là nhóm tồn kho, trục y là tồn kho chia 1e12 (đơn vị nghìn tỷ đồng), màu sắc cũng theo nhóm. Dòng lệnh (2) tự động tính giá trị trung bình từng nhóm và vẽ cột tương ứng, giúp nhìn rõ khoảng cách giữa các nhóm. Việc sử dụng palette “Pastel2” làm màu sắc dịu dễ phân biệt.

Biểu đồ cho thấy việc tồn kho dồn mạnh về nhóm ’Cao, điều này phản ánh nguy cơ “chôn vốn” lớn trong hàng hóa, gây nhiều áp lực tài chính như tăng chi phí lưu kho, giảm hiệu quả sử dụng vốn, và gia tăng rủi ro giảm giá trị, hư hỏng hoặc lỗi thời sản phẩm.

Doanh nghiệp cần rà soát lại chiến lược lưu kho, giảm định mức tồn kho xuống mức hợp lý. Tăng cường dự báo nhu cầu bán hàng, tối ưu hóa chu kỳ mua/bán nhằm giải phóng vốn khỏi tồn kho dư thừa. Đồng thời cũng đẩy mạnh thúc đẩy tiêu thụ sản phẩm, ứng dụng công nghệ quản lý kho, phối hợp giữa các phòng ban để sớm phát hiện tồn kho gấp và kiểm soát biến động tồn kho theo sát thực tế.

2.4.3 Mối quan hệ giữa hai biến: tiền & chứng khoán đầu tư ngắn hạn và hàng tồn kho

ggplot(data_10var, aes(x = TVCKTDT110 / 1e12, y = HTK140 / 1e12)) +
  geom_point(color = "#2b8cbe", size = 3, alpha = 0.8) +
  geom_smooth(method = "lm", color = "red", linewidth = 1) +
  labs(title = "MQH giữa HTK140 và TVCKTĐT110",
       x = "TVCKTĐT110 (nghìn tỷ đồng)", y = "HTK140 (nghìn tỷ đồng)") +
  scale_x_continuous(labels = scales::comma) +
  scale_y_continuous(labels = scales::comma) +
  theme_bw(base_size = 14) +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Các điểm xanh là từng năm dữ liệu: mỗi điểm thể hiện mối quan hệ giữa số dư tiền mặt/tương đương tiền (TVCKTĐT110) và giá trị hàng tồn kho (HTK140) của doanh nghiệp.

Đường đỏ (hồi quy tuyến tính) nghiêng xuống, chứng tỏ giữa hai biến này có mối quan hệ ngược chiều. Khi tiền mặt tăng, tồn kho có xu hướng giảm, hoặc ngược lại – đúng như kết quả hệ số tương quan âm ở các phân tích trước đó.

Vùng xám (confidence interval) cho thấy khoảng tin cậy của xu hướng tuyến tính. Nếu vùng này hẹp, tốc độ biến động và dự báo xu hướng rất rõ ràng, nếu rộng thì mối quan hệ không chắc chắn mạnh.

Doanh nghiệp có chính sách luân chuyển vốn hiệu quả thường duy trì tồn kho hợp lý khi tiền mặt tăng mạnh (hoặc giảm bớt tồn kho khi dòng tiền về dồi dào). Ngược lại, khi doanh nghiệp cần tích trữ hàng (tồn kho tăng), thường thì lượng tiền mặt sẽ giảm đi để tài trợ cho nhập hàng.

Biểu đồ này thể hiện rõ xu hướng “dồn vốn” hoặc “giải phóng vốn” trong vận hành tài chính – giúp quản lý hoặc học viên nắm quy luật vận động vốn và hàng hóa doanh nghiệp qua các năm một cách trực quan.

2.4.4 Biểu đồ area chart (biểu đồ lớp phủ xu hướng tích lũy)

library(tidyr)
data_long <- data_10var1 %>% 
  select(Time, TSNH100, HTK140, TVCKTDT110) %>% 
  gather(key = "Indicator", value = "Value", -Time)
ggplot(data_long,aes(x = Time, y = Value / 1e12,fill = Indicator, group = Indicator)) +
  scale_x_continuous(breaks = unique(data_long$Time)) +
  geom_area(alpha = 0.7, color = "white", size = 1) +
  geom_line(size=1.2, aes(color=Indicator)) + 
  scale_fill_manual(values = c("TSNH100"="#D81B60", "HTK140"="#1E88E5", "TVCKTDT110"="#FFC400")) +
  scale_color_manual(values = c("TSNH100"="#AD1457", "HTK140"="#1565C0", "TVCKTDT110"="#FFA000")) +
  theme_minimal(base_size = 14) +
  labs(title = "Xu hướng tích lũy tài sản ngắn hạn, tồn kho, tiền",
       x = "Năm", y = "Giá trị (nghìn tỷ đồng)") +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Biểu đồ area chart (biểu đồ lớp phủ) trong hình này minh họa trực quan sự tích lũy và phân bổ theo thời gian của ba chỉ tiêu tài chính quan trọng: tài sản ngắn hạn (TSNH100), hàng tồn kho (HTK140) và tiền & tương đương tiền (TVCKTĐT110).

Mỗi màu sắc biểu diễn một biến tài chính, diện tích phủ lên nhau trực tiếp phản ánh động thái tích lũy (cộng dồn) từng phần cấu thành tổng tài sản lưu động của doanh nghiệp qua từng năm.

Đường biên giữa các lớp giúp người xem nhận biết rõ thành phần nào tăng, giảm, hay ổn định trong cấu trúc vốn ngắn hạn, và dễ dàng xác định thời điểm doanh nghiệp đẩy mạnh tích trữ tiền mặt, nhập kho hoặc mở rộng tài sản ngắn hạn nói chung.

2.4.5 Biểu đồ bậc thang hàng tồn kho qua các năm

ggplot(data_10var, aes(x = Time, y = HTK140 / 1e12)) +
  scale_x_continuous(breaks = unique(data_long$Time)) +
  scale_y_continuous(expand = expansion(mult = c(0.05, 0.18))) +
  geom_step(color="#D7263D", size=1.2, direction="vh") +
  geom_point(size=3, color="#0000FF", alpha=0.6) +
  theme_linedraw(base_size = 14) +
  labs(title = "Tồn kho qua các năm: Động học kiểu bậc thang",
       x = "Năm", y = "HTK140 (nghìn tỷ đồng)") +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Biểu đồ stepplot (bậc thang – động học kiểu bậc thang) này trực quan hóa rõ rệt từng pha chuyển động của giá trị hàng tồn kho (HTK140) qua các năm dưới dạng các “nấc thang”.

Đường đỏ bậc thang cho thấy các điểm chuyển đổi đột ngột của tồn kho giữa các năm, giúp nhận diện các bước nhảy giá trị lớn, năm tồn kho tăng/giảm bất thường hoặc các giai đoạn ổn định (khi đường song song trục hoành).

Các điểm xanh rải trên nấc thang, đi kèm nhãn trị số từng năm, minh họa cụ thể vị trí cũng như quy mô của mỗi pha.

Việc bậc thang đứng dốc diễn giải cho “bước ngoặt chiến lược” (doanh nghiệp tăng nhập kho, xả hàng hoặc thay đổi phương án sử dụng vốn), còn đoạn ngang gợi ý giai đoạn ổn định hoặc kiểm soát ngưỡng tồn kho hợp lý qua các quý/năm liên tục.

2.4.6 Biểu đồ polar (dạng bánh xe – radar/circular barplot)

summary_year_HTK <- data_10var1 %>%
  group_by(Time) %>%
  summarise(HTK = mean(HTK140, na.rm=TRUE) / 1e12)
ggplot(summary_year_HTK, aes(x = as.factor(Time), y = HTK, fill = HTK)) +
  geom_bar(stat = "identity", width = 1, color = "white", alpha=0.8) +
  scale_fill_gradient(low="#B2EBF2", high="#01579B") +
  coord_polar(start=0) +
  theme_void() +
  labs(title="Tổng quan tồn kho từng năm dưới dạng bánh xe") +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Biểu đồ bánh xe (circular barplot/radial bar) này trực quan hóa tổng quan giá trị hàng tồn kho (HTK140) từng năm trên hệ trục cực, qua đó tạo hình ảnh sinh động như các nan xe, mỗi nan ứng với một năm tài chính và chiều dài/màu sắc của từng nan phản ánh quy mô tồn kho tương ứng.

Mỗi thanh đứng hay “lát” hướng ra ngoài từ tâm vòng tròn, càng dài/lớn (màu đậm hơn) chứng tỏ năm đó tồn kho đạt mức cao vượt trội so với các năm còn lại.

Giá trị tồn kho của từng năm được gán trực tiếp trên đầu mỗi lát, giúp thuận tiện tra cứu, so sánh mà không phải nhìn bảng chi tiết.

2.4.7 Circular Dotplot (điểm rải hình tròn) HTK

ggplot(data_10var, aes(x=as.factor(Time), y=HTK140 / 1e12)) +
  geom_point(size=8, color="#00909e", alpha=0.8) +
  coord_polar() +
  theme_light(base_size=14) +
  theme(plot.title = element_text(hjust = 0.5)) +
  labs(title="Dotplot tròn tồn kho từng năm", x="Năm", y="HTK140 (nghìn tỷ đồng)")+
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Biểu đồ circular line (biểu đồ đường nối tròn) này minh họa sinh động diễn tiến giá trị hàng tồn kho (HTK140) của doanh nghiệp qua các năm trên một hệ trục tròn, qua đó chuyển tải cảm giác tuần hoàn và quy luật vận động giống như trên một “chiếc đồng hồ thời gian”.

Mỗi điểm trên vòng tròn là tồn kho của một năm, nối lại bằng đường cong, thể hiện rõ ràng từng mức tăng, đỉnh hoặc pha giảm mạnh.

Khi chuỗi năm được đặt trên hình tròn, chu kỳ phát triển, giai đoạn tăng trưởng mạnh hoặc điểm “đảo chiều” tồn kho được làm nổi bật hơn nhiều so với biểu đồ đường truyền thống.

Đặc biệt, cốt lõi của biểu đồ circular line là giúp người xem dễ nhận ra tính mùa vụ, vòng lặp vận hành tồn kho (nếu có) hoặc sự thay đổi đáng chú ý giữa các năm liên tiếp về mặt trực quan.

2.4.8 Biểu đồ xu hướng hàng tồn kho qua thời gian

ggplot(data_10var, aes(x = Time, y = HTK140 / 1e12, group = 1)) +
  geom_line(color = "#2b8cbe", linewidth = 1.2) +
  geom_point(color = "#FF8A80", size = 2) +
  labs(title = "Xu hướng hàng tồn kho",
       x = "Năm", y = "HTK140 (nghìn tỷ đồng)") +
  scale_x_continuous(breaks = unique(data_10var$Time)) +
  scale_y_continuous(labels = scales::comma, expand = expansion(mult = c(0, 0.15))) +
  theme_minimal(base_size = 14) +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Đường nối xanh mô tả diễn biến hàng tồn kho (HTK140) qua từng năm từ 2015 đến 2024: cho thấy xu hướng, biến động, các giai đoạn tăng - giảm của tồn kho trong suốt chuỗi thời gian.

Các điểm đỏ nhấn mạnh từng năm, phối hợp nhãn số cho thấy cụ thể giá trị từng năm (tính bằng nghìn tỷ đồng).

Giai đoạn 2015-2017: Tồn kho tương đối thấp và ổn định.

  • 2018 tăng vọt, sau đó có năm giảm (2019), rồi tăng lại: Biểu hiện chiến lược tăng nhập hàng chuẩn bị cho sản xuất/ hợp đồng lớn, rồi tối ưu lại cho phù hợp nhu cầu thực tế.

  • 2021-2023: Tồn kho tăng liên tục, đỉnh cao năm 2023; đây là thời kỳ doanh nghiệp “ôm kho lớn”, tiềm ẩn cả cơ hội (tăng trưởng, chủ động nguồn cung) và rủi ro (chôn vốn, chi phí lưu kho cao).

  • 2024: Bắt đầu giảm mạnh, thể hiện động thái chủ động giải phóng hàng tồn, cơ cấu lại vốn lưu động hoặc thích ứng thay đổi thị trường.

Việc gắn nhãn giúp dễ nhận biết từng năm đặc biệt, hỗ trợ trình bày, báo cáo, giải thích quyết định quản trị qua từng giai đoạn.

Biểu đồ này là minh hoạ trực quan tốt nhất để kể câu chuyện vận hành kho, cơ cấu vốn và thích ứng thị trường của doanh nghiệp qua các giai đoạn. Dựa vào xu hướng này, người quản lý hoặc nhà đầu tư có thể đánh giá hiệu quả chiến lược, phát hiện kịp thời những năm cần cảnh báo hoặc học hỏi mô hình kinh doanh phù hợp.

2.4.9 Biểu đồ Boxplot HTK140 theo nhóm thanh khoản

ggplot(data_10var1, aes(x = Group_TVCKTDT, y = HTK140 / 1e12, fill = Group_TVCKTDT)) +
  geom_boxplot(alpha = 0.7, outlier.color = "red") +
  geom_jitter(width = 0.1, color = "black", alpha = 0.6) +
  labs(title = "Phân bố HTK140 theo nhóm thanh khoản",
       x = "Nhóm TVCKTDT", y = "HTK140 (nghìn tỷ đồng)") +
  scale_y_continuous(labels = scales::comma) +
  scale_fill_manual(values = c("Cao" = "#64B5F6", "Thap" = "#FF8A80"),
                    labels = c("Thap" = "Thấp","Cao" = "Cao"),
                    name = "Nhóm thanh khoản") +
  theme_classic(base_size = 14)+
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Boxplot minh họa phân bố dữ liệu tồn kho từng nhóm năm:

Đường ngang đậm là median (trung vị tồn kho trong nhóm).

Hộp là vùng giữa quartile thứ nhất và thứ ba (50% năm nằm ở vùng này), càng ngắn càng ít biến động.

Các “chấm” ngoài hộp là outlier (năm tồn kho bất thường cao/thấp).

Các điểm jitter cho biết số lượng và vị trí các năm cụ thể (mỗi năm là một chấm đen), hỗ trợ đánh giá độ dày/thưa và nhận diện năm ngoại lệ.

So sánh hai nhóm:

  • Nhóm “Cao” có median thấp hơn rõ rệt, tồn kho tập trung hơn (vùng hộp thu hẹp), chỉ có một số năm tăng vọt là outlier.

  • Nhóm “Thap” median và hộp đều cao hơn, nhiều năm tồn kho lớn, phân bố trải rộng và có outlier nổi bật phía trên.

Kết quả nhấn mạnh: năm doanh nghiệp thanh khoản tốt thường kiểm soát tồn kho hiệu quả, giữ mức thấp ổn định, còn các năm kém thanh khoản thì tồn kho duy trì cao – nhiều năm còn xuất hiện tồn kho vượt mức điển hình. Biểu đồ này là minh họa trực quan cho hiệu quả quản trị vốn ngắn hạn và năng lực vận hành kho theo từng giai đoạn thực tế của doanh nghiệp.

2.4.10 Barplot tổ hợp theo năm và nhóm tồn kho (HTK_Level)

library(ggplot2)
ggplot(data_10var1, aes(x = Time, y = HTK140 / 1e12, fill = HTK_Level)) +
    scale_fill_manual(values = c("Thap" = "#D81B60","Trung_Binh" = "#1E88E5", 
                                 "Cao" = "#FFA000"),
                      labels = c("Thap" = "Thấp","Trung_Binh" = "Trung Bình",
                                 "Cao" = "Cao"),
                      name = "Mức tồn kho") +
  scale_x_continuous(breaks = unique(data_long$Time)) +
  geom_bar(stat = "identity", position = "dodge") +
  labs(title = "Hàng tồn kho từng năm theo mức nhóm",
       x = "Năm", y = "Hàng tồn kho (nghìn tỷ đồng)") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Biểu đồ cột này giúp trực quan hóa rõ ràng sự thay đổi giá trị tồn kho từng năm, phân bổ theo ba nhóm mức: Thấp, Trung Bình, Cao.

Các năm 2021–2024 chuyển sang mức tồn kho Trung Bình và Cao, cho thấy xu hướng tích lũy tài sản tồn kho tăng nhanh ở giai đoạn cuối chuỗi thời gian, cảnh báo doanh nghiệp về hiệu quả sử dụng vốn lưu động và tiềm năng rủi ro ứ đọng tài sản.

Màu sắc khác biệt của mỗi nhóm làm nổi bật chu kỳ biến động tồn kho, hỗ trợ so sánh nhanh và nhận diện giai đoạn nào có chính sách tồn kho hợp lý hoặc cần điều chỉnh chiến lược quản trị tồn kho.

2.4.11 Boxplot đa nhóm: Phân phối tỷ lệ dự phòng/HTK theo TurnoverGroup

ggplot(data_10var1, aes(x = TurnoverGroup, y = Ratio_DPG_HTK, fill = TurnoverGroup)) +
  geom_boxplot() +
  scale_fill_manual(
    values = c("VeryLow"  = "#90CAF9","Low"= "#81C784","High"= "#FFD54F", 
               "VeryHigh" = "#E57373"),
    name = "Nhóm Turnover",labels = c("VeryLow"  = "Rất Thấp","Low"= "Thấp",
                                      "High"     = "Cao","VeryHigh" = "Rất Cao")) +
  labs(title = "Phân phối tỷ lệ Dự phòng/HTK theo nhóm Turnover",
       x = "Nhóm Turnover", y = "Tỷ lệ Dự phòng/HTK") +
  theme_minimal() +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Boxplot này giúp trực quan hóa sự phân phối tỷ lệ dự phòng giảm giá hàng tồn kho so với hàng tồn kho (Ratio_DPG_HTK) theo từng nhóm tốc độ quay vòng tồn kho (TurnoverGroup).

Các nhóm turnover có sự khác biệt rõ về vị trí trung tâm (median), độ phân tán và mức độ rủi ro: nhóm “Rất Thấp” có tỷ lệ trừ dự phòng/HTK thấp nhất và biến động mạnh nhất; nhóm “Rất Cao” có tỷ lệ trung bình cao hơn, biến động hẹp hơn, phản ánh mức độ dự phòng rủi ro tồn kho tăng theo tỷ lệ quay vòng tồn kho.

Chỉ tiêu này rất quan trọng khi đánh giá chính sách trích lập dự phòng tài sản tồn kho, kiểm soát rủi ro ứ đọng, minh bạch tài chính từng nhóm doanh nghiệp, là cơ sở để tối ưu hóa chính sách quản trị hàng tồn kho và vốn lưu động doanh nghiệp.

2.4.12 Biểu đồ trung bình hàng tồn kho theo TurnoverGroup

data_10var1 %>%
  group_by(TurnoverGroup) %>%
  summarise(mean_HTK = mean(HTK140, na.rm=TRUE)) %>%
  ggplot(aes(x = TurnoverGroup, y = mean_HTK / 1e12, fill = TurnoverGroup)) +
  geom_col(alpha = 0.8) +
  geom_text(aes(label = round(mean_HTK / 1e12, 1)), vjust = -0.5) +
  labs(title = "Trung bình hàng tồn kho theo TurnoverGroup",
       x = "Nhóm Turnover", y = "HTK140 trung bình (nghìn tỷ đồng)") +
  scale_y_continuous(labels = scales::comma, expand = expansion(mult = c(0, 0.1))) +
  scale_fill_manual(values = c("VeryLow"="#F9D5E5", "Low"="#97DFFC", 
                               "High"="#FEB144", "VeryHigh"="#FFF9B0")) +
  theme_light(base_size = 14) +
  theme(plot.title = element_text(hjust = 0.5, margin = margin(t = 20))) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

4 cột màu thể hiện 4 nhóm TurnoverGroup dựa trên chỉ tiêu quay vòng vốn proxy, mỗi cột là giá trị trung bình hàng tồn kho của các năm nằm trong nhóm đó.

VeryLow (0.7 nghìn tỷ): Hiệu quả quay vòng vốn thấp, tồn kho trung bình ở mức thấp nhất. Doanh nghiệp thận trọng, ít chôn vốn vào kho.

Low (0.8 nghìn tỷ): Bắt đầu cải thiện vận động vốn, tồn kho tăng hơn nhóm thấp nhưng vẫn kiểm soát tốt.

High (1.2 nghìn tỷ): Nhóm này tồn kho trung bình lớn nhất, chấp nhận rủi ro “ôm kho” để tận dụng vận động vốn nhanh.

VeryHigh (1.1 nghìn tỷ): Cũng duy trì mức tồn kho cao, doanh nghiệp chủ động mở rộng nguồn hàng để đáp ứng thị trường hoặc dự phòng sản xuất.

Biểu đồ này giúp quản lý và nhà đầu tư thấy rõ mối liên hệ giữa hiệu quả quay vòng vốn và chính sách tồn kho. Khi doanh nghiệp vận hành vốn tốt (Turnover cao), thường dám duy trì tồn kho lớn. Ngược lại nếu quay vòng vốn kém, luôn giữ tồn kho thấp để giảm rủi ro. Đây là cách trình bày trực quan hiệu quả cho quyết định quản trị chuỗi cung ứng và dòng tiền DN.

2.4.13 Biểu đồ ma trận tương quan giữa 3 biến HTK140, TVCKTĐT110, PTNHCKH131

library(reshape2)
cor_matrix <- cor(data_10var[, c("HTK140","TVCKTDT110","PTNHCKH131")], use="complete.obs")
melt(cor_matrix) %>%
  ggplot(aes(Var1, Var2, fill = value)) +
  geom_tile() +
  geom_text(aes(label = round(value,2)), color = "black", size = 9) +
  scale_fill_gradientn(
    colours = c("#FADADD", "#FEB144", "#97DFFC", "#64B5F6" ),
    values = scales::rescale(c(-0.5, 0, 0.5, 1))) +
  labs(title = "Ma trận tương quan", x = "", y = "") +
  theme_minimal(base_size = 14)+
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Các ô màu thể hiện mức độ tương quan tuyến tính giữa từng cặp biến:

Gần 1 (xanh đậm): Tương quan thuận rất mạnh, hai biến tăng/giảm cùng nhau (vd: HTK140 và chính nó hoặc các biến cùng hệ số cao).

Gần 0 (màu trung tính, nhạt): Ít liên hệ, thay đổi của biến này không nói lên biến kia.

Gần -0.5 (cam): Tương quan ngược chiều khá mạnh, khi một biến tăng, biến kia thường giảm.

Ví dụ từ biểu đồ:

HTK140 và TVCKTĐT110: `-0.61 — tồn kho tăng, tiền thanh khoản giảm và ngược lại.

HTK140 và PTNHCKH131:-0.42 — tồn kho tăng thì khoản phải thu khách hàng thường giảm.

TVCKTĐT110 và PTNHCKH131:+0.68 — tiền mặt tăng cùng với khoản phải thu khách hàng ngắn hạn.

Biểu đồ này trực quan hóa sức mạnh và hướng các mối quan hệ tài chính chủ chốt: nhà quản lý có thể nhận biết nhanh các chỉ tiêu tài chính nào “tác động cùng chiều” hay ngược chiều, dễ kiểm soát rủi ro hoặc tối ưu vận hành. Ma trận này là nền tảng để xây dựng các mô hình tài chính đa biến hoặc đưa ra cảnh báo sớm về vận động vốn, tồn kho trong DN.

2.4.14 Phân bố tiền và chứng khoán đầu tư ngắn hạn

ggplot(data_10var, aes(x = TVCKTDT110 / 1e12)) +
  geom_density(fill = "#FF8A80", color ="red"  , alpha = 0.7) +
  labs(title = "Phân bố tiền và chứng khoán đầu tư ngắn hạn",
       x = "TVCKTDT110 (nghìn tỷ đồng)", y = "Mật độ(nghìn tỷ đồng)") +
  scale_x_continuous(labels = scales::comma) +
  theme_light(base_size = 14) +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Biểu đồ mật độ (density plot) cho biết phân bố số của TVCKTĐT110 (tiền & CK đầu tư ngắn hạn) qua chuỗi thời gian.

Đỉnh của biểu đồ là vùng xuất hiện với tần suất cao nhất — số lượng năm có giá trị đó chiếm ưu thế.

Vùng thấp/xuôi là năm có giá trị xuất hiện ít, phân bố rải rác.

Đường cong lệch về trái cho thấy đa số các năm có giá trị thấp, chỉ một số năm tiền/CK đầu tư lên tới mức cao (đuôi dài bên phải).

Doanh nghiệp phần lớn duy trì tiền/đầu tư ngắn hạn ở mức thấp, chỉ có ít năm “dồn vốn” lên cao (chuẩn bị cho dự án lớn hoặc thay đổi chiến lược dòng tiền).

Phân bố như vậy phản ánh mô hình vận hành tập trung an toàn, ít rủi ro “ôm tiền nhàn rỗi” hoặc đầu tư ngắn hạn quá lớn một lúc.

Biểu đồ này giúp quản lý dự báo và kiểm tra các năm “bất thường” về dòng tiền, đồng thời ra quyết định cân đối lại vốn lưu động cho các năm tiếp theo.

2.4.15 Biểu đồ cột: Tần suất các nhóm Turnover

library(ggplot2)
ggplot(data_10var1, aes(x = TurnoverGroup, fill = TurnoverGroup)) +
  geom_bar() +
  labs(title = "Tần suất các nhóm Turnover",
       x = "Nhóm Turnover", y = "Số năm") +
  theme_minimal() +
  scale_fill_brewer(palette = "Set2") +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Biểu đồ này thể hiện rõ tần suất xuất hiện các nhóm Turnover qua 10 năm, cho phép nhìn nhận trực quan sự phân bổ các nhóm tốc độ quay vòng tồn kho trong lịch sử DHG.

Nhóm “VeryLow” và “VeryHigh” đều xuất hiện ba lần, thể hiện doanh nghiệp có giai đoạn tồn kho biến động mạnh ở hai cực: rất thấp và rất cao. Nhóm “Low” và “High” chiếm tỷ trọng trung bình, chỉ xuất hiện hai lần mỗi nhóm, cho thấy sự luân phiên giữa các chính sách/quy trình quản trị tồn kho ở DHG trong các giai đoạn khác nhau.

Thông tin này có giá trị cho kiểm soát, dự báo và tối ưu hóa chu kỳ tồn kho, giúp đánh giá mức độ ổn định/thay đổi của quản trị vốn lưu động trong từng chu kỳ sản xuất – kinh doanh.

2.4.16 Biến động HTK140 qua thời gian theo nhóm Turnover

ggplot(data_10var1, aes(x = Time, y = HTK140 / 1e12, group = TurnoverGroup, color = TurnoverGroup)) +
  geom_line(linewidth = 1.2) +
  geom_point(size = 2) +
  facet_wrap(~TurnoverGroup) +
  labs(title = "Biến động HTK140 theo nhóm Turnover",
       x = "Năm", y = "HTK140 (nghìn tỷ đồng)") +
  scale_x_continuous(breaks = seq(min(data_10var1$Time), 
                                  max(data_10var1$Time), by = 2)) +
  theme_minimal(base_size = 14) +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Biểu đồ chia thành 4 ô nhỏ (facet) tương ứng 4 nhóm TurnoverGroup: VeryLow, Low, High, VeryHigh.

Mỗi ô thể hiện diễn biến tồn kho (HTK140) qua từng năm cho các năm nằm trong nhóm đó, giúp nhìn rõ xu hướng đặc thù của từng nhóm vận hành vốn.

Nhóm VeryLow/Low: Tồn kho luôn ở mức thấp, thay đổi không mạnh qua các năm, doanh nghiệp thận trọng, ít “ôm kho”.

Nhóm High/VeryHigh: Tồn kho lớn, đặc biệt nhóm High còn có năm tăng vọt giá trị, nhóm VeryHigh có biến động rõ theo chu kỳ; doanh nghiệp linh hoạt hoặc chấp nhận rủi ro vốn để nắm bắt cơ hội thị trường.

Thể hiện rõ sự khác biệt xu hướng tồn kho giữa các nhóm hiệu quả quay vòng vốn, hỗ trợ so sánh quản trị kho và đề xuất chính sách vận hành hợp lý cho từng trạng thái doanh nghiệp.

Biểu đồ này giúp nhà quản lý, phân tích tài chính nhận diện những năm, những nhóm vận hành vốn nào có xu hướng tích cực, chủ động hay thận trọng với tồn kho — từ đó quyết định kiểm soát rủi ro hoặc thúc đẩy tăng trưởng kho phù hợp từng hoàn cảnh doanh nghiệp.

2.4.17 Biểu đồ cột tần suất nhóm tồn kho (Bar chart theo cut nhóm)

data_10var1 <- data_10var %>% 
  mutate(HTK_group = cut(HTK140, breaks = 3, labels = c("Thap", "TrungBinh", "Cao")))
ggplot(data_10var1, aes(x = HTK_group, fill = HTK_group)) +
  geom_bar(alpha = 0.8, width = 0.7) +
  labs(title = "Tần suất số năm theo nhóm giá trị hàng tồn kho",
       x = "Nhóm tồn kho (thấp – trung bình – cao)", y = "Số năm") +
  theme_minimal(base_size = 14) +
  scale_fill_manual(values = c("Thap"="#B3E5FC", "TrungBinh"="#81D4FA", 
                               "Cao"="#0288D1")) +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(
    plot.title   = element_text(size = 36, hjust = 0.5),
    axis.title   = element_text(size = 32),
    axis.text    = element_text(size = 28),
    legend.title = element_text(size = 28),    
    legend.text  = element_text(size = 28),
    strip.text   = element_text(size = 30)
  )

Biểu đồ cho thấy trực tiếp doanh nghiệp đã có bao nhiêu năm thuộc nhóm tồn kho thấp, trung bình, cao, tính dựa theo giá trị thực tế chia đều làm 3 nhóm.

Cực kỳ hữu ích để xác định liệu doanh nghiệp có thiên về vận hành an toàn (nhiều năm tồn kho thấp), thường xuyên “ôm kho” (nhiều năm ở mức cao) hay phân phối đều.

Hiển thị số lượng (label) mỗi cột giúp dễ đọc kết quả và làm rõ tỉ trọng từng nhóm trong tổng số năm phân tích.

Biểu đồ này là công cụ phân tích risk management, tối ưu kho và kiểm soát chu kỳ tài chính – đặc biệt thích hợp khi trình bày cho ban quản trị hoặc thuyết trình slide so sánh nhóm.

Kết luận

Quá trình phân tích dữ liệu báo cáo tài chính Công ty Cổ phần Dược Hậu Giang (DHG) qua một thập niên (2015–2024) đã mang lại nhiều phát hiện quan trọng. Dữ liệu gốc gồm hơn 130 biến số tài chính chủ đạo đã được chuẩn hóa, xử lý sạch và trích rút những nhóm chỉ tiêu trọng tâm phục vụ cho việc đánh giá cấu trúc tài chính, hiệu quả hoạt động cũng như quản trị vốn lưu động của doanh nghiệp.

Thông qua các bước làm sạch, chuẩn hóa, mã hóa và phân nhóm biến số, bộ dữ liệu đã trở nên “sẵn sàng phân tích” cho mọi mục tiêu thống kê mô tả, kiểm định, phân tích chuỗi thời gian và mô hình hóa tài chính hiện đại. Các chỉ tiêu như tỷ lệ hàng tồn kho so với tài sản ngắn hạn, mức độ biến động tồn kho qua từng năm, hay phân nhóm doanh nghiệp theo các ngưỡng chỉ tiêu tài chính, được tạo lập một cách trực quan, minh bạch và thuận tiện cho mọi thao tác tiếp theo.

Kết quả cho thấy dữ liệu tài chính có độ tin cậy cao, không bị thiếu hụt hoặc trùng lặp, cho phép người phân tích rút ra các nhận xét có giá trị thực tiễn: thấy rõ xu hướng sử dụng vốn lưu động, hiệu quả quản trị tồn kho, tiềm năng tăng trưởng cũng như rủi ro tài chính trong từng giai đoạn lịch sử. Nền tảng dữ liệu và kỹ năng xử lý qua R cũng mở rộng khả năng ứng dụng, từ dự báo, phân tích năng lực cạnh tranh, cho đến hoạch định chiến lược tài chính tại doanh nghiệp.

Phân tích này nhấn mạnh vai trò thiết yếu của dữ liệu sạch, quy trình chuẩn hóa và tuyến tính hóa các chỉ tiêu tài chính trong việc cung cấp “bức tranh toàn diện” về sức khỏe tài chính doanh nghiệp — là cơ sở không thể thiếu cho mọi quyết định quản trị, kiểm toán, cũng như phát triển năng lực phân tích số liệu tài chính chuyên nghiệp trong kỷ nguyên số hiện nay.