因子水平和因子标签之间的混淆

125

在R语言中,因子(factor)的水平(levels)和标签(labels)似乎有所区别。 迄今为止,我一直认为水平是因子水平的“真实”名称,而标签是输出(例如表格和图形)中使用的名称。但显然情况并非如此,以下示例说明了这一点:

df <- data.frame(v=c(1,2,3),f=c('a','b','c'))
str(df)
'data.frame':   3 obs. of  2 variables:
 $ v: num  1 2 3
 $ f: Factor w/ 3 levels "a","b","c": 1 2 3

df$f <- factor(df$f, levels=c('a','b','c'),
  labels=c('Treatment A: XYZ','Treatment B: YZX','Treatment C: ZYX'))
levels(df$f)
[1] "Treatment A: XYZ" "Treatment B: YZX" "Treatment C: ZYX"

我原本以为可以通过脚本获取到('a','b','c')这些级别,但实际上并不行:

> df$f=='a'
[1] FALSE FALSE FALSE
但是这个可以:
> df$f=='Treatment A: XYZ' 
[1]  TRUE FALSE FALSE

所以,我的问题分为两个部分:

  • 水平和标签有什么区别?

  • 是否可以为脚本和输出的因子级别使用不同的名称?

背景:对于较长的脚本,使用短因子级别进行脚本编写似乎更加容易。然而,在报告和绘图方面,这种简短的因子级别可能不足够,应该用更精确的名称替代。

3个回答

148
非常简洁:在factor()函数中,级别是输入,标签是输出。 因子仅具有一个level属性,该属性由factor()函数中的labels参数设置。 这与统计软件(如SPSS)中标签的概念不同,并且可能会在开始时引起混淆。

您在此行代码中执行的操作

df$f <- factor(df$f, levels=c('a','b','c'),
  labels=c('Treatment A: XYZ','Treatment B: YZX','Treatment C: ZYX'))

告诉 R 有一个名为 df$f 的向量,你想将它转换为因子(factor),其中不同的水平(级别)编码为a、b和c,并希望这些级别用 "Treatment A"等标记。

factor函数会查找值a、b和c,将它们转换为数字因子类,并将标签值添加到因子的 level 属性中。此属性用于将内部数字值转换为正确的标签。但是,如您所见,没有 label 属性。

> df <- data.frame(v=c(1,2,3),f=c('a','b','c'))    
> attributes(df$f)
$levels
[1] "a" "b" "c"

$class
[1] "factor"

> df$f <- factor(df$f, levels=c('a','b','c'),
+   labels=c('Treatment A: XYZ','Treatment B: YZX','Treatment C: ZYX'))    
> attributes(df$f)
$levels
[1] "Treatment A: XYZ" "Treatment B: YZX" "Treatment C: ZYX"

$class
[1] "factor"

3
谢谢您的迅速回复!我猜我现在明白了级别和标签的目的。您是否有任何建议,以使输出更易于人类阅读,而无需手动编辑表名和图例? - donodarazao
7
通常我会在绘制或创建标签之前改变级别,例如在操作时保留级别为"a"、"b"、"c",然后在绘图时使用levels(f)<-paste("Treatment", toupper(levels(f)),sep =" ") [或其他方式]。或者创建一个名为f_pretty的平行因子,并仅用于输出... - Ben Bolker
我考虑了两种方法,但是两种方法都有缺点。第一种方法在绘制大量图形时可能会变得繁琐,而第二种方法在脚本中涉及大量数据聚合时可能会变得繁琐。但显然没有简单的方法可以避免这些问题,所以我会采纳你的建议。 :) - donodarazao
@42- 我不确定你所说的“数字值”的意思。如果您指的是因子中的内部值,那么这正是我上面所说的。因此提到了内部数字值。如果您指定了“levels”参数,则会给出输入中必须与“labels”参数匹配的值。 R将标签保留为属性levels,并且有些混淆。它在内部存储整数代码。这些整数代码与原始值无关,无论它们是什么类型。我认为你误解了我的意思。 - Joris Meys
抱歉。您所写的内容也是我的理解,现在我重新阅读您的问题,我看不到我认为您说了什么不同的地方。我会删除我的评论,因为它没有任何意义。 - IRTFM
显示剩余6条评论

