1 Giới thiệu

Ba năm về trước, sự ra đời của package « magrittr » và những toán tử « Pipes » đã tạo ra một thay đổi mang tính cách mạng cho ngôn ngữ R. Năm 2017, những toán tử pipe này được tích hợp vào tidyverse, một module trong Rstudio có tính chất « hệ sinh thái ». Toán tử pipe sau đó được sử dụng ngày càng nhiều trong những sách giáo trình về data science phát hành năm 2017.

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.

Trong bài thực hành ngắn này, Nhi giới thiệu với các bạn về những toán tử pipe và ý nghĩa của chúng qua những thí dụ nhỏ, đơn giản.

2 Toán tử Forward Pipe %>%

Đây là dạng pipe thông dụng nhất. Công dụng của %>% 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ử %>% 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).

Thí dụ sau :

library(magrittr)
## Warning: package 'magrittr' was built under R version 3.4.1
# 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.

Một cách viết khác : đặt các hàm tính toán bên trong dấu .

Ta có thể so sánh 2 cách viết code có và không có sử dụng %>%, chúng cho ra kết quả như nhau nhưng cách thứ nhất rõ ràng và trong sáng hơn hẳn

x=c(1:100) %>% sqrt() %>% `log2` %>% `/`(2)

head(x)
## [1] 0.0000000 0.2500000 0.3962406 0.5000000 0.5804820 0.6462406
y=log2(sqrt(c(1:100)))/2

head(y)
## [1] 0.0000000 0.2500000 0.3962406 0.5000000 0.5804820 0.6462406
x==y
##   [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [15] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [29] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [43] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [57] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [71] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [85] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [99] TRUE TRUE

3 Toán tử T pipe %T>%

Toán tử T pipe %T>% 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")

Quy trình trên có nội dung là : : 1) tạo ngẫu nhiên 100 số theo phân phối chuẩn, trung bình = 10, sd=1, 2) Nhánh thứ nhất : vẽ một đồ thị time series, 3) Nhánh thứ 2 : vẽ một density plot

Như vậy bạn sẽ có 2 đồ thị ở đầu ra 2 nhánh của ống T

4 Toán tử Assigning pipe %$%

Toán tử %$% 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.

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")

Thí dụ này: 1) Tạo 1 dataframe gồm 2 biến x,y, 2) chỉ sử dụng đích danh biến x hoặc y để vẽ đồ thị time series

5 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

x=abs(rnorm(100))
y=x

cbind(x,y)%>%head()
##               x          y
## [1,] 0.71256940 0.71256940
## [2,] 1.16392409 1.16392409
## [3,] 1.21927797 1.21927797
## [4,] 1.02724986 1.02724986
## [5,] 0.05937096 0.05937096
## [6,] 1.14162496 1.14162496
x %<>% .^2 %>% sqrt()

x==y
##   [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [15] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [29] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [43] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [57] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [71] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [85] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [99] TRUE TRUE

Bây giờ các bạn có thể install packages magrittr hoặc tidyverse và bắt đầu thử dùng Pipe khi viết code. Cảm giác khi dùng Pipe rất dễ chịu và thoải mái, vì mọi thứ đều liền mạch và logic.

LS0tDQp0aXRsZTogIlRvw6FuIHThu60gUGlwZSB0cm9uZyBSIg0Kc3VidGl0bGU6ICJWw6BpIHbDrSBk4bulIG1pbmggaOG7jWEiDQphdXRob3I6ICJMw6ogTmfhu41jIEto4bqjIE5oaSINCmRhdGU6ICIyMCBUaMOhbmcgOCAyMDE3Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0aGVtZTogImRlZmF1bHQiDQogICAgdG9jOiBUUlVFDQogICAgdG9jX2Zsb2F0OiBUUlVFDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQoNCmBgYA0KDQohW10ocGlwZXMucG5nKQ0KDQojIEdp4bubaSB0aGnhu4d1DQoNCkJhIG7Eg20gduG7gSB0csaw4bubYywgc+G7sSByYSDEkeG7nWkgY+G7p2EgcGFja2FnZSDCqyBtYWdyaXR0ciDCuyB2w6Agbmjhu69uZyB0b8OhbiB04butIMKrIFBpcGVzIMK7IMSRw6MgdOG6oW8gcmEgbeG7mXQgdGhheSDEkeG7lWkgbWFuZyB0w61uaCBjw6FjaCBt4bqhbmcgY2hvIG5nw7RuIG5n4buvIFIuIE7Eg20gMjAxNywgbmjhu69uZyB0b8OhbiB04butIHBpcGUgbsOgeSDEkcaw4bujYyB0w61jaCBo4bujcCB2w6BvIHRpZHl2ZXJzZSwgbeG7mXQgbW9kdWxlIHRyb25nIFJzdHVkaW8gY8OzIHTDrW5oIGNo4bqldCDCqyBo4buHIHNpbmggdGjDoWkgwrsuIFRvw6FuIHThu60gcGlwZSBzYXUgxJHDsyDEkcaw4bujYyBz4butIGThu6VuZyBuZ8OgeSBjw6BuZyBuaGnhu4F1IHRyb25nIG5o4buvbmcgc8OhY2ggZ2nDoW8gdHLDrG5oIHbhu4EgZGF0YSBzY2llbmNlIHBow6F0IGjDoG5oIG7Eg20gMjAxNy4gDQoNClRvw6FuIHThu60gcGlwZSBraMO0bmcgY2jhu4kgdGhheSDEkeG7lWkgduG7gSBjw6FjaCB0aOG7qWMgdmnhur90IFIgY29kZSwgbMOgbSBjaG8gUiBjb2RlIHRyb25nIHPDoW5nIHbDoCDEkcaw4bujYyBj4bqldSB0csO6YyB04buRdCBoxqFuIG3DoCBjw7JuIHRoYXkgxJHhu5VpIGPDoWNoIHTGsCBkdXkgY+G7p2EgbmfGsOG7nWkgZMO5bmcgUi4NCg0KVHJvbmcgYsOgaSB0aOG7sWMgaMOgbmggbmfhuq9uIG7DoHksIE5oaSBnaeG7m2kgdGhp4buHdSB24bubaSBjw6FjIGLhuqFuIHbhu4Egbmjhu69uZyB0b8OhbiB04butIHBpcGUgdsOgIMO9IG5naMSpYSBj4bunYSBjaMO6bmcgcXVhIG5o4buvbmcgdGjDrSBk4bulIG5o4buPLCDEkcahbiBnaeG6o24uDQoNCiMgVG/DoW4gdOG7rSBGb3J3YXJkIFBpcGUgJT4lDQoNCsSQw6J5IGzDoCBk4bqhbmcgcGlwZSB0aMO0bmcgZOG7pW5nIG5o4bqldC4gQ8O0bmcgZOG7pW5nIGPhu6dhICU+JSBsw6AgY2h1eeG7g24gdG/DoG4gYuG7mSBr4bq/dCBxdeG6oyBj4bunYSBow6BtIMSRaSB0csaw4bubYyAoYsOqbiB0csOhaSkgdGjDoG5oIGThu68gbGnhu4d1IMSR4bqndSB2w6BvIGPhu6dhIGjDoG0gxJFpIHNhdSAoYsOqbiBwaOG6o2kpIHRyb25nIG3hu5l0IGNodeG7l2kgcXV5IHRyw6xuaC4gS+G6v3QgcXXhuqMgc2F1IGPDuW5nIGzDoCBj4bunYSBow6BtIGN14buRaSBjw7luZyAoYsOqbiBwaOG6o2kpIHRyb25nIGNodeG7l2kuIE5oxrAgduG6rXkgdG/DoW4gdOG7rSAlPiUgY8OzIHTDrW5oIGNo4bqldCAxIGNoaeG7gXUgdsOgIMSR4buLbmggaMaw4bubbmcgdOG7qyB0csOhaSBzYW5nIHBo4bqjaS4gQ8OzIHRo4buDIGjDrG5oIGR1bmcgduG7gSBt4buZdCBkw7JuZyBjaOG6o3kgY+G7p2EgZGF0YSBxdWEgbeG7mXQgxJHGsOG7nW5nIOG7kW5nIChwaXBlKS4NCg0KVGjDrSBk4bulIHNhdSA6DQoNCmBgYHtyfQ0KbGlicmFyeShtYWdyaXR0cikNCg0KIyBGb3J3YXJkIHBpcGUNCg0Kcm5vcm0oMTAwLDIwLDUpJT4lDQogIG1hdHJpeChuY29sPTIpJT4lDQogIGRhdGEuZnJhbWUoeD0uWywxXSx5PS5bLDJdKSU+JQ0KICBwbG90KGNvbD0icmVkIikNCiAgDQpgYGANCg0KTMOgIG3hu5l0IGNodeG7l2kgcXV5IHRyw6xuaCA6IDEpIHThuqFvIG5n4bqrdSBuaGnDqm4gMTAwIHPhu5EgdGhlbyBwaMOibiBwaOG7kWkgY2h14bqpbiwgdHJ1bmcgYsOsbmggPSAyMCwgc2Q9NSA7IDIpIGNoaWEgxJHhu4F1IHZlY3RvciBuw6B5IHRow6BuaCAxIG1hdHJpeCBjw7MgMiBj4buZdCwgMykgY2h1eeG7g24gbWF0cml4IG7DoHkgdGjDoG5oIDEgZGF0YWZyYW1lLCB2w6AgZ8OhbiBnacOhIHRy4buLIDIgY+G7mXQgMSwgMiBj4bunYSBtYXRyaXggY2hvIDIgYmnhur9uIHgseSwgNCkgY3Xhu5FpIGPDuW5nLCB24bq9IHNjYXR0ZXJkb3QgbWF0cml4IGdp4buvYSBYMSxYMix4LHkgYuG6sW5nIGjDoG0gcGxvdCgpLiANCg0KQ8OhY2ggdHLDrG5oIGLDoHkgbsOgeSByw7UgcsOgbmcgdsOgIGThu4UgaGnhu4N1LCB0csOhbmggdmnhu4djIHThuqFvIG9iamVjdCB0cnVuZyBnaWFuIHbDoCBs4buTbmcgZ2jDqXAgY8OhYyBow6BtIG7hur91IG5oxrAgdGEga2jDtG5nIGTDuW5nIHBpcGUuDQoNCk3hu5l0IGPDoWNoIHZp4bq/dCBraMOhYyA6IMSR4bq3dCBjw6FjIGjDoG0gdMOtbmggdG/DoW4gYsOqbiB0cm9uZyBk4bqldSBgIC4gYA0KDQpUYSBjw7MgdGjhu4Mgc28gc8OhbmggMiBjw6FjaCB2aeG6v3QgY29kZSBjw7MgdsOgIGtow7RuZyBjw7Mgc+G7rSBk4bulbmcgJT4lLCBjaMO6bmcgY2hvIHJhIGvhur90IHF14bqjIG5oxrAgbmhhdSBuaMawbmcgY8OhY2ggdGjhu6kgbmjhuqV0IHLDtSByw6BuZyB2w6AgdHJvbmcgc8OhbmcgaMahbiBo4bqzbg0KDQpgYGB7cn0NCng9YygxOjEwMCkgJT4lIHNxcnQoKSAlPiUgYGxvZzJgICU+JSBgL2AoMikNCg0KaGVhZCh4KQ0KDQp5PWxvZzIoc3FydChjKDE6MTAwKSkpLzINCg0KaGVhZCh5KQ0KDQp4PT15DQoNCmBgYA0KDQojIFRvw6FuIHThu60gVCBwaXBlICVUPiUNCg0KVG/DoW4gdOG7rSBUIHBpcGUgJVQ+JSBjw7MgdGjhu4MgxJHGsOG7o2MgaMOsbmggZHVuZyBuaMawIDEg4buRbmcgbsaw4bubYyBow6xuaCBjaOG7ryBULCBraGnhur9uIGNobyBk4buvIGxp4buHdSDEkeG6p3UgdsOgbyBj4bunYSAxIGjDoG0gQSDEkWkgdHLGsOG7m2Mgc+G6vSDEkcaw4bujYyB0cnV54buBbiBjaG8gMiBuaMOhbmggdMawxqFuZyDhu6luZyB24bubaSBxdXkgdHLDrG5oICBCMSAobMOgIDEgaMOgbSkgdsOgIHF1eSB0csOsbmggQjIuIE3hu5l0IOG7qW5nIGThu6VuZyBwaOG7lSBiaeG6v24gbmjhuqV0IGPhu6dhIFQgcGlwZSBsw6AgxJHhu4MgduG6vSAyIMSR4buTIHRo4buLIGtow6FjIG5oYXUgY2hvIGPDuW5nIDEgZ8OzaSBk4buvIGxp4buHdSwgbmjGsCB0aMOtIGThu6Ugc2F1IDoNCg0KYGBge3J9DQojIFQgcGlwZQ0KDQpybm9ybSgxMDAsMTAsMSklVD4lDQogIHRzLnBsb3QoY29sPSJyZWQiKSU+JQ0KICBkZW5zaXR5KCklPiUNCiAgcGxvdChjb2w9ImJsdWUiLCJkZW5zaXR5cGxvdCIpDQpgYGANCg0KUXV5IHRyw6xuaCB0csOqbiBjw7MgbuG7mWkgZHVuZyBsw6AgOiA6IDEpIHThuqFvIG5n4bqrdSBuaGnDqm4gMTAwIHPhu5EgdGhlbyBwaMOibiBwaOG7kWkgY2h14bqpbiwgdHJ1bmcgYsOsbmggPSAxMCwgc2Q9MSwgMikgTmjDoW5oIHRo4bupIG5o4bqldCA6IHbhur0gbeG7mXQgxJHhu5MgdGjhu4sgdGltZSBzZXJpZXMsIDMpIE5ow6FuaCB0aOG7qSAyIDogduG6vSBt4buZdCBkZW5zaXR5IHBsb3QNCg0KTmjGsCB24bqteSBi4bqhbiBz4bq9IGPDsyAyIMSR4buTIHRo4buLIOG7nyDEkeG6p3UgcmEgMiBuaMOhbmggY+G7p2Eg4buRbmcgVA0KDQojIFRvw6FuIHThu60gQXNzaWduaW5nIHBpcGUgJSQlDQoNClRvw6FuIHThu60gJSQlIGNobyBwaMOpcCB0csOtY2ggeHXhuqV0IMSRw61jaCBkYW5oIG3hu5l0IMSR4buRaSB0xrDhu6NuZyB0cm9uZyBr4bq/dCBxdeG6oyBj4bunYSBow6BtIMSRaSB0csaw4bubYyDEkeG7gyBz4butIGThu6VuZyBuaMawIGThu68gbGnhu4d1IMSR4bqndSB2w6BvIGNobyBow6BtIMSRaSBzYXUuIE5oxrAgduG6rXkgbsOzIGPDsyB0w61uaCDEkeG7i25oIGjGsOG7m25nIDEgY2hp4buBdSBuaMawbmcgY2h1ecOqbiBiaeG7h3QuDQoNCmBgYHtyfQ0KZGF0YS5mcmFtZSh4PXJub3JtKDEwMCwxMCw1KSwNCiAgICAgICAgICAgeT1ybm9ybSgxMDAsMjAsNSkpICUkJSB0cy5wbG90KHgsY29sPSJyZWQiKQ0KDQpkYXRhLmZyYW1lKHg9cm5vcm0oMTAwLDEwLDUpLA0KICAgICAgICAgICB5PXJub3JtKDEwMCwyMCw1KSkgJSQlIHRzLnBsb3QoeSxjb2w9ImJsdWUiKQ0KDQpgYGANCg0KVGjDrSBk4bulIG7DoHk6IDEpIFThuqFvIDEgZGF0YWZyYW1lIGfhu5NtIDIgYmnhur9uIHgseSwgMikgY2jhu4kgc+G7rSBk4bulbmcgxJHDrWNoIGRhbmggYmnhur9uIHggaG/hurdjIHkgxJHhu4MgduG6vSDEkeG7kyB0aOG7iyB0aW1lIHNlcmllcw0KDQoNCiMgVG/DoW4gdOG7rSBCYWNrd2FyZCBwaXBlICU8PiUNCg0KQ8O0bmcgZOG7pW5nIGPhu6dhIGJhY2t3YXJkIHBpcGUgdHLDoWkgY2hp4buBdSB24bubaSBmb3J3YXJkIHBpcGUsIHThu6ljIGzDoCBr4bq/dCBxdeG6oyBjdeG7kWkgY8O5bmcgc2F1IG3hu41pIHF1eSB0csOsbmggYsOqbiBwaOG6o2kgdHJvbmcgY2h14buXaSBz4bq9IMSRxrDhu6NjIHRydXnhu4FuIG5nxrDhu6NjIHbhu4Egb2JqZWN0IMSR4bqndSB0acOqbiBu4bqxbSBuZ2/DoGkgY8O5bmcgYsOqbiB0csOhaSBj4bunYSBjaHXhu5dpLg0KDQrEkOG7gyBtaW5oIGjhu41hLCB0YSBjw7MgdGjDrSBk4bulIHNhdSDEkcOieToNCg0KWCBsw6AgMSB2ZWN0b3IgY2jhu6lhIDEwMCBnacOhIHRy4buLIG5n4bqrdSBuaGnDqm4gY8OzIHBow6JuIHBo4buRaSBjaHXhuqluDQpZIGPFqW5nIHRo4bq/IChjaMOtbmggbMOgIFgpDQoNClRhIGTDuW5nIGJhY2t3YXJkIHBpcGUgxJHhu4MgdGjhu7FjIGhp4buHbiBs4bqnbiBsxrDhu6N0IDIgcGjDqXAgdG/DoW4gOiBiw6xuaCBwaMawxqFuZyBYLCBzYXUgxJHDsyBraGFpIGPEg24gYuG6rWMgMi4gROG7hSB0aOG6pXkga+G6v3QgcXXhuqMgc2F1IGPDuW5nIGNow61uaCBsw6AgZ2nDoSB0cuG7iyBiYW4gxJHhuqd1DQoNCkPDsyB0aOG7gyBraeG7g20gdHJhIGLhurFuZyBjw6FjaCBzbyBzw6FuaCB4LCB5LCBjaMO6bmcgaG/DoG4gdG/DoG4gbmjGsCBuaGF1DQoNCmBgYHtyfQ0KeD1hYnMocm5vcm0oMTAwKSkNCnk9eA0KDQpjYmluZCh4LHkpJT4laGVhZCgpDQoNCnggJTw+JSAuXjIgJT4lIHNxcnQoKQ0KDQp4PT15DQpgYGANCg0KQsOieSBnaeG7nSBjw6FjIGLhuqFuIGPDsyB0aOG7gyBpbnN0YWxsIHBhY2thZ2VzIG1hZ3JpdHRyIGhv4bq3YyB0aWR5dmVyc2UgdsOgIGLhuq90IMSR4bqndSB0aOG7rSBkw7luZyBQaXBlIGtoaSB2aeG6v3QgY29kZS4gQ+G6o20gZ2nDoWMga2hpIGTDuW5nIFBpcGUgcuG6pXQgZOG7hSBjaOG7i3UgdsOgIHRob+G6o2kgbcOhaSwgdsOsIG3hu41pIHRo4bupIMSR4buBdSBsaeG7gW4gbeG6oWNoIHbDoCBsb2dpYy4NCg==