7.3 因子
因子是一个用于存储类别变量的特殊的变量类型。它有时像字符串,有时又像整数。
7.3.1 创建因子
当你用一列文本数据创建数据框时,R将文本默认为类别数据并进行转换。下例中的数据集包含了随机的10个成年人的身高数据:
(heights <- data.frame(
height_cm = c(153, 181, 150, 172, 165, 149, 174, 169, 198, 163),
gender = c(
"female", "male", "female", "male", "male",
"female", "female", "male", "male", "female"
)
))
## height_cm gender
## 1 153 female
## 2 181 male
## 3 150 female
## 4 172 male
## 5 165 male
## 6 149 female
## 7 174 female
## 8 169 male
## 9 198 male
## 10 163 female
通过检查gender
一列的类,正如你所料,我们发现它不是一个字符向量,而是一个因子:
class(heights$gender)
## [1] "factor"
把这列打印出来可看到它更多的本质信息:
heights$gender
## [1] female male female male male female female male male female
## Levels: female male
因子中的每个值都是一个字符串,它们被限制为“female”、“male”或缺失值。如果我们把不同的字符串添加到genders中,此项约束则变得很明显:
heights$gender[1] <- "Female" #注意是大写的"F"
## Warning: invalid factor level, NA generated
heights$gender
## [1] <NA> male female male male female female male male female
## Levels: female male
选项“female”和“male”被称为因子水平,它能用levels
函数查询到:
levels(heights$gender)
## [1] "female" "male"
水平的级数(相当于因子level
的length
)可由nlevel
函数查询到:
nlevels(heights$gender)
## [1] 2
除了使用数据框在内部自动创建因子之外,也可以使用factor
函数创建它。它的第一个参数(唯一的强制要求)是一个字符向量:
gender_char <- c(
"female", "male", "female", "male", "male",
"female", "female", "male", "male", "female"
)
(gender_fac <- factor(gender_char))
## [1] female male female male male female female male male female
## Levels: female male
7.3.2 更改因子水平
我们可以通过指定levels
参数来更改因子被创建时水平的先后顺序:
factor(gender_char, levels = c("male", "female"))
## [1] female male female male male female female male male female
## Levels: male female
如果想在因子创建之后再改变因子水平的顺序,就再次使用factor
函数,这时它的参数是当前的因子(而不是字符向量):
factor(gender_char, levels = c("male", "female"))
## [1] female male female male male female female male male female
## Levels: male female
警告
不能使用
levels
函数直接改变因子的水平值。因为这将重新为每一个水平打上标签,更改数据值,这通常不是我们想要的。
在下例中,直接设置因子的水平值会将数据中的male变成female,female变成male,这并不是我们想要的:
levels(gender_fac) <- c("male", "female")
gender_fac
## [1] male female male female female male male female female male
## Levels: male female
relevel
函数是另一种改变因子水平顺序的方法。在这种情况下,你只需指定第一个水平值。正如你所料,此功能的使用场景还是相当少的(在将不同类别和引用类别相比较的回归模型中,它很好用)。大多数时候,使用factor
函数来设置水平值会更方便:
relevel(gender_fac, "male")
## [1] male female male female female male male female female male
## Levels: male female
7.3.3 去掉因子水平
在数据集清理的过程中,最终你可能需要去掉所有与因子水平对应的数据。考虑以下数据集,它记录了上班途中所使用的交通工具的次数:
getting_to_work <- data.frame(
mode = c(
"bike", "car", "bus", "car", "walk",
"bike", "car", "bike", "car", "car"
),
time_mins = c(25, 13, NA, 22, 65, 28, 15, 24, NA, 14)
)
并非每次都有记录,所以我们的第一个任务是去掉那些time_mins
是NA
的行 :
(getting_to_work <- subset(getting_to_work, !is.na(time_mins)))
## mode time_mins
## 1 bike 25
## 2 car 13
## 4 car 22
## 5 walk 65
## 6 bike 28
## 7 car 15
## 8 bike 24
## 10 car 14
请看mode
一列,其中只有三个不同值,而因子水平却是four。我们可以使用unique
函数(当然,也可以用levels
函数)看清这一点:
unique(getting_to_work$mode)
## [1] bike car walk
## Levels: bike bus car walk
如果要删除未使用的水平因子,我们可以使用droplevels
函数。它接受因子或是数据框作为参数。对后者来说,它将丢弃输入因子中所有未使用的水平。因为在我们的数据框里只有一个因子未使用,所以下例中的两行代码是等价的:
getting_to_work$mode <- droplevels(getting_to_work$mode)
getting_to_work <- droplevels(getting_to_work)
levels(getting_to_work$mode)
## [1] "bike" "car" "walk"
7.3.4 有序因子
有些因子的水平在语义上大于或小于其他水平。这在多选择题的调查问题中很常见。例如,调查问题“你有多快乐?”可能的回答包括:“depressed”、“grumpy”、“so-so”、“cheery”和“ecstatic”2。结果是类别变量,所以我们可以创建一个拥有五个选项的因子。在此,使用sample
函数来产生10000个随机回复:
2 有时这被称为李克特量(Likert scale)表。
happy_choices <- c("depressed", "grumpy", "so-so", "cheery", "ecstatic")
happy_values <- sample(
happy_choices,
10000,
replace = TRUE
)
happy_fac <- factor(happy_values, happy_choices)
head(happy_fac)
## [1] grumpy depressed cheery ecstatic grumpy grumpy
## Levels: depressed grumpy so-so cheery ecstatic
在这种情况下,对调查者来说,五个选项其实是有顺序的:“grumpy”(暴躁的)比“depressed”(沮丧)更快乐些,而“so-so”(马马虎虎)又比“grumpy”更快乐些等。这意味着最好把回复存储在一个按顺序排列的因子中。使用ordered
函数(或通过给factor
传入order = TRUE
参数)可实现这个功能:
happy_ord <- ordered(happy_values, happy_choices)
head(happy_ord)
## [1] grumpy depressed cheery ecstatic grumpy grumpy
## Levels: depressed < grumpy < so-so < cheery < ecstatic
一个有序的因子还是因子,但一般的因子却不一定是有序的:
is.factor(happy_ord)
## [1] TRUE
is.ordered(happy_fac)
## [1] FALSE
在大多数情况下,你无需为有序因子的使用担心,而仅会在某些模型中看到差别。不过,在分析调查数据时,它们比较有用。
7.3.5 将连续变量转换为类别
一个汇总数值变量的方法是计算有多少个值落入不同的“组”(bins)中,cut
函数能将数值变量切成不同的块,然后返回一个因子。它通常使用table
函数得到每组数字的总和。(hist
函数能画出直方图,它也能实现这个功能,就像我们将在后面看到的包含在plyr
包中的count
一样。)
在下例中,我们随机地生成10 000名工人的年龄数据(从16到66,使用Beta分布),并将他们按每10年分组:
ages <- 16 + 50 * rbeta(10000, 2, 3)
grouped_ages <- cut(ages, seq.int(16, 66, 10))
head(grouped_ages)
## [1] (26,36] (16,26] (26,36] (26,36] (26,36] (46,56]
## Levels: (16,26] (26,36] (36,46] (46,56] (56,66]
table(grouped_ages)
## grouped_ages
## (16,26] (26,36] (36,46] (46,56] (56,66]
## 1844 3339 3017 1533 267
在本例中,大部分工人的年龄分布于26到36和36到46两个类别中(这正是使用Beta分布的结果) 。
请注意,ages
是一个数字变量,而grouped_ages
是一个因子:
class(ages)
## [1] "numeric"
class(grouped_ages)
## [1] "factor"
7.3.6 将类别变量转换为连续变量
与上面相反的情况是把因子转换成数值变量,这在数据清理中非常有用。如果你有一些脏数据,例如打错了的数字,在数据导入的过程中,R会将它们解释为字符串,并将其转换为因子。在下例中,其中一个数字有两个小数点。诸如read.table
的导入函数(第12章将深入探讨)将无法把这样的字符串解析成数字格式,而会默认把这一列转换成字符向量:
dirty <- data.frame(
x = c("1.23", "4..56", "7.89")
)
要想把x
列转换为数字,显而易见的解决方法是调用as.numeric
。可惜,它给出了错误的答案:
as.numeric(dirty$x)
## [1] 1 2 3
将as.numeric
函数作用于因子上却看到了整数值,这说明因子使用了整数来存储数据。在一般情况下,可以从levels(f)[as.integer(f)]
中重构出f
因子。
为了正确地把因子转换为数字,可先把因子转换为字符向量,再转换为数值。第二个值是NA
,因为4..56
并不是一个真正的数字:
as.numeric(as.character(dirty$x))
## Warning: NAs introduced by coercion
## [1] 1.23 NA 7.89
这不够高效,因为相同的值须被多次转换。正如FAQ on R中所指出的,更好的方法是将因子水平转换为数字,然后再像上面一样重建因子:
as.numeric(levels(dirty$x))[as.integer(dirty$x)]
## Warning: NAs introduced by coercion
## [1] 1.23 NA 7.89
因为这不够直观,所以如果你经常使用它,就把它包装成一个函数以方便调用:
factor_to_numeric <- function(f)
{
as.numeric(levels(f))[as.integer(f)]
}
7.3.7 生成因子水平
为了平衡数据,使到在每个水平上的数据点的数目相等,可用gl
函数来生成因子。它最简单的形式是:第一个整型参数为要生成的因子的水平数,第二个为每个水平需要重复的次数。通常你想为每个水平命名,这可以通过给label
参数传入一个字符向量来实现。你还可以通过传入length
参数以创建更复杂的水平排序,例如交替值(alternating value):
gl(3, 2)
## [1] 1 1 2 2 3 3
## Levels: 1 2 3
gl(3, 2, labels = c("placebo", "drug A", "drug B"))
## [1] placebo placebo drug A drug A drug B drug B
## Levels: placebo drug A drug B
gl(3, 1, 6, labels = c("placebo", "drug A", "drug B")) #交替
## [1] placebo drug A drug B placebo drug A drug B
## Levels: placebo drug A drug B
7.3.8 合并因子
如果我们有多个类别变量,有时把它们合并成一个单一的因子是有用的,其中每个水平由各个变量之间的交叉合并(使用interaction
函数)组成:
treatment <- gl(3, 2, labels = c("placebo", "drug A", "drug B"))
gender <- gl(2, 1, 6, labels = c("female", "male"))
interaction(treatment, gender)
## [1] placebo.female placebo.male drug A.female drug A.male
## [5] drug B.female drug B.male
## 6 Levels: placebo.female drug A.female drug B.female ... drug B.male