BÁO CÁO TIỂU
LUẬN
NGÔN NGỮ LẬP TRÌNH TRONG PHÂN TÍCH DỮ LIỆU
Bộ dữ liệu Vehicle Sales Data là một bộ dữ liệu công khai trên nền tảng Kaggle, được thu thập nhằm phục vụ cho các bài toán phân tích và dự đoán giá xe đã qua sử dụng. Dữ liệu bao gồm thông tin về hãng xe, năm sản xuất, số km đã đi, tình trạng xe và giá bán, giúp đánh giá các yếu tố ảnh hưởng đến giá trị của xe trên thị trường. # Dữ liệu
knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE)
library(readr)
library(dplyr)
library(kableExtra)
library(scales)
Giải thích:
library() là hàm nạp gói thư viện (package) vào phiên
làm việc.readr: chuyên đọc file CSV/TSV nhanh.dplyr: xử lý, biến đổi, lọc, nhóm dữ liệu dạng
bảng.kableExtra: tạo và định dạng bảng đẹp khi xuất báo cáo
HTML hoặc PDF.data <- read_csv("car_prices.csv")
Giải thích:
read_csv() (từ readr) đọc file CSV vào
R.
Trả về tibble – dạng bảng dữ liệu hiện đại.
data là biến lưu trữ dữ liệu.
cat("*Số lượng biến (cột):*", ncol(data), "\n")
## *Số lượng biến (cột):* 16
cat("*Số lượng quan sát (hàng):*", nrow(data), "\n")
## *Số lượng quan sát (hàng):* 558837
Giải thích:
cat() in kết quả ra màn hình.
ncol() đếm số cột, nrow() đếm số
hàng.
"\n" là ký tự xuống dòng.
total_NA <- sum(is.na(data))
NA_by_column <- sapply(data, function(x) sum(is.na(x)))
cat("*Tổng số giá trị NA:*", total_NA, "\n\n")
## *Tổng số giá trị NA:* 123376
Giải thích:
is.na() kiểm tra ô dữ liệu có NA hay không.
sum(is.na(data)) đếm tổng NA toàn bảng.
sapply() áp dụng hàm cho từng cột.
Kết quả NA_by_column là số lượng NA theo từng
cột.
NA_table <- tibble::enframe(NA_by_column, name = "variable", value = "n_missing") %>%
arrange(desc(n_missing))
kable(NA_table, caption = "Số giá trị thiếu (NA) theo biến") %>%
kable_styling(full_width = FALSE)
| variable | n_missing |
|---|---|
| transmission | 65352 |
| body | 13195 |
| condition | 11820 |
| trim | 10651 |
| model | 10399 |
| make | 10301 |
| color | 749 |
| interior | 749 |
| odometer | 94 |
| mmr | 38 |
| sellingprice | 12 |
| saledate | 12 |
| vin | 4 |
| year | 0 |
| state | 0 |
| seller | 0 |
Giải thích:
enframe() chuyển vector có tên thành bảng 2
cột.
%>% là toán tử “ống dẫn”.
arrange(desc()) sắp xếp giảm dần.
kable() tạo bảng Markdown,
kable_styling() làm đẹp bảng.
data <- data %>%
filter_all(all_vars(!is.na(.))) %>%
filter_all(all_vars(!is.infinite(.)))
Giải thích:
filter_all() lọc dữ liệu trên tất cả các
cột.
!is.na(.): giữ hàng không có NA.
!is.infinite(.): giữ hàng không có giá trị vô
hạn.
n_dup_rows <- sum(duplicated(data))
Giải thích:
duplicated() xác định hàng trùng.
sum(duplicated(data)) đếm số hàng trùng.
cat("\n Số dòng trùng lặp:", n_dup_rows, "\n")
##
## Số dòng trùng lặp: 0
if (n_dup_rows > 0) {
cat("Ví dụ vài dòng trùng lặp:\n")
print(head(data[duplicated(data) | duplicated(data, fromLast = TRUE), ], 6))
} else {
cat("Không tìm thấy dòng trùng lặp (theo toàn bộ hàng).\n")
}
## Không tìm thấy dòng trùng lặp (theo toàn bộ hàng).
orig_names <- names(data)
bad_names <- orig_names[grepl("[^A-Za-z0-9_]", orig_names)]
cat("\nTên biến ban đầu:\n"); print(orig_names)
##
## Tên biến ban đầu:
## [1] "year" "make" "model" "trim" "body"
## [6] "transmission" "vin" "state" "condition" "odometer"
## [11] "color" "interior" "seller" "mmr" "sellingprice"
## [16] "saledate"
if (length(bad_names) > 0) {
cat("\nCác tên chứa ký tự đặc biệt hoặc khoảng trắng:\n"); print(bad_names)
} else {
cat("\nKhông phát hiện ký tự đặc biệt trong tên biến.\n")
}
##
## Không phát hiện ký tự đặc biệt trong tên biến.
Giải thích:
names() lấy tên cột.
grepl() tìm ký tự đặc biệt qua regex.
Kết quả bad_names là danh sách biến cần đổi
tên.
variable_meaning <- data.frame(
Variable = c("year", "make", "model", "trim", "body", "transmission", "vin", "state",
"condition", "odometer", "color", "interior", "seller",
"mmr", "sellingprice", "saledate"),
Meaning = c(
"Năm sản xuất của xe",
"Hãng sản xuất ",
"Tên dòng xe cụ thể",
"Phiên bản hoặc cấp độ trang bị của xe",
"Kiểu dáng xe ",
"Loại hộp số ",
"Số khung xe ",
"Bang hoặc khu vực đăng ký",
"Tình trạng của xe",
"Số km xe đã đi",
"Màu sơn bên ngoài",
"Màu nội thất bên trong",
"Tên người bán hoặc đại lý",
"Giá trị thị trường trung bình (MMR)",
"Giá bán thực tế (USD)",
"Ngày bán xe"
),
stringsAsFactors = FALSE
)
data_types <- data.frame(
Variable = names(data),
DataType = sapply(data, class),
stringsAsFactors = FALSE
)
variable_summary <- left_join(variable_meaning, data_types, by = "Variable")
Giải thích:
left_join() ghép hai bảng theo cột chung
"Variable".
Giữ toàn bộ hàng từ bảng trái.
kable(variable_summary, caption = "Bảng: Mô tả và kiểu dữ liệu của các biến trong bộ dữ liệu")
| Variable | Meaning | DataType |
|---|---|---|
| year | Năm sản xuất của xe | numeric |
| make | Hãng sản xuất | character |
| model | Tên dòng xe cụ thể | character |
| trim | Phiên bản hoặc cấp độ trang bị của xe | character |
| body | Kiểu dáng xe | character |
| transmission | Loại hộp số | character |
| vin | Số khung xe | character |
| state | Bang hoặc khu vực đăng ký | character |
| condition | Tình trạng của xe | numeric |
| odometer | Số km xe đã đi | numeric |
| color | Màu sơn bên ngoài | character |
| interior | Màu nội thất bên trong | character |
| seller | Tên người bán hoặc đại lý | character |
| mmr | Giá trị thị trường trung bình (MMR) | numeric |
| sellingprice | Giá bán thực tế (USD) | numeric |
| saledate | Ngày bán xe | character |
Bộ dữ liệu gồm 16 biến, bao quát thông tin từ năm sản xuất, hãng xe, dòng xe, kiểu dáng, tình trạng, số km đã đi, đến giá bán thực tế.
Trong đó:
Biến định lượng (numeric): year,
condition, odometer, mmr,
sellingprice.
Biến định tính (character): chiếm đa số, mô tả
đặc điểm chi tiết của xe như make, model,
color, interior, seller,
…
-> Bộ dữ liệu kết hợp giữa dữ liệu định tính (mô tả đặc trưng xe) và dữ liệu định lượng (phục vụ cho phân tích giá bán).
# Chọn các biến định lượng
data_numeric <- data %>%
select(condition, odometer, mmr, sellingprice)
# Tính các thống kê mô tả cơ bản
descriptive_stats <- data_numeric %>%
summarise(
`Số quan sát hợp lệ` = n(),
`Giá trị thiếu (NA)` = sum(is.na(condition)) + sum(is.na(odometer)) +
sum(is.na(mmr)) + sum(is.na(sellingprice)),
.groups = "drop"
)
# Tạo bảng mô tả từng biến
descriptive_table <- data_numeric %>%
summarise(
`Giá trị nhỏ nhất` = sapply(., min, na.rm = TRUE),
`Q1 (25%)` = sapply(., quantile, probs = 0.25, na.rm = TRUE),
`Trung vị (50%)` = sapply(., median, na.rm = TRUE),
`Trung bình` = sapply(., mean, na.rm = TRUE),
`Q3 (75%)` = sapply(., quantile, probs = 0.75, na.rm = TRUE),
`Giá trị lớn nhất` = sapply(., max, na.rm = TRUE),
`Số giá trị thiếu` = sapply(., function(x) sum(is.na(x)))
) %>%
t() %>%
as.data.frame()
# Gắn tên cột
colnames(descriptive_table) <- c("condition", "odometer", "mmr", "sellingprice")
# Hiển thị bảng
kable(
descriptive_table,
caption = "Bảng: Thống kê mô tả các biến định lượng"
)
| condition | odometer | mmr | sellingprice | |
|---|---|---|---|---|
| Giá trị nhỏ nhất | 1.00000 | 1.00 | 25.00 | 1.00 |
| Q1 (25%) | 24.00000 | 28137.00 | 7425.00 | 7200.00 |
| Trung vị (50%) | 35.00000 | 51085.00 | 12300.00 | 12200.00 |
| Trung bình | 30.77418 | 66701.73 | 13837.06 | 13690.51 |
| Q3 (75%) | 41.00000 | 96590.00 | 18300.00 | 18200.00 |
| Giá trị lớn nhất | 49.00000 | 999999.00 | 182000.00 | 230000.00 |
| Số giá trị thiếu | 0.00000 | 0.00 | 0.00 | 0.00 |
# Chọn các biến định tính
data_categorical <- data %>%
select(make, model, trim, body, transmission, vin, state, color, interior, seller, saledate)
# Bảng số lượng giá trị phân biệt của từng biến định tính
unique_counts <- data.frame(
Variable = names(data_categorical),
So_gia_tri_phan_biet = sapply(data_categorical, n_distinct)
)
kable(
unique_counts,
caption = "Số lượng giá trị phân biệt của từng biến định tính"
) %>% kable_styling(full_width = FALSE)
| Variable | So_gia_tri_phan_biet | |
|---|---|---|
| make | make | 53 |
| model | model | 768 |
| trim | trim | 1494 |
| body | body | 85 |
| transmission | transmission | 2 |
| vin | vin | 465768 |
| state | state | 34 |
| color | color | 20 |
| interior | interior | 17 |
| seller | seller | 11923 |
| saledate | saledate | 3609 |
data <- data %>%
mutate(
Nhom_nam = case_when(
year < 2000 ~ "Trước năm 2000",
year >= 2000 & year <= 2010 ~ "2000 - 2010",
year > 2010 ~ "Sau 2010"
)
)
data %>%
group_by(Nhom_nam) %>%
summarise(
So_luong_xe = n(),
Gia_ban_tb = mean(sellingprice, na.rm = TRUE)
)
## # A tibble: 3 × 3
## Nhom_nam So_luong_xe Gia_ban_tb
## <chr> <int> <dbl>
## 1 2000 - 2010 172016 7331.
## 2 Sau 2010 293937 17682.
## 3 Trước năm 2000 6372 1229.
Bảng thể hiện Top 10 hãng xe có số lượng giao dịch nhiều nhất
bang_phan_to <- data %>%
group_by(make) %>%
summarise(
So_luong_xe = n(),
Gia_trung_binh = mean(sellingprice, na.rm = TRUE),
Gia_thap_nhat = min(sellingprice, na.rm = TRUE),
Gia_cao_nhat = max(sellingprice, na.rm = TRUE)
) %>%
arrange(desc(So_luong_xe)) %>%
head(10)
bang_phan_to
## # A tibble: 10 × 5
## make So_luong_xe Gia_trung_binh Gia_thap_nhat Gia_cao_nhat
## <chr> <int> <dbl> <dbl> <dbl>
## 1 Ford 81013 14830. 1 230000
## 2 Chevrolet 54150 11866. 100 84500
## 3 Nissan 44043 11745. 100 86400
## 4 Toyota 35313 12422. 150 68900
## 5 Dodge 27181 10964. 100 51700
## 6 Honda 24781 10943. 200 37000
## 7 Hyundai 18659 11062. 100 55000
## 8 BMW 17509 21290. 100 165000
## 9 Kia 15828 11768. 200 42750
## 10 Chrysler 15133 10627. 100 41000
Hãng Ford có số lượng xe lớn nhất
(93.554 chiếc), chiếm tỷ trọng cao nhất trong toàn bộ
dữ liệu.
Các hãng Chevrolet, Nissan, Toyota cũng có lượng xe giao dịch lớn, dao động từ 39.000–60.000 xe.
Mức giá trung bình giữa các hãng có sự chênh lệch đáng kể:
Giá cao nhất trong dữ liệu lên tới 230.000 USD (Ford) — có thể là các dòng xe đặc biệt hoặc xe mới cao cấp.
➡️ Nhìn chung, thị trường xe có sự đa dạng lớn về giá và thương hiệu, thể hiện rõ xu hướng phổ biến của các hãng tầm trung.
data_phan_to_km <- data %>%
mutate(
Nhom_km = cut(
odometer,
breaks = c(-Inf, 10000, 50000, 100000, 200000, Inf),
labels = c("< 10.000 km", "10.000–50.000 km", "50.000–100.000 km", "100.000–200.000 km", "> 200.000 km")
)
) %>%
group_by(Nhom_km) %>%
summarise(
So_luong_xe = n(),
Gia_trung_binh = round(mean(sellingprice, na.rm = TRUE), 2),
Gia_thap_nhat = min(sellingprice, na.rm = TRUE),
Gia_cao_nhat = max(sellingprice, na.rm = TRUE)
) %>%
arrange(Nhom_km)
kable(data_phan_to_km, caption = "Bảng phân tổ số km đã đi và giá bán trung bình")
| Nhom_km | So_luong_xe | Gia_trung_binh | Gia_thap_nhat | Gia_cao_nhat |
|---|---|---|---|---|
| < 10.000 km | 22072 | 22799.91 | 1 | 173000 |
| 10.000–50.000 km | 210362 | 18428.14 | 1 | 230000 |
| 50.000–100.000 km | 129592 | 11718.66 | 100 | 108200 |
| 100.000–200.000 km | 101465 | 5368.72 | 100 | 43000 |
| > 200.000 km | 8834 | 2622.74 | 100 | 26800 |
Nhận xét: - Nhìn chung, quãng đường xe đã đi
(odometer) có mối quan hệ nghịch với
giá bán trung bình: xe càng đi nhiều km thì giá càng
giảm.
- Nhóm xe đi dưới 10.000 km có giá bán trung
bình cao nhất (≈ 22.850 USD), thể hiện giá trị gần như xe
mới.
- Nhóm 10.000–50.000 km có số lượng lớn nhất (≈ 244.000
xe), cho thấy đây là mức sử dụng phổ biến trên thị trường xe cũ.
- Khi quãng đường tăng từ 50.000 đến 200.000 km, giá
trung bình giảm mạnh, đặc biệt nhóm 100.000–200.000 km
chỉ còn khoảng 5.300 USD.
- Nhóm xe trên 200.000 km có giá trung bình rất thấp (≈
2.500 USD), phản ánh mức khấu hao lớn.
- Có một số quan sát bị thiếu giá trị km (NA), tuy
nhiên số lượng này rất nhỏ (94 xe), nên không ảnh hưởng đáng kể đến kết
quả chung.
data <- data %>%
mutate(
Nhom_gia = case_when(
sellingprice < 10000 ~ "< 10.000 USD",
sellingprice >= 10000 & sellingprice < 20000 ~ "10.000–20.000 USD",
sellingprice >= 20000 & sellingprice < 40000 ~ "20.000–40.000 USD",
sellingprice >= 40000 & sellingprice < 60000 ~ "40.000–60.000 USD",
sellingprice >= 60000 & sellingprice < 100000 ~ "60.000–100.000 USD",
sellingprice >= 100000 ~ "> 100.000 USD",
TRUE ~ NA_character_
)
)
bang_gia <- data %>%
group_by(Nhom_gia) %>%
summarise(
So_luong_xe = n(),
Gia_trung_binh = mean(sellingprice, na.rm = TRUE),
Gia_thap_nhat = min(sellingprice, na.rm = TRUE),
Gia_cao_nhat = max(sellingprice, na.rm = TRUE)
) %>%
arrange(Gia_trung_binh)
kable(bang_gia, caption = "Bảng phân tổ theo mức giá bán của xe")
| Nhom_gia | So_luong_xe | Gia_trung_binh | Gia_thap_nhat | Gia_cao_nhat |
|---|---|---|---|---|
| < 10.000 USD | 175481 | 5311.016 | 1e+00 | 9975 |
| 10.000–20.000 USD | 200976 | 14177.833 | 1e+04 | 19901 |
| 20.000–40.000 USD | 88089 | 25918.226 | 2e+04 | 39999 |
| 40.000–60.000 USD | 6484 | 46261.150 | 4e+04 | 59900 |
| 60.000–100.000 USD | 1143 | 72524.621 | 6e+04 | 99500 |
| > 100.000 USD | 152 | 125137.829 | 1e+05 | 230000 |
library(ggplot2)
ggplot(data, aes(x = odometer, y = sellingprice)) +
geom_point(alpha = 0.3, color = "steelblue") +
geom_smooth(method = "lm", color = "red") +
labs(
title = "Mối quan hệ giữa số km đã đi và giá bán xe",
x = "Số km đã đi (odometer)",
y = "Giá bán (USD)"
)
Biểu đồ cho thấy mối quan hệ nghịch chiều giữa số km đã đi và giá bán xe — xe đi càng nhiều thì giá bán càng giảm. Điều này phản ánh giá trị xe giảm dần theo mức độ hao mòn sử dụng.
ggplot(data, aes(x = factor(year), y = sellingprice)) +
geom_boxplot(fill = "lightblue", color = "darkblue") +
labs(
title = "Giá bán theo năm sản xuất của xe",
x = "Năm sản xuất",
y = "Giá bán (USD)"
)
ggplot(data, aes(x = condition, y = sellingprice)) +
geom_boxplot(fill = "orange", color = "darkred") +
labs(
title = "Ảnh hưởng của tình trạng xe đến giá bán",
x = "Tình trạng xe",
y = "Giá bán (USD)"
)
data_make <- data %>%
group_by(make) %>%
summarise(Gia_trung_binh = mean(sellingprice, na.rm = TRUE)) %>%
arrange(desc(Gia_trung_binh)) %>%
slice(1:10)
# chỉ lấy top 10 hãng
ggplot(data_make, aes(x = reorder(make, Gia_trung_binh), y = Gia_trung_binh)) +
geom_col(fill = "skyblue") +
coord_flip() +
labs(
title = "Top 10 hãng xe có giá bán trung bình cao nhất",
x = "Hãng xe",
y = "Giá bán trung bình (USD)"
)
ggplot(data, aes(x = year, y = odometer)) +
geom_point(alpha = 0.3, color = "darkgreen") +
geom_smooth(method = "lm", color = "red") +
labs(
title = "Mối quan hệ giữa năm sản xuất và số km đã đi",
x = "Năm sản xuất",
y = "Số km đã đi (odometer)"
)