[关闭]
@evilking 2018-03-03T16:03:39.000000Z 字数 3875 阅读 1972

R基础篇

因子

因子(factor)是R语言中许多强大运算的基础,包括许多针对表格数据的运算;它的设计思想来源于统计学中的名义变量,或称之为分类变量(categorical),这类变量的值本质上不是数字,而是对应为分类

因子可以简单地看作一个附加了更多信息的向量(尽管它们内部机理是不同的),这额外的信息包括向量中不同值的记录,称为“水平”(level)

因子就像一个集合,集合中的元素没有重复,每个元素被称为水平,因子的分类变量本身就是其中部分元素的索引构成的向量

什么是因子

> (x <- c(5,12,13,12))  #创建一个向量
[1]  5 12 13 12

> (xf <- factor(x)) #使用factor()函数将向量转变为因子
[1] 5  12 13 12
Levels: 5 12 13

> str(xf)   #查看因子内部结构
 Factor w/ 3 levels "5","12","13": 1 2 3 2

> unclass(xf)   #去掉因子属性
[1] 1 2 3 2
attr(,"levels")
[1] "5"  "12" "13"

> length(xf)    #查看因子的长度
[1] 4
>

factor()函数可以将一个向量转换为一个因子,上面提到了因子可以看成是一个附加了更多信息的向量;查看因子xf的值可以看到除了本身的数值5 12 13 12外,还有个属性Levels: 5 12 13,这个就是向量转换为因子后额外的信息,称为“水平”

使用str(xf)函数查看因子的内部结构,看到因子xf的核心不是(5,12,13,12)而是(1,2,3,2),后者意味着我们的数据是由水平1的值、接着水平2和水平3的值、最后是由水平2的值构成,因此数据已经重新编码为水平,当然水平本身也被作为字符记录,例如是字符"5",而不是数值5

最后length(xf)可知因子的长度被定义为数据的长度,而不是水平的个数


因子中插入元素

> xf    #当前因子xf
[1] 5  12 13 12
Levels: 5 12 13

> xf[2] <- 88   #向因子中插入不在levels中的数
Warning message:
In `[<-.factor`(`*tmp*`, 2, value = 88) :
  invalid factor level, NA generated

> xf[2] <- 5    #向因子中插入存在levels中的数
> xf
[1] 5  5  13 12
Levels: 5 12 13

> xf[5] <- 12   #因子也能如向量一样动态增加元素
> xf
[1] 5  5  13 12 12
Levels: 5 12 13

> xff <- factor(x,levels = c(5,12,13,88))  #给因子重新设置水平
> xff
[1] 5  12 13 12
Levels: 5 12 13 88

> xff[6] <- 88
> xff
[1] 5    12   13   12   <NA> 88  
Levels: 5 12 13 88

> levels(xff)   #提取因子的水平
[1] "5"  "12" "13" "88"
>

上例展示了可以向因子中动态添加一个存在水平中的元素;如果向因子中添加一个“非法”的水平,则会报错;如果想重新设置因子的水平,比如想添加新水平或者删除某个水平,可以使用factor(x,levels= )中的levels=参数重新设置


有序因子

> score <- c('A','B','A','C','B')

> score_ord <- ordered(score,levels=c('C','B','A')) 
> score_ord
[1] A B A C B
Levels: C < B < A

> score_ord2 <- ordered(score,levels=c('B','A','C'))
> score_ord2
[1] A B A C B
Levels: B < A < C
>

还有类因子叫“有序因子(ordered factor)”,表示有序变量,使用ordered()函数来创建有序因子,其中第一个参数是数据向量,levels参数是有序水平


因子的常用函数

作为实例背景,我们假设一个班级年龄的向量ages,和一个因子sexs,即他们对应的性别,我们希望找到ages中不同性别的同学的平均年龄

> (ages <- c(25,26,23,24,22,23,24,23))
[1] 25 26 23 24 22 23 24 23
> (sexs <- c('M','W','W','M','M','M','W','M'))
[1] "M" "W" "W" "M" "M" "M" "W" "M"

> tapply(ages,sexs,mean)    #先将sexs作为水平因子分组,然后对每一组应用mean函数
       M        W 
23.40000 24.33333 

> (25+24+22+23+23)/5
[1] 23.4
> (26+23+24)/3
[1] 24.33333
>

tapply(x,f,g)需要向量x、因子或因子列表f以及函数g,如果f的一部分是向量,则需要用函数as.factor()强制将其转换为因子

