R语言数据加工厂——plyr包使用
阅读原文时间:2023年07月08日阅读:2

plyr包是Hadley Wickham大神为解决split – apply – combine问题而写的一个包,其动机在与提供超越for循环和内置的apply函数族的一个一揽子解决方案。使用plyr包可以针对不同的数据类型,在一个函数内同时完成split – apply – combine三个步骤。plyr 的功能已经远远超出数据整容的范围,Hadley在plyr中应用了split-apply-combine的数据处理哲学,即:先将数据分离,然后应用某些处理函数,最后将结果重新组合成所需的形式返回。某些人士喜欢用“揉”来表述这样的数据处理;“揉”把数据当面团捣来捣去。

一、plyr包设计思想

在数据分析中,有许多问题可以由类似的类型和方法步骤解决,可称之为模式,设计模式或者分析模式。下面要讨论的是数据转换的一个常用模式:split – apply – combine。

1.1 plyr包的特点

for的显式循环缺点很明显,代码长,易出错,也难以并行化;拜R语言的向量计算特点所赐,在R当中,大多数问题不需要用显示循环方式,而代之以base包中的apply函数族及其它的一些函数,直接对向量,数组,列表和数据框实现循环的操作。Hadley Wickham大神觉得apply族还是不够简洁,所以开发了pylr包,以更少的代码来解决split – apply – combine问题。

1.2 plyr包的split-apply-combine 模式

大型数据集通常是高度结构化的,结构使得我们可以按不同的方式分组,有时候我们需要关注单个组的数据片断,有时需要聚合不同组内的信息,并相互比较。因此对数据的转换,可以采用split-apply-combine模式来进行处理:

    split:把要处理的数据分割成小片断;

    apply:对每个小片断独立进行操作;

    combine:把片断重新组合。

1.3 split函数

R当中是split( ),*apply( ),aggregate( )…,以及plyr包。split函数这个步骤是由split( ),subset( )等等函数完成的。下面主要介绍split这个函数。函数split可以按照分组因子,把向量,矩阵和数据框进行适当的分组。它的返回值是一个列表,代表分组变量每个水平的观测。这个列表可以使用sapply,lappy进行处理(apply-combine步骤),得到问题的最终结果。

split( )的基本用法是:group <- split(X,f),其中X是待分组的向量,矩阵或数据框。f是分组因子。split还有一个逆函数,unsplit,可以让分组完好如初。在base包里和split功能接近的函数有cut(对属性数据分划),strsplit(对字符串分划)以及subset(对向量,矩阵或数据框按给定条件取子集)等。

 library(MASS)
 #使用Cars93数据集,利用其中的Origin变量(两个水平),对Price变量分组
 g<-split(Cars93$Price,Cars93$Origin)
 #计算组内均值
 sapply(g,mean)


 g[[1]]
[1] 15.7 20.8 23.7 26.3 34.7 40.1 13.4 11.4 15.1 15.9 16.3 16.6 18.8 38.0 18.4 15.8 29.5
[18]  9.2 11.3 13.3 19.0 15.6 25.8 12.2 19.3  7.4 10.1 11.3 15.9 14.0 19.9 20.2 20.9 34.3
[35] 36.1 14.1 14.9 13.5 16.3 19.5 20.7 14.4  9.0 11.1 17.7 18.5 24.4 11.1

1.4 aggregate函数

这个函数的功能比较强大,它首先将数据进行分组(按行),然后对每一组数据进行函数统计,最后把结果组合成一个比较nice的表格返回。根据数据对象不同它有三种用法,分别应用于数据框(data.frame)、公式(formula)和时间序列(ts)。

aggregate(x, by, FUN, ..., simplify = TRUE)
aggregate(formula, data, FUN, ..., subset, na.action = na.omit)
aggregate(x, nfrequency = 1, FUN = sum, ndeltat = 1, ts.eps = getOption("ts.eps"), ...)