20
我写了一个名为 "lfactors" 的软件包,它允许你引用水平或标签。
# packages
install.packages("lfactors")
require(lfactors)

flips <- lfactor(c(0,1,1,0,0,1), levels=0:1, labels=c("Tails", "Heads"))
# Tails can now be referred to as, "Tails" or 0
# These two lines return the same result
flips == "Tails"
#[1]  TRUE FALSE FALSE  TRUE  TRUE FALSE
flips == 0 
#[1]  TRUE FALSE FALSE  TRUE  TRUE FALSE

请注意,lfactor要求级别为数字,以免与标签混淆。


4
这是一个不错的软件包,感谢您的发布(和编写)。它似乎是R因子本应具备的功能——很高兴看到这样一个提供内置等效性检查的名称-值对映射的软件包。 - Soren
糟糕!我很兴奋地想使用lfactors,直到我注意到它“要求级别为数字”。需要出版样式标签(希腊字母、斜体、上标等)的数字是一个很好的用例,可以使用因子系统,仍然可以包括文本级别(后者可以通过使数据表更易读来帮助减少错误)。 - curious lab rat
好奇的实验室老鼠,级别是数字,标签是文本。你能想出一个代码示例,其中这是一个问题吗? - pdb
这应该完全包含在base或ggplot中。 - Herman Toothrot

2

我想分享一个处理因子变量水平名称在编程和漂亮打印时不同的技巧:

# Load packages
library(tidyverse)
library(sjlabelled)
library(patchwork)

# Create data frames
df <- data.frame(v = c(1, 2, 3), f = c("a", "b", "c"))
df_labelled <- data.frame(v = c(1, 2, 3), f = c("a", "b", "c")) %>%
  val_labels(
    # levels are characters
    f = c(
      "a" = "Treatment A: XYZ", "b" = "Treatment B: YZX", 
      "c" = "Treatment C: ZYX"
    ), 
    # levels are numeric
    v = c("1" = "Exp. Unit 1", "2" = "Exp. Unit 2", "3" = "Exp. Unit 3")
  )

# df and df_labelled appear exactly the same when printed and nothing changes
# in terms of scripting
df
#>   v f
#> 1 1 a
#> 2 2 b
#> 3 3 c
df_labelled
#>   v f
#> 1 1 a
#> 2 2 b
#> 3 3 c

# Now, let's take a look at the structure of df and df_labelled
str(df)
#> 'data.frame':    3 obs. of  2 variables:
#>  $ v: num  1 2 3
#>  $ f: chr  "a" "b" "c"
str(df_labelled) # notice the attributes
#> 'data.frame':    3 obs. of  2 variables:
#>  $ v: num  1 2 3
#>   ..- attr(*, "labels")= Named num [1:3] 1 2 3
#>   .. ..- attr(*, "names")= chr [1:3] "Exp. Unit 1" "Exp. Unit 2" "Exp. Unit 3"
#>  $ f: chr  "a" "b" "c"
#>   ..- attr(*, "labels")= Named chr [1:3] "a" "b" "c"
#>   .. ..- attr(*, "names")= chr [1:3] "Treatment A: XYZ" "Treatment B: YZX" "Treatment C: ZYX"

# Lastly, create ggplots with and without pretty names for factor levels
p1 <- df_labelled %>% # or, df
  ggplot(aes(x = f, y = v)) + 
  geom_point() + 
  labs(x = "Treatment", y = "Measurement")
p2 <- df_labelled %>%
  ggplot(aes(x = to_label(f), y = to_label(v))) + 
  geom_point() + 
  labs(x = "Treatment", y = "Experimental Unit")

p1 / p2

2021年8月17日由reprex package (v2.0.0)创建


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接