天天看點

R語言dplyr簡介0 初始化1 基本操作2 分組動作 group_by()3 連接配接符 %.%深入學習

參考:http://bqnw.me/post/dplyr-note

引言

2014年剛到, 就在 Feedly 訂閱裡看到 RStudio Blog 介紹 dplyr 包已釋出 (Introducing dplyr), 此包将原本 plyr 包中的 ddply() 等函數進一步分離強化, 專注接受dataframe對象, 大幅提高了速度,并且提供了更穩健的與其它資料庫對象間的接口. 既然是 Hadley Wickham 的新作, 并自稱 a grammar of data manipulation, 當然要先學為快了, 正好新申了域名, 就把原本記在 Rmd 裡的筆記組織一下, 放在這裡,算是個簡短的教程吧, 僅供入門。

正文: 學習筆記

以下内容主要參照 Introducing dplyr 和 dplyr 包自帶的簡介 (Introduction to dplyr), 複制了原文對應代碼, 并夾雜了個人了解和觀點 (多附于括号内)。

0 初始化

  • 0.1 安裝
install.packages("dplyr")
           
  • 0.2 示範資料
library(Lahman): Lahman 包裡的棒球比賽資料集 Batting
library(hflights): hflights 包裡的飛機航班資料
           
  • 0.3 資料集類型
# 将過長過大的資料集轉換為顯示更友好的 tbl_df 類型:

hflights_df <- tbl_df(hflights)

# 可以 hflights_df 感受一下不再被刷屏的感覺.
           

1 基本操作

把常用的資料操作行為歸納為以下五種:

1.1 篩選: filter()

按給定的邏輯判斷篩選出符合要求的子資料集, 類似于 base::subset() 函數

例如:

filter(hflights_df, Month == , DayofMonth == )
           

用R自帶函數實作:

hflights[hflightsMonth ==  & hflightsDayofMonth == , ] 
           

除了代碼簡潔外, 還支援對同一對象的任意個條件組合, 如:

filter(hflights_df, Month ==  | Month == ) 
           

注意: 表示 AND 時要使用 & 而避免 &&

1.2 排列: arrange()

按給定的列名依次對行進行排序.

例如:

arrange(hflights_df, DayofMonth, Month, Year)
           

對列名加 desc() 進行倒序:

arrange(hflights_df, desc(ArrDelay))
           

這個函數和 plyr::arrange() 是一樣的, 類似于 order(),用R自帶函數實作:

hflights[order(hflightsMonth, hflightsArrDelay)), ]
           

1.3 選擇: select()

用列名作參數來選擇子資料集:

select(hflights_df, Year, Month, DayOfWeek)
           

還可以用 : 來連接配接列名, 沒錯, 就是把列名當作數字一樣使用:

select(hflights_df, Year:DayOfWeek)
           

用 - 來排除列名:

select(hflights_df, -(Year:DayOfWeek))
           

同樣類似于R自帶的 subset() 函數 (但不用再寫一長串的 c(“colname1”, “colname2”) 或者 which(colname(data) == “colname3”), 甚至還要去查找列号)

1.4 變形: mutate()

對已有列進行資料運算并添加為新列:

mutate(hflights_df, gain = ArrDelay - DepDelay, speed = Distance / AirTime * )
           

作用與 plyr::mutate() 相同, 與 base::transform() 相似, 優勢在于可以在同一語句中對剛增加的列進行操作:

mutate(hflights_df, gain = ArrDelay - DepDelay, gain_per_hour = gain / (AirTime / ))
           

而同樣操作用R自帶函數 transform() 的話就會報錯:

transform(hflights,   gain = ArrDelay - DepDelay,   gain_per_hour = gain / (AirTime / ))
           

1.5 彙總: summarise()

對資料框調用其它函數進行彙總操作, 傳回一維的結果:

summarise(hflights_df, delay = mean(DepDelay, na.rm = TRUE))
           

等同于 plyr::summarise(), 原文說該函數功能尚不是非常有用, 大概以後的更新會加強吧.

2 分組動作 group_by()

以上5個動詞函數已經很友善了, 但是當它們跟分組操作這個概念結合起來時, 那才叫真正的強大! 當對資料集通過

group_by()

添加了分組資訊後,

mutate()

,

arrange()

summarise()

函數會自動對這些 tbl 類資料執行分組操作 (R語言泛型函數的優勢).

例如: 對飛機航班資料按飛機編号 (TailNum) 進行分組, 計算該飛機航班的次數 (

count = n()

), 平均飛行距離 (

dist = mean(Distance, na.rm = TRUE)

) 和 延時 (

delay = mean(ArrDelay, na.rm = TRUE)

)

planes <- group_by(hflights_df,TailNum)
delay <- summarise(planes, count = n(), dist = mean(Distance, na.rm = TRUE), delay = mean(ArrDelay, na.rm = TRUE))
delay <- filter(delay, count > , dist < )
           

