R语言小白学习笔记6—分组操作

笔记链接

学习笔记1—R语言基础.
学习笔记2—高级数据结构.
学习笔记3—R语言读取数据.
学习笔记4—统计图.
学习笔记5—编写R语言函数和简单的控制循环语句.

学习笔记6—分组操作

数据分析中数据处理会占据大部分的工作时间,经常需要“分离—应用—合并”的操作,R语言中有许多不同的迭代数据的方法。

6.1 apply函数族

6.1.1apply函数

apply函数只能用于矩阵,即所有元素必须是同类型的数据。如果在其他对象(如数据框)上使用,apply函数会先将其转化为矩阵。

apply函数第一个参数是操作的矩阵对象,第二个参数是应用函数的维度,1代表对行进行操作,2代表对列,第三个参数是处理数据所调用的函数

例:求矩阵行或者列的和

> theMatrix <- matrix(1:9, nrow=3)
> apply(theMatrix, 1, sum)
[1] 12 15 18
> theMatrix
     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9
> apply(theMatrix, 2, sum)
[1]  6 15 24

即使向量中只有一个元素是NA,它的求和结果也是NA,所以可以通过设置参数na.rm=TRUE,这样会把NA元素去掉再相加

例:

> theMatrix[2, 1] <- NA
> theMatrix
     [,1] [,2] [,3]
[1,]    1    4    7
[2,]   NA    5    8
[3,]    3    6    9
> apply(theMatrix, 1, sum)
[1] 12 NA 18
> apply(theMatrix, 1, sum, na.rm=TRUE)
[1] 12 13 18

6.1.2 lapply和sapply函数

lapply函数的工作原理是将某个函数应用到一个列表的每个元素,并将结果作为list返回

> theList <- list(A=matrix(1:9, 3), B=1:5, C=matrix(1:4, 2), D=2)
> lapply(theList, sum)
$A
[1] 45

$B
[1] 15

$C
[1] 10

$D
[1] 2

sapply函数可以将结果作为一个向量返回

> sapply(theList, sum)
 A  B  C  D 
45 15 10  2 

6.1.3 mapply函数

mapply函数能够将某个函数应用到多个列表的每个元素

这种情况我们通常习惯用循环,但mapply函数更简单。

> firstList <- list(A=matrix(1:16, 4), B=matrix(1:16, 2), C=1:5)
> secondList <- list(A=matrix(1:16, 4), B=matrix(1:16, 8), C=15:1)
> mapply(identical, firstList, secondList)
    A     B     C 
 TRUE FALSE FALSE 
#identical是判断其是否相同
> simpleFunc <- function(x, y)
+ {
+     NROW(x) + NROW(y)
+ }
> mapply(simpleFunc, firstList, secondList)
 A  B  C 
 8 10 20 

6.2 aggregate函数

aggregate函数支持聚合和分组操作,调用aggregate函数的方式很多,最方便:formula(翻译:公式)

formula包括被符号“~”分开的两部分,左侧是待操作的变量,右侧是以其为依据进行分组的一个或多个变量

例:这里使用ggplot2包中的diamonds数据

> data(diamonds, package = 'ggplot2')
> head(diamonds)
  carat       cut color clarity depth table price    x
1  0.23     Ideal     E     SI2  61.5    55   326 3.95
2  0.21   Premium     E     SI1  59.8    61   326 3.89
3  0.23      Good     E     VS1  56.9    65   327 4.05
4  0.29   Premium     I     VS2  62.4    58   334 4.20
5  0.31      Good     J     SI2  63.3    58   335 4.34
6  0.24 Very Good     J    VVS2  62.8    57   336 3.94
     y    z
1 3.98 2.43
2 3.84 2.31
3 4.07 2.31
4 4.23 2.63
5 4.35 2.75
6 3.96 2.48

之后计算cut各种类型的平均价格

aggregate函数第一个参数是formula,它指定price变量按照cut变量的值分组,第二个参数是操作的数据,第三个参数是应用于数据的函数

> aggregate(price ~ cut, diamonds, mean)
        cut    price
