dplyr是r語言裡面處理資料資料非常好使的包,但是最近使用它解決一些問題時遇到了瓶頸,並且搜到的教程都特別基礎,所以我打算從原始碼的角度去找解決方案。
為了理解dplyr::mutate
這個函式,我們需要借助乙個例項,分別思考mutate(mtcars)
,mutate(mtcars, gear+carb)
和mutate(mtcars, new=gear+carb)
會在執行的時候的處理流。
在r語言中直接輸入函式的名字,就能看到"mutate"的原始碼
> dplyr::mutate
function (.data, ...)
這告訴我們,"mutate"其實是乙個泛型函式( generic function),因為它使用usemethod()
呼叫和資料結構對應的"mutate". 我們可以用」methods「去找給定泛型函式的所有實現方法
ps: 如果你輸入的不是資料框或者tbl_df
,那麼泛型函式就會報錯, 因為找不到對應的
> methods(mutate)
[1] mutate.data.frame* mutate.default* mutate.tbl_df*
由於"mtcars"是資料框,那麼usemethod
就會選擇mutate.data.frame
作為實際使用的函式。但是你不能通過直接在命令裡輸這些mutate.data.frame
來檢視它的源**,這是因為*
標註的是不可見函式(nonvisible function),就是不在預設命名空間中的而函式,,你需要用getanywhere()
找到這些函式,然後使用命名空間限定符來訪問。
> getanywhere(mutate.data.frame)
a single object matching 『mutate.data.frame』 was found
it was found in the following places
registered s3 method for mutate from namespace dplyr
namespace:dplyr
with value
function (.data, ...)
這說明對於data.frame類的資料,會先用tbl_df
更改資料結構,重新呼叫mutate函式。那麼對於tbl_df
類,泛型函式就會呼叫mutate.tbl_df*
> getanywhere(mutate.tbl_df)
a single object matching 『mutate.tbl_df』 was found
it was found in the following places
registered s3 method for mutate from namespace dplyr
namespace:dplyr
with value
function (.data, ...)
第乙個函式named_quos
有兩個作用,第一返回quosure類,第二保證quosure類都是由名字的,所以mutate(mtcars, gear+carb)
的新增列的名字就是gear+carb.
第二個函式呼叫了mutate_impl
,以.data和dots作為輸入,這個函式也是不可見的,所以也要用getanywhere
> getanywhere(mutate_impl)
a single object matching 『mutate_impl』 was found
it was found in the following places
namespace:dplyr
with value
function (df, dots)
.call函式是c/c++**的互動介面,負責呼叫_dplyr_mutate_impl
模組,傳入的就是資料框和dots物件。
r語言部分的**到此就結束了,因為後續就是呼叫c/c++**編譯後的函式。
這部分的**在github上託管,雖然我幾乎沒用c/c++寫**,但還能勉強看**
接著之前_dplyr_mutate_impl
,對應**如下
// [[rcpp::export]]
***p mutate_impl(dataframe df, quosurelist dots) else if (is(df)) else
}
***p類由rcpp包提供,讓r包能夠方便的使用.call
和c/c++**互動,這樣子就不需要寫專門的**將r語言的資料結構轉換成c++資料結構。
然後判斷quosure列表的長度,資料框是否存在無效的列名,是否需要分組計算。當長度為0時返回原來的資料框。後面就是具體運算的**,讀起來真的是費勁,但是對於我而言,只需要了解quosurelist dots最後是如何被使用的就行。
dots只是存放表達的中間態,隨後會經由迴圈傳給namedquosure類,後續這些指令傳給call_proxy
,而這個類來自於"#include "。不能再繼續了,因為此恨綿綿無絕期,繼續就是rcpp這個無底洞,放棄吧。
總結一句:理解dplyr包的關鍵在於,你得知道dplyr包本身不參與的資料處理,它只是生成sql語言轉述給後端的資料庫,讓資料庫完成資料處理部分。換句話說,它對sql語句的簡潔封裝,實現前後端分離。通過讀源**的方式,我理解到掌握
mutate
的核心其實學會dplyr程式設計,學會將你需要執行的表示式傳遞給dots,你就能自由的使用mutate
甚至是其他所有dplyr系列。
mutate
拓展函式:mutate_if
,mutate_all
,mutate_at
就是學習mutate
的最好案例
首先以.tbl=mtcars
,.funs=funs(mean)
設定函式內區域性變數,
然後呼叫manip_all
,源**如下
manip_all <- function (.tbl, .funs, .quo, .env, ...)
syms <- syms(tbl_nongroup_vars(mtcars))
funlist <- dplyr:::as_fun_list(funs(mean), quo(mean), caller_env())
# 執行結果
$mpg
expr: ^mean(mpg)
env: global
$cyl
expr: ^mean(cyl)
env: global
...$carb
expr: ^mean(carb)
env: global
最後就會出現乙個非常神奇的表達形式!(!(!funs))
, 我目前還不知道有什麼用。可能是rlange!!!
,用於將列表的內容解壓
,所以下面兩個表達是等價的
# 表達一
mutate_all(mtcars,funs(mean)
# 表達二
syms <- syms(tbl_nongroup_vars(mtcars))
funlist <- dplyr:::as_fun_list(funs(mean), quo(mean), caller_env())
mutate(mtcars,!!!rs)
閱讀mutate原始碼學習dplyr
dplyr是r語言裡面處理資料資料非常好使的包,但是最近使用它解決一些問題時遇到了瓶頸,並且搜到的教程都特別基礎,所以我打算從原始碼的角度去找解決方案。為了理解dplyr mutate這個函式,我們需要借助乙個例項,分別思考mutate mtcars mutate mtcars,gear carb ...
《原始碼閱讀》原始碼閱讀技巧,原始碼閱讀工具
檢視某個類的完整繼承關係 選中類的名稱,然後按f4 quick type hierarchy quick type hierarchy可以顯示出類的繼承結構,包括它的父類和子類 supertype hierarchy supertype hierarchy可以顯示出類的繼承和實現結構,包括它的父類和...
原始碼閱讀 Glide原始碼閱讀之with方法(一)
前言 本篇基於4.8.0版本 原始碼閱讀 glide原始碼閱讀之with方法 一 原始碼閱讀 glide原始碼閱讀之load方法 二 原始碼閱讀 glide原始碼閱讀之into方法 三 大多數情況下,我們使用glide 就一句 但是這一句 裡面蘊含著成噸的 with方法有以下幾個過載方法 publi...