TÌM HIỂU VỀ DPLYR

DPLYR là một thư viện nằm trong quần thể eco-system tidyverse, có chức năng thao tác và biến đổi dữ liệu một cách trực quan. Package dplyr là thành phần rất quan trọng trong hệ sinh thái tidyverse, vì nó cung cấp những function hoán chuyển và thao tác trên dữ liệu sau khi nó đã được tải vào R. Package này được phát hành lần đầu tiên vào ngày 17 tháng 1 năm 2014, và nhanh chóng tạo ra phong trào thay thế baseR bằng dplyr trong cộng đồng. Phiên bản mới nhất hiện nay là dplyr 0.7.4 (phát hành ngày 29/9/2017).

dplyr được viết bởi Hadley Wickham và đem lại cho chúng ta những hàm hữu ích, dễ sử dụng khi thao tác xử lý dữ liệu. Một trong những khía cạnh độc đáo của dplyr đó là với cùng một tập các tools, chúng ta có thể thao tác với nhiều nguồn dữ liệu khác, bao gồm data frames, data tables, databases và multidimensional arrays.

Để tìm hiểu về các hàm xử lý dữ liệu cơ bản, trong nội dung này tôi chỉ đề cập đến một vài bộ dữ liệu cơ bản, bộ dữ liệu này chứa những thông tin về chất lượng không khí từ tháng 5 năm 19734 tháng 9 năm 1973 tại New York.

CÁC HÀM TRONG DPLYR

Cài thư viện dplyr

library(dplyr)
data("airquality")
head(airquality)
##   Ozone Solar.R Wind Temp Month Day
## 1    41     190  7.4   67     5   1
## 2    36     118  8.0   72     5   2
## 3    12     149 12.6   74     5   3
## 4    18     313 11.5   62     5   4
## 5    NA      NA 14.3   56     5   5
## 6    28      NA 14.9   66     5   6

Hàm select

Trong khi filter() đặt con một khung dữ liệu theo hàng, select() trả về một tập con gồm các cột.

Hàm này có thể lấy tên cột (ngay cả khi không có dấu ngoặc kép) hoặc số vị trí cột bắt đầu từ bên trái. Hơn nữa, không giống như thư viện base trong R, các lệnh trong dấu ngoặc trong select() không cần phải được nối bằng c().

select(airquality, Ozone, Temp) %>% head(4)
##   Ozone Temp
## 1    41   67
## 2    36   72
## 3    12   74
## 4    18   62
airquality %>% select(-Ozone, -Temp) %>% head(4)
##   Solar.R Wind Month Day
## 1     190  7.4     5   1
## 2     118  8.0     5   2
## 3     149 12.6     5   3
## 4     313 11.5     5   4

Hàm filter

Hàm này có tác dụng lọc dữ liệu, kết quả của nó trả về các dữ liệu thỏa mãn một điều kiện nào đó, thông thường hàm filter sẽ lọc dữ liệu theo dòng (lọc theo quan sát- filter by observations)

Ví dụ: Chúng ta muốn lọc các kết quả có chất lượng \(Ozone > 106\); lọc các kết quả có chất lượng \(Ozone > 106\) và rơi vào tháng 8

filter(airquality, Ozone > 106) %>% head(5)
##   Ozone Solar.R Wind Temp Month Day
## 1   115     223  5.7   79     5  30
## 2   135     269  4.1   84     7   1
## 3   108     223  8.0   85     7  25
## 4   122     255  4.0   89     8   7
## 5   110     207  8.0   90     8   9
filter(airquality, Ozone > 106 & Month == 8) %>% head(5)
##   Ozone Solar.R Wind Temp Month Day
## 1   122     255  4.0   89     8   7
## 2   110     207  8.0   90     8   9
## 3   168     238  3.4   81     8  25
## 4   118     225  2.3   94     8  29
filter(airquality, Ozone > 106, Month == 8, Wind > 7) %>% head(5)
##   Ozone Solar.R Wind Temp Month Day
## 1   110     207    8   90     8   9

Hàm mutate

Hàm mutate có tác dụng tạo thêm một cột biến mới thỏa mãn một điều kiện nào đó hoặc đơn thuần là tạo ra cột biến mới dựa theo một công thức tính toán nào đó. Ví dụ: Chúng ta muốn thêm một cột biến mới, hiển thị nhiệt độ theo độ C trong bộ dữ liệu airquality

