PHẦN 1. TRÌNH BÀY BỘ DỮ LIỆU VỀ CÁC CHUYẾN BAY TRONG 2 THÁNG ĐẦU NĂM 2024

CHƯƠNG 1. GIỚI THIỆU BỘ DỮ LIỆU

1.1. Giới thiệu tổng quan

Tên bộ dữ liệu: Flight Delay Dataset 2024
Nguồn bộ dữ liệu: https://www.kaggle.com/datasets/hrishitpatil/flight-data-2024
Thông tin bộ dữ liệu: Bộ dữ liệu này chứa thông tin chi tiết về hiệu suất chuyến bay và độ trễ cho các chuyến bay nội địa vào năm 2024, được hợp nhất từ các tệp BTS TranStats hàng tháng thành một bộ dữ liệu được làm sạch duy nhất. Nó cung cấp thông tin toàn diện về thời gian bay theo lịch trình và thực tế, sự chậm trễ, hủy chuyến, chuyển hướng và khoảng cách giữa các sân bay. Bộ dữ liệu phù hợp cho phân tích dữ liệu khám phá (EDA), các tác vụ học máy như dự đoán độ trễ, phân tích chuỗi thời gian và nghiên cứu hiệu suất của hãng hàng không/sân bay.
Mục đích lấy dữ liệu: Dữ liệu được dùng để xử lý, làm sạch và phân tích bộ dữ liệu nhằm hiểu rõ đặc điểm của các chuyến bay.

flight_data <- read.csv("C:/Users/TO UYEN/Downloads/flight_data.csv")

1.2. Kích thước bộ dữ liệu

dim(flight_data)
## [1] 1048575      26

Giải thích:
- dim(): dùng để kiểm tra kích thước của bộ dữ liệu.
- Giá trị đầu tiên là số hàng trong bảng dữ liệu, tức là số quan sát hay số chuyến bay.
- Giá trị thứ hai là số cột trong bảng dữ liệu, tức là số biến hay đặc điểm được ghi nhận cho mỗi chuyến bay.

Nhận xét:
- Bộ dữ liệu có 1.048.575 quan sát và 26 biến.

1.3. Tên và ý nghĩa của các biến

 data_dict <- data.frame(
  "Tên biến" = c(
    "month", "day_of_month", "day_of_week", "fl_date", "op_unique_carrier",
    "op_carrier_fl_num", "origin", "origin_city_name", "origin_state_nm",
    "dest", "dest_city_name", "dest_state_nm", "crs_dep_time", "dep_time",
    "dep_delay", "taxi_out", "wheels_off", "wheels_on", "taxi_in",
    "crs_arr_time", "arr_time", "arr_delay", "crs_elapsed_time",
    "actual_elapsed_time", "air_time", "distance"),
  "Ý nghĩa" = c(
    "Tháng trong năm, cho biết chuyến bay vào tháng nào trong năm.",
    "Ngày trong tháng, cho biết ngày bay vào ngày nào trong tháng.",
    "Ngày trong tuần, cho biết chuyến bay vào ngày nào trong tuần.",
    "Ngày bay.",
    "Mã hãng hàng không thực hiện chuyến bay.",
    "Số hiệu chuyến bay của hãng.",
    "Mã sân bay khởi hành.",
    "Tên thành phố khởi hành.",
    "Tên bang hoặc khu vực sân bay khởi hành.",
    "Mã sân bay đến.",
    "Tên thành phố đến.",
    "Tên bang hoặc khu vực sân bay đến.",
    "Giờ dự kiến cất cánh (theo lịch).",
    "Giờ thực tế cất cánh.",
    "Độ trễ cất cánh (phút), giá trị âm nếu cất cánh sớm hơn dự kiến.",
    "Khoảng thời gian máy bay di chuyển từ cổng ra tới vị trí chuẩn bị cất cánh trên đường băng.",
    "Thời điểm bánh xe rời mặt đất.",
    "Thời điểm bánh xe chạm đất tại sân bay đến.",
    "Khoảng thời gian máy bay di chuyển trên mặt đất từ khi vừa hạ cánh cho đến khi dừng hẳn tại cổng.",
    "Giờ dự kiến hạ cánh (theo lịch).",
    "Giờ thực tế hạ cánh.",
    "Độ trễ hạ cánh (phút), giá trị âm nếu hạ cánh sớm hơn dự kiến.",
    "Thời gian dự kiến bay (phút).",
    "Thời gian bay thực tế (phút).",
    "Thời gian bay trên không (phút).",
    "Khoảng cách giữa sân bay đi và đến (dặm)."))
library(knitr)
kable(data_dict, col.names = c("Tên biến",  "Ý nghĩa"))
Tên biến Ý nghĩa
month Tháng trong năm, cho biết chuyến bay vào tháng nào trong năm.
day_of_month Ngày trong tháng, cho biết ngày bay vào ngày nào trong tháng.
day_of_week Ngày trong tuần, cho biết chuyến bay vào ngày nào trong tuần.
fl_date Ngày bay.
op_unique_carrier Mã hãng hàng không thực hiện chuyến bay.
op_carrier_fl_num Số hiệu chuyến bay của hãng.
origin Mã sân bay khởi hành.
origin_city_name Tên thành phố khởi hành.
origin_state_nm Tên bang hoặc khu vực sân bay khởi hành.
dest Mã sân bay đến.
dest_city_name Tên thành phố đến.
dest_state_nm Tên bang hoặc khu vực sân bay đến.
crs_dep_time Giờ dự kiến cất cánh (theo lịch).
dep_time Giờ thực tế cất cánh.
dep_delay Độ trễ cất cánh (phút), giá trị âm nếu cất cánh sớm hơn dự kiến.
taxi_out Khoảng thời gian máy bay di chuyển từ cổng ra tới vị trí chuẩn bị cất cánh trên đường băng.
wheels_off Thời điểm bánh xe rời mặt đất.
wheels_on Thời điểm bánh xe chạm đất tại sân bay đến.
taxi_in Khoảng thời gian máy bay di chuyển trên mặt đất từ khi vừa hạ cánh cho đến khi dừng hẳn tại cổng.
crs_arr_time Giờ dự kiến hạ cánh (theo lịch).
arr_time Giờ thực tế hạ cánh.
arr_delay Độ trễ hạ cánh (phút), giá trị âm nếu hạ cánh sớm hơn dự kiến.
crs_elapsed_time Thời gian dự kiến bay (phút).
actual_elapsed_time Thời gian bay thực tế (phút).
air_time Thời gian bay trên không (phút).
distance Khoảng cách giữa sân bay đi và đến (dặm).

Giải thích:
- data_dict: là bảng dữ liệu mới.
- data.frame(): dùng để tạo một bảng dữ liệu dạng khung, mỗi cột là một vector.
- kable(): dùng để hiển thị bảng dưới dạng đẹp và dễ đọc.

Nhận xét:
- Bảng kết quả giúp người đọc hiểu rõ vai trò của từng biến.

1.4. Kiểu dữ liệu của các biến

1.4.1. Kiểu dữ liệu của từng biến

sapply(flight_data, typeof)
##               month        day_of_month         day_of_week             fl_date 
##           "integer"           "integer"           "integer"         "character" 
##   op_unique_carrier   op_carrier_fl_num              origin    origin_city_name 
##         "character"           "integer"         "character"         "character" 
##     origin_state_nm                dest      dest_city_name       dest_state_nm 
##         "character"         "character"         "character"         "character" 
##        crs_dep_time            dep_time           dep_delay            taxi_out 
##           "integer"           "integer"           "integer"           "integer" 
##          wheels_off           wheels_on             taxi_in        crs_arr_time 
##           "integer"           "integer"           "integer"           "integer" 
##            arr_time           arr_delay    crs_elapsed_time actual_elapsed_time 
##           "integer"           "integer"           "integer"           "integer" 
##            air_time            distance 
##           "integer"           "integer"

Giải thích:
- sapply(flight_data, typeof): kiểm tra kiểu dữ liệu của từng cột trong bảng flight_data.
- typeof(): dùng để kiểm tra kiểu dữ liệu.

Nhận xét:
- Các biến định lượng (integer) như: month, day_of_month, day_of_week, op_carrier_fl_num, crs_dep_time, dep_time, dep_delay,… được dùng để tính trung bình, so sánh, đo lường, vẽ biểu đồ phân bố (histogram, boxplot).
- Các biến định tính (character) như: fl_date, op_unique_carrier, origin, origin_city_name, origin_state_nm, dest, dest_city_name,… dùng để mô tả, phân loại, vẽ bar chart hoặc pie chart.

1.4.2. Số lượng theo từng kiểu dữ liệu

table(sapply(flight_data, typeof))
## 
## character   integer 
##         8        18

Giải thích:
- sapply(flight_data, typeof): kiểm tra kiểu dữ liệu của từng cột trong bảng flight_data.
- table(): đếm tần suất xuất hiện của mỗi kiểu dữ liệu.

Nhận xét:
- Bộ dữ liệu có 8 biến thuộc kiểu character và 18 biến thuộc kiểu integer.

1.5. Số quan sát bị trùng lặp

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

Giải thích:
- duplicated(flight_data): dùng để kiểm tra các dòng trùng lặp trong bảng dữ liệu.
+ Nếu kết quả là TRUE thì có sự trùng lặp với dòng trước đó.
+ Nếu kết quả là FALSE thì không có sự trùng lặp với dòng trước đó.
- sum(): dùng để đếm tổng giá trị TRUE.

Nhận xét:
- Không có quan sát bị trùng lặp trong bộ dữ liệu vì tổng số dòng dữ liệu bị trùng lặp bằng 0.

1.6. Xem 10 dòng đầu của bộ dữ liệu

