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

  • Post author:
  • Post category:其他




笔记链接


学习笔记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包里边的例子和前边都相同,只是比较了使用时间和数据类型,所以就不举例了。



版权声明:本文为LL_2048原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。