自 R 诞生以来,至少在默认情况下,在使用 data.frame()
直接创建数据框或使用 read.table()
变体读取表格数据时,会将(字符)字符串转换为因子。很可能,这种情况很快就会改变。
在 R 0.62(1998 年发布)中,原始内部数据框代码被 John Chambers 贡献的解释型 Statlib 代码取代,其效果是 data.frame()
始终会将字符串转换为因子(除非受 I()
保护),而 read.table()
仍然具有 as.is
参数(默认为 FALSE
)。
在 R 2.4.0(2006 年发布)中,data.frame()
和 read.table()
获得了 stringsAsFactors
参数,默认为 default.stringsAsFactors()
,而后者又会在设置时使用 stringsAsFactors
选项,否则默认情况下会提供 TRUE
。当时,这似乎是一个可以接受的进步方式,但事后看来,这是一个不太好的主意:由于与他人共享的代码不再能够对所使用的 stringsAsFactors 默认值做出安全的假设,因此它应该(至少在理论上)始终使用 data.frame()
和 read.table()
,并明确指定 stringsAsFactors
参数以进行防御性保护。(除了这种对“安全”使用的需求相当烦人之外,也没有直接的机制来以编程方式确保它:可以在用户或站点配置文件中设置选项,但在检查时并不总是读取这些选项。)
至少还有两个充分的理由来更改当前机制。
自动字符串到因子的转换会引入不可重复性。在从字符向量创建因子时,如果没有明确给出级别,则会使用已排序的唯一值作为级别,当然排序结果取决于区域设置。因此,在自动字符串到因子转换的情况下,后续统计分析的结果可能会有所不同。
有人可能会假设,在一切都只有 ASCII 码的过去,这不是一个问题。然而,事实并非如此。在我的 Debian 系统上,locale -a
找到了 872 个区域设置(当然并非所有区域设置都不同)。使用这些区域设置对全 ASCII 向量进行排序
c("0", "1", "A", "B", "a", "b")
会发现以下排序模式的频率
01aAbB 01AaBb 01ABab aAbB01
793 57 14 8
第二组包含例如丹麦语、挪威语和土耳其语;第三组包含 C/POSIX、日语和韩语;最后一组包含捷克语和斯洛伐克语。
这清楚地表明,可重复的数据分析实际上应避免所有自动字符串到因子的转换。(经过仔细考虑,R 核心团队得出结论,通过采用特定区域设置来对排序进行区域设置独立转换在通常情况下不可行。)
最后,查看数据框的现代替代方案表明,data.table 默认使用 stringsAsFactors = FALSE,而 tibble 从不转换。
因此,在 2019 年图卢兹举行的 R 核心会议上,决定默认使用 stringsAsFactors = FALSE
,理想情况下从 4.0.0 版本开始。
最终,stringsAsFactors 选项将消失。目前,实际上已可以通过内部环境变量 _R_OPTIONS_STRINGS_AS_FACTORS_
:基本和推荐的包已在去年修改为无论默认设置如何都能正确工作,并且一些常规 CRAN 检查将很快切换为使用 _R_OPTIONS_STRINGS_AS_FACTORS_=false
。
不幸的是,事情并不像将 stringsAsFactors
参数的默认值更改为 data.frame()
和 read.table()
那样简单(当然,即使从理论上来说它无关紧要,也会产生相当大的影响)。在 R 2.4.0 中将 stringsAsFactors
参数添加到 read.table()
时,data()
已更改为使用
read.table(..., header = TRUE, as.is = FALSE)
以 .tab
或 .csv
格式读取数据文件时。因此,在读取此类数据文件时,字符串始终转换为因子。由于始终执行此转换,无论 stringsAsFactors 设置如何,它将保留,但会修改为始终在转换中使用 C 排序顺序,从而使加载此类数据集变得与区域设置无关。
为了提高可重复性,R 核心团队还考虑添加一种机制,用于选择性地记录自动字符串到因子的转换(即,在不给出级别的情况下对字符向量调用 factor()
,或对字符向量调用 as.factor()
)。