library(knitr)
kable(head(flight_data, 10))
month day_of_month day_of_week fl_date op_unique_carrier op_carrier_fl_num origin origin_city_name origin_state_nm dest dest_city_name dest_state_nm crs_dep_time dep_time dep_delay taxi_out wheels_off wheels_on taxi_in crs_arr_time arr_time arr_delay crs_elapsed_time actual_elapsed_time air_time distance
1 1 1 01/01/2024 9E 4814 JFK New York, NY New York DTW Detroit, MI Michigan 1252 1247 -5 31 1318 1442 7 1508 1449 -19 136 122 84 509
1 1 1 01/01/2024 9E 4815 MSP Minneapolis, MN Minnesota CLE Cleveland, OH Ohio 1015 1001 -14 20 1021 1249 6 1325 1255 -30 130 114 88 622
1 1 1 01/01/2024 9E 4817 JFK New York, NY New York RIC Richmond, VA Virginia 1415 1411 -4 21 1432 1533 8 1601 1541 -20 106 90 61 288
1 1 1 01/01/2024 9E 4817 RIC Richmond, VA Virginia JFK New York, NY New York 1650 1643 -7 13 1656 1747 12 1841 1759 -42 111 76 51 288
1 1 1 01/01/2024 9E 4818 DTW Detroit, MI Michigan MKE Milwaukee, WI Wisconsin 1015 1010 -5 21 1031 1016 4 1034 1020 -14 79 70 45 237
1 1 1 01/01/2024 9E 4822 JAX Jacksonville, FL Florida LGA New York, NY New York 1410 1403 -7 14 1417 1559 4 1627 1603 -24 137 120 102 833
1 1 1 01/01/2024 9E 4822 LGA New York, NY New York JAX Jacksonville, FL Florida 955 947 -8 26 1013 1218 13 1244 1231 -13 169 164 125 833
1 1 1 01/01/2024 9E 4823 CHS Charleston, SC South Carolina LGA New York, NY New York 1140 1135 -5 8 1143 1309 5 1338 1314 -24 118 99 86 641
1 1 1 01/01/2024 9E 4823 LGA New York, NY New York CHS Charleston, SC South Carolina 815 810 -5 14 824 1005 8 1044 1013 -31 149 123 101 641
1 1 1 01/01/2024 9E 4828 ITH Ithaca/Cortland, NY New York JFK New York, NY New York 1300 1248 -12 12 1300 1343 12 1419 1355 -24 79 67 43 189

Giải thích:
- head(flight_data, 10): dùng để hiển thị 10 dòng đầu tiên của bộ dữ liệu.
- kable(): dùng để hiển thị bảng dưới dạng đẹp và dễ đọc.

Nhận xét:
- Việc quan sát 10 dòng đầu giúp hiểu cấu trúc dữ liệu trước khi phân tích thống kê.

1.7. Xem 10 dòng cuối của bộ dữ liệu

library(knitr)
kable(tail(flight_data, 10))
month day_of_month day_of_week fl_date op_unique_carrier op_carrier_fl_num origin origin_city_name origin_state_nm dest dest_city_name dest_state_nm crs_dep_time dep_time dep_delay taxi_out wheels_off wheels_on taxi_in crs_arr_time arr_time arr_delay crs_elapsed_time actual_elapsed_time air_time distance
1048566 2 29 4 29/02/2024 AA 1610 JFK New York, NY New York MIA Miami, FL Florida 730 732 2 15 747 1029 8 1041 1037 -4 191 185 162 1089
1048567 2 29 4 29/02/2024 AA 1611 PHX Phoenix, AZ Arizona LAS Las Vegas, NV Nevada 825 824 -1 18 842 827 6 839 833 -6 74 69 45 255
1048568 2 29 4 29/02/2024 AA 1612 DCA Washington, DC Virginia MIA Miami, FL Florida 1351 1406 15 24 1430 1642 15 1641 1657 16 170 171 132 919
1048569 2 29 4 29/02/2024 AA 1612 MIA Miami, FL Florida DCA Washington, DC Virginia 959 1007 8 21 1028 1243 5 1238 1248 10 159 161 135 919
1048570 2 29 4 29/02/2024 AA 1613 MIA Miami, FL Florida SJU San Juan, PR Puerto Rico 1004 1001 -3 23 1024 1325 4 1344 1329 -15 160 148 121 1045
1048571 2 29 4 29/02/2024 AA 1613 SJU San Juan, PR Puerto Rico MIA Miami, FL Florida 1455 1447 -8 24 1511 1640 7 1646 1647 1 171 180 149 1045
1048572 2 29 4 29/02/2024 AA 1614 PHX Phoenix, AZ Arizona MIA Miami, FL Florida 2255 59 124 12 111 647 3 502 650 108 247 231 216 1972
1048573 2 29 4 29/02/2024 AA 1615 JAX Jacksonville, FL Florida CLT Charlotte, NC North Carolina 2018 2014 -4 20 2034 2128 10 2143 2138 -5 85 84 54 328
1048574 2 29 4 29/02/2024 AA 1616 CLT Charlotte, NC North Carolina MCI Kansas City, MO Missouri 900 1104 124 12 1116 1210 7 1033 1217 104 153 133 114 808
1048575 2 29 4 29/02/2024 AA 1616 MCI Kansas City, MO Missouri CLT Charlotte, NC North Carolina 1123 1307 104 13 1320 1559 22 1428 1621 113 125 134 99 808

Giải thích:
- tail(flight_data, 10): dùng để hiển thị 10 dòng cuối cùng của bộ dữ liệu.
- kable(): dùng để hiển thị bảng dưới dạng đẹp và dễ đọc.

Nhận xét:
- Việc quan sát 10 dòng cuối giúp kiểm tra tính đầy đủ, phát hiện các dữ liệu bị thiếu ở cuối bảng trước khi tiến hành phân tích thống kê.

1.8. Tính số lượng hãng bay khác nhau

length(unique(flight_data$op_unique_carrier))
## [1] 15

Giải thích:
- flight_data$op_unique_carrier: truy cập vào cột “op_unique_carrier” trong bảng dữ liệu. - unique(): dùng để xác định có bao nhiêu hãng bay khác nhau .
- length(): dùng để đếm tổng số hãng bay duy nhất xuất hiện.

Nhận xét:
- Bộ dữ liệu có 15 hãng hàng không khác nhau thực hiện chuyến bay trong tháng 1 và tháng 2.

1.9. Tính số lượng chuyến bay mỗi tháng

table(flight_data$month)
## 
##      1      2 
## 547271 501304

Giải thích:
- flight_data$month: truy cập vào cột “month” trong bảng dữ liệu.
- table(): dùng để đếm số lần xuất hiện của từng giá trị khác nhau trong một biến.

Nhận xét:
- Từ kết quả ta thấy có 547.271 chuyến bay vào tháng 1 và 501.304 chuyến bay vào tháng 2.

1.10. Tính tổng số chuyến bay theo điểm đến

library(dplyr)
library(DT)
datatable(as.data.frame(table(flight_data$dest)),
          colnames = c("Điểm đến", "Số chuyến bay"),
          caption = "Tổng số chuyến bay theo điểm đến")

Giải thích:
- table(flight_data$dest): đếm số chuyến bay của từng điểm đến.
- as.data.frame(): chuyển kết quả thành bảng.
- datatable(): hiển thị ở bảng đẹp, có phân chia thành nhiều trang.

Nhận xét:
- Số chuyến bay của các điểm khác có sự chênh lệch lớn cho thấy mức độ hoạt động không đồng đều giữa các sân bay, phản ánh sự khác biệt về như cầu đi lại hoặc tầm quan trọng của từng sân bay.

CHƯƠNG 2: XỬ LÝ THÔ VÀ MÃ HÓA DỮ LIỆU

2.1. Phát hiện số quan sát bị thiếu

Kiểm tra xem trong toàn bộ bảng dữ liệu có tồn tại giá trị bị thiếu (NA) ở bất kỳ vị trí nào hay không:

any(is.na(flight_data))
## [1] TRUE

Giải thích:
- is.na(): dùng để kiểm tra từng ô trong bộ dữ liệu xem có bị thiếu giá trị hay không.
+ Kết quả là TRUE nếu ô đó bị thiếu (NA).
+ Kết quả là FALSE nếu ô đó có giá trị.
- any(): dùng để kiểm tra xem toàn bộ dữ liệu có ít nhất một giá trị TRUE hay không.
+ Kết quả là TRUE nếu có ít nhất một giá trị bị thiếu.
+ Kết quả là FALSE nếu không có giá trị nào bị thiếu.

Nhận xét:
- Vì kết quả trả về là TRUE nên bộ dữ liệu có ít nhất một ô dữ liệu bị thiếu (NA) ở bất kỳ hàng, cột nào.
Ta thực hiện đếm số lượng giá trị bị thiếu (NA) ở từng biến.

colSums(is.na(flight_data))
##               month        day_of_month         day_of_week             fl_date 
##                   0                   0                   0                   0 
##   op_unique_carrier   op_carrier_fl_num              origin    origin_city_name 
##                   0                   0                   0                   0 
##     origin_state_nm                dest      dest_city_name       dest_state_nm 
##                   0                   0                   0                   0 
##        crs_dep_time            dep_time           dep_delay            taxi_out 
##                   0               22553               22650               23125 
##          wheels_off           wheels_on             taxi_in        crs_arr_time 
##               23125               23677               23677                   0 
##            arr_time           arr_delay    crs_elapsed_time actual_elapsed_time 
##               23675               25751                   1               25751 
##            air_time            distance 
##               25751                   0

Giải thích:
- colsums(is.na()): dùng để tính tổng số giá trị TRUE (NA) trong từng cột.

