R:意外地使用因子列对数据帧进行子集设置,就好像它是合乎逻辑的



我继承了一些遗留的R代码来工作,这是在同一行中被错误地认为是布尔值的其他列中的值的基础上重新编码列中的一些值,在现实中,这些值实际上是(字符串被转换为)因素,如:

df <- data.frame(value = c(1, 2, 3, 4, 5, 6),
                 reversed = c("true", "false",
                              "true", "true",
                              "false", "false"))
str(df)
#> 'data.frame':    6 obs. of  2 variables:
#>  $ value   : num  1 2 3 4 5 6
#>  $ reversed: Factor w/ 2 levels "false","true": 2 1 2 2 1 1
df$recoded_value <- df$value
df$recoded_value[df$reversed] <- 7 - df$recoded_value[df$reversed]

如果检查结果,会产生意想不到的结果。df[2, "recoded_value"]是5,但目的是让它是2。此外,df[3, "recoded_value"]为3,但意图是使其为4。

我想知道这里发生了什么。我的第一个假设是R将一个因子水平视为TRUE,另一个因子水平视为FALSE。但显然不是这样的,因为相同的因子水平不是被相同地对待:

df[c(1,3), ]
#>   value reversed recoded_value
#> 1     1     true             6
#> 3     3     true             3
df[c(2,5), ]
#>   value reversed recoded_value
#> 2     2    false             5
#> 5     5    false             5

这是怎么回事?

澄清一下:我对问题的解决方案不感兴趣。我知道如何修复代码以产生预期的结果。我想了解:
  1. 为什么这段代码可以工作?如何在因子列的基础上进行子集?`[`做了什么,甚至允许这样做?
  2. 为什么相同值的东西(即。,相同水平的一个因素)被区别对待?

正如文章中提到的,reversedfactor而不是logical向量。在R中,TRUE/FALSE值为逻辑值,因此转换为logical向量

df$reversed <- df$reversed=="true"

关于为什么我们有意外的输出(来自OP的代码),

df$reversed
#[1] true  false true  true  false false
#Levels: false true

levels按字母顺序排列,factor的存储模式为integer,即

as.integer(df$reversed)
#[1] 2 1 2 2 1 1

因此,当我们使用'reversed'对'recoded_value'进行子集时,它将基于数字索引

进行子集
df$recoded_value[df$reversed]
#[1] 2 1 2 2 1 1

。输出中的第一个值是'recoded_value'的第二次观测值和第二次观测值,以此类推,如果我们使用正确的逻辑索引

df$recoded_value[df$reversed=="true"]
#[1] 1 3 4

让我们检查一下改变'reversed'后的效果

df$reversed <- df$reversed=="true"
df$recoded_value[df$reversed] <- 7 - df$recoded_value[df$reversed]
df[c(1,3), ]
#  value reversed recoded_value
#1     1     TRUE             6
#3     3     TRUE             4
df[c(2,5),]
#  value reversed recoded_value
#2     2    FALSE             2
#5     5    FALSE             5

相关内容

最新更新