[學習分享] R語言擴展包dplyr筆記 (轉載)

 2014年剛到, 就在 Feedly 訂閱裏看到 RStudio Blog 介紹 dplyr 包已發佈 (Introducing dplyr), 此包將原本 plyr 包中的 ddply() 等函數進一步分離強化, 專注接受dataframe對象大幅提高了速度, 並且提供了更穩健的與其它數據庫對象間的接口。

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 == 1, DayofMonth == 1)

## 用R自帶函數實現:

hflights[hflights$Month == 1 & hflights$DayofMonth == 1, ] ;

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

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

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

1.2 排列: arrange()

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

例如:

arrange(hflights_df, DayofMonth, Month, Year)

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

arrange(hflights_df, desc(ArrDelay))

## 這個函數和 plyr::arrange() 是一樣的, 類似於 order()

## 用R自帶函數實現:

hflights[order(hflights$DayofMonth,hflights$Month, hflights$Year),] ;

hflights[order(desc(hflights$ArrDelay)), ] ;

1.3 選擇: select()

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

select(hflights_df, Year, Month, DayOfWeek)

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

select(hflights_df, Year : DayOfWeek)

## 選擇從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 * 60)

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

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

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

transform(hflights,   gain = ArrDelay - DepDelay,   gain_per_hour = gain / (AirTime / 60))1.5 彙總: summarise()

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

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

## 該函數功能尚不是非常有用

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 > 20, dist < 2000);

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

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


## 更多例子見 vignette("introduction", package = "dplyr")

另: 一些彙總時的小函數
  • n(): 計算個數
  • n_distinct(): 計算 x 中唯一值的個數. (原文爲 count_distinct(x), 測試無用)
  • 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(5)

## 在R中試了一下上面的代碼,顯示報錯如下,沒有找到原因,在help裏也搜索不到%.%函數,不曉得是不是dplyr包中這個函數已經取消了,如有問題,歡迎批評指正:

Batting %.%

+ group_by(playerID) %.%

+ summarise(total = sum(G)) %.%

+ arrange(desc(total)) %.%

+ head(5)

Error in Batting %.% group_by(playerID) %.% summarise(total = sum(G)) %.%  : 

  could not find function "%.%"

? "%.%"

No documentation for ‘%.%’ in specified packages and libraries:

you could try ‘??%.%’

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

head(arrange(summarise(group_by(Batting, playerID), total = sum(G)) , desc(total)), 5)

## 或者像下面方法

totals <- aggregate(. ~ playerID, data=Batting[,c("playerID","R")], sum)

ranks <- sort.list(-totals$R)totals[ranks[1:5],]

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

**************** 感想 ****************************************************************************

可以看到, 用 dplyr 所含函數實現的代碼都要簡潔易讀得多, 說到底, R語言只是一個工具, 作爲工具, 就是要拿來用的, 越稱手越便利越簡潔越好, 反思之下, 本人也是將大把的時間花在了對數據的反覆調整上, 或許是手生, 當然R語言在這方面也確實有一定不足, 大神又說了:

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

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


深入學習

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

  • dplyr 包自帶的60頁詳細文檔
  • 其餘幾個vignettes (網頁) 或 vignette(package = "dplyr") , 包含了數據庫相關, 混合編程, 運算性能比較, 以及新的 window-functions 等內容.
    簡單看了下vignette("window-functions", package = "dplyr"), 提供了一系列函數, 擴展了原來只能返回一個數值的聚焦類函數(如sum(), mean())至返回等長度的值, 變成 cumsum()和 cummean(), 以及 n(), lead() 和 lag()等便捷功能.
  • plyr 包的相關文檔
  • 還有 data.table 包也是很強大的哦, 空下來可以學一學

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章