@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
>