用 ggplot2 包作個圖觀察一下, 發現飛機延時不延時跟飛行距離沒太大相關性:

ggplot(delay, aes(dist, delay)) + geom_point(aes(size = count), alpha = /) + geom_smooth() + scale_size_area()
           

更多例子見

vignette("introduction", package = "dplyr")

另: 一些彙總時的小函數

1. n(): 計算個數

2. n_distinct(): 計算 x 中唯一值的個數. (原文為 count_distinct(x), 測試無用)

3. first(x), last(x) 和 nth(x, n): 傳回對應秩的值, 類似于自帶函數 x[1], x[length(x)], 和 x[n]

注意: 分組計算得到的統計量要清楚樣本已經發生了變化, 此時的中位數是不可靠的

3 連接配接符 %.%

包裡還新引進了一個操作符, 使用時把資料名作為開頭, 然後依次對此資料進行多步操作.

比如:

Batting %.% group_by(playerID) %.% summarise(total = sum(G)) %.% arrange(desc(total)) %.%    head()
           

這樣可以按進行資料處理時的思路寫代碼, 一步步深入, 既易寫又易讀, 接近于從左到右的自然語言順序, 對比一下用R自帶函數實作的:

head(arrange(summarise(group_by(Batting, playerID), total = sum(G)) , desc(total)), )
# 或者像這篇文章所用的方法:
totals <- aggregate(. ~ playerID, data=Batting[,c("playerID","R")], sum)
ranks <- sort.list(-totals$R)
totals[ranks[:],]
           

文章裡還表示: 用他的 MacBook Air 跑 %.% 那段代碼用了 0.036 秒, 跑上面這段代碼則用了 0.266 秒, 運算速度提升了近7倍. (當然這隻是一例, 還有其它更大的數字.)

更多請 ?”%.%”, 至于這個新鮮的概念會不會和 ggplot2 裡的 + 連接配接号一樣, 發揮出種種奇妙的功能呢? 還是在實際使用中多體驗感受吧。

可以看到, 用 dplyr 所含函數實作的代碼都要簡潔易讀得多, 說到底, R語言隻是一個工具, 作為工具, 就是要拿來用的, 越稱手越便利越簡潔越好, 可是, 正如 Hadley Wickham 在2013年的訪談中提到的那樣:

如果你用了8小時進行資料清理和資料整理,而隻用了2小時進行模組化,那麼很明顯,你希望了解如何将資料清理和整理的時間盡可能縮短。

反思之下, 本人也是将大把的時間花在了對資料的反複調整上, 或許是手生, 當然R語言在這方面也确實有一定不足, 大神又說了:

資料分析有兩個瓶頸,一是我們的目标是什麼,二是我們如何用計算機去實作。我現有的很多作品,如 ggplot2,plyr 和 reshape2,更關注的是如何更簡單地表達你的目标,而不是如何讓計算機算得更快。

這種内在的理念正是要将工具工具化, 把無謂的時間減少, 讓精力用在真正需要考慮的地方. 正如 Vim 一樣, 在投入一定的學習成本後, 繼續用繼續學, 不知不覺地就能心手如一, 想做什麼, 就已經按下去了, 進而更多地思考要編輯什麼, 而不必糾結于光标移動選擇等細節. 這其中的巧妙之處在于: 實作過程要以人腦的思維運作方式為标準, 讓工具來适應人, 以實作目的為導向, ggplot2 的圖形圖層文法也是如此. 不管是軟體也好, 程式設計語言也好, 高效的方法都是相通的, 這也正是許多人努力的方向, 另外平素語出驚人的王垠最近也表達了類似觀點.

順便肖凱老師在網易雲課堂新開的R語言初級教程裡提到了十大必學R包的說法, 并把 plyr 列為之一, 有趣的是居然還有人在問答平台上求詳情, 好奇之下放狗一搜, 原來出處在此 (脫水版), 其中 ggplot2 和 reshape2 是平時都有在用的, 還有實用的 knitr 和 Slidify , 其它就沒什麼發言權了.

深入學習

暫時沒有太多的相關資料, 如欲進一步學習, 可參閱:

- dplyr 包自帶的60頁詳細文檔

- 其餘幾個

vignettes

(網頁) 或

vignette(package = "dplyr")

, 包含了資料庫相關, 混合程式設計, 運算性能比較, 以及新的 window-functions 等内容.

- 簡單看了下

vignette("window-functions", package = "dplyr")

, 提供了一系列函數, 擴充了原來隻能傳回一個數值的聚焦類函數(如sum(), mean())至傳回等長度的值, 變成 cumsum()和 cummean(), 以及 n(), lead() 和 lag()等便捷功能.

- plyr 包的相關文檔: 首頁

還有data.table 包也是很強大的哦, 空下來可以學一學

繼續閱讀