mutate(airquality, temp.celsius = Temp - 31) %>% head(5)
##   Ozone Solar.R Wind Temp Month Day temp.celsius
## 1    41     190  7.4   67     5   1           36
## 2    36     118  8.0   72     5   2           41
## 3    12     149 12.6   74     5   3           43
## 4    18     313 11.5   62     5   4           31
## 5    NA      NA 14.3   56     5   5           25

Hàm summarize và group_by

Hàm summarize giúp khái quát thống kê, đơn thuần là tổng kết nhiều giá trị nào đó trở thành một giá trị. Hàm này thông thường kết hợp với hàm sắp xếp theo nhóm, tạo ra sức mạnh thống kê khá tuyệt vời. Ví dụ: Chúng ta muốn nhóm các dữ liệu theo các tháng thành các nhóm, sau đó tính trung bình nhiệt độ theo các tháng để so sánh

summarize(group_by(airquality, Month), round(mean(Temp, na.rm = T)))
## # A tibble: 5 x 2
##   Month `round(mean(Temp, na.rm = T))`
##   <int>                          <dbl>
## 1     5                             66
## 2     6                             79
## 3     7                             84
## 4     8                             84
## 5     9                             77

Hàm sample

Hàm này có tác dụng lấy ngẫu nhiên từ bộ dữ liệu ra bao nhiêu quan sát, có thể lấy theo số lượng hoặc lấy theo \(\%\). Ví dụ:

sample_n(airquality, 4) # Lấy ngẫu nhiên 4
##   Ozone Solar.R Wind Temp Month Day
## 1    NA      NA 14.3   56     5   5
## 2     1       8  9.7   59     5  21
## 3     7      NA  6.9   74     5  11
## 4    97     272  5.7   92     7   9
sample_frac(airquality, size = 0.02) # Lấy ngẫu nhiên 2%
##   Ozone Solar.R Wind Temp Month Day
## 1    34     307 12.0   66     5  17
## 2    61     285  6.3   84     7  18
## 3    23     299  8.6   65     5   7

Hàm count

Hàm count có tác dụng thống kê số lượng. Chức năng của hàm này hoàn toàn giống như tác dụng của hàm table() khi chúng ta cần thống kê số lượng cho các biến phân loại. Ví dụ: Muốn đếm số lượng Nam hoặc Nữ cho một tập dữ liệu gồm 100 quan sát chẳng hạn, với ví dụ airquality:

count(airquality, Month > 5) # Đếm xem có bao nhiêu quan sát có tháng lớn hơn 5 và nhỏ hơn
##   Month > 5   n
## 1     FALSE  31
## 2      TRUE 122
count(airquality, Month)  # Thống kê theo tháng
##   Month  n
## 1     5 31
## 2     6 30
## 3     7 31
## 4     8 31
## 5     9 30

Hàm arrange

Hàm arrange() được sử dụng để sắp xếp các hàng theo các biến. Hiện tại, tập dữ liệu airquality được sắp xếp dựa trên Tháng và sau đó là Ngày. Chúng ta có thể sử dụng chức năng sắp xếp để sắp xếp các hàng theo thứ tự giảm dần của Tháng, và sau đó theo thứ tự tăng dần của Ngày.

arrange(airquality, Month, Day) %>% head(5) # mặc định là ascending- tăng dần
##   Ozone Solar.R Wind Temp Month Day
## 1    41     190  7.4   67     5   1
## 2    36     118  8.0   72     5   2
## 3    12     149 12.6   74     5   3
## 4    18     313 11.5   62     5   4
## 5    NA      NA 14.3   56     5   5
arrange(airquality, desc(Month), Day) %>% head(5) # để sắp giảm dần, dùng desc (tức descending)
##   Ozone Solar.R Wind Temp Month Day
## 1    96     167  6.9   91     9   1
## 2    78     197  5.1   92     9   2
## 3    73     183  2.8   93     9   3
## 4    91     189  4.6   93     9   4
## 5    47      95  7.4   87     9   5

Hàm contains()

