Trực quan hóa dữ liệu
Visualization in R
Sự cần thiết của trực quan dữ liệu
Một số khái niệm về trực quan
Có một vấn đề được đặt ra là: Với hàng triệu mẫu DNA và con số ngày nay cùng với hàng triệu ca nhiễm COVID-19 trên toàn thế giới ngày càng gia tăng thì chúng ta cần làm gì để có thể biết được xu hướng, ý nghĩa và cách phân phối của dữ liệu (ứng với từng quốc gia cho các ca nhiễm COVID-19 chẳng hạn). Việc phân tích mối tương quan hoặc xu hướng phân bố của dữ liệu đóng vai trò cực kỳ quan trọng trong nghiên cứu khoa học và đánh giá thống kê.The number of infected cases in INDIA
Người Trung Hoa hay có câu một hình ảnh có giá trị tương đương với một vạn chữ. Quả đúng như vậy, với một “đại dương” số liệu thì việc khai thác hiệu quả “đại dương” này là một vấn đề nan giải. Chúng ta cần phải hiểu rằng, khi có dữ liệu thì việc truyền tải thông tin tới người khác là vô cùng khó khăn. Phải làm thế nào để họ hiểu, họ nắm được xem bộ dữ liệu đó nói về cái gì, miêu tả cái gì và rút ra điều gì từ bộ dữ liệu đó. Chữ viết hoàn toàn có thể sử dụng cho những bộ dữ liệu đơn giản nhưng không thể thực hiện được việc truyền tải nội dung về xu hướng và dao dộng của dữ liệu
Một số biểu đồ trực quan
Hiển thị dữ liệu, mô phỏng dữ liệu hay trực quan dữ liệu (Visualization) nói chung là một bước quan trọng trong tổng thể phân tích dữ liệu. Trước khi phân tích bất kỳ một mô hình nào, việc đầu tiên là chúng ta cần “cảm thụ” được bộ dữ liệu đó, tức là có một cái nhìn khái quát nhất về bộ dữ liệu mà chúng ta đang có. Hiện nay, trong phân tích nghiên cứu khoa học, một số biểu đồ thường được sử dụng, đó là:
- Biểu đồ tương quan (scatterplot)
- Biểu đồ phân phối (histogram)
- Biểu đồ thanh (barchat)
- Biểu đồ hộp (box plot)
Lịch sử hiển thị dữ liệu
Biểu đồ có một lịch sử rất thú vị. Trước thể kỷ 18, dữ liệu khoa học thường được trình bày dạng bảng (tables) và đôi khi biểu đồ được coi là vô dụng vì không phản ánh bất kỳ điều gì. Quan điểm này khá là sai lầm, năm 1786, William Playfair (một nhà kinh tế chính trị học) sáng tạo ra biểu đồ tròn (pie chart), biểu đồ thanh (bar chart) và biểu đồ dây (line chart), những cái mà chúng ta vẫn sử dụng cho tới ngày nay. Mãi đến năm 1832, nhà thiên văn học người Anh là Alexander S. Herche sáng chế ra biểu đồ tương quan (scatter plot) và trở thành rất phổ biến trong khoa học thực nghiệm.
Nguyên tắc thiết kế biểu đồ
Hãy làm quen với Edward Tufte, vì ông là một guru - bậc thầy về biểu đồ. Ông là giáo sư thống kê học của Đại học Yale, giáo sư chính trị học và giáo sư về khoa học máy tính của đại học Yale, ông là người có ảnh hưởng rất lớn tới lĩnh vực trực quan dữ liệu. Ông là người đã dám thế chấp căn nhà của mình để vay tiền ngân hàng nghiên cứu và cho ra công trình data visualization mà sau này ông không bao giờ phải hối hận. Tờ báo New York Times gọi ông là Leonardo Da Vinci of Data
Edward Tufte đặt ra 4 triết lý và nguyên tắc chính trong trình bày dữ liệu bằng biểu đồ. Có thể tóm gọn như sau “Graphical excellence is that which gives to the viewer the greatest number of ideas in the shortest time with the least ink in the smallest space”. Như vậy, khi trình bày dữ liệu bằng biểu đồ phải để ý tới thông tin của 4 khía cạnh: Lượng thông tin, thời gian, lượng mực in và không gian.
Từ đó có thể hiểu 4 nguyên tắc chính khi trình bày dữ liệu chính là: Phản ảnh dữ liệu một cách đầy đủ, tối ưu hóa dữ liệu trên mực in, tối ưu hóa mật độ dữ liệu và trình bày dữ liệu chứ không phải trang trí biểu đồ.Edward Tufte
Boxplot1
Boxplot2
Barplot1
Barplot1
Thư viện GGPLOT2
Giới thiệu về ggplot2
Chương trình ggplot2 do tác giả Hadley Wickham phát triển và phổ biến là một thư viện rất có ích cho việc biên soạn biểu đồ với chất lượng cao. Cú pháp của ggplot2 được mô phỏng theo việc chồng lấp các lớp (layers); mỗi lớp sẽ có một chức năng riêng. Tuy nhiên có 3 layers chính, đó là:
- Lớp xác định biến số cần trực quan dữ liệu
- Lớp hình thức thể hiện
- Lớp trang trí, gán nhãn cho biểu đồ..
Lớp biến số
Về định nghĩa biến số, thông thường một biểu đồ thường có 2 biến số chính là \(x\) và \(y\). Nếu chúng ta muốn thể hiện mối liên quan giữa giới tính và thu nhập thì thông thường \(x\) sẽ là biến giới tính và \(y\) là biến thu nhập. Ví dụ dưới đây chỉ ra mối liên hệ giữa thu nhập và tuổi thọ trung bình giữa các quốc gia trong thư viện ggplot2.
Cú pháp của lớp này như sau:
\(p=ggplot(data=gapminder,aes(x=gdpPercap,y=lifeExp))\)
=ggplot(data=gapminder,aes(x=gdpPercap,y=lifeExp))
p+ geom_point(aes(color = continent)) p
Hình thức thể hiện:
Hình thức thể hiện của biến x và y thể hiện qua argument có tên là geom(), trong đó có một số loại phổ biến sau:
- geom_point(): Biểu đồ tương quan
- geom_box(): Biểu đồ hộp
- geom_histogram(): Biểu đồ phân bố
- geom_line(): Biểu đồ dây
- geom_text(): Biểu đồ chữ viết
- geom_smooth(): Chọn đường biểu diễn làm mịn dữ liệu
- geom_jitter(): Tạo sàng rung, hiển thị dữ liệu, fix lỗi overplotting
- geom_hline(): Đường biểu diễn nằm ngang
- geom_vline(): Đường biểu diễn thẳng đứng
Trang trí biểu đồ
Tiêu đề của biểu đồ
ggtitle(“tên biểu đồ”) + theme(plot.title = element_text(line.height = 0.8, face = “bold”, hjust = 0.5))
Nhãn và trục tung, trục hoành
labs(x=“Tiêu đề trục x”, y = “Tiêu đề trục y”)
Màu và kích thước của nhãn trục tung và hoành
theme(axis.title.x = element_text(color = “blue, size = 14, face =”bold“), axis.title.y = element_text(color =”blue, size = 14, face =“bold”))
Khoảng cách giá trị của trục tung và trục hoành
scale_x_continuous(name=“…”, limits =c(min, max)) + scale_y_continuous(name=“…”, limits = c(min,max)) Một trong những lợi thế của ggplot2 là cho phép người sử dụng hoán chuyển dữ liệu ngày trong phần arguments của hàm ggplot. Chúng ta có thể hoán chuyển đơn vị của trục tung (thu nhập) sang đơn vị log:
= ggplot(data=gapminder,aes(x=gdpPercap,y=lifeExp)) + geom_point(aes(color = continent))
p1 <- p1 + scale_x_log10() + geom_smooth(method="loess")
p1 p1
## `geom_smooth()` using formula 'y ~ x'
Thực hành ggplot2
Để áp dụng cách sử dụng thư viện ggplot2, chúng ta sử dụng bộ dữ liệu có tên là gapminder, vẽ biểu đồ tương quan giữa 2 biến là tuổi thọ trung bình và thu nhập bình quân. Xem qua về dữ liệu
<- gapminder
mydata head(gapminder)
## # A tibble: 6 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Afghanistan Asia 1952 28.8 8425333 779.
## 2 Afghanistan Asia 1957 30.3 9240934 821.
## 3 Afghanistan Asia 1962 32.0 10267083 853.
## 4 Afghanistan Asia 1967 34.0 11537966 836.
## 5 Afghanistan Asia 1972 36.1 13079460 740.
## 6 Afghanistan Asia 1977 38.4 14880372 786.
str(gapminder)
## tibble [1,704 x 6] (S3: tbl_df/tbl/data.frame)
## $ country : Factor w/ 142 levels "Afghanistan",..: 1 1 1 1 1 1 1 1 1 1 ...
## $ continent: Factor w/ 5 levels "Africa","Americas",..: 3 3 3 3 3 3 3 3 3 3 ...
## $ year : int [1:1704] 1952 1957 1962 1967 1972 1977 1982 1987 1992 1997 ...
## $ lifeExp : num [1:1704] 28.8 30.3 32 34 36.1 ...
## $ pop : int [1:1704] 8425333 9240934 10267083 11537966 13079460 14880372 12881816 13867957 16317921 22227415 ...
## $ gdpPercap: num [1:1704] 779 821 853 836 740 ...
Chúng ta bắt đầu trực quan dữ liệu
<- ggplot(data = mydata, mapping = aes(x = gdpPercap, y = lifeExp))
p2 + geom_point() -> p2 p2
Thêm một đường biểu diễn tương quan tuyến tính
+ geom_smooth(method = "loess") -> p2 p2
Tuy nhiên, quá nhiều điểm dữ liệu cùng màu, rất khó phân biệt, trực quan theo biến lục địa
ggplot(data = mydata, mapping = aes(x = gdpPercap, y = lifeExp)) + geom_point(aes(color = continent)) +
geom_smooth(method = "loess")
## `geom_smooth()` using formula 'y ~ x'
Chuyển sang đơn vị log() cho dễ nhìn
ggplot(data = mydata, mapping = aes(x = gdpPercap, y = lifeExp)) + geom_point(aes(color = continent)) +
geom_smooth(method = "loess") +
scale_x_log10()
## `geom_smooth()` using formula 'y ~ x'
Gán thêm các nhãn cho biểu đồ
ggplot(data = mydata, mapping = aes(x = gdpPercap, y = lifeExp)) + geom_point(aes(color = continent)) +
geom_smooth(method = "loess") +
scale_x_log10() +
labs(x =" Log GDP per Capita", y = "Life Expectancy") +
ggtitle("Association between GDP Per Capita and Life Expectancy") + theme(plot.title = element_text(lineheight = 0.8, face = "bold", hjust = 0.5))
## `geom_smooth()` using formula 'y ~ x'
Thay đổi theo phong cách màu của tạp chí “The Economist” như sau:
library(ggthemes)
ggplot(data = mydata, mapping = aes(x = gdpPercap, y = lifeExp)) + geom_point(aes(color = continent)) +
geom_smooth(method = "loess") +
scale_x_log10() +
labs(x =" Log GDP per Capita", y = "Life Expectancy") +
ggtitle("Association between GDP Per Capita and Life Expectancy") + theme(plot.title = element_text(lineheight = 0.8, face = "bold", hjust = 0.5)) + theme_economist()
## `geom_smooth()` using formula 'y ~ x'
Một số biểu đồ phổ biến khác sử dụng ggplot2
Biểu đồ histogram
Đây còn được gọi là biểu đồ phân bố, sử dụng rất hữu ích khi thể hiện sự phân bố của một biến số liên tục nào đó. Trong bộ dữ liệu gapminder, thì biến gdpPercap là một biến số liên tục ngẫu nhiên, chúng ta thử vẽ biểu đồ histogram cho biến số này vào năm 2007. Sử dụng lệnh subset() đã học để bóc tách dữ liệu cho năm 2007
<- subset(mydata, year == 2007)
year2007 head(year2007)
## # A tibble: 6 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Afghanistan Asia 2007 43.8 31889923 975.
## 2 Albania Europe 2007 76.4 3600523 5937.
## 3 Algeria Africa 2007 72.3 33333216 6223.
## 4 Angola Africa 2007 42.7 12420476 4797.
## 5 Argentina Americas 2007 75.3 40301927 12779.
## 6 Australia Oceania 2007 81.2 20434176 34435.
ggplot(data = year2007, mapping = aes(gdpPercap)) +
geom_histogram(fill = "lightblue", color = "white") +
labs(title = "Distribution of GDP per Capita in 2007")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Nếu chúng ta không muốn trục Oy là count mà muốn là xác suất (tần suất xuất hiện)
ggplot(data = year2007, mapping = aes(gdpPercap)) +
geom_histogram(aes(y = ..density..), fill = "lightblue", color = "white")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Nếu chúng ta quan tâm phân phối theo từng lục địa
ggplot(data = year2007, mapping = aes(gdpPercap, fill = continent)) +
geom_density(alpha = 0.4)
Biểu đồ thanh Barplot
Biểu đồ này thường được dùng để biểu thị tần số xuất hiện của 1 hay nhiều hơn 1 biến liên tục. Ví dụ: Chúng ta muốn tìm hiểu số quốc gia trong mỗi lục địa là bao nhiêu, sử dụng dữ liệu gapminder cho năm 2007.
dim(year2007)
## [1] 142 6
ggplot(data = year2007, mapping = aes(x = continent, fill = continent)) +
geom_bar()
Tuy nhiên phần legend hơi thừa, xóa bớt
dim(year2007)
## [1] 142 6
ggplot(data = year2007, mapping = aes(x = continent, fill = continent)) +
geom_bar() + theme(legend.position = "none")
Ngoài ra biểu đồ barplot còn thể hiện được sự liên quan giữa hai biến số thông qua kiểu (stacked bar plot), biểu đồ thanh chồng lên nhau. Ví dụ tuổi của các quốc gia có thể phân thành các nhóm sau đây:
- Nhóm 1: Gồm các quốc gia có tuổi thọ dưới 60
- Nhóm 2: Gồm các quốc gia có tuổi thọ lớn hơn 60 và nhỏ hơn 80
- Nhóm 3: Gồm các quốc gia có tuổi thọ trên 80
Sử dụng toán tử điều kiện [] để tạo ra một biến mới tên là level_age có tính thứ bậc
$level_age[year2007$lifeExp < 60.0] <- "< 60 age" year2007
## Warning: Unknown or uninitialised column: `level_age`.
$level_age[60.0 <= year2007$lifeExp & year2007$lifeExp <= 80.0] <- "60-80 age"
year2007$level_age[year2007$lifeExp > 80.0] <- "> 80 age"
year2007ggplot(data = year2007, aes(x = continent, fill = level_age)) +
geom_bar()
Biểu đồ Barplot cũng có thể miêu tả một biến liên tục như sau. Ví dụ: Chúng ta muốn thể hiện tuổi thọ trung bình của mỗi quốc gia thuộc Châu Á (Asian). Trước tiên cần lọc các quốc gia có lục địa là Asian:
<- subset(mydata, year == 2007 & continent == "Asia")
asia dim(asia)
## [1] 33 6
Như vậy ở Châu Á có 33 quốc gia. Trực quan dữ liệu
ggplot(data = asia, mapping = aes(x = country, y = lifeExp, fill = country)) + geom_bar(stat = "identity", width = 0.9)
Tuy nhiên giao diện rất khó nhìn và bị chồng lên nhau ở trục Ox, chúng ta đảo ngược
ggplot(data = asia, mapping = aes(x = country, y = lifeExp, fill = country)) + geom_bar(stat = "identity", width = 0.9) + coord_flip()
Biểu đồ này rất khó so sánh, chúng ta nên sắp xếp lại theo trật tự
ggplot(data = asia, mapping = aes(x = reorder(country, lifeExp), y = lifeExp, fill = country)) + geom_bar(stat = "identity", width = 0.9) + coord_flip() + theme(legend.position = "none") + labs(x="", y="Life Expectancy") -> graph1
Ngoài ra chúng ta có thể trình bày nhiều biểu đồ trên cùng một hình, sử dụng gói gridExtra, biểu diễn hai đồ thị thanh cho tuổi thọ và thu nhập bình quân đầu người
ggplot(data = asia, mapping = aes(x = reorder(country, gdpPercap), y = gdpPercap, fill = country)) + geom_bar(stat = "identity", width = 0.9) + coord_flip() + theme(legend.position = "none") + labs(x="", y="GDP Per Capita") -> graph2
grid.arrange(graph1, graph2, ncol = 2)
Biểu đồ hộp (Boxplot)
Giới thiệu về biểu đồ hộp
Biểu đồ hộp trong tiếng Anh là Box Plot hay Box and Whisker plot, là một loại biểu đồ thể hiện các khuôn hình của dữ liệu định tính (quantitative data).
Biểu đồ hộp do John Tukey sáng tạo ra năm 1977.
Biểu đồ hộp (Box plot) hay còn gọi là biểu đồ hộp và râu (Box and whisker plot) là biểu đồ diễn tả 5 vị trí phân bố của dữ liệu, đó là: - Giá trị nhỏ nhất (min) - Giá trị tứ phân vị thứ nhất (Q1) - Giá trị trung vị (median) - Giá trị tứ phân vị thứ 3 (Q3) - Giá trị lớn nhất (max)
John Tukey
John Tukey : “Đặt câu hỏi đúng quan trọng hơn ngàn lần tìm câu trả lời đúng cho một câu hỏi sai.”
Đặc trưng của biểu đồ hộp
Số phân tử hay còn gọi là tứ phân vị (Quartiles): Tứ phân vị là đại lượng mô tả sự phân bố và sự phân tán của tập dữ liệu. Số phân tử có 3 giá trị, đó là số phân tử thứ nhất (Q1), thứ nhì (Q2) và thứ ba (Q3). Ba giá trị này chia một tập hợp dữ liệu (đã sắp xếp dữ liệu theo trật từ bé đến lớn) thành 4 phần có số lượng quan sát đều nhau.
Tứ phân vị được xác định như sau:
Sắp xếp các số theo thứ tự tăng dần
Cắt dãy số thành 4 phần bằng nhau
Tứ phân vị là các giá trị tại vị trí cắt
Tứ phân vị
IQR
Biểu đồ boxplot còn thể hiện 2 đại lượng phổ biến khi nói về độ rộng của một tập dữ liệu:
- Khoảng dữ liệu (range): Nếu bạn quan tâm đến độ rộng của tất cả dữ liệu thì, đó đơn giản là khoảng cách giữa giá trị lớn nhất và giá trị nhỏ nhất trong tập dữ liệu. Còn nếu bạn muốn loại trừ các giá trị ngoại lai, thì đó là khoảng cách giữa 2 đầu ria mép!
- Khoảng liên phần tư (IQR): là nửa giữa của tập dữ liệu nằm giữa 2 điểm Q3 và Q1. Trong biểu đồ trên, IQR là khoảng 7 – 3 = 4
Độ lệch
Đây là 3 hình dạng chủ yếu về độ lệch. Nếu đường trung vị chia chiếc hộp thành 2 nửa đều nhau, thì tập dữ liệu này đối xứng (symmetric). Nếu nửa phải lớn hơn (nửa trái) thì tập dữ liệu bị lệch phải (right-skewed), và ngược lại, nếu nửa trái lớn hơn thì tập dữ liệu bị lệch trái (left-skewed).
Ví dụ Boxplot và cách đọc
Dưới đây mô tả sử dụng biểu đồ hộp để phân tích, nhận biết vấn đề.
Ví dụ: Với số liệu thu thập được về tỉ lệ làm lại (Rework Ratio) trong quá trình sản xuất, (có xmin = 0,0; Q1 = 14,9; x = 19,0; x = 15,8; Q3 = 20,6; xmax =23,2) ta có biểu đồ hộp với hình dáng biểu đồ như sau:Rework Ration
Trung bình tỉ lệ làm lại là 15,8%, trung vị là 19%. Dữ liệu có xu hướng nghiêng nhiều về phía trên giá trị trung bình:
Khoảng số phân tử = Q3 - Q1 = 20,6 - 14,9 = 5,7
Khoảng cách giữa giá trị lớn nhất và nhỏ nhất là 23,2 - 0 = 23,2.
Rework Ration
Nhận xét:
Với ba lần thu thập dữ liệu về tỉ lệ làm lại vào thời điểm tháng 11/2011, tháng 3/2012 và tháng 6/2012, dữ liệu vào thời điểm tháng 11/2011 cho thấy quá trình kiểm soát lỗi kém vì xu hướng tập trung của dữ liệu (trung vị) ở mức cao, độ dao động lớn.
Kiểm soát chất lượng vào thời điểm tháng 3/2012 là tốt nhất vì dữ liệu về tỉ lệ làm lại tập trung ở mức thấp, dao động ở phạm vi hẹp.
Áp dụng vào dữ liệu
Giả sử chúng ta muốn tóm tắt thu nhập bình quân năm 2007 của 142 quốc gia bằng biểu đồ hộp
ggplot(data = year2007, aes(x = continent, y = gdpPercap, fill = continent)) + geom_boxplot(alpha = 0.6)
ggplot(data = year2007, aes(x = continent, y = gdpPercap, fill = continent)) + geom_boxplot(alpha = 0.6) + geom_jitter(alpha = 0.3)