R語言分組計算系列

總結一下日常數據處理工作中涉及到的R語言分組計算常用函數,主要包括以下內容:

1、apply系列——tapply()函數

2、sqldf()函數

3、plyr包——ddply()函數

4、aggregate()函數

5、cut()函數

6、dplyr包——group_by()函數

7、summaryBy()函數

8、describeBy()函數

為方便有興趣的朋友進行實操,這裡使用R基礎包中自帶的數據集iris(鳶尾花)數據集做實例展示。數據集包含150個數據集,包含3類鳶尾花(Setosa,Versicolour,Virginica),每類50個數據,每個數據包含4個屬性(Sepal.Length花萼長度 ,Sepal.Width 花萼寬度,Petal.Length花瓣長度,Petal.Width花瓣寬度)。

先來看看iris數據集。

head(iris) Sepal.Length Sepal.Width Petal.Length Petal.Width Species1 5.0 3.5 1.4 0.2 setosa2 4.9 3.0 1.4 0.2 setosa3 4.7 3.2 1.3 0.2 setosa4 4.6 3.1 1.5 0.2 setosa5 5.0 3.6 1.4 0.2 setosa6 5.4 3.9 1.7 0.4 setosa

#head()函數默認展示數據集前6行數據。

一、apply系列——tapply()函數

apply系列函數為R自帶函數,無需下載安裝包,可以直接調用。

格式:

tapply(X, INDEX, FUN = NULL, ...)

#X為數據集

#INDEX為分組欄位(需為因子類型)

#FUN為需要向各組數據應用的函數

例1:

#使用一個變數進行分組

#計算各品種的鳶尾花的萼片長度均值

tapply(iris$Sepal.Length,iris$Species,mean)

結果:

setosa versicolor virginica 5.004 5.936 6.588

換一個寫法:

tapply(iris[,1],iris[,5],mean)

#iris[,1]表示iris$Sepal.Length,1為欄位位置

#iris[,5]表示iris$Species,5也為欄位位置

這種操作適用於欄位數較少,且對欄位位置非常熟悉。

例2:

#使用兩個及以上變數分組

為了不損毀原數據,我將iris數據集拷貝到newiris中,使用newiris數據集進行接下來的數據演示。

newiris<-iris[,]

#向newiris數據集中增加一列判斷,欄位名為Sepal.Length_compare_with_5,判斷Sepal.Length(花萼長度)是否大於5.

newiris$Sepal.Length_compare_with_5<-ifelse(newiris$Sepal.Length>5.0,"over_than_5","lower_than_5")

#這裡使用了簡單的ifelse語句,類似於Excel中的if()函數。

#判斷結果如下:

head(newiris) Sepal.Length Sepal.Width Petal.Length Petal.Width Species Sepal.Length_compare_with_51 5.0 3.5 1.4 0.2 setosa lower_than_52 4.9 3.0 1.4 0.2 setosa lower_than_53 4.7 3.2 1.3 0.2 setosa lower_than_54 4.6 3.1 1.5 0.2 setosa lower_than_55 5.0 3.6 1.4 0.2 setosa lower_than_56 5.4 3.9 1.7 0.4 setosa over_than_5

#這時,newiris數據集中已經有兩個因子類型的欄位,分別是原有的Species,和我剛才添加的判斷列Sepal.Length_compare_with_5,我們接下來就可以使用tapply()函數進行兩個因子的分組計算了。

#計算各鳶尾花品種和花萼長度是否大於5,組合情況下花萼的寬度均值。

代碼如下:

tapply(newiris$Sepal.Width,newiris[,c("Species","Sepal.Length_compare_with_5")],mean)

結果:

Sepal.Length_compare_with_5Species lower_than_5 over_than_5setosa 3.213793 3.723810versicolor 2.233333 2.804255virginica 2.500000 2.983673

#可以看出,花萼長度大於5的鳶尾花,各個品種的花萼寬度均值均大於花萼長度小於5的花萼寬度均值。

