Bộ dữ liệu sử dụng là iris. Kiểm tra sơ bộ về bộ dữ liệu này
library(dplyr)
head(iris)
str(iris)
## 'data.frame': 150 obs. of 5 variables:
## $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
## $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
## $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
## $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
## $ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
Giải thích các Argument được áp dụng trong công thức:
3 kiểu đuôi hàm với ý nghĩa áp dụng
Các hàm cơ bản trong tidyverse
#Tạo thêm trường Sepal.NuaChuvi = Sepal.Length+Sepal.Width đính kèm vào bảng cũ
iris %>% mutate(Sepal.NuaChuVi = Sepal.Length + Sepal.Width) %>% head()
#Chỉ tạo thêm trường Sepal.NuaChuvi và drop các trường thuộc bảng cũ.
iris %>% transmute(Sepal.NuaChuVi = Sepal.Length + Sepal.Width) %>% head()
Các hàm thuộc nhóm transmute kết quả hoàn toàn tương tự như mutate chỉ khác ở điểm duy nhất là có giữa nguyên các trường cũ hay không nên các ví dụ sau sẽ không trình bày về transmute. Bạn đọc tự rút ra từ mutate.
#Muốn biết bình quân Sepal.Length và số dòng ứng với mỗi group Species()
iris %>% group_by(Species) %>%
summarise(mean = mean(Sepal.Length), n = n())
#Khi đó dữ liệu đã được group nên các thống kê sẽ thực hiện cho mỗi group và kết quả trả về ứng với mỗi dòng là 1 group.
#Muốn biết đang group theo biến nào dùng groups()
iris %>% group_by(Species, Petal.Length) %>%
groups()
## [[1]]
## Species
##
## [[2]]
## Petal.Length
#Muốn không group các biến nữa dùng ungroup()
iris %>% group_by(Species, Petal.Length) %>%
ungroup() %>%
groups()
## NULL
#Muốn biết bình quân Sepal.Length và số dòng cho toàn bộ các quan sát
iris %>% summarise(mean = mean(Sepal.Length), n = n())
#Tính mean toàn bộ các trường:
iris[,1:4] %>% summarise_all(mean)
#TÍnh thêm sum nữa:
iris[,1:4] %>% summarise_all(c('mean','sum'))
#Nếu bạn không biết công dụng tuyệt vời của summarise_all trong thống #kê cho toàn bộ các trường bạn sẽ phải dùng lapply 2 lần
iris[,1:4] %>% lapply(mean)
## $Sepal.Length
## [1] 5.843333
##
## $Sepal.Width
## [1] 3.057333
##
## $Petal.Length
## [1] 3.758
##
## $Petal.Width
## [1] 1.199333
iris[,1:4] %>% lapply(sum)
## $Sepal.Length
## [1] 876.5
##
## $Sepal.Width
## [1] 458.6
##
## $Petal.Length
## [1] 563.7
##
## $Petal.Width
## [1] 179.9
#TÍnh mean các trường theo group
iris %>% group_by(Species) %>%
summarise_all(mean)
#Chỉ tính mean cho một số trường thuộc về Sepal
iris %>% summarise_at(vars(matches("Sepal")),mean)
#Tính mean theo group cho các trường thuộc về Sepal
iris %>% group_by(Species) %>%
summarise_at(vars(matches("Sepal")),mean)
#Tính mean các trường là is.numeric
iris %>% summarise_if(is.numeric,mean)
#Về bản chất là kết hợp giữa Filter và summarise_all
iris %>% Filter(is.numeric,.) %>% summarise_all(mean)
#Hoặc muốn chậm hơn thì dùng select_if trong cùng hệ sinh thái.
iris %>% select_if(is.numeric) %>% summarise_all(mean)
#Tính log của toàn bộ các trường
iris[,1:4] %>% mutate_all(log) %>% head()
#Nếu muốn tự định nghĩa các trường không sẵn có thì truyền vào funs
iris[,1:4] %>% mutate_all(funs((.-min(.))/sd(.))) %>% head()
#Hoặc lưu hàm vào Global Environment trước rồi gọi sau:
chuan_hoa_sd <- function(x) {(x-min(x))/sd(x)}
iris[,1:4] %>% mutate_all(chuan_hoa_sd) %>% head()
#Ai không biết đến có thể dùng lapply
#iris[,1:4] %>% lapply(chuan_hoa_sd)
#Hoặc dùng apply để trả về matrix
iris[,1:4] %>% apply(2,chuan_hoa_sd) %>% head()
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## [1,] 0.9661064 3.441422 0.2265906 0.1311927
## [2,] 0.7245798 2.294282 0.2265906 0.1311927
## [3,] 0.4830532 2.753138 0.1699430 0.1311927
## [4,] 0.3622899 2.523710 0.2832383 0.1311927
## [5,] 0.8453431 3.670851 0.2265906 0.1311927
## [6,] 1.3283963 4.359135 0.3965336 0.3935780
#Chuyển trường Sepal.Length sang giá trị mean của nó
iris %>% mutate_at(c("Sepal.Length"),mean) %>% head()
#Hoặc sử dụng thứ tự của trường
iris %>% mutate_at(c(1),mean) %>% head()
#Hoặc sử dụng vars(mathes('fieldName')) nhưng rất hiếm, chủ yếu dùng #trong lập trình nâng cao
iris %>% mutate_at(vars(matches("Sepal")), log) %>% head()
#Những trường thỏa mãn có tên chứa Sepal sẽ được lựa chuyển sang log
iris %>% mutate_at(vars(matches("Sepal")), log) %>% head()
#Định dạng của Species đang là Factor
iris %>% str()
## 'data.frame': 150 obs. of 5 variables:
## $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
## $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
## $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
## $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
## $ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
#Chuyển định dạng của trường Species từ factor sang character
iris %>% mutate_if(is.factor,as.character) %>% str()
## 'data.frame': 150 obs. of 5 variables:
## $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
## $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
## $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
## $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
## $ Species : chr "setosa" "setosa" "setosa" "setosa" ...
#Chuyển sang dạng log đối với các trường numerics
iris %>% mutate_if(is.numeric,log) %>% head()
Giả sử ta có 2 data.frame mẫu về student và province được tạo như bên dưới.
student <- data.frame(STUDENT_ID = c(1:5),
NAME = c("Khanh", "Linh", "Ngoc", "Toan", "Hanh"),
CLASS = c("7A","8B","7A","8B","7E"),
MATH_POINT = c(10,9,8,9,9), PROVINCE_ID = c(1,2,4,2,1))
student
province <- data.frame(PROVINCE_ID = c(1,2,3),
PROVINCE_NAME = c("Ha Noi", "Thanh Hoa","Da Nang"))
province
Đối với bảng student key sẽ là STUDENT_ID và bảng province có key là PROVINCE_ID. Ta sẽ dùng 2 bảng này để tìm hiểu về các lệnh join của dplyr.
Cú pháp: left_join(left_table,right_table, by = “key_name”)
left join: lấy toàn các record gốc ở bảng bên trái join với các record ở bảng bên phải theo điều kiện của key. Nếu thỏa mãn điều kiện bảng sẽ được join, nếu không thỏa mãn điều kiện bảng sẽ để NULL các trường thuộc bảng bên phải.
library(dplyr)
left_join(student, province, by = "PROVINCE_ID")
Cú pháp: right_join(left_table,right_table, by = “key_name”)
right join: tương tự như left join nhưng các record gốc được xét là bảng bên phải và bảng bên trái sẽ để null nếu không thỏa mãn điều kiện join.
library(dplyr)
right_join(student, province, by = "PROVINCE_ID")
Cú pháp: inner_join(left_table,right_table, by = “key_name”)
inner join: sẽ chỉ lấy các record thỏa mãn điều kiện join của hai bảng, nếu không thỏa mãn điều kiện sẽ bị loại bỏ khỏi kết quả.
library(dplyr)
inner_join(student, province, by = "PROVINCE_ID")
Cú pháp: full_join(left_table,right_table, by = “key_name”)
full join: sẽ lấy toàn bộ record của bảng bên trái ghép với toàn bộ record của bảng bên phải. Nếu không thỏa mãn điều kiện join thì bảng bên trái hoặc bên phải dữ liệu sẽ bị null.
library(dplyr)
full_join(student, province, by = "PROVINCE_ID")
Cú pháp: anti_join(left_table,right_table, by = “key_name”)
anti join: lấy toàn bộ record ở bảng bên trái làm gốc join với dữ liệu bảng bên phải đối với dữ liệu không thỏa mãn điều kiện join sẽ được lựa chọn vào kết quả trả về.
#lay nhung record cua student ma province khong co trong bang province
library(dplyr)
anti_join(student, province, by = "PROVINCE_ID")
#lay nhung record cua province ma province khong co trong bang student
anti_join(province,student,by = "PROVINCE_ID")
Hàm anti join là một hàm mà chỉ R mới có. Thậm chí ngôn ngữ truy vấn dữ liệu như SQL cũng không có bất kì một hàm nào thực hiện chức năng này.
Đây là một hàm của base chứ không nằm trong dplyr. Cú pháp của hàm như sau:
merge(student,province, by.x="PROVINCE_ID", by.y = "PROVINCE_ID")
#Trường hợp 2 trường key ở 2 bảng trùng tên có thể viết ngắn gọn:
merge(student,province, by = "PROVINCE_ID")
Dữ liệu được sử dụng trong ví dụ này là ship. Bộ dữ liệu thuộc package MASS tổng hợp thông tin vê số vụ tai nạn vận chuyển theo ngành dịch vụ.
library(MASS)
ships %>% head(10)
Nếu bạn là người mới đối với ngôn ngữ R và chưa thành thạo hết các hàm xứ lý dữ liệu của R thì có thể sử dụng package RODBC để kết nối với SQL và sử dụng câu lệnh như của SQL. Quá trình xử lý gồm 2 bước:
library(RODBC)
dbhandler <- odbcDriverConnect('driver={SQL Server};
server=192.168.91.2;
database=AdventureWorks;
trusted_connection=true')
TransactHistory <- sqlQuery(dbhandler,
"select TransactionDate,Quantity,ActualCost from AdventureWorks.Production.TransactionHistory"
)
TransactHistory %>% head(10)
Ngoài ra nếu không kết nối tới SQL mà chỉ cần truy vấn dữ liệu trong Global Enviroment bằng câu lệnh SQL thì có thể sử dụng package sqldf
library(sqldf)
#Tính trung bình độ rộng tán, chiều dài tán và số lượng hoa ở mỗi loài trong bộ dữ liệu iris
sqldf("select Species,
AVG([Sepal.Length]) AvgSepalLength,
AVG([Sepal.Width]) AvgSepalWidth,
COUNT(*) n
from iris group by Species"
)
## Note: no visible binding for global variable 'ref'
#Sử dụng dplyr thì đơn giản như sau:
iris %>% group_by(Species) %>%
summarise(AvgSepalLength = mean(Sepal.Length),
AvgSepalWidth = mean(Sepal.Width),
n = n())
library(reshape)
melt.ships <- melt(ships,id = c("type", "year"))
melt.ships
Khi đó tên các trường không phải key được sử dụng để xác định label cho record trong trường variable. Trường value là giá trị ở bảng gốc ứng với tên trường và nhóm key.
cast(melt.ships,type+year~variable, sum)
Kết quả cho thấy các nhóm thuộc cột variable đã được trải ra thành các trường ở data.frame mới dựa trên công thức sum.
Một ví dụ khác cho thấy tiện ích của hàm cast() so với SQL.
Chúng ta có bảng AdventureWorks.Production.TransactionHistory là bảng lịch sử giao dịch mua hàng của khác hàng đã được ví dụ trong phần SQL.
TransactHistory %>% str()
## 'data.frame': 113443 obs. of 3 variables:
## $ TransactionDate: POSIXct, format: "2007-09-01" "2007-09-01" ...
## $ Quantity : int 2 1 1 1 1 1 1 1 1 1 ...
## $ ActualCost : num 0 0 0 0 0 0 0 0 0 0 ...
Chẳng hạn chúng ta cần biết tổng chi phí của từng nhóm số lượng Quantity theo ngày giao dịch trong đó các nhóm Quantity này được trải theo dạng cột. Trên SQL chúng ta sẽ phải sử dụng hàm pivot khá phức tạp:
SummaryTransCost <- sqlQuery(dbhandler,
"SELECT TransactionDate, [1],[2],[3],[4],[5] FROM
(SELECT TransactionDate, Quantity, ActualCost FROM AdventureWorks.Production.TransactionHistory) A
PIVOT (
SUM(ActualCost)
FOR Quantity IN ([1],[2],[3],[4],[5])
) as PivotTable;
"
)
SummaryTransCost %>% head(10)
Tuy nhiên sử dụng hàm cast() trên R đơn giản hơn rất nhiều:
cast(TransactHistory,TransactionDate ~ Quantity,sum) %>% head(10)
Điều đó cho thấy các xử lý dữ liệu trên R rất phong phú và hoàn toàn có thể đáp ứng được các yêu cầu xử lý chuyên sâu. Tuy nhiên hạn chế là tốc độ xử lý của R so với SQL thì đang kém gấp vài nghìn lần.