Cách đọc biểu đồ hộp
Biểu đồ hộp (box plot) nén cả một phân phối thành năm con số rồi vẽ chúng thành một hình gọn gàng. Chỉ mất ba mươi giây để học cách đọc, nên ta bắt đầu từ đó. Mỗi phần đều có ý nghĩa chính xác:
- Đường đậm bên trong hộp là trung vị (median): một nửa dữ liệu nằm dưới nó, một nửa nằm trên.
- Bản thân chiếc hộp trải trên khoảng tứ phân vị (IQR), từ tứ phân vị thứ nhất (25% dữ liệu nằm dưới) đến tứ phân vị thứ ba (75% nằm dưới). Một nửa ở giữa của dữ liệu nằm gọn trong hộp.
- Râu (whisker) là các đường kéo dài ra từ hộp. Mỗi râu vươn tới điểm dữ liệu xa nhất vẫn nằm trong khoảng 1,5 × IQR tính từ cạnh hộp, nên râu bao "khoảng thông thường" của dữ liệu, không phải toàn bộ khoảng.
- Các điểm riêng lẻ bên ngoài râu là những giá trị cách hộp hơn 1,5 × IQR: chúng được đánh dấu là nghi là ngoại lai (outlier) và được vẽ từng điểm một để bạn đếm được.
Hộp cao nghĩa là dữ liệu phân tán; đường trung vị lệch khỏi giữa hộp nghĩa là dữ liệu bị lệch; một chuỗi điểm vượt qua một bên râu nghĩa là phía đó có đuôi dày. Histogram cho thấy cùng phân phối đó chi tiết hơn, nhưng sức mạnh đặc biệt của biểu đồ hộp là cả chục hộp có thể đặt cạnh nhau, nên đây chính là biểu đồ dành cho việc so sánh các nhóm.
Boxplot cho một biến
Hàm cần dùng là boxplot(), và với một vector số thì chỉ cần một lần gọi:
scores <- c(52, 55, 58, 60, 61, 63, 64, 66, 68, 70, 72, 95)
boxplot(scores,
main = "Test scores",
ylab = "Score")
Hình được vẽ ra: một hộp từ 59 đến 69 với đường trung vị ở 63,5, râu kéo xuống 52 và lên 72, cùng một điểm đơn độc lơ lửng ở 95. Đó là giá trị ngoại lai mà một bản tóm tắt bằng trung bình và độ lệch chuẩn sẽ lặng lẽ nuốt mất. Cảm nhận tức thì rằng "có một giá trị khác hẳn phần còn lại" chính là lý do biểu đồ hộp tồn tại.
(Như mọi trang trong chương này, các lệnh vẽ biểu đồ là đoạn code tĩnh; trình chạy bên dưới chỉ hiển thị đầu ra dạng văn bản. Hãy chạy chúng trên máy của bạn để xem hình.)
So sánh các nhóm: dùng công thức
Cách dùng hằng ngày thực sự là mỗi nhóm một hộp, và để làm vậy boxplot() nhận một công thức (formula): y ~ group, đọc là "y chia theo group". Bộ dữ liệu có sẵn của R là ToothGrowth (chiều dài răng của 60 con chuột lang được cho uống vitamin C qua nước cam (OJ) hoặc axit ascorbic (VC)) là ví dụ kinh điển:
boxplot(len ~ supp,
data = ToothGrowth,
main = "Tooth growth by supplement",
xlab = "Supplement",
ylab = "Tooth length")
Hai hộp xuất hiện trên cùng một thang đo dọc: hộp OJ nằm cao hơn hẳn hộp VC, với trung vị khoảng 22 so với khoảng 19. Vì hai hộp dùng chung một trục, phép so sánh trung thực ngay từ cấu trúc: không có chuyện hai biểu đồ với thang đo khác nhau làm một nhóm trông đẹp hơn. Cột phân nhóm nên là một factor (hoặc vector character); mỗi mức trở thành một hộp.
Công thức cũng có thể kết hợp: boxplot(len ~ supp * dose, data = ToothGrowth) vẽ sáu hộp, mỗi tổ hợp loại bổ sung và liều lượng một hộp. Khi sự khác biệt giữa các nhóm trông có vẻ thật trên các hộp, câu hỏi tự nhiên tiếp theo (liệu nó có hơn nhiễu ngẫu nhiên không?) là việc của kiểm định t.
Nhãn, màu sắc và boxplot nằm ngang
Các đối số định dạng theo quy ước vẽ đồ thị cơ bản. names đặt nhãn cho các hộp, col tô màu cho chúng; truyền vào một vector để tô mỗi nhóm một màu khác nhau:
boxplot(len ~ supp,
data = ToothGrowth,
names = c("Orange juice", "Ascorbic acid"),
col = c("orange", "lightblue"),
main = "Tooth growth by supplement",
ylab = "Tooth length")
Thêm hai công tắc đáng dùng:
horizontal = TRUExoay cả biểu đồ để các hộp nằm dọc theo trục ngang. Hãy làm vậy mỗi khi tên nhóm dài: nhãn nằm ngang vẫn đọc được ở chỗ nhãn dọc chồng lên nhau.notch = TRUEkhoét một rãnh quanh mỗi trung vị; khi rãnh của hai nhóm không chồng lên nhau, trung vị của chúng có khả năng khác nhau. Hãy xem đây là gợi ý trực quan, không phải một phép kiểm định.
Các con số đằng sau chiếc hộp
Mọi thứ biểu đồ vẽ ra đều đến từ những con số bạn có thể in ra. quantile() cho bản tóm tắt năm số, còn boxplot.stats() cho bạn chính xác những gì biểu đồ dùng, kể cả các giá trị ngoại lai. Đoạn này bạn có thể chạy ngay tại đây:
Lưu ý rằng hai kết quả này giống nhau nhưng không phải song sinh: quantile() báo giá trị nhỏ nhất và lớn nhất thực sự, còn s$stats dừng ở râu, tức điểm xa nhất nằm trong ngưỡng 1,5 × IQR. Phần chênh lệch giữa chúng chính là s$out, ở đây là số 95 đơn độc. Khi ai đó hỏi "những chấm ngoại lai kia là hàng nào?", x[x %in% boxplot.stats(x)$out] (hoặc một phép lọc theo giá trị ngưỡng) sẽ trả lời.
Boxplot với ggplot2
Trong ggplot2, việc phân nhóm nằm trong ánh xạ thẩm mỹ (aesthetic mapping) thay vì trong công thức:
library(ggplot2)
ggplot(ToothGrowth, aes(x = supp, y = len, fill = supp)) +
geom_boxplot() +
labs(title = "Tooth growth by supplement",
x = "Supplement", y = "Tooth length")
Vẫn là hai hộp đó, kèm chú giải và theme mà không tốn thêm công. Phiên bản ggplot2 mở rộng tốt hơn khi biểu đồ phức tạp dần: chia facet theo dose, phủ các điểm dữ liệu thô bằng geom_jitter(width = 0.1), và code vẫn mang tính khai báo. Còn để so sánh nhanh một lần trong lúc phân tích, boxplot(y ~ g, data = df) vẫn là thứ nhanh nhất bạn có thể gõ.
Những điều cần nhớ
- Cấu tạo hộp: đường trung vị, hộp = IQR, râu = điểm xa nhất trong khoảng 1,5 × IQR, các chấm bên ngoài = nghi là ngoại lai.
boxplot(x)cho một biến;boxplot(y ~ group, data = df)để so sánh các nhóm cạnh nhau trên cùng một thang đo trung thực.- Định dạng bằng
names, một vectorcolcho từng nhóm, vàhorizontal = TRUEkhi nhãn dài. boxplot.stats(x)$statsvà$outlà biểu đồ dưới dạng con số; dùng chúng để lấy ra các giá trị ngoại lai mà biểu đồ chỉ tới.- ggplot2:
geom_boxplot()với nhóm được ánh xạ vàoxhoặcfill.
Tiếp theo: biểu đồ phân tán, từ phân phối của một biến sang mối quan hệ giữa hai biến.
Câu hỏi thường gặp
Làm sao để vẽ boxplot trong R?
Gọi boxplot(x) trên một vector số để có một hộp, hoặc dùng công thức boxplot(y ~ group, data = df) để có mỗi nhóm một hộp, đặt cạnh nhau. Cả hai đều có sẵn trong base R, không cần cài thêm package.
Các phần của biểu đồ hộp có ý nghĩa gì?
Đường đậm bên trong hộp là trung vị. Hộp trải dài trên khoảng tứ phân vị (IQR), từ tứ phân vị thứ nhất đến tứ phân vị thứ ba. Râu vươn tới điểm dữ liệu xa nhất vẫn nằm trong khoảng 1,5 lần IQR tính từ hộp, và mọi điểm nằm ngoài râu được vẽ thành một điểm riêng: đó là giá trị nghi là ngoại lai.
Làm sao để vẽ boxplot theo nhóm trong R?
Dùng công thức: boxplot(len ~ supp, data = ToothGrowth) vẽ mỗi mức (level) của supp một hộp. Biến phân nhóm nên là cột factor hoặc character; mỗi mức trở thành một hộp trên cùng một thang đo.
Làm sao để tìm các giá trị ngoại lai mà boxplot hiển thị?
boxplot.stats(x)$out trả về đúng những giá trị mà biểu đồ sẽ vẽ thành điểm bên ngoài râu, còn boxplot.stats(x)$stats cho năm con số đằng sau chiếc hộp: hai đầu râu, hai cạnh hộp (hinge) và trung vị.