contains() là một hàm trợ giúp được sử dụng với select(), tương tự với hàm tìm chuỗi str_detect() được sử dụng với filter(), contains() cũng hữu ích để chọn tất cả các tên cột có một ký tự nhất định.

Ví dụ để chỉ chọn các cột có tên chứa chữ cái \(y\):

mpg_df %>% select(contains('y')) %>% head(4)
## # A tibble: 4 x 4
##    year   cyl   cty   hwy
##   <int> <int> <int> <int>
## 1  1999     4    18    29
## 2  1999     4    21    29
## 3  2008     4    20    31
## 4  2008     4    21    30

Hàm starts_with()

start_with() và end_with() cung cấp tính năng lựa chọn cụ thể hơn so với select(). Nếu chúng ta muốn tất cả các cột bắt đầu bằng chữ cái \(c\):

mpg_df %>% select(starts_with('c')) %>% head(4)
## # A tibble: 4 x 3
##     cyl   cty class  
##   <int> <int> <chr>  
## 1     4    18 compact
## 2     4    21 compact
## 3     4    20 compact
## 4     4    21 compact
mpg_df %>% select( 2, 1, class, contains('y')) %>% head(4)
## # A tibble: 4 x 7
##   model manufacturer class    year   cyl   cty   hwy
##   <chr> <chr>        <chr>   <int> <int> <int> <int>
## 1 a4    audi         compact  1999     4    18    29
## 2 a4    audi         compact  1999     4    21    29
## 3 a4    audi         compact  2008     4    20    31
## 4 a4    audi         compact  2008     4    21    30

Hàm everything()

Một hàm trợ giúp rất hữu ích là hàm everything(), trả về tất cả các tên cột chưa được chỉ định. Nó thường được sử dụng khi sắp xếp lại thứ tự tất cả các cột trong khung dữ liệu:

mpg_df %>% select(class,displ,year,everything()) %>% head(4)
## # A tibble: 4 x 11
##   class   displ  year manufacturer model   cyl trans     drv     cty   hwy fl   
##   <chr>   <dbl> <int> <chr>        <chr> <int> <chr>     <chr> <int> <int> <chr>
## 1 compact   1.8  1999 audi         a4        4 auto(l5)  f        18    29 p    
## 2 compact   1.8  1999 audi         a4        4 manual(m~ f        21    29 p    
## 3 compact   2    2008 audi         a4        4 manual(m~ f        20    31 p    
## 4 compact   2    2008 audi         a4        4 auto(av)  f        21    30 p

Toán tử pipe

Giới thiệu về toán tử pipe (%>%)

Trong lập trình, pipe là 1 khái niệm thường được dùng để mô tả việc sử dụng đầu ra của 1 hàm xử lý này để sử dụng như đầu vào của 1 hàm xử lý khác. Khi khối xử lý kéo dài thành 1 chuỗi liên tiếp nhau thì còn được gọi là chaining và có lẽ việc gắn kết liên tục thành luồng đó gợi đến hình tượng của ống nước nên người ta gọi hành vi nối đó là nối ống piping.

Toán tử pipe không chỉ thay đổi về cách thức viết R code, làm cho R code trong sáng và được cấu trúc tốt hơn mà còn thay đổi cách tư duy của người dùng R.

Toán tử Forward Pipe

Đây là dạng pipe thông dụng nhất. Công dụng của Forward Pipe là chuyển toàn bộ kết quả của hàm đi trước (bên trái) thành dữ liệu đầu vào của hàm đi sau (bên phải) trong một chuỗi quy trình. Kết quả sau cùng là của hàm cuối cùng (bên phải) trong chuỗi. Như vậy toán tử Forward Pipe có tính chất 1 chiều và định hướng từ trái sang phải. Có thể hình dung về một dòng chảy của data qua một đường ống (pipe).

# Forward pipe
rnorm(100,20,5)%>%
  matrix(ncol=2)%>%
  data.frame(x=.[,1],y=.[,2])%>%
  plot(col="red")

Là một chuỗi quy trình :

  1. Tạo ngẫu nhiên 100 số theo phân phối chuẩn, trung bình = 20, sd=5 ;

  2. Chia đều vector này thành 1 matrix có 2 cột,

  3. Chuyển matrix này thành 1 dataframe, và gán giá trị 2 cột 1, 2 của matrix cho 2 biến x,y.

  4. Cuối cùng, vẽ scatterdot matrix giữa X1,X2,x,y bằng hàm plot().