Nhận xét:
- Các cột như: month, day_of_month, day_of_week, fl_date, op_unique_carrier, op_carrier_fl_num, origin, origin_city_name,… có giá trị 0. Đây là các cột không bị thiếu dữ liệu.
- Các cột như: dep_time, dep_delay, taxi_out, Wheels_off, Wheels_on, taxi_in, arr_time, arr_delay, crs_elapsed_time, actual_elapsed_time, air_time có nhiều giá trị bị thiếu. Nghĩa là có nhiều chuyến bay không có thông tin về giờ hoặc thời gian thực tế.

2.2. Xử lý giá trị thiếu

Vì các biến bị thiếu không phải là biến trọng yếu và số lượng giá trị thiếu ở các biến là nhỏ, không ảnh hưởng đến tổng thể dữ liệu nên phương pháp xử lý NA phù hợp nhất là loại bỏ các dòng bị thiếu.

flight_data_clean <- na.omit(flight_data)
colSums(is.na(flight_data_clean))
##               month        day_of_month         day_of_week             fl_date 
##                   0                   0                   0                   0 
##   op_unique_carrier   op_carrier_fl_num              origin    origin_city_name 
##                   0                   0                   0                   0 
##     origin_state_nm                dest      dest_city_name       dest_state_nm 
##                   0                   0                   0                   0 
##        crs_dep_time            dep_time           dep_delay            taxi_out 
##                   0                   0                   0                   0 
##          wheels_off           wheels_on             taxi_in        crs_arr_time 
##                   0                   0                   0                   0 
##            arr_time           arr_delay    crs_elapsed_time actual_elapsed_time 
##                   0                   0                   0                   0 
##            air_time            distance 
##                   0                   0

Giải thích:
- flight_data_clean <- na.omit(flight_data): dùng để tạo ra một bộ dữ liệu mới tên là flight_data_clean bằng cách loại bỏ tất cả các quan sát bị NA.
- colSums(is.na(flight_data_clean)): dùng để kiểm tra lại toàn bộ dữ liệu sau khi làm sạch.

Nhận xét:
- Bộ dữ liệu không còn giá trị thiếu.

2.3. Chuyển đổi biến op_unique_carrier sang dạng Factor

Biến op_unique_carrier thể hiện mã hãng hàng không thực hiện chuyến bay. Mặc dù được lưu dưới dạng ký tự (character), nhưng giá trị của biến này chỉ dùng để phân loại các hãng bay chứ không mang ý nghĩa văn bản. Do đó, việc chuyển sang dạng factor giúp phần mềm nhận diện đúng bản chất là biến định tính phân loại, thay vì biến chuỗi thông thường.

flight_data_clean$op_unique_carrier <- as.factor(flight_data_clean$op_unique_carrier)
class(flight_data_clean$op_unique_carrier)
## [1] "factor"

Giải thích:
- as.factor(): chuyển đổi biến op_unique_carrier từ dạng ký tự (character) sang dạng phân loại (factor), giúp R hiểu rằng các giá trị trong biến là các nhóm phân biệt đại diện cho từng hãng hàng không.
- class() được dùng để kiểm tra lại kiểu dữ liệu sau khi chuyển đổi; kết quả “factor” xác nhận việc chuyển đổi đã thành công.

Nhận xét:
- Kết quả “factor” xác nhận việc chuyển đổi đã thành công.

2.4. Chuyển đổi biến op_carrier_fl_num sang dạng Character

Biến op_carrier_fl_num thể hiện mã số chuyến bay của hãng hàng không. Biến này ban đầu được lưu dưới dạng số nguyên (integer), tuy nhiên các giá trị của nó chỉ mang tính định danh cho từng chuyến bay, chứ không mang ý nghĩa đo lường hay thứ tự. Do đó, việc chuyển sang dạng character giúp lưu trữ chính xác hơn bản chất nhận dạng (ID) của biến mà không làm phát sinh các xử lý nhầm lẫn như biến số.

flight_data_clean$op_carrier_fl_num <- as.character(flight_data_clean$op_carrier_fl_num)
class(flight_data_clean$op_carrier_fl_num)
## [1] "character"

Giải thích:
- as.character(): chuyển đổi biến op_carrier_fl_num từ dạng số nguyên (integer) sang kiểu ký tự (character), giúp R hiểu rằng các giá trị trong biến là chuỗi định danh chứ không phải giá trị số học.
- class(): được sử dụng để kiểm tra lại kiểu dữ liệu sau khi chuyển đổi; kết quả “character” xác nhận rằng việc chuyển đổi đã thành công.

Nhận xét:
- Kết quả “character” xác nhận việc chuyển đổi đã thành công.

2.5. Tạo biến weekend_flight – chuyến bay cuối tuần

  • Biến day_of_week trong bộ dữ liệu biểu thị ngày trong tuần với giá trị từ 1 đến 7.
  • Trong đó: 1 = Thứ Hai, 2 = Thứ Ba, …, 6 = Thứ Bảy, 7 = Chủ Nhật.
  • Theo đó, một chuyến bay được xem là chuyến bay cuối tuần nếu giá trị của day_of_week là 6 hoặc 7.
flight_data_clean$weekend_flight <- ifelse(flight_data_clean$day_of_week %in% c(6, 7), 1, 0)
table(flight_data_clean$weekend_flight)
## 
##      0      1 
## 753717 269107

Giải thích:
- ifelse(): trong R được sử dụng để tạo biến mới weekend_flight
- Nếu giá trị day_of_week thuộc tập {6, 7}, hàm trả về giá trị 1 (tức là chuyến bay diễn ra vào cuối tuần).Ngược lại, hàm trả về 0 (tức là chuyến bay diễn ra trong ngày thường).
- table(): được dùng để thống kê tần suất của hai nhóm chuyến bay cuối tuần và trong tuần.

Nhận xét:
-Trong bộ dữ liệu có tổng cộng 269.107 chuyến bay diễn ra vào cuối tuần (Thứ Bảy và Chủ Nhật), còn lại diễn ra vào các ngày thường gồm 753717 chuyến bay.

2.6. Tạo biến day_period – Phân loại chuyến bay theo thời điểm trong ngày

Phân loại các chuyến bay trong tập dữ liệu thành bốn giai đoạn trong ngày (Morning, Afternoon, Evening, Night) dựa trên thời gian khởi hành (dep_time) nhằm phục vụ phân tích xu hướng trễ chuyến bay theo thời điểm.

flight_data_clean$day_period <- ifelse(
  flight_data_clean$dep_time >= 300 & flight_data_clean$dep_time < 720, "Morning",
  ifelse(
    flight_data_clean$dep_time >= 720 & flight_data_clean$dep_time < 1080, "Afternoon",
    ifelse(
      flight_data_clean$dep_time >= 1080 & flight_data_clean$dep_time < 1320, "Evening",
      "Night"
    )
  )
)
flight_data_clean$day_period <- as.factor(flight_data_clean$day_period)
summary(flight_data_clean$day_period)
## Afternoon   Evening   Morning     Night 
##    223079    142217    136900    520628

Giải thích:
- ifelse(): được sử dụng để gán nhãn cho từng khoảng thời gian cụ thể:
Từ 05:00 đến trước 12:00 → “Morning”
Từ 12:00 đến trước 18:00 → “Afternoon”
Từ 18:00 đến trước 22:00 → “Evening”
Các trường hợp còn lại (22:00 – 05:00) → “Night”
- as.factor(): chuyển biến day_period sang dạng factor để R hiểu đây là biến phân loại.
- summary(): Hiển thị số lượng chuyến bay trong từng nhóm thời điểm.
Nhận xét:
- Việc phân chia chuyến bay theo bốn khoảng thời gian - Morning (136.900 chuyến), Afternoon (223.079 chuyến), Evening (142.217 chuyến), và Night (520.628 chuyến) - cho thấy sự tập trung rõ ràng của lưu lượng bay vào ban đêm.

2.7. Tạo biến speed_mph – Tốc độ trung bình của chuyến bay (dặm/giờ)

Trong phân tích hoạt động bay, tốc độ trung bình là chỉ số quan trọng giúp đánh giá hiệu quả khai thác và điều kiện vận hành của các chuyến bay. Để tính tốc độ trung bình (đơn vị: dặm/giờ), ta dựa vào hai biến sẵn có trong dữ liệu là distance và air_time.
Công thức tính tốc độ trung bình:
\[ \text{speed_mph} = \frac{\text{distance}}{\text{air_time} / 60} \]

flight_data_clean$speed_mph <- flight_data_clean$distance / (flight_data_clean$air_time / 60)
summary(flight_data_clean$speed_mph)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   21.16  360.00  407.75  405.57  453.75 4473.33

Giải thích:
- Vận tốc trung bình của chuyến bay (speed_mph) bằng quãng đường bay (distance) chia cho thời gian bay thực tế (air_time) đã được chuyển đổi sang đơn vị giờ.
- summary() dùng để mô tả thống kê cơ bản của biến mới tạo (gồm giá trị nhỏ nhất, trung vị, trung bình, lớn nhất, và các tứ phân vị), giúp nhận biết phạm vi dao động và xu hướng chung của tốc độ bay trong toàn bộ dữ liệu.

Nhận xét:
- Ta thấy giá trị nhỏ nhất chỉ 21.16 mph và giá trị lớn nhất tới 4473.33 mph là không thực tế, vì vận tốc này chênh lệch hơn nhiều so với tốc độ cất hạ cánh của máy bay.
- Nguyên nhân có thể do lỗi nhập liệu, thời gian bay ghi sai, hoặc sự cố trong quá trình đo đạc.
- Do đó, các giá trị tốc độ quá thấp hoặc quá cao (ngoại lai) cần được loại bỏ hoặc hiệu chỉnh ở bước xử lý kế tiếp, nhằm đảm bảo tính chính xác cho các phân tích mô tả và hồi quy sau này.

