R中有用的data.frame
的一个非常意外的行为来自于将character
列作为因素保留。如果不考虑,这会导致许多问题。例如,假设以下代码:
foo=data.frame(name=c("c","a"),value=1:2)
# name val
# 1 c 1
# 2 a 2
bar=matrix(1:6,nrow=3)
rownames(bar)=c("a","b","c")
# [,1] [,2]
# a 1 4
# b 2 5
# c 3 6
那么您对运行bar[foo$name,]
的期望是什么?它通常应该返回根据bar
命名的foo$name
行,这意味着行'c'和'a'。但结果却不同:
bar[foo$name,]
# [,1] [,2]
# b 2 5
# a 1 4
原因在于:foo$name
不是字符向量,而是整数向量。
foo$name
# [1] c a
# Levels: a c
为了获得预期的行为,我手动将其转换为字符向量:
foo$name = as.character(foo$name)
bar[foo$name,]
# [,1] [,2]
# c 3 6
# a 1 4
但问题是我们可能很容易错过执行此操作,并在我们的代码中隐藏了错误。有没有更好的解决方案?
答案 0 :(得分:6)
这是一项功能,R正在记录中。这通常可以通过以下几种方式处理:
stringsAsFactors = TRUE
的调用中使用参数data.frame()
。请参阅?data.frame
如果您不喜欢此行为,请通过
全局设置该选项options(stringsAsFactors = FALSE)
(@JoshuaUlrich在评论中指出)第三个选项是将字符变量包装在I(....)
中。这会将分配给数据框组件的对象的类更改为包含"AsIs"
。一般来说,这应该不是问题,因为对象继承了(在本例中)类"character"
,所以应该像以前一样工作。
您可以通过以下方式检查当前正在运行的R流程中stringsAsFactors
的默认值:
> default.stringsAsFactors()
[1] TRUE
问题范围略宽于范围data.frame()
,因为这也会影响read.table()
。在该函数中,以及上面的两个选项中,您还可以通过参数colClasses
告诉R变量的所有类,R将尊重它,例如。
> tmp <- read.table(text = '"Var1","Var2"
+ "A","B"
+ "C","C"
+ "B","D"', header = TRUE, colClasses = rep("character", 2), sep = ",")
> str(tmp)
'data.frame': 3 obs. of 2 variables:
$ Var1: chr "A" "C" "B"
$ Var2: chr "B" "C" "D"
答案 1 :(得分:0)
在下面的示例数据中,作者和标题会自动转换为因子(除非您在创建数据时添加参数stringsAsFactors = FALSE
)。如果我们忘记更改默认设置而又不想全局设置选项怎么办?
我找到的某些代码(很可能是SO)使用sapply()
来识别因子并将它们转换为字符串。
dat = data.frame(title = c("title1", "title2", "title3"),
author = c("author1", "author2", "author3"),
customerID = c(1, 2, 1))
# > str(dat)
# 'data.frame': 3 obs. of 3 variables:
# $ title : Factor w/ 3 levels "title1","title2",..: 1 2 3
# $ author : Factor w/ 3 levels "author1","author2",..: 1 2 3
# $ customerID: num 1 2 1
dat[sapply(dat, is.factor)] = lapply(dat[sapply(dat, is.factor)],
as.character)
# > str(dat)
# 'data.frame': 3 obs. of 3 variables:
# $ title : chr "title1" "title2" "title3"
# $ author : chr "author1" "author2" "author3"
# $ customerID: num 1 2 1
我假设这比使用stringsAsFactors = FALSE
参数重新读取数据集更快,但从未测试过。