Cách trình bày này rõ ràng và dễ hiểu, tránh việc tạo object trung gian và lồng ghép các hàm nếu như ta không dùng pipe.

Toán tử T pipe

Toán tử T pipe có thể được hình dung như 1 ống nước hình chữ T, khiến cho dữ liệu đầu vào của 1 hàm A đi trước sẽ được truyền cho 2 nhánh tương ứng với quy trình B1 (là 1 hàm) và quy trình B2. Một ứng dụng phổ biến nhất của T pipe là để vẽ 2 đồ thị khác nhau cho cùng 1 gói dữ liệu, như thí dụ sau :

# T pipe
rnorm(100,10,1)%T>%
  ts.plot(col="red")%>%
  density()%>%
  plot(col="blue","densityplot")

Toán tử Assigning pipe

Toán tử Assigning pipe cho phép trích xuất đích danh một đối tượng trong kết quả của hàm đi trước để sử dụng như dữ liệu đầu vào cho hàm đi sau. Như vậy nó có tính định hướng 1 chiều nhưng chuyên biệt, để sử dụng toán tử này được, cần sử dụng thư viện magrittr.

library(magrittr)
data.frame(x=rnorm(100,10,5),
           y=rnorm(100,20,5)) %$% ts.plot(x,col="red")

data.frame(x=rnorm(100,10,5),
           y=rnorm(100,20,5)) %$% ts.plot(y,col="blue")

Toán tử Backward pipe

Công dụng của backward pipe trái chiều với forward pipe, tức là kết quả cuối cùng sau mọi quy trình bên phải trong chuỗi sẽ được truyền ngược về object đầu tiên nằm ngoài cùng bên trái của chuỗi.

Để minh họa, ta có thí dụ sau đây:

X là 1 vector chứa 100 giá trị ngẫu nhiên có phân phối chuẩn Y cũng thế (chính là X). Ta dùng backward pipe để thực hiện lần lượt 2 phép toán : bình phương X, sau đó khai căn bậc 2. Dễ thấy kết quả sau cùng chính là giá trị ban đầu. Có thể kiểm tra bằng cách so sánh x, y, chúng hoàn toàn như nhau

library(magrittr)
x=abs(rnorm(100))
y=x
cbind(x,y)%>%head()
##              x         y
## [1,] 1.2824727 1.2824727
## [2,] 0.3962973 0.3962973
## [3,] 1.1776062 1.1776062
## [4,] 0.2534644 0.2534644
## [5,] 1.8974508 1.8974508
## [6,] 0.2274406 0.2274406
x %<>% .^2 %>% sqrt()
x==y
##   [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [16] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [31] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [46] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [61] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [76] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [91] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE

THỰC HÀNH VỚI DỮ LIỆU MPG