2.8. Loại bỏ giá trị ngoại lai của biến speed_mph

library(dplyr)

Q1 <- quantile(flight_data_clean$speed_mph, 0.25, na.rm = TRUE)
Q3 <- quantile(flight_data_clean$speed_mph, 0.75, na.rm = TRUE)
IQR_value <- Q3 - Q1

lower_bound <- Q1 - 1.5 * IQR_value
upper_bound <- Q3 + 1.5 * IQR_value

flight_data_no_outlier <- flight_data_clean %>%
  filter(speed_mph >= lower_bound & speed_mph <= upper_bound)

summary(flight_data_no_outlier$speed_mph)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   219.4   361.6   408.7   407.8   454.2   594.4

Giải thích:
- Gói dplyr được nạp để sử dụng các hàm xử lý dữ liệu như %>% và filter().
- quantile() dùng để tính các phân vị thứ nhất (Q1) và thứ ba (Q3) của biến speed_mph.
- Từ đó, hai ngưỡng loại ngoại lệ được xác định: Lower bound và Upper bound. Các quan sát nằm ngoài khoảng này bị xem là ngoại lệ.
- filter() được dùng để giữ lại các giá trị speed_mph thõa mãn điều kiện, tạo ra bộ dữ liệu mới flight_data_no_outlier.
- summary() cung cấp các thống kê mô tả giúp kiểm tra lại kết quả sau khi loại ngoại lệ.
Nhận xét:
- Sau khi lọc, khoảng giá trị từ 219,4 đến 594,4 dặm/giờ trở nên hợp lý, với tốc độ trung bình khoảng 408 dặm/giờ, phù hợp với đặc điểm kỹ thuật của máy bay phản lực dân dụng, có độ tin cậy cao hơn, sẵn sàng cho các phân tích mô tả và mô hình hóa tiếp theo.

2.9. Chuẩn hóa dữ liệu thời gian bay

Khi chuyển tất cả các cột thời gian (departure, arrival, wheels_off, wheels_on) đều ở đơn vị “phút trong ngày”, giúp dữ liệu trở nên đồng bộ, khiến dữ liệu trở nên đơn giản và trực quan hơn.

library(dplyr)
convert_to_minutes <- function(x) {
  hour <- floor(x / 100)
  minute <- x %% 100
  total_minutes <- hour * 60 + minute
  return(total_minutes)
}
flight_data_no_outlier <- flight_data_no_outlier %>%
  mutate(
    crs_dep_time_min = convert_to_minutes(crs_dep_time),
    dep_time_min = convert_to_minutes(dep_time),
    wheels_off_min = convert_to_minutes(wheels_off),
    wheels_on_min = convert_to_minutes(wheels_on),
    crs_arr_time_min = convert_to_minutes(crs_arr_time),
    arr_time_min = convert_to_minutes(arr_time)
  )

View(flight_data_no_outlier)

Giải thích: - hour <- floor(x / 100): lấy phần giờ của biến thời gian.
- minute <- x %% 100: lấy phần phút.
- total_minutes <- hour * 60 + minute: Quy đổi tổng thời gian sang phút.
- return(total_minutes): trả về kết quả tính được.
- convert_to_minutes(): chuyển đổi thời gian dạng giờ–phút sang tổng số phút trong ngày. - crs_dep_time_min: giờ khởi hành dự kiến (phút)
- dep_time_min: giờ khởi hành thực tế (phút)
- wheels_off_min: thời điểm cất cánh thực tế (phút)
- wheels_on_min: thời điểm hạ cánh thực tế (phút)
- crs_arr_time_min: giờ đến dự kiến (phút)
- arr_time_min: giờ đến thực tế (phút)
- mutate(): tạo thêm các biến mới
→ Mỗi cột được tính bằng cách gọi hàm convert_to_minutes() vừa định nghĩa ở trên. - View(flight_data_no_outlier): Mở bảng dữ liệu kết quả trong cửa sổ Data Viewer của RStudio.

2.10. Phân tích tỷ lệ chuyến bay bị trễ theo thời điểm trong ngày

Sau khi đã tạo biến day_period để phân loại thời điểm cất cánh của chuyến bay (sáng, trưa, chiều, tối), bước này tiến hành tổng hợp dữ liệu nhằm đánh giá tỷ lệ các chuyến bay bị trễ theo từng khung giờ.

flight_delay_period <- flight_data_no_outlier %>%
  group_by(day_period) %>%
  summarise(
    total_flights = n(),
    delayed_flights = sum(arr_delay > 0, na.rm = TRUE),
    delay_rate = delayed_flights / total_flights
  )
head(flight_delay_period)

Giải thích:
- group_by() để nhóm dữ liệu theo biến day_period, từ đó tính toán số lượng và tỷ lệ chuyến bay bị trễ trong từng khung giờ.
- head() dùng để hiển thị những dòng đầu tiên của bảng kết quả.

Nhận xét:
- Phân tích cho thấy tỷ lệ trễ chuyến bay tăng dần từ sáng đến đêm, phản ánh ảnh hưởng tích lũy của các yếu tố vận hành hàng không trong ngày. Cụ thể, tỷ lệ trễ trung bình tăng từ 22.43% buổi sáng lên 41.57% ban đêm, tương ứng mức tăng 19.14%.

CHƯƠNG 3. Thực hiện các thống kê cơ bản

3.1. Thống kê mô tả biến định lượng

Phân tích đặc điểm cơ bản của 3 biến định lượng: dep_delay, arr_delay và distance.

3.1.1. Chọn 3 biến định lượng

quant_vars <- flight_data_no_outlier[, c("dep_delay", "arr_delay", "distance")]

Giải thích:
- quant_vars <- flight_data_no_outlier[, c(“dep_delay”, “arr_delay”, “distance”)]: dùng để trích 3 biến định lượng từ bộ dữ liệu flight_data_no_outlier.

Nhận xét:
- Lọc 3 biến định lượng này ra nhằm thống kê mô tả, tính trung bình, tính độ lệch chuẩn và tính khoảng của từng biến ở các bước tiếp theo.

3.1.2. Xem thống kê mô tả cơ bản

summary(quant_vars)
##    dep_delay         arr_delay           distance   
##  Min.   : -96.00   Min.   :-117.000   Min.   :  31  
##  1st Qu.:  -6.00   1st Qu.: -17.000   1st Qu.: 406  
##  Median :  -2.00   Median :  -7.000   Median : 699  
##  Mean   :  11.52   Mean   :   5.494   Mean   : 839  
##  3rd Qu.:   8.00   3rd Qu.:   9.000   3rd Qu.:1072  
##  Max.   :3125.00   Max.   :3136.000   Max.   :5095

Giải thích:
- summary(): dùng để tính các thống kê mô tả cơ bản như: min, 1st quartile, median, mean, 3rd quartile, max.

Nhận xét:
- Biến dep_delay (Độ trễ khi khởi hành - phút):
+ Giá trị trung bình (Mean) của độ trễ khởi hành là 11.52 phút, trung vị (Median) là -2 phút, nghĩa là một nửa số chuyến bay khởi hành sớm hơn hoặc đúng giờ.
+ Giá trị lớn nhất lên đến 3125 phút cho thấy một số trường hợp khởi hành trễ bất thường, có thể là do thời tiết xấu, sự cố kỹ thuật, hoặc hoãn chuyến dài.
+ Khoảng tứ phân vị từ -6 đến 8 phút phản ánh phần lớn các chuyến bay chỉ dao động trong khoảng ±10 phút quanh giờ dự kiến, tức là khá ổn định về thời gian cất cánh.
→ Như vậy, dữ liệu của biến dep_delay cho thấy đa số chuyến bay khởi hành sớm hoặc đúng giờ, nhưng có một số ít chuyến trễ nghiêm trọng làm kéo trung bình lên cao.
- Biến arr_delay (Độ trễ khi hạ cánh - phút):
+ Giá trị trung bình là 5.494 phút, trung vị là -7 phút, nghĩa là phần lớn chuyến bay đến nơi sớm hơn kế hoạch.
+ Giá trị lớn nhất lên đến 3136 phút cho thấy tồn tại một số chuyến trễ rất lớn.
+ Khoảng tứ phân vị từ -17 đến 9 phút phản ánh phần lớn các chuyến bay đến dao động trong khoảng ±15 phút quanh giờ dự kiến.
→ Như vậy, dữ liệu của biến arr-delay cho thấy tình trạng tương tự độ trễ khi khởi hành, đa số đúng giờ hoặc sớm và có một số chuyến bị trễ lớn.
- Biến distance (Khoảng cách bay - dặm):
+ Giá trị trung bình 839 dặm và trung vị 699 dặm cho thấy phần lớn chuyến bay là chuyến bay ngắn đến trung bìnhn.
+ Giá trị lớn nhất 5095 dặm phản ánh sự tồn tại của một số chuyến bay đường dài.
+ Khoảng tứ phân vị từ 406 đến 1072 dặm cho thấy các chuyến bay có độ dài trung bình khoảng 600–700 dặm.

3.1.3. Tính độ lệch chuẩn

sapply(quant_vars, sd)
## dep_delay arr_delay  distance 
##  56.23152  59.03097 584.93699

Giải thích:
- sd: tính độ lệch chuẩn - đo mức độ phân tán của dữ liệu quanh giá trị trung bình.
- sapply(quant_vars, sd): lấy từng cột trong quant_vas và áp dụng hàm sd lên nó.