#請注意:使用c()函數連接多個分組欄位時,如這裡的c("Species","Sepal.Length_compare_with_5"),請用引號將欄位名引起來,不然R會毫不留情地會報錯。

也可以像下面這麼寫:

attach(newiris)tapply(Sepal.Width,list(Species,Sepal.Length_compare_with_5),mean)detach(newiris)

#採用list()的方式將分組欄位裝在一起,這樣就不用打引號了。

結果:

lower_than_5 over_than_5setosa 3.213793 3.723810versicolor 2.233333 2.804255virginica 2.500000 2.983673

看到這兒,可能會有朋友想,既然能添加多個分組欄位,那能不能添加多個值欄位呢?就像Excel中的透視表那樣,你也可以試試下面這個代碼,當然結果是失敗的。

tapply(iris[,c("Sepal.Length","Sepal.Width")],iris$Species,mean)

想要實現上述功能,其實,我們可以使用接下來的這個函數。

二、sqldf()函數

R中的sqldf包能對數據集使用SQL語句進行操作,並且無需安裝資料庫。

先安裝並載入sqldf包

install.packages("sqldf")library(sqldf)

在正式使用sqldf()函數進行數據分組計算前,我們還需要做一項工作,講原iris數據集的欄位名更改了,這是因為資料庫語言中不允許欄位名中包含「.」。

更名代碼如下:

#再次複製iris數據集

newiris2<-iris[,]

#將原數據中所有欄位中"."替換為"_".

names(newiris2)<-c("Sepal_Length","Sepal_Width","Petal_Length","Petal_Width","Species")

效果如下:

head(newiris2) Sepal_Length Sepal_Width Petal_Length Petal_Width Species1 5.0 3.5 1.4 0.2 setosa2 4.9 3.0 1.4 0.2 setosa3 4.7 3.2 1.3 0.2 setosa4 4.6 3.1 1.5 0.2 setosa5 5.0 3.6 1.4 0.2 setosa6 5.4 3.9 1.7 0.4 setosa

sqldf()格式:

sqldf("SQL語句")

例1:

#按照Species欄位分組,並計算各組的花萼長度均值、花萼寬度均值。

sqldf("select Species,avg(Sepal_Length),avg(Sepal_Length) from newiris2 group by Species")

結果:

Species avg(Sepal_Length) avg(Sepal_Length)1 setosa 5.004 5.0042 versicolor 5.936 5.9363 virginica 6.588 6.588

#這裡涉及到SQL語句的掌握,有需要的朋友可以參照《深入淺出MYSQL》進行相關學習。

鏈接:pan.baidu.com/s/1ZlMh5A 密碼:5n5c

更多關於sqldf的使用案例可以參照code.google.com/p/sqldf

三、plyr包——ddply()函數

ddply()函數位於plyr包,需要手動安裝並載入後才能使用。

install.packages("plyr")library(plyr)

ddply()函數是plyr包中最最常用的函數,因為它輸入和輸出的數據類型都是數據框,不需要進行額外的格式轉換,非常方便。

格式:

ddply(.data, .variables, .fun = NULL, ...)

#.data表示數據集

#.variables為分組欄位

#.fun為需要向各組數據應用的函數

例1:

#計算各個品種鳶尾花花萼長度均值

ddply(iris,.(Species),function(sub){data.frame(Sepal.Length.mean=mean(sub$Sepal.Length))})

#.(Species)設置了分組依據為鳶尾花品種

#function(sub){data.frame(Sepal.Length.mean=mean(sub$Sepal.Length))}為自定義函數,這一段看上去比tapply()函數略顯複雜,但是,正如前面所說,ddply()的優勢在於輸入和輸出數據格式均為數據框,因此,根據需要選擇使用哪款吧。

結果:

Species Sepal.Length.mean1 setosa 5.0062 versicolor 5.9363 virginica 6.588

例2:

#使用多個欄位進行分組計算

#計算各個鳶尾花品種,在花萼長度分別高於5和低於5毫米時,花萼寬度的均值。