library(tidyverse)
library(dplyr)
mpg_df <- mpg 
# filter the mpg_df data for cars manufactured in the year 1999
filter(mpg_df, year == 1999) %>% head(5)
## # A tibble: 5 x 11
##   manufacturer model      displ  year   cyl trans  drv     cty   hwy fl    class
##   <chr>        <chr>      <dbl> <int> <int> <chr>  <chr> <int> <int> <chr> <chr>
## 1 audi         a4           1.8  1999     4 auto(~ f        18    29 p     comp~
## 2 audi         a4           1.8  1999     4 manua~ f        21    29 p     comp~
## 3 audi         a4           2.8  1999     6 auto(~ f        16    26 p     comp~
## 4 audi         a4           2.8  1999     6 manua~ f        18    26 p     comp~
## 5 audi         a4 quattro   1.8  1999     4 manua~ 4        18    26 p     comp~
# Show the dimension
filter(mpg_df, year == 1999) %>%  dim()
## [1] 117  11
# let’s take all vehicles in the ‘midsize’ class:
filter(mpg_df, class == "midsize") %>% head(5)
## # A tibble: 5 x 11
##   manufacturer model      displ  year   cyl trans  drv     cty   hwy fl    class
##   <chr>        <chr>      <dbl> <int> <int> <chr>  <chr> <int> <int> <chr> <chr>
## 1 audi         a6 quattro   2.8  1999     6 auto(~ 4        15    24 p     mids~
## 2 audi         a6 quattro   3.1  2008     6 auto(~ 4        17    25 p     mids~
## 3 audi         a6 quattro   4.2  2008     8 auto(~ 4        16    23 p     mids~
## 4 chevrolet    malibu       2.4  1999     4 auto(~ f        19    27 r     mids~
## 5 chevrolet    malibu       2.4  2008     4 auto(~ f        22    30 r     mids~
# filter for vehicles built in 1999 and with mileage in the city (cty) greater than 18.
mpg_df %>% filter(year==1999 & cty > 18) %>% head(4)
## # A tibble: 4 x 11
##   manufacturer model  displ  year   cyl trans      drv     cty   hwy fl    class
##   <chr>        <chr>  <dbl> <int> <int> <chr>      <chr> <int> <int> <chr> <chr>
## 1 audi         a4       1.8  1999     4 manual(m5) f        21    29 p     comp~
## 2 chevrolet    malibu   2.4  1999     4 auto(l4)   f        19    27 r     mids~
## 3 honda        civic    1.6  1999     4 manual(m5) f        28    33 r     subc~
## 4 honda        civic    1.6  1999     4 auto(l4)   f        24    32 r     subc~
# filter for vehicles (i.e., rows) where the manufacturer is Chevrolet or the class is ‘suv’.
mpg_df %>% filter(manufacturer=='chevrolet' | class=='suv') %>% head(4)
## # A tibble: 4 x 11
##   manufacturer model      displ  year   cyl trans  drv     cty   hwy fl    class
##   <chr>        <chr>      <dbl> <int> <int> <chr>  <chr> <int> <int> <chr> <chr>
## 1 chevrolet    c1500 sub~   5.3  2008     8 auto(~ r        14    20 r     suv  
## 2 chevrolet    c1500 sub~   5.3  2008     8 auto(~ r        11    15 e     suv  
## 3 chevrolet    c1500 sub~   5.3  2008     8 auto(~ r        14    20 r     suv  
## 4 chevrolet    c1500 sub~   5.7  1999     8 auto(~ r        13    17 r     suv
mpg_df %>% filter( (manufacturer=='chevrolet' | class=='suv') & hwy < 20) %>% head(4)
## # A tibble: 4 x 11
##   manufacturer model      displ  year   cyl trans  drv     cty   hwy fl    class
##   <chr>        <chr>      <dbl> <int> <int> <chr>  <chr> <int> <int> <chr> <chr>
## 1 chevrolet    c1500 sub~   5.3  2008     8 auto(~ r        11    15 e     suv  
## 2 chevrolet    c1500 sub~   5.7  1999     8 auto(~ r        13    17 r     suv  
## 3 chevrolet    c1500 sub~   6    2008     8 auto(~ r        12    17 r     suv  
## 4 chevrolet    k1500 tah~   5.3  2008     8 auto(~ 4        14    19 r     suv

Sử dụng str_detect() và %in

Thông thường, chúng ta muốn nắm bắt các hàng có chứa một chuỗi các chữ cái cụ thể. Ví dụ: có 10 kiểu xe khác nhau có chứa các chữ cái \('4wd'\). Một cách tốt hơn nhiều là ‘phát hiện’ các chữ cái \('4wd'\) trong cột và trả về tất cả các hàng mà chúng có mặt, bằng cách sử dụng str_detect ().

Khi chúng ta quan tâm đến một tập hợp con các hàng có thể chứa một số giá trị khác nhau, thay vì viết một lệnh OR dài, sẽ hữu ích khi chỉ đưa ra một vectơ có giá trị quan tâm.

Ví dụ: để lấy tập hợp con các phương tiện trong mpg_df có 4, 5 hoặc 6 xi lanh, chúng ta có thể chỉ định trong tập hợp (4,5,6)

