Lấy và làm sạch dữ liệu với R: Thao tác dữ liệu với dplyr

ETL

ETL


Ở bài viết này, chúng ta sẽ thao tác trên dữ liệu với dplyr. dplyr là một package mạnh mẽ của R được viết bởi Hadley Wickham và Romain Francois cho phép chúng ta làm việc với dữ liệu dạng bảng (tabular). 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. Trong bài viết này, chúng ta chỉ tập trung thao tác trên data frames. Tuy nhiên, bạn vẫn có thể áp dụng cho các dạng format khác.

Như chúng ta đã biết, “CRAN là một mạng ftp gồm các web server được đặt ở khắp nơi trên thế giới, đồng nhất với nhau về phiên bản, source code và document về R (http://cran.rstudio.com/). RStudio duy trì một phiên bản như vậy gọi là “CRAN mirrors”, họ lưu lại file logs tất cả việc download các package của R (http://cran-logs.rstudio.com/). Chúng ta sẽ làm việc với file log từ ngày 8 tháng bảy, 2014 (chứa thông tin của gần 225,000 package được download). Các bạn có thể download ở link này:

http://cran-logs.rstudio.com/2014/2014-07-08.csv.gz

Giả sử path2csv là đường dẫn đến file bạn vừa download ở trên (đã được giải nén thành file .csv). Ta sử dụng hàm read.csv() để load dữ liệu như sau:

mydf <- read.csv(path2csv, stringsAsFactors = FALSE)

Như thường lệ, chúng ta sẽ thử quan sát tổng thể dữ liệu mình chuẩn bị làm việc:

# Quan sát số chiều của dữ liệu
dim(mydf)
[1] 225468     11

# Quan sát 6 dòng đầu tiên của dữ liệu
head(mydf)
  X       date     time   size r_version r_arch      r_os      package version
1 1 2014-07-08 00:54:41  80589     3.1.0 x86_64   mingw32    htmltools   0.2.4
2 2 2014-07-08 00:59:53 321767     3.1.0 x86_64   mingw32      tseries 0.10-32
3 3 2014-07-08 00:47:13 748063     3.1.0 x86_64 linux-gnu        party  1.0-15
4 4 2014-07-08 00:48:05 606104     3.1.0 x86_64 linux-gnu        Hmisc  3.14-4
5 5 2014-07-08 00:46:50  79825     3.0.2 x86_64 linux-gnu       digest   0.6.4
6 6 2014-07-08 00:48:04  77681     3.1.0 x86_64 linux-gnu randomForest   4.6-7
  country ip_id
1      US     1
2      US     2
3      US     3
4      US     3
5      CA     4
6      US     3

Để làm việc với dplyr, chúng ta cần load thư viện này lên:

library(dplyr)
# Kiểm tra phiên bản dplyr
packageVersion("dplyr")
[1] ‘0.4.2’

Bước đầu tiên để làm việc với dữ liệu trong dplyr, ta cần chuyển dữ liệu sang ‘data frame tbl’ như sau:

cran <- tbl_df(mydf)
# Ta loại bỏ mydf để tránh gây nhầm lẫn trong quá trình thao tác
rm("mydf")

Khi in tập dữ liệu cran ra màn hình ta sẽ có kết quả như sau:

Source: local data frame [225,468 x 11]

    X       date     time    size r_version r_arch      r_os      package version
1   1 2014-07-08 00:54:41   80589     3.1.0 x86_64   mingw32    htmltools   0.2.4
2   2 2014-07-08 00:59:53  321767     3.1.0 x86_64   mingw32      tseries 0.10-32
3   3 2014-07-08 00:47:13  748063     3.1.0 x86_64 linux-gnu        party  1.0-15
4   4 2014-07-08 00:48:05  606104     3.1.0 x86_64 linux-gnu        Hmisc  3.14-4
5   5 2014-07-08 00:46:50   79825     3.0.2 x86_64 linux-gnu       digest   0.6.4
6   6 2014-07-08 00:48:04   77681     3.1.0 x86_64 linux-gnu randomForest   4.6-7
7   7 2014-07-08 00:48:35  393754     3.1.0 x86_64 linux-gnu         plyr   1.8.1
8   8 2014-07-08 00:47:30   28216     3.0.2 x86_64 linux-gnu      whisker   0.3-2
9   9 2014-07-08 00:54:58    5928        NA     NA        NA         Rcpp  0.10.4
10 10 2014-07-08 00:15:35 2206029     3.0.2 x86_64 linux-gnu     hflights     0.1
.. ..        ...      ...     ...       ...    ...       ...          ...     ...
Variables not shown: country (chr), ip_id (int)

Ta thấy kết quả hiện thị đầy đủ và xúc tích hơn khi chúng ta in dữ liệu dạng data frame mydf ban đầu. Dòng đầu tiên cho biết tên lớp và số chiều của tập dữ liệu. Ngay bên dưới, ta có thể quan sát tổng quan về dữ liệu. Thay vì in ra toàn bộ tập dữ liệu, dplyr chỉ hiển thị cho chúng ta 10 dòng đầu tiên và nhiều nhất có thể các cột dữ liệu để quan sát trên màn hình console. Bên dưới cùng, ta thấy danh sách các tên và các lớp của các cột dữ liệu còn lại.

Triết lý của dplyr đó là với tập nhỏ các hàm chúng sẽ làm thật tốt từng hàm này. Cụ thể, dplyr cung cấp 5 “hành động” chính được sử dụng để thao tác cơ bản với tập dữ liệu gồm: select(), filter(), arrange(), mutate(), và summarize().

select

Trong hầu hết mọi trường hợp, đặc biệt là các bảng dữ liệu lớn, ta chỉ quan tâm đến một vài cột dữ liệu. Ví dụ, ta chỉ muốn chọn các cột dữ liệu sau để hiển thị gồm ip_id, package, và country. Ta có thể sử dụng hàm select() như sau:

select(cran, ip_id, package, country)
# Ta có thể làm tương tự như sau
# cran$ip_id, cran$package, và cran$country

Source: local data frame [225,468 x 3]

   ip_id      package country
1      1    htmltools      US
2      2      tseries      US
3      3        party      US
4      3        Hmisc      US
5      4       digest      CA
6      3 randomForest      US
7      3         plyr      US
8      5      whisker      US
9      6         Rcpp      CN
10     7     hflights      US
..   ...          ...     ...

Ta thấy rằng các cột dữ liệu trả ra sẽ theo thứ tự đã rút trích, mặc dù ip_id nằm ở bên phải cùng của tập dữ liệu ban đầu. Ta có thể sử dụng dạng chuỗi (r_arch:country) để rút trích các cột dữ liệu như sau:

select(cran, r_arch:country)

Source: local data frame [225,468 x 5]

   r_arch      r_os      package version country
1  x86_64   mingw32    htmltools   0.2.4      US
2  x86_64   mingw32      tseries 0.10-32      US
3  x86_64 linux-gnu        party  1.0-15      US
4  x86_64 linux-gnu        Hmisc  3.14-4      US
5  x86_64 linux-gnu       digest   0.6.4      CA
6  x86_64 linux-gnu randomForest   4.6-7      US
7  x86_64 linux-gnu         plyr   1.8.1      US
8  x86_64 linux-gnu      whisker   0.3-2      US
9      NA        NA         Rcpp  0.10.4      CN
10 x86_64 linux-gnu     hflights     0.1      US
..    ...       ...          ...     ...     ...

Ta có thể làm tương tự như trên nhưng đảo ngược thứ tự các cột dữ liệu:

select(cran, country:r_arch)

Thay vì hiển thị những cột dữ liệu được hiển thị, chúng ta cũng có thể chỉ định những cột dữ liệu cần lược bỏ bằng cách sử dụng dấu “-“:

# không hiển thị cột time
select(cran, -time)
# không hiển thị từ cột X đến cột size
select(cran, -(X:size))

filter

Bây giờ, bạn đã biết cách rút trích tập con của các cột dữ liệu bằng cách sử dụng hàm select(), bước tiếp theo ta sẽ học cách rút trích tập của của các dòng dữ liệu thông qua hàm filter(). Ví dụ, ta chỉ muốn lọc ra những dòng dữ liệu mà có thuộc tính (cột dữ liệu) package là “swirl”. Ta thực hiện như sau:

filter(cran, package == "swirl")

Source: local data frame [820 x 11]

      X       date     time   size r_version r_arch         r_os package version
1    27 2014-07-08 00:17:16 105350     3.0.2 x86_64      mingw32   swirl   2.2.9
2   156 2014-07-08 00:22:53  41261     3.1.0 x86_64    linux-gnu   swirl   2.2.9
3   358 2014-07-08 00:13:42 105335    2.15.2 x86_64      mingw32   swirl   2.2.9
4   593 2014-07-08 00:59:45 105465     3.1.0 x86_64 darwin13.1.0   swirl   2.2.9
5   831 2014-07-08 00:55:27 105335     3.0.3 x86_64      mingw32   swirl   2.2.9
6   997 2014-07-08 00:33:06  41261     3.1.0 x86_64      mingw32   swirl   2.2.9
7  1023 2014-07-08 00:35:36 106393     3.1.0 x86_64      mingw32   swirl   2.2.9
8  1144 2014-07-08 00:00:39 106534     3.0.2 x86_64    linux-gnu   swirl   2.2.9
9  1402 2014-07-08 00:41:41  41261     3.1.0   i386      mingw32   swirl   2.2.9
10 1424 2014-07-08 00:44:49 106393     3.1.0 x86_64    linux-gnu   swirl   2.2.9
..  ...        ...      ...    ...       ...    ...          ...     ...     ...
Variables not shown: country (chr), ip_id (int)

Chúng ta có thể dùng dấu “,” để nối liên tiếp các điều kiện “&” và dùng toán tử “|” để nối các điều kiện “or”.

# lọc ra những dòng dữ liệu thỏa hai điều kiện r_version == "3.1.1" và country = "US"
filter(cran, r_version == "3.1.1", country == "US")
# lọc ra những dòng dữ liệu thỏa điều kiện country == "US" hoặc "IN"
filter(cran, country == "US" | country == "IN")
# lọc ra những dòng dữ liệu mà thuộc tính r_version không có dữ liệu bị thiếu
filter(cran, !is.na(r_version))

arrange

Thỉnh thoảng, chúng ta muốn sắp xếp các cột dữ liệu theo hướng tăng dần hoặc giảm dần. Để làm được như vậy ta sử dụng hàm arrange(). Để thấy được cách thức của hàm arrange(), trước tiên ta sẽ lấy ra mẫu nhỏ trong tập dữ liệu cran để thí nghiệm. Ở đây, ta chỉ lấy 8 cột dữ liệu từ size cho đến ip_id.

cran2 <- select(cran, size:ip_id)

Source: local data frame [225,468 x 8]

      size r_version r_arch      r_os      package version country ip_id
1    80589     3.1.0 x86_64   mingw32    htmltools   0.2.4      US     1
2   321767     3.1.0 x86_64   mingw32      tseries 0.10-32      US     2
3   748063     3.1.0 x86_64 linux-gnu        party  1.0-15      US     3
4   606104     3.1.0 x86_64 linux-gnu        Hmisc  3.14-4      US     3
5    79825     3.0.2 x86_64 linux-gnu       digest   0.6.4      CA     4
6    77681     3.1.0 x86_64 linux-gnu randomForest   4.6-7      US     3
7   393754     3.1.0 x86_64 linux-gnu         plyr   1.8.1      US     3
8    28216     3.0.2 x86_64 linux-gnu      whisker   0.3-2      US     5
9     5928        NA     NA        NA         Rcpp  0.10.4      CN     6
10 2206029     3.0.2 x86_64 linux-gnu     hflights     0.1      US     7
..     ...       ...    ...       ...          ...     ...     ...   ...

Bây giờ, ta muốn sắp xếp các dòng dữ liệu sao cho cột ip_id được sắp theo hướng tăng dần. Ta làm như sau:

arrange(cran2, ip_id)

Source: local data frame [225,468 x 8]

     size r_version r_arch         r_os     package version country ip_id
1   80589     3.1.0 x86_64      mingw32   htmltools   0.2.4      US     1
2  180562     3.0.2 x86_64      mingw32        yaml  2.1.13      US     1
3  190120     3.1.0   i386      mingw32       babel   0.2-6      US     1
4  321767     3.1.0 x86_64      mingw32     tseries 0.10-32      US     2
5   52281     3.0.3 x86_64 darwin10.8.0    quadprog   1.5-5      US     2
6  876702     3.1.0 x86_64    linux-gnu         zoo  1.7-11      US     2
7  321764     3.0.2 x86_64    linux-gnu     tseries 0.10-32      US     2
8  876702     3.1.0 x86_64    linux-gnu         zoo  1.7-11      US     2
9  321768     3.1.0 x86_64      mingw32     tseries 0.10-32      US     2
10 784093     3.1.0 x86_64    linux-gnu strucchange   1.5-0      US     2
..    ...       ...    ...          ...         ...     ...     ...   ...

Tương tự, ta có thể sắp xếp giảm dần bằng cách sử dụng hàm desc(), desc() là viết tắt của “descending”.

arrange(cran2, desc(ip_id))

Source: local data frame [225,468 x 8]

      size r_version r_arch         r_os      package version country ip_id
1     5933        NA     NA           NA          CPE   1.4.2      CN 13859
2   569241     3.1.0 x86_64      mingw32 multcompView   0.1-5      US 13858
3   228444     3.1.0 x86_64      mingw32        tourr   0.5.3      NZ 13857
4   308962     3.1.0 x86_64 darwin13.1.0          ctv   0.7-9      CN 13856
5   950964     3.0.3   i386      mingw32        knitr     1.6      CA 13855
6    80185     3.0.3   i386      mingw32    htmltools   0.2.4      CA 13855
7  1431750     3.0.3   i386      mingw32        shiny  0.10.0      CA 13855
8  2189695     3.1.0 x86_64      mingw32       RMySQL   0.9-3      US 13854
9  4818024     3.1.0   i386      mingw32       igraph   0.7.1      US 13853
10  197495     3.1.0 x86_64      mingw32         coda  0.16-1      US 13852
..     ...       ...    ...          ...          ...     ...     ...   ...

Ngoài ra, chúng ta cũng có thể sắp xếp dựa trên nhiều cột dữ liệu.

# sắp xếp các dòng dữ liệu tăng dần theo cột package, sau đó theo cột ip_id
arrange(cran2, package, ip_id)
# sắp xếp các dòng dữ liệu tăng dần theo cột country, sau đó giản dần theo cột r_version, và tăng dần theo cột ip_id
arrange(cran2, country, desc(r_version), ip_id)

mutate

Thông thường ta sẽ muốn thêm một cột dữ liệu mới từ các cột dữ liệu trước đó. Trước khi tiến hành sử dụng hàm mutate(), ta tạo mẫu dữ liệu mới cran3 chỉ gồm 3 cột dữ liệu ip_id, package, và size.

cran3 <- select(cran, ip_id, package, size)

Source: local data frame [225,468 x 3]

   ip_id      package    size
1      1    htmltools   80589
2      2      tseries  321767
3      3        party  748063
4      3        Hmisc  606104
5      4       digest   79825
6      3 randomForest   77681
7      3         plyr  393754
8      5      whisker   28216
9      6         Rcpp    5928
10     7     hflights 2206029
..   ...          ...     ...

Cột size ở trên biểu diễn độ lớn dữ liệu ở đơn vị byte, ta muốn tạo một cột dữ liệu mới theo đơn vị megabytes (MB). Ta biết rằng một megabyte bằng 2^20 byte. Ta sử dụng hàm mutate() để tạo cột dữ liệu size_mb như sau:

mutate(cran3, size_mb = size / 2^20)
# tạo cột dữ liệu size_gb (GB) từ cột dữ liệu size_mb
mutate(cran3, size_mb = size / 2^20, size_gb = size_mb / 2^10)

Source: local data frame [225,468 x 5]

   ip_id      package    size     size_mb      size_gb
1      1    htmltools   80589 0.076855659 7.505435e-05
2      2      tseries  321767 0.306860924 2.996689e-04
3      3        party  748063 0.713408470 6.966880e-04
4      3        Hmisc  606104 0.578025818 5.644783e-04
5      4       digest   79825 0.076127052 7.434282e-05
6      3 randomForest   77681 0.074082375 7.234607e-05
7      3         plyr  393754 0.375513077 3.667120e-04
8      5      whisker   28216 0.026908875 2.627820e-05
9      6         Rcpp    5928 0.005653381 5.520880e-06
10     7     hflights 2206029 2.103833199 2.054525e-03
..   ...          ...     ...         ...          ...

summarize

Cuối cùng, trong 5 “hành động” vừa kể trên gồm select(), filter(), arrange(), mutate() đó là hàm summarize(). Hàm này có chức năng gom tập dữ liệu thành một dòng dữ liệu tổng hợp. Ví dụ, ta muốn xem kích thức download dữ liệu trung bình là bao nhiêu, ta thực hiện như sau:

summarize(cran, avg_bytes = mean(size))
Source: local data frame [1 x 1]
  avg_bytes
1  844086.5

Hàm summarize() hữu ích trong việc tổng hợp trên dữ liệu được gom nhóm bởi một cột dữ liệu định sẵn. Ý tưởng chung đó là, hàm summarize() sử dụng lệnh FOR EACH ở mỗi nhóm chúng ta đã gom được để tổng hợp ra thông tin cho từng nhóm này.

Qua bài viết này, chúng ta đã học được cách thao tác trên dữ liệu bằng cách sử dụng 5 hàm của dplyr. Ở các bài viết sau, chúng ta sẽ tìm hiểu sâu hơn về dplyr và các tính năng giúp cho công việc của các nhà phân tích dữ liệu trở nên dễ dàng hơn. Bạn có thể download cheat sheet (bảng tóm tắt) về dplyr do RStudio cung cấp ở link sau:

https://www.rstudio.com/wp-content/uploads/2015/02/data-wrangling-cheatsheet.pdf

Select and Filter function

Select and Filter function

Mutate and Summarize function

Mutate and Summarize function

Nguồn tham khảo: http://swirlstats.com/

Advertisements

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Đăng xuất / Thay đổi )

Connecting to %s