1      Fair 4358.758
2      Good 3928.864
3 Very Good 3981.760
4   Premium 4584.258
5     Ideal 3457.542

根据多个变量分组数据:

> aggregate(price ~ cut + color, diamonds, mean)
         cut color    price
1       Fair     D 4291.061
2       Good     D 3405.382
3  Very Good     D 3470.467
4    Premium     D 3631.293
5      Ideal     D 2629.095
6       Fair     E 3682.312
7       Good     E 3423.644
8  Very Good     E 3214.652
9    Premium     E 3538.914
10     Ideal     E 2597.550
11      Fair     F 3827.003
12      Good     F 3495.750
13 Very Good     F 3778.820
14   Premium     F 4324.890
15     Ideal     F 3374.939
16      Fair     G 4239.255
17      Good     G 4123.482
18 Very Good     G 3872.754
19   Premium     G 4500.742
20     Ideal     G 3720.706
21      Fair     H 5135.683
22      Good     H 4276.255
23 Very Good     H 4535.390
24   Premium     H 5216.707
25     Ideal     H 3889.335
26      Fair     I 4685.446
27      Good     I 5078.533
28 Very Good     I 5255.880
29   Premium     I 5946.181
30     Ideal     I 4451.970
31      Fair     J 4975.655
32      Good     J 4574.173
33 Very Good     J 5103.513
34   Premium     J 6294.592
35     Ideal     J 4918.186

如果要聚合两个变量,可以用cbind函数

> aggregate(cbind(price, carat) ~ cut, diamonds, mean)
        cut    price     carat
1      Fair 4358.758 1.0461366
2      Good 3928.864 0.8491847
3 Very Good 3981.760 0.8063814
4   Premium 4584.258 0.8919549
5     Ideal 3457.542 0.7028370

但aggregate函数运行很慢,可以用plyr、dplyr和data.table以更快运行。

6.3 plyr包

plyr包的核心是ddply、llply和ldply等函数,其后三个字母总是ply,第一个字母表示输入数据类型,第二个字母表示输出数据类型

6.3.1 ddply函数

ddply函数输入类型为数据框根据指定变量对数据集分类并进行相应的运算,然后返回一个数据框。

例:这次引用plyr包中的数据集baseball

> library(plyr)
> head(baseball)
           id year stint team lg  g  ab  r  h X2b X3b hr
4   ansonca01 1871     1  RC1    25 120 29 39  11   3  0
44  forceda01 1871     1  WS3    32 162 45 45   9   4  0
68  mathebo01 1871     1  FW1    19  89 15 24   3   1  0
99  startjo01 1871     1  NY2    33 161 35 58   5   1  1
102 suttoez01 1871     1  CL1    29 128 35 45   3   7  3
106 whitede01 1871     1  CL1    29 146 40 47   6   5  1
    rbi sb cs bb so ibb hbp sh sf gidp
4    16  6  2  2  1  NA  NA NA NA   NA
44   29  8  0  4  0  NA  NA NA NA   NA
68   10  2  1  2  0  NA  NA NA NA   NA
99   34  4  2  3  0  NA  NA NA NA   NA
102  23  3  1  1  0  NA  NA NA NA   NA
106  21  2  2  4  1  NA  NA NA NA   NA

现在对数据进行处理,目的是计算每个球员在其职业生涯的OBP指标(上垒率)

计算公式:OBP=(H+BB+HBP)/(AB+BB+HBP+SF)

1954年前,SF(高飞牺牲打)算作牺牲打的一部分,所以1954年前的SF设为0.

其次原始数据许多缺失部分,我们也将其设为0.

另外,剔除掉一个赛季小于50打数的球员数据

> baseball$sf[baseball$year < 1954] <- 0
> any(is.na(baseball$sf))
[1] FALSE
> baseball$hbp[is.na(baseball$hbp)] <- 0
> any(is.na(baseball$hbp))
[1] FALSE
> baseball <- baseball[baseball$ab >= 50,]
计算每个球员在指定年份的OBP只需进行向量操作:
> baseball$OBP <- with(baseball, (h + bb + hbp) / (ab + bb + hbp + sf))
> tail(baseball)
             id year stint team lg   g  ab  r   h X2b
