在數據分析中,整理數據的本質可以歸納為:對數據進行分割(Split),然后應用(Apply)某些處理函數,最后將結果重新組合(Combine)成所需的格式返回,簡單描述為:Split - Apply - Combine,各個步驟的作用是:
- Split:把要處理的數據分割成小片斷,常用的函數是split(),subset();
- Apply:對每個小片斷獨立進行操作,常用的是apply家族函數,plyr包核心函數;
- Combine:把片斷重新組合,常用的函數是unlist()函數。
這個過程可以通過Base包的apply家族函數來實現,apply家族函數包括了apply、sapply、lapply、tapply、aggregate等,可以應用於數據分析的各個階段。
plyr包是apply家族函數的升級版本,使用plyr包可以實現:在一個函數內同時完成“Split - Apply - Combine”,並且,plyr包實現R類型(vector, list, data.frame)之間的分組變換,基本上可以取代Base包中的apply家族函數。
plyr包對核心函數的命名采用統一的格式:**ply,所有的函數名都由5個字符組成,且最后三個字符是ply,函數名的第一個字符代表輸入數據的類型,第二個字符代表輸出數據的類型,R類型的簡寫是:
- d:data.frame
- l:list
- a:array,vector,matrix
- r:代表replicate,重復多次
- m:多輸入
- _:舍棄輸出結果
這種統一的命名格式,使得plyr包的函數更容易記憶和使用,但是,plyr包不是預裝於R語言中,使用之前,需要加載和引用plyr包:
install.packages("plyr") library(plyr)
一,plyr包函數
plyr包用於在R中實現split-apply-combine的模式,這中模式在數據分析中是極其常見的,通過把數據分解為小的分片,然后在分片上做操作,最后把結果組合在一起,以解決復雜的分析問題。因此,當遇到復雜的數據分析問題時,一般都需要把復雜的問題分組,然后在每個分組上做操作,最終把每個分組上的結果組合到一起。plyr包的函數很多,除了**ply的核心函數之外,還有一些輔助函數,在處理數據時,都十分有用。
1,ddply
plyr包中最常用的函數是ddply()函數,該函數對數據框進行操作,對每一行調用一個函數,並返回數據框類型:
ddply(.data, .variables, .fun = NULL, ...)
參數注釋:
- .data:函數處理的數據框;
- .variables:要進行拆分的變量名稱,傳遞變量的格式是: .(col_name),就是把進行分組的變量名包含在.()中;
- .fun:應用到每行的函數
- ...:傳遞到fun的其他參數
對於參數fun,有兩種賦值方式:
第一種: 如果使用colwise()函數,那么這使ddply函數把參數fun應用於每一列,除了參數.variable指定的數據列之外,例如:
ddply(diamonds,.(color),colwise(mean))
第二種: 使用summarize函數對指定的列執行操作,更為靈活,例如:
> ddply(diamonds,.(color),summarize,avg_price=mean(price),avg_carat=mean(carat)) color avg_price avg_carat 1 D 3169.954 0.6577948 2 E 3076.752 0.6578667
.....
2,each函數
plyr包的each()函數,能夠把多個函數整合到一個函數中,每一個函數必須只能返回一個數值:
each(...)
使用each()函數,可以使函數aggregate()同時調用多個函數:
> aggregate(cbind(price,carat)~cut+color,diamonds,each(mean,sum)) cut color price.mean price.sum carat.mean carat.sum 1 Fair D 4291.061 699443.000 0.9201227 149.9800000 2 Good D 3405.382 2254363.000 0.7445166 492.8700000 ......
3,rename函數
按照名字對變量重命名:
rename(x, replace, warn_missing = TRUE, warn_duplicated = TRUE)
參數注釋:
- x: 重命名的對象
- replace:命名的向量,格式是:c(new_name=old_name,...)
使用rename函數對數據框的變量進行重命名,例如:
rename(mtcars, c("disp" = "displacement"))
4,arrange函數
按照數據框的變量對數據框排序,注意,arrange()函數不會保留行名稱(row.names)
arrange(df, ...)
例如,按照變量cyl和disp,對數據框mtcars進行排序:
# sort mtcars data by cylinder and displacement mtcars[with(mtcars, order(cyl, disp)), ] # Same result using arrange: no need to use with(), as the context is implicit arrange(mtcars, cyl, disp)
5,mutate函數
對數據框進行轉換,或增加新的變量,或替換已經存在的變量,該函數和transfrom函數十分相似,不過,mutate()函數是遞進式的,這使得后期的轉換可以使用早期創建的變量。
# Things transform can't do mutate(airquality, Temp = (Temp - 32) / 1.8, OzT = Ozone / Temp)
6,name_rows函數
在設計時,沒有plyr函數會保留行名稱(row names)。如果想保留行名稱,可以使用name_rows()把行名稱轉換為顯式的列值,在執行為相應的plyr操作之后,再使用name_rows把列值轉換為行名稱。
name_rows(df)
參數df :數據框對象,擁有 rownames,或者顯式的列名 .rownames
二,拆分-應用-組合
在R語言中,分組聚合可以通過三步實現:拆分-應用-合並(Split-Apply-Combine)。例如,對玩家的游戲成績進行統計和分析,創建示例數據:
> players_scores <- data.frame( player=rep(c('Tom','Dick','Jim'),times=c(2,5,3)), score=round(runif(10,1,100),-1) )
1,分組數據
計算每個玩家的平均得分,首先對玩家分組,需要用到split()函數,按照特定的字段對數據進行分組:
split(x, f, drop = FALSE, ...)
參數注釋:
- x:數據框或向量,是被分組的數據;
- f:因子類型,按照f對x進行分組;
函數的返回值是一個列表對象,每一個列表項都是包含分組數據的向量。
例如,split(score,player)函數的作用是按照player字段把數據框中的score拆分成一組,也就是說,player 相同的score是同一個分組,填充到同一個列表項中:
> (scores_by_player <- with(players_scores,split(score,player))) $Dick [1] 70 20 30 70 70 $Jim [1] 80 90 50 $Tom [1] 80 90
2,應用函數
當數據分割之后,對每個分組計算平均分。使用lapply()函數,對於每個列表項,應用mean()函數,計算單個列表項的平均值,例如:
list_mean_by_player <- lapply(scores_by_player,mean)
3,組合數據
組合數據是為了顯示數據,在顯示最終的數據時,通常把列表轉換為向量。lapply()函數返回的結果是一個列表對象,每一個列表項都是一個向量,因此可以使用unlist()函數,把列表轉換為向量,例如:
> unlist(list_mean_by_player) Dick Jim Tom 52.00000 73.33333 85.00000
三,使用apply家族函數實現分組聚合
在apply家族函數中,每個函數都用於特定的數據類型:
- apply函數只能用於矩陣,
- lapply函數能夠用於向量和列表(list),其工作原理是把一個函數應用於一個列表中的每個元素上,並且把結果作為列表返回;
- sapply處理列表,返回向量。
- mapply函數,把調用的函數應用到多個列表的每一個元素中。
- tapply函數用於分組聚合運算,在研究數據時,有時需要對數據按照特定的字段進行分組,然后統計各個分組的數據,這就是SQL語法中的分組聚合。
在數據分析中,使用Base包實現”拆分-應用-合並“ 顯得十分繁瑣,可以使用tapply()函數一次完成所有的三個步驟,一氣呵成:
with(players_scores,tapply(score,player,mean))
tapply()函數常用的參數共有三個,第一個參數是數據框對象或向量,第二個參數是因子列表,也就是分組字段,第三個參數是指對單個分組應用的函數:
tapply(X, INDEX, FUN = NULL, ...)
by()函數和aggregate()函數是tapply()函數的包裝函數,功能相同,接口稍微不同。
by(data, INDICES, FUN, ..., simplify = TRUE)
aggregate(x, by, FUN, ..., simplify = TRUE, drop = TRUE)
四,使用plyr包實現分組聚合
函數daply的作用是分割數據框,對每個分組應用聚合函數,最后把每個分組的聚合值組合起來,以數組的形式返回:
daply(.data, .variables, .fun = NULL, ...)
參數注釋:
- .data:數據框,存儲用於分析的數據;
- .variables:分組字段,指定分組字段的格式是 .(col_name);
- .fun:應用於每個分組的函數,有兩種方式,上文有詳細介紹。
為了計算每個player的平均得分,可以使用daply()函數,例如,
unlist(daply(players_scores,.(player),summarize,varScore=mean(score)))
在示例中,daply()函數返回的類型是list,通過unlist()函數轉換為向量。至於為什么返回的是list,而不是數組,我也很疑惑。
參考文檔: