1

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,]呢?它通常应该返回根据'c' 和 'a' 行bar命名的行。foo$name但结果不同:

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

但问题是我们很容易错过执行此操作,并在我们的代码中隐藏错误。有没有更好的解决方案?

4

2 回答 2

6

这是一个功能,R 正在按文档说明工作。这通常可以通过以下几种方式处理:

  1. stringsAsFactors = TRUE在对 的调用中使用参数data.frame()。看?data.frame
  2. 如果您讨厌这种行为,请通过以下方式全局设置选项

    options(stringsAsFactors = FALSE)
    
  3. (正如@JoshuaUlrich 在评论中指出的那样)第三种选择是将字符变量包装在I(....). 这会将分配给数据框组件的对象的类更改为 include "AsIs"。一般来说,这应该不是问题,因为对象继承了(在这种情况下)类"character",所以应该像以前一样工作。

您可以通过以下方式检查stringsAsFactors当前运行的 R 进程的默认值:

> default.stringsAsFactors()
[1] TRUE

这个问题比范围稍宽data.frame(),因为这也会影响read.table(). 在该函数以及上面的两个选项中,您还可以通过参数告诉 R 变量的所有类是什么colClasses,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"
于 2012-10-19T21:52:37.993 回答
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,但从未测试过。

于 2012-10-20T04:25:28.850 回答