Nhận xét:
- Biến dep_delay: có độ lệch chuẩn 56.23152 phút cao hơn nhiều so với giá trị trung bình 11.52 phút, thể hiện thời gian trễ khởi hành có biến động khá lớn. Phản ánh sự không ổn định trong quá trình khởi hành.
- Biến arr_delay: có độ lệch chuẩn 59.03097 phút cũng cao hơn nhiều so với giá trị trung bình 5.494 phút, cho thấy mức dao động cao hơn độ trễ khởi hành, do đó thời gian đến của các chuyến bay cũng thiếu ổn định.
- Biến distance: có độ lệch chuẩn 584.93699 dặm thấp hơn so với giá trị trung bình 839 dặm cho thấy sự chênh lệch lớn giữa các chặng bay. Phản ánh sự đa dạng trong hoạt động hàng không.

3.1.4. Tính khoảng của từng biến

sapply(quant_vars, function(x) max(x) - min(x))
## dep_delay arr_delay  distance 
##      3221      3253      5064

Giải thích:
- max(x)-min(x): tính khoảng biến thiên giữa giá trị lớn nhất và nhỏ nhất của mỗi biến định lượng.
- function(x): lấy từng cột trong quant_vars và thực hiện phép tính để ra khoảng biến thiên.

Nhận xét:
- Biến dep_delay: có khoảng biến thiên là 3221 phút, cho thấy sự khác biệt rất lớn giữa chuyến bay khởi hành sớm nhất và muộn nhất.
- Biến arr_delay: có khoảng biến thiên 3253 phút, cho thấy tình trạng trễ khi đến nơi cũng có biến động mạnh.
- Biến distance: có khoảng biến thiên 5064 dặm, cho thấy mức độ đa dạng về độ dài hành trình.

3.2. Thống kê mô tả biến định tính

3.2.1. Chọn 3 biến định tính

qual_vars <- flight_data_no_outlier[, c("op_unique_carrier", "origin", "dest")]

Giải thích:
- qual_vars <- flight_data_no_outlier[, c(“op_unique_carrier”, “origin”, “dest”)]: dùng để trích 3 biến định lượng từ bộ dữ liệu flight_data_no_outlier.

Nhận xét:
- Lọc 3 biến định tính này ra nhằm xem tần suất xuất hiện, tạo bảng tần suất, tính tần suất tương đối và sắp xếp các nhóm theo tần suất giảm dần.

3.2.2. Bảng tần số tuyệt đối

Giải thích:
- table(): tạo bảng tần số tuyệt đối thể hiện số lần xuất hiện của từng giá trị của biến.
- as.data.frame(): Chuyển bảng tần suất từ dạng table sang dạng data frame, giúp dễ xử lý và hiển thị hơn.
- datatable(): Dùng gói DT để hiển thị bảng động, đẹp và có thể chia trang, sắp xếp, tìm kiếm.
- pageLength=10: hiển thị 10 dòng mỗi trang.
- caption: thêm tiêu đề cho bảng.

3.2.2.1 Bảng tần số tuyệt đối của hãng hàng không thực hiện chuyến bay
table_carrier <- as.data.frame(table(flight_data_no_outlier$op_unique_carrier))
datatable(table_carrier,
          options = list(pageLength = 10),
          caption = "Bảng tần suất hãng hàng không thực hiện chuyến bay")

Nhận xét:
- Số lượng chuyến bay giữa các hãng phân bố không đều, chứng tỏ mức độ hoạt động của từng hãng có sự chênh lệch lớn.

3.2.2.2 Bảng tần số tuyệt đối của các sân bay xuất phát
table_origin  <- as.data.frame(table(flight_data_no_outlier$origin))
datatable(table_origin,
          options = list(pageLength = 10),
          caption = "Bảng tần suất sân bay xuất phát")

Nhận xét:
- Số lượng sân bay xuất phát là 334, cho thấy mạng lưới hoạt động hàng không rất rộng, bao phủ nhiều địa điểm.
- Một số sân bay có tần suất chuyến bay rất cao như ABQ với 3.338 chuyến, chứng tỏ đó là các trung tâm hàng không lớn.
- Ngược lại, các sân bay như ADK, ADQ chỉ có vài chục chuyến bay phản ánh vai trò nhỏ hoặc hoạt động hạn chế trong mạng lưới.
→ Phân bố tần suất này không đồng đều, thể hiện sự tập trung hoạt động vào một số sân bay lớn.

3.2.2.2 Bảng tần số tuyệt đối của các sân bay đến
table_dest    <- as.data.frame(table(flight_data_no_outlier$dest))
datatable(table_dest,
          options = list(pageLength = 10),
          caption = "Bảng tần suất sân bay đến")

Nhận xét:
- Có 334 sân bay đến, cho thấy phạm vi bay rộng khắp các khu vực.
- Một số sân bay có tần suất chuyến bay đến rất cao, chẳng hạn như ABQ với 3.333 chuyến bay, thể hiện vai trò là các trung tâm hàng không lớn.
- Các sân bay như ADK hay ADQ có tần suất thấp (dưới 200 chuyến), phản ánh vai trò nhỏ hoặc ít được khai thác trong mạng lưới bay.
- Phân bố số chuyến bay đến không đồng đều giữa các sân bay, cho thấy hoạt động hàng không tập trung chủ yếu vào một số điểm đến lớn.

3.2.3. Tạo bảng tần suất tương đối (%)

Giải thích:
- table(): tạo bảng tần số tuyệt đối.
- prop.table(): chuyển sang bảng tần số tương đối.
- round(…,2): làm tròn 2 chữ số thập phân.
- datatable(): hiển thị ở dạng đẹp, có thể lật sang trang khác.

3.2.3.1. Tạo bảng tần suất tương đối của hãng hàng không thực hiện chuyến bay
table_carrier_rel <- as.data.frame(round(prop.table(table(flight_data_no_outlier$op_unique_carrier)) * 100, 2))
datatable(table_carrier_rel,
          options = list(pageLength = 10),
          caption = "Bảng tần suất tương đối (%) của hãng hàng không thực hiện chuyến bay")

Nhận xét:
- Hãng WN chiếm 20.96% tổng số chuyến bay, là hãng có quy mô hoạt động lớn nhất trong bộ dữ liệu.
- AA với 14.59% và DL với 14.02%, cùng với UA với 10.56% và OO với 9.91% cho thấy đây là các hãng hàng không lớn, chiếm phần lớn thị phần vận tải.
- Các hãng như B6, MQ, NK và YX dao động khoảng 3–4%, thể hiện quy mô khai thác ở mức vừa phải.
- Các hãng như HA, G4 chỉ chiếm 1–2%, phản ánh quy mô nhỏ hoặc khai thác ở các tuyến hạn chế.
→ Kết quả này có thể sử dụng để so sánh mức độ chậm chuyến hoặc huỷ chuyến theo quy mô hãng, hay đánh giá mức độ cạnh tranh trong thị trường hàng không.

3.2.3.2. Tạo bảng tần suất tương đối của các sân bay xuất phát
table_origin_rel <- as.data.frame(round(prop.table(table(flight_data_no_outlier$origin)) * 100, 2))
datatable(table_origin_rel,
          options = list(pageLength = 10),
          caption = "Bảng tần suất tương đối (%) của các sân bay xuất phát")

Nhận xét:
- Bảng có 334 sân bay.
- Việc phân tích toàn bộ bảng có thể giúp xác định các sân bay trọng điểm, các sân bay ít hoạt động, từ đó cho thấy phân bố không đều trong mạng lưới hàng không.

3.2.3.3. Tạo bảng tần suất tương đối của các sân bay đến
table_dest_rel <- as.data.frame(round(prop.table(table(flight_data_no_outlier$dest)) * 100, 2))
datatable(table_dest_rel,
          options = list(pageLength = 10),
          caption = "Bảng tần suất tương đối (%) của các sân bay đến")

Nhận xét:
- Bảng có 334 sân bay.
- Phân bố tần suất tương đối khá không đồng đều, khi chỉ một số sân bay có tỷ trọng cao, trong khi đa số sân bay có tần suất rất nhỏ, phản ánh sự tập trung của các chuyến bay vào một số điểm đến chính, nhiều sân bay khác chỉ đóng vai trò thứ yếu.

3.3. Phân tích độ trễ theo ngày trong tuần

library(DT)
delay_by_day <- flight_data_no_outlier %>%
  group_by(day_of_week) %>%
  summarise(
    avg_dep_delay = round(mean(dep_delay), 2),
    median_dep_delay = round(median(dep_delay), 2),
    total_flights = n()
  ) %>%
  arrange(day_of_week)
datatable(
  delay_by_day,
  options = list(pageLength = 10)
)

Giải thích:
- delay_by_day <- flight_data_no_outlier: tạo biến mới có tên delay_by_day từ bộ dữ liệu flight_data_no_outlier.
- %>%: chuyển kết quả của bước này sang bước tiếp theo. - group_by(day_of_week): Nhóm dữ liệu theo ngày trong tuần.
- avg_dep_delay = round(mean(dep_delay), 2),: Tính độ trễ khởi hành trung bình (mean(dep_delay)) của mỗi nhóm. Sau đó làm tròn 2 chữ số thập phân bằng round().
- median_dep_delay = round(median(dep_delay), 2),: Tính độ trễ khởi hành trung vị. Cũng làm tròn 2 chữ số.
- total_flights = n(): Đếm tổng số chuyến bay trong từng nhóm.
- summarise(): Bắt đầu hàm tóm tắt thống kê cho từng nhóm.
- arrange(day_of_week): Sắp xếp bảng kết quả theo thứ tự tăng dần của ngày trong tuần (1 → 7).
- options = list(pageLength = 10): hiển thị 10 dòng mỗi trang.
- datatable(): hiển thị bảng đẹp, có thể sắp xếp và lật trang.