89499 claytro01 2007     1  TOR AL  69 189 23  48  14
89502 cirilje01 2007     1  MIN AL  50 153 18  40   9
89521 bondsba01 2007     1  SFN NL 126 340 75  94  14
89523 biggicr01 2007     1  HOU NL 141 517 68 130  31
89530 ausmubr01 2007     1  HOU NL 117 349 38  82  16
89533  aloumo01 2007     1  NYN NL  87 328 51 112  19
      X3b hr rbi sb cs  bb  so ibb hbp sh sf gidp
89499   0  1  12  2  1  14  50   0   1  3  3    8
89502   2  2  21  2  0  15  13   0   1  3  2    9
89521   0 28  66  5  0 132  54  43   3  0  2   13
89523   3 10  50  4  3  23 112   0   3  7  5    5
89530   3  3  25  6  1  37  74   3   6  4  1   11
89533   1 13  49  3  0  27  30   5   2  0  3   13
            OBP
89499 0.3043478
89502 0.3274854
89521 0.4800839
89523 0.2846715
89530 0.3180662
89533 0.3916667

这里使用了新函数with,该函数可以对指定数据框的列进行操作,且无需每次操作都指明数据框的名称。

为了计算每个球员在其整个职业生涯的OBP指标,我们需要先对分子上的变量进行求和,再除以分母上的变量之和。(指OBP公式的分子分母)

首先定义一个函数完成上述功能,再使用ddply函数进行计算。

> obp <- function(data)
+ {
+     c(OBP=with(data, sum(h + bb + hbp) / sum(ab + bb + hbp + sf)))
+ }
> careerOBP <- ddply(baseball, .variables = "id", .fun = obp)
> careerOBP <- careerOBP[order(careerOBP$OBP, decreasing = TRUE),]
> head(careerOBP, 10)
            id       OBP
1089 willite01 0.4816861
875   ruthba01 0.4742209
658  mcgrajo01 0.4657478
356  gehrilo01 0.4477848
85   bondsba01 0.4444622
476  hornsro01 0.4339068
184   cobbty01 0.4329655
327   foxxji01 0.4290509
953  speaktr01 0.4283386
191  collied01 0.4251246

6.3.2 plyr的辅助函数

plyr包有很多辅助函数,如:

each函数,能够在使用像aggregate这样的函数时调用多个函数。缺点:使用each函数时不能为调用的函数添加额外的参数。

> aggregate(price ~ cut, diamonds, each(mean, median))
        cut price.mean price.median
1      Fair   4358.758     3282.000
2      Good   3928.864     3050.500
3 Very Good   3981.760     2648.000
4   Premium   4584.258     3185.000
5     Ideal   3457.542     1810.000

idata.frame函数,该函数创建了一个数据框的引用地址,使得取子集更快,并且能够更有效地利用内存。

哈哈,这里我用例子试了一下发现结果正好相反,但又输入一遍发现时间相同,可能是受到数据集大小的影响吧。

> system.time(dlply(baseball, "id", nrow))
用户 系统 流逝 
0.07 0.00 0.08 
> iBaseball <- idata.frame(baseball)
> system.time(dlply(iBaseball, "id", nrow))
用户 系统 流逝 
0.10 0.02 0.11 
> system.time(dlply(iBaseball, "id", nrow))
用户 系统 流逝 
0.09 0.00 0.10 
> system.time(dlply(baseball, "id", nrow))
用户 系统 流逝 
0.10 0.00 0.09 

6.4 data.table包

data.table包扩展和增强了data.frame的功能。(data.frame在高级数据结构这章笔记里介绍过)

data.table包运行快的原因是它有类似于数据库一样的索引,这使得其在获取值、分组操作和合并时访问速度更快。

因为data.table包里边的例子和前边都相同,只是比较了使用时间和数据类型,所以就不举例了。

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值