tapply()函数的执行操作是,(暂时)将x分组,每组对应一个因子水平(或在多重因子的情况下对应一组因子水平的组合),得到x的子向量,然后这些子向量应用函数g()

在上例中,函数tapply()是先将向量sexs作为具有水平"M"和"W"的因子,它注意到"M"出现在索引1、4、5、6、8的位置,"W"出现在索引2、3、7的位置,为了方便我们用向量x指代向量(1,4,5,6,8),y指代向量(2,3,7),然后tapply()函数计算mean(ages[x])和mean(ages[y]),并将这些均值返回到二元向量,该向量的元素名称分别为"M"和"W",反映出tapply()函数所调用的因子水平


> split(ages,sexs)  #按照因子sexs列出分组
$M
[1] 25 24 22 23 23

$W
[1] 26 23 24

> 

split(x,f)函数的参数说明跟tapply()函数类似,x为向量,f为因子,该函数可以把x划分为组,并返回分组的列表

x在split()中可以是数据框,而在tapply()中不可以

这个例子中将sexs作为水平因子"M"和"W"分为两组,索引(1,4,5,6,8)和(2,3,7),对应向量ages分别为(25,24,22,23,23)和(26,23,24)


tapply(x,f,g)函数要求x必须是向量,而不是矩阵或数据框,这就需要by(x,f,g)函数了,它的功能与tapply()类似,只是x可以是矩阵或数据框

> ages <- cbind(ages,1:8)   #构造矩阵
> ages
     ages  
[1,]   25 1
[2,]   26 2
[3,]   23 3
[4,]   24 4
[5,]   22 5
[6,]   23 6
[7,]   24 7
[8,]   23 8

> tapply(ages,sexs,mean)    #使用tapply()函数报错
Error in tapply(ages, sexs, mean) : 参数的长度必需相同

> by(ages,sexs,function(m) m[,1]+m[,2]) #可以使用by()函数
INDICES: M
[1] 26 28 27 29 31
--------------------------------------------------------- 
INDICES: W
[1] 28 26 31
> 

by(x,f,g)函数是先按因子sexs将ages分组,为了方便索引分别记为x和y,x是(1,4,5,6,8),y是(2,3,7),则第一组为ages[,x],为子矩阵,第二组为ages[,y]也是子矩阵,然后将每组的矩阵传入g()函数,上面例子中就是参数m,然后将矩阵m的第一列与第二列相加

其中function(m) m[,1]+m[,2]这种写法是创建一个匿名函数的写法,这里不用太过深究,我们会在后面的函数章节详细讲解

最后我们简单介绍一下表,一般用的不是很多;在统计学中,叫关联表(contingency table)

> ages
[1] 25 26 23 24 22 23 24 23
> sexs
[1] "M" "W" "W" "M" "M" "M" "W" "M"
> classname <- c("1","2","3","2","3","3","2","1")

> tapply(ages,list(classname,sexs),length)
  M  W
1 2 NA
2 1  2
3 2  1
>

这个例子展示了当tapply()函数的因子部分有多列的情况下,生成的结果是将多列因子进行排列组合后分组,再应用函数;将因子classname和sexs排列组合后得到表一样的结构,然后对每组应用length()函数


但是应用tapply()生成的结果中存在一个缺失值NA的问题,这个值应该是0,代表班级为"1"且性别为"W"的人数为0

> table(list(classname,sexs))   #传入二维因子
   .2
.1  M W
  1 2 0
  2 1 2
  3 2 1

> table(ages)   #传入一维因子
ages
22 23 24 25 26 
 1  3  2  1  1 
> 

table(f)函数会按因子f分组,并计算频数,如上面班级为"1"且性别为"M"的出现了两次;当传入的是一维向量时,可以得到一维的频数表,也就是每个因子的频数


> (tab <- table(list(classname,sexs)))
   .2
.1  M W
  1 2 0
  2 1 2
  3 2 1

> class(tab)    #表是一种类型
[1] "table"

> tab[1,1]  #表可以像矩阵一样操作元素
[1] 2
> tab[1,]
M W 
2 0 

> tab/5
   .2
.1    M   W
  1 0.4 0.0
  2 0.2 0.4
  3 0.4 0.2

> dimnames(tab) #查看表的维度信息
$`.1`
[1] "1" "2" "3"

$`.2`
[1] "M" "W"

> apply(tab,1,sum)  #也可以对表像矩阵一样应用apply()函数
1 2 3 
2 3 3 
> 
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注