library(tidyverse)
mpg_df %>% filter(str_detect(model,'4wd')) %>% head(5)
## # A tibble: 5 x 11
##   manufacturer model     displ  year   cyl trans   drv     cty   hwy fl    class
##   <chr>        <chr>     <dbl> <int> <int> <chr>   <chr> <int> <int> <chr> <chr>
## 1 chevrolet    k1500 ta~   5.3  2008     8 auto(l~ 4        14    19 r     suv  
## 2 chevrolet    k1500 ta~   5.3  2008     8 auto(l~ 4        11    14 e     suv  
## 3 chevrolet    k1500 ta~   5.7  1999     8 auto(l~ 4        11    15 r     suv  
## 4 chevrolet    k1500 ta~   6.5  1999     8 auto(l~ 4        14    17 d     suv  
## 5 dodge        dakota p~   3.7  2008     6 manual~ 4        15    19 r     pick~
mpg_df %>% filter(cyl %in% c(4,5,6)) %>% head(5)
## # A tibble: 5 x 11
##   manufacturer model displ  year   cyl trans      drv     cty   hwy fl    class 
##   <chr>        <chr> <dbl> <int> <int> <chr>      <chr> <int> <int> <chr> <chr> 
## 1 audi         a4      1.8  1999     4 auto(l5)   f        18    29 p     compa~
## 2 audi         a4      1.8  1999     4 manual(m5) f        21    29 p     compa~
## 3 audi         a4      2    2008     4 manual(m6) f        20    31 p     compa~
## 4 audi         a4      2    2008     4 auto(av)   f        21    30 p     compa~
## 5 audi         a4      2.8  1999     6 auto(l5)   f        16    26 p     compa~

Phát hiện dữ liệu trống

Nếu có giá trị \(NA\) (bị thiếu) trong một cột cụ thể, chúng ta có thể kiểm tra hoặc loại bỏ chúng bằng cách sử dụng trình trợ giúp is.na ().

Để kiểm tra sự hiện diện của các giá trị \(NA\) trong cột year, ví dụ:

mpg %>% filter(is.na(year))
## # A tibble: 0 x 11
## # ... with 11 variables: manufacturer <chr>, model <chr>, displ <dbl>,
## #   year <int>, cyl <int>, trans <chr>, drv <chr>, cty <int>, hwy <int>,
## #   fl <chr>, class <chr>
# To drop out all the missing value
mpg %>% filter(!is.na(year)) %>% head(4)
## # A tibble: 4 x 11
##   manufacturer model displ  year   cyl trans      drv     cty   hwy fl    class 
##   <chr>        <chr> <dbl> <int> <int> <chr>      <chr> <int> <int> <chr> <chr> 
## 1 audi         a4      1.8  1999     4 auto(l5)   f        18    29 p     compa~
## 2 audi         a4      1.8  1999     4 manual(m5) f        21    29 p     compa~
## 3 audi         a4      2    2008     4 manual(m6) f        20    31 p     compa~
## 4 audi         a4      2    2008     4 auto(av)   f        21    30 p     compa~

Kiểm tra giá trị bằng complete.cases(.)

Để chỉ lọc các hàng không có giá trị bị thiếu:

mpg %>% filter(complete.cases(.)) %>% head(4)
## # A tibble: 4 x 11
##   manufacturer model displ  year   cyl trans      drv     cty   hwy fl    class 
##   <chr>        <chr> <dbl> <int> <int> <chr>      <chr> <int> <int> <chr> <chr> 
## 1 audi         a4      1.8  1999     4 auto(l5)   f        18    29 p     compa~
## 2 audi         a4      1.8  1999     4 manual(m5) f        21    29 p     compa~
## 3 audi         a4      2    2008     4 manual(m6) f        20    31 p     compa~
## 4 audi         a4      2    2008     4 auto(av)   f        21    30 p     compa~
# And to filter for all rows with a missing value in at least one column:
mpg %>% filter( !complete.cases(.) )
## # A tibble: 0 x 11
## # ... with 11 variables: manufacturer <chr>, model <chr>, displ <dbl>,
## #   year <int>, cyl <int>, trans <chr>, drv <chr>, cty <int>, hwy <int>,
## #   fl <chr>, class <chr>

Chaining dplyr and ggplot

mpg_df %>% 
  filter(class=='midsize') %>% 
  select(class,manufacturer,displ,year) %>% 
  arrange(displ) %>% 
  ggplot(aes(x=class,y=displ)) + geom_boxplot()

