這是R數據科學的讀書筆記之一,《R數據科學》是一本教你如何用R語言進行數據分析的書。即便我使用R語言快2年多了,但是讀這本書還是受益頗多。
(好吧,這兩年來我一直在舒適區原地踏步)
這一篇學習筆記對應第13章:使用magrittr進行管道操作。關於管道這個概念,我最早在Linux系統中接觸,它是Unix系統設計哲學的體現,「組合小功能完成大任務」,比如說BWA比對後排序用管道的寫法就是
bwa mem ref 1.fq 2.fq | samtools sort > align.bam
在R語言接觸管道符號"%>%"是在學習 dplyr包時候,那個時候我以為這個符號是 Hadley Wickham 創造出來的,其實是來源於Stefan Milton Bache開發的 magrittr中。
基礎部分在沒有管道符號之前,如果我要對一個變量做一系列的分析的話,那麼寫法是下面這個樣子
# 先創建100個隨機數
nums <- rnorm(100)
# 分成兩列
nums_matrix <- matrix(nums, ncol = 2)
# 分別求兩列的均值
nums_mean <- Matrix::colMeans(nums_matrix)
這裡面我寫了很多中間變量,要多敲很多字,而且如果我要修改輸入的話的100個隨機數的話,我需要修改兩處。當然可以進行函數嵌套.
Matrix::colMeans(matrix(rnorm(100), ncol=2))
但是這種寫法不利於人的閱讀,當我讀到這個函數的時候,我需要先連續往大腦裡塞進去兩個函數後,才能抵達核心,然後再從裡往外解析。
但是有了管道符號之後一切就不一樣了,寫法就是
rnorm(100) %>% matrix(ncol=2) %>% Matrix::colMeans()
你會發現從左往右閱讀,代碼讀起來非常的流暢。
雖然管道看起來很美好,但是在如下的場景中就不太適合了,
簡單點說,就是類似於A > B > C > D 這種場景用管道比較好。
除了 %>%這個好用的符號外,magrittr還提供了其他三個比較好用的符號, %$%, %<>%和 %T>%。
高級部分上面都是常規操作,作為有一定基礎的R語言使用者,更希望探索點這個符號的本質。
首先明確一點,在R語言中一切符號本質上都是函數,比如說"+"也是一個函數,常規用法都是 1+2, 但是我們可以用函數的方式來寫哦
`+`(4,5)
# 9
因此 rnorm(100)%>%matrix(ncol=2)其實應該理解成
`%>%`(rnorm(100), matrix(ncol=2))
那麼我們就可以看看管道符號的原始碼了
?magrittr::`%>%`
function (lhs, rhs)
{
parent <- parent.frame()
env <- new.env(parent = parent)
chain_parts <- split_chain(match.call(), env = env)
pipes <- chain_parts[["pipes"]]
rhss <- chain_parts[["rhss"]]
lhs <- chain_parts[["lhs"]]
env[["_function_list"]] <- lapply(1:length(rhss), function(i) wrap_function(rhss[[i]],
pipes[[i]], parent))
env[["_fseq"]] <- `class<-`(eval(quote(function(value) freduce(value,
`_function_list`)), env, env), c("fseq", "function"))
env[["freduce"]] <- freduce
if (is_placeholder(lhs)) {
env[["_fseq"]]
}
else {
env[["_lhs"]] <- eval(lhs, parent, parent)
result <- withVisible(eval(quote(`_fseq`(`_lhs`)), env,
env))
if (is_compound_pipe(pipes[[1L]])) {
eval(call("<-", lhs, result[["value"]]), parent,
parent)
}
else {
if (result[["visible"]])
result[["value"]]
else invisible(result[["value"]])
}
}
}
這個代碼的核心在於如下兩行
env[["_function_list"]] <- lapply(1:length(rhss), function(i) wrap_function(rhss[[i]],
pipes[[i]], parent))
env[["_fseq"]] <- `class<-`(eval(quote(function(value) freduce(value,
`_function_list`)), env, env), c("fseq", "function"))
這兩行幹的活其實是進行詞法轉換,也就是把我們之前的管道串聯起來的部分轉換成
my_pipe <- function(.){
. <- rnorm(.)
. <- matrix(., ncol = 2)
. <- Matrix::colMeans(.)
}
my_pipe(100)