attach(mtcars)
aggregate(mtcars, by=list(cyl), FUN=mean)
aggregate(mtcars, by=list(cyl, gear), FUN=mean)
aggregate(cbind(mpg,hp) ~ cyl+gear, FUN=mean)
detach(mtcars)


aggregate(cbind(mpg,hp) ~ cyl+gear, FUN=mean)
  cyl gear    mpg       hp
1   4    3 21.500  97.0000
2   6    3 19.750 107.5000
3   8    3 15.050 194.1667
4   4    4 26.925  76.0000
5   6    4 19.750 116.5000
6   4    5 28.200 102.0000
7   6    5 19.700 175.0000
8   8    5 15.400 299.5000

二、plyr包函数命名规则

apply族函数是R语言中很有特色的一类函数,包括了apply、sapply、lapply、tapply、aggregate等等。这一类函数本质上是将数据进行分割、计算和整合。它们在数据分析的各个阶段都有很好的用处。例如在数据准备阶段,我们可以按某个标准将数据分组,然后获得各组的统计描述。或是在建模阶段,为不同组的数据建立模型并比较建模结果。apply族函数与Google提出的mapreduce策略有着一致的思路。因为mapreduce的思路也是将数据进行分割、计算和整合。只不过它是将分割后的数据分发给多个处理核心进行运算。如果你熟悉了apply族函数,那么将数据转为并行运算是轻而易举的事情。plyr包则可看作是apply族函数的扩展,使之更容易运用,功能更为强大。

plyr包的主函数是**ply形式的,其中首字母可以是(d、l、a),第二个字母可以是(d、l、a、_),不同的字母表示不同的数据格式,d表示数据框格式,l表示列表,a表示数组,_则表示没有输出。第一个字母表示输入的待处理的数据格式,第二个字母表示输出的数据格式。例如ddply函数,即表示输入一个数据框,输出也是一个数据框。

2.1 命名规则

plyr的基本函数集

array

data frame

list

nothing

array

aaply

adply

alply

a_ply

data frame

daply

ddply

dlply

d_ply

list

laply

ldply

llply

l_ply

n replicates

raply

rdply

rlply

r_ply

function

arguments

maply

mdply

mlply

m_ply

命名规则:前三行是基本类型。根据输入类型和输出类型:a=array,d=data frame,l=list,_ 表示输出放弃。第一个字母表示输入,第2个字母表示输出。后两行是对应apply族的replicates和mapply这两个函数,分别表示n次重复和多元函数参数的情况,第2个字母还是表示输出类型。从命名特点来看,我们不需要列出每个函数的情况了,只要从输入和输出两方面分别讨论即可。

2.2 参数说明

a*ply(.data, .margins, .fun, …, .progress = "none");d*ply(.data, .variables, .fun, …, .progress = "none");l*ply(.data, .fun, …, .progress = "none")

这些函数有两到三个主要的参数,依赖于输入的类型:

.data是我们要用来分片-计算-合并的;参数.margins或者.variables****.fun表示用来处理的函数,其它更多的参数(…)是传递给处理函数的;参数.progress用来控制显示一个进度条。

2.3 参数输入

输入类型有三种,每一种类型给出了如何进行分片的不同方法。

   a*ply( ):数组(包括矩阵和向量)按维数分为低维的片。

   d*ply( ):数据框被变量组合分成子集。

   l*ply( ):列表的每个元素就是一个分片。

  因此,对输入数据集的分片,不是取决于数据的结构,而是取决于所采用的方法。

三种类型各自的特点:

  (a): 输入数组( a*ply )

  a*ply的分片特点在于.margins参数,它和apply很相似。对于2维数组, .margins 可以取1,2,或者c(1:2),对应2维数组的3种分片方式。

.margins = 1: Slice up intorows. • .margins = 2: Slice up intocolumns. • .margins = c( 1, 2): Slice up intoindividual cells.

  对于2维数组,则有3种分片方式:

  对于3维数组,则有7种分片方式:

  .margins对应更高维的情况,可能会面临一种爆发式的组合。

  (b)输入数据框( d*ply )

  使用d*ply时,需要特别指定分组所用的变量或变量函数,它们会被首先计算,然后才是整个数据框。