這裡我們使用上面創建的newiris數據集進行演示。

ddply(newiris,.(Species,Sepal.Length_compare_with_5),function(sub){ data.frame(Sepal.Width.mean=mean(sub$Sepal.Width))})

#.(Species,Sepal.Length_compare_with_5)將兩個分組依據囊括在一起

結果:

Species Sepal.Length_compare_with_5 Sepal.Width.mean1 setosa lower_than_5 3.2035712 setosa over_than_5 3.7136363 versicolor lower_than_5 2.2333334 versicolor over_than_5 2.8042555 virginica lower_than_5 2.5000006 virginica over_than_5 2.983673

#3種鳶尾花加上2種Sepal.Length_compare_with_5,共有6種組合,從所求的花萼寬度均值可以看出,花萼長度大於5的鳶尾花,各個品種的花萼寬度均值均大於花萼長度小於5的花萼寬度均值,結論與上面一致。

四、aggregate()函數

如果說tapply()和ddply()是兩把不錯的斧頭,那aggregate()函數堪比一把金剛鑽,使用起來非常靈活,極大方便了分組計算工作。

格式1:

aggregate(x, by, FUN, ...)

#x為數據集中需要計算的欄位

#by為分組依據

#FUN為需要向分組數據應用的函數

格式2:

aggregate(formula, data, FUN, ...)

#formula為數據公式,常見的形式有:

one ~ one, many ~ one, one ~ many, many ~ many

(參見:R: 矩陣運算及常用函數 II - aggregate)

#formula右側為分組規則,左側為將要用FUN來計算的數據

日常最為常用的是格式2,具體詳見以下案例:

例1:#one ~ one#型

#計算各鳶尾花品種對應的花萼長度均值。

aggregate(Sepal.Length~Species,data=iris,FUN=mean)

結果:

Species Sepal.Length1 setosa 5.0062 versicolor 5.9363 virginica 6.588

例2:#many ~ one#型

#計算各鳶尾花品種對應的花萼長度均值、花萼寬度均值。

aggregate(cbind(Sepal.Length,Sepal.Width)~Species,data=iris,FUN=mean)

#多個計算欄位要用cbind()函數進行連接,如:cbind(Sepal.Length,Sepal.Width)

結果:

Species Sepal.Length Sepal.Width1 setosa 5.006 3.4282 versicolor 5.936 2.7703 virginica 6.588 2.974

例3:#one ~ many#型

#格式參照以下代碼:

aggregate(breaks ~ wool + tension, data = warpbreaks, mean)

例4:#many~many#型

#格式參照以下代碼:

aggregate(cbind(ncases, ncontrols) ~ alcgp + tobgp, data = esoph, sum)

因鳶尾花數據集所含因子類型欄位較少,不適合做one ~ many,many~many型演示,有興趣的朋友可以自行編製數據做練習。

五、cut()函數

基本格式:

cut(x, breaks, ...)

#x為待分組的數值

#breaks為分組間距

不得不說,cut()函數在數據分組方面確實有點意思,它的強大之處在於能快速對數值型數據進行分組,分組間隔可以個性化定義,通過下面兩個例子來看看:

1、指定分組個數,不指定分組間距

我們將iris數據集中鳶尾花花萼長度(數值)進行分組,指定分組結果為3組,但我懶得告訴它每組應該間距多少,這種方法適用於我對數據不太了解的情況下,快速獲得數據情況。

a<-cut(iris$Sepal.Length,3)summary(a)(4.3,5.5] (5.5,6.7] (6.7,7.9] 59 71 20

從summary()函數返回的結果來看,cut()函數在得到我的指令後,自主地將Sepal.Length劃分為3組,並以(4.3,5.5] (5.5,6.7] (6.7,7.9] 為間距,每組數據包含的記錄數量也返回給我了,可以看出,大部分鳶尾花的花萼長度是介於4.3~6.7這個區間的。

2、指定分組間距

通過第一個實驗,我大概知道了數據分布的情況,現在,我希望知道,4~5,5~6,6~7,7以上這些細分區間的分布情況,代碼如下:

b<-cut(iris$Sepal.Length,c(4,5,6,7,Inf))summary(b)(4,5] (5,6] (6,7] (7,Inf] 33 56 49 12

#c(4,5,6,7,Inf)用於確定分組,其中的Inf表示正無窮大,與之相對的有-Inf,表示負無窮大。

在如此細分的情況下,我們可以看到,(5,6] 這個區間的記錄較多,相信Sepal.Length均值大概率上會落在這個區間。

不妨來算算看:

mean(iris$Sepal.Length)[1] 5.842667

六、dplyr包——group_by()+summarise()組合

1、group_by()

功能:按照指定欄位對數據進行分組

基本格式:

group_by(data, ...)

#data為原始數據

#...表示分組欄位,如果有多個分組欄位,則寫作:group_by(data,col1,col2,col3...)

2、summarise()

功能:對數據調用其它函數進行匯總操作

基本格式:

summarise(data, ...)

#data為待匯總的數據,往往為已經進行分組後的數據

#...為需要對data進行的運算,包括常用的取均值,求和,計數求最值,方差等等

如果有多個運算需要進行,則使用逗號進行分隔,如:summarise(data,operation1,operation2,...)

例1:對iris數據集按照鳶尾花種類(Species列)進行分組,並計算每種鳶尾花花萼長度(Sepal.Length)均值和花萼寬度(Sepal.Width)均值。

iris_group<-group_by(iris,Species)summarise(iris_group,mean_Sepal.Length=mean(Sepal.Length),mean_Sepal.Width=mean(Sepal.Width))# A tibble: 3 x 3 Species mean_Sepal.Length mean_Sepal.Width <chr> <dbl> <dbl>1 setosa 5.00 3.432 versicolor 5.94 2.773 virginica 6.59 2.97

例2:按照鳶尾花種類(Species)分組,並計算每組樣本數量

iris_grouped<-group_by(iris,Species)summarise(iris_grouped, n()) Species `n()` <chr> <int>1 setosa 502 versicolor 503 virginica 50

#n()函數用於計數

例3:按照鳶尾花種類(Species)分組,並計算每組鳶尾花花萼長度最小值

iris_grouped<-group_by(iris,Species)summarise(iris_grouped, min(Sepal.Length)) Species `min(Sepal.Length)` <chr> <dbl>1 setosa 4.302 versicolor 4.903 virginica 4.90

七、doBy包的summaryBy()函數

基本格式:

summaryBy(formula, data = parent.frame(),FUN = mean,...)

#formula為公式,使用公式確定分組依據以及需要進行計算的欄位,常間的格式有兩種:

格式1:col1+col2+col3~col4+col5

~符號左邊,col1+col2+col3為需要進行計算的欄位,最終會在運算結果中出現

~符號右邊,col4+col5為分組的依據

格式2:list(c("col1","col2","col3"),c("col4","col5"))

list()函數中,第一部分:c("col1","col2","col3")為需要進行計算的欄位,最終會在運算結果中出現

list()函數中,第一部分:c("col4","col5")為分組依據

注意,使用格式2時,欄位名需要加引號,否則會報錯。

#data為原始數據,需為數據框格式

#FUN = mean,表示默認對數據進行求均值計算,也可以改為所需的計算,如果有多個計算需求,可以使用c()函數進行連接,如:FUN=c(mean,var)

例:對iris數據集按照Species進行分組,並分別計算花萼長度(Sepal.Length)、花萼寬度(Sepal.Width)的均值、最大值、最小值和方差。