DỮ LIỆU DIAMOND

Trong nội dung phần này, chúng ta sẽ tổng hợp thêm một số hàm khác và các cách phối hợp các hàm trong thư viện dplyr để thao tác với dữ liệu

Tạo ra một cột biến mới, với giá trị là đô la Úc, biết rằng \(AUD=USD\times 1.25\)

library(ggplot2)
diamonds_df <- diamonds
diamonds_df %>% select(-x, -y, -z) %>% mutate(AUD = price*1.25) %>% head(4)
## # A tibble: 4 x 8
##   carat cut     color clarity depth table price   AUD
##   <dbl> <ord>   <ord> <ord>   <dbl> <dbl> <int> <dbl>
## 1  0.23 Ideal   E     SI2      61.5    55   326  408.
## 2  0.21 Premium E     SI1      59.8    61   326  408.
## 3  0.23 Good    E     VS1      56.9    65   327  409.
## 4  0.29 Premium I     VS2      62.4    58   334  418.

Chúng ta có thể thêm một cột biến nữa là giá 1 carat bằng cách lấy cột price chia cho cột carat

diamonds_df %>% select(-x, -y , -z) %>% mutate(ppc = price/carat) %>% head(4)
## # A tibble: 4 x 8
##   carat cut     color clarity depth table price   ppc
##   <dbl> <ord>   <ord> <ord>   <dbl> <dbl> <int> <dbl>
## 1  0.23 Ideal   E     SI2      61.5    55   326 1417.
## 2  0.21 Premium E     SI1      59.8    61   326 1552.
## 3  0.23 Good    E     VS1      56.9    65   327 1422.
## 4  0.29 Premium I     VS2      62.4    58   334 1152.

Thử thách: Biết rằng 1 carat nặng 0.2 grams, hãy sử dụng hàm mutate() để thêm 2 cột biến, một cột biến theo giá AUD và một cột biến tính giá trị của 1 gram kim cương theo AUD (Australian Dollars).

Việc kết hợp hàm \(ifelse()\) và hàm mutate() có thể được thực hiện để tạo ra nhiều biến số một cách dễ dàng. Ví dụ: tạo ra một biến số là price-label với điều kiện nếu giá \(price>5000\) thì kim cương là loại đắt (expensive), ngược lại là loại rẻ (cheap)

diamonds_df %>% select(-x, -y, -z) %>% mutate(price_label = ifelse(price > 5000, "expensive", "cheap")) %>% head(10)
## # A tibble: 10 x 8
##    carat cut       color clarity depth table price price_label
##    <dbl> <ord>     <ord> <ord>   <dbl> <dbl> <int> <chr>      
##  1  0.23 Ideal     E     SI2      61.5    55   326 cheap      
##  2  0.21 Premium   E     SI1      59.8    61   326 cheap      
##  3  0.23 Good      E     VS1      56.9    65   327 cheap      
##  4  0.29 Premium   I     VS2      62.4    58   334 cheap      
##  5  0.31 Good      J     SI2      63.3    58   335 cheap      
##  6  0.24 Very Good J     VVS2     62.8    57   336 cheap      
##  7  0.24 Very Good I     VVS1     62.3    57   336 cheap      
##  8  0.26 Very Good H     SI1      61.9    55   337 cheap      
##  9  0.22 Fair      E     VS2      65.1    61   337 cheap      
## 10  0.23 Very Good H     VS1      59.4    61   338 cheap
# Make the plot 
diamonds_df %>% select(-x,-y,-z) %>% 
  mutate(price_label = ifelse(price > 5000,'expensive','cheap')) %>% 
  ggplot(aes(x=price, fill = price_label)) + 
  geom_histogram()
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Lệnh case_when()

Lệnh case_when() được sử dụng khi chúng ta có nhiều điều kiện ràng buộc, câu lệnh này được coi là thay thế trong trường hợp phải dùng quá nhiều ifelse(). Giả dụ về độ tinh khiết (clarity) của kim cương, chúng ta phát biểu vài giả định và quy ước như sau:

  • IF: internally flawless (Hoàn toàn tinh khiết)

  • VVS1 and 2: very very slight impurity 1 and 2 (Có tạp chất rất rất nhỏ)

  • VS1 and 2: very slight impurity 1 and 2 (Có lượng nhỏ tạp chất)

  • SI1 and 2: slight impurity 1 and 2 (Tạp chất ít)

  • I1: impurity (Tạp chất, mờ đục)

