library(moments)
library(psych)
## Warning: package 'psych' was built under R version 4.5.1
library(readxl)
## Warning: package 'readxl' was built under R version 4.5.1
library(dplyr)
## Warning: package 'dplyr' was built under R version 4.5.1
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(tidyr)
## Warning: package 'tidyr' was built under R version 4.5.1
library(stringr)
## Warning: package 'stringr' was built under R version 4.5.1
library(ggplot2)
## Warning: package 'ggplot2' was built under R version 4.5.1
##
## Attaching package: 'ggplot2'
## The following objects are masked from 'package:psych':
##
## %+%, alpha
library(scales)
## Warning: package 'scales' was built under R version 4.5.1
##
## Attaching package: 'scales'
## The following objects are masked from 'package:psych':
##
## alpha, rescale
library(kableExtra)
## Warning: package 'kableExtra' was built under R version 4.5.1
##
## Attaching package: 'kableExtra'
## The following object is masked from 'package:dplyr':
##
## group_rows
library(janitor)
## Warning: package 'janitor' was built under R version 4.5.1
##
## Attaching package: 'janitor'
## The following objects are masked from 'package:stats':
##
## chisq.test, fisher.test
library(reshape2)
## Warning: package 'reshape2' was built under R version 4.5.1
##
## Attaching package: 'reshape2'
## The following object is masked from 'package:tidyr':
##
## smiths
dataset <- read_excel(file.choose())
head(dataset)
## # A tibble: 6 × 13
## `Mal ID` Username Gender `Days Watched` `Mean Score` Watching Completed
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 1 Xinil Male 142. 7.37 1 233
## 2 3 Aokaado Male 68.6 7.34 23 137
## 3 4 Crystal Female 213. 6.68 16 636
## 4 20 vondur Male 73.1 8.06 11 94
## 5 36 Baman Male 272. 5.9 27 1144
## 6 44 beddan Male 18.6 7.6 0 37
## # ℹ 6 more variables: `On Hold` <dbl>, Dropped <dbl>, `Plan to Watch` <dbl>,
## # `Total Entries` <dbl>, Rewatched <dbl>, `Episodes Watched` <dbl>
Giải thích: Hàm head() hiển thị 6 dòng đầu tiên của bộ dữ liệu.
Nhận xét: Giúp người phân tích có cái nhìn trực quan về cấu trúc dữ
liệu: tên biến, kiểu dữ liệu, dạng giá trị.Phát hiện nhanh lỗi nhập
liệu, giá trị NA, hoặc định dạng sai (chẳng hạn cột số nhưng bị lưu dạng
ký tự).
dim(dataset)
## [1] 224383 13
Giải thích: Hàm dim() trả về kích thước của bộ dữ liệu, bao gồm số
dòng (quan sát) và số cột (biến).
Nhận xét: Kết quả cho biết bộ dữ liệu có 224,383 quan sát và 13 biến (có
224,383 người dùng và 13 đặc trưng mô tả mỗi người dùng đó).
names(dataset)
## [1] "Mal ID" "Username" "Gender" "Days Watched"
## [5] "Mean Score" "Watching" "Completed" "On Hold"
## [9] "Dropped" "Plan to Watch" "Total Entries" "Rewatched"
## [13] "Episodes Watched"
Giải thích: Hàm name() hiển thị tên của tất cả các biến (cột) trong
bộ dữ liệu.
Nhận xét: Việc liệt kê tên biến giúp người phân tích nắm được cấu trúc
thông tin, phục vụ cho việc chọn lọc, xử lý và trực quan hóa dữ liệu ở
các bước sau.
capture.output(str(dataset))
## [1] "tibble [224,383 × 13] (S3: tbl_df/tbl/data.frame)"
## [2] " $ Mal ID : num [1:224383] 1 3 4 20 36 44 47 66 70 77 ..."
## [3] " $ Username : chr [1:224383] \"Xinil\" \"Aokaado\" \"Crystal\" \"vondur\" ..."
## [4] " $ Gender : chr [1:224383] \"Male\" \"Male\" \"Female\" \"Male\" ..."
## [5] " $ Days Watched : num [1:224383] 142.3 68.6 212.8 73.1 272.1 ..."
## [6] " $ Mean Score : num [1:224383] 7.37 7.34 6.68 8.06 5.9 7.6 6.84 7.53 7.18 6.38 ..."
## [7] " $ Watching : num [1:224383] 1 23 16 11 27 0 15 34 30 13 ..."
## [8] " $ Completed : num [1:224383] 233 137 636 94 1144 ..."
## [9] " $ On Hold : num [1:224383] 8 99 303 11 11 0 22 13 9 0 ..."
## [10] " $ Dropped : num [1:224383] 93 44 0 2 55 0 3 6 8 0 ..."
## [11] " $ Plan to Watch : num [1:224383] 64 40 45 20 338 0 19 10 22 2 ..."
## [12] " $ Total Entries : num [1:224383] 399 343 1000 138 1575 ..."
## [13] " $ Rewatched : num [1:224383] 60 15 10 7 36 0 1 50 15 0 ..."
## [14] " $ Episodes Watched: num [1:224383] 8458 4072 12781 4374 16309 ..."
Giải thích:
- Hàm stc() mô tả cấu trúc chi tiết của dataset, bao gồm loại dữ liệu
(numeric, character, factor,…) và vài giá trị mẫu của từng biến.
- Hàm capture.output() được dùng để lưu kết quả xuất ra thay vì in ra
màn hình.
Nhận xét: Giúp người phân tích hiểu rõ kiểu dữ liệu của từng biến, từ đó
chọn phương pháp xử lý phù hợp.
summary(dataset)
## Mal ID Username Gender Days Watched
## Min. : 1 Length:224383 Length:224383 Min. : 0.00
## 1st Qu.: 131461 Class :character Class :character 1st Qu.: 6.30
## Median : 317908 Mode :character Mode :character Median : 29.20
## Mean : 384653 Mean : 53.99
## 3rd Qu.: 481725 3rd Qu.: 72.60
## Max. :1291097 Max. :105338.60
## NA's :8
## Mean Score Watching Completed On Hold
## Min. : 0.000 Min. : 0.00 Min. : 0.0 Min. : 0.000
## 1st Qu.: 7.000 1st Qu.: 1.00 1st Qu.: 9.0 1st Qu.: 0.000
## Median : 7.800 Median : 4.00 Median : 59.0 Median : 1.000
## Mean : 6.808 Mean : 10.18 Mean : 151.9 Mean : 7.979
## 3rd Qu.: 8.470 3rd Qu.: 10.00 3rd Qu.: 183.0 3rd Qu.: 7.000
## Max. :10.000 Max. :2934.00 Max. :13226.0 Max. :5167.000
## NA's :8 NA's :8 NA's :8 NA's :8
## Dropped Plan to Watch Total Entries Rewatched
## Min. : 0.00 Min. : 0.00 Min. : 0.0 Min. : 0.00
## 1st Qu.: 0.00 1st Qu.: 0.00 1st Qu.: 16.0 1st Qu.: 0.00
## Median : 1.00 Median : 7.00 Median : 94.0 Median : 0.00
## Mean : 10.39 Mean : 42.11 Mean : 222.5 Mean : 10.45
## 3rd Qu.: 8.00 3rd Qu.: 37.00 3rd Qu.: 279.0 3rd Qu.: 5.00
## Max. :14341.00 Max. :21804.00 Max. :24817.0 Max. :13215.00
## NA's :8 NA's :8 NA's :8 NA's :8
## Episodes Watched
## Min. : 0
## 1st Qu.: 377
## Median : 1748
## Mean : 3441
## 3rd Qu.: 4386
## Max. :5433345
## NA's :8
Giải thích: Hàm summary() cung cấp thống kê mô tả cơ bản cho từng
biến.
- Biến định lượng hiển thị min, max, median, mean, quartiles.
- Biến định tính hiển thị tần suất từng mức giá trị.
Nhận xét: Giúp nắm được xu hướng trung tâm, phạm vi giá trị, và phát
hiện các giá trị bất thường (outlier) trong dữ liệu.
sum(duplicated(dataset))
## [1] 0
Giải thích:
- Hàm duplicated() xác định những dòng (quan sát) bị trùng trong bộ dữ
liệu, trả về giá trị logic TRUE/FALSE.
- Hàm sum() cộng tổng số TRUE → tức là đếm số dòng bị trùng lặp. Nếu kết
quả > 0, bộ dữ liệu có các bản ghi lặp lại và cần xử lý (loại bỏ hoặc
gộp lại). Nếu = 0, dữ liệu là duy nhất, không bị trùng.
sum(sapply(dataset, is.numeric))
## [1] 11
Giải thích:
- Hàm sapply() áp dụng is.numeric cho từng biến trong dataset để kiểm
tra xem biến đó có phải dạng số hay không.
- Hàm sum() sau đó cộng tổng số biến có giá trị TRUE chính là số lượng
biến định lượng trong dataset.
Nhận xét: Giúp xác định có bao nhiêu biến có thể sử dụng cho các phép
tính thống kê, mô hình hóa hoặc trực quan hóa định lượng.
sum(sapply(dataset, function(x) is.factor(x) | is.character(x)))
## [1] 2
Giải thích:
- Kiểm tra xem từng biến có phải kiểu phân loại (factor) hoặc kiểu chuỗi
ký tự (character) hay không.
- Hàm sum() sẽ cộng tổng các biến có kiểu đó trả về số lượng biến định
tính.
Nhận xét: Giúp xác định có bao nhiêu biến định tính để dùng trong việc
phân tích nhóm, vẽ biểu đồ tần suất, hoặc dùng làm biến phân loại trong
mô hình thống kê.
names(dataset)[sapply(dataset, is.numeric)]
## [1] "Mal ID" "Days Watched" "Mean Score" "Watching"
## [5] "Completed" "On Hold" "Dropped" "Plan to Watch"
## [9] "Total Entries" "Rewatched" "Episodes Watched"
Giải thích: Hàm sapply(dataset, is.numeric) tạo ra một vector logic,
TRUE tại những biến có kiểu số. Sau đó names(dataset)[…] lọc ra tên của
các biến định lượng.
Nhận xét: Giúp người phân tích biết rõ biến nào có thể sử dụng cho các
phép tính trung bình, phương sai, hoặc vẽ biểu đồ dạng histogram,
scatter plot,…
names(dataset)[sapply(dataset, function(x) is.factor(x) | is.character(x))]
## [1] "Username" "Gender"
Giải thích: Hàm sapply(dataset, function(x) is.factor(x) |
is.character(x)) kiểm tra từng cột trong dataset. Trong đó:
- is.factor(x) kiểm tra xem biến có kiểu phân loại (như giới tính, trạng
thái xem phim, thể loại, v.v.) không.
- is.character(x) kiểm tra xem biến có kiểu chuỗi ký tự không (ví dụ:
tên anime, username, thể loại…).
- Dấu | nghĩa là “hoặc”: chỉ cần thỏa một trong hai điều kiện là
TRUE.
- names(dataset)[…] để lấy tên của các biến có giá trị TRUE (biến định
tính)
Kết quả trả về danh sách các biến định tính trong bộ dữ liệu, ví dụ như
gender, type, status, title,…
Những biến này được dùng cho việc phân tích nhóm hoặc tần suất (đếm số
lượng từng loại), vẽ biểu đồ cột, biểu đồ tròn hay phân tích mối quan hệ
giữa các nhóm.
Nhận xét: Việc xác định các biến định tính giúp người phân tích biết
được đâu là biến phân loại, qua đó chọn kỹ thuật xử lý và trực quan hóa
phù hợp.
variable_meaning <- data.frame(
Variable = c("mal_id", "gender", "type", "episodes_watched", "mean_score",
"days_watched", "rewatched", "on_hold", "dropped", "completed",
"plan_to_watch", "total_entries", "title"),
Meaning = c("Mã định danh duy nhất của anime trên hệ thống (MyAnimeList ID)",
"Giới tính của người dùng (Nam/Nữ/Khác)",
"Loại anime (TV, Movie, OVA, ONA, Special, ...)",
"Số tập phim mà người dùng đã xem",
"Điểm trung bình mà người dùng đánh giá anime (thang điểm 1–10)",
"Tổng số ngày mà người dùng dành để xem anime",
"Số lần người dùng xem lại anime (Rewatched)",
"Số anime người dùng đang tạm dừng xem (On-hold)",
"Số anime người dùng đã bỏ xem (Dropped)",
"Số anime người dùng đã hoàn thành (Completed)",
"Số anime người dùng dự định sẽ xem (Plan to watch)",
"Tổng số anime mà người dùng có trong danh sách xem",
"Tên anime hoặc series"))
variable_meaning
## Variable
## 1 mal_id
## 2 gender
## 3 type
## 4 episodes_watched
## 5 mean_score
## 6 days_watched
## 7 rewatched
## 8 on_hold
## 9 dropped
## 10 completed
## 11 plan_to_watch
## 12 total_entries
## 13 title
## Meaning
## 1 Mã định danh duy nhất của anime trên hệ thống (MyAnimeList ID)
## 2 Giới tính của người dùng (Nam/Nữ/Khác)
## 3 Loại anime (TV, Movie, OVA, ONA, Special, ...)
## 4 Số tập phim mà người dùng đã xem
## 5 Điểm trung bình mà người dùng đánh giá anime (thang điểm 1–10)
## 6 Tổng số ngày mà người dùng dành để xem anime
## 7 Số lần người dùng xem lại anime (Rewatched)
## 8 Số anime người dùng đang tạm dừng xem (On-hold)
## 9 Số anime người dùng đã bỏ xem (Dropped)
## 10 Số anime người dùng đã hoàn thành (Completed)
## 11 Số anime người dùng dự định sẽ xem (Plan to watch)
## 12 Tổng số anime mà người dùng có trong danh sách xem
## 13 Tên anime hoặc series
Giải thích:
- Dùng hàm data.frame() để tạo một bảng dữ liệu mới (data frame). Mỗi
cột trong bảng được truyền vào qua đối số, có thể là vector, list hoặc
giá trị cụ thể.
- Dùng Variable = c(…) để tạo một vector chứa tên các biến có trong bộ
dữ liệu gốc (dataset). Các tên biến như mal_id, gender, type,
episodes_watched,… được liệt kê đầy đủ để đảm bảo người đọc biết rõ từng
biến xuất hiện trong dataset. Đây là cột đầu tiên trong bảng
variable_meaning, giữ vai trò liệt kê biến gốc.
- Dùng Meaning = c(…), cột thứ hai là một vector mô tả ý nghĩa cụ thể
của từng biến. Mỗi phần tử tương ứng 1 dòng với biến ở cột
Variable.
- Variable_meaning <- … Dấu <- dùng để gán kết quả (tức là bảng
mới vừa tạo) vào một biến có tên variable_meaning. Sau khi chạy lệnh
này, người thực hiện có thể xem bảng bằng cách gọi variable_meaning ở
dòng sau.
- variable_meaning: Khi gọi riêng tên bảng, R sẽ hiển thị nội dung của
nó ra màn hình. Kết quả là một bảng gồm 2 cột là Variable (Tên biến
trong dữ liệu gốc) và Meaning (Giải thích ý nghĩa của biến đó).
Nhận xét: Giúp người phân tích nắm bắt nhanh và rõ ràng nội dung của
từng biến, đảm bảo quá trình phân tích, trực quan hóa và diễn giải kết
quả được chính xác, logic và có cơ sở.
colnames(dataset) <- colnames(dataset) %>%
str_to_lower() %>%
str_replace_all("\\s+", "_") %>%
str_replace_all("[^a-z0-9_]", "")
Giải thích: Lấy tên cột hiện tại (colnames(dataset)), chuyển tất cả
thành chữ thường (str_to_lower()), thay mọi khoảng trắng (1 hoặc nhiều)
bằng dấu gạch dưới _ (str_replace_all(“\s+”, “”)), rồi loại bỏ mọi
ký tự không phải chữ cái thường, chữ số hoặc
(str_replace_all(”[^a-z0-9_]“,”“)).
Nhận xét: Việc chuẩn hóa tên cột giúp bộ dữ liệu đạt được tính thống
nhất và chuyên nghiệp trong quá trình xử lý. Các tên biến sau khi chuyển
thành chữ thường, loại bỏ khoảng trắng và ký tự đặc biệt sẽ giúp hạn chế
tối đa lỗi cú pháp khi gọi biến trong R, đồng thời đảm bảo khả năng
tương thích cao với các gói phân tích (đặc biệt là tidyverse). Bên cạnh
đó, thao tác này còn giúp việc đọc hiểu tên biến trở nên trực quan, dễ
thao tác và đồng nhất.
num_candidates <- c("score","members","episodes","popularity","votes")
for (v in num_candidates) {
if (v %in% colnames(dataset)) {
dataset[[v]] <- as.numeric(str_replace_all(as.character(dataset[[v]]), ",", ""))
}
}
Giải thích: Với từng tên trong num_candidates nếu có trong dataset,
mã sẽ:
- Ép kiểu sang character (để chắc chắn thao tác string)
- Loại bỏ dấu phẩy , (ví dụ “1,234” → “1234”) bằng
str_replace_all,
- Chuyển sang numeric bằng as.numeric.
Nhận xét: Dữ liệu định lượng nếu được lưu ở dạng ký tự (text) sẽ không
thể thực hiện các phép tính thống kê hay mô hình hoá. Do đó, việc chuyển
đổi về dạng numeric là bước quan trọng nhằm chuẩn bị dữ liệu cho quá
trình phân tích sau này. Đồng thời, việc loại bỏ dấu phẩy trong các giá
trị như “1,000” giúp tránh lỗi ép kiểu và đảm bảo tính chính xác của các
phép tính trung bình, phương sai hoặc hồi quy.
dataset <- dataset %>%
mutate(
username = str_trim(username),
gender = str_trim(gender)
)
Giải thích: Hàm str_trim() loại bỏ khoảng trắng ở đầu và cuối chuỗi
(leading/trailing). mutate() cập nhật lại hai cột username và
gender.
Nhận xét: Khoảng trắng dư thừa hoặc định dạng không nhất quán trong
chuỗi ký tự có thể khiến các thao tác nhóm hoặc lọc dữ liệu bị sai lệch.
Việc loại bỏ khoảng trắng và chuẩn hóa chữ giúp các biến dạng text (như
username, gender) được đồng nhất, từ đó đảm bảo độ tin cậy cho các phân
tích so sánh hoặc thống kê tần suất. Đây là một bước nhỏ nhưng đóng vai
trò quan trọng trong việc làm sạch dữ liệu.
for (cname in c("type","rating","status","popularity_bucket")) {
if (cname %in% colnames(dataset)) dataset[[cname]] <- as.factor(dataset[[cname]])
}
Giải thích: Với mỗi tên cột liệt kê, nếu tồn tại trong dataset thì
chuyển kiểu của cột đó thành factor.
Nhận xét: Biến factor trong R được hiểu như biến định tính có giới hạn
số lượng giá trị (levels). Việc chuyển các cột như type, rating, status,
popularity_bucket sang dạng factor giúp chương trình nhận biết được đây
là biến phân loại, không phải biến số. Điều này giúp cho quá trình trực
quan hóa trở nên chính xác và có ý nghĩa thống kê hơn.
dataset <- dataset %>%
mutate(
gender = case_when(
str_to_lower(gender) %in% c("male", "m") ~ "Male",
str_to_lower(gender) %in% c("female", "f") ~ "Female",
TRUE ~ gender
)
)
Giải thích: Dùng case_when() để chuẩn hóa giá trị trong cột gender:
các biến thể như “male”, “M” → “Male”, tương tự cho “Female”. Các giá
trị khác giữ nguyên.
Nhận xét: Giới tính là biến thường xuyên bị nhập dữ liệu không đồng nhất
(ví dụ “Male”, “male”, “M”, “m”). Việc chuẩn hóa các giá trị này về cùng
một dạng giúp dữ liệu trở nên đồng nhất, dễ đọc và dễ xử lý. Đồng thời,
việc thống nhất cách viết (viết hoa chữ cái đầu) còn giúp biểu đồ, bảng
thống kê trở nên nhất quán và chuyên nghiệp hơn.
if ("genre" %in% colnames(dataset)) {
dataset <- dataset %>% mutate(genre_list = str_split(genre, ",\\s*"))
}
Giải thích: Nếu tồn tại cột genre (chuỗi chứa nhiều thể loại phân
tách bằng dấu phẩy), str_split(…, “,\s*“) sẽ tách mỗi ô thành một list
các thể loại và lưu vào cột genre_list.
Nhận xét: Trong nhiều tập dữ liệu, một bản ghi có thể thuộc nhiều thể
loại (multi-label). Việc tách cột genre thành danh sách riêng
(genre_list) cho phép khai thác sâu hơn các mối quan hệ giữa thể loại,
giúp người phân tích dễ dàng thực hiện thống kê theo từng nhãn riêng
biệt. Nhờ đó, có thể thực hiện các phân tích đa chiều.
numeric_cols <- names(dataset)[sapply(dataset, is.numeric)]
for (nc in numeric_cols) {
na_count <- sum(is.na(dataset[[nc]]))
if (na_count > 0) {
dataset[[nc]][is.na(dataset[[nc]])] <- median(dataset[[nc]], na.rm = TRUE)
message("Imputed ", na_count, " NA in ", nc, " with median.")
}
}
## Imputed 8 NA in days_watched with median.
## Imputed 8 NA in mean_score with median.
## Imputed 8 NA in watching with median.
## Imputed 8 NA in completed with median.
## Imputed 8 NA in on_hold with median.
## Imputed 8 NA in dropped with median.
## Imputed 8 NA in plan_to_watch with median.
## Imputed 8 NA in total_entries with median.
## Imputed 8 NA in rewatched with median.
## Imputed 8 NA in episodes_watched with median.
Giải thích: Xác định các cột numeric,với mỗi cột có NA, thay các giá
trị NA bằng median của cột đó và in thông báo. Vì Median là lựa chọn
robust (không bị ảnh hưởng mạnh bởi outlier) so với mean. Việc impute
giữ kích thước mẫu đầy đủ để thuận tiện cho phân tích hoặc mô hình mà
không làm méo phân bố quá nhiều.
Nhận xét: Trung vị (median) là một thước đo đại diện cho xu hướng trung
tâm, ít bị ảnh hưởng bởi các giá trị ngoại lai. Việc thay thế các giá
trị thiếu (NA) trong biến số bằng trung vị giúp giữ lại kích thước mẫu
đầy đủ, đảm bảo dữ liệu không bị mất mát khi thực hiện các mô hình hồi
quy hoặc phân tích mô tả. Đồng thời, phương pháp này giúp giảm thiểu sai
lệch so với việc loại bỏ hàng chứa giá trị thiếu.
fac_cols <- names(dataset)[sapply(dataset, is.factor)]
for (fc in fac_cols) dataset[[fc]] <- fct_explicit_na(dataset[[fc]], na_level = "unknown")
Giải thích: Tìm các cột kiểu factor và biến các giá trị NA thành một
level mới có tên “unknown” bằng forcats::fct_explicit_na(). Việc giữ NA
như NA có thể gây lỗi khi vẽ hoặc khi chuyển dữ liệu cho mô hình (một số
hàm bỏ NA). Biến NA thành level unknown vừa giữ thông tin rằng “thiếu dữ
liệu”, vừa cho phép phân tích tần suất, so sánh và trực quan hóa nhóm
unknown.
Nhận xét: Thay vì loại bỏ các giá trị bị thiếu, việc gán nhãn “unknown”
giúp bảo toàn thông tin và phản ánh rõ ràng tình trạng “thiếu dữ liệu”
trong phân tích. Cách xử lý này có thể hữu ích trong việc mô tả dữ liệu,
giúp người đọc dễ dàng nhận thấy tỷ lệ thiếu trong từng nhóm, đồng thời
tránh lỗi khi vẽ biểu đồ hoặc khi chuyển dữ liệu vào mô hình học
máy.
if (all(c("title","release_year") %in% colnames(dataset))) {
dataset <- dataset %>% distinct(title, release_year, .keep_all = TRUE)
}
Giải thích: Nếu cả hai cột tồn tại, distinct(title, release_year,
.keep_all = TRUE) giữ lại bản ghi đầu tiên cho mỗi cặp (title,
release_year) và loại bỏ các bản ghi trùng lặp.
Nhận xét: Các bản ghi trùng lặp có thể làm sai lệch kết quả thống kê,
đặc biệt là khi tính số lượng hoặc trung bình. Việc loại bỏ trùng lặp
theo cặp (title, release_year) giúp đảm bảo tính duy nhất cho mỗi bộ
phim trong từng năm phát hành, góp phần làm cho tập dữ liệu trở nên
chính xác, tin cậy và phản ánh đúng thực tế.
if ("members" %in% colnames(dataset)) {
dataset <- dataset %>% mutate(
popularity_bucket = case_when(
members >= quantile(members, .90, na.rm=TRUE) ~ "very_high",
members >= quantile(members, .75, na.rm=TRUE) ~ "high",
members >= quantile(members, .50, na.rm=TRUE) ~ "medium",
TRUE ~ "low"
)
)
dataset$popularity_bucket <- factor(dataset$popularity_bucket, levels = c("low","medium","high","very_high"))
}
Giải thích: Sử dụng phân vị (quantile) của cột members để chia thành
4 nhóm: low (<50th), medium (50–75), high (75–90), very_high (≥90th).
Sau đó chuyển sang factor với thứ tự mong muốn.
Nhận xét: Việc phân nhóm mức độ phổ biến dựa trên các phân vị
(quantiles) giúp chia dữ liệu thành các nhóm có ý nghĩa thống kê như
“low”, “medium”, “high”, “very high”. Nhờ đó, người phân tích có thể dễ
dàng nhận biết sự khác biệt giữa các nhóm, phục vụ cho việc so sánh,
trực quan hóa và xây dựng mô hình dự đoán. Bước này không chỉ giúp dữ
liệu trở nên dễ hiểu mà còn tăng giá trị ứng dụng thực tiễn trong việc
đánh giá xu hướng phổ biến của từng bộ phim hoặc tác phẩm.
num_data <- dataset %>%
select(days_watched, mean_score, watching, completed, on_hold,
dropped, plan_to_watch, total_entries, rewatched, episodes_watched)
Giải thích: Lệnh select() trong gói dplyr được dùng để chọn ra các
cột định lượng cần thống kê mô tả. Ở đây, ta chọn 10 biến thể hiện hành
vi xem anime của người dùng như: số ngày đã xem (days_watched), điểm
trung bình (mean_score), số lượng anime đã hoàn thành (completed), đang
xem (watching), tạm hoãn (on_hold), bỏ xem (dropped),dự định xem
(plan_to_watch),Tổng số anime mà người dùng có trong danh sách xem
(total_entries),xem lại (rewatched), tập phim đã xem
(episodes_watched).
Nhận xét: Việc tách riêng dữ liệu định lượng giúp cho quá trình phân
tích thống kê trở nên chính xác và thuận tiện hơn, vì các phép tính như
trung bình, phương sai hay độ lệch chuẩn chỉ áp dụng cho biến số.
summary(num_data)
## days_watched mean_score watching completed
## Min. : 0.00 Min. : 0.000 Min. : 0.00 Min. : 0.0
## 1st Qu.: 6.30 1st Qu.: 7.000 1st Qu.: 1.00 1st Qu.: 9.0
## Median : 29.20 Median : 7.800 Median : 4.00 Median : 59.0
## Mean : 53.99 Mean : 6.808 Mean : 10.18 Mean : 151.9
## 3rd Qu.: 72.60 3rd Qu.: 8.470 3rd Qu.: 10.00 3rd Qu.: 183.0
## Max. :105338.60 Max. :10.000 Max. :2934.00 Max. :13226.0
## on_hold dropped plan_to_watch total_entries
## Min. : 0.000 Min. : 0.00 Min. : 0.00 Min. : 0.0
## 1st Qu.: 0.000 1st Qu.: 0.00 1st Qu.: 0.00 1st Qu.: 16.0
## Median : 1.000 Median : 1.00 Median : 7.00 Median : 94.0
## Mean : 7.979 Mean : 10.39 Mean : 42.11 Mean : 222.5
## 3rd Qu.: 7.000 3rd Qu.: 8.00 3rd Qu.: 37.00 3rd Qu.: 279.0
## Max. :5167.000 Max. :14341.00 Max. :21804.00 Max. :24817.0
## rewatched episodes_watched
## Min. : 0.00 Min. : 0
## 1st Qu.: 0.00 1st Qu.: 377
## Median : 0.00 Median : 1748
## Mean : 10.45 Mean : 3441
## 3rd Qu.: 5.00 3rd Qu.: 4386
## Max. :13215.00 Max. :5433345
Giải thích: Hàm summary() cung cấp tóm tắt thống kê cơ bản cho từng
biến: giá trị nhỏ nhất (Min), giá trị lớn nhất (Max), trung vị (Median),
giá trị trung bình (Mean) và các tứ phân vị (1st Qu., 3rd Qu.).
Nhận xét: Kết quả giúp nắm được đặc điểm phân bố và mức độ biến thiên
của dữ liệu. Ví dụ, nếu mean và median chênh lệch nhiều, có thể dữ liệu
bị lệch (skewed).
mean_days <- mean(num_data$days_watched, na.rm=TRUE)
mean_days
## [1] 53.99096
mean_score <- mean(num_data$mean_score, na.rm=TRUE)
mean_score
## [1] 6.808495
mean_completed <- mean(num_data$completed, na.rm=TRUE)
mean_completed
## [1] 151.863
Giải thích: Hàm mean() tính giá trị trung bình của từng biến, giúp
biết mức độ “đại diện” của dữ liệu.
Tham số na.rm=TRUE được thêm vào để loại bỏ các giá trị thiếu (NA) khi
tính toán.
- mean_days_watched: cho biết trung bình người dùng đã xem anime trong
bao nhiêu ngày.
- mean_mean_score: thể hiện mức điểm trung bình mà người dùng thường
chấm.
- mean_completed: trung bình số lượng anime đã hoàn thành của mỗi người
dùng.
Nhận xét: Nếu giá trị trung bình của days_watched lớn và mean_score ở
mức cao, điều đó cho thấy nhóm người dùng hoạt động tích cực và có mức
đánh giá khá cao.
Kết quả cho thấy trung bình mỗi người dùng trong tập dữ liệu đã xem
anime trong khoảng 54 ngày. Con số này cho thấy phần lớn người dùng duy
trì thói quen xem anime trong thời gian khá dài, thể hiện mức độ gắn bó
tương đối cao với nội dung.
Trung bình điểm đánh giá anime là 6.8/10, nằm ở mức khá. Điều này phản
ánh rằng đa số người xem có đánh giá tích cực nhưng chưa thật sự xuất
sắc có thể do chênh lệch về chất lượng các bộ anime hoặc tiêu chí đánh
giá của người xem.
Trung bình mỗi người dùng đã hoàn thành khoảng 152 bộ anime. Đây là con
số rất cao, cho thấy nhóm mẫu trong dữ liệu bao gồm nhiều người dùng
hoạt động tích cực, có thể là những fan lâu năm hoặc người xem thường
xuyên.
median_days_watched <- median(num_data$days_watched, na.rm=TRUE)
median_days_watched
## [1] 29.2
median_mean_score <- median(num_data$mean_score, na.rm=TRUE)
median_mean_score
## [1] 7.8
Giải thích:
- median(x, na.rm=TRUE) tính trung vị của biến x; na.rm=TRUE loại bỏ giá
trị NA trước khi tính.
- Gán kết quả vào median_days_watched, median_mean_score để sử dụng
tiếp.
Nhận xét: Trung vị phản ánh giá trị nằm giữa phân bố dữ liệu, tức là 50%
quan sát có giá trị nhỏ hơn hoặc bằng trung vị và 50% còn lại lớn hơn
hoặc bằng. Đây là chỉ tiêu quan trọng khi dữ liệu có ngoại lệ hoặc phân
bố lệch, vì trung vị ít bị ảnh hưởng bởi các giá trị cực đoan hơn trung
bình. Trong đó, trung vị của days_watched thể hiện số ngày xem trung
bình của người dùng điển hình trong mẫu dữ liệu, còn trung vị của
mean_score phản ánh mức điểm đánh giá trung tâm mà người dùng thường
dành cho các bộ anime.
Kết quả cho thấy một nửa số người dùng xem anime ít hơn 29 ngày, còn một
nửa xem nhiều hơn 29 ngày. So với giá trị trung bình (≈54 ngày), điều
này cho thấy phân phối dữ liệu lệch phải (right-skewed) nghĩa là vẫn có
một nhóm nhỏ người xem cực kỳ nhiều (outlier), kéo giá trị trung bình
lên cao.
Mức trung vị này cao hơn trung bình (6.8) cho thấy đa số người dùng chấm
điểm cao hơn 7, trong khi một số ít đánh giá rất thấp đã kéo điểm trung
bình xuống. Điều này phản ánh xu hướng đánh giá tích cực chiếm ưu thế
trong tập dữ liệu.
min_days_watched <- min(num_data$days_watched, na.rm=TRUE)
min_days_watched
## [1] 0
min_mean_score <- min(num_data$mean_score, na.rm=TRUE)
min_mean_score
## [1] 0
Giải thích: Hàm min() được sử dụng để tìm giá trị nhỏ nhất của từng
biến, đồng thời loại bỏ các giá trị NA với tham số na.rm = TRUE.
Nhận xét: Giá trị nhỏ nhất cho thấy giới hạn thấp nhất mà dữ liệu có thể
đạt được. Với biến days_watched, giá trị này giúp xác định liệu có người
dùng nào chưa xem ngày nào (0 ngày) hay không. Còn đối với mean_score,
giá trị nhỏ nhất thể hiện mức đánh giá thấp nhất mà người dùng đã chấm.
Việc xác định giá trị nhỏ nhất cũng giúp phát hiện các bất thường trong
dữ liệu, chẳng hạn như điểm âm hoặc số ngày xem không hợp lý.
max_days_watched <- max(num_data$days_watched, na.rm=TRUE)
max_days_watched
## [1] 105338.6
max_mean_score <- max(num_data$mean_score, na.rm=TRUE)
max_mean_score
## [1] 10
Giải thích: Hàm max() được dùng để xác định giá trị lớn nhất trong
mỗi biến định lượng, loại bỏ các giá trị thiếu.
Nhận xét: Giá trị lớn nhất thể hiện mức độ cực đại trong dữ liệu. Đối
với days_watched, nó cho biết người dùng xem nhiều nhất bao nhiêu ngày,
giúp nhận diện hành vi “xem nhiều bất thường” (outlier) trong mẫu. Còn
với mean_score, giá trị lớn nhất phản ánh điểm số tối đa mà người dùng
có thể chấm. Việc biết được giới hạn trên này giúp đánh giá mức độ đa
dạng và sự lan tỏa trong hành vi người dùng.
sd_days_watched <- sd(num_data$days_watched, na.rm=TRUE)
sd_days_watched
## [1] 236.2184
sd_mean_score <- sd(num_data$mean_score, na.rm=TRUE)
sd_mean_score
## [1] 2.991183
Giải thích: Sử dụng hàm sd() để tính độ lệch chuẩn là thước đo mức độ
phân tán của dữ liệu quanh giá trị trung bình.
Nhận xét: Độ lệch chuẩn cho biết dữ liệu có dao động nhiều hay ít so với
trung bình. Nếu độ lệch chuẩn của days_watched lớn, điều đó chứng tỏ sự
khác biệt rõ rệt giữa người xem ít và người xem nhiều; còn nếu nhỏ, phần
lớn người dùng có thói quen xem tương đối giống nhau. Đối với
mean_score, độ lệch chuẩn cao thể hiện sự đa dạng trong quan điểm đánh
giá giữa người dùng, còn độ lệch chuẩn thấp chứng tỏ các đánh giá tương
đối đồng nhất. Chỉ tiêu này đặc biệt quan trọng khi muốn đánh giá tính
ổn định và độ tập trung của dữ liệu. Kết quả cho thấy độ lệch chuẩn bằng
236.2184 rất cao so với giá trị trung bình (≈54 ngày), cho thấy mức độ
phân tán lớn trong thời gian xem anime của người dùng. Điều này nghĩa là
có sự khác biệt rõ rệt giữa những người xem ít (chỉ vài ngày) và những
người xem rất nhiều (hàng trăm ngày). Phân phối này lệch phải mạnh
(right-skewed) và thể hiện sự tồn tại của một nhóm nhỏ “người xem cường
độ cao” kéo độ lệch chuẩn lên cao.
Đối với mean_score (SD = 2.99): Với thang điểm 1–10, độ lệch chuẩn gần 3
cho thấy mức độ chênh lệch đánh giá khá lớn. Nghĩa là người dùng có quan
điểm rất khác nhau khi đánh giá anime có người cho điểm rất thấp (dưới
4), nhưng cũng có nhóm chấm rất cao (9–10). Điều này phản ánh sự đa dạng
trong cảm nhận cá nhân, có thể do khác biệt về thể loại, độ tuổi hoặc
tiêu chuẩn đánh giá của từng người.
var_days_watched <- var(num_data$days_watched, na.rm=TRUE)
var_days_watched
## [1] 55799.13
var_mean_score <- var(num_data$mean_score, na.rm=TRUE)
var_mean_score
## [1] 8.947179
Giải thích: Hàm var() trong R được dùng để tính phương sai của một
biến định lượng. Tham số na.rm=TRUE giúp loại bỏ các giá trị bị thiếu
(NA) trong quá trình tính toán. Ở đây, ta lần lượt tính phương sai cho
hai biến days_watched (số ngày xem trung bình) và mean_score (điểm trung
bình đánh giá anime).
Nhận xét: Phương sai của days_watched cho thấy mức độ chênh lệch trong
thói quen xem anime giữa các người dùng. Nếu phương sai cao, có nghĩa là
có sự khác biệt lớn giữa những người xem ít và người xem nhiều — thể
hiện tính đa dạng trong mức độ “gắn bó” với anime.
Ngược lại, phương sai của mean_score thể hiện mức độ khác biệt trong
cách người dùng đánh giá anime. Nếu giá trị này thấp, người dùng có xu
hướng đồng thuận khi chấm điểm (điểm đánh giá tương đối đồng đều). Nếu
cao, điều đó cho thấy ý kiến về chất lượng anime rất phân tán, có thể do
khác biệt về gu, thể loại hoặc độ nổi tiếng của từng bộ phim.
Kết quả cho thấy phương sai của day_watched xấp sỉ 55799 con số này rất
lớn, phù hợp với độ lệch chuẩn cao, cho thấy dữ liệu có mức độ dao động
mạnh. Người dùng trong mẫu nghiên cứu không đồng nhất về thói quen xem
anime — một số xem rất ít, trong khi một số khác xem cực kỳ nhiều.
Phương sai của mean_score (≈ 8.95): So với thang điểm 1–10, đây cũng là
một phương sai khá đáng kể. Điều này cho thấy sự không nhất quán trong
đánh giá chất lượng anime: cùng một bộ phim có thể được chấm rất cao
hoặc rất thấp tùy người xem.
range_days_watched <- max_days_watched - min_days_watched
range_days_watched
## [1] 105338.6
range_mean_score <- max_mean_score - min_mean_score
range_mean_score
## [1] 10
Giải thích: Khoảng biến thiên được tính bằng hiệu giữa giá trị lớn
nhất (max_days_watched, max_mean_score) và giá trị nhỏ nhất
(min_days_watched, min_mean_score). Đây là một cách đơn giản để đo lường
độ trải rộng của dữ liệu.
Nhận xét: Khoảng biến thiên của days_watched giúp xác định mức độ chênh
lệch cực đại trong thời gian xem anime của người dùng. Nếu khoảng này
lớn, nghĩa là có người chỉ xem vài ngày, trong khi người khác xem hàng
trăm hoặc hàng nghìn ngày phản ánh sự khác biệt lớn trong cường độ yêu
thích anime. Trong khi đó, khoảng biến thiên của mean_score thể hiện
biên độ đánh giá của người dùng. Ví dụ, nếu khoảng biến thiên chỉ dao
động từ 6 đến 9, điều đó chứng tỏ phần lớn anime được đánh giá khá cao.
Nếu trải dài từ 1 đến 10, nghĩa là có nhiều phim bị đánh giá rất thấp và
rất cao thể hiện tính phân hóa mạnh về chất lượng hoặc cảm nhận người
xem. Kết quả cho thấy giá trị cao nhất của số ngày xem và thấp nhất
chênh lệch nhau 105838,6 ngày. Điều này cho thấy dữ liệu có mức độ phân
tán rất lớn, có thể tồn tại những người dùng xem anime trong thời gian
cực kỳ dài so với những người xem rất ít. Đây là dấu hiệu của phân phối
lệch phải (skewed), và có thể có outlier (giá trị ngoại lai). Đối với
mean_score có range bằng 10 chứng tỏ dữ liệu đánh giá bao phủ đủ toàn bộ
thang điểm, tức có cả người dùng chấm điểm rất thấp và rất cao.
iqr_days_watched <- IQR(num_data$days_watched, na.rm=TRUE)
iqr_days_watched
## [1] 66.3
iqr_mean_score <- IQR(num_data$mean_score, na.rm=TRUE)
iqr_mean_score
## [1] 1.47
Giải thích: Hàm IQR() tính khoảng tứ phân vị, là hiệu giữa phân vị
thứ 3 (Q3) và phân vị thứ 1 (Q1). Nó biểu thị phạm vi mà 50% giá trị
trung tâm của dữ liệu nằm trong đó.
Nhận xét: Nếu IQR của days_watched nhỏ, điều đó cho thấy phần lớn người
dùng có thói quen xem tương đối giống nhau. Nếu lớn, nghĩa là nhóm người
dùng trung bình có sự khác biệt đáng kể trong số ngày xem. Tương tự, IQR
của mean_score cho biết mức độ đa dạng trong cách chấm điểm ở nhóm người
dùng trung bình. Nếu nhỏ, các đánh giá khá đồng nhất; nếu lớn, điều đó
thể hiện người dùng đánh giá anime một cách rất khác nhau, có thể do sự
khác biệt về thể loại hoặc độ nổi tiếng của từng bộ phim. Kết quả cho
thấy:
- Đối với days_watched có IQR bằng 66.3 nghĩa là 50% người dùng nằm
trong khoảng giữa (giữa Q1 và Q3) có số ngày xem chênh lệch nhau 66,3
ngày. Mức này tương đối lớn, phản ánh mức độ phân tán cao của hành vi
xem anime.
- Đối với mean_score có IQR bằng 1.47 cho thấy phần lớn người dùng đánh
giá anime trong khoảng hẹp chỉ khoảng 1,47 điểm quanh trung vị hay hành
vi chấm điểm tương đối ổn định, ít biến động, chủ yếu tập trung quanh
mức trung bình.
quantile(num_data$days_watched, probs=c(0.25,0.5,0.75), na.rm=TRUE)
## 25% 50% 75%
## 6.3 29.2 72.6
quantile(num_data$mean_score, probs=c(0.25,0.5,0.75), na.rm=TRUE)
## 25% 50% 75%
## 7.00 7.80 8.47
Giải thích: quantile() trả về các giá trị phân vị: Q1 (25%), Q2 (50%
- trung vị) và Q3 (75%). Mỗi phân vị cho biết mốc mà tại đó một tỷ lệ
phần trăm người dùng có giá trị thấp hơn.
Nhận xét:
- Đối với days_watched, Q1 – Q3 cho thấy mức xem anime phổ biến của nhóm
người dùng trung bình. Nếu Q1 thấp nhưng Q3 rất cao, nghĩa là chỉ một
nhóm nhỏ xem cực kỳ nhiều anime, trong khi đa số xem ít.
- Với mean_score, các phân vị này giúp xác định xu hướng đánh giá. Ví
dụ, nếu cả Q1, Q2, Q3 đều cao (>7), điều đó cho thấy phần lớn anime
được đánh giá tích cực; ngược lại, nếu trung vị thấp, có thể người dùng
đánh giá nghiêm khắc hơn. Kết quả cho thấy:
Đối với biến days_watched 25% người dùng xem ít hơn 6.3 ngày, 50% xem ít
hơn 29.2 ngày và 75% xem ít hơn 72.6 ngày. Có thể thấy đa số người dùng
xem trong khoảng 30 đến 80 ngày, chỉ một số ít vượt hơn mức này.
Đối với biến mean_score 25% người dùng đánh giá anime thấp hơn 7 điểm,
50% đánh giá dưới 7.8 điểm, và 75% dưới 8.47 điểm.Cho thấy phần lớn
người dùng chấm điểm khá cao (trên 7), cho thấy xu hướng tích cực trong
đánh giá, chỉ có số ít người cho điểm thấp hơn đáng kể.
skew_days_watched <- skewness(num_data$days_watched, na.rm=TRUE)
skew_days_watched
## [1] 395.2411
skew_mean_score <- skewness(num_data$mean_score, na.rm=TRUE)
skew_mean_score
## [1] -1.623193
Giải thích: skewness() đo độ lệch của phân phối dữ liệu.
- Skewness > 0: lệch phải (có nhiều giá trị nhỏ, ít giá trị cực
lớn).
- Skewness < 0: lệch trái (nhiều giá trị cao, ít giá trị nhỏ).
Nhận xét: days_watched có skewness bằng 395.2411, cho thấy đa số người
dùng chỉ xem ít anime, nhưng có một số ít người xem rất nhiều. Đối với
mean_score, giá trị âm cho thấy phân phối lệch trái nhẹ. Phần lớn người
dùng có xu hướng đánh giá cao, trong khi chỉ có một phần nhỏ người dùng
cho điểm rất thấp.
kurt_days_watched <- kurtosis(num_data$days_watched, na.rm=TRUE)
kurt_days_watched
## [1] 175897.1
kurt_mean_score <- kurtosis(num_data$mean_score, na.rm=TRUE)
kurt_mean_score
## [1] 4.163564
Giải thích: kurtosis() đo mức độ tập trung của phân phối quanh trung
bình.
- Giá trị > 3: phân phối nhọn (tập trung mạnh, nhiều outlier).
- Giá trị < 3: phân phối bẹt (phân tán đều hơn).
Nhận xét: Nếu days_watched có kurtosis cực kỳ cao, cho thấy đa số người
dùng xem một lượng tương tự nhau, nhưng cũng tồn tại một số ít cá nhân
có hành vi cực đoan (xem cực kỳ nhiều hoặc rất ít). Mean_score có
kurtosis lớn hơn 3, điều đó cho thấy điểm đánh giá phân tán rộng, không
có sự tập trung rõ ở mức trung bình — người dùng có gu anime rất khác
nhau.
mean_days_watched <- mean(dataset$days_watched, na.rm = TRUE)
sd_days_watched <- sd(dataset$days_watched, na.rm = TRUE)
cv_days_watched <- sd_days_watched / mean_days_watched
cv_days_watched
## [1] 4.375147
cv_mean_score <- sd(dataset$mean_score, na.rm = TRUE) / mean(dataset$mean_score, na.rm = TRUE)
cv_mean_score
## [1] 0.4393311
Giải thích: Hệ số biến thiên (CV) đo mức độ biến động tương đối so
với trung bình, bằng tỷ lệ giữa độ lệch chuẩn và trung bình.
Nhận xét:
- CV của days_watched cao (lớn hơn 1), cho thấy mức biến động rất cao so
với giá trị trung bình. nghĩa là mức độ chênh lệch trong thói quen xem
anime giữa các người dùng là rất lớn tức có người xem cực nhiều, có
người gần như không xem.
- Trong khi đó, CV của mean_score cho thấy độ đồng thuận trong đánh giá:
giá trị này thấp hơn 1 phản ánh sự nhất quán trong nhận định về chất
lượng anime; còn CV cao chỉ ra rằng đánh giá của người dùng rất khác
nhau không xảy ra sự chênh lệch quá lớn trong cách chấm điểm, có thể do
sự khác biệt về thể loại, độ nổi tiếng hoặc tiêu chí đánh giá cá
nhân.
sum_days_watched <- sum(num_data$days_watched, na.rm=TRUE)
sum_days_watched
## [1] 12114653
sum_mean_score <- sum(num_data$mean_score, na.rm=TRUE)
sum_mean_score
## [1] 1527711
Giải thích: Sử dụng hàm sum() để tính tổng giá trị của hai biến định
lượng là days_watched (tổng số ngày người dùng xem anime) và mean_score
(điểm trung bình đánh giá anime). Tham số na.rm=TRUE giúp bỏ qua các giá
trị bị thiếu (NA) khi tính toán, đảm bảo kết quả chính xác.
Nhận xét:
- Tổng giá trị của days_watched cho thấy mức độ tổng thể mà toàn bộ
người dùng trong mẫu dữ liệu đã dành thời gian cho việc xem anime. Giá
trị này càng cao thể hiện mức độ tương tác lớn và thời gian đầu tư cho
nội dung cao.
- Tổng của mean_score biểu thị tổng điểm đánh giá mà tất cả người dùng
đã chấm. Chỉ số này giúp hiểu được quy mô tổng thể của dữ liệu đánh giá
và có thể dùng để so sánh giữa các nhóm người dùng hoặc giai đoạn khác
nhau.
valid_days <- sum(!is.na(num_data$days_watched))
valid_days
## [1] 224383
valid_score <- sum(!is.na(num_data$mean_score))
valid_score
## [1] 224383
Giải thích: Hàm is.na() trả về giá trị TRUE nếu phần tử bị thiếu, và
!is.na() đảo ngược để chọn những giá trị hợp lệ. Sau đó, sum() cộng tổng
số giá trị TRUE, tức là tổng số quan sát không bị thiếu trong từng
biến.
Nhận xét:
- Số lượng quan sát hợp lệ của days_watched và mean_score cho biết mức
độ hoàn thiện của dữ liệu.
- Cả hai biến đều có 224383 số lượng quan sát hợp lệ bằng tổng số dòng
của dataset cho thấy toàn bộ dữ liệu đầy đủ, không có giá trị NA.
na_total <- sum(is.na(num_data))
na_total
## [1] 0
Giải thích: Lệnh này tính tổng toàn bộ số lượng giá trị bị thiếu (NA)
trong toàn bộ bảng dữ liệu định lượng num_data.
Nhận xét: Kết quả cho thấy dữ liệu còn bao nhiêu giá trị bị thiếu. Đây
là bước quan trọng để đánh giá chất lượng dữ liệu đầu vào. Nếu số lượng
NA cao, mô hình phân tích sau này có thể bị sai lệch, và cần phải xử lý
bằng phương pháp thay thế trung vị, trung bình hoặc loại bỏ dòng. Trong
trường hợp này, việc kiểm tra NA giúp đảm bảo dữ liệu đã được làm sạch ở
bước 2 trước đó.
cor_days_score <- cor(num_data$days_watched, num_data$mean_score, use="complete.obs")
cor_days_score
## [1] 0.04657811
Giải thích: Hàm cor() tính hệ số tương quan Pearson giữa hai biến
định lượng days_watched và mean_score. Tham số use=“complete.obs” đảm
bảo chỉ tính toán với các quan sát đầy đủ (không chứa NA ở hai biến). Hệ
số tương quan (r) dao động từ -1 đến 1:
- r > 0: Mối quan hệ thuận, khi số ngày xem tăng thì điểm đánh giá
cũng có xu hướng tăng.
- r < 0: Mối quan hệ nghịch, khi xem nhiều ngày thì điểm đánh giá có
xu hướng giảm.
- r ≈ 0: Không có mối liên hệ đáng kể.
Nhận xét: Kết quả cho thấy hệ số tương quan gần bằng 0 thể hiện mối liên
hệ giữa số ngày xem anime và điểm đánh giá trung bình hầu như không đáng
kể.
cor_matrix <- cor(num_data, use="complete.obs")
cor_matrix
## days_watched mean_score watching completed on_hold
## days_watched 1.00000000 0.04657811 0.08909481 0.27191605 0.07455846
## mean_score 0.04657811 1.00000000 0.09358653 0.08625524 0.05174231
## watching 0.08909481 0.09358653 1.00000000 0.27563638 0.16701886
## completed 0.27191605 0.08625524 0.27563638 1.00000000 0.24370111
## on_hold 0.07455846 0.05174231 0.16701886 0.24370111 1.00000000
## dropped 0.07624655 0.02001563 0.09494424 0.26743060 0.20491943
## plan_to_watch 0.08851837 0.06120565 0.25272542 0.33204146 0.24186015
## total_entries 0.24775387 0.09734002 0.38814331 0.90558924 0.38102608
## rewatched 0.11922316 0.06025678 0.07110934 0.20963029 0.07515581
## episodes_watched 0.61279105 0.04543834 0.08219492 0.23970288 0.07015880
## dropped plan_to_watch total_entries rewatched
## days_watched 0.07624655 0.08851837 0.24775387 0.11922316
## mean_score 0.02001563 0.06120565 0.09734002 0.06025678
## watching 0.09494424 0.25272542 0.38814331 0.07110934
## completed 0.26743060 0.33204146 0.90558924 0.20963029
## on_hold 0.20491943 0.24186015 0.38102608 0.07515581
## dropped 1.00000000 0.15864750 0.40469141 0.06463776
## plan_to_watch 0.15864750 1.00000000 0.66489377 0.08702700
## total_entries 0.40469141 0.66489377 1.00000000 0.20047691
## rewatched 0.06463776 0.08702700 0.20047691 1.00000000
## episodes_watched 0.06585478 0.07785181 0.21881104 0.12370288
## episodes_watched
## days_watched 0.61279105
## mean_score 0.04543834
## watching 0.08219492
## completed 0.23970288
## on_hold 0.07015880
## dropped 0.06585478
## plan_to_watch 0.07785181
## total_entries 0.21881104
## rewatched 0.12370288
## episodes_watched 1.00000000
Giải thích: Hàm cor() được áp dụng lên toàn bộ bảng dữ liệu định
lượng num_data để tạo ma trận tương quan giữa tất cả các biến.
Nhận xét: Ma trận tương quan giúp ta quan sát mối quan hệ tuyến tính
giữa từng cặp biến trong dataset. Thông qua ma trận này, có thể phát
hiện biến nào có mối liên hệ mạnh, biến nào độc lập. Việc này rất hữu
ích trong phân tích hồi quy hoặc xây dựng mô hình dự báo vì giúp tránh
đa cộng tuyến.
Kết quả cho thấy một số cặp biến có mỗi tương quan dương mạnh:
- completed và total_entries (r = 0.9055): Đây là mối tương quan dương
rất mạnh, gần như tuyến tính hoàn hảo. Nghĩa là số anime đã hoàn thành
gần như tăng tương ứng với tổng số anime mà người dùng đã thêm vào danh
sách.
- plan_to_watch và total_entries (r = 0.6648): Mối tương quan dương khá
cao, cho thấy người dùng có nhiều anime đã xem thường cũng có nhiều
anime dự định xem.
describe(num_data)
## vars n mean sd median trimmed mad min
## days_watched 1 224383 53.99 236.22 29.2 39.20 40.18 0
## mean_score 2 224383 6.81 2.99 7.8 7.31 1.07 0
## watching 3 224383 10.18 28.13 4.0 5.64 5.93 0
## completed 4 224383 151.86 270.45 59.0 95.47 85.99 0
## on_hold 5 224383 7.98 30.34 1.0 3.47 1.48 0
## dropped 6 224383 10.39 51.04 1.0 3.90 1.48 0
## plan_to_watch 7 224383 42.11 141.99 7.0 18.36 10.38 0
## total_entries 8 224383 222.54 382.51 94.0 146.13 134.92 0
## rewatched 9 224383 10.45 48.08 0.0 2.91 0.00 0
## episodes_watched 10 224383 3440.74 16790.89 1748.0 2368.61 2407.74 0
## max range skew kurtosis se
## days_watched 105338.6 105338.6 395.24 175892.52 0.50
## mean_score 10.0 10.0 -1.62 1.16 0.01
## watching 2934.0 2934.0 25.29 1566.98 0.06
## completed 13226.0 13226.0 6.64 117.21 0.57
## on_hold 5167.0 5167.0 57.55 7229.67 0.06
## dropped 14341.0 14341.0 130.48 31514.33 0.11
## plan_to_watch 21804.0 21804.0 33.27 3166.77 0.30
## total_entries 24817.0 24817.0 8.80 278.84 0.81
## rewatched 13215.0 13215.0 102.21 25725.71 0.10
## episodes_watched 5433345.0 5433345.0 236.00 69141.40 35.45
Giải thích: Hàm describe() trong thư viện psych tạo ra bảng thống kê
mô tả đầy đủ cho tất cả các biến định lượng, bao gồm số lượng quan sát,
giá trị trung bình, độ lệch chuẩn, min, max, median, skewness,
kurtosis,…
Nhận xét: Đây là bước tổng hợp toàn diện giúp ta nhìn nhanh toàn bộ đặc
điểm của dữ liệu sau khi xử lý. Dựa vào bảng này, ta có thể đánh giá độ
phân tán, xu hướng trung tâm, mức độ lệch, cũng như sự khác biệt giữa
các biến. Đối với dữ liệu về người dùng anime, bảng này giúp nhận diện
các hành vi nổi bật: ví dụ, người dùng trung bình xem bao nhiêu ngày,
đánh giá trung bình bao nhiêu điểm, hay biến nào có mức độ dao động lớn
nhất.
ggplot(dataset, aes(x = gender, y = mean_score, fill = gender)) +
geom_boxplot(alpha = 0.7) +
geom_jitter(color = "black", alpha = 0.2) +
stat_summary(fun = mean, geom = "point", color = "red", size = 3) +
labs(title = "Phân bố điểm trung bình theo giới tính",
x = "Giới tính", y = "Điểm trung bình") +
theme_minimal() +
theme(legend.position = "none")
Giải thích: Biểu đồ sử dụng ggplot2 để thể hiện phân bố điểm trung bình
(mean_score) theo giới tính (gender).
- geom_boxplot() hiển thị hộp dữ liệu thể hiện trung vị, tứ phân vị và
ngoại lệ.
- geom_jitter() thêm các điểm rải rác để minh họa phân bố từng cá
nhân.
- stat_summary(fun = mean) đánh dấu giá trị trung bình bằng điểm màu
đỏ.
- labs() đặt tiêu đề và nhãn trục, theme_minimal() giúp biểu đồ rõ ràng,
dễ đọc. - theme_minimal(): Tùy chỉnh giao diện, loại bỏ chi tiết thừa để
biểu đồ sạch, hiện đại và dễ đọc.
Nhận xét: Biểu đồ thể hiện sự phân bố điểm trung bình của người dùng
theo từng giới tính cho thấy có sự khác biệt nhất định giữa các nhóm.
Các hộp trong biểu đồ biểu thị mức độ tập trung và phân tán của điểm số,
trong đó nhóm có hộp nhỏ hơn thể hiện sự đồng nhất cao hơn trong cách
cho điểm, còn nhóm có hộp rộng cho thấy sự đa dạng hơn trong hành vi
đánh giá. Điểm tròn màu đỏ biểu diễn giá trị trung bình của mỗi nhóm,
giúp ta dễ dàng nhận thấy nhóm nào có xu hướng đánh giá cao hơn hoặc
thấp hơn trung bình chung.
ggplot(dataset, aes(x = days_watched)) +
geom_histogram(binwidth = 50, fill = "skyblue", color = "black", alpha = 0.8) +
geom_vline(aes(xintercept = mean(days_watched, na.rm = TRUE)),
color = "red", linetype = "dashed") +
annotate("text", x = mean(dataset$days_watched, na.rm = TRUE) + 200,
y = 100, label = "Trung bình", vjust = -1, color = "red") +
labs(title = "Phân bố số ngày xem phim", x = "Số ngày", y = "Tần suất") +
xlim(0, 3000) +
theme_light()
## Warning: Removed 9 rows containing non-finite outside the scale range
## (`stat_bin()`).
## Warning: Removed 2 rows containing missing values or values outside the scale range
## (`geom_bar()`).
Giải thích:
- geom_histogram(): Tạo biểu đồ cột thể hiện tần suất xuất hiện của biến
days_watched trong từng khoảng (mỗi khoảng rộng 50 ngày). Màu cột xanh
da trời giúp dễ quan sát, đường viền đen tạo độ tách biệt giữa các
cột.
– geom_vline(): Thêm đường gạch đỏ (dashed line) biểu diễn giá trị trung
bình của days_watched, giúp nhận biết vị trí của giá trị trung
tâm.
– annotate(): Gắn nhãn “Trung bình” ngay vị trí đường đỏ, giúp người xem
dễ hiểu ý nghĩa của đường đánh dấu này.
– labs(): Đặt tiêu đề và nhãn trục X, Y, giúp biểu đồ có ngữ cảnh rõ
ràng và chuyên nghiệp.
- xlim(0, 3000): Giới hạn phần dữ liệu chính.
– theme_light(): Sử dụng giao diện sáng, nền trắng – giúp biểu đồ sạch,
dễ nhìn.
Nhận xét: Biểu đồ histogram mô tả phân bố số ngày người dùng xem phim
cho thấy phần lớn người dùng chỉ xem trong một khoảng thời gian ngắn,
trong khi có một số ít người dành thời gian xem phim rất lớn. Phân phối
dữ liệu thể hiện rõ xu hướng lệch phải, nghĩa là đa số tập trung ở các
giá trị nhỏ, còn phần đuôi kéo dài về phía bên phải do ảnh hưởng của một
số người dùng “xem nhiều bất thường”. Đường trung bình màu đỏ nằm khá xa
về phía đuôi phân phối, điều này cho thấy giá trị trung bình bị chi phối
bởi các trường hợp ngoại lệ. Vì vậy, trong trường hợp này, giá trị trung
vị (median) sẽ phản ánh đúng hơn xu hướng chung của dữ liệu. Biểu đồ
giúp ta nhận ra rằng hành vi xem phim của người dùng không đồng đều: có
sự chênh lệch lớn giữa nhóm xem ít và nhóm xem nhiều.
ggplot(dataset, aes(x = days_watched, y = episodes_watched)) +
geom_point(alpha = 0.4, color = "darkblue") +
geom_smooth(method = "lm", color = "red", se = FALSE) +
geom_density_2d(color = "gray50") +
labs(title = "Mối quan hệ giữa số ngày xem và số tập đã xem",
x = "Số ngày xem", y = "Số tập đã xem") +
xlim(0, 5000) +
ylim(0, 200000) +
theme_bw()
## `geom_smooth()` using formula = 'y ~ x'
## Warning: Removed 31 rows containing non-finite outside the scale range
## (`stat_smooth()`).
## Warning: Removed 31 rows containing non-finite outside the scale range
## (`stat_density2d()`).
## Warning: Removed 31 rows containing missing values or values outside the scale range
## (`geom_point()`).
## Warning: Removed 7 rows containing missing values or values outside the scale range
## (`geom_smooth()`).
Giải thích:
– geom_point(): Vẽ các điểm dữ liệu (scatter plot), mỗi điểm biểu diễn
một người dùng với tọa độ gồm days_watched (trục X) và episodes_watched
(trục Y).
+ alpha = 0.4 làm mờ để giảm chồng lấn điểm.
+ color = “darkblue” giúp điểm nổi bật, dễ quan sát.
– geom_smooth(method = “lm”): Thêm đường hồi quy tuyến tính màu đỏ, biểu
diễn xu hướng quan hệ giữa hai biến.
+ method = “lm” nghĩa là dùng linear model (hồi quy tuyến tính).
+ se = FALSE tắt vùng tin cậy (confidence interval) để đồ thị gọn
hơn.
– geom_density_2d(): Vẽ đường đồng mật độ (contour lines) thể hiện vùng
có mật độ điểm cao, giúp thấy khu vực tập trung dữ liệu (nhiều người
dùng có hành vi tương tự).
– labs(): Thêm tiêu đề và nhãn trục X, Y để biểu đồ rõ nghĩa: “Mối quan
hệ giữa số ngày xem và số tập đã xem”.
- xlim(0, 5000) +
ylim(0, 200000) + : Giới hạn phần chính.
– theme_bw(): Dùng giao diện trắng đen (black & white theme) cho
phong cách chuyên nghiệp, dễ đọc và làm nổi bật điểm dữ liệu.
Nhận xét: Biểu đồ thể hiện mối quan hệ giữa số ngày xem phim và số tập
đã xem của người dùng cho thấy xu hướng tuyến tính rõ rệt và dương tính
mạnh. Đường hồi quy màu đỏ thể hiện rằng khi số ngày xem phim tăng, số
tập phim đã xem cũng tăng gần như tỷ lệ thuận, chứng tỏ người dùng có xu
hướng duy trì mức độ xem ổn định theo thời gian. Các điểm dữ liệu phân
bố dày đặc ở góc dưới bên trái cho thấy phần lớn người dùng chỉ xem
trong thời gian ngắn và với số lượng tập hạn chế, trong khi một số ít
điểm nằm xa về phía bên phải biểu diễn những người có tần suất xem rất
cao – đây có thể là những người hâm mộ hoặc “nghiện phim”. Sự xuất hiện
của các cụm điểm đậm ở một số vùng phản ánh hành vi xem phổ biến.
ggplot(dataset, aes(x = gender, y = watching, fill = gender)) +
geom_col(position = "dodge") +
geom_text(aes(label = watching), vjust = -0.5) +
labs(title = "Số anime đang xem theo giới tính",
x = "Giới tính", y = "Số lượng anime") +
scale_fill_brewer(palette = "Set2") +
theme_classic()
Giải thích:
- geom_col(): Vẽ biểu đồ cột thể hiện số anime đang xem (watching) theo
giới tính (gender), với các cột đặt cạnh nhau (position =
“dodge”).
- geom_text(): Hiển thị giá trị số lượng trên đầu mỗi cột giúp dễ so
sánh.
- labs() – Thêm tiêu đề và nhãn trục X, Y: “Số anime đang xem theo giới
tính”.
- scale_fill_brewer(palette = “Set2”): Dùng bảng màu tươi sáng, phân
biệt rõ giới tính.
- theme_classic(): Dùng giao diện đơn giản, làm nổi bật dữ liệu.
Nhận xét: Biểu đồ cột so sánh số lượng anime đang xem theo giới tính cho
thấy có sự khác biệt nhẹ giữa các nhóm. Trong đó, nam giới có xu hướng
theo dõi nhiều anime hơn một chút so với nữ giới, trong khi nhóm người
dùng thuộc giới tính khác (“Non-binary”) có số lượng thấp hơn đáng kể –
điều này có thể do quy mô nhóm nhỏ hoặc mức độ tham gia cộng đồng thấp
hơn. Các cột được tô màu khác nhau giúp làm nổi bật sự khác biệt trực
quan giữa các nhóm giới, đồng thời giá trị hiển thị trên đỉnh mỗi cột
giúp người đọc dễ dàng so sánh chính xác.
ggplot(dataset, aes(x = as.factor(rewatched), y = mean_score, fill = as.factor(rewatched))) +
geom_boxplot() +
geom_jitter(alpha = 0.3) +
stat_summary(fun = mean, geom = "line", aes(group=1), color="red") +
labs(title = "Điểm trung bình theo số lần xem lại",
x = "Số lần xem lại", y = "Điểm trung bình") +
theme_minimal() +
theme(legend.position = "none")
Giải thích:
- geom_boxplot(): Vẽ biểu đồ hộp thể hiện sự phân bố của mean_score
(điểm trung bình) theo rewatched (số lần xem lại). Hộp thể hiện trung
vị, tứ phân vị và các giá trị ngoại lai.
- geom_jitter(alpha = 0.3): Thêm các điểm dữ liệu rải rác để hiển thị
phân tán thực tế, tránh chồng lấn.
- stat_summary(fun = mean, geom = “line”, aes(group=1), color=“red”): Vẽ
đường nối các giá trị trung bình giữa các nhóm, giúp quan sát xu hướng
thay đổi điểm trung bình theo số lần xem lại.
- labs(): Thêm tiêu đề và nhãn trục, giúp người xem dễ hiểu nội dung
biểu đồ.
- theme_minimal(): Dùng giao diện tối giản, loại bỏ chi tiết thừa để tập
trung vào dữ liệu (ẩn chú thích bằng
theme(legend.position=“none”)).
Nhận xét: Biểu đồ thể hiện mối quan hệ giữa số lần xem lại anime và điểm
trung bình mà người xem đánh giá. Có thể thấy, khi số lần xem lại tăng
lên, điểm trung bình có xu hướng duy trì ở mức ổn định và cao hơn trung
bình chung. Điều này cho thấy những bộ anime được xem lại nhiều lần
thường là các tác phẩm có chất lượng nội dung cao, dễ để lại ấn tượng
mạnh hoặc có yếu tố cảm xúc khiến người xem muốn thưởng thức lại. Tổng
thể, biểu đồ phản ánh rằng tần suất xem lại có tương quan tích cực với
mức độ yêu thích của khán giả.
ggplot(dataset, aes(x = total_entries, y = mean_score)) +
geom_point(alpha = 0.4, color = "orange") +
geom_density_2d(color = "blue") +
geom_smooth(method = "lm", color = "red", se = FALSE) +
labs(title = "Tương quan giữa tổng số anime và điểm trung bình") +
theme_light() +
theme(plot.title = element_text(face = "bold"))
## `geom_smooth()` using formula = 'y ~ x'
Giải thích:
- geom_point(alpha = 0.4, color = “orange”): Vẽ các điểm phân tán thể
hiện mối quan hệ giữa tổng số anime mà người dùng đã thêm vào danh sách
(total_entries) và điểm trung bình (mean_score) mà họ đánh giá. Mỗi điểm
đại diện cho một người dùng.
- geom_density_2d(color = “blue”): Thêm đường đồng mật độ 2D, thể hiện
vùng tập trung cao của các điểm dữ liệu, giúp xác định khu vực có nhiều
người dùng có đặc điểm tương tự.
- geom_smooth(method = “lm”, color = “red”, se = FALSE): Thêm đường hồi
quy tuyến tính, minh họa xu hướng tổng thể giữa hai biến. Màu đỏ nổi bật
giúp dễ nhận thấy mối tương quan (tăng hoặc giảm).
- labs(title = “Tương quan giữa tổng số anime và điểm trung bình”): Thêm
tiêu đề giúp người xem hiểu rõ nội dung biểu đồ.
- theme_light() và theme(plot.title = element_text(face = “bold”)): Tạo
phong cách sáng, đơn giản, đồng thời làm nổi bật tiêu đề để biểu đồ rõ
ràng và dễ đọc.
Nhận xét: Biểu đồ này mô tả mối quan hệ giữa tổng số anime mà người dùng
đã xem và điểm trung bình họ chấm. Dễ nhận thấy xu hướng đường hồi quy
(màu đỏ) cho thấy người xem càng có kinh nghiệm xem nhiều anime, điểm
trung bình họ chấm càng có xu hướng giảm nhẹ. Điều này phản ánh một thực
tế phổ biến: những người đã xem nhiều anime hơn thường khó tính hơn
trong việc đánh giá, bởi họ có chuẩn so sánh rộng hơn và dễ nhận ra điểm
yếu trong từng tác phẩm. Ngược lại, những người xem ít anime thường có
xu hướng chấm điểm cao hơn, do họ dễ bị ấn tượng bởi các yếu tố mới lạ.
Như vậy, biểu đồ này làm nổi bật mối quan hệ nghịch nhẹ giữa số lượng
anime đã xem và mức độ hài lòng trung bình.
ggplot(dataset, aes(x = gender, y = mean_score, fill = gender)) +
geom_violin(alpha = 0.7) +
geom_boxplot(width = 0.1, fill = "white") +
geom_jitter(width = 0.2, alpha = 0.3) +
labs(title = "Phân bố điểm trung bình theo giới tính") +
theme_bw() +
theme(legend.position = "none")
Giải thích:
- geom_violin(alpha = 0.7): Vẽ biểu đồ violin, thể hiện phân bố xác suất
của điểm trung bình (mean_score) theo giới tính (gender). Phần dày của
hình violin biểu thị vùng có nhiều quan sát tập trung.
- geom_boxplot(width = 0.1, fill = “white”): Thêm biểu đồ hộp (boxplot)
bên trong mỗi violin để hiển thị thống kê tóm tắt như trung vị, tứ phân
vị và giá trị ngoại lai.
- geom_jitter(width = 0.2, alpha = 0.3): Hiển thị các điểm dữ liệu riêng
lẻ bằng cách rải nhẹ theo chiều ngang để tránh chồng chéo, giúp nhìn rõ
mật độ phân bố thực tế.
- labs(title = “Phân bố điểm trung bình theo giới tính”): Thêm tiêu đề
mô tả rõ nội dung biểu đồ.
- theme_bw() + theme(legend.position = “none”): Dùng giao diện nền trắng
(black and white) giúp biểu đồ gọn gàng, tập trung vào dữ liệu, đồng
thời ẩn chú giải vì màu sắc đã thể hiện rõ giới tính.
Nhận xét: Biểu đồ violin cho thấy sự khác biệt trong phân bố điểm trung
bình giữa các nhóm giới tính: Female, Male và Non-Binary.
Nhìn chung, cả ba nhóm có xu hướng tập trung điểm trung bình trong
khoảng 6.5 đến 8 điểm, cho thấy mức độ hài lòng tương đối đồng nhất giữa
các giới. Tuy nhiên, có thể nhận thấy nhóm Non-Binary có độ phân tán
rộng hơn, biểu hiện qua phần thân violin dài hơn — điều này có nghĩa là
ý kiến đánh giá trong nhóm này đa dạng và không đồng nhất như hai nhóm
còn lại.
Ngược lại, nhóm Female và Male có phân bố tương đối đối xứng và tập
trung, cho thấy mức độ đồng thuận cao hơn trong việc đánh giá anime.
Tổng thể, biểu đồ chỉ ra rằng giới tính không phải là yếu tố tạo ra sự
chênh lệch đáng kể trong điểm trung bình, nhưng có sự khác biệt nhỏ về
mức độ phân tán và độ ổn định trong đánh giá.
ggplot(subset(dataset, episodes_watched < 500000 & days_watched < 10000),
aes(x = episodes_watched, y = days_watched, color = rewatched)) +
geom_point(alpha = 0.6) +
geom_smooth(method = "lm", se = FALSE, color = "black") +
scale_color_gradient(low = "blue", high = "red") +
labs(title = "Quan hệ giữa số tập và số ngày xem theo số lần xem lại (loại bỏ ngoại lệ)",
x = "Số tập đã xem", y = "Số ngày xem") +
theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'
Giải thích:
- geom_point(alpha = 0.6): Vẽ biểu đồ phân tán (scatter plot), hiển thị
mối quan hệ giữa số tập đã xem (episodes_watched) và số ngày xem
(days_watched). Mỗi điểm đại diện cho một người dùng. Độ trong suốt
(alpha = 0.6) giúp tránh chồng chéo khi dữ liệu dày đặc.
- geom_smooth(method = “lm”, se = FALSE, color = “black”): Thêm đường
hồi quy tuyến tính (linear model) để thể hiện xu hướng chung giữa hai
biến. Đường này giúp ta nhận biết liệu người xem nhiều tập có xu hướng
dành nhiều ngày hơn để xem không.
- scale_color_gradient(low = “blue”, high = “red”): Tô màu dựa trên biến
rewatched (số lần xem lại): Màu xanh thể hiện ít hoặc không xem lại. Màu
đỏ thể hiện xem lại nhiều lần. Việc này giúp làm nổi bật sự khác biệt
trong hành vi giữa những người xem lại ít và nhiều.
- labs(title = “Quan hệ giữa số tập và ngày xem theo số lần xem lại”):
Đặt tiêu đề biểu đồ, mô tả rõ ràng nội dung và biến được phân
tích.
- theme_minimal(): Áp dụng giao diện tối giản, giúp biểu đồ sáng sủa,
tập trung vào dữ liệu và tránh yếu tố gây nhiễu thị giác.
Nhận xét: Biểu đồ thể hiện mối quan hệ giữa số tập anime đã xem
(episodes_watched) và số ngày xem (days_watched), được tô màu theo số
lần xem lại (rewatched). Quan sát thấy phần lớn các điểm dữ liệu tập
trung theo đường xu hướng tăng, cho thấy rằng người xem càng xem nhiều
tập thì tổng số ngày xem càng tăng theo tỉ lệ thuận — điều này hoàn toàn
hợp lý vì người xem cần nhiều thời gian hơn để hoàn thành nhiều tập phim
hơn. Đáng chú ý, các điểm có màu đỏ (tức là rewatched cao) thường nằm ở
khu vực có số tập và số ngày cao hơn trung bình, cho thấy những người có
thói quen xem lại anime cũng là những người xem nhiều và đầu tư thời
gian dài hơn cho sở thích của mình. Tóm lại, biểu đồ phản ánh mối quan
hệ tích cực giữa khối lượng xem anime và thời gian dành cho nó, đồng
thời nhấn mạnh rằng sự đam mê và mức độ gắn bó với anime có liên hệ chặt
chẽ với thói quen xem lại.
ggplot(dataset, aes(x = completed, y = mean_score)) +
geom_point(alpha = 0.4, color = "steelblue") +
geom_smooth(method = "loess", color = "red") +
geom_density_2d(color = "gray50") +
labs(title = "Điểm trung bình theo số anime đã hoàn thành") +
theme_classic() +
theme(plot.title = element_text(size = 12, face = "bold"))
## `geom_smooth()` using formula = 'y ~ x'
## Warning: Failed to fit group -1.
## Caused by error in `predLoess()`:
## ! workspace required (75523896008) is too large probably because of setting 'se = TRUE'.
Giải thích:
- geom_point(alpha = 0.4, color = “steelblue”): Vẽ biểu đồ phân tán
(scatter plot), thể hiện từng quan sát giữa hai biến:
+ Trục X: completed (số anime đã hoàn thành).
+ Trục Y: mean_score (điểm trung bình người dùng đánh giá).
Mỗi điểm đại diện cho một người dùng. Độ trong suốt alpha = 0.4 giúp
tránh chồng lấn khi dữ liệu dày.
- geom_smooth(method = “loess”, color = “red”): Thêm đường xu hướng
(LOESS curve) – một phương pháp hồi quy phi tuyến tính, thể hiện xu
hướng thay đổi của điểm trung bình theo số anime đã hoàn thành. Đường
màu đỏ mô tả mối quan hệ trơn tru, không nhất thiết tuyến tính giữa hai
biến.
- geom_density_2d(color = “gray50”): Thêm đường mật độ 2 chiều (contour
lines) để biểu diễn khu vực tập trung nhiều điểm dữ liệu. Những vùng có
nhiều đường bao dày hơn thể hiện mật độ người xem cao hơn, tức là nhiều
người có cùng hành vi xem và điểm số tương tự.
- labs(title = “Điểm trung bình theo số anime đã hoàn thành”): Đặt tiêu
đề biểu đồ, giúp người đọc hiểu rõ nội dung biểu diễn là mối liên hệ
giữa số anime hoàn thành và điểm đánh giá trung bình.
- theme_classic() + theme(plot.title = element_text(size = 12, face =
“bold”)): Áp dụng giao diện cổ điển (classic) với nền trắng và đường
trục rõ nét, đồng thời làm nổi bật tiêu đề bằng cỡ chữ lớn và in đậm để
tăng tính trực quan.
Nhận xét: Biểu đồ thể hiện mối quan hệ giữa số anime đã hoàn thành
(completed) và điểm trung bình đánh giá (mean_score). Quan sát cho thấy
có xu hướng tăng nhẹ: những người xem và hoàn thành nhiều anime hơn
thường có xu hướng đưa ra điểm trung bình cao hơn, phản ánh rằng họ là
nhóm người xem có kinh nghiệm và chọn lọc nội dung kỹ hơn. Tuy nhiên,
mật độ điểm dữ liệu dày ở vùng số anime thấp cho thấy phần lớn người
dùng mới hoặc người xem thông thường vẫn chiếm tỷ lệ lớn. Tổng thể, biểu
đồ gợi ý rằng mức độ trải nghiệm có thể ảnh hưởng tích cực đến đánh giá
anime.
dataset %>%
summarise(across(c(watching, completed, on_hold, dropped, plan_to_watch),
sum, na.rm = TRUE)) %>%
pivot_longer(everything(), names_to = "status", values_to = "count") %>%
ggplot(aes(x = status, y = count, fill = status)) +
geom_col() +
geom_text(aes(label = count), vjust = -0.5) +
labs(title = "So sánh các trạng thái xem anime",
x = "Trạng thái", y = "Số lượng") +
scale_fill_brewer(palette = "Set3") +
theme_minimal()
## Warning: There was 1 warning in `summarise()`.
## ℹ In argument: `across(...)`.
## Caused by warning:
## ! The `...` argument of `across()` is deprecated as of dplyr 1.1.0.
## Supply arguments directly to `.fns` through an anonymous function instead.
##
## # Previously
## across(a:b, mean, na.rm = TRUE)
##
## # Now
## across(a:b, \(x) mean(x, na.rm = TRUE))
Giải thích:
- %>%: là toán tử pipe, nghĩa là “chuyển kết quả của bước trước sang
bước sau”.
- summarise(): tạo ra bảng tóm tắt.
- across(): áp dụng cùng một hàm (sum) lên nhiều cột cùng lúc.
- c(watching, completed, on_hold, dropped, plan_to_watch): đây là 5 cột
thể hiện các trạng thái xem anime.
- sum(…, na.rm = TRUE): tính tổng số lượng anime cho từng trạng thái, bỏ
qua giá trị NA.
- pivot_longer() biến đổi dữ liệu từ 5 cột thành 2 cột mới:
+ status: chứa tên trạng thái (watching, completed, …)
+ count: chứa giá trị tổng tương ứng.
- ggplot(aes(…)): khởi tạo biểu đồ, gán biến trục x, y và màu
(fill).
- geom_col(): vẽ biểu đồ cột (height = count cho từng status).
- labs() giúp thêm tiêu đề chính, và tên trục X/Y cho biểu đồ.
- scale_fill_brewer(palette = “Set3”): chọn bảng màu “Set3” — các màu
nhạt, dễ phân biệt.
- theme_minimal(): giao diện nhẹ nhàng, ít đường kẻ — giúp nổi bật dữ
liệu.
- theme(plot.title = …): in đậm tiêu đề và chỉnh cỡ chữ.
Nhận xét: Biểu đồ cột cho thấy sự khác biệt rõ rệt trong số lượng anime
ở các trạng thái xem khác nhau: “Completed”, “Watching”, “On-Hold”,
“Dropped” và “Plan to Watch”. Kết quả minh họa rằng đa số người dùng có
xu hướng hoàn thành hoặc lên kế hoạch xem nhiều anime, trong khi tỷ lệ
anime bị “bỏ dở” hoặc “tạm dừng” thấp hơn. Điều này phản ánh sự gắn bó
và kiên trì của cộng đồng người xem, đồng thời cho thấy rằng phần lớn
người dùng có mục tiêu theo đuổi trọn vẹn bộ anime hơn là bỏ ngang.
ggplot(dataset, aes(x = on_hold)) +
geom_histogram(binwidth = 2, fill = "gold", color = "black", alpha = 0.7) +
geom_vline(aes(xintercept = mean(on_hold, na.rm = TRUE)),
color = "red", linetype = "dashed") +
geom_density(aes(y = ..count.. * 3), color = "brown") +
labs(title = "Phân bố số anime đang tạm dừng",
x = "Số anime on-hold", y = "Tần suất") +
xlim(0, 100) +
theme_light()
## Warning: The dot-dot notation (`..count..`) was deprecated in ggplot2 3.4.0.
## ℹ Please use `after_stat(count)` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning: Removed 1808 rows containing non-finite outside the scale range
## (`stat_bin()`).
## Warning: Removed 1808 rows containing non-finite outside the scale range
## (`stat_density()`).
## Warning: Removed 2 rows containing missing values or values outside the scale range
## (`geom_bar()`).
Giải thích:
- ggplot(dataset, aes(x = on_hold)): Khởi tạo biểu đồ với trục hoành là
số lượng anime đang tạm dừng (on_hold).
- geom_histogram(…): Vẽ biểu đồ tần suất (histogram) để hiển thị phân bố
số anime đang bị tạm dừng.
binwidth = 2: mỗi cột đại diện cho nhóm 2 đơn vị. Và fill = “gold”,
color = “black”, alpha = 0.7: chọn màu vàng nhạt, viền đen, độ trong
suốt 70%.
- geom_vline(…): Thêm đường thẳng đứng màu đỏ nét đứt biểu diễn giá trị
trung bình của biến on_hold. Giúp người xem dễ dàng nhận biết mức trung
bình trên biểu đồ.
- geom_density(aes(y = ..count..*3), color = “brown”): Vẽ đường mật độ
(density curve) chồng lên histogram, mô tả xu hướng phân bố mượt mà.
Nhân hệ số 3 để điều chỉnh tỷ lệ y cho khớp với histogram.
Màu nâu giúp dễ phân biệt với các lớp khác.
- labs(…): Thêm tiêu đề và nhãn trục, giúp biểu đồ rõ ràng, có ngữ
nghĩa.
- xlim(0, 100): Giới hạn phần dữ liệu chính.
- theme_light(): Dùng giao diện sáng nhẹ, dễ đọc.
Nhận xét: Biểu đồ histogram thể hiện phân bố số lượng anime đang ở trạng
thái “On-Hold” của người xem. Dễ thấy rằng đa số người dùng chỉ có một
vài anime bị tạm dừng, trong khi số lượng người có danh sách “On-Hold”
dài là rất ít. Đường trung bình được thêm vào giúp xác định mức trung
bình của dữ liệu, cho thấy phần lớn người xem chỉ tạm ngừng ở mức độ
thấp, có thể do họ chuyển sang xem các anime mới hoặc đợi phần tiếp theo
phát hành. Điều này thể hiện mức độ duy trì mối quan tâm cao của khán
giả đối với các bộ anime đang theo dõi.
ggplot(dataset, aes(x = dropped)) +
geom_histogram(binwidth = 2, fill = "salmon", color = "black", alpha = 0.7) +
geom_vline(aes(xintercept = mean(dropped, na.rm = TRUE)),
color = "blue", linetype = "dotted") +
annotate("text", x = mean(dataset$dropped, na.rm = TRUE) + 3, y = 150,
label = "Mean", color = "blue") +
labs(title = "Phân bố số anime bị bỏ dở",
x = "Số anime bị bỏ", y = "Tần suất") +
xlim(0, 100) +
theme_bw()
## Warning: Removed 3614 rows containing non-finite outside the scale range
## (`stat_bin()`).
## Warning: Removed 2 rows containing missing values or values outside the scale range
## (`geom_bar()`).
Giải thích:
- ggplot(dataset, aes(x = dropped)): Khởi tạo biểu đồ với dữ liệu từ
dataset, trong đó biến dropped được chọn làm trục hoành. Biến này biểu
thị số lượng anime mà người dùng đã bỏ dở.
- geom_histogram(binwidth = 2, fill = “salmon”, color = “black”, alpha =
0.7): Vẽ biểu đồ tần suất (histogram) mô tả phân bố của biến
dropped.
+ binwidth = 2: mỗi cột đại diện cho một khoảng 2 đơn vị số anime bị bỏ
dở.
+ fill = “salmon”: tô màu hồng nhạt cho các cột, giúp biểu đồ dễ
nhìn.
+ color = “black”: viền đen bao quanh từng cột, làm nổi bật ranh giới
giữa các khoảng.
+ alpha = 0.7: tăng độ trong suốt để biểu đồ mềm mại, trực quan
hơn.
- geom_vline(aes(xintercept = mean(dropped, na.rm = TRUE)), color =
“blue”, linetype = “dotted”): Thêm đường thẳng đứng tại vị trí giá trị
trung bình của biến dropped.
+ color = “blue” giúp đường trung bình nổi bật.
+ linetype = “dotted” (nét chấm chấm) dùng để phân biệt với đường trục
chính, tăng tính thẩm mỹ.
- annotate(“text”, x = mean(dataset$dropped, na.rm = TRUE) + 3, y = 150,
label = “Mean”, color = “blue”): Thêm nhãn chữ “Mean” gần đường trung
bình để người xem dễ nhận biết.
- labs(title = “Phân bố số anime bị bỏ dở”, x = “Số anime bị bỏ”, y =
“Tần suất”): Đặt tiêu đề và nhãn trục cho biểu đồ.
+ Tiêu đề “Phân bố số anime bị bỏ dở” giúp người đọc nắm được ý nghĩa
biểu đồ.
+ Trục X là số anime bị bỏ, trục Y là tần suất xuất hiện của từng giá
trị.
- xlim(0, 100): Giới hạn trục X để dễ thấy phần chính.
- theme_bw(): Áp dụng giao diện nền trắng – kiểu hiển thị đơn giản, rõ
ràng, giúp biểu đồ dễ đọc và phù hợp khi in trong báo cáo học
thuật.
Nhận xét: Biểu đồ này cho thấy phân bố số lượng anime bị bỏ dở trong
cộng đồng người xem. Phần lớn các giá trị tập trung ở mức thấp, thể hiện
rằng đa số người dùng ít khi bỏ dở anime, trong khi chỉ có một nhóm nhỏ
có số lượng “Dropped” cao hơn. Việc thêm đường trung bình (và nhãn
“Mean”) giúp dễ dàng nhận thấy mức bỏ dở trung bình rất thấp, phản ánh
rằng phần lớn khán giả có thói quen kiên nhẫn và hoàn thành bộ phim
trước khi đưa ra đánh giá. Như vậy, đây là tín hiệu tích cực về mức độ
yêu thích và cam kết của người xem đối với anime.
ggplot(dataset, aes(x = plan_to_watch)) +
geom_histogram(binwidth = 10, fill = "deepskyblue", color = "black", alpha = 0.7) +
geom_vline(aes(xintercept = mean(plan_to_watch, na.rm = TRUE)),
color = "red", linetype = "dashed") +
geom_density(aes(y = ..count.. * 2), color = "blue") +
labs(title = "Phân bố số anime dự định xem",
x = "Số anime plan_to_watch", y = "Tần suất") +
xlim(0, 500) +
theme_minimal()
## Warning: Removed 2168 rows containing non-finite outside the scale range
## (`stat_bin()`).
## Warning: Removed 2168 rows containing non-finite outside the scale range
## (`stat_density()`).
## Warning: Removed 2 rows containing missing values or values outside the scale range
## (`geom_bar()`).
Giải thích:
- ggplot(dataset, aes(x = plan_to_watch)): Khởi tạo biểu đồ từ bộ dữ
liệu dataset, trong đó biến plan_to_watch được đặt ở trục hoành. Biến
này thể hiện số lượng anime mà người dùng có kế hoạch xem trong tương
lai, phản ánh ý định hoặc sở thích tiềm năng của họ.
- geom_histogram(binwidth = 10, fill = “deepskyblue”, color = “black”,
alpha = 0.7): Vẽ biểu đồ tần suất (histogram) biểu diễn phân bố của biến
plan_to_watch. + binwidth = 10: mỗi cột biểu đồ đại diện cho một khoảng
10 anime dự định xem, giúp dữ liệu mượt và dễ quan sát hơn.
+ fill = “deepskyblue”: sử dụng màu xanh da trời tươi để tăng tính trực
quan và gợi cảm giác nhẹ nhàng.
+ color = “black”: viền đen quanh từng cột giúp các khoảng phân bố rõ
ràng.
+ alpha = 0.7: làm các cột hơi trong suốt, tạo cảm giác mềm mại và
chuyên nghiệp.
- geom_vline(aes(xintercept = mean(plan_to_watch, na.rm = TRUE)), color
= “red”, linetype = “dashed”): Thêm đường thẳng đứng biểu diễn giá trị
trung bình của số anime dự định xem.
+ color = “red”: màu đỏ nổi bật, giúp người đọc dễ dàng nhận biết vị trí
trung bình.
+ linetype = “dashed”: sử dụng nét đứt để phân biệt đường trung bình với
đường trục, đồng thời tăng tính thẩm mỹ cho biểu đồ.
- geom_density(aes(y = ..count..2), color = “blue”): Thêm đường mật
độ (density curve) chồng lên biểu đồ tần suất, giúp biểu diễn dạng phân
bố trơn của dữ liệu.
+ aes(y = ..count..2): điều chỉnh tỷ lệ trục tung để đường mật độ
khớp với chiều cao của các cột histogram, đảm bảo hai lớp hiển thị tương
thích.
+ color = “blue”: tô đường mật độ màu xanh lam, tạo cảm giác cân đối với
màu của cột histogram và dễ nhận diện.
- labs(title = “Phân bố số anime dự định xem”, x = “Số anime
plan_to_watch”, y = “Tần suất”): Đặt tiêu đề và nhãn cho các trục của
biểu đồ.
+ Tiêu đề “Phân bố số anime dự định xem” giúp người đọc hiểu rõ nội dung
biểu đồ.
+ Trục X biểu diễn số lượng anime mà người dùng dự định xem, trục Y thể
hiện tần suất xuất hiện của các giá trị này trong dữ liệu.
- xlim(0, 500): Giới hạn trục X để thấy rõ vùng phổ biến.
- theme_minimal(): Áp dụng giao diện nền tối giản, loại bỏ các chi tiết
không cần thiết (đường viền, nền xám…), giúp tập trung sự chú ý vào các
yếu tố dữ liệu chính và tạo cảm giác hiện đại, tinh gọn cho biểu
đồ.
Nhận xét: Biểu đồ thể hiện số lượng anime mà người xem dự định sẽ xem
trong tương lai (Plan to Watch). Kết quả cho thấy phần lớn người dùng có
danh sách “Plan to Watch” khá dài, thể hiện sự quan tâm và nhu cầu khám
phá anime mới liên tục. Đường density và đường trung bình giúp minh họa
rằng có xu hướng nghiêng phải (right-skewed), nghĩa là vẫn có một số
người dùng cực kỳ năng động với danh sách dự định dài vượt trội. Biểu đồ
này phản ánh sự đam mê và hứng thú mạnh mẽ của cộng đồng anime, với xu
hướng liên tục cập nhật và mở rộng trải nghiệm giải trí.
ggplot(dataset, aes(x = total_entries)) +
geom_histogram(binwidth = 10, fill = "violet", color = "black", alpha = 0.7) +
geom_vline(aes(xintercept = median(total_entries, na.rm = TRUE)),
color = "red", linetype = "dashed") +
geom_density(aes(y = ..count.. * 3), color = "darkblue") +
labs(title = "Phân bố tổng số anime của người dùng",
x = "Tổng số anime", y = "Tần suất") +
xlim(0, 1000) +
theme_classic()
## Warning: Removed 8186 rows containing non-finite outside the scale range
## (`stat_bin()`).
## Warning: Removed 8186 rows containing non-finite outside the scale range
## (`stat_density()`).
## Warning: Removed 2 rows containing missing values or values outside the scale range
## (`geom_bar()`).
Giải thích:
- geom_histogram(binwidth = 10, fill = “violet”, color = “black”, alpha
= 0.7): Vẽ biểu đồ tần suất (histogram) mô tả phân bố của biến
total_entries – tức tổng số anime mà mỗi người dùng đã từng tương tác
(bao gồm xem, lên kế hoạch, bỏ dở, v.v.). Các cột có màu tím (violet),
đường viền đen giúp dễ phân biệt, và độ trong suốt alpha = 0.7 tạo cảm
giác nhẹ, tránh biểu đồ bị quá đặc. Mỗi cột biểu diễn số lượng người
dùng nằm trong từng khoảng giá trị tổng số anime.
- geom_vline(aes(xintercept = median(total_entries, na.rm = TRUE)),
color = “red”, linetype = “dashed”): Thêm một đường thẳng đứng tại vị
trí trung vị của tổng số anime. Đường màu đỏ, nét gạch (dashed) giúp dễ
nhận biết vị trí giá trị trung vị – tức mức mà 50% người dùng có tổng số
anime thấp hơn và 50% cao hơn.
- geom_density(aes(y = ..count.. * 3), color = “darkblue”): Chồng thêm
đường mật độ (density curve) để biểu diễn dạng phân bố mượt hơn của dữ
liệu. Đường màu xanh đậm cho thấy xu hướng tập trung hay phân tán của
người dùng theo tổng số anime. Hệ số nhân (×3) giúp đường mật độ tỷ lệ
thuận với trục tần suất, dễ so sánh trực quan với các cột
histogram.
- labs(title = “Phân bố tổng số anime của người dùng”, x = “Tổng số
anime”, y = “Tần suất”): Đặt tiêu đề và nhãn cho các trục, giúp người
đọc dễ hiểu rằng biểu đồ đang mô tả sự phân bố của tổng số anime theo
từng người dùng trong bộ dữ liệu.
- xlim(0, 1000): Giới hạn trục hoành từ 0 đến 1000 nhằm loại bỏ các giá
trị ngoại lai (outliers) và giúp biểu đồ trực quan, tập trung hơn vào
vùng dữ liệu chính.
- theme_classic(): Áp dụng giao diện cổ điển với nền trắng và khung trục
rõ ràng, giúp biểu đồ gọn gàng, dễ đọc và mang phong cách học
thuật.
Nhận xét: Biểu đồ này thể hiện phân bố tổng số anime mà người dùng đã
tương tác (bao gồm các trạng thái như xem, đang xem, hoặc đã hoàn
thành). Phần lớn người dùng có tổng số anime ở mức trung bình hoặc thấp,
thể hiện qua mật độ tập trung cao ở vùng đầu của trục hoành. Đường trung
bình màu đỏ cho thấy số lượng anime trung bình mỗi người theo dõi không
quá lớn, trong khi một số ít người dùng có giá trị cao hơn hẳn, cho thấy
sự đa dạng trong mức độ tham gia của cộng đồng. Nhìn chung, biểu đồ phản
ánh rằng phần đông người dùng có xu hướng theo dõi anime ở mức vừa phải,
trong khi một nhóm nhỏ lại thể hiện độ đam mê cao hơn hẳn.
ggplot(dataset, aes(x = days_watched, fill = gender)) +
geom_histogram(bins = 40, color = "black", alpha = 0.7) +
geom_density(aes(y = ..count..), color = "red", size = 1) +
geom_vline(aes(xintercept = mean(days_watched, na.rm = TRUE)),
color = "blue", linetype = "dashed") +
facet_wrap(~gender) +
xlim(0, 1000) +
theme_minimal() +
labs(title = "Phân phối số ngày xem phim theo giới tính",
x = "Số ngày xem", y = "Tần suất")
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning: Removed 60 rows containing non-finite outside the scale range
## (`stat_bin()`).
## Warning: Removed 60 rows containing non-finite outside the scale range
## (`stat_density()`).
## Warning: Removed 6 rows containing missing values or values outside the scale range
## (`geom_bar()`).
Giải thích:
- geom_histogram(bins = 40, color = “black”, alpha = 0.7): Vẽ biểu đồ
tần suất (histogram) thể hiện phân bố số ngày xem anime (days_watched)
của người dùng. Biểu đồ được chia thành 40 khoảng (bins), mỗi cột thể
hiện số lượng người dùng có tổng số ngày xem nằm trong một phạm vi nhất
định. Các cột có viền đen và độ trong suốt alpha = 0.7 giúp dễ quan sát
khi dữ liệu được tô màu khác nhau theo giới tính.
- geom_density(aes(y = ..count..), color = “red”, size = 1): Thêm đường
mật độ màu đỏ để làm mượt phân bố dữ liệu, giúp quan sát xu hướng tổng
thể thay vì chỉ nhìn qua các cột rời rạc. Đường này có cùng đơn vị với
biểu đồ tần suất (do dùng ..count..), nên dễ dàng so sánh mật độ người
xem ở các mức days_watched khác nhau.
- geom_vline(aes(xintercept = mean(days_watched, na.rm = TRUE)), color =
“blue”, linetype = “dashed”): Thêm đường thẳng đứng màu xanh lam, nét
đứt (dashed line) để biểu diễn giá trị trung bình của biến days_watched.
Đường này giúp người đọc xác định vị trí trung bình so với phần lớn dữ
liệu và quan sát xem phân bố có bị lệch (skewed) về bên trái hay
phải.
- facet_wrap(~gender): Chia biểu đồ thành hai phần riêng biệt tương ứng
với từng giới tính (nam và nữ). Cách hiển thị này giúp dễ dàng so sánh
trực quan hành vi xem anime giữa hai giới, đặc biệt là về độ tập trung,
mức trung bình và độ lệch phân bố.
- xlim(0, 1000): Giới hạn trục X từ 0 đến 1000 để tập trung hiển thị
vùng dữ liệu chính, tránh các giá trị ngoại lai (outliers) khiến biểu đồ
bị kéo giãn và khó quan sát phần trung tâm.
- theme_minimal(): Sử dụng giao diện tối giản, loại bỏ các đường nền và
yếu tố không cần thiết, giúp biểu đồ rõ ràng, nhấn mạnh dữ liệu
chính.
- labs(title = “Phân phối số ngày xem phim theo giới tính”, x = “Số ngày
xem”, y = “Tần suất”): Đặt tiêu đề và nhãn trục, mô tả rõ nội dung biểu
đồ là sự phân bố số ngày xem anime giữa các nhóm giới tính.
Nhận xét: Biểu đồ minh họa phân bố số ngày xem phim (days_watched) theo
từng giới tính. Kết quả cho thấy các nhóm giới tính có phân bố tương đối
tương đồng, tuy nhiên vẫn có sự khác biệt nhỏ: nhóm nam giới thường có
số ngày xem cao hơn một chút, trong khi nhóm nữ giới có phân bố nghiêng
về phía các giá trị thấp hơn. Đường trung bình màu đỏ cho thấy vị trí
trung bình của mỗi nhóm, giúp dễ dàng nhận thấy sự chênh lệch nhẹ giữa
các giới tính. Biểu đồ này gợi ý rằng giới tính có thể là một yếu tố ảnh
hưởng đến thói quen xem anime, mặc dù mức độ khác biệt không quá
lớn.
ggplot(dataset, aes(x = rewatched)) +
geom_histogram(binwidth = 1, fill = "lightpink", color = "black", alpha = 0.7) +
geom_vline(aes(xintercept = mean(rewatched, na.rm = TRUE)),
color = "darkred", linetype = "dotted") +
geom_density(aes(y = ..count.. * 2), color = "darkred") +
labs(title = "Phân bố số lần xem lại anime",
x = "Số lần xem lại", y = "Tần suất") +
xlim(0, 100) +
theme_bw()
## Warning: Removed 4576 rows containing non-finite outside the scale range
## (`stat_bin()`).
## Warning: Removed 4576 rows containing non-finite outside the scale range
## (`stat_density()`).
## Warning: Removed 2 rows containing missing values or values outside the scale range
## (`geom_bar()`).
Giải thích:
- geom_histogram(binwidth = 1, fill = “lightpink”, color = “black”,
alpha = 0.7): Vẽ biểu đồ tần suất (histogram) thể hiện phân bố của biến
rewatched – số lần người dùng xem lại anime. Mỗi cột đại diện cho số
lượng người dùng có cùng (hoặc gần giống) số lần xem lại. Màu nền hồng
nhạt (lightpink) giúp biểu đồ nhẹ mắt, đường viền đen làm nổi bật các
cột, và độ trong suốt alpha = 0.7 tạo sự cân bằng giữa độ đậm và rõ
ràng. Việc chọn binwidth = 1 cho phép hiển thị chi tiết số lần xem lại
theo từng đơn vị, phù hợp với dữ liệu rời rạc.
- geom_vline(aes(xintercept = mean(rewatched, na.rm = TRUE)), color =
“darkred”, linetype = “dotted”): Thêm một đường thẳng đứng biểu thị giá
trị trung bình của số lần xem lại anime. Đường màu đỏ đậm và kiểu nét
chấm (dotted) giúp dễ dàng nhận biết vị trí của trung bình – từ đó so
sánh xem phần lớn người dùng xem lại ít hay nhiều hơn mức trung
bình.
- geom_density(aes(y = ..count.. * 2), color = “darkred”): Chồng thêm
đường mật độ (density curve) màu đỏ đậm để biểu diễn dạng phân bố mượt
mà của dữ liệu. Đường này giúp hình dung xu hướng chung của tần suất xem
lại anime, trong khi hệ số nhân (×2) giúp đường tỷ lệ hợp lý với cột
histogram, đảm bảo tính trực quan khi so sánh.
- labs(title = “Phân bố số lần xem lại anime”, x = “Số lần xem lại”, y =
“Tần suất”): Đặt tiêu đề và nhãn cho trục hoành và trục tung. Biểu đồ
thể hiện rõ mục đích là mô tả phân bố số lần người dùng xem lại anime,
giúp người đọc hiểu được hành vi tái xem trong cộng đồng người
dùng.
- xlim(0, 100): Giới hạn trục X từ 0 đến 100 để loại bỏ các giá trị cực
lớn hiếm gặp, giúp biểu đồ tập trung và mịn hơn ở vùng giá trị chính,
nơi phần lớn người dùng tập trung.
- theme_bw(): Sử dụng giao diện nền trắng (black & white) mang tính
học thuật, với đường trục rõ ràng, giúp biểu đồ chuyên nghiệp và dễ đọc
hơn.
Nhận xét: Biểu đồ thể hiện tần suất số lần xem lại anime của người dùng.
Dễ thấy rằng phần lớn người xem chỉ xem lại ít anime, trong khi chỉ có
một nhóm nhỏ xem lại nhiều lần. Đường trung bình và đường mật độ thể
hiện rõ xu hướng nghiêng phải (right-skewed), nghĩa là có một vài người
dùng có xu hướng xem lại anime rất nhiều lần. Hiện tượng này phản ánh sự
yêu thích mạnh mẽ của một nhóm nhỏ người xem trung thành, đồng thời cho
thấy rằng hành vi xem lại không phổ biến rộng rãi trong cộng đồng nói
chung.
watch_status <- dataset %>%
summarise(
watching = sum(watching, na.rm=TRUE),
completed = sum(completed, na.rm=TRUE),
on_hold = sum(on_hold, na.rm=TRUE),
dropped = sum(dropped, na.rm=TRUE),
plan_to_watch = sum(plan_to_watch, na.rm=TRUE)
) %>% tidyr::pivot_longer(everything(), names_to="status", values_to="count")
ggplot(watch_status, aes(x=reorder(status, -count), y=count, fill=status)) +
geom_col() +
geom_text(aes(label=comma(count)), vjust=-0.5, size=3.5) +
theme_bw() +
labs(title="Tổng số lượng anime theo trạng thái", x="Trạng thái", y="Số lượng")
Giải thích:
- summarise(…): Lệnh này tính tổng số lượng anime của từng trạng thái
xem trong bộ dữ liệu. Cụ thể:
+ watching: tổng số anime mà người dùng hiện đang xem.
+ completed: tổng số anime đã hoàn thành.
+ on_hold: tổng số anime đang tạm hoãn.
+ dropped: tổng số anime bị bỏ dở.
+ plan_to_watch: tổng số anime người dùng dự định xem trong tương lai.
Việc dùng na.rm = TRUE giúp loại bỏ giá trị thiếu (NA), đảm bảo kết quả
chính xác.
- pivot_longer(everything(), names_to = “status”, values_to = “count”):
Chuyển bảng dữ liệu từ dạng rộng sang dài, tức là gom các cột trạng thái
thành hai cột mới:
+ status: chứa tên trạng thái (watching, completed, on_hold, dropped,
plan_to_watch).
+ count: chứa tổng số lượng tương ứng của mỗi trạng thái. Cấu trúc này
thuận tiện cho việc trực quan hóa bằng ggplot2.
- ggplot(watch_status, aes(x = reorder(status, -count), y = count, fill
= status)): Khởi tạo biểu đồ cột, trong đó:
+ X: tên trạng thái xem anime.
+ Y: tổng số lượng anime ở mỗi trạng thái.
+ fill = status: tô màu từng cột theo nhóm trạng thái khác nhau, giúp dễ
phân biệt.
Hàm reorder(status, -count) sắp xếp các cột theo thứ tự giảm dần của giá
trị count, từ cao nhất đến thấp nhất.
- geom_col(): Vẽ biểu đồ cột (column chart), trong đó chiều cao của mỗi
cột thể hiện tổng số lượng anime theo từng trạng thái. Đây là dạng biểu
đồ phù hợp để so sánh tổng thể giữa các nhóm phân loại.
- geom_text(aes(label = comma(count)), vjust = -0.5, size = 3.5): Thêm
nhãn hiển thị giá trị thực tế trên đầu mỗi cột.
+ Hàm comma(count) (thuộc thư viện scales) giúp định dạng số có dấu phẩy
ngăn cách hàng nghìn (ví dụ: 12,345), dễ đọc hơn.
+ Tham số vjust = -0.5 điều chỉnh vị trí nhãn nằm ngay trên cột để dễ
nhìn.
- theme_bw(): Áp dụng giao diện nền trắng – khung đen (black-white
theme), giúp biểu đồ rõ ràng, chuyên nghiệp, phù hợp cho báo cáo học
thuật.
- labs(title = “Tổng số lượng anime theo trạng thái”, x = “Trạng thái”,
y = “Số lượng”): Đặt tiêu đề và nhãn trục, mô tả rõ ý nghĩa biểu đồ —
thể hiện tổng số anime mà người dùng đã hoàn thành, đang xem, tạm dừng,
bỏ dở hoặc có kế hoạch xem.
Nhận xét: Biểu đồ cột so sánh số lượng anime theo từng trạng thái xem
bao gồm: “Watching”, “Completed”, “On-Hold”, “Dropped” và “Plan to
Watch”. Kết quả cho thấy số anime ở trạng thái “Completed” và “Plan to
Watch” chiếm tỷ trọng cao nhất, chứng tỏ phần lớn người dùng hoặc đã
hoàn thành xem, hoặc đang lên kế hoạch xem thêm nhiều anime mới. Ngược
lại, hai trạng thái “Dropped” và “On-Hold” chiếm tỷ lệ thấp, phản ánh
rằng người xem anime thường có xu hướng kiên trì thay vì bỏ dở giữa
chừng. Biểu đồ này cho thấy cộng đồng người xem anime duy trì mức độ
tương tác và hứng thú cao, thể hiện một văn hóa xem kiên định và liên
tục mở rộng danh sách phim.
ggplot(watch_status, aes(x=status, y=count, group=1)) +
geom_line(color="darkblue", size=1) +
geom_point(size=3, color="red") +
geom_text(aes(label=count), vjust=-0.5) +
theme_classic() +
labs(title="Xu hướng số lượng phim theo trạng thái", x="Trạng thái", y="Số lượng")
Giải thích:
- ggplot(watch_status, aes(x = status, y = count, group = 1)): Khởi tạo
biểu đồ đường (line chart) với dữ liệu từ bảng watch_status.
+ status: biểu diễn trên trục X, là các trạng thái xem anime (watching,
completed, on_hold, dropped, plan_to_watch).
+ count: biểu diễn trên trục Y, là tổng số lượng anime tương ứng với
từng trạng thái.
+ group = 1: đảm bảo các điểm dữ liệu được nối liền thành một đường duy
nhất (thay vì nhiều nhóm tách biệt).
- geom_line(color = “darkblue”, size = 1): Vẽ đường nối giữa các điểm dữ
liệu, thể hiện xu hướng thay đổi số lượng anime theo từng trạng
thái.
+ Đường màu xanh đậm (darkblue) giúp nổi bật, thể hiện rõ mạch xu
hướng.
+ size = 1 quy định độ dày của đường, tăng độ dễ nhìn trong báo
cáo.
- geom_point(size = 3, color = “red”): Thêm các điểm dữ liệu màu đỏ tại
mỗi vị trí trạng thái để làm nổi bật giá trị cụ thể. Các điểm này giúp
người xem dễ dàng nhận biết và phân biệt từng giá trị hơn là chỉ nhìn
đường xu hướng.
- geom_text(aes(label = count), vjust = -0.5): Thêm nhãn số lượng cụ thể
ngay trên mỗi điểm dữ liệu.
vjust = -0.5 giúp các con số hiển thị phía trên điểm đỏ, tránh bị chồng
lấn. Nhờ đó, người xem có thể đọc trực tiếp giá trị mà không cần ước
lượng từ trục Y.
- theme_classic(): Áp dụng giao diện nền trắng và đường trục rõ nét, tạo
cảm giác gọn gàng, chuyên nghiệp, phù hợp cho báo cáo thống kê hoặc học
thuật.
- labs(title = “Xu hướng số lượng phim theo trạng thái”, x = “Trạng
thái”, y = “Số lượng”): Đặt tiêu đề và nhãn cho hai trục, giúp người đọc
hiểu rằng biểu đồ thể hiện sự biến động của tổng số anime giữa các trạng
thái khác nhau trong quá trình xem.
Nhận xét: Biểu đồ cho thấy sự khác biệt rõ rệt giữa các trạng thái xem
phim. Số lượng phim “đã xem” thường cao nhất, trong khi nhóm “đang xem”
và “dự định xem” có xu hướng thấp hơn. Điều này phản ánh thói quen xem
phim của người dùng có xu hướng hoàn thành danh sách phim đã chọn.
ggplot(dataset, aes(x = episodes_watched)) +
geom_histogram(bins=50, fill="skyblue", color="black") +
scale_x_log10() +
geom_vline(aes(xintercept=median(episodes_watched,na.rm=TRUE)), color="red") +
theme_bw() +
labs(title="Phân phối log-scale số tập phim", x="Log(Số tập)", y="Tần suất")
## Warning in scale_x_log10(): log-10 transformation introduced infinite values.
## Warning: Removed 25766 rows containing non-finite outside the scale range
## (`stat_bin()`).
Giải thích:
- ggplot(dataset, aes(x = episodes_watched)): Khởi tạo biểu đồ phân bố
(histogram) với dữ liệu từ dataset, trong đó biến episodes_watched (số
tập phim mà người dùng đã xem) được chọn làm trục hoành. Mục tiêu là xem
xét mức độ phân tán và tần suất của số tập phim giữa các người
dùng.
- geom_histogram(bins = 50, fill = “skyblue”, color = “black”): Vẽ biểu
đồ tần suất gồm 50 cột, mỗi cột đại diện cho một khoảng giá trị số tập
phim.
+ bins = 50 giúp chia nhỏ dữ liệu thành 50 khoảng, thể hiện chi tiết hơn
sự phân bố.
+ fill = “skyblue” tạo màu xanh nhạt nhẹ nhàng cho các cột, giúp dễ quan
sát.
+ color = “black” thêm đường viền đen cho từng cột để phân tách rõ ràng
giữa các nhóm dữ liệu.
- scale_x_log10(): Chuyển trục X sang thang đo logarit (log-scale). Đây
là một bước rất quan trọng khi dữ liệu bị lệch mạnh về một phía — chẳng
hạn có nhiều người xem rất ít tập, trong khi một số ít người xem hàng
ngàn tập. Việc dùng log giúp thu hẹp khoảng cách giữa các giá trị lớn và
nhỏ, làm cho biểu đồ cân đối và dễ đọc hơn, đồng thời thể hiện rõ hơn xu
hướng tổng thể của dữ liệu.
- geom_vline(aes(xintercept = median(episodes_watched, na.rm = TRUE)),
color = “red”): Thêm đường thẳng đứng màu đỏ tại vị trí trung vị
(median) của số tập phim đã xem. Việc dùng trung vị thay vì trung bình
giúp biểu diễn giá trị “đại diện” cho phần lớn người dùng, tránh bị ảnh
hưởng bởi các giá trị ngoại lai (outliers) — chẳng hạn vài người xem quá
nhiều anime.
- theme_bw(): Áp dụng chủ đề nền trắng (black-white theme), giúp các chi
tiết như cột màu xanh và đường trung vị đỏ nổi bật rõ ràng, phù hợp cho
các báo cáo học thuật.
- labs(title = “Phân phối log-scale số tập phim”, x = “Log(Số tập)”, y =
“Tần suất”): Đặt tiêu đề và nhãn trục cho biểu đồ, làm rõ rằng biểu đồ
này thể hiện phân bố của số tập phim đã xem trên thang logarit.
Nhận xét: Biểu đồ cho thấy phần lớn người dùng chỉ xem một lượng nhỏ đến
trung bình số tập phim, thể hiện qua mật độ tập trung ở phía bên trái
(tức giá trị log nhỏ). Khi chuyển sang thang log, ta dễ dàng nhận ra xu
hướng giảm dần của tần suất khi số tập phim tăng. Điều này phản ánh thực
tế rằng chỉ có một nhóm nhỏ người dùng xem số lượng anime rất lớn, trong
khi phần đông dừng lại ở mức vừa phải.
dataset %>%
group_by(gender) %>%
summarise(mean_score_avg = mean(mean_score, na.rm=TRUE)) %>%
ggplot(aes(x=gender, y=mean_score_avg, fill=gender)) +
geom_col() +
geom_text(aes(label=round(mean_score_avg,2)), vjust=-0.5) +
theme_minimal() +
labs(title="Điểm trung bình theo giới tính", x="Giới tính", y="Điểm trung bình")
Giải thích:
- group_by(gender):chia bộ dữ liệu thành các nhóm theo biến gender (giới
tính nam, nữ, hoặc khác nếu có).
- summarise(mean_score_avg = mean(mean_score, na.rm=TRUE))
+ Hàm mean() tính giá trị trung bình của biến mean_score.
+ Tham số na.rm=TRUE giúp loại bỏ các giá trị bị thiếu (NA) trong quá
trình tính toán. Kết quả trả về là một bảng mới gồm hai cột: gender và
mean_score_avg
- geom_col(): Dùng để vẽ cột với chiều cao tương ứng giá trị trung bình
của mỗi nhóm giới tính. Màu sắc của từng cột được xác định bởi biến fill
= gender.
- geom_text(aes(label=round(mean_score_avg,2)), vjust=-0.5)
+ label hiển thị giá trị trung bình được làm tròn đến 2 chữ số thập
phân.
+ vjust = -0.5 điều chỉnh vị trí nhãn nằm phía trên đầu cột, giúp dễ
nhìn.
- theme_minimal(): Sử dụng giao diện tối giản giúp biểu đồ gọn gàng, dễ
đọc.
- labs(title=“Điểm trung bình theo giới tính”, x=“Giới tính”, y=“Điểm
trung bình”)
Nhận xét: Điểm trung bình đánh giá phim giữa các giới tính có sự chênh
lệch nhẹ. Nhóm nữ thường cho điểm cao hơn một chút so với nhóm nam, phản
ánh xu hướng đánh giá tích cực hơn của người dùng nữ.
ggplot(dataset, aes(x = gender, y = mean_score, fill = gender)) +
geom_boxplot(alpha = 0.7) +
geom_jitter(width = 0.2, alpha = 0.2, color = "black") +
stat_summary(fun=mean, geom="point", shape=18, color="red", size=3) +
theme_light() +
labs(title="Phân phối điểm trung bình theo giới tính", x="Giới tính", y="Điểm trung bình")
Giải thích:
- ggplot() với dữ liệu đầu vào là dataset, trong đó trục hoành biểu diễn
biến giới tính (gender) và trục tung biểu diễn điểm trung bình
(mean_score). Các hộp trong biểu đồ được tô màu theo giới tính nhờ tham
số fill = gender, giúp dễ dàng phân biệt giữa các nhóm.
- geom_boxplot(alpha = 0.7) được sử dụng để vẽ biểu đồ hộp (boxplot),
thể hiện phân bố điểm trung bình của từng nhóm giới tính với độ trong
suốt vừa phải để không che mất các lớp khác.
- geom_jitter(width = 0.2, alpha = 0.2, color = “black”) được thêm vào
để hiển thị các điểm dữ liệu riêng lẻ, cho phép quan sát rõ hơn mức độ
phân tán của từng cá nhân trong mỗi nhóm giới tính, đồng thời tránh việc
các điểm bị chồng lên nhau.
- stat_summary(fun = mean, geom = “point”, shape = 18, color = “red”,
size = 3) được dùng để đánh dấu giá trị trung bình của từng nhóm bằng
một điểm màu đỏ hình thoi, giúp người xem dễ dàng nhận biết xu hướng
trung bình của mỗi giới.
- theme_light(): Giao diện sáng, nhẹ nhàng, rõ ràng và dễ đọc.
- labs(), với tiêu đề “Phân phối điểm trung bình theo giới tính” cùng
các nhãn trục “Giới tính” và “Điểm trung bình”, giúp biểu đồ có tính mô
tả tốt hơn.
Nhận xét: Biểu đồ thể hiện sự khác biệt về điểm trung bình giữa các giới
tính. Kết quả cho thấy điểm trung bình của các nhóm không có sự chênh
lệch quá lớn, tuy nhiên vẫn tồn tại những khác biệt nhỏ về giá trị trung
vị và mức độ phân tán. Nhóm nam nhìn chung có điểm trung bình cao hơn
một chút so với nhóm nữ và nhóm phi nhị nguyên giới, cho thấy có thể tồn
tại sự khác biệt trong xu hướng hoặc tiêu chí đánh giá phim giữa các
giới tính.
dataset_filtered <- dataset %>% filter(episodes_watched < 10000)
ggplot(dataset_filtered, aes(x = episodes_watched, fill = gender)) +
geom_histogram(bins = 50, color = "black", alpha = 0.6) +
geom_density(aes(y = ..count..), color = "red", size = 1) +
geom_vline(aes(xintercept = mean(episodes_watched, na.rm = TRUE)),
color = "blue", linetype = "dashed", size = 1) +
facet_wrap(~gender) +
theme_minimal() +
labs(title = "Phân phối số tập phim đã xem (loại bỏ outlier)",
x = "Số tập phim đã xem",
y = "Tần suất") +
scale_x_continuous(labels = scales::comma)
Giải thích:
- dataset_filtered <- dataset %>% filter(episodes_watched <
10000): giúp giữ lại những quan sát có số tập phim nhỏ hơn 10.000, đảm
bảo dữ liệu không bị ảnh hưởng bởi các trường hợp quá lớn, từ đó phản
ánh trung thực hơn thói quen xem phim của người dùng.
- hàm ggplot() được sử dụng với biến episodes_watched làm trục hoành,
thể hiện số tập phim đã xem, và fill = gender để tô màu khác nhau cho
từng giới tính.
- geom_histogram(bins = 50, color = “black”, alpha = 0.6) vẽ biểu đồ cột
thể hiện tần suất xem phim, trong đó dữ liệu được chia thành 50 khoảng
(bins), giúp biểu đồ mượt mà hơn. Mỗi cột có viền đen (color = “black”)
và độ trong suốt 60% (alpha = 0.6) để các cột không bị che lấp khi chồng
màu. Tiếp theo, geom_density(aes(y = ..count..), color = “red”, size =
1) thêm đường mật độ phân phối (density curve) màu đỏ phủ lên biểu đồ
cột, cho phép quan sát xu hướng chung của dữ liệu dưới dạng đường cong
liên tục thay vì chỉ qua các cột rời rạc.
- geom_vline(aes(xintercept = mean(episodes_watched, na.rm = TRUE)),
color = “blue”, linetype = “dashed”, size = 1) vẽ một đường thẳng đứng
màu xanh tại vị trí giá trị trung bình của số tập phim đã xem, giúp
người xem dễ dàng nhận biết mức trung bình so với toàn bộ phân
phối.
- facet_wrap(~gender), cho phép so sánh trực tiếp phân phối giữa các
nhóm nam và nữ trên cùng một thang đo. Giao diện được làm gọn gàng và
hiện đại nhờ chủ đề theme_minimal().
- labs() để biểu đồ có tiêu đề “Phân phối số tập phim đã xem (loại bỏ
outlier)” cùng các trục được đặt tên rõ ràng (“Số tập phim đã xem” và
“Tần suất”).
- scale_x_continuous(labels = scales::comma) giúp hiển thị giá trị trục
hoành ở dạng có dấu phẩy (ví dụ: 1,000; 5,000) nhằm tăng tính trực quan
và dễ đọc.
Nhận xét: Biểu đồ mô tả phân phối số tập phim đã xem (sau khi loại bỏ
các giá trị ngoại lai) của người dùng theo giới tính. Kết quả cho thấy
phân phối của cả ba nhóm đều lệch phải, nghĩa là phần lớn người dùng chỉ
xem một số lượng nhỏ tập phim, trong khi chỉ một số ít xem rất nhiều.
Trung bình số tập phim đã xem của nhóm nam cao hơn so với nhóm nữ và
nhóm phi nhị nguyên giới, điều này gợi ý rằng nhóm nam có xu hướng dành
nhiều thời gian cho việc xem phim hơn. Sự khác biệt về giá trị trung
bình giữa các nhóm giới tính phản ánh mức độ quan tâm và tần suất xem
phim khác nhau, cho thấy yếu tố giới tính có thể ảnh hưởng đến hành vi
tiêu thụ nội dung giải trí.