dplyr是由Hadley Wickham主持開發和維護的一個主要針對數據框快速計算、整合的函數包,同時提供一些經常使用函數的高速寫法以及幾個開源數據庫的鏈接。此包是plyr包的深化功能包,其名字中的字母「d」即來源於data frame,以示其專一於數據框數據的整理和操做。咱們將在本章中着重介紹一些數據處理方面的經常使用功能函數。shell
1.1管道函數數據庫
在前面的簡介中,咱們計算了cran上的可用的函數包的數量:app
> contrib.url("http://mirrors.xmu.edu.cn/CRAN","source")%>%available.packages%>%nrow函數
[1] 6450工具
在以上的代碼中,咱們使用了「%>%」這個符號(或者說函數),這能夠被稱爲管道函數。管道函數在其餘的語言(好比shell)中也常常被使用,而其餘的函數包中也有相似的功能函數(pipeR包和magrittr包),咱們稍後也會對magrittr包中管道函數作介紹。性能
「%>%」這個管道函數把左件的值發送給右件的表達式,並做爲右件表達式函數的第一個參數。測試
左件%>%右件url
一般來講,能夠把「%>%」讀做then,即而後。component
若是你須要同時操做多個數據集或者多個函數時,使用管道函數將會更加方便、快速和有邏輯性。好比以上的計算函數包數量的代碼若改成通常寫法:orm
nrow(available.packages(contrib.url("http://mirrors.xmu.edu.cn/CRAN","source")))
或者是這樣:
x=contrib.url("http://mirrors.xmu.edu.cn/CRAN","source")
x= available.packages (x)
nrow(x)
對比這三段代碼,第二段代碼包含了3對括號,可讀性比較差;第三段代碼將之分爲三句,代碼量增長,而且留下了x這個中間產物;而第一段代碼乍一看很長,但實際上對於代碼的運行、傳遞機制表現得很清晰,各階段的函數分工明確,邏輯清楚,若是習慣使用後,可用性至關好。
值得一提的是,管道函數還能夠用在自定義函數(function)中,好比咱們定義一個對向量中的數求整後取絕對值求和的函數。
通常代碼:
> f1=function(x)sum(abs(round(x)))
管道函數代碼
> f2=.%>%round%>abs%>%sum
咱們來看看源碼的顯示:
> f1
function(x)sum(abs(round(x)))
> f2
Functional sequence with the following components:
1. round(.)
2. abs(.)
3. sum(.)
Use 'functions' to extract the individual functions.
毫無疑問的是,二者並無什麼不一樣,來看下面的結果。
> set.seed(1000)
> a=rnorm(20,10,10)
> a%>f1
[1] 161
> a%>f2
[1] 161
固然,R中並不只僅只有這一個管道函數,一樣由Hadley Wickham開發的magrittr包中介紹了其餘功能的管道函數。
函數名 功能
%<>% 在%>%的基礎上,會把右件的最終返回值返回給左件(注意是最終)。
%T>% 把左件的值傳入後,不產生任何返回值(你能夠對計算的中間過程畫個圖,再接着計算)。
%$% 選取左件中的任意個變量名來操做,但左件中原來的數據將不會被保存,僅剩下計算後的值。
> test=data.frame(x=a,y=sample(0:1,20,replace=T))%T>%
+ plot%$%
+ f2(x)
> test%<>%"*"(10)
以上代碼簡單地運用了上述3個管道函數,第一行先構建一個數據框對數據框畫散點圖並對x變量運算賦值給test,第二行對test乘以10倍。
管道函數是一個很是方便快捷的工具,在本書後續的其餘程序裏,咱們將大量地使用管道函數,以增長代碼的可讀性,減小代碼量。
1.2基礎函數
在數據分析中,咱們每每受限於分析目標和具體實現兩個瓶頸。在平常交流中,數據分析師們也常常表示本身大概會分配70%-80%的項目時間用於數據處理,毫無疑問,Hadley Wickham所開發的一系列函數包也都圍繞着如何減小數據處理時間這個目標來進行。可以快捷方便地表達並實現本身心中的目標,纔有更多的時間用於思考和分析。
dplyr包的函數能處理很大一部分結構化數據處理場景中所需的功能,篩選、排序、變量選擇、變形、彙總等。
1.2.1 filter篩選
filter按照篩選條件或邏輯篩選出符合目標的子集,與base中的subset十分類似,不過跟其餘dplyr包中的基礎函數同樣,filter中能夠直接調用數據框中的變量名,而無需attach或者使用」$」。
舉個栗子:iris數據集中Species不等於setosa和virginica,且Sepal.Width大於等於3.2.
> iris%>%filter(!Species%in%c("setosa","virginica"),Sepal.Width>=3.2)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 7.0 3.2 4.7 1.4 versicolor
2 6.4 3.2 4.5 1.5 versicolor
3 6.3 3.3 4.7 1.6 versicolor
4 5.9 3.2 4.8 1.8 versicolor
5 6.0 3.4 4.5 1.6 versicolor
1.2.2 arrange排序
arrange能夠根據變量名依次對數據框進行排序,靠前的變量優先級越高,對變量名使用desc函數即爲倒序。plyr(咱們之後會介紹的一個包,一樣出品自Hadley Wickham)中也有一個相同的此函數。
在R的base中,可使用order來實現相同功能。
> arrange(mtcars, cyl, disp)%>%head(3)
mpg cyl disp hp drat wt qsec vs am gear carb
1 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
2 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
3 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
> arrange(mtcars, desc(disp))%>%head(3)
mpg cyl disp hp drat wt qsec vs am gear carb
1 10.4 8 472 205 2.93 5.250 17.98 0 0 3 4
2 10.4 8 460 215 3.00 5.424 17.82 0 0 3 4
3 14.7 8 440 230 3.23 5.345 17.42 0 0 3 4
Base寫法:
mtcars[with(mtcars,order(cyl,disp)),]%>%head(3)
mtcars[order(mtcars$cyl,mtcars$disp),]%>%head(3)
提及排序,就不能忘記了排名,dplyr中有一系列排名的函數,就是ranking系列。
函數名 功能
row_number 通用排名,並列的名次結果按前後順序不同,靠前出現的元素排名在前
min_rank 通用排名,並列的名次結果同樣,佔用下一名次。
dense_rank 中國式排名,並列排名不佔用名次,如:不管有幾個並列第2名,以後的排名仍應該是第3名
percent_rank 按百分比的排名
cume_dist 累計分佈區間的排名
ntile 粗略地把向量按堆排名,n便是堆的數量
> set.seed(1000)
> x=sample(1:5,7,replace=T)
> print(x)
[1] 2 4 1 4 3 1 4
> row_number(x)
[1] 3 5 1 6 4 2 7
> min_rank(x)
[1] 3 5 1 5 4 1 5
> dense_rank(x)
[1] 2 4 1 4 3 1 4
> percent_rank(x)
[1] 0.3333333 0.6666667 0.0000000 0.6666667 0.5000000 0.0000000 0.6666667
> cume_dist(x)
[1] 0.4285714 1.0000000 0.2857143 1.0000000 0.5714286 0.2857143 1.0000000
> ntile(x,3)
[1] 1 2 1 3 2 1 3
另外一個函數top_n還實現了min_rank和filter的組合,用以篩選按排序顯示的數據框。
1.2.3 select變量選擇
對於一個稍微瞭解SQL或者更多的數據分析師來講,select天然是再熟悉不過的一個單詞了,這裏的select在某種程度上也相似於SQL中的select,其功能是按變量名選擇數據框中的變量。
> select(mtcars,mpg,cyl,carb)%>%head(3)
mpg cyl carb
Mazda RX4 21.0 6 4
Mazda RX4 Wag 21.0 6 4
Datsun 710 22.8 4 1
同時,「-」即減號也是能夠運用在這裏的,選擇除此之外的變量:
> select(mtcars,-mpg,-cyl,-carb)%>%head(3)
disp hp drat wt qsec vs am gear
Mazda RX4 160 110 3.90 2.620 16.46 0 1 4
Mazda RX4 Wag 160 110 3.90 2.875 17.02 0 1 4
Datsun 710 108 93 3.85 2.320 18.61 1 1 4
也能夠把用「:」,把變量名鏈接起來,這貨把變量當成數字了:
> select(mtcars,mpg:vs)%>%head(3)
mpg cyl disp hp drat wt qsec vs
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1
固然,直接使用數字也是能夠的:
> select(mtcars,2:8)%>%head(3)
cyl disp hp drat wt qsec vs
Mazda RX4 6 160 110 3.90 2.620 16.46 0
Mazda RX4 Wag 6 160 110 3.90 2.875 17.02 0
Datsun 710 4 108 93 3.85 2.320 18.61 1
PS:這裏要亂入一個雞肋函數,slice,按行號篩選(若是這算篩選的話)數據框。
slice(mtcars,1:2);mtcars[1:2,]
slice(mtcars,n())
slice(mtcars,30:n())
誠如所言,有點雞肋,徹底能夠用filter和row_number來實現,或者直接用「[]」好了,不過,天知道。
第三行代碼的n()是計數的一個函數,不能單獨使用。
1.2.4 mutate變量變形
mutate能夠對數據框中已有的變量進行操做或者增長變量,值得稱讚的是,一段mutate的代碼中,靠後的變量操做能夠操做前期新添加或改變的變量,這是transform所不具有的特性。
> mutate(mtcars,V1=mpg/cyl,V2=disp/hp,V3=V1+V2)%>%
+ select(V1:V3)%>%
+ head(3)
V1 V2 V3
1 3.5 1.454545 4.954545
2 3.5 1.454545 4.954545
3 5.7 1.161290 6.861290
同時若你嫌麻煩一個個地對變量進行操做,還可使用mutate_each函數對數據框中的變量批量操做,經過調整funs(即functions)和vars(variables)參數控制functions的數量,以及參與變形的variables,這裏控制variables的技巧與select函數類似。
iris%>%mutate_each(funs(dense_rank)) iris%>%mutate_each(funs(dense_rank,min_rank),-Petal.Width)
其家族內的另外一個函數,transmute,返回值中不包含原數據集變量,只保留計算轉換後的變量。
1.2.5 summarise數據彙總
summarise是對數據框中的變量調用函數進行數據彙總,單一地說來,其與plyr包中的summarise是同樣的,不過,咱們即將介紹dplyr包中的另外一大功能,分組計算,使用分組計算的summarise能作的事情就多了很是多,其能夠實現幾乎全部的相似於Excel中數據透視表的彙總功能。
> summarise(mtcars,meanDisp=mean(disp),sumMpg=sum(mpg))
meanDisp sumMpg
1 230.7219 642.9
summarise也一樣有個each版本,
> iris%>%summarise_each(funs(mean,sum))
Sepal.Length_mean Sepal.Width_mean Petal.Length_mean Petal.Width_mean
1 5.843333 3.057333 3.758 1.199333
Species_mean Sepal.Length_sum Sepal.Width_sum Petal.Length_sum
1 2 876.5 458.6 563.7
Petal.Width_sum Species_sum
1 179.9 300
1.2.6 join——拒絕merge
在base中,咱們使用merge函數來合併兩個數據框的行或列。雖然merge如此強大,但其也有個致命的弱點,那就是——慢。咱們構建一個一百萬行的隨機數據框來測試一下。
> set.seed(1000)
> a=sample(1:1000000,1000000)
> df1=data.frame(a,x=rnorm(1000000))
> df2=data.frame(a=sample(a,1000000,replace=T),y=rnorm(1000000,10,10))
> system.time(merge(df1,df2,by="a",all=T))
用戶 系統 流逝
8.11 0.02 8.13
> system.time(full_join(df1,df2,by="a"))
用戶 系統 流逝
1.50 0.09 1.59
以上full_join的性能完爆了merge,速度提高了80%左右,若是是把數量級提高到千萬的話,join的優點更加明顯:
> system.time(merge(df1,df2,by="a",all=T))
用戶 系統 流逝
122.26 1.60 123.91
> system.time(full_join(df1,df2,by="a"))
用戶 系統 流逝
21.12 0.50 21.63
以上的測試使用的是以下所示的電腦,有興趣的童鞋也能夠自行作作測試:
dplyr包中所帶的6個join函數的功能以下所示,其差別僅在於返回值的不一樣,咱們假設其形式均爲join(x,y):
函數名 功能
inner_join 返回全部在y中能查找到的x的行,且包含x和y的全部列;
left_join 返回全部x的行,且包含x和y的全部列,在y中沒有查找到的x的行新增的列的值會以NA填充;
right_join 同上,只是x和y調換了一下;
full_join 返回全部x和y的行和列,未查找的部分一樣會被NA填充;
anti_join 返回全部未能在y中能查找到的x的行,也只返回x的列
semi_join 返回全部在y中能查找到的x的行,也只返回x的列
1.3分組操做
提起group_by,想必或多或少接觸過SQL的數據分析師們並不陌生。對,你沒有聽錯……
此group_by的語法意義幾乎與SQL中的group by徹底同樣,其也是針對被group by的變量進行分組的操做與計算,前提是有這樣的操做與計算。在1.2.5中,咱們提到了summarise配合使用分組計算能作到很大部分的數據透視表能夠作的事情:
> group_by(iris,Species)%>%
+ summarise(mean=mean(Sepal.Length),max=max(Sepal.Width),
+ min=min(Sepal.Width),sd=sd(Petal.Width))%>%
+ ungroup%>%
+ mutate(distTest=max-min)
Source: local data frame [3 x 6]
Species mean max min sd distTest
1 setosa 5.006 4.4 2.3 0.1053856 2.1
2 versicolor 5.936 3.4 2.0 0.1977527 1.4
3 virginica 6.588 3.8 2.2 0.2746501 1.6
在上面這段代碼中,咱們根據鳶尾花的種類(Species)進行了分組,統計(summarise)了Sepal.Length的均值,Sepal.Width的最大值和最小值,Petal.Width的標準差。在結束統計後(ungroup的目的是解除group_by的操做),又插入了一個新的字段(mutate),計算不一樣種類的Sepal.Width極差。
一般來講,group_by與summarise這兩個函數會放在一塊兒使用,不過特殊的應用場景中,group_by也能夠單獨出現,好比針對每一個組進行一些操做:
> group_by(iris,Species)%>%
+ filter(Petal.Width<=max(Petal.Width)-0.5)%>%
+ ungroup%>%
+ head(3)
Source: local data frame [3 x 5]
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.5 2.3 4.0 1.3 versicolor
2 5.7 2.8 4.5 1.3 versicolor
3 4.9 2.4 3.3 1.0 versicolor
這裏咱們的篩選條件是選出小於每一個種類的鳶尾花的Petal.Width的最大值減去0.5,看起來很繞是吧,反正你說不定哪天就須要作這樣的操做了呢。
1.4 rowwise
rowwise這個函數的名字就十分萌萌噠,其還有個兄弟(or姐妹)函數叫作colwise(源自plyr包),二者之間用法雖然不盡相同,卻也有着類似的思想(一個按行分組一個按列分組好嗎,要知道行和列在data.frame裏面差距仍是蠻大的哎)。
對,你理解的沒錯,其實就是apply(x,1,FUN)啦,可是apply的效率,你懂得……咱們運行一個較大的數據集來測試一下。
> m=matrix(1:16000000,ncol=2)%>�ta.frame
> system.time(m%>%rowwise%>%summarise(sum(X1,X2)))
用戶 系統 流逝
10.52 0.00 10.52
> system.time(m%>%apply(1,sum))
用戶 系統 流逝
55.87 0.10 55.97
1.5其餘工具函數
Hadley Wickham在dplyr中開發了大量用到想哭的函數——是感動哭,若是當年這個包早點出現,說不定我看起來沒有如今這麼老……
1.5.1 tally系列
tally是一個很方便的計數函數,其根據最初的調用而決定下一次調用n或者sum(n)。它還有其餘的小夥伴好比count和n,都是計數家族的。
> iris%>%group_by(Species)%>%tally
Source: local data frame [3 x 2]
Species n
1 setosa 50
2 versicolor 50
3 virginica 50
以上代碼與
iris%>%group_by(Species)%>%summarise(n=n())
iris%>%count(Species)
等價,而若是用base寫法,即table(iris$Species),但輸出結果明顯不夠友善。
> iris%>%group_by(Species)%>%tally%>%tally
Using n as weighting variable
Source: local data frame [1 x 1]
n
1 150
1.5.2 sample系列
此sample系列是對數據框進行隨機抽樣,只做用於數據框和dplyr自帶的tbl等格式的數據。sample_n爲按行數隨機抽樣,而sample_frac爲按比例抽樣;其weight參數能夠設置抽樣的權重而replace參數爲有放回抽樣。
sample_n(by_cyl,2,replace=TRUE)
sample_n(by_cyl,2,weight=mpg/mean(mpg))
sample_frac(mtcars,0.1)
sample_frac(mtcars,0.1,weight=1/mpg)
1.5.3 cumall系列
dplyr在base的cum系列函數的基礎上增長了幾個新的累積計算函數,cumall,cummean,cumany。對於cumall和cumany,會將輸入的向量轉化爲邏輯值,其可用於多條件的「和運算」和「或運算」;而cummean更可能是補充cumsum之類的函數,得出累積的平均數。
> x=c(5,2,3,0,1,NA,2)
> cumall(x)
[1] TRUE TRUE TRUE FALSE FALSE FALSE FALSE
> cumany(x)
[1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE
> cummean(x)
[1] 5.000000 3.500000 3.333333 2.500000 2.200000 NA NA
1.5.4 distinct系列
distinct就和它的名字同樣孤獨,dplyr中有兩個函數,distinct爲計算數據集中的惟一值,是unique的高效版本;
> x=sample_n(mtcars,1000000,replace=T)
> system.time(distinct(x))
用戶 系統 流逝
0.11 0.00 0.11
> system.time(unique(x))
用戶 系統 流逝
11.55 0.01 11.56
而n_distinct至關於length+unique,計算向量中惟一值的個數,不過這個函數的速度反而不及length+unique,這明顯不符合Hadley Wickham的風格啊。
> set.seed(1000)
> x=rnorm(10000000)
> system.time(unique(x)%>%length)
用戶 系統 流逝
0.78 0.05 0.82
> system.time(n_distinct(x))
用戶 系統 流逝
7.08 0.31 7.39
1.5.5 glimpse
顧名思義,對數據的驚鴻一瞥,本函數把數據集倒轉,有點像str,但結果更爲友善,其會顯示出全部的變量名,同時儘可能顯示出更多的原始數據,有興趣的童鞋能夠glimpse(mtcars)感覺一下。
1.5.6 failwith
這個函數更多用於構建function時產生的錯誤值的處理,通常狀況咱們須要使用if等語句來判斷錯誤值產生時function的返回值,而使用failwith的話,能夠很方便地給予一個返回值給function。
> f=function(x)ifelse(x==1,stop("error"),1)
> f(1)
Error in ifelse(x == 1, stop("error"), 1) : error
> f(2)
[1] 1
> f1=failwith(NA,f)
> f1(1)
Error in ifelse(x == 1, stop("error"), 1) : error
[1] NA
能夠看到雖然報出error,可是函數仍然有了返回值。
1.5.7 rbind_all系列
在最新版本的dplyr中,rbind_all已然被做者建議棄用,代替以bind_cols和bind_rows,bind_rows支持各類按行的合併,好比兩個變量一致的數據框,或者由多個相同格式數據框組成的一個列表的合併。而使用bind_cols的時候,還須要匹配相同的行,因爲強大的join系列的存在,bind_cols相對不太經常使用。
1.5.8 其餘函數
between提供了x>= left & x<=right的簡略版。
> between(1:5,2,3)
[1] FALSE TRUE TRUE FALSE FALSE
nth系列(first,last,nth)函數,其返回序列(vector)中的第一個(最後一個或者第N個)值。
> x=c(5,2,3,0,1,NA,2)
> first(x)
[1] 5
> last(x)
[1] 2
> nth(x,3)
[1] 3
dplyr包配合其餘幾個程序包(plyr,tidyr等等)幾乎解決了平常數據整理中遇到的大部分問題,其還包括了鏈接幾個開源數據庫的函數,可用於遠程數據庫計算取數;也推出了data_frame和tbl_df等快速方便的數據存儲對象;do語句使得在同一數據集中根據不一樣分組狀況建立不一樣的模型更加方便。在後面的章節中,咱們將大量的應用到本包的函數,而且在代碼風格上也將大量植入管道函數,這也是咱們在第一章即介紹本包的初衷。