在R中,因子常被用来表示分类变量。分类变量是有固定和已知的可能值集合的一类变量。
因子比字符串更容易处理。R语言中的许多基本函数会自动将字符串转换为因子(比如read.csv()),但很多时候这样的转换并不合适,需要注意(比如read.csv()中,需要设置参数stringsAsFactors = FALSE)。
2017年12月21日
在R中,因子常被用来表示分类变量。分类变量是有固定和已知的可能值集合的一类变量。
因子比字符串更容易处理。R语言中的许多基本函数会自动将字符串转换为因子(比如read.csv()),但很多时候这样的转换并不合适,需要注意(比如read.csv()中,需要设置参数stringsAsFactors = FALSE)。
x1为记录月份的字符型向量。
x1 <- c("Dec", "Apr", "Jan", "Mar")
使用字符型变量有两个问题:
不能避免拼写错误(尽管只有十二个月):
x2 <- c("Dec", "Apr", "Jam", "Mar")不能以有用的方式进行排序(从1月到12月):
sort(x1)
[1] "Apr" "Dec" "Jan" "Mar"
可以用因子解决上述问题。
创建因子首先要创建一个因子水平集合(levels)。
month_levels <- c("Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec")
进而创建一个因子。
y1 <- factor(x1, levels = month_levels) y1
[1] Dec Apr Jan Mar Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
sort(y1)
[1] Jan Mar Apr Dec Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
没有在因子水平集合中的值会被转换为NA,但不会给出警告(warning)。
y2 <- factor(x2, levels = month_levels) y2
[1] Dec Apr <NA> Mar Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
如果忽略了levels参数,会将字母顺序作为因子水平的排序。
factor(x1)
[1] Dec Apr Jan Mar Levels: Apr Dec Jan Mar
factor(c("一月", "二月", "三月"))
[1] 一月 二月 三月 Levels: 二月 三月 一月
使用levels()可以获取因子水平集合。
y1
[1] Dec Apr Jan Mar Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
levels(y1)
[1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" [12] "Dec"
本章使用forcats包,以及包中的gss_cat数据。这是一份由芝加哥大学负责的美国综合社会调查的样本数据。调查包括数千个问题,gss_cat数据从中选择了一些问题来演示因子的处理。
# install.packages("forcats")
library(forcats)
library(dplyr)
library(ggplot2)
data("gss_cat")
由于gss_cat是包中提供的数据,因此可以用?gss_cat获取数据的介绍。
str(gss_cat)
Classes 'tbl_df', 'tbl' and 'data.frame': 21483 obs. of 9 variables: $ year : int 2000 2000 2000 2000 2000 2000 2000 2000 2000 2000 ... $ marital: Factor w/ 6 levels "No answer","Never married",..: 2 4 5 2 4 6 2 4 6 6 ... $ age : int 26 48 67 39 25 25 36 44 44 47 ... $ race : Factor w/ 4 levels "Other","Black",..: 3 3 3 3 3 3 3 3 3 3 ... $ rincome: Factor w/ 16 levels "No answer","Don't know",..: 8 8 16 16 16 5 4 9 4 4 ... $ partyid: Factor w/ 10 levels "No answer","Don't know",..: 6 5 7 6 9 10 5 8 9 4 ... $ relig : Factor w/ 16 levels "No answer","Don't know",..: 15 15 15 6 12 15 5 15 15 15 ... $ denom : Factor w/ 30 levels "No answer","Don't know",..: 25 23 3 30 30 25 30 15 4 25 ... $ tvhours: int 12 NA 2 4 1 NA 3 NA 0 3 ...
当因子存储在tibble中时,无法直接看到因子水平,这时可以使用count()。
gss_cat %>% count(race)
# A tibble: 3 x 2
race n
<fctr> <int>
1 Other 1959
2 Black 3129
3 White 16395
或者绘制柱状图。
qplot(race, data = gss_cat, geom = "bar")
需要注意,在默认情况下,qplot不会绘制没有因子的因子水平。
rincome(reported income)的分布图,绘制出的柱状图难懂的原因是什么?relig是什么?最常见的政党partyid是什么?relig有教派denom?处理因子时,最常见的操作是改变因子水平的顺序或因子水平的值。
改变因子水平的顺序常用于绘图时。比如分析不同宗教信仰的人每天看电视的平均小时数。
relig_summary <- gss_cat %>% group_by(relig) %>%
summarise(age = mean(age, na.rm = TRUE),
tvhours = mean(tvhours, na.rm = TRUE), n = n())
qplot(tvhours, relig, data = relig_summary)
使用fct_reorder()改变relig的因子水平顺序,能使图形更加易读。fct_reorder(f, x, fun)有三个参数:
f 想要改变因子水平顺序的因子。x 用来排序因子水平的数值向量。fun 可选函数,如果同一因子水平存在多个x,则用fun计算每一个因子水平中x的取值。默认为median。qplot(tvhours, fct_reorder(relig, tvhours), data = relig_summary)
对relig的因子水平进行重新排序后可以很容易的看出,宗教信仰为Don't know的人看电视时间最长,为Hinduism & Other Eastern religions的人看电视时间最短。
如果需要做更复杂的数据变换,可以单独的写一个mutate()函数。
relig_summary <- relig_summary %>%
mutate(relig = fct_reorder(relig, tvhours))
qplot(tvhours, relig, data = relig_summary)
采用同样的方法分析不同收入水平的平均年龄?
rincome_summary <- gss_cat %>% group_by(rincome) %>%
summarise(age = mean(age, na.rm = TRUE),
tvhours = mean(tvhours, na.rm = TRUE), n = n())
qplot(age, fct_reorder(rincome, age), data = rincome_summary)
在上个图形中,对rincome的因子水平进行重新排序并不合适,因为rincome原先就有更加合理的因子水平顺序。
levels(rincome_summary$rincome)
[1] "No answer" "Don't know" "Refused" "$25000 or more" [5] "$20000 - 24999" "$15000 - 19999" "$10000 - 14999" "$8000 to 9999" [9] "$7000 to 7999" "$6000 to 6999" "$5000 to 5999" "$4000 to 4999" [13] "$3000 to 3999" "$1000 to 2999" "Lt $1000" "Not applicable"
唯一需要调整的是应该将Not applicable调整到其他因子水平之前。
可以使用fct_relevel(),第一个参数为因子,第二个参数为需要调整到前面的因子水平。
qplot(age, fct_relevel(rincome, "Not applicable"), data = rincome_summary)
思考为什么Not applicable的平均年龄特别高?
对于柱状图,还可以用fct_infreq()根据因子出现频率排序因子水平,如果要改变顺序,可以结合使用fct_rev()。
gss_cat <- gss_cat %>%
mutate(marital = marital %>% fct_infreq() %>% fct_rev())
qplot(marital, data = gss_cat, geom = "bar")
gss_cat数据框中每个因子的因子水平顺序是任意指定的还是遵循一定原则的?fct_relevel函数中还有一个after参数,研究该参数有什么用?改变因子水平的名称比改变因子水平的顺序功能更强大。最通用的改变因子水平名称的函数是fct_recode()。以gss_cat$partyid为例。
gss_cat %>% count(partyid)
# A tibble: 10 x 2
partyid n
<fctr> <int>
1 No answer 154
2 Don't know 1
3 Other party 393
4 Strong republican 2314
5 Not str republican 3032
6 Ind,near rep 1791
7 Independent 4119
8 Ind,near dem 2499
9 Not str democrat 3690
10 Strong democrat 3490
其中因子水平名称均为缩写,改成全称后更加易懂。fct_recode()不会改变没有提及的因子水平名称,一旦遇到了不存在因子水平名称,会给出警告。
gss_cat %>% mutate(partyid = fct_recode(partyid,
"Republican, strong" = "Strong republican",
"Republican, weak" = "Not str republican",
"Independent, near rep" = "Ind,near rep",
"Independent, near dem" = "Ind,near dem",
"Democrat, weak" = "Not str democrat",
"Democrat, strong" = "Strong democrat")) %>% count(partyid)
# A tibble: 10 x 2
partyid n
<fctr> <int>
1 No answer 154
2 Don't know 1
3 Other party 393
4 Republican, strong 2314
5 Republican, weak 3032
6 Independent, near rep 1791
7 Independent 4119
8 Independent, near dem 2499
9 Democrat, weak 3690
10 Democrat, strong 3490
另外可以将多个因子水平名称赋予同一个新的因子水平名称以达到归并因子水平的目的。使用这一技巧必须小心,如果归并的因子水平显著不同,会误导结果。
gss_cat %>% mutate(partyid = fct_recode(partyid,
"Republican, strong" = "Strong republican",
"Republican, weak" = "Not str republican",
"Independent, near rep" = "Ind,near rep",
"Independent, near dem" = "Ind,near dem",
"Democrat, weak" = "Not str democrat",
"Democrat, strong" = "Strong democrat",
"Other" = "No answer", "Other" = "Don't know",
"Other" = "Other party")) %>% count(partyid)
# A tibble: 8 x 2
partyid n
<fctr> <int>
1 Other 548
2 Republican, strong 2314
3 Republican, weak 3032
4 Independent, near rep 1791
5 Independent 4119
6 Independent, near dem 2499
7 Democrat, weak 3690
8 Democrat, strong 3490
如果需要归并许多因子水平,可以使用fct_collapse(),其是fct_recode()的一个变体。对一个新的因子水平名称,提供一个旧的因子水平名称的向量。
gss_cat %>% mutate(partyid = fct_collapse(partyid,
other = c("No answer", "Don't know", "Other party"),
rep = c("Strong republican", "Not str republican"),
ind = c("Ind,near rep", "Independent", "Ind,near dem"),
dem = c("Not str democrat", "Strong democrat"))) %>% count(partyid)
# A tibble: 4 x 2 partyid n <fctr> <int> 1 other 548 2 rep 5346 3 ind 8409 4 dem 7180
rincome中的因子水平(比如将收入分为高、中、低等)?