数据框和使用字符列时非常常见的错误

时间:2012-10-19 21:46:26

标签: r character dataframe

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

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

2 个答案:

答案 0 :(得分:6)

这是一项功能,R正在记录中。这通常可以通过以下几种方式处理:

  1. stringsAsFactors = TRUE的调用中使用参数data.frame()。请参阅?data.frame
  2. 如果您不喜欢此行为,请通过

    全局设置该选项
    options(stringsAsFactors = FALSE)
    
  3. (@JoshuaUlrich在评论中指出)第三个选项是将字符变量包装在I(....)中。这会将分配给数据框组件的对象的类更改为包含"AsIs"。一般来说,这应该不是问题,因为对象继承了(在本例中)类"character",所以应该像以前一样工作。

  4. 您可以通过以下方式检查当前正在运行的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参数重新读取数据集更快,但从未测试过。