diamonds_df %>% 
  select(clarity) %>% 
  mutate(clarity_group = case_when(clarity == 'IF' ~ 'flawless',
                                   str_detect(clarity, 'VVS') ~ 'VV_slight',
                                   str_detect(clarity, 'VS') ~ 'V_slight',
                                   str_detect(clarity, 'SI') ~ 'slight',
                                   clarity == 'I1' ~ 'impurity',
                                   TRUE ~ 'other')) %>% head(10)
## # A tibble: 10 x 2
##    clarity clarity_group
##    <ord>   <chr>        
##  1 SI2     slight       
##  2 SI1     slight       
##  3 VS1     V_slight     
##  4 VS2     V_slight     
##  5 SI2     slight       
##  6 VVS2    VV_slight    
##  7 VVS1    VV_slight    
##  8 SI1     slight       
##  9 VS2     V_slight     
## 10 VS1     V_slight

Toán tử n() và summarize()

Trong trường hợp chúng ta muốn thực hiện thống kê nhóm hoặc thống kê theo các hàm với các cột, sử dụng summarize(), giống như ở lúc đầu đã đề cập đến. Ở đây tôi đề cập thêm đến toán tử n() để đếm số dòng hay số quan sát cho các cột. Tuy nhiên cần nói lại rằng hàm summarize() thực sự phát huy sức mạnh khi kết hợp cùng với hàm thống kê theo nhóm group_by().

diamonds_df %>% summarize(mean_price = mean(price),
                         sd_price = sd(price),
                         min_price = min(price),
                         max_price = max(price),
                         n_rows = n())
## # A tibble: 1 x 5
##   mean_price sd_price min_price max_price n_rows
##        <dbl>    <dbl>     <int>     <int>  <int>
## 1      3933.    3989.       326     18823  53940

Hàm summarize và group_by khi kết hợp.

diamonds_df %>% group_by(clarity) %>% 
  summarize(mean_price = mean(price),
            sd_price = sd(price),
            min_price = min(price),
            max_price = max(price),
            n_rows = n()) %>% head(10)
## # A tibble: 8 x 6
##   clarity mean_price sd_price min_price max_price n_rows
##   <ord>        <dbl>    <dbl>     <int>     <int>  <int>
## 1 I1           3924.    2807.       345     18531    741
## 2 SI2          5063.    4260.       326     18804   9194
## 3 SI1          3996.    3799.       326     18818  13065
## 4 VS2          3925.    4042.       334     18823  12258
## 5 VS1          3839.    4012.       327     18795   8171
## 6 VVS2         3284.    3822.       336     18768   5066
## 7 VVS1         2523.    3335.       336     18777   3655
## 8 IF           2865.    3920.       369     18806   1790
diamonds_df %>% 
  group_by(clarity, cut) %>% 
  summarize(mean_price = mean(price),
            sd_price = sd(price),
            min_price = min(price),
            max_price = max(price),
            n_rows = n()) %>% head(10)
## `summarise()` has grouped output by 'clarity'. You can override using the `.groups` argument.
## # A tibble: 10 x 7
## # Groups:   clarity [2]
##    clarity cut       mean_price sd_price min_price max_price n_rows
##    <ord>   <ord>          <dbl>    <dbl>     <int>     <int>  <int>
##  1 I1      Fair           3704.    3099.       584     18531    210
##  2 I1      Good           3597.    2285.       361     11548     96
##  3 I1      Very Good      4078.    2720.       511     15984     84
##  4 I1      Premium        3947.    2827.       345     16193    205
##  5 I1      Ideal          4336.    2671.       413     16538    146
##  6 SI2     Fair           5174.    3928.       536     18308    466
##  7 SI2     Good           4580.    3901.       335     18788   1081
##  8 SI2     Very Good      4989.    4126.       383     18692   2100
##  9 SI2     Premium        5546.    4488.       345     18784   2949
## 10 SI2     Ideal          4756.    4252.       326     18804   2598