參考: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 包也是很強大的哦, 空下來可以學一學