plyr是莱斯大学的
Hardley
Wickham
大神的杰作之一(
如果你没听说过这个人——ggplot也是他开发的。。。
),他以split - apply - combine (SAC)的模式和理念自己写了这么一个函数包。
plyr函数系列的命名方式很简单,后缀-ply是通用名,前缀分两个部分,第一个字母是input的数据结构,第二个字母是output的数据结构。一共有六种input和output:
a: array
l: list
d: data.frame
m: multiple inputs
r: repeat multiple times
_: nothing
考虑到"_"无法做input,"m"和"r"无法做output,plyr包实际上一共提供了20个直观的函数,可以实现几乎所有用到SAC概念的数据处理模式。
回想一下这个前两篇写到的*apply一族的函数,a,l,d开头的三行plyr函数大概可以对应apply, lapply和sapply一类;而m,r开头的两行大概对应的是mapply和replicate函数。
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
1. a*ply: 输入项为array的函数
函数主要的参数有:
.data, .
margins 以及
.fun。作用跟apply里的X, MARGIN和FUN一样。
先看看自带的例子,这里用到了plyr包里的ozone数据
> str(ozone)
num [1:24, 1:24, 1:72] 260 258 258 254 252 252 250 248 248 248 ...
- attr(*, "dimnames")=List of 3
..$ lat : chr [1:24] "-21.2" "-18.7" "-16.2" "-13.7" ...
..$ long: chr [1:24] "-113.8" "-111.3" "-108.8" "-106.3" ...
..$ time: chr [1:72] "1" "2" "3" "4" ...
- attr(*, "dimnames")=List of 3
..$ lat : chr [1:24] "-21.2" "-18.7" "-16.2" "-13.7" ...
..$ long: chr [1:24] "-113.8" "-111.3" "-108.8" "-106.3" ...
..$ time: chr [1:72] "1" "2" "3" "4" ...
这是一个24 x 24 x 72的
三维数组,三个维度分别是维度、经度和时间。记录了1995到2000年之间每个月的臭氧平均值。
> head(aaply(ozone, 2, mean))
-113.8 -111.3 -108.8 -106.3 -103.8 -101.3
268.5637 268.5509 267.9213 267.1319 267.1748 266.9769
-113.8 -111.3 -108.8 -106.3 -103.8 -101.3
268.5637 268.5509 267.9213 267.1319 267.1748 266.9769
> head(alply(ozone, 2, mean))
$`1`
[1] 268.5637
$`2`
[1] 268.5509
$`3`
[1] 267.9213
$`4`
[1] 267.1319
$`5`
[1] 267.1748
$`6`
[1] 266.9769
> head(adply(ozone, 2, mean))
long V1
1 -113.8 268.5637
2 -111.3 268.5509
3 -108.8 267.9213
4 -106.3 267.1319
5 -103.8 267.1748
6 -101.3 266.9769
$`1`
[1] 268.5637
$`2`
[1] 268.5509
$`3`
[1] 267.9213
$`4`
[1] 267.1319
$`5`
[1] 267.1748
$`6`
[1] 266.9769
> head(adply(ozone, 2, mean))
long V1
1 -113.8 268.5637
2 -111.3 268.5509
3 -108.8 267.9213
4 -106.3 267.1319
5 -103.8 267.1748
6 -101.3 266.9769
上面的3个例子中,a*ply返回的是以经度为基准(第二维度),不同维度和时间点上的臭氧平均值。
这里aaply将结果压缩成一维数组,也就是一个vector(可以用is.vector来检查),vector的names即24个不同的经度点。alply返回一个list,adply返回一个data frame。
plyr包还提供了一系列小函数,有很多可以拿出来使用,如each(),用于合并多个函数供*ply返回,对数据处理的方便性不言而喻。
(用
> library(help = plyr)
可以查看这个包里附带的所有函数。)
> head(aaply(ozone, 2, each(mean, min, max)))
long mean min max
-113.8 268.5637 232 390
-111.3 268.5509 232 368
-108.8 267.9213 232 368
-106.3 267.1319 234 362
-103.8 267.1748 234 354
-101.3 266.9769 232 354
long mean min max
-113.8 268.5637 232 390
-111.3 268.5509 232 368
-108.8 267.9213 232 368
-106.3 267.1319 234 362
-103.8 267.1748 234 354
-101.3 266.9769 232 354
这些函数也可以与其他plyr包外的函数结合使用:
> apply(ozone, 2, each(mean, min, max))[,1:6]
long
-113.8 -111.3 -108.8 -106.3 -103.8 -101.3
mean 268.5637 268.5509 267.9213 267.1319 267.1748 266.9769
min 232.0000 232.0000 232.0000 234.0000 234.0000 232.0000
max 390.0000 368.0000 368.0000 362.0000 354.0000 354.0000
long
-113.8 -111.3 -108.8 -106.3 -103.8 -101.3
mean 268.5637 268.5509 267.9213 267.1319 267.1748 266.9769
min 232.0000 232.0000 232.0000 234.0000 234.0000 232.0000
max 390.0000 368.0000 368.0000 362.0000 354.0000 354.0000
*ply一系中还有一些其它参数,如.progress,用于创建进度条,当数据量大的时候可以通过进度条来查看*ply的处理进度。当数据少的时候,可以不考虑使用它,因为这个时候.progress反而会拖慢计算进度。
> l_ply(1:100000, identity, .progress = progress_text(char = "."))
|...................................................................| 100%
|...................................................................| 100%
自己在做数据处理的时候可根据不同需求调用相应的参数,一般来说只需按各参数默认值运行。
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
-+-
说到这里,我们需要理解一下*ply系列函数的在
分解
不同维度input时的逻辑(在这个系列的第一篇里有提到过Hardley大神写过的一篇文章,The Split-Apply-Combine Strategy for Data Analysis,下面的解释是直接从这篇文章里提取出来的)。
对于2维数组,我们有三种方法分解,.margins = 1,按行分解;.margins = 2,按列分解;.margins = c(1,2),对每个单元格逐个运算。
当input扩大到三维时,我们有七种方法去分解。可以按面分解,此时只需要明确是哪一面即可,如.margins = 1;可以按线分解,即按三个维度上任意两个维度的交界线分解,如.margins = c(1,2);也
可以逐个单元进行分解,即以三个维度的交界点来进行分解,.margins = 1:3。
扩展到更多维度,也按照此种方法类推。