閱讀mutate原始碼學習dplyr

2021-09-29 03:41:55 字數 4203 閱讀 2990

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...