Nhận xét:
- Về độ trễ trung bình (avg_dep_delay):
+ Các giá trị dao động từ 7.84 đến 14.06 phút, cho thấy nhìn chung các chuyến bay chỉ bị trễ nhẹ.
+ Ngày Thứ Hai (day_of_week = 2) có độ trễ trung bình cao nhất (14.06 phút) – đây có thể là do lưu lượng bay lớn vào đầu tuần khi nhu cầu di chuyển tăng.
+ Ngày Thứ Tư (3) và Thứ Năm (4) lại có mức trễ trung bình thấp nhất (7.87 và 7.84 phút) – gợi ý rằng giữa tuần là thời điểm hệ thống hàng không hoạt động ổn định hơn.
- Về độ trễ trung vị (median_dep_delay):
+ Các giá trị trung vị nằm quanh -2 đến -3 phút, tức là một nửa số chuyến bay thực tế khởi hành sớm hơn 2–3 phút.
+ Điều này phản ánh rằng mặc dù có một số chuyến bay bị trễ đáng kể nhưng phần lớn các chuyến vẫn đúng hoặc sớm giờ khởi hành.
- Về tổng số chuyến bay (total_flights):
+ Tổng số chuyến bay giữa các ngày trong tuần khá đồng đều, dao động quanh 140.000–158.000 chuyến.
+ Ngày Thứ Hai (1) có số lượng chuyến bay cao nhất (158.769 chuyến), phù hợp với xu hướng nhu cầu di chuyển tăng đầu tuần.
+ Ngày Thứ Sáu (6) có ít chuyến bay hơn (123.143 chuyến) — có thể do các tuyến bay được phân bổ ít hơn vào cuối tuần.

3.4. Tạo bảng trung bình độ trễ khi khởi hành và đến theo hãng bay

library(DT)
carrier_delay <- flight_data_no_outlier %>%
  group_by(op_unique_carrier) %>%
  summarise(
    avg_depdelay = mean(dep_delay),
    avg_arrdelay = mean(arr_delay)
  ) %>%
  arrange(desc(avg_depdelay))
datatable(
  head(carrier_delay, 20),
       options = list(pageLength = 10))

Giải thích:
- group_by(): nhóm dữ liệu theo hãng bay (op_unique_carrier).
- %>%: chuyển kết quả của bước trước làm đầu vào cho bước sau. - summarise(): tính toán giá trị tổng hợp cho từng nhóm.
- avg_depdelay = mean(dep_delay): tính độ trễ trung bình khi khởi hành của mỗi hãng với tên mới là avg_depdelay.
- avg_arrdelay = mean(arr_delay): tính độ trễ trung bình khi đến của mỗi hãng với tên mới là avg_arrdelay.
- arrange(): sắp xếp dữ liệu theo thứ tự giảm dần (desc) của biến avg_depdelay.
- datatable(): tạo bảng dữ liệu có thể tìm kiếm, sắp xếp và lật trang.
- head(carrier_delay, 20): Lấy 20 dòng đầu tiên của bảng carrier_delay.
- options = list(pageLength = 10): Mỗi trang của bảng sẽ hiển thị 10 dòng dữ liệu.

Nhận xét:
- Kết quả cho thấy hãng AA có độ trễ trung bình lớn nhất ở cả hai giai đoạn cất cánh và hạ cánh, cho thấy tình trạng chậm chuyến xảy ra thường xuyên hơn so với các hãng khác.Ngược lại, hãng YX có độ trễ trung bình thấp nhất, cho thấy khả năng duy trì lịch trình cất cánh đúng giờ tốt nhất của hãng.

3.5. Phân tích thống kê thời gian bay theo thành phố xuất phát

library(DT)
origin_time_stats <- flight_data_no_outlier %>%
  group_by(origin_city_name) %>%
  summarise(
    total_flights = n(),
    min_time = min(actual_elapsed_time),
    max_time = max(actual_elapsed_time),
    mean_time = mean(actual_elapsed_time),
    median_time = median(actual_elapsed_time)
  ) %>%
  arrange(desc(total_flights))
datatable(
  head(origin_time_stats, 20),
       options = list(pageLength = 10))

Giải thích:
- origin_time_stats <- flight_data_no_outlier %>%: tạo biến mới có tên origin_time_stats lấy từ flight_data_no_outlier.
- group_by(origin_city_name) %>%: nhóm dữ liệu theo thành phố xuất phát.
- total_flights = n(): tổng số chuyến bay xuất phát từ thành phố đó.
- min_time = min(actual_elapsed_time): thời gian bay ngắn nhất.
- max_time = max(actual_elapsed_time): thời gian bay dài nhất.
- mean_time = mean(actual_elapsed_time): thời gian bay trung bình.
- median_time = median(actual_elapsed_time): thời gian bay trung vị.
- summarise(): tính các chỉ tiêu thống kê mô tả cho mỗi thành phố.
- arrange(desc(total_flights)): Sắp xếp kết quả theo thứ tự giảm dần của biến total_flights.
- head(origin_time_stats, 20): chỉ lấy 20 thành phố có nhiều chuyến bay nhất.
- options = list(pageLength = 10): thiết lập hiển thị 10 dòng mỗi trang.
- datatable(): Bảng kết quả cho phép người xem tìm kiếm, sắp xếp và lật trang.

Nhận xét:
- Atlanta (GA) là thành phố có số lượng chuyến bay xuất phát lớn nhất với 49.667 chuyến, tiếp theo là Chicago (IL) với 47.465 chuyến và Dallas/Fort Worth (TX) với 44.431 chuyến.
- Về thời gian bay, giá trị trung bình (mean_time) dao động từ khoảng 120 đến 190 phút, trong khi thời gian trung vị (median_time) thường thấp hơn, cho thấy phần lớn các chuyến bay có độ dài vừa phải, chỉ một số ít chuyến đường dài kéo giá trị trung bình tăng lên.
- Các thành phố như Los Angeles (CA), Boston (MA) hay Miami (FL) có thời gian bay trung bình cao nhất (trên 180 phút), phản ánh vị trí địa lý xa hơn so với trung tâm. Ngược lại, các thành phố như Charlotte (NC) và Detroit (MI) có thời gian bay trung bình thấp hơn (khoảng 120–135 phút), chủ yếu phục vụ các chặng bay ngắn trong khu vực.

3.6. Chênh lệch giữa thời gian bay dự kiến và thực tế theo từng hãng hàng không

library(dplyr)
flight_diff_stats <- flight_data_no_outlier %>%
  mutate(time_diff = actual_elapsed_time - crs_elapsed_time) %>%
  group_by(op_unique_carrier) %>%
  summarise(
    total_flights = n(),
    mean_diff = round(mean(time_diff), 2),
    median_diff = median(time_diff),
    min_diff = min(time_diff),
    max_diff = max(time_diff)
  ) %>%
  arrange(desc(mean_diff))
datatable(
  head(flight_diff_stats, 20),
  options = list(pageLength = 10)
)

Giải thích:
- mutate(time_diff = actual_elapsed_time - crs_elapsed_time): tạo biến mới time_diff thể hiện chênh lệch giữa thời gian thực tế và dự kiến.
- group_by(op_unique_carrier): Gom nhóm dữ liệu theo mã hãng hàng không.
- total_flights = n(): Tổng số chuyến bay của hãng.
- mean_diff = round(mean(time_diff), 2): Độ chênh lệch trung bình (âm: bay nhanh hơn dự kiến; dương: bay lâu hơn dự kiến) và tròn làm 2 chữ số thập phân.
- median_diff = median(time_diff): Giá trị chênh lệch trung vị.
- min_diff, max_diff: Chênh lệch nhỏ nhất và lớn nhất trong từng hãng.
- arrange(desc(mean_diff)): Sắp xếp danh sách hãng theo thứ tự giảm dần của độ chênh lệch trung bình.
- datatable(…): Hiển thị bảng kết quả tương tác, giúp xem chi tiết 20 hãng đầu tiên.

Nhận xét:
- Hầu hết các hãng hàng không có mean_diff âm, chứng tỏ phần lớn chuyến bay thực tế ngắn hơn thời gian dự kiến, tức là thời gian bay được lên kế hoạch khá chặt chẽ và có độ dự phòng.
- Hãng HA có độ lệch trung bình gần bằng 0 (–0.01), thể hiện sự ổn định và chính xác cao trong dự báo thời gian bay.
- Ngược lại, các hãng như DL, YX, và 9E có mean_diff lớn âm (từ –7.8 đến –8.06 phút), cho thấy thời gian thực tế thường ngắn hơn dự kiến khoảng 7–8 phút, có thể do tuyến bay thuận lợi hoặc kế hoạch dự kiến dư thời gian.
- Một số giá trị max_diff rất lớn (như 500 phút ở YX) cho thấy vẫn tồn tại các chuyến bay bất thường, có thể do thời tiết xấu hoặc lý do kỹ thuật.

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

4.1. Trực quan hóa độ trễ của các hãng

library(dplyr)
library(tidyr)
library(ggplot2)

carrier_delay_long <- carrier_delay %>%
  pivot_longer(
    cols = c(avg_depdelay, avg_arrdelay),
    names_to = "delay_type",
    values_to = "delay_value"
  )
carrier_delay_long$delay_type <- factor(carrier_delay_long$delay_type,
                                        levels = c("avg_depdelay", "avg_arrdelay"))