有下面几种指定方式:

.(var1)。按照变量var1的值来对数据框分组 • 多重变量 .(a,b, c)。将按照三个变量的交互值来分组。

这种形式输出的时候,有点复杂。如果输出为数组,则数组会有三个维度,分别以 a,b,c 的值作为维数名。如果输出为数据框,将会包含 a,b,c 取值的三个额外的列。如果输出为列表,则列表元素名为按周期分割的 a,b,c 的值。

作为列名的字符向量: c( "var1", "var2")。 • 公式~ var1 + var2。

  (c) 输入列表( l*ply )

   l * ply 不需要描述如何分片的函数,因为列表本身就是按照元素的分划。使用l * ply相当于a*ply作用于一维数组的效果。

三、plyr包应用

这里使用plyr包来进行数据处理,重点在于plyr包的应用,而不是深入地探讨数据的分析。此例来源于 Wickham 的论文[Hadley Wickham :The Split-Apply-Combine Strategy for Data Analysis Journal of Statistical Software,April 2011, Volume 40, Issue 1.],布拉德.皮特在《点球成金》里用数据方法发掘棒球运动员的价值。plyr包的baseball数据集包括了1887-2007年间1228位美国职业棒球运动员15年以上的击球记录。

library(plyr)
data(baseball)
dim(baseball)         #数据集和数据结构


baberuth <- subset(baseball, id== "ruthba01")
baberuth <- transform(baberuth, cyear = year - min(year) + 1)
baseball <- ddply( baseball, .( id), transform, cyear = year - min( year) + 1)


library(ggplot2)
cyear <- baberuth$cyear
ra <- (baberuth$rbi)/(baberuth$ab)
a <- data.frame(cyear,ra)
p <- ggplot(a,aes(cyear,ra))
p + geom_line
qplot(cyear, rbi / ab, data= baberuth, geom = "line")

四、plyr包其他函数

plyr包还有几个简单的函数:arrange, mutate,summarise, join, match_df, rename, round_any, count. 这几个函数在plyr包精华ddply系列函数中有不同程度的应用。

4.1 arrange函数

语法:arrange(data.frame,colnames|desc(colnames))),用于对数据框的一列或几列排序

set.seed(124)
data <- data.frame(A=LETTERS[sample(5,replace=TRUE)],B=letters[sample(5,replace=TRUE)],C=round(runif(5,0,1),3),D=sample(100,1:5))
#对数据框data先按A升序排列在按D降序排列
arrange(data,A,desc(D))

4.2 mutate

语法:mutate(data.frame,newcolname=expression(oldcolname)),用于对数据框中的列进行某种函数运算生成新列。

mutate(data,AD=paste(A,D,sep=''),CD=C*100+D,CCD=CD+C)

4.3 summarise

语法:summarise(data.frame,newcolname=expression(oldcolname)),类似与mutate,但创造新的数据框。

summarise(data,AD=paste(A,D,sep=''),CD=C*100+D,CCD=CD+C)

4.4 colwise

语法:colwise(.fun, .cols = true, …) #全部

     catcolwise(.fun, …)   #非数值型

     numcolwise(.fun, …)   #数值型

用于迭代地对数据框中的每一列运行函数fun。

nmissing <- function(x) sum(is.na(x))
colwise(nmissing)(baseball)
numcolwise(nmissing)(baseball)
catcolwise(nmissing)(baseball)

4.5 rename

语法:rename(x, replace, warn_missing = TRUE, warn_duplicated = TRUE)

x <- c("a" = 1, "b" = 2, d = 3, 4)
x <- rename(x, replace = c("d" = "c"))

4.6 count

语法:count(data.frame, vars = NULL, wt_var = NULL),对数据框进行计数操作,类似于table

count(baseball[1:100,], vars = "id")

参考文献

(R语言中plyr包 )[https://i.cnblogs.com/posts/edit]