R 的 magrittr 套件提供了一個管線運算子,可以讓函數或運算式串聯在一起,以資料流的方式處理資料的傳遞。

在 Unix/Linux 系統上的各種指令都可以運用管線(pipe)的方式串接起來使用,以資料流的方式來處理各種資料,使用管線並不會改變程式實際執行的動作,主要的差異只在於讓程式碼更容易被撰寫與閱讀。

而在 R 中我們則可以使用 magrittr 套件來達到相同的效果,以下介紹 R 傳統的程式撰寫方式以及改用此套件後的寫法。

傳統方式

若要計算 LogSumExp 函數,以傳統的作法是這樣寫:

log(sum(exp(my.var)), exp(1))

這樣的寫法會讓許多函數與小括號擠在一起,造成程式碼不好閱讀,為了讓程式碼看起來更清晰,有些人會喜歡改成這樣的寫法:

my.var <- exp(my.var)
my.var <- sum(my.var)
log(my.var, exp(1))

這種寫法雖然可讓程式碼的邏輯很清楚的呈現出來,但是缺點就是 my.var 這個字眼重複出現好幾次。

以上兩種寫法各有優缺點,傳統的 R 語言寫法只能從兩者之間擇一使用,而後來新的 magrittr 套件結合上述兩種寫法的優點,提供了一種嶄新的簡潔語法。

magrittr 套件

magrittr 套件導入一種管線(pipe)的語法,以串流的方式來處理資料的運算,讓程式碼更簡潔、語意更清楚。以下是 magrittr 套件的使用方式教學與範例。

首先安裝並載入 magrittr 套件:

install.packages("magrittr")
library(magrittr)

%>% 管線運算子

magrittr 套件中最重要的功能就是 %>% 管線運算子,它的作用在於將左側的運算結果傳遞至右側函數的第一個參數,舉例來說,假側我們有一個傳統的 R 運算式如下:

exp(my.var)

這個運算式是將 my.var 傳遞至 exp 函數中進行冪運算,若以 %>% 管線運算子的方式,則可以改寫為:

my.var %>% exp()

在包含 %>% 的運算式中,若一個函數沒有其餘的參數,也可以將小括號省略,簡寫成這樣:

my.var %>% exp

這樣的寫法可以將許多層函數包裹在一起的運算拆解開來,以上述的 LogSumExp 函數來說,用 %>% 管線運算子改寫之後就會變成這樣:

my.var %>% exp %>% sum %>% log(exp(1))

預設的狀況下,%>% 管線運算子會將資料傳遞至右側函數的第一個參數,如果需要改變參數的位置,可以使用句點 . 的方式指定資料安插的位置。以下是拿 iris 這個鳶尾花資料集做簡單線性迴歸模型的例子:

iris.lm <- lm(Sepal.Length ~ Sepal.Width, data = iris)
summary(iris.lm)

我們可以使用 %>% 管線運算子配合句點 . 將其改寫成:

iris %>%
  lm(Sepal.Length ~ Sepal.Width, data = .) %>%
  summary

第一個 %>% 管線運算子會將 iris 安插在 lm 參數列中句點出現的位置,所以執行起來的效果就跟原來的程式一模一樣。

%$% 運算子

R 的許多函數都會使用 data 參數來指定資料來源的 data frame,然後在函數中取用該 data frame 中的變數,而 %$% 運算子可以讓其左方的物件套用至右方函數的 data 參數中,例如:

iris %$%
  lm(Sepal.Length ~ Sepal.Width) %>%
  summary

這樣的用法跟上面 %>% 管線運算子配合句點 .的方式類似,不過可讓程式碼更簡潔。

這是另外一個計算相關係數的例子:

mtcars %$% cor(disp, mpg)

%<>% 運算子

%<>% 運算子除了具有 %>% 管線運算子的串接功能之外,還可以將其右方的運算結果儲存至左方的變數中。

假設我們想要讓一個變數進行一連串的運算後,再將結果儲存回原來的變數中,典型的寫法大概是像這樣:

set.seed(3)
x <- rnorm(5)
x <- x %>% abs %>% sqrt

這種情況我們就可以用 %<>% 運算子將上面的程式碼改寫為:

set.seed(3)
x <- rnorm(5)
x %<>% abs %>% sqrt

%T>% tee 運算子

%T>% 運算子的功能也是類似 %>% 管線運算子,只不過在運算完成後,它會傳回原本其左方的輸入值,而不是最終右方運算式計算完的結果。

假設我們產生了一個含有兩個行(column)的矩陣,想要將資料畫出來,並且同時也計算出兩行數值的總和,一般普通的寫法會像這樣:

set.seed(3)
x <- matrix(rnorm(200), ncol = 2)
plot(x)
colSums(x)

這裡的資料流結構跟前面的狀況不太一樣,第二行所建立的 x 同時傳給第三行與第四行來使用,這種將資料一分為二的情況就要使用 %T>% tee 運算子來處理,經過 %T>% 改寫後就會變成:

set.seed(3)
rnorm(200) %>%
  matrix(ncol = 2) %T>%
  plot %>%
  colSums

這裡使用 %T>% 的效果就是會讓 matrix 的計算結果同時傳給 plotcolSums 兩個數,達到預期的計算結果。

參考資料:R for Data Sciencemagrittr