ggplot(carrier_delay_long, aes(x = op_unique_carrier, y = delay_value, fill = delay_type)) +
  geom_bar(stat = "identity", position = "dodge", color = "black") +                           
  geom_text(aes(label = round(delay_value, 1)), 
            position = position_dodge(width = 1),
            vjust = -0.5, size = 3.2) +                                                       
  geom_hline(yintercept = mean(carrier_delay_long$delay_value, na.rm = TRUE),
             color = "darkgreen", linetype = "dashed", size = 1) +                          
  labs(
    x = "Airline",
    y = "Average Delay (minutes)",
    title = "Average Departure and Arrival Delays per Airline",
    subtitle = "Comparison of delay performance across airlines",
    caption = "Source: flight_data_no_outlier"
  ) +
  scale_fill_manual(
    values = c("avg_depdelay" = "#FFB7C5", "avg_arrdelay" = "#A7D8F0"),
    labels = c("avg_depdelay" = "Departure delay", "avg_arrdelay" = "Arrival delay")
  ) +                                                                                         
  theme_minimal(base_size = 13) +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    plot.title = element_text(face = "bold", size = 15),
    plot.subtitle = element_text(size = 12, face = "italic", color = "gray30"),
    legend.position = "right"                                                               
  )

Giải thích:
- library(dplyr), library(tidyr), library(ggplot2):
+ Gọi ba thư viện hỗ trợ xử lý dữ liệu và trực quan hóa.
+ dplyr: thao tác dữ liệu dạng bảng (lọc, nhóm, tính trung bình).
+ tidyr: chuyển đổi định dạng dữ liệu từ wide sang long để dễ vẽ biểu đồ.
+ ggplot2: tạo biểu đồ chuyên nghiệp, dễ tùy chỉnh.
- pivot_longer(): Chuyển dữ liệu từ dạng wide (các cột avg_depdelay, avg_arrdelay) thành dạng long, giúp ggplot có thể hiểu delay_type (loại trễ) như một biến phân loại và delay_value là giá trị tương ứng.
- geom_bar(stat = “identity”, position = “dodge”): Vẽ biểu đồ cột nhóm (mỗi hãng có hai cột – trễ khởi hành và trễ đến).
- position = “dodge” giúp hai cột hiển thị cạnh nhau thay vì chồng lên nhau.
- geom_text(): Hiển thị giá trị cụ thể trên đỉnh mỗi cột để người xem dễ nhận biết mức trễ trung bình.
- geom_hline(): Thêm đường ngang biểu thị trung bình toàn bộ độ trễ, giúp so sánh từng hãng với mức trung bình ngành.
- labs(): Thiết lập tiêu đề, nhãn trục và chú thích nguồn dữ liệu, giúp biểu đồ rõ ràng và mang tính học thuật.
- scale_fill_manual(): Tùy chỉnh màu sắc cho hai loại độ trễ:
+ Màu đỏ gạch (firebrick) cho trễ khởi hành.
+ Màu xanh thép (steelblue) cho trễ đến.
+ Đồng thời đổi nhãn hiển thị trong chú giải (legend).
- theme_minimal() và theme(): Tạo giao diện tối giản, dễ đọc, đồng thời chỉnh góc nghiêng nhãn trục x, vị trí legend, và định dạng tiêu đề.

Nhận xét
- Kết quả cho thấy thời gian chậm khởi hành và chậm đến của các hãng hàng không nhìn chung dao động quanh mức trung bình từ 8 đến 10 phút. Trong đó, một số hãng ghi nhận mức độ trễ cao nhất trên 16 phút, trong khi một số hãng khác đạt mức trễ rất thấp, thậm chí âm, phản ánh khả năng vận hành hiệu quả và đúng giờ.
- Xét tổng thể, độ trễ khởi hành có xu hướng cao hơn độ trễ khi đến, cho thấy sự chậm trễ phần lớn bắt nguồn từ khâu khởi hành thay vì trong quá trình bay.

4.2. Biểu đồ Barplot: So sánh chuyến bay cuối tuần với ngày thường

library(dplyr)
library(ggplot2)
library(tidyr)

delay_weekend_month <- flight_data_no_outlier %>%
  filter(month %in% c(1, 2)) %>%
  group_by(month, weekend_flight) %>%
  summarise(
    avg_depdelay = mean(dep_delay, na.rm = TRUE),
    avg_arrdelay = mean(arr_delay, na.rm = TRUE)
  ) %>%
  
  pivot_longer(
    cols = c(avg_depdelay, avg_arrdelay),
    names_to = "delay_type",
    values_to = "delay_value"
  ) %>%
 
  mutate(
    delay_type = factor(delay_type, 
                        levels = c("avg_depdelay", "avg_arrdelay")),
    weekend_flight = factor(weekend_flight, 
                            levels = c(0, 1),
                            labels = c("Weekday", "Weekend"))
  )


ggplot(delay_weekend_month, aes(x = weekend_flight, y = delay_value, fill = delay_type)) +
  geom_bar(stat = "identity", position = "dodge", color = "black") +                 
  
  geom_text(aes(label = round(delay_value, 1)), 
            position = position_dodge(width = 1),
            vjust = -0.3, size = 3.2) +                                              
  geom_hline(yintercept = mean(delay_weekend_month$delay_value, na.rm = TRUE),
             color = "darkgreen", linetype = "dashed", size = 1) +                   
  facet_wrap(~ month, ncol = 2, labeller = labeller(month = c(`1` = "January", `2` = "February"))) +  
  scale_fill_manual(
    values = c("avg_depdelay" = "#F8AFA6", "avg_arrdelay" = "#CBB2FE"),
    labels = c("Departure delay", "Arrival delay")
  ) +                                                                                
  geom_point(aes(group = delay_type), size = 2, color = "black", position = position_dodge(width = 1)) + 
  labs(
    x = "Flight Type",
    y = "Average Delay (minutes)",
    title = "Average Departure and Arrival Delays: Weekday vs Weekend (Jan & Feb)",
    subtitle = "Comparison between weekday and weekend performance across the first two months",
    caption = "Source: flight_data_no_outlier"
  ) +                                                                         
  theme_minimal(base_size = 13) +
  theme(
    axis.text.x = element_text(face = "bold"),
    plot.title = element_text(face = "bold", size = 15),
    plot.subtitle = element_text(size = 12, face = "italic", color = "gray30"),
    legend.position = "bottom",
    panel.grid.minor = element_blank()
  )

Giải thích:
- group_by(month, weekend_flight): chia dữ liệu thành các nhóm theo tháng (1, 2, …) và loại chuyến bay (ngày thường hoặc cuối tuần).
- summarise(…): tính độ trễ trung bình khi khởi hành (avg_depdelay) và độ trễ trung bình khi đến (avg_arrdelay) trong từng nhóm.
- pivot_longer() biến dữ liệu từ dạng rộng (wide) sang dạng dài (long).
- cols = c(avg_depdelay, avg_arrdelay): chỉ định hai cột sẽ được gom lại.
- names_to = “delay_type”: tạo một cột mới ghi loại độ trễ (“avg_depdelay” hay “avg_arrdelay”).
- values_to = “delay_value”: chứa giá trị trung bình tương ứng của từng loại trễ.
- ggplot(…): khởi tạo biểu đồ với dữ liệu.
- aes(…): ánh xạ các biến lên trục:
- x = factor(weekend_flight) → chia cột thành Weekday và Weekend.
- y = delay_value → độ trễ trung bình.
- fill = delay_type → màu khác nhau cho khởi hành và đến.
- geom_bar(stat = “identity”): mỗi cột thể hiện đúng giá trị delay_value (không đếm tần suất).
- position = “dodge”: đặt hai cột cạnh nhau (thay vì chồng lên nhau).
- geom_text(…): hiển thị số liệu trên đầu cột (làm tròn 1 chữ số).
- geom_hline(…): thêm đường ngang biểu thị mức trung bình chung của tất cả độ trễ.
- linetype = “dashed”: nét đứt để phân biệt.
- color = “darkgreen”: tô màu xanh lá để nổi bật.
- facet_wrap(~ month): tách biểu đồ thành hai ô nhỏ — một cho tháng 1, một cho tháng 2.
- ncol = 2: hiển thị theo 2 cột (đặt hai tháng cạnh nhau).
- labeller: đổi nhãn trục từ “1” → “January”, “2” → “February” cho dễ hiểu.
- scale_fill_manual(…): tự đặt màu cho từng loại độ trễ (xanh cho khởi hành, đỏ cho đến).
- labs(…): đặt tiêu đề, nhãn trục và chú thích nguồn dữ liệu
- theme_minimal(): dùng giao diện đơn giản, hiện đại.
- theme(…): điều chỉnh vị trí chữ, font và vị trí legend ở dưới biểu đồ.

Nhận xét:
- Biểu đồ cho thấy nhìn chung các chuyến bay trong tháng 1 có mức độ trễ cao hơn so với tháng 2, đặc biệt là độ trễ khởi hành với trung bình trên 15 phút. Ngoài ra, các chuyến bay cuối tuần thường bị trễ nhiều hơn ngày thường, phản ánh tình trạng tăng nhu cầu di chuyển và tần suất khai thác cao vào cuối tuần. Tổng thể, xu hướng độ trễ giảm dần sang tháng 2 cho thấy tình hình khai thác bay đã ổn định hơn sau giai đoạn cao điểm đầu năm.

4.3. Phân tích độ trễ trung bình theo thời điểm trong ngày, có phân tách theo tháng (1, 2) và trạng thái cuối tuần, nhằm phát hiện mô hình trễ đặc trưng.

library(dplyr)
library(ggplot2)
library(tidyr)

flight_data_no_outlier <- flight_data_no_outlier %>%
  mutate(
    day_period = case_when(
      dep_time >= 500 & dep_time < 1200 ~ "Morning",
      dep_time >= 1200 & dep_time < 1700 ~ "Afternoon",
      dep_time >= 1700 & dep_time < 2100 ~ "Evening",
      TRUE ~ "Night"
    )
  )

