library(DT)
library(WDI)
library(tidyverse)
library(scales) # Để sử dụng hàm percent, đinh dạng %
options(digits = 4)
Để lấy được và lấy đúng dữ liệu chúng ta cần từ Worldbank chúng ta phải cài Package WDI và hiểu được tên các biến thống kê (indecator) do WB đặt. Tên và ý nghĩa của các indicator có thể tìm hiểu ở đây: https://datahelpdesk.worldbank.org/knowledgebase/articles/201175-how-does-the-world-bank-code-its-indicators.
Sau khi đã hoàn thành bước cài đặt và tìm hiểu chúng ta sẽ thực hiện một số thao tác sau để tìm dữ liệu chúng ta cần và download về. Trong package WDI chúng ta thường xuyên sử dụng 2 hàm sau:
ind <- WDIsearch('gdp')
datatable( ind)
ind <- WDIsearch('Total reserves')
datatable(ind)
Chúng ta thấy kết quả của câu lệnh này sẽ trả về tên của các indicator và chú thích của chúng.
d <- WDI(indicator = 'NY.GDP.PCAP.KD.ZG', country = 'VN', extra = T, start = 1960, end = 2022)
tmp <- na.omit(d)
tmp <- tmp |> select(year, NY.GDP.PCAP.KD.ZG, income)
tmp$NY.GDP.PCAP.KD.ZG <- .01*tmp$NY.GDP.PCAP.KD.ZG
tmp$NY.GDP.PCAP.KD.ZG <- percent(tmp$NY.GDP.PCAP.KD.ZG, accuracy = .01)
datatable(tmp)
Tương tự chúng có thể download dữ liệu về Dự trữ ngoại hối của Việt Nam theo tháng nhập khẩu và xử lý thì được kết quả như sau:
d <- WDI(indicator = 'FI.RES.TOTL.MO', country = c('VN'), extra = T)
tmp <- na.omit(d)
tmp <- tmp |> select(year, FI.RES.TOTL.MO, income)
tmp$FI.RES.TOTL.MO <- paste(round(tmp$FI.RES.TOTL.MO,2), 'Tháng')
datatable(tmp)
Trực quan hóa dữ liệu, hay nói cách khác là thể hiện dữ liệu dưới dạng hình ảnh. Khi dữ liệu được thể hiện dưới dạng hình ảnh sẽ giúp người đọc sẽ dễ hình dung hơn về dữ liệu.
Chúng ta sẽ bắt đầu với các loại biểu đồ, đồ thị truyền thống và sẽ phát triển lên những dạng tốt hơn.
Trong R sẽ có nhiều công cụ (package, hàm) khác nhau để trực quan hóa dữ liệu, nhưng chúng tôi sẽ chủ yếu tập trung vào package ggplot2.
Để thuận tiện cho việc thực hành, chúng ta sẽ sử dụng một số bộ dữ liệu (data set) đã có sẵn trong R.
Trong những ví dụ sau chúng ta sẽ sử dụng bộ dữ liệu trees để thực hiện thao tác vẽ đồ thị.
library(ggplot2)
data("trees")
datatable(trees)
trees |> ggplot(map = aes(x = Girth, y = Volume)) +
geom_point()
trees |> ggplot(map = aes(x = Girth, y = Volume)) +
geom_point(color = 'red')
trees |> ggplot(map = aes(x = Girth, y = Volume)) +
geom_point(color = 'red') +
xlab('Chu vi') +
ylab('Thể tích')
Trong trường hợp cần thiết chúng ta có thể vẽ thêm đường hồi quy (tuyến tính) tương ứng với dữ liệu mà chúng ta dùng để vẽ đồ thị scatter như sau:
trees |> ggplot(aes(x = Girth, y = Volume)) +
geom_smooth(formula = y ~ x, method = 'lm', color = 'green') +
geom_point(color = 'red') +
labs(title = 'Đồ Thị Dạng Scatter', x = 'Chu vi', y = 'Thể tích')
Chúng ta cũng có thể nối các điểm trong đồ thị dạng scatter của 2 biến Girth và Volume lại với nhau bằng những đoạn thẳng như sau:
trees |> ggplot(aes(x = Girth, y = Volume)) +
geom_point(color = 'blue') +
geom_line(color = 'red')
Để mở rộng việc sử dụng đồ thị dạng scatter chúng ta sẽ gọi bộ dữ liệu penguins trong package palmerpenguins. Bộ dữ liệu này được các nhà khoa học thu thập nhằm mục đích nghiên cứu về các loài chim cánh cụt ở Nam cực.
Để có thể thấy được tương quan giữa biến flipper_length_mm và biến body_mass_g, chúng ta sẽ vẽ đồ thị dạng scatter cho 2 biến này như sau:
library(palmerpenguins)
data(penguins)
datatable(penguins)
penguins |> ggplot(aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point(na.rm = T) +
xlab('Chiều dài cánh') +
ylab('Trọng lượng cơ thể')
Tuy nhiên chúng ta còn có thể có cái nhìn sâu hơn về tương quan giữa 2 biến này theo loài hoặc theo nơi chúng sinh sống như sau:
penguins |> ggplot(aes(x = flipper_length_mm, y = body_mass_g, color = species)) +
geom_point(na.rm = T) +
xlab('Chiều dài cánh') +
ylab('Trọng lượng cơ thể')
penguins |> ggplot(aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point(aes(color = species), na.rm = T) +
xlab('Chiều dài cánh') +
ylab('Trọng lượng cơ thể')
penguins |> ggplot(aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point(aes(color = island), na.rm = T) +
xlab('Chiều dài cánh') +
ylab('Trọng lượng cơ thể')
Ngoài việc phân loại dữ liệu bằng màu sắc chúng ta còn có thể phân biệt
dữ liệu bằng hình dáng như sau:
penguins |> ggplot(aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point(aes(shape = island), na.rm = T, color = 'blue', size = 3) +
xlab('Chiều dài cánh') +
ylab('Trọng lượng cơ thể')
Đôi khi chúng ta quan tâm đến một số quan sát đặc biệt nào đó, chúng ta có thể tạo một sự khác biệt để có thể nhận biết được biết được các quan sát này dễ hơn. Điều này được thực hiện như sau:
penguins |> ggplot(aes(x = flipper_length_mm, y = body_mass_g, shape = island)) +
geom_point(na.rm = T, size = 3, color = 'red', size = 3) +
geom_point(data = penguins |> filter(sex == 'male'), size = 3, color = 'blue') +
xlab('Chiều dài cánh') +
ylab('Trọng lượng cơ thể')
## Warning: Duplicated aesthetics after name standardisation: size
Chúng ta cũng có thể thêm hàm hồi quy (tuyến tính) vào đồ thị scatter như ví dụ bên trên, tuy nhiên chúng ta còn có thể thêm được nhiều hàm hồi quy (tuyến tính) tương ứng với từng loài hoặc tương ứng với nhiều nơi sinh sống như sau:
penguins |> ggplot(aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point(aes(color = species), na.rm = T) +
geom_smooth(aes(color = species), formula = y ~ x, method = 'lm', na.rm = T) +
xlab('Chiều dài cánh') +
ylab('Trọng lượng cơ thể')
penguins |> ggplot(aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point(aes(color = island), na.rm = T) +
geom_smooth(aes(color = island), formula = y ~ x, method = 'lm', na.rm = T) +
xlab('Chiều dài cánh') +
ylab('Trọng lượng cơ thể')
Hoặc chúng ta có thể vẽ thành nhiều đồ thị tương ứng với từng loài hoặc từng nơi sinh sống như sau:
penguins |> ggplot(aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point(aes(color = species), na.rm = T) +
geom_smooth(formula = y ~ x, method = 'lm', na.rm = T) +
facet_grid(. ~ species) +
xlab('Chiều dài cánh') +
ylab('Trọng lượng cơ thể')
penguins |> ggplot(aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point(aes(color = island), na.rm = T) +
geom_smooth(formula = y ~ x, method = 'lm', na.rm = T) +
facet_grid(. ~ island) +
xlab('Chiều dài cánh') +
ylab('Trọng lượng cơ thể')
Hoặc chúng ta có thể xắp xếp các đồ thị này theo phương dọc như sau:
penguins |> ggplot(aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point(aes(color = island), na.rm = T) +
geom_smooth(formula = y ~ x, method = 'lm', na.rm = T) +
facet_grid(island ~ .) +
xlab('Chiều dài cánh') +
ylab('Trọng lượng cơ thể')
Chúng ta sẽ sử dụng bộ dữ liệu Supermarket Transactions.csv để thực hành.
d <- read.csv('Supermarket Transactions.csv', header = T)
str(d)
## 'data.frame': 14059 obs. of 16 variables:
## $ X : int 1 2 3 4 5 6 7 8 9 10 ...
## $ PurchaseDate : chr "2007-12-18" "2007-12-20" "2007-12-21" "2007-12-21" ...
## $ CustomerID : int 7223 7841 8374 9619 1900 6696 9673 354 1293 7938 ...
## $ Gender : chr "F" "M" "F" "M" ...
## $ MaritalStatus : chr "S" "M" "M" "M" ...
## $ Homeowner : chr "Y" "Y" "N" "Y" ...
## $ Children : int 2 5 2 3 3 3 2 2 3 1 ...
## $ AnnualIncome : chr "$30K - $50K" "$70K - $90K" "$50K - $70K" "$30K - $50K" ...
## $ City : chr "Los Angeles" "Los Angeles" "Bremerton" "Portland" ...
## $ StateorProvince : chr "CA" "CA" "WA" "OR" ...
## $ Country : chr "USA" "USA" "USA" "USA" ...
## $ ProductFamily : chr "Food" "Food" "Food" "Food" ...
## $ ProductDepartment: chr "Snack Foods" "Produce" "Snack Foods" "Snacks" ...
## $ ProductCategory : chr "Snack Foods" "Vegetables" "Snack Foods" "Candy" ...
## $ UnitsSold : int 5 5 3 4 4 3 4 6 1 2 ...
## $ Revenue : num 27.38 14.9 5.52 4.44 14 ...
datatable(d)
## Warning in instance$preRenderHook(instance): It seems your data is too big for
## client-side DataTables. You may consider server-side processing:
## https://rstudio.github.io/DT/server.html
Chúng ta sẽ đồ thị cột cho biến Gender.
d |> ggplot(aes(x = Gender )) +
geom_bar( fill = 'blue')
Thay vì thể hiện bằng số đếm thì chúng ta có thể thì chúng ta sẽ thể hiện bằng tỉ lệ % như sau:
d |> group_by(Gender) |>
summarise(n = n()) |>
mutate(pG = percent(n/sum(n), accuracy = ,01)) |>
ggplot(aes(x = Gender, y = pG)) +
geom_col(fill = 'blue') +
theme_classic() +
labs(x = 'Giới tính', y = 'Tỷ lệ %')
Chúng ta có thể ghi chú tỷ lệ % hoặc số đếm lên đỉnh mỗi cột như sau:
d |> ggplot(aes(x = Gender, y = after_stat(count))) +
geom_bar(fill = 'blue') +
geom_text(aes(label = scales::percent(after_stat(count/sum(count)))), stat = 'count', color = 'red', vjust = - .5) +
theme_classic() +
labs(x = 'Giới tính', y = 'Số người')
Ngoài việc tính tỷ lệ % và đếm số người theo giới tính chúng ta có thể
phân chia đồ thị theo một một số tiêu chí (biến) khác để phân tích. Ví
dụ chúng ta phân chia theo biến Homeowner
d |> ggplot(aes(x = Gender, y = after_stat(count))) +
geom_bar(fill = 'blue') +
geom_text(aes(label = scales::percent(after_stat(count/sum(count)))), stat = 'count', color = 'red', vjust = - .5) +
facet_grid(. ~ Homeowner) +
# theme_classic() +
labs(x = 'Giới tính', y = 'Số người')
Hoặc phân chia theo biến city
d |> ggplot(aes(x = Gender, y = after_stat(count))) +
geom_bar(fill = 'blue') +
geom_text(aes(label = scales::percent(after_stat(count/sum(count)))), stat = 'count', color = 'red', vjust = - .5) +
facet_grid(. ~ Country) +
# theme_classic() +
labs(x = 'Giới tính', y = 'Số người')
Chúng ta có thể vẽ biểu đồ cột cho biến Children như sau:
d |> count(Children) |>
mutate(pC = percent(n/sum(n),accuracy = 0.01)) |>
ggplot(aes(x = Children, y = n)) +
geom_col(fill = 'blue') +
geom_text(aes(label = pC),color = 'yellow', vjust = 2, size = 5) +
ylab('Số Người') +
xlab('Số Con')
Với biểu đồ như trên chúng ta có thể quay ngang như sau:
d |> count(Children) |>
mutate(pC = percent(n/sum(n),accuracy = 0.01)) |>
ggplot(aes(x = Children, y = n)) +
geom_col(fill = 'blue') +
geom_text(aes(label = pC),color = 'yellow', hjust = 2, size = 5) +
ylab('Số Người') +
xlab('Số Con') +
coord_flip()
Chúng ta cũng có thể vẽ đồ thi dạng cột theo biến Children nhưng đồng thời chúng ta sẽ phân đồ thì này thành 2 nhóm theo biến Homeowner như sau:
d |> ggplot(aes(x = Children, y = after_stat(count),fill = Homeowner)) +
geom_bar(position = 'dodge') +
ylab('Số Người') +
xlab('Số Con')
Chúng ta có thể thêm ghi chú cho đồ thị để người đọc dễ so sánh như sau:
d |> count(Children, Homeowner) |>
mutate(pCH = prop.table(n)) |>
ggplot(aes(x = Children, y = n, fill = Homeowner)) +
geom_col(position = 'dodge') +
geom_text(aes(label = percent(pCH, accuracy = .01)), position = position_dodge(1), vjust = -.5, size = 3) +
ylab('Số Người') +
xlab('Số Con')
Với dạng đồ thị như trên thay vì để 2 cột cạnh nhau, chúng ta có thể chồng nó lên nhau như sau để dễ phân tích hơn.
d |> ggplot(aes(x = Children)) +
geom_bar(aes(y = after_stat(count), fill = Homeowner), stat = 'count') +
ylab('Số Người') +
xlab('Số Con')
Tương tự như trường hợp ở trên, chúng ta cũng có thể thêm ghi chú và đồ
thị để người đọc dễ so sánh như sau:
d |> count(Children, Homeowner) |>
group_by(Children) |>
mutate(pH = n/sum(n)) |>
ggplot(aes(x = Children, y = n, fill = Homeowner)) +
geom_col() +
geom_text(aes(label = percent(pH, accuracy = .01)), position = position_stack(vjust = 0.5), size = 4) +
ylab('Số Người') +
xlab('Số Con')
Với biểu đồ cột chúng ta còn có thể làm được nhiều việc hơn những gì chúng ta đã làm bên trên. Ví dụ chúng ta sẽ vẽ đồ thị thể hiện số tiền trung bình của hóa đơn theo giới tính hoặc theo số con như sau:
d |> group_by(Gender) |>
summarise(mr = mean(Revenue, na.rm = T)) |>
ggplot(aes(x = Gender, y = mr)) +
geom_col(fill = 'blue') +
geom_text(aes(label = round(mr,2)), vjust = 2, color = 'red', size = 9) +
xlab('Giới Tính') +
ylab('Số tiền trung bình trên mỗi hóa đơn (USD)')
d |> group_by(Children) |>
summarise(mr = mean(Revenue, na.rm = T)) |>
ggplot(aes(x = Children, y = mr)) +
geom_col(fill = 'blue') +
geom_text(aes(label = round(mr,2)), vjust = 2, color = 'red', size = 5) +
xlab('Số con') +
ylab('Số tiền trung bình trên mỗi hóa đơn (USD)')
Tương tự chúng ta cũng có thể dùng đồ thị cột để vẽ phương sai, độ lệch chuẩn, trung vị hay bất kỳ một đặc trưng thống kê nào để thực hiện việc so sánh. Ví dụ:
d |> group_by(Children) |>
summarise(sdr = sd(Revenue, na.rm = T)) |>
ggplot(aes(x = Children, y = sdr)) +
geom_col(stat = 'identity', fill = 'blue') +
geom_text(aes(y = sdr + .5, label = round(sdr,2)), color = 'red', size = 4) +
xlab('Số con') +
ylab('Độ lệch chuẩn của số tiền trên hóa đơn (USD)')
## Warning in geom_col(stat = "identity", fill = "blue"): Ignoring unknown
## parameters: `stat`
Hoặc cũng là so sánh độ lệch chuẩn về số tiền trên hóa đơn theo số con
thì chúng ta còn có thể phân chia ra theo giới tính và số con như
sau:
d |> group_by(Children, Gender) |>
summarise(sdr = sd(Revenue, na.rm = T)) |>
ggplot(aes(x = Children, y = sdr, fill = Gender)) +
geom_col(position = 'dodge') +
geom_text(aes(label = round(sdr,2)), color = 'red', size = 4, position = position_dodge(1), vjust = -.5) +
xlab('Số con') +
ylab('Độ lệch chuẩn của số tiền trên hóa đơn (USD)')
## `summarise()` has grouped output by 'Children'. You can override using the
## `.groups` argument.
Trong phần này chúng ta sẽ sử dụng bộ dữ liệu Supper Market Sale gồm những biến sau:
d <- read.csv('https://github.com/sushantag9/Supermarket-Sales-Data-Analysis/blob/master/supermarket_sales%20-%20Sheet1.csv', header = T)