summaryBy(Sepal.Length+Sepal.Width~Species, data=iris, FUN=c(mean,max,min,var)) Species Sepal.Length.mean Sepal.Width.mean Sepal.Length.max1 setosa 5.004 3.428 5.82 versicolor 5.936 2.770 7.03 virginica 6.588 2.974 7.9 Sepal.Width.max Sepal.Length.min Sepal.Width.min Sepal.Length.var1 4.4 4.3 2.3 0.12406532 3.4 4.9 2.0 0.26643273 3.8 4.9 2.2 0.4043429 Sepal.Width.var1 0.143689802 0.098469393 0.10400408

也可以用以下代碼實現:

summaryBy(list(c("Sepal.Length","Sepal.Width"), "Species"), data=iris, FUN=c(mean,max,min,var))

八、describeBy()函數

功能:返回目標數據常用的描述性統計量,具體包括n(記錄數量)、mean(均值)、 sd(標準差)、median(中位數)、trimmed(截尾均值)、mad(中位數)、min(最小值)、 max(最大值)、range(極差)、skew(偏度)、kurtosis(峰度)、se(標準誤)。

基本格式:

describeBy(x, group=NULL,mat=FALSE,digits=15,...)

#x為原始數據,可為數據框、矩陣

#group=NULL表示默認無分組,如果需要對數據進行分組,可以設置group=data$colname,若有多個分組依據,則可以使用list()函數進行連接,如group=list(c(data$col1,data$col2))

#mat=FALSE表示默認輸出格式非矩陣,為列表。

#digits=15表示默認小數點精確到15位

例:計算iris數據集各鳶尾花品種的常用描述統計值

describeBy(iris,iris$Species) Descriptive statistics by group group: setosa vars n mean sd median trimmed mad min max range skew kurtosis seSepal.Length 1 50 5.00 0.35 5.0 5.00 0.30 4.3 5.8 1.5 0.13 -0.44 0.05Sepal.Width 2 50 3.43 0.38 3.4 3.42 0.37 2.3 4.4 2.1 0.04 0.60 0.05Petal.Length 3 50 1.46 0.17 1.5 1.46 0.15 1.0 1.9 0.9 0.10 0.65 0.02Petal.Width 4 50 0.25 0.11 0.2 0.24 0.00 0.1 0.6 0.5 1.18 1.26 0.01Species* 5 50 NaN NA NA NaN NA Inf -Inf -Inf NA NA NA------------------------------------------------------------------------- group: versicolor vars n mean sd median trimmed mad min max range skew kurtosis seSepal.Length 1 50 5.94 0.52 5.90 5.94 0.52 4.9 7.0 2.1 0.10 -0.69 0.07Sepal.Width 2 50 2.77 0.31 2.80 2.78 0.30 2.0 3.4 1.4 -0.34 -0.55 0.04Petal.Length 3 50 4.26 0.47 4.35 4.29 0.52 3.0 5.1 2.1 -0.57 -0.19 0.07Petal.Width 4 50 1.33 0.20 1.30 1.32 0.22 1.0 1.8 0.8 -0.03 -0.59 0.03Species* 5 50 NaN NA NA NaN NA Inf -Inf -Inf NA NA NA------------------------------------------------------------------------- group: virginica vars n mean sd median trimmed mad min max range skew kurtosis seSepal.Length 1 50 6.59 0.64 6.50 6.57 0.59 4.9 7.9 3.0 0.11 -0.20 0.09Sepal.Width 2 50 2.97 0.32 3.00 2.96 0.30 2.2 3.8 1.6 0.34 0.38 0.05Petal.Length 3 50 5.55 0.55 5.55 5.51 0.67 4.5 6.9 2.4 0.52 -0.37 0.08Petal.Width 4 50 2.03 0.27 2.00 2.03 0.30 1.4 2.5 1.1 -0.12 -0.75 0.04Species* 5 50 NaN NA NA NaN NA Inf -Inf -Inf NA NA NA

數據按照iris$Species列進行分組,在分組基礎上進行描述統計量的計算,至於每個統計指標的統計意義,我們後續在統計篇會進行介紹。

(原創內容,轉載請註明出處。)


推薦閱讀:

TAG:R編程語言 | 數據處理 | 分組 |