carrier_delay <- flight_data_no_outlier %>%
  group_by(month, weekend_flight, day_period) %>%
  summarise(
    avg_depdelay = mean(dep_delay, na.rm = TRUE),
    avg_arrdelay = mean(arr_delay, na.rm = TRUE)
  ) %>%
  pivot_longer(
    cols = c(avg_depdelay, avg_arrdelay),
    names_to = "delay_type",
    values_to = "delay_value"
  )

ggplot(carrier_delay, aes(x = day_period, y = delay_value, fill = delay_type)) +
  geom_bar(stat = "identity", position = "dodge", color = "black") +              # Layer 1: Bar chart
  geom_text(aes(label = round(delay_value, 1)),                                   # Layer 2: Labels
            position = position_dodge(width = 1),
            vjust = -0.4, size = 3) +
  geom_hline(yintercept = mean(carrier_delay$delay_value, na.rm = TRUE),          # Layer 3: Mean line
             color = "darkgreen", linetype = "dashed", size = 1) +
  facet_grid(month ~ weekend_flight,                                              # Layer 4: Split by month & weekend
             labeller = labeller(
               month = c("1" = "Month 1", "2" = "Month 2"),
               weekend_flight = c("0" = "Weekday", "1" = "Weekend")
             )) +
  scale_fill_manual(                                                              # Layer 5: Colors and legend
    values = c("avg_depdelay" = "#E8A0BF", "avg_arrdelay" = "#A8D1D1"),
    labels = c("avg_depdelay" = "Departure delay", "avg_arrdelay" = "Arrival delay")
  ) +
  labs(                                                                           # Layer 6: Titles and labels
    x = "Time of Day",
    y = "Average Delay (minutes)",
    title = "Average Flight Delays by Time of Day and Month",
    subtitle = "Comparison of Departure and Arrival Delays across Months and Weekends",
    caption = "Source: flight_data_no_outlier"
  ) +
  theme_minimal(base_size = 13) +                                                 # Layer 7: Theme customization
  theme(
    plot.title = element_text(face = "bold", size = 15, hjust = 0.5),
    plot.subtitle = element_text(size = 12, color = "gray30", hjust = 0.5),
    axis.text.x = element_text(angle = 45, hjust = 1),
    legend.title = element_blank(),
    legend.position = "bottom"
  )

Giải thích:

Nhận xét:

4.

library(ggplot2)
library(dplyr)

# Tính giá trị trung bình để thêm chú thích
avg_points <- flight_data_no_outlier %>%
  group_by(op_unique_carrier) %>%
  summarise(mean_distance = mean(distance, na.rm = TRUE),
            mean_delay = mean(arr_delay, na.rm = TRUE))

ggplot(flight_data_clean, aes(x = distance, y = arr_delay)) +

  # 🟢 Lớp 1: Nền mờ để tạo khung trực quan
  geom_rect(aes(xmin = 0, xmax = Inf, ymin = -10, ymax = 10),
            fill = "lightblue", alpha = 0.1, inherit.aes = FALSE) +

  # 🔵 Lớp 2: Điểm dữ liệu chính
  geom_point(aes(color = op_unique_carrier, size = air_time), alpha = 0.6) +

  # 🔴 Lớp 3: Đường hồi quy tuyến tính (trend line)
  geom_smooth(aes(color = op_unique_carrier), method = "lm", se = FALSE, size = 1) +

  # 🟣 Lớp 4: Đường trung bình toàn bộ (mean line)
  geom_hline(yintercept = mean(flight_data_clean$arr_delay, na.rm = TRUE),
             color = "red", linetype = "dashed", size = 0.8) +

  # 🟠 Lớp 5: Chú thích trung bình cho từng hãng
  geom_text(data = avg_points,
            aes(x = mean_distance, y = mean_delay, label = op_unique_carrier),
            color = "black", size = 3, fontface = "bold", vjust = -1) +

  # 🟡 Lớp 6: Tùy chỉnh chủ đề trực quan
  theme_light(base_size = 12) +

  # ⚫ Lớp 7: Tiêu đề, nhãn trục, chú giải
  labs(title = "Mối quan hệ giữa khoảng cách, độ trễ và thời gian bay của các hãng hàng không",
       subtitle = "Kích thước điểm thể hiện thời gian bay; màu sắc phân biệt theo hãng",
       x = "Khoảng cách bay (miles)",
       y = "Độ trễ khi đến (phút)",
       color = "Hãng hàng không",
       size = "Thời gian bay (phút)") +
  theme(plot.title = element_text(face = "bold", size = 14, hjust = 0.5),
        plot.subtitle = element_text(size = 11, hjust = 0.5))

4.5. Biểu đồ thể hiện số chuyến bay theo từng hãng hàng không đến 6 điểm đến phổ biến nhất

library(ggplot2)
library(dplyr)

table_dest <- flight_data_no_outlier %>%
  group_by(dest, op_unique_carrier) %>%
  summarise(Freq = n(), .groups = "drop")

top_dest <- table_dest %>%
  group_by(dest) %>%
  summarise(total = sum(Freq)) %>%
  arrange(desc(total)) %>%
  slice_head(n = 6) %>%
  pull(dest)

dest_summary <- table_dest %>%
  filter(dest %in% top_dest) %>%
  group_by(dest) %>%
  mutate(percentage = Freq / sum(Freq) * 100)

ggplot(dest_summary, aes(x = "", y = percentage, fill = op_unique_carrier)) +
  geom_col(width = 1, color = "white") +
  coord_polar(theta = "y") +
  facet_wrap(~dest, ncol = 3) +
  labs(title = "Tỷ trọng chuyến bay theo điểm đến và hãng hàng không (Top 6)",
       fill = "Hãng hàng không", y = NULL, x = NULL) +
  theme_void() +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold", size = 14),
    legend.position = "bottom"
  )

geom_text(aes(label = paste0(round(percentage, 1), "%")),
          position = position_stack(vjust = 0.5),
          size = 3, color = "white")
## mapping: label = ~paste0(round(percentage, 1), "%") 
## geom_text: parse = FALSE, check_overlap = FALSE, size.unit = mm, na.rm = FALSE
## stat_identity: na.rm = FALSE
## position_stack

Giải thích:
- dplyr: dùng để gom nhóm, tóm tắt, lọc, và xử lý dữ liệu.
- ggplot2: dùng để vẽ biểu đồ (ở đây là biểu đồ tròn theo từng điểm đến).
- (group_by(dest, op_unique_carrier) → nhóm dữ liệu theo điểm đến (dest) và hãng hàng không (op_unique_carrier).
- summarise(Freq = n()) → đếm số chuyến bay (n()) trong mỗi nhóm.
- group_by(dest) và summarise(total = sum(Freq)) → tính tổng số chuyến bay đến mỗi điểm đến.
- arrange(desc(total)) → sắp xếp theo thứ tự giảm dần.
- slice_head(n = 6) → chọn ra 6 điểm đến có nhiều chuyến bay nhất.
- pull(dest) → lấy tên các điểm đến đó để dùng tiếp.
- filter(dest %in% top_dest) → chỉ giữ lại những dòng thuộc 6 điểm đến được chọn.
- mutate(percentage = Freq / sum(Freq) * 100)→ tính tỷ lệ phần trăm số chuyến bay của từng hãng trong cùng một điểm đến.
- aes(x = ““, y = percentage, fill = op_unique_carrier) → Không cần trục x cụ thể (để trống), trục y là phần trăm, màu theo hãng hàng không. - geom_col(width = 1, color =”white”) Tạo các cột (dạng “miếng bánh”), viền trắng để dễ phân biệt.
- coord_polar(theta = “y”): Biến biểu đồ cột thành biểu đồ tròn (pie chart). - facet_wrap(~dest, ncol = 3) Vẽ nhiều biểu đồ tròn, mỗi biểu đồ tương ứng với một điểm đến (tối đa 6 điểm, chia thành 3 cột).
- labs(): Đặt tiêu đề và chú thích.
- theme_void(): Loại bỏ trục, khung, lưới (làm biểu đồ tròn gọn gàng hơn).
- theme(…): Căn giữa tiêu đề, in đậm, điều chỉnh vị trí chú thích ở phía dưới.

Nhận xét: Biểu đồ trên thể hiện cơ cấu thị phần chuyến bay của các hãng hàng không đến 6 điểm đến có lượng chuyến bay cao nhất trong bộ dữ liệu. Mỗi hình tròn biểu thị một điểm đến là ATL, CLT, DEN, DFW, ORD, PHX, trong đó các lát cắt thể hiện tỷ trọng (%) chuyến bay của từng hãng hàng không tại điểm đến đó.
- Các màu sắc khác nhau đại diện cho từng hãng hàng không, giúp dễ dàng so sánh mức độ hoạt động của từng hãng ở mỗi sân bay.
- Biểu đồ cho thấy tại một số sân bay lớn như ATL, CLT, DFW, có một hãng hàng không chiếm ưu thế rõ rệt, thể hiện tính tập trung cao trong khai thác chuyến bay.
- Ngược lại, ở các sân bay như ORD hoặc PHX, tỷ lệ các hãng phân bố đa dạng hơn, thể hiện mức cạnh tranh cao hơn giữa các hãng.
- Việc phân tích tỷ trọng này giúp xác định sân bay “trọng điểm” của từng hãng hàng không, cũng như đánh giá mức độ tập trung hay phân tán trong mạng lưới bay nội địa.

PHẦN 2. TRÌNH BÀY BỘ DỮ LIỆU CỦA MÃ CHỨNG KHOÁN NVL