R中的因素:不仅仅是烦恼?

JD *_*ong 95 language-design r internals r-factor

R中的一种基本数据类型是因子.根据我的经验,因素基本上是一种痛苦,我从不使用它们.我总是转换为角色.我觉得奇怪的是我错过了一些东西.

是否有一些重要的函数示例使用因子作为分组变量,其中因子数据类型变得必要?我应该使用哪些因素?

Vin*_*nce 49

你应该使用因素.是的,他们可以是一个痛苦,但我的理论是,90%的为什么他们是一个痛苦是因为在read.tableread.csv,参数stringsAsFactors = TRUE默认情况下(大多数用户错过这个微妙的).我说它们很有用,因为像lme4这样的模型拟合包使用因子和有序因子来差异拟合模型并确定要使用的对比类型.图形包也使用它们进行分组.ggplot并且大多数模型拟合函数将特征向量强制转换为因子,因此结果是相同的.但是,您的代码中最终会出现警告:

lm(Petal.Length ~ -1 + Species, data=iris)

# Call:
# lm(formula = Petal.Length ~ -1 + Species, data = iris)

# Coefficients:
#     Speciessetosa  Speciesversicolor   Speciesvirginica  
#             1.462              4.260              5.552  

iris.alt <- iris
iris.alt$Species <- as.character(iris.alt$Species)
lm(Petal.Length ~ -1 + Species, data=iris.alt)

# Call:
# lm(formula = Petal.Length ~ -1 + Species, data = iris.alt)

# Coefficients:
#     Speciessetosa  Speciesversicolor   Speciesvirginica  
#             1.462              4.260              5.552  
Run Code Online (Sandbox Code Playgroud)

警告信息:在model.matrix.default(mt, mf, contrasts):

变量Species转换为factor

一点都是棘手的事情drop=TRUE.在向量中,这可以很好地去除数据中不存在的因子级别.例如:

s <- iris$Species
s[s == 'setosa', drop=TRUE]
#  [1] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [11] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [21] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [31] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [41] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# Levels: setosa
s[s == 'setosa', drop=FALSE]
#  [1] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [11] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [21] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [31] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [41] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# Levels: setosa versicolor virginica
Run Code Online (Sandbox Code Playgroud)

但是,使用data.frames,行为[.data.frame()是不同的:请参阅此电子邮件?"[.data.frame".使用drop=TRUEon data.frames并不像你想象的那样工作:

x <- subset(iris, Species == 'setosa', drop=TRUE)  # susbetting with [ behaves the same way
x$Species
#  [1] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [11] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [21] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [31] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# [41] setosa setosa setosa setosa setosa setosa setosa setosa setosa setosa
# Levels: setosa versicolor virginica
Run Code Online (Sandbox Code Playgroud)

幸运的是,您可以轻松地降低因子,droplevels()以降低单个因子或a中每个因子的未使用因子水平data.frame(自R 2.12起):

x <- subset(iris, Species == 'setosa')
levels(x$Species)
# [1] "setosa"     "versicolor" "virginica" 
x <- droplevels(x)
levels(x$Species)
# [1] "setosa"
Run Code Online (Sandbox Code Playgroud)

这是如何保持你从传入中选择的水平ggplot.

在内部,factors是具有属性级别字符向量的整数(请参阅attributes(iris$Species)class(attributes(iris$Species)$levels)),这是干净的.如果您必须更改级别名称(并且您使用的是字符串),那么这将是一种效率得多的操作.而且我更改了级别名称,特别是对于ggplot传说.如果您使用字符向量伪造因子,则存在您只更改一个元素并意外创建单独的新级别的风险.


mds*_*ner 30

有序的因素是很棒的,如果我碰巧喜欢橘子和讨厌苹果,但不介意葡萄,我不需要管理一些奇怪的索引来说:

d <- data.frame(x = rnorm(20), f = sample(c("apples", "oranges", "grapes"), 20, replace = TRUE, prob = c(0.5, 0.25, 0.25)))
d$f <- ordered(d$f, c("apples", "grapes", "oranges"))
d[d$f >= "grapes", ]
Run Code Online (Sandbox Code Playgroud)

  • ordered()从任何值创建任意排序 - 按照您说的顺序排序.不幸的是,我使用了按字典顺序排序的值,这是巧合.例如,我使用这个数据,其中"Z"是坏的,"3"是好的,但标签不是数字*或*字母 - 所以我做了有序(数据,c("Z","B","A" ,"0","1","2","3"))然后我可以做数据>"A",这是快乐的日子. (4认同)

Bri*_*ggs 19

A factor最类似于其他语言中的枚举类型.适当的用途是变量,它只能采用一组规定的值.在这些情况下,并非每个可能的允许值都可以存在于任何特定数据集中,并且"空"级别准确地反映了这一点.

考虑一些例子.对于在美国各地收集的一些数据,应将州记录为一个因素.在这种情况下,没有从特定州收集案件的事实是相关的.可能有来自该州的数据,但发生了(无论出于何种原因,这可能是感兴趣的原因).如果收集家乡,那将不是一个因素.没有预先设定的可能的家乡.如果从三个城镇而不是在全国范围内收集数据,该城镇将成为一个因素:一开始就有三个选择,如果在这三个城镇中没有一个发现相关案例/数据,则这是相关的.

factors的其他方面,例如提供给一组字符串赋予任意排序顺序的方法,是factors的有用的次要特征,但不是它们存在的原因.

  • +1.Brian,我认为你抓住了数据中没有捕获水平的头部. (3认同)

Far*_*rel 13

当人们进行统计分析并实际探索数据时,因素非常棒.然而,在此之前,当人们正在阅读,清理,排除故障,合并并且通常操纵数据时,因素是完全痛苦的.最近,与过去几年一样,许多功能已得到改进,可以更好地处理这些因素.例如,rbind与他们很好地配合.我仍然发现在子集函数之后留下空级别是一件非常麻烦的事情.

#drop a whole bunch of unused levels from a whole bunch of columns that are factors using gdata
require(gdata)
drop.levels(dataframe)
Run Code Online (Sandbox Code Playgroud)

我知道重新编码因子的级别并重新标记标签是很简单的,并且还有很好的方法来重新排序级别.我的大脑根本无法记住它们,我每次使用它时都必须重新学习它.重新编码应该比它更容易.

R的字符串函数非常容易使用.因此,在操纵时,我通常更喜欢角色而不是因素.

  • 现在有一个base-R函数`droplevels()`.并且默认情况下它不会重新排序因子. (3认同)

Jos*_*ich 6

多么狡猾的头衔!

我相信很多估计函数允许您使用因子来轻松定义虚拟变量......但我不会使用它们.

当我有非常大的字符向量且几乎没有独特的观察时,我就使用它们.这可以减少内存消耗,特别是如果字符向量中的字符串更长.

PS - 我在开玩笑说这个头衔.我看到了你的推文.;-)

  • 好吧,至少它曾经;-).但是一些R版本之前的字符存储被重写为内部哈希,因此这一历史性论点的一部分现在无效.仍有因素对于分组和建模非常有用. (13认同)
  • N < - 1000; a < - 样本(c("a","b","c"),N,replace = TRUE); print(object.size(a),units ="Kb"); print(object.size(factor(a)),units ="Kb"); 8 Kb 4.5 Kb所以它似乎仍然节省了一些空间. (2认同)
  • @Eduardo我得到4Kb vs 4.2Kb.对于"N = 100000",我得到391.5 Kb对391.8 Kb.因此,因素需要更少的内存. (2认同)